Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
vonglan
Active Participant
As a long time SAP customer, you usually have a lot of code modifications and enhancements.

Wouldn’t it be useful sometimes to be able to search that code?

If you are already on HANA, you can use the search in Eclipse: https://blogs.sap.com/2014/01/23/abap-sourcecode-search/

But our main system ist not yet running HANA.

So, I modified SAPs standard search report RS_ABAP_SOURCE_SCAN to search for modifications and enhancements. I will tell you in this blog what to do, so you can use the same function in your system.

Modifications in 5 places in the code are needed.

First, we need to add a selection parameter for the new Option.

I inserted this after the block with the search string (a05), after line 28 (in the unmodified source).
                  BEGIN OF BLOCK zz1 WITH FRAME TITLE TEXT-zz1.
SELECTION-SCREEN: BEGIN OF LINE.
PARAMETERS: p_zz_mod TYPE xfeld AS CHECKBOX.
SELECTION-SCREEN: COMMENT (79) TEXT-zz3.
SELECTION-SCREEN: END OF LINE.
SELECTION-SCREEN: END OF BLOCK zz1,

Of course, you also need to add the texts. I use the following:

  • p_zz_mod: DUMMY - see text-zz3

  • text-zz1: Special Options

  • text-zz3: Also search all modif., and enhancements for selected packages (default Z* Y*)


Next is the definition of the local class that contains the logic.

I put this behind SAPs definition of local class lcl_source_scan (line 229 in the unmodified source):
CLASS lcl_zz DEFINITION FINAL.
PUBLIC SECTION.
TYPES: BEGIN OF tp_dynpro, " needs to be identical to ty_dynpro in lcl_source_scan
repname LIKE d020s-prog,
dynnr LIKE d020s-dnum,
END OF tp_dynpro.
TYPES: ttp_dynpro TYPE STANDARD TABLE OF tp_dynpro WITH DEFAULT KEY.
CLASS-METHODS:
add_modification_includes
CHANGING c_includes TYPE prognames
c_screens TYPE ttp_dynpro
, add_enhancement_includes
CHANGING c_includes TYPE prognames
, filter_modifications
IMPORTING i_include TYPE progname
i_screen TYPE sy-dynnr
i_source_tab TYPE abaptxt255_tab
CHANGING c_findings TYPE match_result_tab
.
PRIVATE SECTION.
TYPES: BEGIN OF tp_mod_include
, include TYPE reposrc-progname
, without_assistant TYPE abap_bool
, END OF tp_mod_include
, ttp_mod_includes TYPE STANDARD TABLE OF tp_mod_include WITH KEY include

, BEGIN OF tp_mod_screen
, program TYPE d020s-prog
, screen TYPE d020s-dnum
, without_assistant TYPE abap_bool
, END OF tp_mod_screen
, ttp_mod_screens TYPE STANDARD TABLE OF tp_mod_screen WITH KEY program
.
CLASS-METHODS:
get_method_include
IMPORTING i_class TYPE seoclsname
i_method TYPE seocpdname
RETURNING VALUE(r_include) TYPE progname
, get_function_include
IMPORTING i_function TYPE tfdir-funcname
RETURNING VALUE(r_include) TYPE progname
, get_all_includes_and_screens
IMPORTING i_type TYPE smodilog-obj_type
i_name TYPE smodilog-obj_name
EXPORTING e_includes TYPE prognames
e_screens TYPE ttp_mod_screens
, get_screen_flow_logic
IMPORTING i_screen TYPE tp_mod_screen
RETURNING VALUE(r_source_tab) TYPE d022s_t
.
CLASS-DATA:
s_mod_includes TYPE ttp_mod_includes
, s_mod_screens TYPE ttp_mod_screens
.
ENDCLASS.

Because I wanted to modify the report in a “minimally invasive” way, the additional logic is inserted in two places:

  • Where the includes to be searched are collected

  • And then, after an include has been searched, to filter out the search results that occur in the unmodified parts of the code.


The next position to modify code is for this filtering. It is in method search_source, after the two FIND ALL statements, before the CHECK lt_results IS NOT INITIAL (in the unmodified report, line 946).

Code to be inserted:
        IF p_zz_mod = abap_true AND lt_results IS NOT INITIAL.
lcl_zz=>filter_modifications( EXPORTING i_include = gv_report
i_screen = gv_dynpro
i_source_tab = gt_source
CHANGING c_findings = lt_results ).
ENDIF.

