, Cross references made visible, Acorel
Gratis demo
, Cross references made visible, Acorel

Cross references made visible

Olaf Pohlmann

Suppose you as a developer need to change an object in an environment with a lot of existing developments and you want to assess the possible impact. What to do? One way is to check how this object is interconnected with it surrounding objects. Of course this can be done manually but wouldn’t it be nice if this kind of information was gathered automatically and presented in a graphical way? Like this for instance:

, Cross references made visible, Acorel

The object under investigation is a class called ZCLASS1. From this picture is easy to see which objects depend on this class (in the upper left section) and which other objects are called (lower right section). So this gives you an idea of the impact when changing ZCLASS1.

So what information is needed to create such a graphic. Well in fact, not much. A simple list of connected objects is sufficient:

zprog1 -> zfunc1
zfunc1 -> zclass1
zfunc3 -> zclass1

Several tools exists which can create graphs. Graphviz is one, I have used Mathematica in this blog. It can almost directly construct a so-called directed graph based on such a list.

And now the other part, how to gather information on cross references? For this I want to discuss two API’s, one for creating a Where Used list programmatically and the other for investigating external calls.

API for creating a Where Used list

For this function RS_EU_CROSSREF can be used. A simple call to get the Where Used list for class ZCLASS1 might look like this:

data(lt_findstrings) = value rinfoobj( ( object = 'ZCLASS1' ) ).
data(lt_scope_object_cls) = value stringtab( ( `PROG` ) ( `FUNC` ) ( `CLAS` ) ( `METH` ) ).
data(lt_founds) = value sci_findlst( ).

call function 'RS_EU_CROSSREF'
  exporting
    i_find_obj_cls     = 'CLAS'     "Class of objects to be searched
    i_answer           = 'N'        "Answer in the pop-up "Include components of the type"
    no_dialog          = abap_true
  tables
    i_findstrings      = lt_findstrings       "Objects to be searched
    i_scope_object_cls = lt_scope_object_cls  "Used in
    o_founds           = lt_founds            "Found usages
  exceptions
    others             = 0.

Here I am limiting the search to include only programs, functions and classes by setting the scope for the object class. Possible object classes can be found in table EUOBJ. The I_ANSWER and NO_DIALOG options suppress any user interaction.

And now for the result of the call, the found usages. Assume ZCLASS1 is used in 1 program, 1 function and 1 class then the result is like:

OBJECT   ENCL_OBJEC   OBJECT_CLS
ZPROG1   P
LZFUGR1U01   P
ZCLASS2=======================CM001   ZCLASS2   OM

 

Some explanation:

  1. Object class P is used for programs. Here the internal code is used, see table EUOBJ for the mapping with object class.
  2. Functions are also seen as programs and the function include is returned, not the function name. The function name however can easily be resolved by function FUNCTION_INCLUDE_INFO.
  3. Classes can be identified by object class OM in which case ENCL_OBJEC contains the class name.

API for checking external calls

Class CL_ABAP_COMPILER can analyze a whole bunch of things, one of which is external calls. The following example show the external calls from program ZPROG1.

cl_abap_compiler=>create( 'ZPROG1' )->get_all_refs(
  exporting p_local    = abap_true
            p_types    = value #( sign = 'I' option = 'EQ'
                                  ( low = cl_abap_compiler=>tag_program )
                                  ( low = cl_abap_compiler=>tag_function )
                                  ( low = cl_abap_compiler=>tag_type ) )
            p_grades   = value #( sign = 'I' option = 'EQ'
                                  ( low = cl_abap_compiler=>grade_direct ) )
            p_extended = abap_true 
  importing p_result   = data(lt_result) ).

Again the result is limited to include only programs, functions and global types (this includes classes as well) by specifying P_TYPES. Grade “Direct” is specified to exclude variable definitions and calls to ZPROG1 itself. The last import parameter P_EXTENDED fills the SYMBOL-field in the result table with extra details.

The result is something like this:

FULL_NAME   SYMBOL   TAG
\FU:ZFUNC1   ->{O:31*\CLASS=CL_ABAP_COMP_SYMBOL}   FU
\PR:ZPROG2   ->{O:27*\CLASS=CL_ABAP_COMP_SYMBOL}   PR
\TY:T001   ->{O:32*\CLASS=CL_ABAP_COMP_DDIC_DBTAB}   TY
\TY:ZCLASS1   ->{O:29*\CLASS=CL_ABAP_COMP_CLASS}   TY

 

TAG indicates the type of object that is found. SYMBOL indicates in more detail the type of object. Especially when classes don’t follow the regular naming convention this comes in handy to tell them apart from DDIC types, T001 in the above example, since both are tagged with TY. In fact classes and global types share the same namespace. So in my example I cannot create a table named ZCLASS1 as well.

Wrapping it up

Using both API’s it is relatively easy to build a small program to extract all cross references of an object and export the result to a file. ABAP is good at this task but not the best tool for displaying the result in a graphical way. It much easier to use dedicated graph tools for this.

And finally

Another example which does not need any coding at all. Does the following look familiar?
, Cross references made visible, Acorel

This is the alternative representation. Created with almost no effort because the table already contains a list of connected objects. Nice isn’t it?

, Cross references made visible, Acorel

, Cross references made visible, Acorel

Olaf Pohlmann