Dynamic Programming Techniques

Content
  • Field symbols 
    • Modifying internal table records
    • Appending to internal table
    • Reading the internal table
    • Read components of a structure using ABAP RTTS
    • Declare generic field symbols
    • Reading internal table using generic field symbol
    • Change rows of the dynamic table 
  • Data reference 
    • Assigning existing data object to data reference
    • Working with structures
    • Working with internal tables
    • Dynamically create data objects
  • ABAP RTTS
    • Read components of a structure using ABAP RTTS
    • Check if a column exists in a table 
  • Examples
    •  Get a type of a data reference 
  • itpfed (materials)

Additional information

  • https://wiki.scn.sap.com/wiki/pages/viewpage.action?pageId=42965

Field symbols 

Field symbol is a placeholder for data object, which points to the value present at the memory address of a data object. It does not reserve any physical memory space when we declare them. It only points to a data object at run time. Field symbols are of two types:
  • Typed Field Symbol
  • Generic Field Symbol

Modifying internal table records

DATA: lt_mara TYPE STANDARD TABLE OF mara.
FIELD-SYMBOLS: <fs_mara> TYPE mara.
SELECT * FROM mara INTO TABLE lt_mara UP TO 10 ROWS.
LOOP AT lt_mara ASSIGNING <fs_mara>.
<fs_mara>-matkl = 'DEMO'.
ENDLOOP.

Appending to internal table

DATA: lt_mara TYPE STANDARD TABLE OF mara.
FIELD-SYMBOLS: <fs_mara> TYPE mara.

APPEND INITIAL LINE TO lt_mara ASSIGNING <fs_mara>.
IF <fs_mara> IS ASSIGNED.
<fs_mara>-matnr = 'MAT1'.
<fs_mara>-matkl = '001'.
UNASSIGN <fs_mara>.
ENDIF.

APPEND INITIAL LINE TO lt_mara ASSIGNING <fs_mara>.
IF <fs_mara> IS ASSIGNED.
<fs_mara>-matnr = 'MAT2'.
<fs_mara>-matkl = '001'.
UNASSIGN <fs_mara>.
ENDIF.

Reading the internal table

READ TABLE lt_mara ASSIGNING <fs_mara> WITH KEY matnr = 'MAT1'.

Declare generic field symbols

FIELD-SYMBOLS: <fs_str> TYPE ANY.
FIELD-SYMBOLS: <fs_data> TYPE ANY.

DATA: lw_mara TYPE mara.

ASSIGN lw_mara TO <fs_str>.
IF <fs_str> IS ASSIGNED.
  ASSIGN COMPONENT 'MATNR' OF STRUCTURE <fs_str> TO <fs_data>.
  IF <fs_data> IS ASSIGNED.
    <fs_data> = 'MAT001'.
    UNASSIGN <fs_data>.
  ENDIF.
  UNASSIGN <fs_str>.
ENDIF.

  • After assigning lw_mara to <fs_str>, we cannot directly use the ‘-‘ operator on field symbol to access the fields of MARA structure i.e. <fs_str>-matnr would produce syntax error. This is because the field symbol type is declared only at runtime not at compile time.
  • So to access the matnr field with field symbol, first we need to assign that field component to a different field symbol and then use the new field symbol to update the matnr field as show in above code snippet.
  • After execution of above code snippet, the value of lw_mara-matnr would be MAT001.

FIELD-SYMBOLS: <fs_tab> TYPE ANY TABLE.
FIELD-SYMBOLS: <fs_str> TYPE any.
FIELD-SYMBOLS: <fs_data> TYPE any.
DATA: lt_mara TYPE STANDARD TABLE OF mara.
DATA: lw_mara TYPE mara.

ASSIGN lt_mara TO <fs_tab>.
SELECT * FROM mara INTO TABLE lt_mara UP TO 10 ROWS.

LOOP AT <fs_tab> ASSIGNING <fs_str>.
  IF <fs_str> IS ASSIGNED.
    ASSIGN COMPONENT 'MATKL' OF STRUCTURE <fs_str> TO <fs_data>.
    IF <fs_data> IS ASSIGNED.
      IF <fs_data> EQ '01'.
