This is a test message to test the length of the message box.
Login
ABAP in Practice String Processing
Created by Software-Heroes

ABAP in Practice - String Processing

499

In this practical example we look at the string processing to determine the CDS names in CamelCase and how you can implement this with ABAP.



In the following sections, we will discuss the simple and efficient processing of strings. We will use Modern ABAP to get the names of the Core Data Services.

 

Introduction

In some situations, we lack the right interfaces to get direct information. For example, we had not found a class or function module to get the CamelCase names of the Core Data Services. These are stored in tables in the classic way and the information is converted to uppercase. Unfortunately, this means that they are not so easy for us to use.

In this blog, we break down the source code of the Core Data Services into processable components and determine the name with upper and lower case as defined in the source code.

 

Preparation

Before you can start with the actual task, we need a basis that you can develop further. To do this, we create an executable class in the system. We are working outside of ABAP Cloud because we want to access a table that is not released. To do this, we create three types that we need for processing: a range for selection, a structure for mapping and a table type for returning the data.

TYPES tt_r_name TYPE RANGE OF ddddlsrc-ddlname.

TYPES: BEGIN OF ts_mapping,
         ddlname  TYPE ddddlsrc-ddlname,
         cds_name TYPE string,
       END OF ts_mapping.
TYPES tt_mapping TYPE SORTED TABLE OF ts_mapping WITH UNIQUE KEY ddlname.

 

The next step is to create a method that will perform the determination. We want to make it reusable and pass the CDS views for which we want to determine names so that we don't have to read all the objects.

METHODS extract_cds_name
  IMPORTING it_r_name        TYPE tt_r_name
  RETURNING VALUE(rt_result) TYPE tt_mapping.

 

We now fill the method with some data in order to quickly obtain a result from the logic. When filling it, we choose an inline declaration and fill the range table. Since SIGN and OPTION only have to be set once, we leave them outside the data records and save ourselves the further enumeration.

DATA(lt_r_names) = VALUE tt_r_name( sign   = 'I'
                                    option = 'EQ'
                                    ( low = 'I_COMPANYCODE' )
                                    ( low = '/1BS/SADL_CDS_EXP' )
                                    ( low = 'A_CHANGEMASTEROBJECTTYPETEXT' )
                                    ( low = 'C_BUDGETPERIODCHILDGROUP' )
                                    ( low = 'I_ABOPCHECKINGRULE' )
                                    ( low = 'I_JOBSTATUS' )
                                    ( low = 'SADL_CDS_RS_SO_ROOT_W_DB_HINT' )
                                    ( low = 'SADL_GW_V_AUNIT_V2_VH_WRONG_AN' )
                                    ( low = 'SEPM_SDDL_EXTENSIONS' ) ).

 

Finally, we read the selected Core Data Services and the stored source code from the DDDDLSRC table.

SELECT FROM ddddlsrc
  FIELDS ddlname, source
  WHERE ddlname IN @it_r_name
  INTO TABLE @DATA(lt_views).

 

This allows you to use the following class for the implementation. If you want to go even further, you can implement it using Test Driven Development and create the test class before implementation.

CLASS zcl_bs_demo_cds_names DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.

  PRIVATE SECTION.
    TYPES tt_r_name TYPE RANGE OF ddddlsrc-ddlname.

    TYPES: BEGIN OF ts_mapping,
             ddlname  TYPE ddddlsrc-ddlname,
             cds_name TYPE string,
           END OF ts_mapping.
    TYPES tt_mapping TYPE SORTED TABLE OF ts_mapping WITH UNIQUE KEY ddlname.

    METHODS extract_cds_name
      IMPORTING it_r_name        TYPE tt_r_name
      RETURNING VALUE(rt_result) TYPE tt_mapping.
ENDCLASS.


CLASS zcl_bs_demo_cds_names IMPLEMENTATION.
  METHOD if_oo_adt_classrun~main.
    DATA(lt_r_names) = VALUE tt_r_name( sign   = 'I'
                                        option = 'EQ'
                                        ( low = 'I_COMPANYCODE' )
                                        ( low = '/1BS/SADL_CDS_EXP' )
                                        ( low = 'A_CHANGEMASTEROBJECTTYPETEXT' )
                                        ( low = 'C_BUDGETPERIODCHILDGROUP' )
                                        ( low = 'I_ABOPCHECKINGRULE' )
                                        ( low = 'I_JOBSTATUS' )
                                        ( low = 'SADL_CDS_RS_SO_ROOT_W_DB_HINT' )
                                        ( low = 'SADL_GW_V_AUNIT_V2_VH_WRONG_AN' )
                                        ( low = 'SEPM_SDDL_EXTENSIONS' ) ).

    out->write( extract_cds_name( lt_r_names ) ).
  ENDMETHOD.


  METHOD extract_cds_name.
    SELECT FROM ddddlsrc
      FIELDS ddlname, source
      WHERE ddlname IN @it_r_name
      INTO TABLE @DATA(lt_views).

    " Implement here
  ENDMETHOD.
ENDCLASS.

 

Task

