Friday, 23 June 2023

ABAP CDS View Where used List – A simple utility

Introduction:

CDS doesn’t need any introduction so let’s get to the core of the topic for which I am writing this blog post.

I was investigating on certain CDS annotations and I was missing something called “where used” option for CDS, this is a feature which we ABAPers use almost every day! But when comes to this CDS, no such option I could find. If you are using DDIC view instead of DDLS Entity then probably you can, but that is NOT the right approach and with “define view entity” syntax you wouldn’t get any DDIC view generated, right?

updates: This “where used” feature is available from S4 onwards, which I missed to add before.

So let’s build it, the good point is SAP keeps everything in some table at the end. The utility can search:

1. Any programs which might be using the CDS you have entered as an input

2. Any parent CDS (VDM) which is using the input CDS

SAP ABAP, SAP ABAP CDS, SAP ABAP Career, SAP ABAP Jobs, SAP ABAP Preparation, SAP ABAP Certification

Disclaimer: The coding may have bugs and have ample scopes of improvement, so feel free to get this tuned / tailored, the way you want. 

REPORT zcds_utility.

TYPES:BEGIN OF ty_ddls_w,

        ddls     TYPE ddlname,

        o_type   TYPE trobjtype,

        obj_name TYPE progname,

      END OF ty_ddls_w,

      BEGIN OF ty_funcm,

        funcname TYPE rs38l_fnam,

        pname    TYPE pname,

      END OF ty_funcm,

      ty_ddls_t  TYPE STANDARD TABLE OF ty_ddls_w WITH EMPTY KEY,

      ty_funcm_t TYPE STANDARD TABLE OF ty_funcm WITH EMPTY KEY.

DATA:w_ddls_f TYPE string,

     t_ddls_w TYPE ty_ddls_t,

     t_func   TYPE ty_funcm_t.

SELECTION-SCREEN: BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001.

SELECTION-SCREEN SKIP 1.

SELECTION-SCREEN BEGIN OF LINE.

PARAMETERS:p_rad1 RADIOBUTTON GROUP rb1.

SELECTION-SCREEN COMMENT 5(20) comment1.

PARAMETERS: p_ddls1  TYPE ddlname.

SELECTION-SCREEN END OF LINE.

SELECTION-SCREEN SKIP 1.

SELECTION-SCREEN BEGIN OF LINE.

PARAMETERS:p_rad2 RADIOBUTTON GROUP rb1.

SELECTION-SCREEN COMMENT 5(20) comment2.

PARAMETERS: p_ddls2  TYPE ddlname.

SELECTION-SCREEN END OF LINE.

SELECTION-SCREEN:END OF BLOCK b1.

AT SELECTION-SCREEN OUTPUT.

  comment1 = 'CDS Where Used:'.

  comment2 = 'CDS Components:'.

CLASS lcl_event_handler DEFINITION.

  PUBLIC SECTION.

    TYPES:BEGIN OF lty_source_code,

            src_cd TYPE char1000_idoc,

          END OF lty_source_code.

    DATA:

      lo_alv TYPE REF TO cl_salv_table,

      lt_alv TYPE ty_ddls_t.

    METHODS constructor

      IMPORTING

        i_alv TYPE REF TO cl_salv_table.

    METHODS on_double_click

      FOR EVENT if_salv_events_actions_table~double_click

                OF cl_salv_events_table

      IMPORTING row column.

ENDCLASS.

CLASS lcl_event_handler IMPLEMENTATION.

  METHOD constructor.

    lo_alv = i_alv.

  ENDMETHOD.

  METHOD on_double_click.

    DATA: lt_source_cd TYPE STANDARD TABLE OF lty_source_code,

          l_obj_name   TYPE progname,

          l_type       TYPE trobjtype,

          l_ddls       TYPE ddlname.

    CHECK column = 'OBJ_NAME'.

    l_obj_name = lt_alv[ row ]-obj_name.

    l_type = lt_alv[ row ]-o_type.

    l_ddls = lt_alv[ row ]-ddls.

    CASE l_type.

      WHEN 'DDLS'.

        TRY.

            cl_dd_ddl_handler_factory=>create( )->read(

                  EXPORTING

                    name         = CONV ddlname( to_upper( l_obj_name ) )

                  IMPORTING

                    ddddlsrcv_wa = DATA(ddlsrcv_wa) ).

          CATCH cx_dd_ddl_read INTO DATA(exc).

            cl_demo_output=>display( exc->get_text( ) ).

        ENDTRY.

        IF ddlsrcv_wa-source IS INITIAL.

          cl_demo_output=>display( |Nothing found| ).

          RETURN.

        ENDIF.

        cl_demo_output=>display( replace( val = ddlsrcv_wa-source

                                          sub = |\n|

                                          with = ``

                                          occ = 0 ) ).

      WHEN OTHERS.