*********** Do some processing *********
      ENDIF.
      UNASSIGN <fs_data>.
    ENDIF.
  ENDIF.
ENDLOOP.

Reading internal table using generic field symbol

FIELD-SYMBOLS: <fs_tab> TYPE ANY TABLE.
FIELD-SYMBOLS: <fs_str> TYPE any.
DATA: lt_mara TYPE STANDARD TABLE OF mara.

ASSIGN lt_mara TO <fs_tab>.
SELECT * FROM mara INTO TABLE lt_mara UP TO 10 ROWS.

READ TABLE <fs_tab> ASSIGNING <fs_str> WITH KEY ('MATNR') = 'MAT001'.

Since <fs_tab> is a generic field symbol, its type will be known only at runtime, hence we cannot directly write the fields of MARA structure after WITH KEY, instead we have to write the field name within parenthesis as shown above.
In ABAP, this parenthesis indicates the compiler that the value of the operand will be decided at runtime, hence we don’t get any compilation error.

Change rows of the dynamic table 

  LOOP AT ct_data ASSIGNING <fs_data>.
    ASSIGN COMPONENT 'KEY' OF STRUCTURE <fs_data> TO <fs_feld_fu_key>.
    IF <fs_feld_fu_key> IS ASSIGNED AND 
          <fs_feld_fu_key> = <fs_mci_items>-parent_key.
      ASSIGN COMPONENT 'ZZ_CHANGED_FIELD' OF STRUCTURE <fs_data>  TO <fs_feld_walzzyk>.
      IF <fs_feld_walzzyk> IS ASSIGNED.
        <fs_feld_walzzyk> = <fs_mci_items>-zz_changed_field.
        " MODIFY TABLE ct_data FROM <fs_data>.
      ENDIF.
    ENDIF.
  ENDLOOP.

Data reference 

According to SAP documentation, Data references can point to any data objects or to their parts (components, rows of internal tables, or sections specified by offsets and lengths).

So data references are nothing but pointers. It stores the memory address of any data object. But to access the actual data object which data reference is pointing to, we first need to deference it using dereferencing operator ->*.

Declaration 

There can be two types of data references:
  • Typed Data Reference
  • Generic Data Reference
Typed
DATA lr_num TYPE REF TO i.
CREATE DATA lr_num.

With ABAP 7.40, instead of CREATE DATA, the NEW operator can also be used to create an anonymous data object and assigns its reference to a data reference variable.

DATA lr_num TYPE REF TO i.
lr_num = NEW #( ).

Generic
DATA: lr_num TYPE REF TO data.
CREATE DATA lr_num TYPE i.

So in the case of generic data reference, it can only be dereferenced using a field symbol, and this field symbol can be used at any operand position to manipulate the value of data object as shown below:
DATA: lr_num TYPE REF TO data.
FIELD-SYMBOLS: <num> TYPE any.

CREATE DATA lr_num TYPE i.
ASSIGN lr_num->* TO <num>.

<num> = 3.

Assigning existing data object to data reference

DATA: lv_num TYPE i VALUE 2.
DATA: lr_num TYPE REF TO i.

GET REFERENCE OF lv_num INTO lr_num.
lr_num->* = 4.
WRITE: / lv_num.

With ABAP 7.40, instead of GET REFERENCE, the REF operator also can be used to assign the reference of an existing data objects to a data reference.

Working with structures

Typed
DATA: lr_mara TYPE REF TO mara.
CREATE DATA lr_mara.
lr_mara->matnr = '1111'.
lr_mara->matkl = '03'.

Generic
DATA: lr_str TYPE REF TO data.
FIELD-SYMBOLS: <str> TYPE any.
FIELD-SYMBOLS: <data> TYPE any.
CREATE DATA lr_str TYPE mara.

ASSIGN lr_str->* TO <str>.
ASSIGN COMPONENT 'MATNR' OF STRUCTURE <str> TO <data>.
<data> = '112'.

Working with internal tables

DATA: lr_mara TYPE REF TO mara.
DATA: lt_mara TYPE TABLE OF mara.