The task now is to fill the return table and derive the real CamelCase names from the code. In the internal table LT_VIEWS you will find the name of the view and the coding:

 

Hint: In the next section we will go into the solution, if you want to do the task on your own, you should pause here.

 

Solution

In this section we will look at the different steps for setting up the table.

 

Loop

In the first step we want to process the different views, for this we create a loop and work with a reference. You can find more information about how to work with it in an older article from us. In the second step, we add a new row to our results table and fill in the view name. We assign the generated row to a new reference in order to add the name later.

LOOP AT lt_views REFERENCE INTO DATA(lr_view).
  INSERT VALUE #( ddlname = lr_view->ddlname ) INTO TABLE rt_result REFERENCE INTO DATA(lr_result).

ENDLOOP.

 

We take the name of the view directly, since it is a sorted table and we have to fill in the key, otherwise we will get an error. Since we have no exclusion criteria that exclude individual rows, but we include everything in the result, this step also makes sense.

 

Division

Next, we want to split the individual statements of the source. To do this, we use a classic SPLIT command and get a string table with the individual elements from the source code.

SPLIT lr_view->source AT ` ` INTO TABLE DATA(lt_split).

 

Search

We can now start processing the individual statements. The table would currently look like this after the split:

 

Now we just need to search the table with elements and filter for the CDS view we are looking for. Once we have found the view, we accept it and exit the routine. To make the values comparable, we convert the CamelCase name using TO_UPPER and compare the result. Since we are working with a reference here, we need to use "->*" for access.

LOOP AT lt_split REFERENCE INTO DATA(lr_split).
  IF to_upper( lr_split->* ) = lr_view->ddlname.
    lr_result->cds_name = lr_split->*.
    EXIT.
  ENDIF.
ENDLOOP.

 

Finally, we leave the logic with EXIT and go to the next Core Data Service.

 

Performance

To improve performance a little, we don't always have to start with the first line, especially if there are a lot of annotations at the beginning. Basically, we can search for the keyword DEFINE, as this introduces and defines the view. To do this, we search for "define" in the table, but since this has no fields, we use access via TABLE_LINE. We use the LINE_INDEX function to return the current line. We then add the FROM addition to the loop to start from this position. 

DATA(ld_start) = line_index( lt_split[ table_line = `define` ] ).

LOOP AT lt_split REFERENCE INTO DATA(lr_split) FROM ld_start.
ENDLOOP.

 

If LINE_INDEX does not find a statement, the position is set to 0 and can then continue to be used for the LOOP to start with the first position.

 

Line breaks

In some cases it can happen that "define" cannot be found. If we take a closer look at these cases, we will see that the statement was not separated cleanly.

 

The backslash & "n" is a line break, which is also stored in the table. In such a case, we should replace all line breaks in the source code before the SPLIT so that we get a clean result. To do this, we use the Replace function and replace all occurrences of the line break with a space.

lr_view->source = replace( val  = lr_view->source
                           sub  = cl_abap_char_utilities=>cr_lf
                           with = ` `
                           occ  = 0 ).

 

If we now look at the table after the SPLIT, the statements are cleanly separated so that we can then search for the beginning.

 

Names

In our examples there is also a case that returns an empty Core Data Service. If we look at the mapping in detail here, we will see that the name in the source code is not the same as the name of the object.

 

In the simplest case, we then take the name of the Core Data Service we are looking for as the result. There is no CamelCase name for the object here.

IF lr_result->cds_name IS INITIAL.
  lr_result->cds_name = lr_view->ddlname.
ENDIF.

 

Complete example

In this section you will find the complete class with the complete implementation so that you can recreate the example. In principle, you can also remove the restriction of the individual views in order to get all views as a result. However, this will significantly increase the runtime. You can find the resource in our GitHub repository for extracting the CDS information.

 

Conclusion

In this practical example, we wanted to give you a better idea of how to work with strings and how you can use the different techniques to get the result. If you have an interesting solution to the problem, please post it in the comments.


Included topics:
QuickABAP in PracticeStringsCDS Names
Comments (0)



And further ...

Are you satisfied with the content of the article? We post new content in the ABAP area every Friday and irregularly in all other areas. Take a look at our tools and apps, we provide them free of charge.


ABAP in Practice - Test Driven Development

Category - ABAP

How does TDD actually work in practice and are there simple examples for learning in ABAP? In this exercise we will look at the practical part.

09/24/2024

ABAP in Practice - Merge data sets

Category - ABAP

How do we merge two different data sets in ABAP, especially with regard to Modern ABAP? A practical task for this topic.

09/17/2024

ABAP in Practice - Modern ABAP

Category - ABAP

In this small task we look at existing classic ABAP source code and try to optimize it according to Modern ABAP.

08/27/2024

ABAP Quick - Performance Data Filtering

Category - ABAP

Which statement do you use in ABAP to filter internal tables and is it performant? Read more in this article.

08/13/2024

ABAP in Practice - Type Conversion

Category - ABAP

How would you perform this type conversion in ABAP? A practical example and a suggested solution.

07/16/2024