Thursday, November 1, 2018

Search and value help for custom fields - tx. SM_CRM - Seach: Change documents

As I am still newbie the Solution manager seems to me still as miracle black box. There are nice techniques like e.g. dynamic mapping of searched database table fields and class methods within table CRMC_REPDY.

Version


My task was to make alive search of already made custom fields. The thing is the version of Solution manager heavily matters here. In my case it was Solman 7.1.

I found two precious tutorials to follow:


  1. Basically all the stuff should work according to perfect manual of Peter Weigel.
    http://www.hybrid-eichhörnchen.de/business-transaction-search-enhancement/
  2. Second valuable article is describing how to work with web UI written by Robyn Osby
    https://blogs.sap.com/2015/02/06/how-to-update-smcrm-search-criteria-to-report-based-on-the-value-of-a-custom-field/

The corner stone is appending of custom fields to structures CRMST_QUERY_SRV_REQ_BTIL and CRMST_QUERY_R_SRV_REQ_BTIL. It has made the search abilities alive. Then there is a need to play with web UI and create a new Custom configuration area.

Unfortunately value help within list-box on search page for custom field did not work at all. I have touched all the ATX_* tables like e.g. ATX_RUN_FIELDDEF according to Peter's manual. Still no gain. I have spend bunch of time by creating various custom search helps and their proper associations, by playing with data dictionary. Nothing led to the goal in this Solman version.

Custom value help


I had already working search with custom fields on search and result page, but still no possible value lists.

Finally I found bellow steps to make it work. I had fixed value list within domain of data dictionary.


  • Open Changed document search page
  • Locate the custom field - select it to have the focus on it
  • Press key F2, you should see something like:








  • Take here Application component name: AIC_CMCD_S 
  • Run tx. BSP_WD_CMPWB and use the component name click display
  • Within the tree locate the proper View - see the picture. 







The view leads you to a main implementation class CL_AIC_CMCD_SEARCHQUERYVI_IMPL. Now it becomes easy. Just enhance the class in a standard way with new methods for each custom field. The naming convention is what matters here to correctly trigger the method for field on the web page. So each value help method must start with prefix GET_V_ + field key name. In my case GET_V_ZZFLD00000K.

Content of the method could look like:

METHOD GET_V_ZZFLD00000K .

  DATAlt_domain_entries TYPE STANDARD TABLE OF dd07v,
        lt_sel_table TYPE crmt_thtmlb_search_ddlb_nvp,
        ls_sel_table LIKE LINE OF lt_sel_table.

  FIELD-SYMBOLS <fs_domain> TYPE dd07v.

  CALL FUNCTION 'DD_DOMVALUES_GET'
  EXPORTING
    domname        'CRM_BOOLEAN' " any dictionary domain... 
    TEXT           'X'
    langu          sy-langu
  TABLES
    dd07v_tab      lt_domain_entries
  EXCEPTIONS
    wrong_textflag 1
    OTHERS         2.

  IF sy-subrc <> 0.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
    WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ELSE.

    LOOP AT lt_domain_entries ASSIGNING <fs_domain>.
      ls_sel_table-KEY <fs_domain>-domvalue_l.
      ls_sel_table-VALUE <fs_domain>-ddtext.
      APPEND ls_sel_table TO lt_sel_table.
      CLEAR ls_sel_table.
    ENDLOOP.

    cs_result-ddlb_options lt_sel_table.
  ENDIF.

ENDMETHOD.



Have fun with Solman ;-)


Other sources:
https://stackoverflow.com/questions/37372466/changing-default-dropdown-value-in-sap-crm-web-ui
http://saptechnical.com/Tutorials/CRM/Dropdown/Index.htm

















Saturday, October 27, 2018

Update any table on remote system? YES! Remote updater via remote enabled function module

If you are looking for a way how to control database table synchronization process between two systems in a custom way, check out below solution aimed for transferring of transparent tables.

Of course we need two parts, some sender part and receiver part. The base is to do a XML transformation into binary Xstring which is suitable for transferring of any data, even whole table. I did lot of tests, it really nicely flexible.


The sender part


I am not providing the full source of  sender part. The corner stone of sender program would be the below XML transformation. Keep in mind to name source on both sides in the same way. Here is the source name "remote_data",it could be anything you want, but it has to be the same on both sides sender and receiver part of XML transformation (see the receiver FM as well).

It is possible to pack into Xstring whole tables, part of the tables based on certain conditions, anything that fits your needs.



Part of the sender code:

" Declaration snippet
    METHODS:
      encode_input
        IMPORTING it_data           TYPE ANY TABLE
        EXPORTING ev_xstring        TYPE xstring,


" Implementation
  METHOD encode_input.

    DATAlo_writer TYPE REF TO cl_sxml_string_writer.

    lo_writer cl_sxml_string_writer=>createif_sxml=>co_xt_json ).

    TRY.
        CALL TRANSFORMATION id
        SOURCE remote_data it_data
        RESULT XML lo_writer.
      CATCH cx_xslt_format_error.
        MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDTRY.

    ev_xstring lo_writer->get_output).

  ENDMETHOD.                    "encode_input




The receiver part


Let me describe a bit the receiver part. It consists of remote enabled function module on remote system. There has to be established RFC connection between servers, ask your BC team. As input serves few parameters:
     
  • Table name - in a character way
  • Table content as a binary string
  • Update type with below options

Possible update types:

  1. I – insert only, it adds just new records 
  2. N – overwrite older records by newer ones based on standard date colums ERSDA or LAEDA. It skips update of tables where there is no LAEDA, ERSDA column present. This relays on DATE column only, other DATE/DATS columns are not considered.
  3. F – force update overwrite all records

Furthermore there is an exporting parameter receiving a possible error code or success confirmation. By the sent error code you can act accordingly in your sender part.





The source code of receiver:


FUNCTION /yournamespace/customizing_update.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     VALUE(IV_TABNAME) TYPE  TABNAME
*"     VALUE(IV_UPDATE_TYPE) TYPE  C1
*"     VALUE(IV_XSTRING_TAB) TYPE  XSTRING
*"  EXPORTING
*"     VALUE(EV_STATUS) TYPE  CHAR4
*"----------------------------------------------------------------------

  IF iv_tabname IS INITIAL )
    OR
     iv_xstring_tab IS INITIAL ).
    ev_status 'ERR0'.
    RETURN.
  ENDIF.


 "Note: Do here your own AUTHORITY-CHECK stuff!

  TYPES BEGIN OF ty_where_clause,
            line TYPE char72,
          END OF ty_where_clause.

  DATA:
    lo_typedescr     TYPE REF TO cl_abap_typedescr,
    lo_struct        TYPE REF TO cl_abap_structdescr,
    lo_new_tab       TYPE REF TO cl_abap_tabledescr,
    lt_comp          TYPE cl_abap_structdescr=>component_table,
    lt_desc_fields   TYPE ddfields,
    lt_data_remote   TYPE REF TO data,
    ls_data_remote   TYPE REF TO data,
    ls_data_local    TYPE REF TO data,
    lv_count_ok      TYPE i,
    lv_count_err     TYPE i,
    lt_where_cond    LIKE STANDARD TABLE OF hrcond,
    ls_where_cond    LIKE LINE OF lt_where_cond,
    lt_where_clause  TYPE STANDARD TABLE OF ty_where_clause,
    lt_date_col      TYPE STANDARD TABLE OF cl_abap_structdescr=>component-name,
    lv_date_col      LIKE LINE OF lt_date_col,
    lv_date_col_comp LIKE LINE OF lt_date_col.

  FIELD-SYMBOLS:
    <ft_data_remote> TYPE ANY TABLE,
    <fs_data_remote> TYPE data,
    <fs_data_local>  TYPE data,
    <fv_value>       TYPE any,
    <fv_date_remote> TYPE dats,
    <fv_date_local>  TYPE dats,
    <fs_desc_fields> LIKE LINE OF lt_desc_fields,
    <fv_col>         LIKE LINE OF lt_comp,
    <fv_col_date>    LIKE LINE OF lt_date_col.


  " Fill known date columns to be checked in received data (it is used in update type "N" - newer records)
  lv_date_col 'LAEDA'" Date of last modification (in loop checked as the first one)
  APPEND lv_date_col TO lt_date_col.
  lv_date_col 'ERSDA'" Date of creation
  APPEND lv_date_col TO lt_date_col.


  " Create target itab type
  cl_abap_typedescr=>describe_by_name" Note: cx_root does not catch type_not_found here
    EXPORTING  p_name         iv_tabname
    RECEIVING  p_descr_ref    lo_typedescr
    EXCEPTIONS type_not_found 1
  ).
  IF sy-subrc NE 0.
    ev_status 'ERR1'.
    "MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. --> causes dump within RFC call
    WRITE 'Error, unknown target structure/table type in data dictionary: '.
    WRITE iv_tabname.
    RETURN.
  ENDIF.

  IF lo_typedescr IS NOT INITIAL.

    " Get structure components
    lo_struct ?= lo_typedescr.
    lt_comp lo_struct->get_components).

    " Is date column of last modification/creation present?
    IF iv_update_type EQ 'N' ).
      LOOP AT lt_date_col ASSIGNING <fv_col_date>" Loop over known date columns (suitable for comparison), items order does matter
        LOOP AT lt_comp ASSIGNING <fv_col>" Loop over table components
          IF <fv_col_date> EQ <fv_col>-name.
            lv_date_col_comp <fv_col_date>.
            EXIT.
          ENDIF.
        ENDLOOP.

        IF lv_date_col_comp IS NOT INITIAL.
          EXIT.
        ENDIF.
      ENDLOOP.
    ENDIF.

    " Create new target itab
    lo_new_tab cl_abap_tabledescr=>create(
      p_line_type  lo_struct
      p_table_kind cl_abap_tabledescr=>tablekind_std
      p_unique     abap_false
    ).

    IF lo_new_tab IS NOT INITIAL.
      CREATE DATA lt_data_remote TYPE HANDLE lo_new_tab.
    ELSE.
      ev_status 'ERR2'.
      RETURN.
    ENDIF.
  ELSE.
    ev_status 'ERR3'.
    RETURN.
  ENDIF.

  " Decode data into itab
  ASSIGN lt_data_remote->TO <ft_data_remote>.

  TRY.
      CALL TRANSFORMATION id
      SOURCE XML iv_xstring_tab
      RESULT remote_data <ft_data_remote>.
    CATCH cx_xslt_format_error.
      ev_status 'ERR4'.
      "MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. --> Note: causes dump on both sides, calling program and this target FM
      WRITE 'Error occured during itab (xstring) decoding for table: '.
      WRITE iv_tabname.
      RETURN.
  ENDTRY.

  " Loop over itab for DB update
  IF <ft_data_remote> IS ASSIGNED.

    CREATE DATA ls_data_remote LIKE LINE OF <ft_data_remote>.
    ASSIGN ls_data_remote->TO <fs_data_remote>.

    CREATE DATA ls_data_local LIKE LINE OF <ft_data_remote>.
    ASSIGN ls_data_local->TO <fs_data_local>.

    " Get key fields of the table (keyflag value)
    lt_desc_fields lo_struct->get_ddic_field_list).

    " Update table according to given type
    LOOP AT <ft_data_remote> ASSIGNING <fs_data_remote>.

      IF iv_update_type EQ 'I' )  " Increment only
         OR
         iv_update_type EQ 'N' )" Update only older

        "*******************************************************************************
        "  Do partial update
        "
        "  I - Increment only = add missing records only
        "    or
        "  N - Update only older recocords by newer ones based on LAEDA, ERSDA columns
        "*******************************************************************************

        " Prepare SQL WHERE condition
        LOOP AT lt_desc_fields ASSIGNING <fs_desc_fields> WHERE keyflag abap_true" get primary key fields

          " Get value of key component
          ASSIGN COMPONENT  <fs_desc_fields>-fieldname OF STRUCTURE <fs_data_remote> TO <fv_value>.

          " Prepare condition table
          ls_where_cond-field <fs_desc_fields>-fieldname.
          ls_where_cond-opera 'EQ'.
          ls_where_cond-low   <fv_value>.
          APPEND ls_where_cond TO lt_where_cond.

          UNASSIGN <fv_value>.
          CLEAR ls_where_cond.

        ENDLOOP.

        IF lt_where_cond IS NOT INITIAL.

          " Convert condition table into SQL WHERE clause
          CALL FUNCTION 'RH_DYNAMIC_WHERE_BUILD'
            EXPORTING
              dbtable         iv_tabname
            TABLES
              condtab         lt_where_cond
              where_clause    lt_where_clause
            EXCEPTIONS
              empty_condtab   1
              no_db_field     2
              unknown_db      3
              wrong_condition 4
              OTHERS          5.
          IF sy-subrc <> 0.
            ev_status 'ERR7'.
            WRITE 'Some error occured during building SQL WHERE condition for table: '.
            WRITE iv_tabname.
          ENDIF.

          SELECT SINGLE *
            FROM  (iv_tabname)
            INTO  <fs_data_local>
            WHERE (lt_where_clause).

          IF sy-subrc EQ 0.

            " Update only older recocords by newer ones based on LAEDA, ERSDA columns
            IF iv_update_type EQ 'N' ).
              IF <fs_data_local> IS ASSIGNED" Then compare date, if it is present
                IF lv_date_col_comp IS NOT INITIAL.
                  " Compare remote and local dates

                  " Get values of date component
                  ASSIGN COMPONENT lv_date_col_comp OF STRUCTURE <fs_data_local>  TO <fv_date_local>.
                  ASSIGN COMPONENT lv_date_col_comp OF STRUCTURE <fs_data_remote> TO <fv_date_remote>.

                  IF <fv_date_local> < <fv_date_remote>.

                    "*******************************************************************************
                    " Overwrite record with newer one
                    "*******************************************************************************
                    UPDATE (iv_tabnameFROM <fs_data_remote>.
                    IF sy-subrc EQ 0.
                      lv_count_ok lv_count_ok + 1.
                      COMMIT WORK.
                    ELSE.
                      lv_count_err lv_count_err + 1.
                      ROLLBACK WORK.
                    ENDIF.

                  ENDIF.

                  UNASSIGN <fv_date_local>.
                  UNASSIGN <fv_date_remote>.

                ENDIF.
              ENDIF.
            ENDIF.

          ELSE.

            "*******************************************************************************
            " Record does not exist = add it (It fits to both types of update I + N)
            "*******************************************************************************
            INSERT (iv_tabnameFROM <fs_data_remote>.
            IF sy-subrc EQ 0.
              lv_count_ok lv_count_ok + 1.
              COMMIT WORK.
            ELSE.
              lv_count_err lv_count_err + 1.
              ROLLBACK WORK.
            ENDIF.

          ENDIF.

          CLEAR lt_where_cond.
          CLEAR lt_where_clause.

        ENDIF.

      ELSEIF iv_update_type EQ 'F'.

        "*******************************************************************************
        "  Do force update = overwrite all already existing records
        "  Inserts + Updates
        "*******************************************************************************
        MODIFY (iv_tabnameFROM <fs_data_remote>.
        IF sy-subrc EQ 0.
          lv_count_ok lv_count_ok + 1.
          COMMIT WORK.
        ELSE.
          lv_count_err lv_count_err + 1.
          ROLLBACK WORK.
        ENDIF.

      ENDIF.

    ENDLOOP.

    IF lv_count_err EQ 0.
      ev_status 'OK'.
    ELSE.
      ev_status 'ERR6'.
      WRITE 'Some error occured during update of table: '.
      WRITE iv_tabname.
      WRITE ', Number of failed records: '.
      WRITE lv_count_err.
      WRITE ', Number of well passed records: '.
      WRITE lv_count_ok.
      RETURN.
    ENDIF.

  ELSE.
    ev_status 'ERR5'.
    WRITE 'No data for table: '.
    WRITE iv_tabname.
    RETURN.
  ENDIF.

ENDFUNCTION.



The sources



  • https://stackoverflow.com/questions/30597747/cant-catch-certain-exceptions
  • http://zevolving.com/2008/09/dynamic-internal-table-creation/
  • https://blogs.sap.com/2013/04/16/writing-dynamic-where-clause-in-abap-select-query/



  • Sunday, September 16, 2018

    Reading plant from Organization levels based on Role name

    Bellow code snippet can become handy and save time if you are looking for a way how to get plant/werks assigned to a specific role. By similar code you can read of course other stuff like Shipping point ($VSTEL),  Sales  distribution channel ($VTWEG), Sales organization ($VKORG) etc. The base used FM 'PRGN_1252_READ_ORG_LEVELS' is taken out of transaction PFCG.


    DATA:
       lt_level      TYPE STANDARD TABLE OF pt1252,
       lt_plant      TYPE STANDARD TABLE OF werks,  " Final plant itab
       lv_plant_low  TYPE werks,
       lv_plant_high TYPE werks.

    FIELD-SYMBOLS:
       <fs_level> LIKE LINE OF lt_level,
       <fs_plant> LIKE LINE OF lt_plant.

    CALL FUNCTION 'PRGN_1252_READ_ORG_LEVELS'
      EXPORTING
        activity_group    '/Your_Name_Space/Role_name' 
      TABLES
        org_levels        lt_level
      EXCEPTIONS
        no_data_available 1.

    LOOP AT lt_level ASSIGNING <fs_level> WHERE varbl EQ '$WERKS'.

      IF <fs_level>-low IS NOT INITIAL AND <fs_level>-high IS INITIAL ).
        APPEND <fs_level>-low TO lt_plant.

      ELSEIF <fs_level>-low IS NOT INITIAL AND <fs_level>-high IS NOT INITIAL ).
        IF <fs_level>-low < <fs_level>-high.

          lv_plant_low  <fs_level>-low.
          lv_plant_high <fs_level>-high.

          WHILE lv_plant_low <= lv_plant_high.
            APPEND lv_plant_low TO lt_plant.
            lv_plant_low lv_plant_low + 1.
          ENDWHILE.

        ELSE.
          APPEND <fs_level>-low TO lt_plant.
        ENDIF.
      ENDIF.
    ENDLOOP.











    Saturday, June 9, 2018

    Update all dynpro/screen fields in one shot

    Lazyness is one of the engines for iprovements. So this is the root cause of this short article.

    Once you are trying to update a classic Dynpro you should call FM DYNP_VALUES_UPDATE. Ok, you can collect ITAB for each screen field providing name and value as on bellow example.

          ls_dynpfield-fieldname  = lv_screen_field_name.
          ls_dynpfield-fieldvalue = lv_screen_field_val.
          APPEND ls_dynpfield TO lt_dynpfield.

    It is still annoying to do so for each screen field...

    Imagine, you can set the screen values as you need and run just once some certain method doing the whole screen update job. Here it is, there is just one simple call. You can reuse it as many times you want. The only condition is to have screen connected to a certain structure variable. That's all.


    Screen update method call

        " Update screen
         lo_ctrl_screen->update_screen_fields(
          iv_prog       sy-cprog                                                      " Program name
          iv_screen     '0200'                       " Dynpro number
          iv_struc_name 'GS_SCREEN_STRUCTURE_NAME' " Name of structure holding the screen values
          is_struc_data gs_real_structure                            " Structure variable itself
        ).




    The method source code: 


      METHOD update_screen_fields.

        DATA:
          lt_dynpfield         TYPE STANDARD TABLE OF dynpread,
          ls_dynpfield         LIKE LINE OF lt_dynpfield,
          lo_struc_descr       TYPE REF TO cl_abap_structdescr.

        lo_struc_descr ?= cl_abap_structdescr=>describe_by_datais_struc_data ).
        CHECK sy-subrc 0.

        FIELD-SYMBOLS:
          <fs_comp> LIKE LINE OF lo_struc_descr->components,
          <fs_val>  TYPE any.

        LOOP AT lo_struc_descr->components ASSIGNING <fs_comp>.
          ASSIGN COMPONENT <fs_comp>-name OF STRUCTURE is_struc_data TO <fs_val>.

          IF <fs_val> IS NOT INITIAL.
            CLEAR ls_dynpfield.
            CONCATENATE iv_struc_name <fs_comp>-name INTO ls_dynpfield-fieldname.
            ls_dynpfield-fieldvalue <fs_val>.
            APPEND ls_dynpfield TO lt_dynpfield.
          ENDIF.

        ENDLOOP.

        " Update screen
        IF lt_dynpfield IS NOT INITIAL.
          CALL FUNCTION 'DYNP_VALUES_UPDATE'
            EXPORTING
              dyname     iv_prog    "Program name
              dynumb     iv_screen  "Screen number
            TABLES
              dynpfields lt_dynpfield
            EXCEPTIONS
              OTHERS     0.
        ENDIF.

      ENDMETHOD.                    "update_screen_fields


    Sunday, May 6, 2018

    How to refresh graph (based on CL_GUI_CHART_ENGINE / GRAPHICS_GUI_CE_DEMO)

    Many of us used the sample simple nice program GRAPHICS_GUI_CE_DEMO as a pattern how to work with graphs. Unfortunately this program does not solve the question how to update/refresh  graph data, furthermore you can find misleading info on some forums.  I do not say the bellow pattern is the best, but it is working fine :)

    In my case I have dedicated modal window for graph. Each time I press ALV toolbar button I want to see related graph to the grid. I have always prepared an internal table with correct graph data (see below t_graph within the coding).




    Screen call e.g.


        CALL SCREEN 500 STARTING AT 13 ENDING AT 175 37.



    PBO


    MODULE status_0500 OUTPUT.

      SET PF-STATUS 'DEFAULT_STATUS'.
      SET TITLEBAR 'GRAPH'.

      lcl_main=>self->generate_graph).

    ENDMODULE.


    PAI

    MODULE exit_command_0500 INPUT.

      LEAVE TO SCREEN 0" Back to main screen

    ENDMODULE.


    The PBO method


    I am keeping just two global variables outside the method itself. There is no need to call any screen container destructor to reuse the graph object again etc. The base screen under the modal window can take care for calling destructors first and freeing the variables..


    The globals


    DATA
      go_ce_container   TYPE REF TO cl_gui_container.
      go_ce_viewer      TYPE REF TO cl_gui_chart_engine.



    The generator method coding


    The whole question is what should be setup each screen call (local variables) and what should remain in memory (global ones). The local class  lo_graph is just my rewrite of two performs/subroutines  from the demo program GRAPHICS_GUI_CE_DEMO. It's methods are responsible for filling the XML data.


      METHOD generate_graph.

        DATA:
          lo_graph           TYPE REF TO lcl_graph" custom class preparing XML data for graph
          lo_ixml_data_doc   TYPE REF TO if_ixml_document,
          lo_ixml_custom_doc TYPE REF TO if_ixml_document,
          lo_ixml            TYPE REF TO if_ixml,
          lo_ixml_sf         TYPE REF TO if_ixml_stream_factory,
          lo_ostream         TYPE REF TO if_ixml_ostream,
          lv_xstr            TYPE xstring.

        IF go_ce_container IS INITIAL.

           go_ce_container = generate_graph_container).

          CREATE OBJECT go_ce_viewer
            EXPORTING
              parent go_ce_container

        ENDIF.

        lo_ixml cl_ixml=>create).
        lo_ixml_sf lo_ixml->create_stream_factory).

        CREATE OBJECT lo_graph.

        lo_graph->create_data(
          EXPORTING
            it_graph    t_graph
            iv_ixml     lo_ixml
          CHANGING
            cv_ixml_doc lo_ixml_data_doc
        ).
        lo_ostream lo_ixml_sf->create_ostream_xstringlv_xstr ).
        lo_ixml_data_doc->renderostream lo_ostream ).
        go_ce_viewer->set_dataxdata lv_xstr ).

        CLEAR lv_xstr.

        lo_graph->create_custom(
          EXPORTING
            iv_title    v_grid_title 
            iv_ixml     lo_ixml
          CHANGING
            cv_ixml_doc lo_ixml_custom_doc
        ).
        lo_ostream lo_ixml_sf->create_ostream_xstringlv_xstr ).
        lo_ixml_custom_doc->renderostream lo_ostream ).
        go_ce_viewer->set_customizingxdata lv_xstr ).

        go_ce_viewer->render).

      ENDMETHOD.                    "generate_graph






    Sunday, April 15, 2018

    Explore program source code including called repository classes


    Intro

    Some years ago I wrote a tiny program exploring program source code covering also all includes. Now I did a rewrite, it is a bit OOP and it offers ability to explore also repository classes used within the report.

    What it offers is obvious from input selection screen





    Possible options


    • Investigate transaction or program directly
      • From transaction it takes program name and continues as with ordinary program.
      • Within program it consider the following:
        • main report
        • all includes - used also for local classes
        • all repository classes defined as:
          • REF TO something  - it is further checked for class existance
          • something=>method( ) - it considers static calls as well
          • Furthermore there is an option to check just custom namespace classes + Y/Z ones. It means classes under the same namespace as main report. Otherwise it reads all the classes including SAP ones as e.g. SALV classes etc.
    • It does the same for repository class as an input.
    • You can provide free text as an input or regular expression to be checked against line of coding.


    Some examples

    Possible output for "select" as a free text. You can check the below picture, the pattern exists within main program and used class as well.






    Free text input "auth 02"


    The result



    Regex - all static class calls


    The result



    Regex - all number from 2 up to 8 digits


    The result


    Source





    Source code


    *&---------------------------------------------------------------------*
    *& Report /namespace/REPORT_REGEX_SEARCH
    *-----------------------------------------------------------------------
    * Purpose:  Search ABAP source code by regex
    *-----------------------------------------------------------------------
    REPORT /namespace/report_regex_search MESSAGE-ID 00.

    INCLUDE /namespace/report_regex_search_c01.


    *******************************************************************
    *   SCREENS - choose transaction or program
    *******************************************************************
    SELECTION-SCREEN BEGIN OF BLOCK frame1 WITH FRAME TITLE TEXT-001.
    " Transaction
    SELECTION-SCREEN BEGIN OF LINE.
    PARAMETERSr_trans  RADIOBUTTON GROUP grp1 USER-COMMAND flagcommand1.
    SELECTION-SCREEN COMMENT 3(60TEXT-002" Transaction
    SELECTION-SCREEN END OF LINE.

    " Program
    SELECTION-SCREEN BEGIN OF LINE.
    PARAMETERSr_prog  RADIOBUTTON GROUP grp1 DEFAULT 'X'.
    SELECTION-SCREEN COMMENT 3(60TEXT-003" Program
    SELECTION-SCREEN END OF LINE.

    " Class
    SELECTION-SCREEN BEGIN OF LINE.
    PARAMETERSr_class  RADIOBUTTON GROUP grp1.
    SELECTION-SCREEN COMMENT 3(60TEXT-005" Class
    SELECTION-SCREEN END OF LINE.

    SELECTION-SCREEN SKIP.

    PARAMETERSp_tcode TYPE tstc-tcode,                                     " Transaction Code
                p_prog  TYPE trdir-name MATCHCODE OBJECT progname,           " Program name
                p_class TYPE seoclass-clsname MATCHCODE OBJECT seoclsnamef4" Class name

    SELECTION-SCREEN END OF BLOCK frame1.

    SELECTION-SCREEN BEGIN OF BLOCK frame2 WITH FRAME TITLE TEXT-004.

    " Free text
    SELECTION-SCREEN BEGIN OF LINE.
    PARAMETERSr_text  RADIOBUTTON GROUP grp2 USER-COMMAND flagcommand2 DEFAULT 'X'.
    SELECTION-SCREEN COMMENT 3(60TEXT-006" Free text switch
    SELECTION-SCREEN END OF LINE.

    " Regular expresson
    SELECTION-SCREEN BEGIN OF LINE.
    PARAMETERSr_regul RADIOBUTTON GROUP grp2.
    SELECTION-SCREEN COMMENT 3(60TEXT-007" Regular expression switch
    SELECTION-SCREEN END OF LINE.

    SELECTION-SCREEN SKIP.

    " Inputs as e.g.:
    *    '^.+(\d{4}).+$'. " all four digits values in coding
    *    `^[^\*].+(')(\d{4})(').+$`. " all uncommented lines
    *    `^[\*].+(')(\d{4})(').+$`. " all commented lines starting with *
    *    `^.+(\BTRY\B).+$`. " get TRY statement lines, here case insensitive
    PARAMETERS p_patern  TYPE LENGTH 100 LOWER CASE" Pattern to be found
    PARAMETERS p_own_cl  AS CHECKBOX DEFAULT 'X'" Process only own namespace classes + Y/Z ones

    SELECTION-SCREEN END OF BLOCK frame2.


    *******************************************************************
    AT SELECTION-SCREEN OUTPUT " PBO action (hide/show)
    *******************************************************************
      LOOP AT SCREEN" Hiding

        IF r_prog NE 'X' AND screen-name CS 'p_prog' ).
          screen-active 0.
          MODIFY SCREEN.
          CONTINUE.
        ENDIF.

        IF r_trans NE 'X' AND screen-name CS 'p_tcode' ).
          screen-active 0.
          MODIFY SCREEN.
          CONTINUE.
        ENDIF.

        IF r_class NE 'X' AND screen-name CS 'p_class' ).
          screen-active 0.
          MODIFY SCREEN.
          CONTINUE.
        ENDIF.

        IF r_trans NE 'X' AND r_prog NE 'X' AND  screen-name CS 'p_own_cl' ).
          screen-active 0.
          MODIFY SCREEN.
          CONTINUE.
        ENDIF.

      ENDLOOP.


    *******************************************************************
    AT SELECTION-SCREEN" PAI check
    *******************************************************************

    * Get program name out of transaction
      IF sy-ucomm EQ 'ONLI'" Check done only after pressing of submit button
        IF r_trans EQ 'X'.
          " Check transaction existance
          CONDENSE p_tcode NO-GAPS.
          CLEAR p_prog.
          SELECT SINGLE pgmna
          FROM tstc
          INTO p_prog
          WHERE tcode p_tcode.
          IF sy-subrc NE 0.
            MESSAGE e001 WITH 'Transaction does not exist : ' p_tcode.
          ENDIF.
        ELSEIF r_prog EQ 'X'.
          " Check program existance
          SELECT SINGLE name
          FROM trdir
          INTO p_prog
          WHERE name p_prog.
          IF sy-subrc NE 0.
            MESSAGE e001 WITH 'Program does not exist : ' p_prog.
          ENDIF.
        ELSEIF r_class EQ 'X'.
          CONDENSE p_class NO-GAPS.
          IF p_class IS INITIAL.
            MESSAGE e001 WITH 'There is no repository class to be processed.'.
          ENDIF.
          " Check class existance
          SELECT SINGLE clsname
          FROM seoclass
          INTO p_class
          WHERE clsname p_class.
          IF sy-subrc NE 0.
            MESSAGE e001 WITH 'Repository class does not exist : ' p_class.
          ENDIF.
        ENDIF.
      ENDIF.


    *******************************************************************
    START-OF-SELECTION.
    *******************************************************************

      DATAgv_code_src  TYPE LENGTH 1" P - program, C - class

      IF r_trans EQ abap_true OR r_prog EQ abap_true ).
        gv_code_src 'P'" = Program
      ELSE.
        gv_code_src 'C'" = Class
    *  else 'F' = function module - possibly in future etc.
      ENDIF.

      lcl_main_regex=>self->run(
        iv_code_src   gv_code_src " P - program, C - class
        iv_regex_src  r_regul     " String or Regular expression
        iv_pattern    p_patern    " Searched pattern
        iv_prog       p_prog      " Program name
        iv_class      p_class     " Repository class name
        iv_own_cl     p_own_cl    " Explore/process only classes from custom namespace (takem from program name) or Z/Y classes
      ).




    *&---------------------------------------------------------------------*
    *&  Include           /namespace/REPORT_REGEX_SEARCH_C01
    *&---------------------------------------------------------------------*

    CLASS lcl_main_regex DEFINITION CREATE PRIVATE FINAL.

      PUBLIC SECTION.

        CLASS-DATA
          self TYPE REF TO lcl_main_regex.

        CLASS-METHODS
          class_constructor.

        METHODS
          run
            IMPORTING
              iv_code_src  TYPE char1
              iv_regex_src TYPE abap_bool
              iv_pattern   TYPE clike
              iv_prog      TYPE progname
              iv_class     TYPE seoclsname
              iv_own_cl    TYPE flag.

      PRIVATE SECTION.

        TYPES:
          BEGIN OF ts_include,
            prgname TYPE progname,
          END OF ts_include,
          BEGIN OF ts_code_line,
            src_type TYPE string,
            src_name TYPE string,
            line_num TYPE i,
            src_code TYPE string,
          END OF ts_code_line,
          tt_include       TYPE STANDARD TABLE OF ts_include,
          tt_code_textpool TYPE TABLE OF textpool,
          tt_code_str      TYPE STANDARD TABLE OF string" Note: edpline (char72) is short for some cases


        CONSTANTS:
          " Get possible class from data declaration
          cv_class_ref        TYPE string VALUE '^.{0,}\s+[Rr][Ee][Ff][[:space:]]{1,}[Tt][Oo]\s+(\S*[^,\.\s]).+$',
          " Get class out of static method call
          cv_class_static     TYPE string VALUE '^.{0,}(\S*)(=>).+$',
          " Get class/program prefix - custom namespace
          cv_namespace_prefix TYPE string VALUE '^.{0,}[/](\S*)[/].+$'.

        DATA:
          v_total_line_num   TYPE i,
          v_found_flag       TYPE abap_bool,
          t_regex_class      TYPE STANDARD TABLE OF string,
          t_clsname          TYPE STANDARD TABLE OF string,
          t_target_code_line TYPE STANDARD TABLE OF ts_code_line,
          o_ref              TYPE REF TO cx_root.

        METHODS:
          convert_code_edpline
            IMPORTING it_code TYPE seop_source
            EXPORTING et_code TYPE tt_code_str,
          convert_code_textpool
            IMPORTING it_code TYPE tt_code_textpool
            EXPORTING et_code TYPE tt_code_str,
          create_regex
            IMPORTING iv_pattern      TYPE clike
            RETURNING VALUE(rv_regexTYPE string,
          check_class_exist
            IMPORTING iv_class        TYPE string
                      iv_own_cl       TYPE flag
                      iv_prog         TYPE progname
            RETURNING VALUE(rv_existTYPE abap_bool,
          process_class_includes
            IMPORTING
              iv_class TYPE string
              iv_regex TYPE clike,
          get_process_class_source
            IMPORTING
              iv_regex   TYPE clike
              iv_class   TYPE seoclskey
              iv_inctype TYPE  c,
          get_program_includes
            IMPORTING iv_prog    TYPE prgname
            EXPORTING et_include TYPE tt_include,
          process_program
            IMPORTING
              iv_prog   TYPE progname
              iv_regex  TYPE string
              iv_own_cl TYPE flag,
          process_class
            IMPORTING
              iv_class TYPE seoclsname
              iv_regex TYPE string,
          process_program_includes
            IMPORTING
              iv_prog    TYPE progname
              iv_regex   TYPE clike
              it_include TYPE tt_include,
          process_lines
            IMPORTING
              iv_regex           TYPE clike
              iv_src_type        TYPE clike
              iv_src_name        TYPE clike
              it_src_code        TYPE tt_code_str
              iv_colect_subclass TYPE abap_bool,
          process_final_alv,
          regex_classname
            IMPORTING
              iv_code_line TYPE string,
          regex_target_string
            IMPORTING
              iv_regex     TYPE clike
              iv_src_type  TYPE clike
              iv_src_name  TYPE clike
              iv_line_num  TYPE i
              iv_code_line TYPE string.

    ENDCLASS.                    "lcl_main_regex DEFINITION

    CLASS lcl_main_regex IMPLEMENTATION.

      METHOD class_constructor.

        CREATE OBJECT self.

        APPEND cv_class_ref TO self->t_regex_class.
        APPEND cv_class_static TO self->t_regex_class.

      ENDMETHOD.                    "class_constructor

      METHOD run.

        DATA lv_regex TYPE string.

        IF iv_regex_src EQ abap_true.
          lv_regex iv_pattern" Regular expression
        ELSE.
          lv_regex create_regexiv_pattern iv_pattern )" Free string
        ENDIF.

        IF iv_code_src 'P'" = Program
          process_program(
            iv_prog   iv_prog
            iv_regex  lv_regex
            iv_own_cl iv_own_cl
          ).
        ELSEIF iv_code_src 'C'" = Class
          process_class(
            iv_class iv_class
            iv_regex lv_regex
          ).
        ENDIF.

      ENDMETHOD.                    "run

      METHOD convert_code_edpline.

        DATA lv_code TYPE string.

        CLEAR et_code[].

        FIELD-SYMBOLS <f_code> LIKE LINE OF it_code.

        LOOP AT it_code ASSIGNING <f_code>.
          CLEAR lv_code.
          lv_code <f_code>" edpline to string
          APPEND lv_code TO et_code.
        ENDLOOP.

      ENDMETHOD.

      METHOD convert_code_textpool.

        DATA lv_code TYPE string.

        CLEAR et_code[].

        FIELD-SYMBOLS <f_code> LIKE LINE OF it_code.

        LOOP AT it_code ASSIGNING <f_code>.
          CLEAR lv_code.
          lv_code <f_code>-entry.
          APPEND lv_code TO et_code.
        ENDLOOP.

      ENDMETHOD.

      METHOD create_regex.

        DATA:
          lt_line    TYPE STANDARD TABLE OF string,
          lv_pattern TYPE string.

        lv_pattern iv_pattern.
        CONDENSE lv_pattern.
        SPLIT lv_pattern AT space INTO TABLE lt_line.

        rv_regex '^'.

        FIELD-SYMBOLS<f_line> LIKE LINE OF lt_line.
        LOOP AT lt_line ASSIGNING <f_line>.

          " Escape stars: e.g for searchings like: "SELECT *"
          IF <f_line> EQ '*'.
            <f_line> '\*'.
          ENDIF.

          CONCATENATE rv_regex '.{0,}(' <f_line> '){1,}' INTO rv_regex.
        ENDLOOP.

        CONCATENATE rv_regex '.{0,}$' INTO rv_regex.

      ENDMETHOD.

      METHOD check_class_exist.

        DATA:
          lv_class      TYPE seoclsname,
          lv_prefix_cl  TYPE string,
          lv_prefix_prg TYPE string.

        rv_exist abap_false.
        lv_class iv_class.
        TRANSLATE lv_class TO UPPER CASE.

        " Check class custom/own namespace
        IF iv_own_cl EQ abap_true.

          CLEAR lv_prefix_cl.
          FIND REGEX cv_namespace_prefix
          IN iv_class IGNORING CASE
          SUBMATCHES lv_prefix_cl.
          CONDENSE lv_prefix_cl.

          CLEAR lv_prefix_prg.
          FIND REGEX cv_namespace_prefix
          IN iv_prog IGNORING CASE
          SUBMATCHES lv_prefix_prg.
          CONDENSE lv_prefix_prg.

          TRANSLATE lv_prefix_prg  TO UPPER CASE.
          TRANSLATE lv_prefix_cl   TO UPPER CASE.

          IF lv_prefix_prg IS NOT INITIAL AND lv_prefix_cl IS NOT INITIAL )
             AND lv_prefix_prg NE lv_prefix_cl ).
            RETURN.
          ENDIF.

          " Check Z/Y classes
          IF lv_prefix_cl IS INITIAL AND lv_class NP 'Z*' AND lv_class NP 'Y*' ).
            RETURN.
          ENDIF.

        ENDIF.

        " Check class existence in database
        SELECT COUNT)
          FROM seoclass
          WHERE clsname lv_class.
        IF sy-subrc EQ 0.
          rv_exist abap_true.
        ENDIF.

      ENDMETHOD.

      METHOD process_class_includes.

        " Inspiration taken from: http://www.sapnet.ru/viewtopic.php?p=7706

        DATA:
          lv_classkey     TYPE seoclskey,
          lv_class_src    TYPE string,
          lt_text         TYPE TABLE OF textpool,
          lt_types        TYPE seoo_types_r,
          lt_include      TYPE seop_methods_w_include,
          lt_source       TYPE seop_source,
          ls_typkey       TYPE seocmpkey,
          lt_code_edp     TYPE seop_source,
          lt_code_str     TYPE tt_code_str,
          lv_include_name TYPE programm.

        FIELD-SYMBOLS:
          <fs_type>    TYPE seoo_type_r,
          <fs_include> TYPE LINE OF seop_methods_w_include.

        lv_classkey  iv_class.
        TRANSLATE lv_classkey TO UPPER CASE.

        " Get class description
        CALL FUNCTION 'SEO_CLASS_TYPEINFO_GET'
          EXPORTING
            clskey            lv_classkey
            version           seoc_version_active
            with_descriptions 'X'
          IMPORTING
            types             lt_types
          EXCEPTIONS
            not_existing      1
            is_interface      2
            model_only        3
            OTHERS            4.

        IF sy-subrc NE 0.
          MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
          WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ENDIF.

        " Gathering together internal type source
        LOOP AT lt_types ASSIGNING <fs_type>.

          ls_typkey-clsname <fs_type>-clsname.
          ls_typkey-cmpname <fs_type>-cmpname.

          CLEAR lt_code_edp[].
          CALL FUNCTION 'SEO_CLASS_GET_TYPE_SOURCE'
            EXPORTING
              typkey                       ls_typkey
            IMPORTING
              source                       lt_code_edp
            EXCEPTIONS
              _internal_class_not_existing 1
              not_existing                 2
              not_edited                   3
              OTHERS                       4.

          CHECK sy-subrc AND lt_code_edp[] IS NOT INITIAL.

          convert_code_edpline(
            EXPORTING it_code lt_code_edp[]
            IMPORTING et_code lt_code_str
          ).

          CLEAR lv_class_src.
          CONCATENATE <fs_type>-clsname <fs_type>-cmpname INTO lv_class_src.
          CONDENSE lv_class_src.

          process_lines(
            iv_regex    iv_regex
            iv_src_type 'CLASS_DEF:'
            iv_src_name lv_class_src
            it_src_code lt_code_str
            iv_colect_subclass abap_false
          ).
        ENDLOOP.
        FREE lt_code_str.
        FREE lt_code_edp[].

        " Extract local definitions (classes, macros)
        CALL FUNCTION 'SEO_CLASS_GET_INCLUDE_BY_NAME'
          EXPORTING
            clskey   lv_classkey
            limu     seok_limu_locals
          IMPORTING
            progname lv_include_name.

        get_process_class_source(
          iv_regex   iv_regex
          iv_class   lv_classkey
          iv_inctype lv_include_name
        ).

        get_process_class_source(
          iv_regex   iv_regex
          iv_class   lv_classkey
          iv_inctype seop_ext_class_locals_def
        ).

        get_process_class_source(
          iv_regex   iv_regex
          iv_class   lv_classkey
          iv_inctype seop_ext_class_locals_imp
        ).

        get_process_class_source(
          iv_regex   iv_regex
          iv_class   lv_classkey
          iv_inctype seop_ext_class_macros
        ).

        " Process texts
        CLEAR lv_include_name.
        CALL FUNCTION 'SEO_CLASS_GET_INCLUDE_BY_NAME'
          EXPORTING
            clskey   lv_classkey
          IMPORTING
            progname lv_include_name.

        DATA lv_lang TYPE spras.
        SELECT spras FROM t002 INTO lv_lang.
          REFRESH lt_text.
          READ TEXTPOOL lv_include_name INTO lt_text LANGUAGE lv_lang.

          CHECK sy-subrc 0.

          convert_code_textpool(
            EXPORTING it_code lt_text
            IMPORTING et_code lt_code_str
          ).

          process_lines(
            iv_regex    iv_regex
            iv_src_type 'CLASS_TEXT:'
            iv_src_name lv_classkey
            it_src_code lt_code_str
            iv_colect_subclass abap_false
          ).
        ENDSELECT.

        " Load all method includes
        CALL FUNCTION 'SEO_CLASS_GET_METHOD_INCLUDES'
          EXPORTING
            clskey                       lv_classkey
          IMPORTING
            includes                     lt_include
          EXCEPTIONS
            _internal_class_not_existing 1
            OTHERS                       2.

        IF sy-subrc NE 0.
          MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
          WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ENDIF.

        LOOP AT lt_include ASSIGNING <fs_include>.

          CLEAR lt_source[].
          CALL FUNCTION 'SEO_METHOD_GET_SOURCE'
            EXPORTING
              mtdkey                        <fs_include>-cpdkey
              state                         'A'
            IMPORTING
              source                        lt_source
            EXCEPTIONS
              _internal_method_not_existing 1
              _internal_class_not_existing  2
              version_not_existing          3
              inactive_new                  4
              inactive_deleted              5
              OTHERS                        6.

          CHECK sy-subrc 0.

          convert_code_edpline(
            EXPORTING it_code lt_source[]
            IMPORTING et_code lt_code_str
          ).

          process_lines(
            iv_regex    iv_regex
            iv_src_type 'CLASS_INC2:'
            iv_src_name <fs_include>-cpdkey
            it_src_code lt_code_str
            iv_colect_subclass abap_false
          ).
        ENDLOOP.

        IF sy-subrc NE 0.
          MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
          WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ENDIF.

      ENDMETHOD.

      METHOD get_process_class_source.

        DATA:
          lt_code_edp  TYPE seop_source,
          lt_code_str  TYPE tt_code_str,
          lv_class_src TYPE string.

        CALL FUNCTION 'SEO_CLASS_GET_INCLUDE_SOURCE'
          EXPORTING
            clskey                       iv_class
            inctype                      iv_inctype
          IMPORTING
            source                       lt_code_edp
          EXCEPTIONS
            _internal_class_not_existing 0
            not_existing                 0
            OTHERS                       0.

        convert_code_edpline(
          EXPORTING it_code lt_code_edp
          IMPORTING et_code lt_code_str
        ).

        CONCATENATE iv_class iv_inctype INTO lv_class_src.
        CONDENSE lv_class_src.

        process_lines(
          iv_regex    iv_regex
          iv_src_type 'CLASS_INC:'
          iv_src_name lv_class_src
          it_src_code lt_code_str
          iv_colect_subclass abap_false
        ).

        FREE lt_code_edp.
        FREE lt_code_str.

      ENDMETHOD.

      METHOD get_program_includes.

        " Get include list of program
        CALL FUNCTION 'GET_INCLUDETAB'
          EXPORTING
            progname iv_prog
          TABLES
            incltab  et_include.

        " Add main program itself
        APPEND iv_prog TO et_include.

      ENDMETHOD.

      METHOD process_program.

        DATA lt_include  TYPE STANDARD TABLE OF ts_include.

        CLEAR t_clsname.

        get_program_includes(
          EXPORTING iv_prog iv_prog
          IMPORTING et_include lt_include
        ).

        process_program_includes(
          iv_prog    iv_prog
          iv_regex   iv_regex
          it_include lt_include
        ).

        " Process program repository classes
        FIELD-SYMBOLS <f_class> LIKE LINE OF t_clsname.
        LOOP AT t_clsname ASSIGNING <f_class>.

          IF check_class_exist(
              iv_class <f_class>
              iv_own_cl iv_own_cl
              iv_prog iv_prog
            EQ abap_true.
            process_class_includes(
              iv_class <f_class>
              iv_regex iv_regex
            ).
          ENDIF.
        ENDLOOP.

        process_final_alv).

      ENDMETHOD.

      METHOD process_class.

        DATA lv_class TYPE string.
        lv_class iv_class.

        process_class_includes(
          iv_class lv_class
          iv_regex iv_regex
        ).

        process_final_alv).

      ENDMETHOD.

      METHOD process_program_includes.

        DATA:
          lv_srctype TYPE string,
          lt_code    TYPE tt_code_str.

        FIELD-SYMBOLS:
          <f_include> LIKE LINE OF it_include.

        " Get includes content
        LOOP AT it_include ASSIGNING <f_include>.

          " Set source type
          CLEAR lv_srctype.
          IF <f_include>-prgname EQ iv_prog.
            lv_srctype 'MAIN_PROG:'.
          ELSE.
            lv_srctype 'INCLUDE:'.
          ENDIF.

          " Load code lines
          READ REPORT <f_include>-prgname INTO lt_code.

          process_lines(
            iv_regex           iv_regex
            iv_src_type        lv_srctype
            iv_src_name        <f_include>-prgname
            it_src_code        lt_code
            iv_colect_subclass abap_true
          ).
          FREE lt_code[].
        ENDLOOP.

      ENDMETHOD.

      METHOD process_lines.

        DATA:
          lv_line_num     TYPE i.

        FIELD-SYMBOLS:
          <f_code_line>   LIKE LINE OF it_src_code.

        LOOP AT it_src_code ASSIGNING <f_code_line>.

          " Line counters
          lv_line_num lv_line_num + 1.
          v_total_line_num v_total_line_num + 1.

          " Does string match the pattern for class declaration or static usage?
          IF iv_colect_subclass EQ abap_true.
            regex_classnameiv_code_line <f_code_line> ).
          ENDIF.

          " Does string match the regex pattern?
          regex_target_string(
            iv_regex     iv_regex
            iv_src_type  iv_src_type
            iv_src_name  iv_src_name
            iv_line_num  lv_line_num
            iv_code_line <f_code_line>
          ).

        ENDLOOP" lines

      ENDMETHOD.

      METHOD process_final_alv.

        IF v_found_flag NE abap_true.
          WRITE 'Pattern WAS NOT FOUND within the source code.'.
        ELSE.

          DATA:
            lo_salv TYPE REF TO cl_salv_table,
            lo_cols TYPE REF TO cl_salv_columns,
            lo_col  TYPE REF TO cl_salv_column.

          TRY.
              CALL METHOD cl_salv_table=>factory
                EXPORTING
                  list_display if_salv_c_bool_sap=>true
                IMPORTING
                  r_salv_table lo_salv
                CHANGING
                  t_table      t_target_code_line.
            CATCH cx_root INTO o_ref.
              WRITE:'ALV error occured : 'o_ref->get_text).
              RETURN.
          ENDTRY.

          lo_cols lo_salv->get_columns).
          lo_cols->set_optimizeif_salv_c_bool_sap=>true ).

          TRY.
              lo_col lo_cols->get_column'SRC_TYPE' ).
              lo_col->set_medium_text'Source' ).

              lo_col lo_cols->get_column'SRC_NAME' ).
              lo_col->set_medium_text'Name' ).

              lo_col lo_cols->get_column'LINE_NUM' ).
              lo_col->set_medium_text'Line' ).

              lo_col lo_cols->get_column'SRC_CODE' ).
              lo_col->set_medium_text'Coding' ).

            CATCH cx_root INTO o_ref.
              WRITE:'ALV columns error occured : 'o_ref->get_text).
              RETURN.
          ENDTRY.

          lo_salv->display).

        ENDIF.

        WRITE:/,'Total number of explored lines: ',  v_total_line_num.

      ENDMETHOD.

      METHOD regex_classname.

        DATA:
          lv_clsname         TYPE string.

        FIELD-SYMBOLS:
          <f_regex_class> LIKE LINE OF t_regex_class.

        LOOP AT t_regex_class ASSIGNING <f_regex_class>.

          CLEAR lv_clsname.

          FIND REGEX <f_regex_class>
          IN iv_code_line IGNORING CASE
          SUBMATCHES lv_clsname.

          CONDENSE lv_clsname.

          IF lv_clsname IS NOT INITIAL.
            APPEND lv_clsname TO t_clsname.
          ENDIF.
        ENDLOOP.

      ENDMETHOD.

      METHOD regex_target_string.

        DATA:
          lo_matcher     TYPE REF TO cl_abap_matcher,
          lv_match       TYPE LENGTH 1,
          lv_regex       TYPE string,
          ls_target_line TYPE ts_code_line.

        " Input searched regex
        MOVE iv_regex TO lv_regex.

        " Does string match the regex pattern?
        lv_match abap_false.
        TRY.
            FREE lo_matcher.
            lo_matcher cl_abap_matcher=>create(
              pattern     lv_regex
              ignore_case abap_true
              text        iv_code_line
            ).
            lv_match lo_matcher->match).
          CATCH cx_root INTO o_ref.
            WRITE:'Input regex error occured : 'o_ref->get_text).
            RETURN.
        ENDTRY.
        IF lv_match EQ abap_true.
          CLEAR ls_target_line.

          ls_target_line-src_type iv_src_type.
          ls_target_line-src_name iv_src_name.
          ls_target_line-line_num iv_line_num.
          ls_target_line-src_code iv_code_line.

          CONDENSE ls_target_line-src_type.
          CONDENSE ls_target_line-src_name.

          APPEND ls_target_line TO t_target_code_line.

          v_found_flag abap_true" General flag
        ENDIF.

      ENDMETHOD.

    ENDCLASS.