SELECT * FROM mara INTO TABLE lt_mara UP TO 10 ROWS.

LOOP AT lt_mara REFERENCE INTO lr_mara.
  WRITE: / lr_mara->matnr.
ENDLOOP.

Dynamically create data objects

PARAMETERS: p_tname TYPE tabname.
DATA: lr_tab TYPE REF TO data.
FIELD-SYMBOLS: <tab> TYPE ANY TABLE.

CREATE DATA lr_tab TYPE TABLE OF (p_tname).
ASSIGN lr_tab->* TO <tab>.
IF sy-subrc EQ 0.
  SELECT * FROM (p_tname) INTO TABLE <tab> UP TO 10 ROWS.
  cl_demo_output=>display( <tab> ).
ENDIF.

ABAP RTTS

ABAP Runtime Type Services (RTTS) consists of two components:
  • Runtime Type Identification (RTTI) – Provides the methods to get the type definition of data objects at runtime.
  • Runtime Type Creation (RTTC) – Provides the methods to create the data objects at runtime with any type definition.

Read components of a structure using ABAP RTTS

DATA: lo_sdescr TYPE REF TO cl_abap_structdescr,
            ls_component  TYPE abap_compdescr.

lo_sdescr ?= cl_abap_typedescr=>describe_by_name( 'LFA1' ).

LOOP AT lo_sdescr->components INTO ls_component.
WRITE: / ls_component-name.
ENDLOOP.

Check if a column exists in a table

    datalx_table     type ref to cl_abap_tabledescr,
          lx_line      type ref to cl_abap_datadescr,
          lx_structure type ref to cl_abap_structdescr.

    lx_table ?= cl_abap_typedescr=>describe_by_datalt_mata ).
    lx_line lx_table->get_table_line_type).

    if lx_line->kind cl_abap_typedescr=>kind_struct.
      lx_structure ?= lx_line.

      if line_existslx_structure->components[ name 'MATNR' ).
        message 'Good!' type 'I'.
      endif.
    endif.

Examples


Get a result for a smartform. 

X-Object with import parameter type any.
inside X-Object create a local object with the type (parametrized)

Example:
assign component 'S_KNA1_RE' of structure is_input to field-symbol(<ls_customer>).
assign component 'S_KOMLFK' of structure is_input to field-symbol(<ls_document>).
    if <ls_customer> is assigned and <ls_document> is assigned.
      assign component 'STCD1' of structure <ls_customer> to <lv_tax_number_1> .
      assign component 'STCEGD' of structure <ls_document> to <lv_vat_number> .
      assign component 'LAND1TX' of structure <ls_document> to <lv_tax_country> .
      if <lv_tax_number_1> is assigned and 
         <lv_vat_number> is assigned and 
         <lv_tax_country> is assigned.
      endif.
    endif.
call the general method to get a VAT number 

itpfed (materials)

Part #1 Field symbol usage 

   DATA: dref    TYPE REF TO data,
         tabname TYPE tabname.

   FIELD-SYMBOLS: <row>       TYPE any,
                  <component> TYPE any.

   START-OF-SELECTION.
     WRITE: / 'EBAN', / 'MARA', / 'VBAK'.

   AT LINE-SELECTION.
     READ CURRENT LINE LINE VALUE INTO tabname.

 * dynamically create appropriate Data Structure
     CREATE DATA dref TYPE (tabname).
     ASSIGN dref->* TO <row>.

 * fetch the data
     SELECT *
     FROM (tabname) UP TO 2 ROWS
     INTO <row>.

 * display the result
       NEW-LINE.
       DO.
         ASSIGN COMPONENT sy-index OF STRUCTURE <row> TO <component>.
         IF sy-subrc <> 0.
           EXIT. " no more components
         ENDIF.
         WRITE: <component>.
       ENDDO.
     ENDSELECT.

Part #2 Table creation