And then, at the end of method get_source_names (line 1189 in the unmodified code):
    IF p_zz_mod = abap_true.
lcl_zz=>add_enhancement_includes( CHANGING c_includes = gt_object ).
lcl_zz=>add_modification_includes( CHANGING c_includes = gt_object
c_screens = gt_dynpro ).
ENDIF.

Finally, at the end of the report, the class implementation:
CLASS lcl_zz IMPLEMENTATION.
METHOD add_enhancement_includes.
DATA devclass_range TYPE RANGE OF tadir-devclass.
IF devclass IS INITIAL. " if no devclass restriction on selection screen, assume the user only wants the customer (Z and Y) enhancements
devclass_range = VALUE #( sign = 'I' option = 'CP' ( low = 'Z*' ) ( low = 'Y*' ) ).
ELSE.
devclass_range = devclass[].
ENDIF.
SELECT obj_name
FROM tadir
WHERE pgmid = 'R3TR'
AND object = 'ENHO'
AND devclass IN @devclass_range
AND delflag = ''
INTO TABLE @data(enhancements).
LOOP AT enhancements INTO DATA(enh).
TRANSLATE enh(30) USING ' ='.
enh+30 = 'E'.
APPEND enh TO c_includes.
ENDLOOP.
ENDMETHOD.

METHOD add_modification_includes.
DATA include TYPE progname.
CLEAR: s_mod_includes, s_mod_screens.
IF sy-batch IS INITIAL.
CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
EXPORTING
text = 'GET MODIFICATION INCLUDES...'.
ENDIF.
SELECT DISTINCT obj_type, obj_name, sub_type, sub_name, operation, prot_only AS without_assistant
FROM smodilog
WHERE NOT operation IN ('MIGR', 'IMPL', 'TRSL', 'NOTE') "in sync with SE95 and Clone Finder
AND inactive = ''
AND int_type NOT IN ('DUMY') "clone finder also ignores 'XXXX'=without modification assistant, but we think this is wrong
AND obj_type IN ('PROG', 'LDBA', " LDBA = logical database, similar to PROG
'FUGR', 'FUGX', 'FUGS', " FUGX and FUGS = parts of function groups for user exits
'CLAS')
AND sub_type NOT IN ( 'REPT', 'FUGT', 'CUAD', 'DOCU', 'VARI' )
ORDER BY obj_type, obj_name, sub_type, sub_name, prot_only
INTO TABLE @DATA(modifications).

LOOP AT modifications REFERENCE INTO DATA(mod).
CASE mod->sub_type.
WHEN 'REPS'. " report source
s_mod_includes = VALUE #( BASE s_mod_includes
( include = mod->sub_name
without_assistant = mod->without_assistant ) ).
WHEN 'METH'.
s_mod_includes = VALUE #( BASE s_mod_includes
( include = get_method_include( i_class = EXACT #( mod->obj_name )
i_method = EXACT #( mod->sub_name+30 ) )
without_assistant = mod->without_assistant ) ).
WHEN 'FUNC'.
s_mod_includes = VALUE #( BASE s_mod_includes
( include = get_function_include( i_function = EXACT #( mod->sub_name ) )
without_assistant = mod->without_assistant ) ).
WHEN 'LDBA'. " logical databases. Seem to have these two code objects
s_mod_includes = VALUE #( BASE s_mod_includes
( include = mod->sub_name && 'SEL'
without_assistant = mod->without_assistant )
( include = 'SAP' && mod->sub_name
without_assistant = mod->without_assistant ) ).
WHEN 'CLAS'. " complete class
cl_oo_classname_service=>get_all_class_includes( EXPORTING class_name = EXACT #( mod->obj_name )
RECEIVING result = DATA(class_includes)
EXCEPTIONS OTHERS = 0 ).
DELETE class_includes WHERE table_line+30(2) = 'CS' OR table_line+30(2) = 'CP'. " CS = complete source, CP = frameprogram for class pool
s_mod_includes = VALUE #( BASE s_mod_includes
FOR class_incl IN class_includes
( include = class_incl
without_assistant = mod->without_assistant ) ).
WHEN 'CINC'. " class include
s_mod_includes = VALUE #( BASE s_mod_includes
( include = EXACT #( mod->sub_name )
without_assistant = mod->without_assistant ) ).
WHEN 'CPUB' OR 'CPRO' OR 'CPRI'. " class public/protected/private definitions
include = mod->sub_name.
TRANSLATE include USING ' ='.
include+30 = SWITCH #( mod->sub_type WHEN 'CPUB' THEN 'CU'
WHEN 'CPRO' THEN 'CO'
WHEN 'CPRI' THEN 'CI' ).
s_mod_includes = VALUE #( BASE s_mod_includes
( include = include
without_assistant = mod->without_assistant ) ).
WHEN 'FUGR' OR 'PROG'. " complete report or function group
get_all_includes_and_screens( EXPORTING i_type = mod->sub_type
i_name = EXACT #( mod->sub_name )
IMPORTING e_includes = DATA(includes)
e_screens = DATA(screens) ).
s_mod_includes = VALUE #( BASE s_mod_includes
FOR incl IN includes
( include = incl
without_assistant = mod->without_assistant ) ).
s_mod_screens = VALUE #( BASE s_mod_screens
FOR scr IN screens
( program = scr-program
screen = scr-screen
without_assistant = mod->without_assistant ) ).
WHEN 'DYNP'. " screen (dynpro) - the program only searches through the flow logic (code)
s_mod_screens = VALUE #( BASE s_mod_screens
( program = mod->sub_name(40)
screen = mod->sub_name+40(4)
without_assistant = mod->without_assistant ) ).
WHEN OTHERS.
LOG-POINT ID zlog FIELDS mod->obj_type mod->obj_name mod->sub_type mod->sub_name. " unknown subtype of modification
ENDCASE.
ENDLOOP.

