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.
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
data: lx_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_data( lt_mata ).
lx_line = lx_table->get_table_line_type( ).
if lx_line->kind = cl_abap_typedescr=>kind_struct.
lx_structure ?= lx_line.
if line_exists( lx_structure->components[ name = 'MATNR' ] ).
message 'Good!' type 'I'.
endif.
endif.
lx_line type ref to cl_abap_datadescr,
lx_structure type ref to cl_abap_structdescr.
lx_table ?= cl_abap_typedescr=>describe_by_data( lt_mata ).
lx_line = lx_table->get_table_line_type( ).
if lx_line->kind = cl_abap_typedescr=>kind_struct.
lx_structure ?= lx_line.
if line_exists( lx_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
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.
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:
- 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)
- 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)
- 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)
- 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)
- Dynamic subroutine specification contains the name of a subroutine. Methods, functions, forms, programs, and transactions can be called dynamically.
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
Post a Comment