*---get the source code (other then DDLS)

        READ REPORT l_obj_name  INTO lt_source_cd.

        IF sy-subrc EQ 0.

          DATA(l_source_code) = REDUCE string( INIT lv_src TYPE string

*---Don't consider ANY commented code

                                        FOR lw_source IN lt_source_cd

                                    INDEX INTO lv_index

                                      WHERE ( table_line+0(1) NE '*' )

                              NEXT lv_src = to_upper( lv_src ) &&

                              space && to_upper( lw_source-src_cd )  && | LINE#: { lv_index }#|   ).

          CLEAR lt_source_cd.

          DATA(l_search_pattern) = `(?:LINE#: (\d)+)(?=#[\w\s]+` && |{  l_ddls  }| && `)`.

          DATA(lv_result) = match(  val = l_source_code

                                               regex = l_search_pattern

                                               case = abap_false    ).

          IF lv_result IS NOT INITIAL.

            DATA(lv_line) = match(

                                     val = lv_result

                                    regex = `(?:LINE#:\s)([0-9]+)`

                                    case = abap_false

                                     ).

            SPLIT lv_line AT ':' INTO DATA(l_part1) DATA(l_part2).

            CONDENSE l_part2 NO-GAPS.

          ELSE.

            MESSAGE |Object: { l_ddls }, has no direct usage| TYPE 'S'.

          ENDIF.

        ENDIF.

        CALL FUNCTION 'RS_TOOL_ACCESS'

          EXPORTING

            operation           = 'SHOW'

            object_name         = l_obj_name

            object_type         = l_type

            position            = COND char10( WHEN l_part2 = 0 THEN space ELSE ( l_part2 + 1 ) )

          EXCEPTIONS

            not_executed        = 1

            invalid_object_type = 2

            OTHERS              = 3.

        IF sy-subrc <> 0.

* Implement suitable error handling here

        ENDIF.

    ENDCASE.

  ENDMETHOD.

ENDCLASS.

START-OF-SELECTION.

  CASE abap_true.

    WHEN p_rad1.

      IF p_ddls1 IS INITIAL.

        MESSAGE 'Enter a CDS View to proceed!' TYPE 'S'.

        LEAVE LIST-PROCESSING.

      ENDIF.

      w_ddls_f = '\TY:' && p_ddls1.

*---Find parent DDLS (if any)

      SELECT * FROM ddls_ris_index

      INTO TABLE @DATA(t_ddls_where_used)

      WHERE used_artifact_fullname = @w_ddls_f.

      IF sy-subrc EQ 0.

        LOOP AT t_ddls_where_used REFERENCE INTO DATA(lo_ddls).

          APPEND INITIAL LINE TO t_ddls_w REFERENCE INTO DATA(lo_ddls_wu).

          SPLIT lo_ddls->used_artifact_fullname AT ':' INTO DATA(w_part1) DATA(w_part2).

          lo_ddls_wu->ddls = w_part2.

          lo_ddls_wu->o_type = 'DDLS'.

          lo_ddls_wu->obj_name = lo_ddls->ddlsrc_name.

        ENDLOOP.

      ENDIF.

*----find all other ABAP Objects where input DDLS been used

      SELECT name,

             include,

             CASE repo~subc

             WHEN '1' THEN 'REPS'

             WHEN 'K' THEN 'CLAS'

             WHEN 'I' THEN 'INCL'

             WHEN 'F' THEN 'FUGR'

             ELSE 'UN'

             END  AS o_type

          FROM wbcrossgt AS wb

          LEFT OUTER JOIN reposrc AS repo

          ON wb~include = repo~progname

      INTO TABLE @DATA(lt_wbcrossgt)

      WHERE otype = 'TY'

      AND name = @p_ddls1

      AND indirect = @space.

      IF sy-subrc EQ 0.

        SORT lt_wbcrossgt BY name include o_type.

*---Check for Function Modules

        LOOP AT lt_wbcrossgt REFERENCE INTO DATA(lo_wbcross) WHERE o_type = 'UN'.

          APPEND INITIAL LINE TO t_func REFERENCE INTO DATA(lo_func).

          lo_func->funcname = lo_wbcross->include.

        ENDLOOP.

        IF t_func IS NOT INITIAL.

          SELECT  funcname,

                  pname

            FROM tfdir INTO TABLE @DATA(t_tfdir)

            FOR ALL ENTRIES IN @t_func

            WHERE funcname = @t_func-funcname.

          IF sy-subrc EQ 0.

            SORT t_tfdir BY funcname.

          ENDIF.

        ENDIF.

        LOOP AT lt_wbcrossgt REFERENCE INTO lo_wbcross.

          APPEND INITIAL LINE TO t_ddls_w REFERENCE INTO lo_ddls_wu.

          IF lo_wbcross->o_type = 'UN' AND line_exists( t_tfdir[ funcname = lo_wbcross->include ] ).

            lo_ddls_wu->ddls = p_ddls1.

            lo_ddls_wu->o_type = 'FUNC'.

            lo_ddls_wu->obj_name = lo_wbcross->include.

          ELSE.

            lo_ddls_wu->ddls = p_ddls1.

            lo_ddls_wu->o_type = lo_wbcross->o_type.

            lo_ddls_wu->obj_name = lo_wbcross->include.

          ENDIF.

        ENDLOOP.

      ENDIF.

    WHEN p_rad2.

      MESSAGE 'This feature, not yet Supported' TYPE 'S'.

      LEAVE LIST-PROCESSING.

  ENDCASE.

  IF t_ddls_w IS NOT INITIAL.

    CALL METHOD cl_salv_table=>factory

      IMPORTING

        r_salv_table = DATA(lo_alv1)

      CHANGING

        t_table      = t_ddls_w.

    IF lo_alv1 IS BOUND.

      lo_alv1->get_functions( )->set_all( abap_true ).

      lo_alv1->get_columns( )->set_optimize( abap_true ).

      DATA(lo_evt_handler) = NEW lcl_event_handler( lo_alv1 ).

      DATA(lo_events) = lo_alv1->get_event( ).

      SET HANDLER lo_evt_handler->on_double_click FOR lo_events.

      lo_evt_handler->lt_alv = t_ddls_w.

      lo_alv1->display( ).

    ENDIF.

  ELSE.

    MESSAGE 'No records found!' TYPE 'S'.

    LEAVE LIST-PROCESSING.

  ENDIF.

Conclusion:

This utility perhaps can save some time and get you to the right piece of program / DDLS where the CDS being used. Double clicking will open the exact line of code or DDLS which you were looking for.

No comments:

Post a Comment