" pass result to caller, but also keep our own data, to be used later for filtering
SORT s_mod_includes BY include. " for binary search later
DELETE ADJACENT DUPLICATES FROM s_mod_includes.
SORT s_mod_screens BY program screen. " for binary search later
DELETE ADJACENT DUPLICATES FROM s_mod_screens COMPARING program screen. " there are duplicates here because of the way SMODILOG is used
c_includes = VALUE #( BASE c_includes
FOR incl_ IN s_mod_includes
( incl_-include ) ).
c_screens = VALUE #( BASE c_screens
FOR scr_ IN s_mod_screens
( repname = scr_-program
dynnr = scr_-screen ) ).
ENDMETHOD.

METHOD get_method_include.
DATA: generic_instance TYPE REF TO if_oo_clif_incl_naming
, class_instance TYPE REF TO if_oo_class_incl_naming.
cl_oo_include_naming=>get_instance_by_name( EXPORTING name = i_class
RECEIVING cifref = generic_instance
EXCEPTIONS no_objecttype = 1
internal_error = 2
OTHERS = 3 ).
class_instance ?= generic_instance.
class_instance->get_include_by_mtdname( EXPORTING mtdname = i_method
with_enhancements = abap_true
with_alias_resolution = abap_true
RECEIVING progname = r_include
EXCEPTIONS internal_method_not_existing = 1
OTHERS = 2 ).
ASSERT sy-subrc = 0. " method not found
ENDMETHOD.

METHOD get_function_include.
SELECT SINGLE pname
FROM tfdir
WHERE funcname = @i_function
INTO @r_include.
ASSERT sy-subrc = 0.
ENDMETHOD.

METHOD get_all_includes_and_screens.
IF i_type = 'PROG'.
DATA(frame_program) = i_name.
ELSE.
DATA(function_group) = EXACT rs38l_area( i_name ).
CALL FUNCTION 'FUNCTION_INCLUDE_CONCATENATE'
CHANGING
program = frame_program
complete_area = function_group
EXCEPTIONS
OTHERS = 1.
ASSERT sy-subrc = 0.
ENDIF.

CALL FUNCTION 'RS_GET_ALL_INCLUDES'
EXPORTING
program = frame_program
TABLES
includetab = e_includes
EXCEPTIONS
OTHERS = 0.
DELETE e_includes WHERE table_line CP 'LSVIM*'. "remove reused includes from maintenance views
SORT e_includes.
DELETE ADJACENT DUPLICATES FROM e_includes.

SELECT prog AS program, dnum AS screen
FROM d020s
WHERE prog = @frame_program
INTO CORRESPONDING FIELDS OF TABLE @e_screens.
ENDMETHOD.