DATA: tabname TYPE tabname,
         dref    TYPE REF TO data,
         alv    TYPE REF TO cl_gui_alv_grid.

   FIELD-SYMBOLS: <itab> TYPE ANY TABLE.

   START-OF-SELECTION.
     WRITE: / 'EBAN', / 'MARA', / 'VBAK'.

   AT LINE-SELECTION.
     READ CURRENT LINE LINE VALUE INTO tabname.

 * dynamically create appropriate internal table
     CREATE DATA dref TYPE TABLE OF (tabname).
     ASSIGN dref->* TO <itab>.

 * fetch the data
     SELECT *
     FROM (tabname) up to 100 rows
     INTO TABLE <itab>.

 * display the result
     CREATE OBJECT alv
       EXPORTING
         i_parent = cl_gui_container=>screen0.

     CALL METHOD alv->set_table_for_first_display
       EXPORTING
         i_structure_name = tabname
       CHANGING
         it_outtab        = <itab>.

Part #3 Token specification 

* dynamic sort name = 'SSN'. SORT itab BY (name). 
* static sort SORT itab BY SSN.
ABAP offers five forms of dynamic token specification, each of which is intended for a specific part of a program statement:
  1. Dynamic field specification contains the name of a field. The field for an ASSIGN statement, which should be assigned to a field symbol, can be specified dynamically. (We have seen examples of this in earlier blogs of this series)
  2. Dynamic type specification contains the name of a type. The type for CREATE DATA can bespecified dynamically. (We have seen examples of this in earlier blogs of this series)
  3. Dynamic component specification contains the name of a component of a structure, such as the sort order for an internal table. (this was our example at the start of this blog)
  4. Dynamic clause specification contains a whole part of a statement. For example, all clauses of the Open SQL statement SELECT can be specified dynamically. In this case, the variable must be an internal table of character fields. (you will se examples of this shortly)
  5. Dynamic subroutine specification contains the name of a subroutine. Methods, functions, forms, programs, and transactions can be called dynamically.