METHOD get_screen_flow_logic.
DATA: header TYPE d020s
, fields TYPE STANDARD TABLE OF d021s
, parameters TYPE STANDARD TABLE OF d023s
, BEGIN OF screen
, prog TYPE d020s-prog
, dnum TYPE d020s-dnum
, END OF screen
.
screen = VALUE #( prog = i_screen-program
dnum = i_screen-screen ).
IMPORT DYNPRO header fields r_source_tab parameters ID screen.
ENDMETHOD.

METHOD filter_modifications.
DATA in_modified_section TYPE abap_bool.

IF i_screen IS INITIAL.
READ TABLE s_mod_includes WITH KEY include = i_include REFERENCE INTO DATA(mod_incl) BINARY SEARCH.
CHECK sy-subrc = 0. " do not filter findings of unmodified code
CHECK mod_incl->without_assistant = abap_false. " do not filter findings of code modified without assistant
ELSE.
READ TABLE s_mod_screens WITH KEY program = i_include screen = i_screen REFERENCE INTO DATA(mod_scr) BINARY SEARCH.
CHECK sy-subrc = 0. " do not filter findings of unmodified code
CHECK mod_scr->without_assistant = abap_false. " do not filter findings of code modified without assistant
ENDIF.

DATA(next_finding_index) = 1.
READ TABLE c_findings INDEX 1 REFERENCE INTO DATA(next_finding).
LOOP AT i_source_tab REFERENCE INTO DATA(source).
DATA(source_index) = sy-tabix.
IF source->*(2) = '*{'.
IF in_modified_section = abap_true.
LOG-POINT ID zlog FIELDS i_include i_screen. " modified include with irregular *{ *} pattern - can happen when modified code is copied into a modification
RETURN. " the best way can do is to leave the remaining source code findings unfiltered
ELSE.
in_modified_section = abap_true.
ENDIF.
ELSEIF source->*(2) = '*}'.
IF in_modified_section = abap_false.
LOG-POINT ID zlog FIELDS i_include i_screen. " modified include with irregular *{ *} pattern - can happen when modified code is copied into a modification
RETURN. " the best way can do is to leave the remaining source code findings unfiltered
ELSE.
in_modified_section = abap_false.
ENDIF.
ENDIF.
DO. " loop necessary, because there can be multiple findings for same source line
IF source_index <> next_finding->line.
EXIT.
ENDIF.
IF in_modified_section = abap_true.
ADD 1 TO next_finding_index. " leave current, continue with next
ELSE.
DELETE c_findings INDEX next_finding_index. " delete current, continue with next
ENDIF.
READ TABLE c_findings INDEX next_finding_index REFERENCE INTO next_finding.
IF sy-subrc <> 0.
RETURN.
ENDIF.
ENDDO.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

There are 3 statements “LOG-POINT ID zlog ...” in the local class implementation. My intention is to have a technical information channel for “strange situations”. For this to work, you need to create a checkpoint group zlog in transaction SAAB (or replace zlog with your own, existing checkpoint group).

Alternatively, you could replace these with an ASSERT, or with a CHECK, or a MESSAGE statement(depending on the context in which the report is used in your company, and your preferred error handling style).

Implementation Notes:

  • Of course, instead of modifying the SAP report, you could also copy and adapt it (cloning). You could even do that as a local object ($TMP) in the development system, to avoid possible overhead. In general though, I would prefer modification over cloning, because cloning comes with potential future cost/effort/risk (if SAP improves or bugfixes the standard code in future releases, and the clone code stays behind, uninformed).

  • We have NetWeaver release 7.50, and I also use the new language features. If you have an older release, you should be able to adapt the Code, so it works with older versions of RS_ABAP_SOURCE_SCAN and ABAP.

  • I also modified the sequence of the radio buttons, putting rb_all first, so this (search ABAP and screen flow logic) is the default


Usage Notes:

  • If no package selection is given, enhancements are searched in all packages Z* and Y.

  • For modifications, the program tries to search only the modified sections. However, if the modification assistant is switched off, or if there are irregular *{ patterns (through copying of modified code), the complete text is searched.

  • Disclaimer: As usual, I cannot guarantee that the code is 100% bug-free.


 

Update 19.02.2020: I recently discovered that someone called "sap-russia" copied my code and cloned the SAP standard code to provide a "ready-to-use" Z-program for code search (without asking me). It is available via ABAPgit  or copy&paste here: https://github.com/sap-russia/ZRS_ABAP_SOURCE_SCAN
16 Comments