CONCATENATE 'cityfrom = ''' depart '''' INTO source_line.
APPEND source_line TO where_tab.
APPEND operator TO where_tab.
CONCATENATE 'cityto = ''' arrive '''' INTO source_line.
APPEND source_line TO where_tab.
SELECT carrid connid cityfrom cityto
FROM spfli
INTO (carrid, connid, depart, arrive)
WHERE (where_tab).

Part #4 RTTI

The class CL_ABAP_TYPEDESCR provides four methods to derive a description object for a type:
  • DESCRIBE_BY_NAME: This method takes a name of a type as an input parameter and returns an object reference to the corresponding description object.
  • DESCRIBE_BY_DATA: This method takes data as input and returns an object reference to the description object of the data type.
  • DESCRIBE_BY_DATA_REF: This method takes a data reference as input and returns an object reference to the description object of the data type for the data pointed to by the reference.
  • DESCRIBE_BY_OBJECT_REF: This method takes an object reference as input and returns an object reference to the description object of the object type for the object pointed to by the reference.

PROGRAM rtti_typecheck_example.
 TYPE-POOLS abap.
 *----------------------------------------------------------------------*
 *       CLASS lcl_typecheck DEFINITION
 *----------------------------------------------------------------------*
 *
 *----------------------------------------------------------------------*
 CLASS lcl_typecheck DEFINITION.
   PUBLIC SECTION.
     METHODS:
     constructor
     IMPORTING p_type_name TYPE string
     EXCEPTIONS type_not_found,
       check
     IMPORTING p_x TYPE any
     RETURNING value(p_flag) TYPE abap_bool.
   PRIVATE SECTION.
     DATA:
     rtti_ref TYPE REF TO cl_abap_typedescr.
 ENDCLASS.                    "lcl_typecheck DEFINITION
 *----------------------------------------------------------------------*
 *       CLASS lcl_typecheck IMPLEMENTATION
 *----------------------------------------------------------------------*
 *
 *----------------------------------------------------------------------*
 CLASS lcl_typecheck IMPLEMENTATION.
   METHOD constructor.
     CALL METHOD cl_abap_typedescr=>describe_by_name
       EXPORTING
         p_name         = p_type_name
       RECEIVING
         p_descr_ref    = rtti_ref
       EXCEPTIONS
         type_not_found = 4.
     IF sy-subrc <> 0.
       RAISE type_not_found.
     ENDIF.
   ENDMETHOD.                    "constructor
   METHOD check.
     DATA check_ref TYPE REF TO cl_abap_typedescr.
     CALL METHOD cl_abap_typedescr=>describe_by_data
       EXPORTING
         p_data      = p_x
       RECEIVING
         p_descr_ref = check_ref.
     IF check_ref = rtti_ref.
       p_flag = abap_true.
     ELSE.
       p_flag = abap_false.
     ENDIF.
   ENDMETHOD.                    "check
 ENDCLASS.                    "lcl_typecheck IMPLEMENTATION
 TYPES:
 my_special_type TYPE HASHED TABLE OF i
     WITH UNIQUE KEY table_line.
 *----------------------------------------------------------------------*
 *       CLASS lcl_app DEFINITION
 *----------------------------------------------------------------------*
 *
 *----------------------------------------------------------------------*
 CLASS lcl_app DEFINITION.
   PUBLIC SECTION.
     CLASS-METHODS except_only_my_special_type
     IMPORTING p_x TYPE any.
 ENDCLASS.                    "lcl_app DEFINITION
 *----------------------------------------------------------------------*
 *       CLASS lcl_app IMPLEMENTATION
 *----------------------------------------------------------------------*
 *
 *----------------------------------------------------------------------*
 CLASS lcl_app IMPLEMENTATION.
   METHOD except_only_my_special_type.
     DATA my_type TYPE REF TO lcl_typecheck.
     CREATE OBJECT my_type
       EXPORTING
         p_type_name = 'MY_SPECIAL_TYPE'.
     IF my_type->check( p_x ) = abap_true.
       WRITE / 'Typecheck OK'.
     ELSE.
       WRITE / 'Typecheck ERROR'.
     ENDIF.
   ENDMETHOD.                    "except_only_my_special_type
 ENDCLASS.                    "lcl_app IMPLEMENTATION
 DATA:
 my_special_data TYPE my_special_type,
 some_other_data TYPE HASHED TABLE OF i
    WITH UNIQUE KEY table_line.

 START-OF-SELECTION.
   CALL METHOD lcl_app=>except_only_my_special_type
     EXPORTING
       p_x = my_special_data.
   CALL METHOD lcl_app=>except_only_my_special_type
     EXPORTING
       p_x = some_other_data.

Part #5 Generate code

PROGRAM generate_program_example.
TYPES:
  source_line(72) TYPE c.

DATA:
  src TYPE TABLE OF source_line,
  msg(120) TYPE c,
  line(10) TYPE c,
  word(10) TYPE c,
  off(3) TYPE c.

CONSTANTS:
  prg_name(30) VALUE 'ZDYNGENPGM1'.

APPEND 'PROGRAM ZDYNGENPGM1.' TO src.
APPEND 'WRITE / ''Hello, I am dynamically created!''.' TO src.

INSERT REPORT prg_name FROM src.

GENERATE REPORT prg_name
  MESSAGE msg LINE line WORD word OFFSET off.

IF sy-subrc <> 0.
   WRITE: / 'Error during generation in line', line,
        / msg, / 'Word:', word, 'at offset', off.
ELSE.
   SUBMIT (prg_name) AND RETURN.
ENDIF.

 Get a type of a data reference

  DATA: lx_type_desc TYPE REF TO cl_abap_typedescr.
    cl_abap_typedescr=>describe_by_data_ref(
      EXPORTING
        p_data_ref           = gz_params
      RECEIVING
        p_descr_ref          = lx_type_desc
      EXCEPTIONS
        reference_is_initial = 1
        OTHERS               = 2 ).
    IF sy-subrc <> 0.

      cl_abap_objectdescr=>describe_by_object_ref(
        EXPORTING
          p_object_ref         = gx_object
        RECEIVING
          p_descr_ref          = lx_type_desc
        EXCEPTIONS
          reference_is_initial = 1
          OTHERS               = 2 ).
      IF sy-subrc <> 0.
 
        RAISE EXCEPTION TYPE XXXXX
      ENDIF.
    ENDIF.

    rv_type = lx_type_desc->get_relative_name( ).

 


Comments