Files
abap-cheat-sheets/src/zcl_demo_abap_dynamic_prog.clas.abap
danrega 270edb9962 Update
2024-12-20 16:12:45 +01:00

4817 lines
196 KiB
ABAP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"! <p class="shorttext"><strong>Dynamic programming</strong><br/>ABAP cheat sheet example class</p>
"!
"! <p>The example class demonstrates syntax and concepts related to dynamic programming.<br/>
"! Choose F9 in ADT to run the class.</p>
"!
"! <h2>Note</h2>
"! <ul><li>Topics covered: Field symbols and data references (both as supporting elements for dynamic
"! programming), dynamic ABAP syntax components, runtime type services (RTTS), i.e., runtime type
"! identification (RTTI) and runtime type creation (RTTC).</li>
"! <li>To provide true dynamic determination at runtime for several code examples in this class,
"! the example class includes local classes in the CCIMP include (local types tab in ADT) whose
"! methods return character-like content for use in ABAP statements. The content is predefined
"! in these classes, but the actual content used is random.</li>
"! <li>Find information on <strong>getting started with the example class</strong> and the
"! <strong>disclaimer</strong> in the ABAP Doc comment of class {@link zcl_demo_abap_aux}.</li></ul>
CLASS zcl_demo_abap_dynamic_prog DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES: if_oo_adt_classrun.
CLASS-METHODS:
class_constructor.
PROTECTED SECTION.
PRIVATE SECTION.
METHODS inst_meth1.
METHODS inst_meth2 IMPORTING text TYPE string
RETURNING VALUE(result) TYPE string.
CLASS-METHODS stat_meth1.
CLASS-METHODS stat_meth2 IMPORTING text TYPE string
EXPORTING result TYPE string.
ENDCLASS.
CLASS zcl_demo_abap_dynamic_prog IMPLEMENTATION.
METHOD class_constructor.
"Filling demo database tables.
zcl_demo_abap_aux=>fill_dbtabs( ).
ENDMETHOD.
METHOD if_oo_adt_classrun~main.
out->write( |ABAP Cheat Sheet Example: Dynamic Programming\n\n| ).
**********************************************************************
out->write( |Excursion: Field Symbols\n\n| ).
out->write( |1) Declaring Field Symbols\n\n| ).
"Some data declarations and type definitions used further down
DATA: str TYPE string.
TYPES: BEGIN OF struc, "Structured data type
num1 TYPE i,
num2 TYPE i,
END OF struc,
tab_type TYPE TABLE OF struc. "Internal table type
"Field symbol declarations
"- Name of the field symbol goes between angle brackets
"- Type: either a complete data type or a generic type.
"Complete types
"Here, a chained statement with a colon is used.
FIELD-SYMBOLS: <fs_i> TYPE i,
<fs_flsch> TYPE zdemo_abap_flsch,
<fs_tab_type> TYPE LINE OF tab_type,
<fs_like> LIKE str.
"Generic types
"There are plenty of options for generic ABAP types. Check the
"keyword docu.
FIELD-SYMBOLS <fs_c> TYPE c. "Text field with a generic length
FIELD-SYMBOLS <fs_cseq> TYPE csequence. "Text-like (c, string)
FIELD-SYMBOLS <fs_data> TYPE data. "Any data type
FIELD-SYMBOLS <fs_any_table> TYPE ANY TABLE. "Internal table with any table type
"Declaring field symbols inline
"Prominent use case: Inline declaration of a field symbol for an internal table
"following ASSIGNING.
DATA demo_itab TYPE TABLE OF zdemo_abap_flsch WITH EMPTY KEY.
LOOP AT demo_itab ASSIGNING FIELD-SYMBOL(<line>).
...
ENDLOOP.
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `2) Assigning Data Objects to Field Symbols` ) ).
"ASSIGN statements assigns the memory area of a data object to a field symbol.
"Once the memory area is assigned, you can work with the content.
"You can also assign a particular component of a structure. Either you
"specify the position of the component or the name of the component.
"Data objects.
DATA: num_a TYPE i,
struc_a TYPE zdemo_abap_fli,
tab_a TYPE string_table.
"Field symbols with complete types
FIELD-SYMBOLS: <fs_i_a> TYPE i,
<fs_struc_a> TYPE zdemo_abap_fli,
<fs_tab_a> TYPE string_table.
"Field symbols with generic types, see more examples bwloe
FIELD-SYMBOLS <fs_data_a> TYPE data.
FIELD-SYMBOLS <fs_anytab_a> TYPE ANY TABLE.
"Assigning data objects to field symbols
ASSIGN num_a TO <fs_i_a>.
ASSIGN struc_a TO <fs_struc_a>.
ASSIGN tab_a TO <fs_tab_a>.
ASSIGN: num_a TO <fs_data_a>,
struc_a TO <fs_data_a>,
tab_a TO <fs_data_a>,
tab_a TO <fs_anytab_a>.
"Inline declaration is possible, too. The field symbol is implicitly typed
"with the generic type data.
ASSIGN num_a TO FIELD-SYMBOL(<fs_inl>).
"Generic typing
"- Generic types are available with which formal parameters of methods or field symbols
" can be specified.
"- At runtime, the actual data type is copied from the assigned actual parameter or
" memory area, i.e. they receive the complete data type only when an actual parameter
" is passed or a memory area is assigned.
FIELD-SYMBOLS:
"Any data type
<data> TYPE data,
<any> TYPE any,
"Any data type can be assigned. Restrictions for formal parameters and 'data': no
"numeric functions, no description functions, and no arithmetic expressions can be
"passed to these parameters. However, you can bypass the restriction by applying the
"CONV operator for the actual parameter.
"Character-like types
<c> TYPE c, "Text field with a generic length
<clike> TYPE clike, "Character-like (c, n, string, d, t and character-like flat structures)
<csequence> TYPE csequence, "Text-like (c, string)
<n> TYPE n, "Numeric text with generic length
<x> TYPE x, "Byte field with generic length
<xsequence> TYPE xsequence, "Byte-like (x, xstring)
"Numeric types
<decfloat> TYPE decfloat, "decfloat16, decfloat34)
<numeric> TYPE numeric, "Numeric ((b, s), i, int8, p, decfloat16, decfloat34, f)
<p> TYPE p, "Packed number (generic length and number of decimal places)
"Internal table types
<any_table> TYPE ANY TABLE, "Internal table with any table type
<hashed_table> TYPE HASHED TABLE,
<index_table> TYPE INDEX TABLE,
<sorted_table> TYPE SORTED TABLE,
<standard_table> TYPE STANDARD TABLE,
<table> TYPE table, "Standard table
"Other types
<simple> TYPE simple, "Elementary data type including enumerated types and
"structured types with exclusively character-like flat components
<object> TYPE REF TO object. "object can only be specified after REF TO; can point to any object
"Data objects to work with
DATA: BEGIN OF s,
c3 TYPE c LENGTH 3,
c10 TYPE c LENGTH 10,
n4 TYPE n LENGTH 4,
str TYPE string,
time TYPE t,
date TYPE d,
dec16 TYPE decfloat16,
dec34 TYPE decfloat34,
int TYPE i,
pl4d2 TYPE p LENGTH 4 DECIMALS 2,
tab_std TYPE STANDARD TABLE OF string WITH EMPTY KEY,
tab_so TYPE SORTED TABLE OF string WITH NON-UNIQUE KEY table_line,
tab_ha TYPE HASHED TABLE OF string WITH UNIQUE KEY table_line,
xl1 TYPE x LENGTH 1,
xstr TYPE xstring,
structure TYPE zdemo_abap_carr, "character-like flat structure
oref TYPE REF TO object,
END OF s.
"The following static ASSIGN statements demonstrate various assignments
"Note:
"- The statements commented out show impossible assignments.
"- If a static assignment is not successful, sy-subrc is not set and no
" memory area is assigned. Dynamic assignments, however, set the value.
"----- Any data type -----
ASSIGN s-c3 TO <data>.
ASSIGN s-time TO <data>.
ASSIGN s-tab_std TO <data>.
ASSIGN s-xstr TO <any>.
ASSIGN s-pl4d2 TO <any>.
ASSIGN s-date TO <any>.
"----- Character-like types -----
ASSIGN s-c3 TO <c>.
ASSIGN s-c10 TO <c>.
"ASSIGN s-str TO <c>.
ASSIGN s-c10 TO <clike>.
ASSIGN s-str TO <clike>.
ASSIGN s-n4 TO <clike>.
ASSIGN s-date TO <clike>.
ASSIGN s-time TO <clike>.
ASSIGN s-structure TO <clike>.
ASSIGN s-c10 TO <csequence>.
ASSIGN s-str TO <csequence>.
"ASSIGN s-n4 TO <csequence>.
ASSIGN s-n4 TO <n>.
"ASSIGN s-int TO <n>.
"ASSIGN s-time TO <n>.
ASSIGN s-xl1 TO <x>.
"ASSIGN s-xstr TO <x>.
ASSIGN s-xl1 TO <xsequence>.
ASSIGN s-xstr TO <xsequence>.
"----- Numeric types -----
ASSIGN s-dec16 TO <numeric>.
ASSIGN s-dec34 TO <numeric>.
ASSIGN s-int TO <numeric>.
ASSIGN s-pl4d2 TO <numeric>.
"ASSIGN s-n4 TO <numeric>.
ASSIGN s-dec16 TO <decfloat>.
ASSIGN s-dec34 TO <decfloat>.
ASSIGN s-pl4d2 TO <p>.
"ASSIGN s-dec34 TO <p>.
"----- Internal table types -----
ASSIGN s-tab_std TO <any_table>.
ASSIGN s-tab_so TO <any_table>.
ASSIGN s-tab_ha TO <any_table>.
ASSIGN s-tab_std TO <index_table>.
ASSIGN s-tab_so TO <index_table>.
"ASSIGN s-tab_ha TO <index_table>.
"ASSIGN s-tab_std TO <sorted_table>.
ASSIGN s-tab_so TO <sorted_table>.
"ASSIGN s-tab_ha TO <sorted_table>.
ASSIGN s-tab_std TO <standard_table>.
ASSIGN s-tab_std TO <table>.
"ASSIGN s-tab_so TO <standard_table>.
"ASSIGN s-tab_so TO <table>.
"ASSIGN s-tab_ha TO <standard_table>.
"ASSIGN s-tab_ha TO <table>.
"ASSIGN s-tab_std TO <hashed_table>.
"ASSIGN s-tab_so TO <hashed_table>.
ASSIGN s-tab_ha TO <hashed_table>.
"----- Other types -----
ASSIGN s-c10 TO <simple>.
ASSIGN s-str TO <simple>.
ASSIGN s-dec34 TO <simple>.
ASSIGN s-date TO <simple>.
ASSIGN s-structure TO <simple>.
ASSIGN s-xl1 TO <simple>.
"ASSIGN s-tab_ha TO <simple>.
ASSIGN s-oref TO <object>.
s-oref = NEW zcl_demo_abap_objects( ).
ASSIGN s-oref TO <object>.
s-oref = cl_abap_random_int=>create( ).
ASSIGN s-oref TO <object>.
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `3) Checking Field Symbol Assignment` ) ).
"When working with field symbols, you should make sure that they are
"assigned. Otherwise, a runtime error occurs.
"You can make use of a logical expression with IS [NOT] ASSIGNED.
"The example includes data object declarations. One data object is
"assigned, the other is not. Consequently, the expression is
"true for the one and false for the other.
DATA num_b TYPE i VALUE 123.
FIELD-SYMBOLS: <fs_i_b> TYPE i,
<fs_str_b> TYPE string.
ASSIGN num_b TO <fs_i_b>.
IF <fs_i_b> IS ASSIGNED.
out->write( `Field symbol <fs_i_b> is assigned.` ).
ELSE.
out->write( `Field symbol <fs_i_b is> not assigned.` ).
ENDIF.
out->write( |\n| ).
IF <fs_str_b> IS ASSIGNED.
out->write( `Field symbol <fs_str_b> is assigned.` ).
ELSE.
out->write( `Field symbol <fs_str_b> is not assigned.` ).
ENDIF.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `4) Unassigning Data Objects from Field Symbols` ) ).
"If you use an unassigned field symbol, an exception is raised. Before
"using it, you can check the assignment with the following logical
"expression. The statement is true if the field symbol is assigned.
"Using the statement UNASSIGN, you can explicitly remove the assignment
"of the field symbol.
DATA num_c TYPE i VALUE 123.
FIELD-SYMBOLS: <fs_i_c> TYPE i.
ASSIGN num_c TO <fs_i_c>.
IF <fs_i_c> IS ASSIGNED.
out->write( `1. Field symbol <fs_i_c> is assigned.` ).
ELSE.
out->write( `1. Field symbol <fs_i_c> is not assigned.` ).
ENDIF.
out->write( |\n| ).
UNASSIGN <fs_i_c>.
IF <fs_i_c> IS ASSIGNED.
out->write( `2. Field symbol <fs_i_c> is assigned.` ).
ELSE.
out->write( `2. Field symbol <fs_i_c> is not assigned.` ).
ENDIF.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `5) Type Casting with Field Symbols` ) ).
"The example demonstrates the CASTING addition. Various additions after
"CASTING are possible.
TYPES type_d_l9 TYPE c LENGTH 9.
DATA: dobj_d_l5 TYPE c LENGTH 5,
dobj_d_l10 TYPE c LENGTH 10 VALUE '1234567890',
type_name_d TYPE string VALUE 'TYPE_D_L9'.
FIELD-SYMBOLS: <fs_d1> TYPE data,
<fs_d2> TYPE type_d_l9.
"Casting to a statically, completely specified type
"CASTING addition without any more additions: Field symbol inherits
"the data type of the data object. The field symbol must be either
"completely typed or with one of the generic built-in ABAP types
"c, n, p, or x. The other field symbol declared in the example
"cannot be used.
ASSIGN dobj_d_l10 TO <fs_d2> CASTING.
out->write( data = <fs_d2> name = `<fs_d2>` ).
out->write( |\n| ).
ASSIGN dobj_d_l10 TO <fs_d1> CASTING TYPE type_d_l9.
out->write( data = <fs_d1> name = `<fs_d1>` ).
out->write( |\n| ).
"Casting to a generic type
ASSIGN dobj_d_l10 TO <fs_d1> CASTING TYPE c.
out->write( data = <fs_d1> name = `<fs_d1>` ).
out->write( |\n| ).
"Casting to a static field type
ASSIGN dobj_d_l10 TO <fs_d1> CASTING LIKE dobj_d_l5.
out->write( data = <fs_d1> name = `<fs_d1>` ).
out->write( |\n| ).
"Casting to a dynamic field type
ASSIGN dobj_d_l10 TO <fs_d1> CASTING LIKE <fs_d1>.
out->write( data = <fs_d1> name = `<fs_d1>` ).
out->write( |\n| ).
"Anticipating dynamic specification of data types
"for the CASTING addition.
"The type name is specified as a character-like data
"object within parentheses.
ASSIGN dobj_d_l10 TO <fs_d1> CASTING TYPE (type_name_d).
out->write( data = <fs_d1> name = `<fs_d1>` ).
out->write( |\n| ).
"Anticipating RTTS
"A type description object is created which can be
"specified after the TYPE HANDLE additions.
DATA(sometype) = CAST cl_abap_datadescr(
cl_abap_typedescr=>describe_by_name( 'TYPE_D_L9' ) ).
ASSIGN dobj_d_l10 TO <fs_d1> CASTING TYPE HANDLE sometype.
out->write( data = <fs_d1> name = `<fs_d1>` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `6) Addressing Field Symbols` ) ).
"The example includes multiple data objects that are assigned to field
"symbols. It is demonstrated that field symbols are addressed in various
"occasions. Among them: Changing the value of data objects assigned to
"field symbols, the use of field symbols in expressions, structures, and
"internal tables.
DATA: num_e TYPE i VALUE 456,
struc_e TYPE zdemo_abap_carr,
tab_e TYPE TABLE OF zdemo_abap_carr WITH EMPTY KEY.
SELECT SINGLE *
FROM zdemo_abap_carr
WHERE carrid = 'LH'
INTO @struc_e.
FIELD-SYMBOLS: <fs_i_e> TYPE i,
<fs_struc_e> TYPE zdemo_abap_carr,
<fs_tab_e> LIKE tab_e,
<fs_anytab_e> TYPE ANY TABLE.
"Without an assignment, this would result in a runtime error:
"<fs_i_e> = 1.
ASSIGN num_e TO <fs_i_e>.
ASSIGN struc_e TO <fs_struc_e>.
ASSIGN tab_e TO <fs_tab_e>.
ASSIGN tab_e TO <fs_anytab_e>.
"Changing values
<fs_i_e> = 789.
out->write( data = <fs_i_e> name = `<fs_i_e>` ).
out->write( |\n| ).
out->write( data = num_e name = `num_e` ).
out->write( |\n| ).
"Use in expressions
DATA(calc_e) = <fs_i_e> + 211.
out->write( data = calc_e name = `calc_e` ).
out->write( |\n| ).
IF <fs_i_e> < 1000.
out->write( `The value of <fs_i_e> is less than 1000` ).
ELSE.
out->write( `The value of <fs_i_e> is greater than 1000` ).
ENDIF.
out->write( |\n| ).
out->write( |\n| ).
"Structure
out->write( data = <fs_struc_e> name = `<fs_struc_e>` ).
out->write( |\n| ).
DATA(comp_e1) = <fs_struc_e>-carrid.
out->write( data = comp_e1 name = `comp_e1` ).
out->write( |\n| ).
<fs_struc_e>-url = 'www.lh.com'.
out->write( data = <fs_struc_e>-url name = `<fs_struc_e>-url` ).
out->write( |\n| ).
"Internal table
SELECT *
FROM zdemo_abap_carr
ORDER BY carrid
INTO TABLE @<fs_tab_e>
UP TO 3 ROWS.
out->write( data = <fs_tab_e> name = `<fs_tab_e>` ).
out->write( |\n| ).
TRY.
DATA(comp_e2) = <fs_tab_e>[ 2 ]-carrname.
out->write( data = comp_e2 name = `comp_e2` ).
CATCH cx_sy_itab_line_not_found INTO DATA(error_e).
ENDTRY.
out->write( |\n| ).
SELECT *
FROM zdemo_abap_carr
ORDER BY carrid
INTO TABLE @<fs_anytab_e>
UP TO 3 ROWS.
out->write( data = <fs_anytab_e> name = `<fs_anytab_e>` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `7) Using Field Symbols when Processing Internal Tables` ) ).
"By using field symbols in the context of loops across internal tables,
"you can avoid an actual copying of content to a work area during
"the loop.
"The example includes multiple loops. First, internal tables are
"declared. One of them is filled. Then, field symbols are declared to
"which data objects are assigned. In the first loop, a previously
"declared field symbol is used as target area to hold the table line
"that is processed. In the course of the loop, some values are changed. The
"components are accessed using the component selector '-'. At the end of
"the loop, another internal table is filled using the currently
"processed line for the second loop. The second loop (the loop is
"carried out based on
"an internal table a field symbol points to) uses a directly declared
"field symbol using ASSIGNING FIELD-SYMBOL(<...>). Also here, some
"values are changed and another internal table is filled. This table
"is of a generic type. At the end, this internal table is output, too.
DATA: tab_f1 TYPE TABLE OF zdemo_abap_fli WITH EMPTY KEY,
tab_f2 LIKE tab_f1,
tab_f3 LIKE tab_f1.
SELECT *
FROM zdemo_abap_fli
ORDER BY carrid
INTO TABLE @tab_f1
UP TO 3 ROWS.
FIELD-SYMBOLS: <fs_struc_f> LIKE LINE OF tab_f1,
<fs_tab_f> LIKE tab_f1,
<fs_anytab_f> TYPE ANY TABLE.
ASSIGN tab_f2 TO <fs_tab_f>.
ASSIGN tab_f3 TO <fs_anytab_f>.
LOOP AT tab_f1 ASSIGNING <fs_struc_f>.
<fs_struc_f>-connid = '99'.
<fs_struc_f>-fldate = cl_abap_context_info=>get_system_date( ).
<fs_struc_f>-price = <fs_struc_f>-price + 100.
<fs_struc_f>-currency = 'EUR'.
CLEAR: <fs_struc_f>-paymentsum,
<fs_struc_f>-seatsocc,
<fs_struc_f>-seatsocc_b,
<fs_struc_f>-seatsocc_f.
"Filling another itab
<fs_tab_f> = VALUE #( BASE <fs_tab_f> ( <fs_struc_f> ) ).
ENDLOOP.
out->write( data = tab_f1 name = `tab_f1` ).
out->write( |\n| ).
"The following example shows a field symbol declared inline.
LOOP AT <fs_tab_f> ASSIGNING FIELD-SYMBOL(<fs_struc_f2>).
<fs_struc_f2>-connid = '100'.
<fs_struc_f2>-fldate = cl_abap_context_info=>get_system_date( ) + 1.
<fs_struc_f2>-price = <fs_struc_f2>-price - 50.
<fs_struc_f2>-currency = 'USD'.
"Filling another itab
<fs_anytab_f> = VALUE #( BASE <fs_anytab_f> ( <fs_struc_f2> ) ).
ENDLOOP.
out->write( data = <fs_tab_f> name = `<fs_tab_f>` ).
out->write( |\n| ).
out->write( data = <fs_anytab_f> name = `<fs_anytab_f>` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `8) Structure Assigned to Field Symbol: Accessing Structure Components` ) ).
"In this example, all components of a structure are processed using
"field symbols and an ASSIGN COMPONENT ... OF STRUCTURE ... statement.
"First, a field symbol is declared with a generic type. A structure is
"filled with values from a demo table. The structure is assigned to the
"field symbol. Using a DO loop, all components are processed. The
"sy-index value represents the position of the component in the
"structure. Once all components have been processed (i. e. if sy-subrc
"does not return '0' for a sy-index value), the loop is exited. The output
"shows all components and their values.
"See more examples for accessing structure components below.
FIELD-SYMBOLS <comp> TYPE data.
DATA comp_tab TYPE string_table.
SELECT SINGLE carrid, carrname, currcode, url
FROM zdemo_abap_carr
WHERE carrid = 'LH'
INTO @DATA(struct).
FIELD-SYMBOLS <struct> TYPE data.
ASSIGN struct TO <struct>.
DO.
"sy-index represents the position of a structure component
ASSIGN <struct>-(sy-index) TO <comp>.
"Old syntax
"ASSIGN COMPONENT sy-index OF STRUCTURE <struct> TO <comp>.
IF sy-subrc <> 0.
"If all components are processed, the loop is exited.
EXIT.
ELSE.
out->write( |sy-index: { sy-index }, component content:| ).
out->write( <comp> ).
out->write( |\n| ).
ENDIF.
ENDDO.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `Data References` ) ).
out->write( |9) Declaring Data References\n\n| ).
"Like field symbols, data reference variables can be declared with both
"a complete and a generic data type using DATA statements and the
"addition REF TO. The type after REF TO represents the static data type.
"The example shows multiple ways of declaring a data reference variable
"using both complete and generic data types.
DATA: some_string TYPE string.
TYPES: ref_type TYPE REF TO zdemo_abap_flsch.
DATA: ref_a1 TYPE REF TO i, "Complete data type
ref_a2 TYPE REF TO zdemo_abap_carr, "Complete data type
ref_a3 LIKE REF TO some_string,
ref_a4 LIKE ref_a1,
ref_a5 TYPE ref_type,
ref_a6 TYPE REF TO data. "Generic data type
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `10) Creating Data References ` &&
`to Existing Data Objects` ) ).
"The example includes data reference variables with both complete and
"generic type. When using the REF operator, the '#' sign means that the
"type is derived from the data object. You can also explicitly specify
"the data type after REF before the parenthesis. Within the parentheses,
"you can provide a value.
"Declaring data object
DATA number_b TYPE i VALUE 5.
"Declaring data reference variables
DATA ref_b1 TYPE REF TO i.
DATA ref_data_b TYPE REF TO data.
"Creating data references to data objects.
"The '#' sign means that the type is derived from the data object.
ref_b1 = REF #( number_b ).
ref_data_b = REF #( number_b ).
"You can also use inline declarations to omit the explicit declaration.
DATA(ref_b2) = REF #( number_b ).
"You can explicitly specify the data type after REF.
"DATA(ref_b3) = REF #( g ).
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `11) Dynamically Creating Data Objects at Runtime Using Static Type Definitions` ) ).
"The example code shows the creation of anonymous data objects. They
"can be created using the statement CREATE DATA, the instance operator
"NEW, or the addition NEW of the INTO clause in a SELECT statement.
"A data reference variable is expected when anonymous objects are
"declared. They cannot be addressed by a name (hence anonymous).
"Note:
"- The examples cover static type definitions. As shown further down,
" there are options to dynamically specify the type definitions.
"- To output the content of the data reference variables, they
" must be dereferenced first. The details are shown further down.
"CREATE DATA statements
"Note that there are many additions available. The examples show a selection.
"Behind TYPE and LIKE, the syntax offers the same possibilities as the DATA statement.
"Creating an anonymous data object with an implicit type.
"If neither of the additions TYPE or LIKE are specified, the data reference variable
"must be completely typed.
DATA dref_c1 TYPE REF TO string.
CREATE DATA dref_c1.
"Creating anonymous data objects with explicit data type specification.
"Data reference variable with a generic type to be used in the following examples
"for the anonymous data object.
DATA dref_c2 TYPE REF TO data.
"Elementary, built-in ABAP type
CREATE DATA dref_c2 TYPE p LENGTH 8 DECIMALS 3.
"Anomyous internal table ...
"using the LIKE addition to refer to an existing internal table
DATA itab_c TYPE TABLE OF zdemo_abap_carr.
CREATE DATA dref_c2 LIKE itab_c.
"by specifying the entire table type
CREATE DATA dref_c2 TYPE HASHED TABLE OF zdemo_abap_carr WITH UNIQUE KEY carrid.
"Anonymous structures
CREATE DATA dref_c2 LIKE LINE OF itab_c.
CREATE DATA dref_c2 TYPE zdemo_abap_carr.
"Creating reference variable
TYPES elem_type_c TYPE c LENGTH 3.
CREATE DATA dref_c2 TYPE REF TO elem_type_c.
"NEW operator
"- Works like CREATE DATA dref TYPE type statements and can be used in general
" expression positions.
"- Allows to assign values to the new anonymous data objects in parentheses
"Creating data reference variables
DATA: dref_c3 TYPE REF TO i,
dref_c4 TYPE REF TO data.
"# character after NEW if the data type can be identified completely
"instead of the explicit type specification (only non-generic types)
dref_c3 = NEW #( 123 ).
dref_c3 = NEW i( 456 ).
dref_c4 = NEW zdemo_abap_carr( ). "not assigning any values
dref_c4 = NEW string( `hi` ).
"Creating anonymous data objects inline
"In doing so, you can omit a prior declaration of a variable.
DATA(dref_c5) = NEW i( 789 ).
DATA(dref_c6) = NEW zdemo_abap_carr( carrid = 'AB' carrname = 'AB Airlines' ).
"ABAP SQL SELECT statements
"Using the NEW addition in the INTO clause, an anonymous data object
"can be created in place.
SELECT *
FROM zdemo_abap_carr
INTO TABLE NEW @DATA(dref_c7) "Internal table
UP TO 3 ROWS.
SELECT SINGLE *
FROM zdemo_abap_carr
WHERE carrid = 'LH'
INTO NEW @DATA(dref_c8). "Structure
out->write( data = dref_c6->* name = `dref_c6->*` ).
out->write( |\n| ).
out->write( data = dref_c7->* name = `dref_c7->*` ).
out->write( |\n| ).
out->write( data = dref_c8->* name = `dref_c8->*` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `12) Data References and Assignments` ) ).
"Regarding the assignment, note that static types of both data
"reference variables must be compatible. As a result of an assignment,
"both the target reference variable and the source reference variable
"point to the same data object.
"Upcast/downcasts: For an assignment to work, the basic rule applies:
"The static type of the target reference variable must be more general
"than or the same as the dynamic type of the source reference variable.
"In the example below:
"Upcast: The target data reference variable is of generic type, the
"source variable is of complete type. The assignment is done with the
"assignment operator '='.
"Downcast: The target data reference variable is of complete type, the
"source variable is of generic type. The assignment is done with casting
"operators, either with the constructor operator CAST or the older ?=.
"Declaring data reference variables
DATA ref_d1 TYPE REF TO i.
DATA ref_d2 TYPE REF TO i.
ref_d1 = NEW #( 789 ).
"Assigning data reference
ref_d2 = ref_d1.
"Casting
"Complete type
DATA(ref_d3) = NEW i( 321 ).
"Generic type
DATA ref_data_d1 TYPE REF TO data.
"Upcast
ref_data_d1 = ref_d3.
"Downcasts
DATA ref_d5 TYPE REF TO i.
"Generic type
DATA ref_data_d2 TYPE REF TO data.
ref_data_d2 = NEW i( 654 ).
ref_d5 = CAST #( ref_data_d2 ).
ref_d5 ?= ref_data_d2.
out->write( data = ref_d2->* name = `ref_d2->*` ).
out->write( |\n| ).
out->write( data = ref_data_d1->* name = `ref_data_d1->*` ).
out->write( |\n| ).
out->write( data = ref_d5->* name = `ref_d5->*` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `13) Addressing Data References ` ) ).
"Before addressing the content of data objects a data reference points
"to, you must dereference data reference variables. Use the
"dereferencing operator ->*.
"The example includes multiple occasions in which data reference are
"addressed: Changing the content of a referenced data object, the use in
"logical expressions, structures, and internal tables.
"Creating data reference variables and assigning values
DATA(ref_e1) = NEW i( 1 ).
DATA(ref_e2) = NEW zdemo_abap_carr( carrid = 'LH'
carrname = 'Lufthansa' ).
"Generic type
DATA ref_data_e TYPE REF TO data.
"Copying reference
ref_data_e = ref_e1.
"Addressing
"Variable receives the content.
DATA(some_num) = ref_e1->*.
out->write( data = ref_e1->* name = `ref_e1->*` ).
out->write( |\n| ).
"Content of referenced data object is changed
ref_e1->* = 10.
out->write( data = ref_e1->* name = `ref_e1->*` ).
out->write( |\n| ).
"Data reference used in a logical expression
IF ref_e1->* > 5.
out->write( `The value of ref_e1 is greater than 5.` ).
ELSE.
out->write( `The value of ref_e1 is lower than 5.` ).
ENDIF.
out->write( |\n| ).
"Dereferenced generic type
DATA(calc) = 1 + ref_data_e->*.
out->write( data = calc name = `calc` ).
out->write( |\n| ).
"Complete structure
DATA(struc) = ref_e2->*.
out->write( data = ref_e2->* name = `ref_e2->*` ).
out->write( |\n| ).
"Individual structure component
"When dereferencing a data reference variable that has a structured
"data type, you can use the component selector -> to address individual components.
DATA(carrid) = ref_e2->carrid.
ref_e2->carrid = 'UA'.
out->write( data = ref_e2->carrid name = `ref_e2->carrid` ).
out->write( |\n| ).
"The following syntax also works (dereferencing operator and the component selector).
ref_e2->*-carrname = 'United Airlines'.
out->write( data = ref_e2->*-carrname name = `ref_e2->*-carrname` ).
out->write( |\n| ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `14) Checking if Data Reference ` &&
`Can Be Dereferenced` ) ).
"You can check if a data reference can be dereferenced by using
"a logical expression with IS [NOT] BOUND.
"The example shows both a data reference that is bound and not bound.
DATA(ref_f1) = NEW string( `hello` ).
DATA ref_f2 TYPE REF TO i.
out->write( `IF statement:` ).
IF ref_f1 IS BOUND.
out->write( `ref_f1 is bound.` ).
ELSE.
out->write( `ref_f1 is not bound.` ).
ENDIF.
out->write( |\n| ).
out->write( `COND operator:` ).
DATA(is_bound) = COND #( WHEN ref_f1 IS BOUND THEN `ref_f1 is bound.` ELSE `ref_f1 is not bound.` ).
out->write( is_bound ).
out->write( |\n| ).
out->write( `IF statement:` ).
IF ref_f2 IS BOUND.
out->write( `ref_f2 is bound.` ).
ELSE.
out->write( `ref_f2 is not bound.` ).
ENDIF.
out->write( |\n| ).
out->write( `COND operator:` ).
is_bound = COND #( WHEN ref_f2 IS BOUND THEN `ref_f2 is bound.` ELSE `ref_f2 is not bound.` ).
out->write( is_bound ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `15) Explicitly Removing a Reference` ) ).
"Note that the garbage collector takes care of removing the references
"automatically once the data is not used any more by a reference.
DATA(ref_g1) = NEW string( `hello` ).
IF ref_g1 IS INITIAL.
out->write( `Before CLEAR: ref_g1 is initial.` ).
ELSE.
out->write( `Before CLEAR: ref_g1 is not intial.` ).
ENDIF.
out->write( |\n| ).
IF ref_g1 IS BOUND.
out->write( `Before CLEAR: ref_g1 is bound.` ).
ELSE.
out->write( `Before CLEAR: ref_g1 is not bound.` ).
ENDIF.
out->write( |\n| ).
CLEAR ref_g1.
IF ref_g1 IS INITIAL.
out->write( `After CLEAR: ref_g1 is initial.` ).
ELSE.
out->write( `After CLEAR: ref_g1 is not initial.` ).
ENDIF.
out->write( |\n| ).
IF ref_g1 IS BOUND.
out->write( `After CLEAR: ref_g1 is bound.` ).
ELSE.
out->write( `After CLEAR: ref_g1 is not bound.` ).
ENDIF.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `16) Overwriting Data Reference Variables` ) ).
"A data reference variable is overwritten if a new object is created
"with a data reference variable already pointing to a data object
DATA ref_h1 TYPE REF TO data.
ref_h1 = NEW i( 111 ).
out->write( data = ref_h1->* name = `ref_h1->*` ).
out->write( |\n| ).
ref_h1 = NEW string( `ref_h1 overwritten.` ).
out->write( data = ref_h1->* name = `ref_h1->*` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `17) Retaining Data References` ) ).
"Storing data reference variables in an internal table using
"TYPE TABLE OF REF TO prevents the overwriting.
"The example demonstrates that three data references are created with
"the same reference variable in the course of a DO loop. There, the data
"reference is overwritten. However, due to saving the data reference
"variable in an internal table, the content is preserved.
DATA: ref_data_i TYPE REF TO data,
itab_i TYPE TABLE OF REF TO data,
number_i TYPE i VALUE 0.
DO 3 TIMES.
"Adding up 1 to demonstrate a changed data object
number_i += 1.
"Creating a data reference and assigning value
"In the course of the loop, the variable is overwritten.
ref_data_i = NEW i( number_i ).
"Adding the reference to an internal table
itab_i = VALUE #( BASE itab_i ( ref_data_i ) ).
ENDDO.
out->write( data = itab_i name = `itab_i` ).
out->write( |\n| ).
out->write( data = `The derefenced value of the data reference - which ` &&
`was changed in the course of the loop - in the second table ` &&
`entry is ` && itab_i[ 2 ]->* && `.` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `18) Processing Internal Tables Using ` &&
`Data References ` ) ).
"Similar use case to using field symbols: In a loop across an internal table,
"you can store the content of the line in a data reference variable
"instead of actually copying the content to boost performance.
"In the example, an internal table is created including values of
"database table. A data reference variable is declared as target area of
"the loop. In the course of the loop, some values are changed.
SELECT *
FROM zdemo_abap_flsch
WHERE distid = 'MI'
ORDER BY carrid
INTO TABLE @DATA(flsch_tab)
UP TO 3 ROWS.
"The table line is written into a data reference variable
"that is declared inline.
LOOP AT flsch_tab REFERENCE INTO DATA(ref_k).
ref_k->connid = '123'.
ref_k->distance = ref_k->distance * '1.609344'.
ref_k->distid = 'KM' .
ENDLOOP.
out->write( data = flsch_tab name = `flsch_tab` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `19) Data References as Part of ` &&
`Structures and Internal Tables` ) ).
"In contrast to field symbols, data reference variables can be used as
"components of structures or columns in internal tables.
"The example includes the declaration of a structure that contains a
"data reference variable.
"The structure is filled. Based on this structure, an
"internal table is created. In a DO loop, the internal table is filled.
"Declaring a structure
DATA: BEGIN OF struc_l,
number_l TYPE i,
ref_l TYPE REF TO i,
END OF struc_l,
some_int TYPE i VALUE 0.
"Filling structure
struc_l = VALUE #( number_l = 1 ref_l = NEW #( 2 ) ).
out->write( data = struc_l ).
out->write( |\n| ).
"Declaring an internal table
DATA itab_l LIKE TABLE OF struc_l WITH EMPTY KEY.
"Filling internal table.
DO 3 TIMES.
some_int += 1.
"Filling structure
struc_l = VALUE #( number_l = some_int
ref_l = NEW #( some_int ) ).
"Filling internal table
itab_l = VALUE #( BASE itab_l ( struc_l ) ).
ENDDO.
out->write( data = itab_l name = `itab_l` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `20) Excursion: Generic Data References` ) ).
"In modern ABAP, variables and field symbols of the generic types
"'any' and 'data' can be used directly, for example, in LOOP and READ statements.
DATA dref_gen TYPE REF TO data.
CREATE DATA dref_gen TYPE TABLE OF zdemo_abap_carr.
SELECT *
FROM zdemo_abap_carr
INTO TABLE @dref_gen->*.
"Note: In case of a fully generic type, an explicit or implicit index operation
"is not possible (indicated by the examples commented out).
LOOP AT dref_gen->* ASSIGNING FIELD-SYMBOL(<loop>).
...
ENDLOOP.
"LOOP AT dref->* ASSIGNING FIELD-SYMBOL(<loop2>) FROM 1 TO 4.
"ENDLOOP.
"The following examples use a dynamic key specification.
"See more syntax examples below.
READ TABLE dref_gen->* ASSIGNING FIELD-SYMBOL(<read>) WITH KEY ('CARRID') = 'AA'.
"READ TABLE dref->* INDEX 1 ASSIGNING FIELD-SYMBOL(<read2>).
out->write( data = <read> name = `<read>` ).
out->write( |\n| ).
"Table expressions
DATA(line) = CONV zdemo_abap_carr( dref_gen->*[ ('CARRID') = 'AA' ] ).
out->write( data = line name = `line` ).
out->write( |\n| ).
dref_gen->*[ ('CARRID') = 'AA' ] = VALUE zdemo_abap_carr( BASE dref_gen->*[ ('CARRID') = 'AA' ] carrid = 'XY' ).
out->write( data = dref_gen->*[ ('CARRID') = 'XY' ] name = `dref_gen->*[ ('CARRID') = 'XY' ]` ).
out->write( |\n| ).
dref_gen->*[ ('CARRID') = 'XY' ]-('CARRID') = 'AA'.
out->write( data = dref_gen->*[ ('CARRID') = 'AA' ]-('CARRNAME') name = `dref_gen->*[ ('CARRID') = 'AA' ]-('CARRNAME')` ).
out->write( |\n| ).
"Table functions
DATA(num_tab_lines) = lines( dref_gen->* ).
out->write( data = num_tab_lines name = `num_tab_lines` ).
out->write( |\n| ).
DATA(idx) = line_index( dref_gen->*[ ('CARRID') = 'LH' ] ).
out->write( data = idx name = `idx` ).
out->write( |\n| ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `Dynamic ABAP Statements` ) ).
out->write( |21) Dynamic ASSIGN Statements (1) - Specifying the Memory Area Dynamically\n\n| ).
"The memory area is not specified directly, but as content of a
"character-like data object in parentheses.
"Note:
"- When specified as unnamed data object, the compiler treats the
" specifications like static assignments. Do not use named data objects
" for ASSIGN statements in ABAP for Cloud Development. It is recommended
" that existing named data objects are put in a structure. Then, the syntax
" for assigning components dynamically can be used so as to avoid a syntax
" warning.
"- Most of the following examples use an unnamed data object.
"- The specification of the name is not case-sensitive.
"Creating and populating various types/data objects to work with
TYPES: BEGIN OF st_type,
col1 TYPE i,
col2 TYPE string,
col3 TYPE string,
END OF st_type.
DATA structure TYPE st_type.
DATA it TYPE TABLE OF st_type WITH EMPTY KEY.
structure = VALUE #( col1 = 1 col2 = `aaa` col3 = `Z` ).
APPEND structure TO it.
DATA(struc_ref) = NEW st_type( col1 = 2 col2 = `b` col3 = `Y` ).
DATA dobj TYPE string VALUE `hallo`.
"The following examples use a field symbol with generic type
FIELD-SYMBOLS <fs> TYPE data.
ASSIGN ('IT') TO <fs>.
ASSIGN ('SRUCTURE') TO <fs>.
"Field symbol declared inline
"Note: The typing is performed with the generic type data.
ASSIGN ('DOBJ') TO FIELD-SYMBOL(<fs_inline>).
"The statements set the sy-subrc value.
ASSIGN ('DOES_NOT_EXIST') TO <fs>.
IF sy-subrc <> 0.
out->write( `Dynamic assignment not successful` ).
ENDIF.
"The memory area can also be a dereferenced data reference
ASSIGN struc_ref->* TO <fs>.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `22) Dynamic ASSIGN Statements (2) - Assigning Components Dynamically` ) ).
"You can chain the names with the component selector (-), or, in
"case of reference variables, the object component selector (->).
ASSIGN structure-('COL1') TO <fs>.
ASSIGN it[ 1 ]-('COL1') TO <fs>.
ASSIGN struc_ref->('COL1') TO <fs>.
"The following example uses the dereferencing operator explicitly
"followed by the component selector.
ASSIGN struc_ref->*-('COL1') TO <fs>.
"Using a named data object for the component specification
DATA columnname TYPE string VALUE `COL1`.
ASSIGN structure-(columnname) TO <fs>.
"Fully dynamic specification
"If the compiler can fully determine the data object in ASSIGN structureatements
"in ABAP for Cloud Development, a warning is not issued.
ASSIGN ('STRUCTURE-COL1') TO <fs>.
"Numeric expressions are possible. Its value is interpreted
"as the position of the component in the structureructure.
ASSIGN structure-(3) TO <fs>.
"If the value is 0, the memory area of the entire structureructure is
"assigned to the field symbol.
ASSIGN structure-(0) TO <fs>.
"The structureatements above replace the following, older structureatements.
ASSIGN COMPONENT 'COL1' OF STRUCTURE structure TO <fs>.
ASSIGN COMPONENT 3 OF STRUCTURE structure TO <fs>.
"More examples
SELECT SINGLE * FROM zdemo_abap_carr INTO @DATA(wa).
"Reading into data reference variable
SELECT SINGLE * FROM zdemo_abap_carr INTO NEW @DATA(ref_m).
DATA(comp_name) = lcl_det_at_runtime=>get_dyn_field( ).
ASSIGN wa-(comp_name) TO FIELD-SYMBOL(<fs_m6>).
ASSIGN wa-('CARRNAME') TO FIELD-SYMBOL(<fs_m7>).
IF sy-subrc = 0.
DATA(subrc1) = sy-subrc.
ENDIF.
"No exception occurs in case of an unsuccessful assignment.
ASSIGN wa-('CRRNM') TO FIELD-SYMBOL(<fs_m8>).
IF sy-subrc <> 0.
DATA(subrc2) = sy-subrc.
ENDIF.
"Numeric expressions are possible. Its value is interpreted as the position
"of the component in the structure.
ASSIGN wa-(4) TO FIELD-SYMBOL(<fs_m9>).
"If the value is 0, the memory area of the entire structure is assigned to the field symbol.
ASSIGN wa-(0) TO FIELD-SYMBOL(<fs_m10>).
"Old syntax
ASSIGN COMPONENT 'CARRID' OF STRUCTURE wa TO FIELD-SYMBOL(<fs_m11>).
ASSIGN COMPONENT 5 OF STRUCTURE wa TO FIELD-SYMBOL(<fs_m12>).
"Dynamically specifying components of structures that are referenced by
"a data reference variable
ASSIGN ref_m->('CARRNAME') TO FIELD-SYMBOL(<fs_m13>).
out->write( data = <fs_m6> name = `<fs_m6>` ).
out->write( |\n| ).
out->write( data = <fs_m7> name = `<fs_m7>` ).
out->write( |\n| ).
out->write( data = subrc1 name = `subrc1` ).
out->write( |\n| ).
out->write( data = subrc2 name = `subrc2` ).
out->write( |\n| ).
out->write( data = <fs_m9> name = `<fs_m9>` ).
out->write( |\n| ).
out->write( data = <fs_m10> name = `<fs_m10>` ).
out->write( |\n| ).
out->write( data = <fs_m11> name = `<fs_m11>` ).
out->write( |\n| ).
out->write( data = <fs_m12> name = `<fs_m12>` ).
out->write( |\n| ).
out->write( data = <fs_m13> name = `<fs_m13>` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `23) Dynamic ASSIGN Statements (3) - Assigning Attributes of Classes or Interfaces Dynamically (1)` ) ).
"The following syntax pattern shows the possible specifications.
"... cref->(attr_name) ... "object reference variable
"... iref->(attr_name) ... "interface reference variable
"... (clif_name)=>(attr_name) ... "class/interface name
"... (clif_name)=>attr ...
"... clif=>(attr_name) ...
"Creating an instance of a class
DATA(objref) = NEW zcl_demo_abap_objects( ).
"Assigning instance attributes using an object reference variable
"All visible attributes of objects can be assigned.
objref->string = `ABAP`. "Assigning a value to the attribute for demo purposes
ASSIGN objref->('STRING') TO <fs>.
"Assigning instance attributes using an interface reference variable
DATA intref TYPE REF TO zdemo_abap_objects_interface.
intref = objref.
ASSIGN intref->('STRING') TO <fs>.
intref->in_str = `hallo`.
ASSIGN intref->('IN_STR') TO <fs>.
"Assigning static attributes
"All visible static attributes in classes and interfaces can be assigned
"In the following example, a class and an interface are specified statically,
"and the attributes are specified dynamically.
ASSIGN zcl_demo_abap_objects=>('PUBLIC_STRING') TO <fs>.
ASSIGN zdemo_abap_objects_interface=>('CONST_INTF') TO <fs>.
"Specifying a class or interface dynamically, and attributes statically
ASSIGN ('ZCL_DEMO_ABAP_OBJECTS')=>public_string TO <fs>.
ASSIGN ('ZDEMO_ABAP_OBJECTS_INTERFACE')=>const_intf TO <fs>.
"Specifying a class or interface as well as attributes dynamically
ASSIGN ('ZCL_DEMO_ABAP_OBJECTS')=>('PUBLIC_STRING') TO <fs>.
ASSIGN ('ZDEMO_ABAP_OBJECTS_INTERFACE')=>('CONST_INTF') TO <fs>.
"Further dynamic syntax options are possible, for example,
"specifying the memory area after ASSIGN with a writable expression
"because the operand position after ASSIGN is a result position.
ASSIGN NEW zcl_demo_abap_objects( )->('PUBLIC_STRING') TO <fs>.
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `24) Dynamic ASSIGN Statements (4) - Assigning Attributes of Classes or Interfaces Dynamically (2)` ) ).
"The following examples demonstrate a selection of various dynamic specifications
"that are possible with ASSIGN statements.
"Dynamic specification of attributes of classes/interfaces that are assigned to a field symbol.
DATA(dobj_name) = lcl_det_at_runtime=>get_dyn_dobj( ).
ASSIGN lcl_det_at_runtime=>(dobj_name) TO FIELD-SYMBOL(<fs_m1>).
out->write( |Data object name determined at runtime: { dobj_name } | ).
out->write( |\n| ).
out->write( |The content of the data object is: { <fs_m1> } | ).
out->write( |\n| ).
out->write( |\n| ).
dobj_name = lcl_det_at_runtime=>get_dyn_dobj( ).
"Completely dynamic assign
ASSIGN ('lcl_det_at_runtime')=>(dobj_name) TO FIELD-SYMBOL(<fs_m2>).
out->write( data = <fs_m2> name = `<fs_m2>` ).
out->write( |\n| ).
ASSIGN ('zdemo_abap_objects_interface')=>('const_intf') TO FIELD-SYMBOL(<fs_m3>).
out->write( data = <fs_m3> name = `<fs_m3>` ).
out->write( |\n| ).
"Class/interface reference variables pointing to an object that contains attributes
"and that are specified dynamically.
DATA iref TYPE REF TO zdemo_abap_objects_interface.
DATA(cl_ref) = NEW zcl_demo_abap_objects( ).
iref = cl_ref.
ASSIGN iref->('const_intf') TO FIELD-SYMBOL(<fs_m4>).
out->write( data = <fs_m4> name = `<fs_m4>` ).
out->write( |\n| ).
ASSIGN cl_ref->('another_string') TO FIELD-SYMBOL(<fs_m5>).
out->write( data = <fs_m5> name = `<fs_m5>` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `25) Dynamic ASSIGN Statements (5) - Setting sy-subrc/ELSE UNASSIGN Addition` ) ).
"In dynamic assignments, the statement ASSIGN sets the return code sy-subrc.
"If ELSE UNASSIGN is specified, no memory area is assigned to the field symbol. It has the state unassigned after the ASSIGN statement.
DATA(attr) = VALUE string_table( ( `another_string` ) ( `public_string` ) ( `this_will_fail` ) ).
LOOP AT attr INTO DATA(attribute).
ASSIGN cl_ref->(attribute) TO FIELD-SYMBOL(<attr>) ELSE UNASSIGN.
IF sy-subrc = 0.
out->write( |Successful assignment for attribute "{ attribute }". sy-subrc = { sy-subrc }. | ).
out->write( |\n| ).
out->write( data = <attr> name = `<attr>` ).
ELSE.
out->write( |Assignment not successful for attribute "{ attribute }". sy-subrc = { sy-subrc }. | ).
ENDIF.
out->write( |\n| ).
IF <attr> IS ASSIGNED.
out->write( `The field symbol is assigned.` ).
out->write( `--------------------` ).
ELSE.
out->write( `The field symbol is not assigned.` ).
ENDIF.
out->write( |\n| ).
ENDLOOP.
"Note: For the static variant of the ASSIGN statement, i.e. if the memory area
"to be assigned following the ASSIGN keyword is statically specified, the addition
"ELSE UNASSIGN is implicitly set and cannot be used explicitly.
DATA(hallo) = `Hallo world`.
ASSIGN ('HALLO') TO FIELD-SYMBOL(<eu>) ELSE UNASSIGN.
ASSERT sy-subrc = 0 AND <eu> IS ASSIGNED.
ASSIGN ('DOES_NOT_EXIST') TO <eu> ELSE UNASSIGN.
ASSERT sy-subrc = 4 AND <eu> IS NOT ASSIGNED.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `26a) Dynamic ASSIGN Statements (6) - Type Casting` ) ).
"As covered above, the CASTING addition of the ASSIGN statement
"has dynamic syntax elements.
DATA dobj_c_l5 TYPE c LENGTH 5 VALUE 'abcde'.
TYPES dtype_c_l2 TYPE c LENGTH 2.
FIELD-SYMBOLS <fs_dyn_as> TYPE data.
"A text literal with the name of a type is specified within the parentheses.
ASSIGN dobj_c_l5 TO <fs_dyn_as> CASTING TYPE ('DTYPE_C_L2').
out->write( data = <fs_dyn_as> name = `<fs_dyn_as1>` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `26b) Dynamic ASSIGN Statements (7) - Type Casting` ) ).
"Assigning a data object to a field symbol casting a dynamically
"specified type as also shown in the example above
TYPES clen5 TYPE c LENGTH 5.
DATA: dobj_c10 TYPE c LENGTH 10 VALUE '1234567890',
some_struct TYPE zdemo_abap_fli.
FIELD-SYMBOLS <casttype> TYPE data.
ASSIGN dobj_c10 TO <casttype> CASTING TYPE ('CLEN5'). "12345
ASSIGN dobj_c10 TO <casttype> CASTING LIKE some_struct-('CARRID'). "123
TYPES: c1 TYPE c LENGTH 1,
c3 TYPE c LENGTH 3,
c10 TYPE c LENGTH 10,
c20 TYPE c LENGTH 20,
str TYPE string.
DATA abc TYPE c LENGTH 26 VALUE 'abcdefghijklmnopqrstuvwxyz'.
DATA(typenames) = VALUE string_table( ( `C1` ) ( `C3` ) ( `C10` ) ( `C20` ) ( `NOPE` ) ( `STR` ) ).
DATA assignment_results TYPE string_table.
FIELD-SYMBOLS <c_like> TYPE clike.
LOOP AT typenames INTO DATA(typename).
TRY.
ASSIGN abc TO <c_like> CASTING TYPE (typename).
assignment_results = VALUE #( BASE assignment_results ( |Type: '{ typename }'; Assignment result: '{ <c_like> }'| ) ).
CATCH cx_root INTO DATA(error).
assignment_results = VALUE #( BASE assignment_results
( |Error! Exception raised: { cl_abap_typedescr=>describe_by_object_ref( error )->get_relative_name( ) }; | &&
|'{ error->get_text( ) }'| ) ).
ENDTRY.
ENDLOOP.
out->write( data = assignment_results name = `assignment_results` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `26c) Dynamic ASSIGN Statements (8) - Type Casting` ) ).
"Note: As covered further down CREATE DATA and ASSIGN statements have the HANDLE addition
"after which dynamically created types can be specified. A type description object is expected.
"Getting type description object
DATA(tdo_elem) = cl_abap_elemdescr=>get_c( 4 ).
ASSIGN dobj_c10 TO <casttype> CASTING TYPE HANDLE tdo_elem.
out->write( data = <casttype> name = `<casttype>` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `27) Accessing Structure Components Dynamically (1)` ) ).
"You can achieve the access using ASSIGN statements as shown above, or
"by statically specifying the structure and the (object) component selector
"followed by a character-like data object in parentheses.
"Write position
structure-('COL1') = 123.
it[ 1 ]-('COL1') = 456.
struc_ref->('COL1') = 789.
"Read position
"The example shows how you can retrieve the textual content of any component
"of any structure.
DATA(content_col2) = CONV string( structure-('COL1') ).
DATA(content_col3) = |{ structure-('COL3') }|.
DATA content_col1 LIKE structure-col1.
content_col1 = structure-('COL1').
DATA dref_comp TYPE REF TO data.
CREATE DATA dref_comp LIKE structure-('COL3').
dref_comp->* = structure-('COL3').
"If the component is not found, a catchable exception is raised.
TRY.
DATA(col_not_existent) = |{ structure-('COL123') }|.
CATCH cx_sy_assign_illegal_component INTO DATA(error_column).
out->write( error_column->get_text( ) ).
ENDTRY.
"Accessing components of generic structures dynamically,
"e.g. if you have a method parameter that is typed with the generic type
"data.
"The example uses a field symbol with the generic type data which is assigned
"a structure.
FIELD-SYMBOLS <gen> TYPE data.
ASSIGN structure TO <gen>.
"As in the examples above, specifying components dynamically is possible.
<gen>-('COL2') = `ABAP`.
DATA(gen_comp) = CONV string( <gen>-('COL2') ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `28) Accessing Structure Components Dynamically (2) - Excursion` ) ).
"In the following example, a structure is assigned to a field symbol that
"has a generic type. The components of the structure are accessed dynamically in
"a DO loop. The sy-index value is interpreted as the position of the component
"in the structure. Plus, using RTTI - as also shown further down - the component
"names are retrieved. Component names and the values are added to a string. As a
"prerequisite, all component values must be convertible to type string.
DATA struc2string TYPE string.
FIELD-SYMBOLS <strco> TYPE data.
ASSIGN structure TO <strco>.
IF sy-subrc = 0.
TRY.
DATA(components) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_data( <strco> ) )->components.
DO.
TRY.
DATA(co) = components[ sy-index ]-name.
struc2string = struc2string &&
COND #( WHEN sy-index <> 1 THEN `, ` ) &&
co && `: "` &&
<strco>-(sy-index) && `"`.
CATCH cx_sy_assign_illegal_component cx_sy_itab_line_not_found.
EXIT.
ENDTRY.
ENDDO.
CATCH cx_sy_move_cast_error.
ENDTRY.
ENDIF.
out->write( data = struc2string name = `struc2string` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `Dynamically Creating Data Objects at Runtime Using Dynamic Type Definitions` ) ).
out->write( |29) Miscellaneous Data Objects (1)\n\n| ).
"In an example above, anonymous data objects are created using static
"type definitions. In this example, anonymous data objects are created
"using a type determined at runtime.
"The values of an internal table of type string represent type names.
"The type name is used for the dynamic type specification in CREATE
"DATA statements that use various additions. The following is
"created dynamically: elementary data object, structure, internal
"table, data reference. For output purposes, the newly created data
"objects are assigned values.
"Note:
"- The NEW operator cannot be used here.
"- The creation of a data object based on a type description object is shown
" below (TYPE HANDLE addition).
"- Dynamic type specifications for ASSIGN statements together with the
" CASTING addition are shown above.
DATA(type_names) = VALUE string_table( ( `I` )
( `STRING` )
( `ZDEMO_ABAP_CARR` ) ).
DATA dataref TYPE REF TO data.
DATA some_str TYPE string VALUE `some string`.
DATA some_structure TYPE zdemo_abap_carr.
LOOP AT type_names REFERENCE INTO DATA(refwa).
out->write( |***** Loop iteration { sy-tabix }. Type: { refwa->* } *****| ).
CASE refwa->*.
WHEN `I`.
CREATE DATA dataref TYPE (refwa->*).
dataref->* = 123.
out->write( data = dataref->* name = `dataref->*` ).
out->write( |\n| ).
CREATE DATA dataref TYPE TABLE OF (refwa->*).
INSERT 1 INTO TABLE dataref->*.
INSERT 2 INTO TABLE dataref->*.
out->write( data = dataref->* name = `dataref->*` ).
out->write( |\n| ).
CREATE DATA dataref TYPE REF TO (refwa->*).
dataref->* = REF i( 456 ).
out->write( data = dataref->* name = `dataref->*` ).
out->write( |\n| ).
WHEN `STRING`.
CREATE DATA dataref TYPE (refwa->*).
dataref->* = `hello`.
out->write( data = dataref->* name = `dataref->*` ).
out->write( |\n| ).
CREATE DATA dataref TYPE TABLE OF (refwa->*).
INSERT `hello` INTO TABLE dataref->*.
INSERT `abap` INTO TABLE dataref->*.
out->write( data = dataref->* name = `dataref->*` ).
out->write( |\n| ).
CREATE DATA dataref TYPE REF TO (refwa->*).
dataref->* = REF string( `hi` ).
out->write( data = dataref->* name = `dataref->*` ).
out->write( |\n| ).
WHEN `ZDEMO_ABAP_CARR`.
CREATE DATA dataref TYPE (refwa->*).
SELECT SINGLE * FROM zdemo_abap_carr INTO @dataref->*.
out->write( data = dataref->* name = `dataref->*` ).
out->write( |\n| ).
CREATE DATA dataref TYPE TABLE OF (refwa->*).
SELECT * FROM zdemo_abap_carr INTO TABLE @dataref->* UP TO 3 ROWS.
out->write( data = dataref->* name = `dataref->*` ).
out->write( |\n| ).
CREATE DATA dataref TYPE REF TO (refwa->*).
SELECT SINGLE * FROM zdemo_abap_carr INTO NEW @dataref->*.
out->write( data = dataref->* name = `dataref->*` ).
out->write( |\n| ).
ENDCASE.
ENDLOOP.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `30) Elementary Data Object (2)` ) ).
"The example demonstrates the following:
"- The method call takes care of providing the name of a built-in data type and more
"- An elementary data object is created based on the data type
"- For demonstration purposes, RTTI (as shown further down in more detail) is used
" to check on the created data object by retrieving the type description.
DATA(b_type) = lcl_det_at_runtime=>get_builtin_type( ).
DATA ref_bt TYPE REF TO data.
TRY.
CASE b_type-builtin_type.
WHEN 'd' OR 'decfloat16' OR 'decfloat34' OR 'f' OR 'i'
OR 'string' OR 't' OR 'xstring'.
CREATE DATA ref_bt TYPE (b_type-builtin_type).
WHEN 'c' OR 'n' OR 'x'.
CREATE DATA ref_bt TYPE (b_type-builtin_type) LENGTH b_type-len.
WHEN 'p'.
CREATE DATA ref_bt TYPE p LENGTH b_type-len DECIMALS b_type-dec.
WHEN OTHERS.
out->write( `That didn't work.` ).
ENDCASE.
"Getting type information using RTTI
DATA(descr_builtin_type) = CAST cl_abap_elemdescr(
cl_abap_typedescr=>describe_by_data( ref_bt->* ) ).
out->write( |Built-in type determined at runtime: { b_type-builtin_type } | ).
out->write( |\n| ).
out->write( `Type information of created data object at runtime:` ).
out->write( |\n| ).
out->write( |\n| ).
out->write( data = descr_builtin_type name = `descr_builtin_type` ).
CATCH cx_root.
out->write( `Something went wrong.` ).
ENDTRY.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `31) Structure (3)` ) ).
"The example demonstrates the following:
"- The method call takes care of providing the name of a database table name.
"- A structured data object is created based on the dynamically determined type.
" It is used as target data object for a SELECT statement. As shown further
" down in more detail, clauses of SELECT statements can be specified
" dynamically.
"Retrieving table name
DATA(type4struc) = lcl_det_at_runtime=>get_dyn_table_name( ).
DATA ref_dynstruc TYPE REF TO data.
"Creating structured data object
CREATE DATA ref_dynstruc TYPE (type4struc).
"Dynamic specification of the FROM clause
SELECT SINGLE *
FROM (type4struc)
INTO @ref_dynstruc->*.
out->write( |Structured data type/database table name determined at runtime: { type4struc } | ).
out->write( |\n| ).
out->write( |\n| ).
out->write( data = ref_dynstruc->* name = `ref_dynstruc->*` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `32a) Internal Table (4)` ) ).
"The example demonstrates the following:
"- The method call takes care of providing the name of a database table name.
"- An internal table is created based on the dynamically determined type.
" It is used as target data object for a SELECT statement.
"- The UP TO ... ROWS addition is provided with a random number in the example.
"- AS in the example above, the SELECT statement includes the dynamic
" specification of the FROM clause.
"Retrieving table name
DATA(type_name) = lcl_det_at_runtime=>get_dyn_table_name( ).
DATA ref_n TYPE REF TO data.
"Creating internal table based on the type determined at runtime
CREATE DATA ref_n TYPE TABLE OF (type_name).
"Specifying random number for up to clause
DATA(random_upto) = cl_abap_random_int=>create(
seed = cl_abap_random=>seed( ) min = 2
max = 6 )->get_next( ).
"Dynamic specification of the FROM clause
SELECT *
FROM (type_name)
INTO TABLE @ref_n->*
UP TO @random_upto ROWS.
out->write( |Table/type name determined at runtime: { type_name } | ).
out->write( |\n| ).
out->write( |At most, { random_upto } lines should have been read from the database table.| ).
out->write( |\n| ).
out->write( |\n| ).
out->write( data = ref_n->* name = `ref_n->*` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `32b) Creating Anonymous Data Objects by Specifying the Type Dynamically (Summarizing Syntax Patterns)` ) ).
"------------ Specifying a type name dynamically ------------
"Anonymous data objects are created using a type determined at
"runtime. In this case, the name of the data type is specified
"dynamically.
"Note that the NEW operator cannot be used here.
"Data reference variable used for the examples
DATA data_ref TYPE REF TO data.
"Example types and data objects
"Elementary type and data object
TYPES t_c3 TYPE c LENGTH 3.
DATA c3 TYPE c LENGTH 3.
"Structured type and data object
TYPES t_fli_struc TYPE zdemo_abap_fli.
DATA fli_struc TYPE zdemo_abap_fli.
"Table type and internal table
TYPES t_carr_tab TYPE SORTED TABLE OF zdemo_abap_carr WITH UNIQUE KEY carrid.
DATA carr_tab TYPE TABLE OF zdemo_abap_carr WITH EMPTY KEY.
"Reference type and data reference variable
TYPES t_str_ref TYPE REF TO string.
DATA str_ref TYPE REF TO string.
"----- Pattern: TYPE (typename) ... -----
"Creating an elementary data object
"Specifying a literal for the dynamic type name (used in most of the
"following examples)
CREATE DATA data_ref TYPE ('T_C3').
"Specifying a named data object
DATA(c3_type) = 'T_C3'.
CREATE DATA data_ref TYPE (c3_type).
"Structured data object
CREATE DATA data_ref TYPE ('T_FLI_STRUC').
"Internal table
CREATE DATA data_ref TYPE ('T_CARR_TAB').
"Data reference
CREATE DATA data_ref TYPE ('T_STR_REF').
"----- Pattern: TYPE ... TABLE OF (typename) ... -----
"Creating internal tables
CREATE DATA data_ref TYPE TABLE OF ('STRING').
CREATE DATA data_ref TYPE TABLE OF ('T_FLI_STRUC') WITH EMPTY KEY.
"Specifying the structured type dynamically, but the key values statically
CREATE DATA data_ref TYPE SORTED TABLE OF ('ZDEMO_ABAP_CARR') WITH UNIQUE KEY carrid.
"Specifying the structured type and the key values dynamically
"An internal table such as the following should be created by dynamically
"specifying the type and keys dynamically. The keys are specified in lines
"of an internal table with character-like line type.
DATA itab_compare TYPE SORTED TABLE OF zdemo_abap_fli WITH UNIQUE KEY carrid connid fldate.
DATA(key_table) = VALUE string_table( ( `CARRID` ) ( `CONNID` ) ( `FLDATE` ) ).
CREATE DATA data_ref TYPE SORTED TABLE OF ('ZDEMO_ABAP_FLI') WITH UNIQUE KEY (key_table).
"----- Pattern: TYPE REF TO (typename) -----
"Creating data reference variables
CREATE DATA data_ref TYPE REF TO ('STRING').
CREATE DATA data_ref TYPE REF TO ('T_C3').
CREATE DATA data_ref TYPE REF TO ('T_FLI_STRUC').
CREATE DATA data_ref TYPE REF TO ('T_CARR_TAB').
"----- Pattern: TYPE LINE OF (typename) -----
"Creating structures based on table types
CREATE DATA data_ref TYPE LINE OF ('T_CARR_TAB').
"----- Pattern: LIKE struc-(dobjname) -----
CREATE DATA data_ref LIKE fli_struc-('CARRID').
"----- Pattern: TYPE (absolute_name) -----
CREATE DATA data_ref TYPE ('\TYPE=STRING').
"Getting an absolute type name; see more information further down
DATA(absolute_name) = cl_abap_typedescr=>describe_by_name( 'ZDEMO_ABAP_CARR' )->absolute_name.
CREATE DATA data_ref TYPE (absolute_name).
"----- Pattern: TYPE HANDLE type_description_object -----
"Getting a type description object. Find more information about RTTI below.
DATA(typedescrobj) = cl_abap_elemdescr=>get_c( 4 ). "type c length 4
CREATE DATA data_ref TYPE HANDLE typedescrobj.
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `33a) Creating Instances of Classes by Specifying the Type Dynamically` ) ).
DATA oref_dyn TYPE REF TO object.
CREATE OBJECT oref_dyn TYPE ('ZCL_DEMO_ABAP_OBJECTS').
DATA cl TYPE string VALUE `ZCL_DEMO_ABAP_OBJECTS`.
CREATE OBJECT oref_dyn TYPE (cl).
"Specifying a wrong/non-existent type name
TRY.
CREATE OBJECT oref_dyn TYPE ('THIS_CLASS_DOES_NOT_EXIST').
CATCH cx_sy_create_object_error.
ENDTRY.
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `33b) Absolute Type Names for Dynamically Specifying Types` ) ).
"In addition to character-like data objects for the type name specified within the
"parentheses, you can also use absolute type names for statements such as CREATE DATA.
"The absolute type name is retrieved using RTTI. See more on RTTI further down.
"Type to refer to
TYPES type4abs TYPE p LENGTH 4 DECIMALS 3.
"Data and object reference variables
DATA dref4abs TYPE REF TO data.
DATA oref4abs TYPE REF TO object.
"Getting absolute names using RTTI
DATA(abs_name_type) = cl_abap_typedescr=>describe_by_name( 'TYPE4ABS' )->absolute_name.
DATA(abs_name_cl) = cl_abap_typedescr=>describe_by_name( 'ZCL_DEMO_ABAP_DYNAMIC_PROG' )->absolute_name.
"Data references
""Named data object holding the absolute name
CREATE DATA dref4abs TYPE (abs_name_type).
"Unnamed data object
CREATE DATA dref4abs TYPE ('\TYPE=STRING').
"Object references
"Named data object
CREATE OBJECT oref4abs TYPE (abs_name_cl).
"Unnamed data object
CREATE OBJECT oref4abs TYPE ('\CLASS=ZCL_DEMO_ABAP_DYNAMIC_PROG').
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `Dynamically Specifying Components/Clauses in Statements for Processing Internal Tables with ...` ) ).
out->write( |34a) SORT (1)\n\n| ).
"A field is determined at runtime on whose basis a sorting is done on an
"internal table.
DATA(field_name) = lcl_det_at_runtime=>get_dyn_field( ).
SELECT *
FROM zdemo_abap_carr
ORDER BY carrid
INTO TABLE @DATA(carr_itab)
UP TO 3 ROWS.
SORT carr_itab BY (field_name).
out->write( |Field name determined at runtime | &&
|by which the sorting was done: { field_name } | ).
out->write( |\n| ).
out->write( |\n| ).
out->write( data = carr_itab name = `carr_itab` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `34b) SORT (2)` ) ).
"Sorting by dynamically specified components in a sort table, i. e.an
"internal table of type abap_sortorder_tab.
"Notes:
"- Each line of this sort table specifies a component of the sort key.
"- If this table is init_sortial, there is no sorting.
"- The sort priorit_sorty is based on the order of the lines in the sort table.
TYPES: BEGIN OF struct,
comp1 TYPE i,
comp2 TYPE string,
comp3 TYPE c LENGTH 3,
END OF struct.
DATA it_sort TYPE TABLE OF struct WITH EMPTY KEY.
it_sort = VALUE #( ( comp1 = 1 comp2 = `B` comp3 = 'a' )
( comp1 = 1 comp2 = `A` comp3 = 'b' )
( comp1 = 2 comp2 = `D` comp3 = 'c' )
( comp1 = 2 comp2 = `C` comp3 = 'd' )
( comp1 = 3 comp2 = `F` comp3 = 'e' )
( comp1 = 3 comp2 = `E` comp3 = 'f' ) ).
DATA(it_sort_original) = it_sort.
"Note: The line type is abap_sortorder.
DATA(sort) = VALUE abap_sortorder_tab( ).
"No sorting because the sort table is init_sortial.
SORT it_sort BY (sort).
out->write( data = it_sort name = `it_sort` ).
out->write( |\n| ).
it_sort = it_sort_original.
"Note: Ascending is the default sort order. The following example flags
"the descending sort order explicit_sortly.
sort = VALUE abap_sortorder_tab( ( name = `COMP1` descending = 'X' ) ).
SORT it_sort BY (sort).
out->write( data = it_sort name = `it_sort` ).
out->write( |\n| ).
it_sort = it_sort_original.
sort = VALUE abap_sortorder_tab( ( name = `COMP1` descending = '' )
( name = `COMP2` descending = 'X' ) ).
SORT it_sort BY (sort).
out->write( data = it_sort name = `it_sort` ).
out->write( |\n| ).
it_sort = it_sort_original.
"Sort priorit_sorty based on the order of lines in the sort table
"In this example, the values of comp3 are set up so that a clear
"sort order is determined. Since the component is specified first in the
"sort table, this sorting has priorit_sorty. Note the values of comp2 in the
"result table.
sort = VALUE abap_sortorder_tab( ( name = `COMP3` descending = 'X' )
( name = `COMP2` descending = 'X' ) ).
SORT it_sort BY (sort).
out->write( data = it_sort name = `it_sort` ).
out->write( |\n| ).
"Specifying an invalid component name raises an exception
sort = VALUE abap_sortorder_tab( ( name = `XYZ` descending = 'X' ) ).
TRY.
SORT it_sort BY (sort).
CATCH cx_sy_dyn_table_ill_comp_val INTO DATA(err).
ENDTRY.
ASSERT err IS NOT INITIAL.
it_sort = it_sort_original.
"Specifying an expression/functional method call whose result is a sort
"table of type abap_sortorder_tab
"In this case, BY is followed by the expression/functional method call,
"not enclosed in parentheses.
"The example shows expressions wit_sorth sort tables created inline
SORT it_sort BY VALUE abap_sortorder_tab( ( name = `COMP1` descending = 'X' ) ).
out->write( data = it_sort name = `it_sort` ).
out->write( |\n| ).
it_sort = it_sort_original.
DATA(compnames) = VALUE string_table( ( `comp1` ) ( `comp2` ) ).
SORT it_sort BY VALUE abap_sortorder_tab( FOR sortwa IN compnames ( name = condense( to_upper( sortwa ) ) ) ).
out->write( data = it_sort name = `it_sort` ).
out->write( |\n| ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `35) READ TABLE (1)` ) ).
"Dynamic key specification in READ TABLE statements
TYPES: BEGIN OF s,
comp TYPE string,
value TYPE string,
END OF s.
TYPES comps_type TYPE TABLE OF s WITH EMPTY KEY.
"Providing components and values for READ TABLE statement
DATA(comps) = VALUE comps_type( ( comp = `CARRID` value = `LH` )
( comp = `CONNID` value = `0555` )
( comp = `SEATSOCC` value = `115` )
( comp = `CARRID` value = `XY` ) ). "not found
SELECT *
FROM zdemo_abap_fli
INTO TABLE @DATA(itab_read_tab_dyn).
LOOP AT comps INTO DATA(wa_comps).
READ TABLE itab_read_tab_dyn
INTO DATA(read_line)
WITH KEY (wa_comps-comp) = wa_comps-value.
IF sy-subrc = 0.
out->write( data = wa_comps-comp name = `wa_comps-comp` ).
out->write( |\n| ).
out->write( data = wa_comps-value name = `wa_comps-value` ).
out->write( |\n| ).
out->write( data = read_line name = `read_line` ).
out->write( |\n| ).
CLEAR read_line.
ELSE.
out->write( data = wa_comps-comp name = `wa_comps-comp` ).
out->write( |\n| ).
out->write( data = wa_comps-value name = `wa_comps-value` ).
out->write( |\n| ).
out->write( `Line not found.` ).
out->write( |\n| ).
ENDIF.
ENDLOOP.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `36) READ TABLE (2)` ) ).
"Data objects and types to work with
TYPES: BEGIN OF demo_struct,
col1 TYPE i,
col2 TYPE string,
col3 TYPE string,
END OF demo_struct.
"Standard table and specification of primary and secondary table key
DATA itab TYPE TABLE OF demo_struct
WITH NON-UNIQUE KEY col1
WITH UNIQUE SORTED KEY sk COMPONENTS col2.
TYPES itab_type LIKE itab.
DATA itab_ref TYPE TABLE OF REF TO demo_struct WITH EMPTY KEY.
itab = VALUE #( ( col1 = 1 col2 = `aaa` col3 = `zzz` )
( col1 = 2 col2 = `bbb` col3 = `yyy` )
( col1 = 3 col2 = `ccc` col3 = `xxx` ) ).
itab_ref = VALUE #( ( NEW demo_struct( col1 = 1 col2 = `aaa` col3 = `zzz` ) ) ).
"Notes
"- In statements using key specifications, secondary table key names (or alias names)
" are usually specified. Also the primary table key using the predefined name
" primary_key or its alias name can be used.
"- Many of the following statements provide similar additions offering dynamic
" specifications, such as USING KEY and dynamic component name specifications.
"Reading by specifying keys dynamically
"Implicitly specifying the table key values in a work area (USING KEY addition)
DATA(wa_read) = VALUE demo_struct( col2 = `aaa` ).
READ TABLE itab FROM wa_read USING KEY ('SK') REFERENCE INTO DATA(read_ref).
"Explicitly specifying the key and key values (TABLE KEY addition)
"The component names can also be specified dynamically (which is done in most of the
"following examples for demonstration purposes). Note that each component of the table
"key must be specified.
READ TABLE itab WITH TABLE KEY ('SK') COMPONENTS ('COL2') = `aaa` REFERENCE INTO read_ref.
"Specifying the predefined name primary_key explicitly and dynamically
READ TABLE itab WITH TABLE KEY ('PRIMARY_KEY') COMPONENTS ('COL1') = 1 REFERENCE INTO read_ref.
"If the addition COMPONENTS is not specified, the primary table key is implicitly used.
READ TABLE itab WITH TABLE KEY ('COL1') = 1 REFERENCE INTO read_ref.
"Reading using a free key (WITH KEY addition)
READ TABLE itab WITH KEY ('COL3') = `yyy` REFERENCE INTO read_ref.
"The addition can also be used by specifying a secondary table key name
READ TABLE itab WITH KEY ('SK') COMPONENTS ('COL2') = `ccc` REFERENCE INTO read_ref.
"Reading based on a table index (INDEX addition)
"Not using the addition USING KEY means reading from the primary table index.
READ TABLE itab INDEX 1 USING KEY ('SK') REFERENCE INTO read_ref.
"More dynamic specification options when specifying the target as work area
"(COMPARING/TRANSPORTING additions)
"TRANSPORTING: Specifying which components shall be respected
READ TABLE itab INDEX 1 INTO DATA(workarea) TRANSPORTING ('COL1') ('COL3').
"COMPARING: If the content of the compared components is identical, sy-subrc is set
"to 0, and otherwise to 2. The line found is assigned to the work area independently
"of the result of the comparison.
workarea-('COL3') = `uvw`.
READ TABLE itab INDEX 1 INTO workarea COMPARING ('COL3') TRANSPORTING ('COL1') ('COL3').
IF sy-subrc <> 0.
...
ENDIF.
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `37) Table Expressions` ) ).
"Similar to READ TABLE statements, you can specify table lines with 3 alternatives:
"index read, read using free key, table key
"Also there, dynamic specifications are possible regarding the key specifications.
"Reading based on index with dynamic key specifications
"Specifying the secondary table index of a sorted secondary key
DATA(wa_te1) = itab[ KEY ('SK') INDEX 1 ].
"Reading using a free key, the keys are specified dynamically
DATA(wa_te2) = itab[ ('COL2') = `bbb` ('COL3') = `yyy` ].
"Reading using a table key
"Specyfing the table key explicitly
"Note: Unlike READ TABLE statements, the name of the table key must be specified. The
"addition COMPONENTS can be omitted.
"In the following example, the component names are also specified dynamically.
DATA(wa_te3) = itab[ KEY ('SK') ('COL2') = `ccc` ].
"Specifying the COMPONENTS addition explicitly
DATA(wa_te4) = itab[ KEY ('PRIMARY_KEY') COMPONENTS ('COL1') = 1 ].
"Accessing components
"As shown above, chaininings with the (object) component selector are possible.
"The examples use index access and write positions.
itab[ 1 ]-('COL2') = `jkl`.
itab_ref[ 1 ]->('COL2') = `mno`.
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `38) LOOP (1)` ) ).
"Dynamic specification of the key in LOOP statements
"In the example, the loop can be executed with the entries 'SKEY' and 'PRIMARY_KEY'.
"This is not case sensitive. Any other entries produce a runtime error.
DATA(keys) = VALUE string_table( ( `PRIMARY_KEY` ) ( `SKEY` ) ).
DATA itab_loop TYPE TABLE OF i
WITH NON-UNIQUE KEY primary_key COMPONENTS table_line
WITH NON-UNIQUE SORTED KEY skey COMPONENTS table_line.
itab_loop = VALUE #( ( 3 ) ( 2 ) ( 1 ) ).
DATA itab_dyn_key LIKE itab_loop.
LOOP AT keys INTO DATA(k).
LOOP AT itab_loop INTO DATA(wa_lo) USING KEY (k).
APPEND wa_lo TO itab_dyn_key.
ENDLOOP.
out->write( |Loop over internal table using key "{ k }".| ).
out->write( |\n| ).
out->write( data = itab_dyn_key name = `itab_dyn_key` ).
out->write( |\n| ).
CLEAR itab_dyn_key.
ENDLOOP.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `39) LOOP (2)` ) ).
"USING KEY addition: Overriding the standard order determined by the table category
LOOP AT itab REFERENCE INTO DATA(ref) USING KEY ('SK').
...
ENDLOOP.
"When the primary table key is specified, the loop behaves as if it was not specified.
"So, the following statement corresponds to the one below.
LOOP AT itab REFERENCE INTO ref USING KEY ('PRIMARY_KEY').
...
ENDLOOP.
LOOP AT itab REFERENCE INTO ref.
...
ENDLOOP.
"Dynamic WHERE condition
"You can specify a character-like data object or a standard table with character-like
"line type.
DATA(cond_loop) = `COL1 > 1`.
LOOP AT itab REFERENCE INTO ref WHERE (cond_loop).
...
ENDLOOP.
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `40) INSERT` ) ).
"The USING KEY addition (which accepts a dynamic specification) affects the order in which lines are inserted.
"Result of the following example when using the ...
"- secondary table key: order of itab entries 5 ... /4 ... /...
"- primary table key: order of itab entries 4 ... /5 ... /...
INSERT LINES OF VALUE itab_type( ( col1 = 4 col2 = `eee` col3 = `www` )
( col1 = 5 col2 = `ddd` col3 = `vvv` ) )
USING KEY ('SK')
"USING KEY ('PRIMARY_KEY')
INTO itab INDEX 1.
"Excursion: Using LOOP AT statements with the USING KEY addition
"and exploring the table index
"Declaring demo tables to hold the internal table entries
DATA it_seckey_idx TYPE TABLE OF demo_struct WITH EMPTY KEY.
DATA it_primekey_idx LIKE it_seckey_idx.
"Visualizing the secondary table index
LOOP AT itab INTO DATA(wa_sk) USING KEY ('SK').
APPEND wa_sk TO it_seckey_idx.
ENDLOOP.
out->write( data = it_seckey_idx name = `it_seckey_idx` ).
out->write( |\n| ).
"Visualizing the primary table index
LOOP AT itab INTO DATA(wa_pk) USING KEY ('PRIMARY_KEY').
APPEND wa_pk TO it_primekey_idx.
ENDLOOP.
out->write( data = it_primekey_idx name = `it_primekey_idx` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `41) MODIFY (1)` ) ).
"Dynamic WHERE condition in MODIFY statements
"Note:
"- The addition WHERE can only be specified together with the addition TRANSPORTING.
"- Invalid logical expressions raise an exception from the class CX_SY_ITAB_DYN_LOOP.
TYPES:
BEGIN OF line,
col1 TYPE c LENGTH 1,
col2 TYPE i,
END OF line.
DATA itab_mod_tab_dyn TYPE SORTED TABLE OF line
WITH UNIQUE KEY col1.
itab_mod_tab_dyn = VALUE #( ( col1 = 'A' col2 = 1 )
( col1 = 'B' col2 = 10 )
( col1 = 'C' col2 = 100 ) ).
out->write( `Internal table content before modifications:` ).
out->write( |\n| ).
out->write( data = itab_mod_tab_dyn name = `itab_mod_tab_dyn` ).
out->write( |\n| ).
"Providing conditions for MODIFY statement
DATA(conditions) = VALUE string_table( ( `col2 < 5` )
( `col2 = 10` )
( `colxyz > 50` ) ). "to fail
LOOP AT itab_mod_tab_dyn INTO DATA(wa_mod_dyn).
TRY.
DATA(condition) = conditions[ sy-tabix ].
MODIFY itab_mod_tab_dyn
FROM VALUE line( col2 = wa_mod_dyn-col2 * 2 )
TRANSPORTING col2
WHERE (condition).
CATCH cx_sy_itab_dyn_loop.
out->write( |Invalid WHERE condition "{ condition }".| ).
ENDTRY.
ENDLOOP.
out->write( `Internal table content after modifications:` ).
out->write( |\n| ).
out->write( data = itab_mod_tab_dyn name = `itab_mod_tab_dyn` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `42) MODIFY (2)` ) ).
"In the following example, a line is modified based on a work area and a table key.
"The component col1 is left out from the work area intentionally.
"If the primary table key was used, the value of sy-subrc would be 4, and no modification was done.
"The optional addition transporting is specified to denote what should be modified. In this example,
"the component is also specified dynamically.
MODIFY TABLE itab FROM VALUE #( col2 = `bbb` col3 = `uuu` ) USING KEY ('SK') TRANSPORTING ('COL3').
"In the following example, a line is modified based on a work area, an index specification and a
"table key.
"INDEX can also be positioned after FROM.
MODIFY itab INDEX 2 USING KEY ('SK') FROM VALUE #( col3 = `ttt` ) TRANSPORTING ('COL3').
"Dynamic WHERE clause (only to be used with the TRANSPORTING addition)
"The USING KEY addition is also possible. Check the ABAP Keyword Documentation
"for special rules that apply.
DATA(cond_mod) = `COL1 < 3`.
MODIFY itab FROM VALUE #( col3 = `sss` ) TRANSPORTING ('COL3') WHERE (cond_mod).
out->write( data = itab name = `itab` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `43) DELETE (1)` ) ).
"Dynamic WHERE condition in DELETE statements
DATA itab_del_tab_dyn TYPE TABLE OF i WITH EMPTY KEY
WITH NON-UNIQUE SORTED KEY skey COMPONENTS table_line.
itab_del_tab_dyn = VALUE #( ( 100 )
( 200 )
( 300 )
( 400 )
( 500 )
( 600 ) ).
out->write( `Internal table content before modifications:` ).
out->write( |\n| ).
out->write( data = itab_del_tab_dyn name = `itab_del_tab_dyn` ).
out->write( |\n| ).
DO 3 TIMES.
TRY.
CASE sy-index.
WHEN 1.
condition = `table_line <= 200`.
WHEN 2.
condition = `table_line >= 500`.
WHEN 3.
condition = `col1 = 600`.
ENDCASE.
DELETE itab_del_tab_dyn
USING KEY skey
WHERE (condition).
out->write( |Condition: { condition }| ).
out->write( |\n| ).
out->write( data = itab_del_tab_dyn name = `itab_del_tab_dyn` ).
out->write( |\n| ).
CATCH cx_sy_itab_dyn_loop.
out->write( |Invalid WHERE condition "{ condition }".| ).
out->write( |\n| ).
ENDTRY.
ENDDO.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `44) DELETE (2)` ) ).
"A single line or multipled lines can be deleted.
"Note that DELETE ADJACENT DUPLICATES statements can also be specified using
"dynamic parts.
"Deleting based on a dynamically specified table key
"The values can be declared either implicitly in a work area after FROM or explicitly
"by listing the components of the table key after TABLE KEY.
"If the USING KEY addition is not specified, the primary table key is used by default.
DELETE TABLE itab FROM VALUE #( col2 = `eee` col3 = `www` ) USING KEY ('SK').
"Each component of the table key must be listed.
DELETE TABLE itab WITH TABLE KEY ('SK') COMPONENTS ('COL2') = `ddd`.
"Deleting based on the table index
DELETE itab INDEX 1 USING KEY ('SK').
"Deleting multiple lines and specifying the WHERE conditions dynamically
"The USING KEY addition is also possible.
DATA(condition_tab) = VALUE string_table( ( `COL1 < 3` )
( `OR` )
( `COL3 = ``www``` ) ).
DELETE itab WHERE (condition_tab).
out->write( data = itab name = `itab` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `Dynamically Specifying Clauses in ABAP SQL SELECT Statements` ) ).
out->write( |45) SELECT List\n\n| ).
"In the example, the SELECT list that is used in a SELECT statement is
"determined at runtime.
"Note: Check out the CL_ABAP_DYN_PRG class, which supports dynamic programming by
"checking the validity of dynamic specifications. See an example below.
DATA(select_list) = lcl_det_at_runtime=>get_dyn_select_list( ).
DATA sel_table TYPE TABLE OF zdemo_abap_flsch WITH EMPTY KEY.
SELECT (select_list)
FROM zdemo_abap_flsch
ORDER BY carrid
INTO CORRESPONDING FIELDS OF TABLE @sel_table
UP TO 3 ROWS.
out->write( |SELECT list determined at runtime: { select_list } | ).
out->write( |\n| ).
out->write( |\n| ).
out->write( data = sel_table name = `sel_table` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `46) FROM Clause` ) ).
"In the example, the FROM clause that is used in a SELECT statement is
"determined at runtime. Here, the number of entries of a database table
"is counted.
DATA(tab_name) = lcl_det_at_runtime=>get_dyn_table_name( ).
SELECT COUNT(*)
FROM (tab_name)
INTO @DATA(count).
out->write( |Table name determined at runtime: { tab_name } | ).
out->write( |\n| ).
out->write( |The table { tab_name } has { count } entries.| ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `47) WHERE Clause` ) ).
"In the example, the WHERE clause that is used in a SELECT statement is
"determined at runtime. Here, the WHERE clause is based on a string
"table.
DATA(where_clause) = lcl_det_at_runtime=>get_dyn_where_clause( ).
SELECT *
FROM zdemo_abap_flsch
WHERE (where_clause)
ORDER BY carrid
INTO TABLE @DATA(where_tab)
UP TO 5 ROWS.
out->write( |WHERE clause determined at runtime:| ).
out->write( |\n| ).
out->write( data = where_clause name = `where_clause` ).
out->write( |\n| ).
out->write( |\n| ).
out->write( data = where_tab name = `where_tab` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `48) ORDER BY Clause` ) ).
DATA(order_by) = 'FLTIME'.
SELECT *
FROM zdemo_abap_flsch
ORDER BY (order_by)
INTO TABLE @DATA(order_by_tab)
UP TO 5 ROWS.
out->write( |ORDER BY clause determined at runtime:| ).
out->write( |\n| ).
out->write( data = order_by name = `order_by` ).
out->write( |\n| ).
out->write( |\n| ).
out->write( data = order_by_tab name = `order_by_tab` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `49) Multiple Dynamically Specified Clauses in an ABAP SQL SELECT Statement` ) ).
"In this example, multiple clauses in a SELECT statement are
"determined at runtime to demonstrate the rich variety of possibilities.
"Note: The rows and target table specifications are not real dynamic
"specifications in the SELECT statement in the sense of syntax elements
"enclosed by parentheses. Here, they are just included to provide some
"more 'dynamic' touch of the statement :)
"Getting all clauses of the SELECT statement
DATA(dyn_syntax_elem) = lcl_det_at_runtime=>get_dyn_syntax_elements( ).
IF dyn_syntax_elem-table IS NOT INITIAL
AND dyn_syntax_elem-select_list IS NOT INITIAL
AND dyn_syntax_elem-where_clause IS NOT INITIAL
AND dyn_syntax_elem-order_by IS NOT INITIAL
AND dyn_syntax_elem-target IS BOUND
AND dyn_syntax_elem-rows IS NOT INITIAL.
out->write( `Dynamically determined syntax elements:` ).
out->write( |\n| ).
out->write( data = dyn_syntax_elem name = `dyn_syntax_elem` ).
out->write( |\n| ).
SELECT (dyn_syntax_elem-select_list)
FROM (dyn_syntax_elem-table)
WHERE (dyn_syntax_elem-where_clause)
ORDER BY (dyn_syntax_elem-order_by)
INTO CORRESPONDING FIELDS OF TABLE @dyn_syntax_elem-target->*
UP TO @dyn_syntax_elem-rows ROWS.
out->write( data = dyn_syntax_elem-target->* name = `dyn_syntax_elem-target->*` ).
ELSE.
out->write( `There's an issue with syntax elements.` ).
ENDIF.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `50) Dynamic Specifications in Other ABAP SQL Statements` ) ).
"Creating a structure to be inserted into the database table
DATA(table) = 'ZDEMO_ABAP_CARR'.
DATA(wherecl) = 'CARRID = ''ZZ'''.
SELECT SINGLE *
FROM (table)
INTO NEW @DATA(refstruc).
refstruc->('CARRID') = 'ZZ'.
INSERT (table) FROM @refstruc->*.
SELECT SINGLE *
FROM (table)
WHERE (wherecl)
INTO NEW @DATA(res1).
out->write( data = res1->* name = `res1->*` ).
out->write( |\n| ).
refstruc->('CARRNAME') = 'ZZ Airlines'.
UPDATE (table) FROM @refstruc->*.
SELECT SINGLE *
FROM (table)
WHERE (wherecl)
INTO NEW @DATA(res2).
out->write( data = res2->* name = `res2->*` ).
out->write( |\n| ).
refstruc->('CURRCODE') = 'GBP'.
MODIFY (table) FROM @refstruc->*.
SELECT SINGLE *
FROM (table)
WHERE (wherecl)
INTO NEW @DATA(res3).
out->write( data = res3->* name = `res3->*` ).
out->write( |\n| ).
DELETE FROM (table) WHERE (wherecl).
SELECT *
FROM (table)
INTO TABLE NEW @DATA(res4).
out->write( data = res4->* name = `res4->*` ).
out->write( |\n| ).
"--------------------- Dynamic UPDATE ... SET ... statement ---------------------
"Inserting demo data into the database table to work with
TYPES carr_tab TYPE TABLE OF zdemo_abap_carr WITH EMPTY KEY.
INSERT ('ZDEMO_ABAP_CARR') FROM TABLE @( VALUE carr_tab( ( carrid = 'WX' carrname = 'WX Airways' )
( carrid = 'XY' carrname = 'Air XY' )
( carrid = 'YZ' carrname = 'YZ Airlines' ) ) ).
"Note that erroneous dynamic specifications can lead to runtime errors
"In the following example, the final inverted comma is missing in the dynamic
"set clause.
DATA(set_clause) = `CURRCODE = 'EUR`.
DATA(where_cl) = `CARRID = 'WX' OR CARRID = 'XY' OR CARRID = 'YZ'`.
TRY.
UPDATE ('ZDEMO_ABAP_CARR') SET (set_clause) WHERE (where_cl).
CATCH cx_sy_dynamic_osql_syntax INTO DATA(err_sql).
DATA(error_text) = err_sql->get_text( ).
ENDTRY.
"Correcting the dynamic specification
"The example sets the value for a component for all entries.
"The example additionally specifies a (dynamic) WHERE clause
"to restrict the range of entries where the update is performed.
"The database table is also specified dynamically.
set_clause = `CURRCODE = 'EUR'`.
UPDATE ('ZDEMO_ABAP_CARR') SET (set_clause) WHERE (where_cl).
SELECT *
FROM ('ZDEMO_ABAP_CARR')
INTO TABLE NEW @DATA(res5).
out->write( data = res5->* name = `res5->*` ).
out->write( |\n| ).
"--------------------- Dynamic UPDATE ... INDICATORS ... statement ---------------------
"The statement changes values of specific fields without overwriting existing values of
"other fields.
"Notes on the example:
"- A structured type is created with the WITH INDICATORS addition.
"- An internal table from which to update a database table is created.
"- The table includes the indicator structure comp_ind.
"- The table is populated, and two components are flagged as
" to be updated.
"- Other fields remain unchanged. Note that key fields must be
" included in ind_tab (indicator setting for key fields has
" no effect).
"- The UPDATE statement includes dynamically specified
" indicator syntax. Additionally, the database table is specified
" dynamically.
"Structured type with WITH INDICATORS addition
TYPES ind_wa TYPE zdemo_abap_carr WITH INDICATORS comp_ind TYPE abap_bool.
DATA ind_tab TYPE TABLE OF ind_wa.
"Filling internal table; only CURRCODE and URL should be updated
ind_tab = VALUE #( ( carrid = 'WX'
carrname = 'WX Airways'
currcode = 'USD'
url = 'some_url_wx'
comp_ind-currcode = abap_true
comp_ind-url = abap_true )
( carrid = 'XY'
carrname = 'Air XY'
currcode = 'USD'
url = 'some_url_xy'
comp_ind-currcode = abap_true
comp_ind-url = abap_true )
( carrid = 'YZ'
carrname = 'YZ Airlines'
currcode = 'USD'
url = 'some_url_yz'
comp_ind-currcode = abap_true
comp_ind-url = abap_true ) ).
DATA(dyn_ind) = `SET STRUCTURE comp_ind`.
UPDATE ('ZDEMO_ABAP_CARR') FROM TABLE @ind_tab INDICATORS (dyn_ind).
SELECT *
FROM ('ZDEMO_ABAP_CARR')
INTO TABLE NEW @DATA(res6).
out->write( data = res6->* name = `res6->*` ).
DELETE FROM ('ZDEMO_ABAP_CARR') WHERE (where_cl).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `51) Validating Input for Dynamic Specifications` ) ).
"The following examples use methods of the CL_ABAP_DYN_PRG class, which supports
"dynamic programming by checking the validity for dynamic specifications.
"Check out the class documentation for more information.
"In the following example, several table names are provided in a string table that
"is looped over. Some wrong table names are intentionally included.
"You can provide the "packages" formal parameter with a table that contains
"the names of packages in which the tables should be included. The
"assumption is that you imported the repository into the package
"specified. If you imported it into a package with a different name,
"change the value accordingly. Otherwise, none of the tables is found.
out->write( `****** Checking if the input is a database table name, inlcuded in given packages ******` ).
out->write( |\n| ).
DATA(table_names) = VALUE string_table( ( `ZDEMO_ABAP_CARR` )
( `ZDEMO_ABAP_FLI` )
( `NO_DBTAB` )
( `ZDEMO_ABAP_FLSCH` )
( `ZDEMO_ABAP_FLI2` )
( `WRONG_DBTAB` ) ).
DATA(check_packages) = VALUE string_hashed_table( ( `ZABAP_CHEAT_SHEETS` ) ).
DATA dbtab TYPE string.
LOOP AT table_names INTO DATA(wa_tab).
TRY.
dbtab = cl_abap_dyn_prg=>check_table_name_tab( val = to_upper( wa_tab )
packages = check_packages ).
SELECT SINGLE * FROM (dbtab) INTO NEW @DATA(ref_wa).
out->write( |Result for { wa_tab }:| ).
out->write( ref_wa->* ).
out->write( |\n| ).
CATCH cx_abap_not_a_table cx_abap_not_in_package INTO DATA(e).
out->write( |Result for { wa_tab }:| ).
out->write( e->get_text( ) ).
out->write( |\n| ).
ENDTRY.
ENDLOOP.
"In the following example, the check_allowlist method is used to check
"whether the input is allowed or not. Similar to above, an internal
"is filled with demo input. Here, a parameter is used that expects
"a comma-separated list of allowed values.
out->write( `****** Checking if input is allowed or not ******` ).
out->write( |\n| ).
DATA(to_be_checked) = VALUE string_table( ( `A` )
( `B` )
( `C` )
( `D` )
( `HALLO` )
( `ABAP` ) ).
LOOP AT to_be_checked INTO DATA(chk).
TRY.
DATA(value) = cl_abap_dyn_prg=>check_allowlist( val = chk
allowlist_str = `A,B,C,ABAP` ).
out->write( |{ value } is allowed.| ).
CATCH cx_abap_not_in_allowlist INTO e.
out->write( e->get_text( ) ).
ENDTRY.
ENDLOOP.
out->write( |\n| ).
"The following example is similar to the one above. However, in this case,
"a parameter is used that expects an internal table containing the allowed
"values.
LOOP AT to_be_checked INTO chk.
TRY.
value = cl_abap_dyn_prg=>check_allowlist( val = chk
allowlist_htab = VALUE #( ( `X` )
( `B` )
( `HALLO` )
( `Y` )
( `ABAP` ) ) ).
out->write( |{ value } is allowed.| ).
CATCH cx_abap_not_in_allowlist INTO e.
out->write( e->get_text( ) ).
ENDTRY.
ENDLOOP.
out->write( |\n| ).
out->write( `****** Checking if input can be a column name and does not contain invalid characters ******` ).
out->write( |\n| ).
"The following example uses a method with which the validity of column names
"of database tables can be checked.
to_be_checked = VALUE #( ( `CARRID` )
( `CONNID` )
( `SOME_COLUMN` )
( `??NOPE??` )
( `...!` ) ).
LOOP AT to_be_checked INTO chk.
TRY.
DATA(col_name) = cl_abap_dyn_prg=>check_column_name( val = chk
strict = abap_true ).
out->write( |{ col_name } is allowed.| ).
CATCH cx_abap_invalid_name INTO e.
out->write( e->get_text( ) ).
ENDTRY.
ENDLOOP.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `52) Dynamic Method Calls (1)` ) ).
"In the example, both class and method are determined at runtime for
"the method call. The suitable parameter table is filled in the
"method get_dyn_class_meth. The example is implemented in a way that
"all the methods that are called store some text in a string. This
"string is displayed to see the effect of the dynamic method call.
lcl_det_at_runtime=>get_dyn_class_meth( IMPORTING cl = DATA(cl_name)
meth = DATA(meth_name)
ptab = DATA(p_tab) ).
CALL METHOD (cl_name)=>(meth_name) PARAMETER-TABLE p_tab.
out->write( |Class name determined at runtime: { cl_name } | ).
out->write( |\n| ).
out->write( |Method name determined at runtime: { meth_name } | ).
out->write( |\n| ).
out->write( `Result of method call (text stored in a variable):` ).
out->write( |\n| ).
out->write( data = lcl_det_at_runtime=>dyn_meth_call_result name = `lcl_det_at_runtime=>dyn_meth_call_result` ).
out->write( |\n| ).
"Further method calls
"The class and method to be used is determined here by just providing
"the character-like content (the name) via a data object in a predefined way.
DATA method TYPE string VALUE `FILL_STRING`.
"Note that the method has no parameters in this example.
"Similar to above. The method stores some text in a string which is
"displayed to see the effect of the dynamic method call.
CALL METHOD lcl_det_at_runtime=>(method).
out->write( data = lcl_det_at_runtime=>dyn_meth_call_result name = `lcl_det_at_runtime=>dyn_meth_call_result` ).
out->write( |\n| ).
DATA class TYPE string VALUE `LCL_DET_AT_RUNTIME`.
CALL METHOD (class)=>fill_string.
out->write( data = lcl_det_at_runtime=>dyn_meth_call_result name = `lcl_det_at_runtime=>dyn_meth_call_result` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `52b) Dynamic Method Calls (2)` ) ).
"Another example for dynamic invoke, using an instance method.
"Example that uses the PARAMETER-TABLE addition
"Creating an instance by specifying the type statically
"An example class of the cheat sheet repository is used.
DATA(oref1) = NEW zcl_demo_abap_objects( ).
"Calling an instance method
"The method multiplies an integer by 3.
"The calculation result is returned.
DATA(result) = oref1->triple( i_op = 2 ).
out->write( data = result name = `result` ).
out->write( |\n| ).
"Dynamic equivalent
"Creating an instance of a class by specifying the type
"dynamically
DATA oref2 TYPE REF TO object.
CREATE OBJECT oref2 TYPE ('ZCL_DEMO_ABAP_OBJECTS').
"Creating parameter table
DATA(ptab) = VALUE abap_parmbind_tab( ( name = 'I_OP'
kind = cl_abap_objectdescr=>exporting
value = NEW i( 3 ) )
( name = 'R_TRIPLE'
kind = cl_abap_objectdescr=>returning
value = NEW i( ) ) ).
"Dynamic method call and specifying a parameter table
CALL METHOD oref2->('TRIPLE') PARAMETER-TABLE ptab.
result = ptab[ name = 'R_TRIPLE' ]-('VALUE')->*.
out->write( data = result name = `result` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `52c) Dynamic Method Calls (Syntax Pattern Overview) (3)` ) ).
"The following examples use both named and unnamed data objects randomly,
"i.e. ...=>(meth_name) or ...=>(`SOME_METH`), for example.
DATA(classname) = `ZCL_DEMO_ABAP_DYNAMIC_PROG`.
DATA(meth_name1) = `STAT_METH1`.
"------------------------------------------------------------------------
"---------------- Calling static methods dynamically --------------------
"------------------------------------------------------------------------
"-------- Method without mandatory parameters defined --------
"The syntax is possible for methods of the same class.
CALL METHOD (meth_name1).
"The previous example method call works like me->(meth).
CALL METHOD me->(meth_name1).
"-------- Class specified statically, method specified dynamically --------
CALL METHOD zcl_demo_abap_dynamic_prog=>(meth_name1).
"-------- Class specified dynamically, method specified statically --------
CALL METHOD (`ZCL_DEMO_ABAP_DYNAMIC_PROG`)=>stat_meth1.
"-------- Class and method specified dynamically --------
CALL METHOD (`ZCL_DEMO_ABAP_DYNAMIC_PROG`)=>(`STAT_METH1`).
"-------- Specifying non-optional parameters --------
CALL METHOD (`ZCL_DEMO_ABAP_DYNAMIC_PROG`)=>stat_meth2 EXPORTING text = `hallo`.
"Specifying the output parameter is optional
DATA res TYPE string.
CALL METHOD (`ZCL_DEMO_ABAP_DYNAMIC_PROG`)=>stat_meth2 EXPORTING text = `hallo` IMPORTING result = res.
ASSERT res = `HALLO`.
"-------- Some examples for handling errors when calling methods wrongly --------
"Instance method called using =>
TRY.
CALL METHOD zcl_demo_abap_dynamic_prog=>(`INST_METH1`).
CATCH cx_sy_dyn_call_illegal_method.
ENDTRY.
"The example method does not specify non-optional parameters.
TRY.
CALL METHOD (`ZCL_DEMO_ABAP_DYNAMIC_PROG`)=>stat_meth2.
CATCH cx_sy_dyn_call_param_missing.
ENDTRY.
"Specifying a wrong parameter name
TRY.
CALL METHOD (`ZCL_DEMO_ABAP_DYNAMIC_PROG`)=>stat_meth2 EXPORTING hallo = `hallo`.
CATCH cx_sy_dyn_call_param_missing.
ENDTRY.
"Assigning wrong, non-compatible type
TRY.
CALL METHOD (`ZCL_DEMO_ABAP_DYNAMIC_PROG`)=>stat_meth2 EXPORTING text = VALUE string_table( ( `hi` ) ).
CATCH cx_sy_dyn_call_illegal_type.
ENDTRY.
"Specifying wrong parameter kinds (the example method specifies importing
"and exporting parameters, and not a returning parameter)
TRY.
CALL METHOD (`ZCL_DEMO_ABAP_DYNAMIC_PROG`)=>stat_meth2 EXPORTING text = `hallo` RECEIVING result = res.
CATCH cx_sy_dyn_call_illegal_type.
ENDTRY.
"------------------------------------------------------------------------
"---------------- Calling instance methods dynamically ------------------
"------------------------------------------------------------------------
"Creating an instance of a class by specifying the type dynamically
DATA object_ref TYPE REF TO object.
CREATE OBJECT object_ref TYPE ('ZCL_DEMO_ABAP_DYNAMIC_PROG').
"--- Object reference variable specified statically, method specified dynamically ---
"Note: This is a also possible for interface reference variables.
CALL METHOD object_ref->(`INST_METH1`).
"-------- Specifying non-optional parameters --------
CALL METHOD object_ref->(`INST_METH2`) EXPORTING text = `abap`.
CALL METHOD object_ref->(`INST_METH2`) EXPORTING text = `abap` RECEIVING result = res.
ASSERT res = `ABAP`.
"Note that calling static methods using object reference variables is also possible.
CALL METHOD object_ref->(`STAT_METH1`).
CALL METHOD object_ref->(`STAT_METH2`) EXPORTING text = `test` IMPORTING result = res.
ASSERT res = `TEST`.
"------------------------------------------------------------------------
"------------------- PARAMETER-TABLE addition ---------------------------
"------------------------------------------------------------------------
"------- Static equivalents to the dynamic statement below -------
DATA(object_ref_stat) = NEW zcl_demo_abap_dynamic_prog( ).
res = object_ref_stat->inst_meth2( `abc` ).
ASSERT res = `ABC`.
"For demo purposes, including chained method call options:
"Functional method call
res = NEW zcl_demo_abap_dynamic_prog( )->inst_meth2( `def` ).
ASSERT res = `DEF`.
"Standalone statement)
NEW zcl_demo_abap_dynamic_prog( )->inst_meth2( EXPORTING text = `ghi` RECEIVING result = res ).
ASSERT res = `GHI`.
"------- Dynamic CALL METHOD statements using the PARAMETER-TABLE addition -------
"Creating parameter table for an instance example method
DATA(paramtab) = VALUE abap_parmbind_tab( ( name = 'TEXT'
kind = cl_abap_objectdescr=>exporting
value = NEW string( `jkl` ) )
( name = 'RESULT'
kind = cl_abap_objectdescr=>returning
value = NEW string( ) )
).
CALL METHOD object_ref->(`INST_METH2`) PARAMETER-TABLE paramtab.
"Excursion: Accessing structure components dynamically
res = paramtab[ name = 'RESULT' ]-('VALUE')->*.
ASSERT res = `JKL`.
"Creating parameter table for a static example method
paramtab = VALUE abap_parmbind_tab( ( name = 'TEXT'
kind = cl_abap_objectdescr=>exporting
value = NEW string( `mno` ) )
( name = 'RESULT'
kind = cl_abap_objectdescr=>importing
value = NEW string( ) ) ).
"Demonstrating static/dynamic specification variants
CALL METHOD (`ZCL_DEMO_ABAP_DYNAMIC_PROG`)=>(`STAT_METH2`) PARAMETER-TABLE paramtab.
res = paramtab[ name = 'RESULT' ]-('VALUE')->*.
ASSERT res = `MNO`.
CALL METHOD zcl_demo_abap_dynamic_prog=>(`STAT_METH2`) PARAMETER-TABLE paramtab.
res = paramtab[ name = 'RESULT' ]-('VALUE')->*.
ASSERT res = `MNO`.
CALL METHOD (`ZCL_DEMO_ABAP_DYNAMIC_PROG`)=>stat_meth2 PARAMETER-TABLE paramtab.
res = paramtab[ name = 'RESULT' ]-('VALUE')->*.
ASSERT res = `MNO`.
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `52d) Dynamic Formatting Option Specifications in String Templates` ) ).
"ALIGN
"Only to be used with WIDTH; only the associated values of the following attributes of the
"class CL_ABAP_FORMAT can be used (they are of type i): A_LEFT (1), A_RIGHT (2), A_CENTER (3)
DATA(demo_string) = `##`.
DATA(s1) = |{ demo_string WIDTH = 10 ALIGN = (1) }<---|.
out->write( data = s1 name = `s1` ).
out->write( |\n| ).
DATA(right) = 2.
DATA(s2) = |{ demo_string WIDTH = 10 ALIGN = (right) }<---|.
out->write( data = s2 name = `s2` ).
out->write( |\n| ).
"The following example uses method chaining with methods of the class
"cl_abap_random_int to get a random integer value (in the range of 1 - 3).
"The get_next method has a returning parameter, and returns an integer value.
DO 5 TIMES.
DATA(s3) = |{ demo_string WIDTH = 10 ALIGN = cl_abap_random_int=>create( seed = cl_abap_random=>seed( )
min = 1 max = 3 )->get_next( ) }<---|.
ENDDO.
out->write( data = s3 name = `s3` ).
out->write( |\n| ).
"CASE
"Values to be used: CL_ABAP_FORMAT=>C_RAW (for not changing the case; 0),
"CL_ABAP_FORMAT=>C_UPPER (1), CL_ABAP_FORMAT=>C_LOWER (2)
demo_string = `AbAp`.
DATA(s4) = |{ demo_string CASE = (1) }|.
out->write( data = s4 name = `s4` ).
out->write( |\n| ).
DATA(s5) = |{ demo_string CASE = CONV i( '2' ) }|.
out->write( data = s5 name = `s5` ).
out->write( |\n| ).
DATA int_tab TYPE TABLE OF i WITH EMPTY KEY.
int_tab = VALUE #( ( 0 ) ( 1 ) ( 2 ) ).
DATA(s6) = |{ demo_string CASE = int_tab[ 1 ] }|.
out->write( data = s2 name = `s2` ).
out->write( |\n| ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `52e) Dynamic Parameter List in EXPORT and IMPORT Statements` ) ).
DATA buffer TYPE xstring.
"--------- Dynamic specification of the parameter list ---------
"Note:
"- The parameter list is specified in an index table with two columns.
"- The column names can have random names, but the type must be character-like.
"- The first column represents the parameter name, the second column represents
" the name of the data object.
"- Note that special behavior applies. See the documentation.
TYPES: BEGIN OF param,
name TYPE string,
dobj TYPE string,
END OF param,
param_tab_type TYPE TABLE OF param WITH EMPTY KEY.
DATA: txt1 TYPE string VALUE `hello`,
txt2 TYPE string VALUE `world`,
txt3 TYPE string VALUE `ABAP`.
DATA(param_table) = VALUE param_tab_type(
( name = `txt1` dobj = `txt1` )
( name = `txt2` dobj = `txt2` )
( name = `txt3` dobj = `txt3` ) ).
EXPORT (param_table) TO DATA BUFFER buffer.
"The example reads the content into structure components.
DATA: BEGIN OF values,
txt1 TYPE string,
txt2 TYPE string,
txt3 TYPE string,
END OF values.
param_table = VALUE param_tab_type(
( name = `txt1` dobj = `values-txt1` )
( name = `txt2` dobj = `values-txt2` )
( name = `txt3` dobj = `values-txt3` ) ).
IMPORT (param_table) FROM DATA BUFFER buffer.
out->write( data = values name = `values` ).
out->write( |\n| ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `52f) Security Considerations in Dynamic Programming Using External Input` ) ).
"Filling demo database tables of the ABAP cheat sheet repository
zcl_demo_abap_aux=>fill_dbtabs( ).
"--------------------------------------------------------------------
"--- Specifying the data object holding external input as operand ---
"--- and literal ----------------------------------------------------
"--------------------------------------------------------------------
"The example explores a dynamic WHERE clause. External content is used
"in the WHERE clause, unchecked.
"Assuming the data object 'input' holds external input inserted on a UI.
DATA(input) = 'LH'.
"Inserting the input value into a dynamic WHERE clause as literal
DATA(cond1) = `CARRID = '` && input && `'`.
SELECT SINGLE * FROM zdemo_abap_fli WHERE (cond1) INTO @DATA(db_entry).
out->write( data = db_entry name = `db_entry` ).
out->write( |\n\n| ).
"Inserting the input value into a dynamic WHERE clause using the data
"object name
DATA(cond2) = `CARRID = @input`.
SELECT SINGLE * FROM zdemo_abap_fli WHERE (cond2) INTO @db_entry.
out->write( data = db_entry name = `db_entry` ).
out->write( |\n\n| ).
"Assuming bad input is provided that is unchecked
DATA(bad_input) = |LH' AND CONNID = '401|.
"Inserting the input value as literal
"Because of using the value as literal, the WHERE clause
"can be manipulated, yielding a potentially different
"result, thus posing a security risk.
DATA(cond3) = `CARRID = '` && bad_input && `'`.
SELECT SINGLE * FROM zdemo_abap_fli WHERE (cond3) INTO @db_entry.
out->write( data = db_entry name = `db_entry` ).
out->write( |\n\n| ).
"Inserting the input value using the data object name
"In doing so, the WHERE clause becomes erroneous, the ABAP
"SQL statement cannot be executed.
DATA(cond4) = `CARRID = @bad_input`.
TRY.
SELECT SINGLE * FROM zdemo_abap_fli WHERE (cond4) INTO @db_entry.
out->write( data = db_entry name = `db_entry` ).
CATCH cx_sy_dynamic_osql_error cx_sy_open_sql_data_error INTO DATA(select_error).
out->write( select_error->get_text( ) ).
ENDTRY.
out->write( |\n\n| ).
out->write( |{ repeat( val = `*` occ = 70 ) }| ).
"--------------------------------------------------------------------
"------------ Accessing not allowed database tables -----------------
"--------------------------------------------------------------------
"Assume the name of a database table is specified externally, and a
"dynamic ABAP SQL statement uses this name. Potentially, users that
"are actually not allowed to access the database table may get access.
"The example uses the CL_ABAP_DYN_PRG class that checks a list of
"allowed database tables.
"The following methods check ...
"- Database table names
"- Whether the database table is contained in a/certain package/s
"Assuming you provide incorrect input for the table name, or
"the table is not contained in the specified packages, you should
"expect an exception to be raied.
"Assuming the following data object contains external input
DATA(input_dbtab_name) = `zdemo_abap_fli`.
"check_table_name_str method: Specifying a single package
TRY.
DATA(databasetable) = cl_abap_dyn_prg=>check_table_name_str(
val = to_upper( input_dbtab_name )
packages = `ZABAP_CHEAT_SHEETS` ).
SELECT SINGLE * FROM (databasetable) INTO NEW @DATA(ref_db_entry).
out->write( data = ref_db_entry name = `ref_db_entry` ).
CATCH cx_abap_not_a_table cx_abap_not_in_package INTO DATA(error_input_dbtab1).
out->write( error_input_dbtab1->get_text( ) ).
ENDTRY.
out->write( |\n\n| ).
"check_table_name_tab method: Specifying multiple packages in an internal
"table
TRY.
dbtab = cl_abap_dyn_prg=>check_table_name_tab(
val = to_upper( input_dbtab_name )
packages = VALUE #( ( `ZABAP_CHEAT_SHEETS` )
( `ZSOME_PACKAGE` ) ) ).
SELECT SINGLE * FROM (dbtab) INTO NEW @ref_db_entry.
out->write( data = ref_db_entry name = `ref_db_entry` ).
CATCH cx_abap_not_a_table cx_abap_not_in_package INTO DATA(error_input_dbtab2).
out->write( error_input_dbtab2->get_text( ) ).
ENDTRY.
out->write( |\n\n| ).
"Not existant database table/invalid name
input_dbtab_name = `not_a_dbtab!!`.
TRY.
dbtab = cl_abap_dyn_prg=>check_table_name_tab(
val = to_upper( input_dbtab_name )
packages = VALUE #( ( `ZABAP_CHEAT_SHEETS` )
( `ZSOME_PACKAGE` ) ) ).
SELECT SINGLE * FROM (dbtab) INTO NEW @ref_db_entry.
out->write( data = ref_db_entry name = `ref_db_entry` ).
CATCH cx_abap_not_a_table cx_abap_not_in_package INTO DATA(error_input_dbtab3).
out->write( error_input_dbtab3->get_text( ) ).
ENDTRY.
out->write( |\n\n| ).
"Database table not existant in packages specified (assuming you have imported
"the ABAP cheat sheet repository, and the database table is available)
input_dbtab_name = `zdemo_abap_fli`.
TRY.
dbtab = cl_abap_dyn_prg=>check_table_name_tab(
val = to_upper( input_dbtab_name )
packages = VALUE #( ( `SAP_BASIS` ) ) ).
SELECT SINGLE * FROM (dbtab) INTO NEW @ref_db_entry.
out->write( data = ref_db_entry name = `ref_db_entry` ).
CATCH cx_abap_not_a_table cx_abap_not_in_package INTO DATA(error_input_dbtab4).
out->write( error_input_dbtab4->get_text( ) ).
ENDTRY.
out->write( |\n\n| ).
out->write( |{ repeat( val = `*` occ = 70 ) }| ).
"--------------------------------------------------------------------
"------------ Verifying input against a given allowlist ------------
"--------------------------------------------------------------------
"Assume a SELECT statement dynamically specifies the column names
"in the SELECT list. Table columns might be accessed although
"they should not be.
"You may check against an allowlist.
"check_allowlist method
"In the following examples, a method is used to check whether
"the input is allowed or not. For this, you specify an allowlist.
"Here, the relevant parameter expects a comma-separated list of
"allowed values.
"Assuming the following data object contains external input
DATA(input_col_name) = `carrid`.
TRY.
DATA(value1) = cl_abap_dyn_prg=>check_allowlist(
val = to_upper( input_col_name )
allowlist_str = `CARRID,CONNID,FLDATE` ).
SELECT SINGLE (input_col_name) FROM zdemo_abap_fli INTO NEW @ref_db_entry.
out->write( data = ref_db_entry name = `ref_db_entry` ).
CATCH cx_abap_not_in_allowlist INTO DATA(error_allowed1).
out->write( error_allowed1->get_text( ) ).
ENDTRY.
out->write( |\n\n| ).
"The allowlist_htab formal parameter expects an internal table.
input_col_name = `price`.
TRY.
DATA(value2) = cl_abap_dyn_prg=>check_allowlist(
val = to_upper( input_col_name )
allowlist_htab = VALUE #( ( `CARRID` )
( `CONNID` )
( `FLDATE` ) ) ).
SELECT SINGLE (input_col_name) FROM zdemo_abap_fli INTO NEW @ref_db_entry.
out->write( data = ref_db_entry name = `ref_db_entry` ).
CATCH cx_abap_not_in_allowlist INTO DATA(error_allowed2).
out->write( error_allowed2->get_text( ) ).
ENDTRY.
out->write( |\n\n| ).
out->write( |{ repeat( val = `*` occ = 70 ) }| ).
"--------------------------------------------------------------------
"------------ Potential manipulation of ABAP SQL clauses ------------
"--------------------------------------------------------------------
"In the following example, a dynamic WHERE clause is set up. For this,
"it is assumed that the WHERE clause uses external input via input fields.
"This is represented by the column and value data objects. It is assumed
"that column holds the name of the table column, value a dedicated value in
"the specified table column.
"The cl_abap_dyn_prg class is used to check content in two ways:
"- Checking if the provided column name is valid using the check_column_name
" method.
"- Using the quote method for putting single quotes around the value and escaping
" single quotes.
"In a DO loop, various example inputs are explored. The fourth loop pass includes
"bad input without using the quote method. This way, an SQL injection takes
"place, yielding a different result. In this case, all database table entries
"are retrieved because the WHERE clause is as follows:
"CARRID = 'LH' OR CARRID <> 'LH'.
"This is prevented using the quote method, resulting in a non-functional SELECT
"statement.
DATA: column TYPE c LENGTH 30,
val TYPE c LENGTH 30.
DO 4 TIMES.
CASE sy-index.
WHEN 1.
"Working example
column = 'carrid'.
val = 'lh'.
WHEN 2.
"Invalid column name
column = '?=('.
val = 'lh'.
WHEN 3.
"Bad input, using cl_abap_dyn_prg
column = 'carrid'.
val = |'LH' OR CARRID <> 'LH'|.
WHEN 4.
"Bad input, not using cl_abap_dyn_prg
column = 'carrid'.
val = |'LH' OR CARRID <> 'LH'|.
ENDCASE.
out->write( |---------- Run { sy-index } ----------| ).
TRY.
cl_abap_dyn_prg=>check_column_name( column ).
CATCH cx_abap_invalid_name INTO DATA(error_col_name).
out->write( error_col_name->get_text( ) ).
ENDTRY.
DATA(cond_syntax) = to_upper( column ) && ` = ` &&
COND #( WHEN sy-index <> 4 THEN cl_abap_dyn_prg=>quote( to_upper( value ) ) ELSE to_upper( value ) ).
TRY.
SELECT *
FROM zdemo_abap_flsch
WHERE (cond_syntax)
INTO TABLE @DATA(itab_flsch).
out->write( itab_flsch ).
CATCH cx_sy_dynamic_osql_error cx_sy_open_sql_data_error INTO DATA(error_select).
out->write( error_select->get_text( ) ).
ENDTRY.
out->write( |\n\n| ).
ENDDO.
"Example manipulating the SET clause in an UPDATE statement
"Inserting a database table entry to work with in the example
INSERT zdemo_abap_carr FROM @( VALUE #( carrid = 'XY' carrname = 'XY Airways' currcode = 'EUR' url = 'some_url' ) ).
SELECT SINGLE * FROM zdemo_abap_carr WHERE carrid = 'XY' INTO @DATA(row4update).
out->write( data = row4update name = `row4update` ).
out->write( |\n\n| ).
"Assuming the carrier name is to be changed (that was previously created and retrieved
"for demo purposes). The carrier name is provided via external input, represented by
"the following data object assignment.
DATA(input_carrname) = 'Air XY'.
"Specifying a potentially dangerous dynamic SET clause by directly using external
"input in the clause
DATA(dyn_set_clause) = `CARRNAME = '` && input_carrname && `'`.
UPDATE zdemo_abap_carr
SET (dyn_set_clause)
WHERE carrid = @row4update-carrid.
SELECT SINGLE * FROM zdemo_abap_carr WHERE carrid = 'XY' INTO @row4update.
out->write( data = row4update name = `row4update` ).
out->write( |\n\n| ).
"Bad input, not using cl_abap_dyn_prg
"In the example, the input is manipulated in a way that also changes
"another field value.
DATA(bad_input_carrname) = |XY Airways', URL = '#########|.
dyn_set_clause = `CARRNAME = '` && bad_input_carrname && `'`.
UPDATE zdemo_abap_carr
SET (dyn_set_clause)
WHERE carrid = @row4update-carrid.
SELECT SINGLE * FROM zdemo_abap_carr WHERE carrid = 'XY' INTO @row4update.
out->write( data = row4update name = `row4update` ).
out->write( |\n\n| ).
"Bad input, using cl_abap_dyn_prg
"Undoing the changes for the demo database table row
MODIFY zdemo_abap_carr FROM @( VALUE #( carrid = 'XY' carrname = 'XY Airways' currcode = 'EUR' url = 'some_url' ) ).
SELECT SINGLE * FROM zdemo_abap_carr WHERE carrid = 'XY' INTO @row4update.
bad_input_carrname = |XY Airways', URL = '#########|.
dyn_set_clause = `CARRNAME = ` && cl_abap_dyn_prg=>quote( bad_input_carrname ).
TRY.
UPDATE zdemo_abap_carr
SET (dyn_set_clause)
WHERE carrid = @row4update-carrid.
CATCH cx_sy_open_sql_data_error INTO DATA(error_set).
out->write( error_set->get_text( ) ).
ENDTRY.
out->write( |{ repeat( val = `*` occ = 70 ) }| ).
"--------------------------------------------------------------------
"---------------------------- Escaping ------------------------------
"--------------------------------------------------------------------
"In various contexts, a replacement of special characters may be important.
"Such an escaping is applied on characters contained in a string according
"to a set of rules.
"The following example deals with Cross Site Scripting, e.g. manipulating
"HTML pages and embedding scripts displayed in a browser. In ABAP, this
"enters the picture, for example, when directly dealing with the Internet
"Communication Framework.
"The built-in function escape can be used to escape content in various contexts.
"The cl_abap_dyn_prg class also offers methods to escape. However, the function
"is recommended due to performance reasons.
"Assuming building HTML code by using external input
DATA your_name TYPE string.
your_name = sy-uname.
DATA(html) = `<p>Hello ` && your_name && `!</p>`.
out->write( data = html name = `html` ).
out->write( |\n\n| ).
"Embedding potentially malicious scripts into the code
your_name = `<script>alert("Hmmm... potentially malicious code!");</script>`.
html = `<p>Hello ` && your_name && `!</p>`.
"Inserted this in an HTML and run in a browser, an alert will be displayed.
out->write( data = html name = `html` ).
out->write( |\n\n| ).
"Escaping may be done as follows
"Check the various methods available for escaping with cl_abap_dyn_prg, as well as
"the formats in the context of the escape function
DATA(esc_js_cl) = `<p>Hello ` && cl_abap_dyn_prg=>escape_xss_javascript( html ) && `!</p>`.
"Using the built-in function escape
DATA(esc_js_fu) = `<p>Hello ` && escape( val = html format = cl_abap_format=>e_xss_js ) && `!</p>`.
"Further character handling and escaping examples using the cl_abap_dyn_prg class
DATA(quote) = |10 o'clock|.
DATA(handle_quotes) = cl_abap_dyn_prg=>quote( quote ).
DATA(backtick) = |The character ` is a backtick|.
DATA(handle_backtick) = cl_abap_dyn_prg=>quote_str( backtick ).
DATA(esc_quotes) = cl_abap_dyn_prg=>escape_quotes( quote ).
DATA(esc_backticks) = cl_abap_dyn_prg=>escape_quotes_str( backtick ).
"You may also do the escaping using string processing techniques, e.g.
"using the replace function.
DATA(esc_quotes_replace) = replace( val = quote sub = |'| with = |''| occ = 0 ).
DATA(esc_backticks_replace) = replace( val = backtick sub = |`| with = |``| occ = 0 ).
out->write( data = esc_js_cl name = `esc_js_cl` ).
out->write( data = esc_js_fu name = `esc_js_fu` ).
out->write( data = handle_quotes name = `handle_quotes` ).
out->write( data = handle_backtick name = `handle_backtick` ).
out->write( data = esc_quotes name = `esc_quotes` ).
out->write( data = esc_backticks name = `esc_backticks` ).
out->write( data = esc_quotes_replace name = `esc_quotes_replace` ).
out->write( data = esc_backticks_replace name = `esc_backticks` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `53) RTTI: Getting Type Information at Runtime/Getting a Reference to a Type Description Object` ) ).
"Getting a reference to a type description object of a type.
"i.e. getting an instance of a type description class
"As shown below, the type decription object can be used
"to create data objects dynamically.
"Type for which information should be retrieved
TYPES: elem_type TYPE c LENGTH 5.
"Creating a data reference variable to hold the reference to
"the type description object
DATA type_descr_obj_elem TYPE REF TO cl_abap_elemdescr.
"Retrieving type information by creating an instance of a type description class
"As the name implies, the describe_by_name method expects the name of the type
"The following example uses the CAST operator for the necessary downcast.
type_descr_obj_elem = CAST #( cl_abap_typedescr=>describe_by_name( 'ELEM_TYPE' ) ).
"Using the older ?= operator
type_descr_obj_elem ?= cl_abap_typedescr=>describe_by_name( 'ELEM_TYPE' ).
"Inline declaration is handy to avoid helper variables.
DATA(type_descr_obj_elem_inl) = CAST cl_abap_elemdescr(
cl_abap_typedescr=>describe_by_name( 'ELEM_TYPE' ) ).
"You may also want to check the type description object in the debugger.
out->write( data = type_descr_obj_elem_inl name = `type_descr_obj_elem_inl` ).
"Various methods/attributes (note that they vary depending on the type) provide
"you with detailed information.
"The following examples show a selection.
"Kind/Type kind/Output length
DATA(kind_elem) = type_descr_obj_elem_inl->kind.
DATA(type_kind_elem) = type_descr_obj_elem_inl->type_kind.
DATA(output_length_elem) = type_descr_obj_elem_inl->output_length.
out->write( data = kind_elem name = `kind_elem` ).
out->write( |\n| ).
out->write( data = type_kind_elem name = `type_kind_elem` ).
out->write( |\n| ).
out->write( data = output_length_elem name = `output_length_elem` ).
out->write( |\n| ).
"In the following example, the type properties are retrieved
"without casting. The data object has the type ref to
"cl_abap_typedescr. See the hierarchy tree of type description classes.
"The reference in the type description object references an
"object from one of the classes CL_ABAP_ELEMDESCR, CL_ABAP_ENUMDESCR,
"CL_ABAP_REFDESCR, CL_ABAP_STRUCTDESCR, CL_ABAP_TABLEDSECR,
"CL_ABAP_CLASSDESCR, or CL_ABAP_INTFDESCR.
"In the following case, it is CL_ABAP_ELEMDESCR.
"Note that in most of the RTTI examples in this class, the explicit
"casting is included when retrieving a reference to the type
"description object.
TYPES another_elem_type TYPE n LENGTH 3.
DATA(type_descr_obj_elem_inl_2) = cl_abap_typedescr=>describe_by_name( 'ANOTHER_ELEM_TYPE' ).
out->write( data = type_descr_obj_elem_inl_2->kind name = `type_descr_obj_elem_inl_2->kind` ).
out->write( |\n| ).
out->write( data = type_descr_obj_elem_inl_2->type_kind name = `type_descr_obj_elem_inl_2->type_kind` ).
out->write( |\n| ).
"More types
"Structured data type (here, using the name of a database table)
DATA(type_descr_obj_struc) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( 'ZDEMO_ABAP_CARR' ) ).
"Various attributes/methods available for detailed information
"Kind
DATA(struc_kind) = type_descr_obj_struc->kind.
"Components of the structure (e.g. the component names and type description
"objects for the individual components)
DATA(comps_struc) = type_descr_obj_struc->get_components( ).
"The attribute also lists the component names and types (but not the type
"desription objects)
DATA(comps_struc2) = type_descr_obj_struc->components.
"Kind of structure
DATA(struct_kind) = type_descr_obj_struc->struct_kind.
out->write( data = struc_kind name = `struc_kind` ).
out->write( |\n| ).
out->write( data = comps_struc name = `comps_struc` ).
out->write( |\n| ).
out->write( data = comps_struc2 name = `comps_struc2` ).
out->write( |\n| ).
out->write( data = struct_kind name = `struct_kind` ).
out->write( |\n| ).
"Internal table type
TYPES table_type TYPE SORTED TABLE OF zdemo_abap_carr WITH UNIQUE KEY carrid.
DATA(type_descr_obj_tab) = CAST cl_abap_tabledescr(
cl_abap_typedescr=>describe_by_name( 'TABLE_TYPE' ) ).
"Kind
DATA(tab_kind) = type_descr_obj_tab->kind.
"The following method returns more information than the attribute below
"(e.g. key kind (unique) etc.)
DATA(tab_keys) = type_descr_obj_tab->get_keys( ).
DATA(tab_keys2) = type_descr_obj_tab->key. "Attribute; lists the keys
"Getting internal table components
"The method get_table_line_type returns a variable of type ref to cl_abap_datadescr.
"This way you can retrieve the table components. Method chaining comes in handy.
DATA(tab_comps) = CAST cl_abap_structdescr(
type_descr_obj_tab->get_table_line_type( ) )->get_components( ).
out->write( data = tab_kind name = `tab_kind` ).
out->write( |\n| ).
out->write( data = tab_keys name = `tab_keys` ).
out->write( |\n| ).
out->write( data = tab_keys2 name = `tab_keys2` ).
out->write( |\n| ).
out->write( data = tab_comps name = `tab_comps` ).
out->write( |\n| ).
"Reference type
TYPES ref_str TYPE REF TO string.
DATA(type_descr_obj_ref) = CAST cl_abap_refdescr(
cl_abap_typedescr=>describe_by_name( 'REF_STR' ) ).
"Kind
DATA(ref_kind) = type_descr_obj_ref->kind.
"Returns type description object of the referenced type
DATA(ref_type) = type_descr_obj_ref->get_referenced_type( ).
"Absolute type name
DATA(ref_type_abs_name) =
type_descr_obj_ref->get_referenced_type( )->absolute_name.
"Type kind
DATA(ref_type_type_kind) =
type_descr_obj_ref->get_referenced_type( )->type_kind.
out->write( data = ref_kind name = `ref_kind` ).
out->write( |\n| ).
out->write( data = ref_type name = `ref_type` ).
out->write( |\n| ).
out->write( data = ref_type_abs_name name = `ref_type_abs_name` ).
out->write( |\n| ).
out->write( data = ref_type_type_kind name = `ref_type_type_kind` ).
"Getting a reference to a type description object of an existing data object.
"Instead of referring to the name of a type, referring to a data object here.
"The relevant method is describe_by_data
"Elementary data object
DATA dobj_elem TYPE i.
DATA(ty_des_obj_el) = CAST cl_abap_elemdescr(
cl_abap_typedescr=>describe_by_data( dobj_elem ) ).
"Structure
DATA dobj_struc TYPE zdemo_abap_carr.
DATA(ty_des_obj_struc) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_data( dobj_struc ) ).
"Internal table
DATA dobj_itab TYPE TABLE OF zdemo_abap_carr WITH EMPTY KEY.
DATA(ty_des_obj_itab) = CAST cl_abap_tabledescr(
cl_abap_typedescr=>describe_by_data( dobj_itab ) ).
"Reference variable
DATA dref_var TYPE REF TO string.
DATA(ty_des_obj_dref) = CAST cl_abap_refdescr(
cl_abap_typedescr=>describe_by_data( dref_var ) ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `54a) RTTI: Getting Type Information at Runtime for Miscellaneous Types` ) ).
"The example demonstrates RTTI as follows:
"- The method call takes care of providing the name of a type. It is implemented
" in a way that various types can be returned, i. e. elementary, structure,
" internal table, reference, class, interface.
"- If the retrieved type is not a class or interface, a data object is created
" based on the type determined at runtime.
"- The type description is retrieved using the method cl_abap_typedescr=>describe_by_data.
" Note the casts for the information retrieval statements.
"- Depending on the type kind, various pieces of information are retrieved. There
" are plenty of options. In ADT, you can use the input help. Just position the
" cursor after the reference variable and ->, e.g. el->, and hit CTRL-Space.
" A dropdown appears showing you the variety you can explore. Check the class
" documentation for more information.
"- If the retrieved type is a class or interface, the type description is
" retrieved using cl_abap_typedescr=>describe_by_name.
"- The example for a class type includes the creation of an object based
" on a type determined at runtime using a CREATE OBJECT statement.
"Retrieving type
DATA(get_type) = lcl_det_at_runtime=>get_random_type( ).
out->write( |Type name determined at runtime: { get_type }| ).
out->write( |\n| ).
DATA dref TYPE REF TO data.
IF get_type <> `LCL_DET_AT_RUNTIME`
AND get_type <> `IF_OO_ADT_CLASSRUN`.
TRY.
CREATE DATA dref TYPE (get_type).
CATCH cx_sy_create_data_error.
out->write( `Create data error!` ).
ENDTRY.
"Retrieving type information
"When referring to a concrete data object name, you can use this method:
DATA(some_type) = cl_abap_typedescr=>describe_by_data( dref->* ).
"Elementary type
IF some_type->kind = cl_abap_typedescr=>kind_elem.
DATA(el) = CAST cl_abap_elemdescr( some_type ).
out->write( data = el name = `el` ).
"Various attributes and methods possible
out->write( data = el->type_kind name = `el->type_kind` ).
out->write( |\n| ).
out->write( data = el->absolute_name name = `el->absolute_name` ).
out->write( |\n| ).
out->write( data = el->get_relative_name( ) name = `el->get_relative_name( )` ).
out->write( |\n| ).
"Structure
ELSEIF some_type->kind = cl_abap_typedescr=>kind_struct.
DATA(stru) = CAST cl_abap_structdescr( some_type ).
out->write( data = stru->absolute_name name = `stru->absolute_name` ).
out->write( |\n| ).
out->write( data = stru->components name = `stru->components` ).
out->write( |\n| ).
out->write( data = stru->struct_kind name = `stru->struct_kind` ).
out->write( |\n| ).
out->write( data = stru->get_components( ) name = `stru->get_components( )` ).
out->write( |\n| ).
"Internal table
ELSEIF some_type->kind = cl_abap_typedescr=>kind_table.
DATA(tab) = CAST cl_abap_tabledescr( some_type ).
out->write( data = tab->absolute_name name = `tab->absolute_name` ).
out->write( |\n| ).
out->write( data = tab->table_kind name = `tab->table_kind` ).
out->write( |\n| ).
out->write( data = tab->get_keys( ) name = `tab->get_keys` ).
out->write( |\n| ).
out->write( data = tab->get_table_line_type( ) name = `tab->get_table_line_type( )` ).
out->write( |\n| ).
"Reference
ELSEIF some_type->kind = cl_abap_typedescr=>kind_ref.
DATA(ref_descr) = CAST cl_abap_refdescr( some_type ).
out->write( data = ref_descr->absolute_name name = `ref_descr->absolute_name` ).
out->write( |\n| ).
out->write( data = ref_descr->get_referenced_type( ) name = `ref_descr->get_referenced_type( )` ).
out->write( |\n| ).
ELSE.
out->write( `Others ...` ).
ENDIF.
ELSE.
"Retrieving type information
"Here, using the type name and not a concrete data object as above.
some_type = cl_abap_typedescr=>describe_by_name( get_type ).
"Class
IF some_type->kind = cl_abap_typedescr=>kind_class.
DATA(class_desc) = CAST cl_abap_classdescr( some_type ).
out->write( data = class_desc->absolute_name name = `class_desc->absolute_name` ).
out->write( |\n| ).
out->write( data = class_desc->attributes name = `class_desc->attributes` ).
out->write( |\n| ).
out->write( data = class_desc->methods name = `class_desc->methods` ).
out->write( |\n| ).
"Creating an object based on a type determined at runtime
DATA oref TYPE REF TO object.
TRY.
CREATE OBJECT oref TYPE (get_type).
"Retrieving type information
DATA(descr_ref) = cl_abap_typedescr=>describe_by_object_ref( oref ).
out->write( data = descr_ref->absolute_name name = `descr_ref->absolute_name` ).
out->write( |\n| ).
out->write( data = descr_ref->kind name = `descr_ref->kind` ).
out->write( |\n| ).
CATCH cx_root.
out->write( `Error` ).
ENDTRY.
"Interface
ELSEIF some_type->kind = cl_abap_typedescr=>kind_intf.
DATA(if_descr) = CAST cl_abap_intfdescr( some_type ).
out->write( data = if_descr->absolute_name name = `if_descr->absolute_name` ).
out->write( |\n| ).
out->write( data = if_descr->methods name = `class_desc->methods` ).
out->write( |\n| ).
ELSE.
out->write( `Others ...` ).
ENDIF.
ENDIF.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `54b) RTTI: Getting Type Information at Runtime for Miscellaneous Types` ) ).
"Data objects to work with in the example
DATA itab_refs TYPE TABLE OF REF TO data.
DATA str_tab TYPE string_table.
DATA dyn_dobj TYPE REF TO data.
DATA dyn_obj TYPE REF TO object.
DATA typdeobj TYPE REF TO cl_abap_typedescr.
"Data objects of different kinds based on which type information shall be retrieved
"Elementary type
DATA elem_dobj TYPE c LENGTH 4 VALUE 'ABAP'.
"Enumerated type
TYPES: BEGIN OF ENUM enum_t,
enum1,
enum2,
enum3,
END OF ENUM enum_t.
DATA(dobj_enum) = enum2.
"demo_structured types
DATA(demo_struct) = VALUE zdemo_abap_carr( carrid = 'XY' carrname = 'XY Airlines' ).
"BDEF derived type (demo_structure)
DATA demo_struct_rap TYPE STRUCTURE FOR CREATE zdemo_abap_rap_ro_m.
"Internal table types
"Standard table with standard table key
DATA(string_table) = VALUE string_table( ( `AB` ) ( `AP` ) ).
"Local demo_structured type as basis for a sorted internal table that
"includes primary and secondary table key specifiactions (including
"an alias name)
TYPES: BEGIN OF struc_type,
a TYPE c LENGTH 3,
b TYPE i,
c TYPE decfloat34,
END OF struc_type.
TYPES demo_tab_type TYPE SORTED TABLE OF struc_type
WITH UNIQUE KEY a
WITH NON-UNIQUE SORTED KEY sec_key ALIAS sk COMPONENTS b c .
DATA(demo_sorted_tab) = VALUE demo_tab_type( ( a = 'aaa' ) ).
"Reference variables
"Data reference variable
DATA(demo_dref) = NEW i( 123 ).
"Object reference variable
DATA(demo_oref) = NEW zcl_demo_abap_objects( ).
"Interface reference variable
DATA demo_iref TYPE REF TO zdemo_abap_objects_interface.
demo_iref = CAST #( demo_oref ).
"Adding the previous (data) objects to an internal table which is
"looped over to retrieve type information for all
itab_refs = VALUE #( ( REF #( elem_dobj ) ) "elementary type (1)
( REF #( dobj_enum ) ) "enumerated type (2)
( REF #( demo_struct ) ) "flat demo_structure (3)
( REF #( demo_struct_rap ) ) "demo_structure typed with BDEF derived type (4)
( REF #( string_table ) ) "internal table, elementary line type (5)
( REF #( demo_sorted_tab ) ) "internal table, local line type (6)
( REF #( demo_dref ) ) "data reference variable (7)
( REF #( demo_oref ) ) "object reference variable (8)
( REF #( demo_iref ) ) "interface reference variable (9)
).
LOOP AT itab_refs INTO DATA(type).
DATA(tabix) = sy-tabix.
TRY.
"The reference returned points to an object from the class CL_ABAP_CLASSDESCR
typdeobj = cl_abap_typedescr=>describe_by_object_ref( type->* ).
CATCH cx_sy_dyn_call_illegal_type.
"The reference returned points to an object from the class CL_ABAP_DATADESCR
typdeobj = cl_abap_typedescr=>describe_by_data( type->* ).
ENDTRY.
"----------------- Exploring general type information -----------------
"At this stage, with using the static methods above, you already get general type
"information such as the type kind or the abosulte name. Check the type description
"object in the debugger for more attributes.
"When performing a down cast to more specific classes, you can access special
"methods of the type object and get more detailed information.
"Getting the type kind
"For the constant values of type abap_typekind, see cl_abap_typedescr. For example, 'h'
"stands for internal table.
DATA(type_kind) = typdeobj->type_kind.
INSERT |{ tabix } Type kind: { type_kind }| INTO TABLE str_tab.
"Type category
"For the constant values of type abap_typecategory, see cl_abap_typedescr.
"C (class), E (elementary), I (interface), R (Reference), S (demo_structure), T (table)
DATA(type_category) = typdeobj->kind.
INSERT |{ tabix } Type category: { type_category }| INTO TABLE str_tab.
"Absolute name (used later for dynamic (data) object creation)
"Note: In ABAP for Cloud Development, absolute names having the pattern \TYPE=%_...
"cannot be used to create (data) objects dynamically.
DATA(absolutename) = typdeobj->absolute_name.
INSERT |{ tabix } Absolute name: { absolutename }| INTO TABLE str_tab.
"Relative name
"Types that are implicitly defined (e.g. created using DATA) do not have a relative
"type name. Explicitly defined types are, for example, standard ABAP types, Dictionary
"types, classes and interfaces.
DATA(relative_name) = typdeobj->get_relative_name( ).
IF relative_name IS NOT INITIAL.
INSERT |{ tabix } Relative name: { relative_name }| INTO TABLE str_tab.
ENDIF.
"Checking if it is a DDIC type
DATA(is_ddic_type) = typdeobj->is_ddic_type( ).
IF is_ddic_type IS NOT INITIAL.
INSERT |{ tabix } Is DDIC type: "{ is_ddic_type }"| INTO TABLE str_tab.
ENDIF.
"----------------- Exploring more specific information by casting -----------------
"For checking the type before performing the cast, you can use statements with
"CASE TYPE OF and IS INSTANCE. The example demonstrates both options.
CASE TYPE OF typdeobj.
WHEN TYPE cl_abap_datadescr.
INSERT |{ tabix } Is instance of cl_abap_datadescr| INTO TABLE str_tab.
"-----------------------------------------------------------------------
"----------------------- Elementary types ------------------------------
"-----------------------------------------------------------------------
IF typdeobj IS INSTANCE OF cl_abap_elemdescr.
INSERT |{ tabix } Is instance of cl_abap_elemdescr| INTO TABLE str_tab.
"Enumerated types
IF typdeobj IS INSTANCE OF cl_abap_enumdescr.
INSERT |{ tabix } Is instance of cl_abap_enumdescr| INTO TABLE str_tab.
DATA(enum) = CAST cl_abap_enumdescr( typdeobj ).
"Various type-specific information retrieval
"Base type of enumerated type
DATA(enum_base_type_kind) = enum->base_type_kind.
INSERT |{ tabix } Base type: { enum_base_type_kind }| INTO TABLE str_tab.
"Elements of the enumerated type
DATA(enum_elements) = enum->members.
INSERT |{ tabix } Elements:| &&
| { REDUCE string( INIT rstr = `` FOR <l> IN enum_elements NEXT rstr = |{ rstr }{ COND #( WHEN rstr IS NOT INITIAL THEN ` / ` ) }| &&
|{ <l>-name } ({ CONV i( <l>-value ) })| ) }| INTO TABLE str_tab.
"Checking the type compatibility of the data object
DATA(applies_enum1) = enum->applies_to_data( enum2 ).
DATA(applies_enum2) = enum->applies_to_data( `nope` ).
DATA(applies_enum3) = enum->applies_to_data_ref( REF #( enum3 ) ).
DATA(applies_enum4) = enum->applies_to_data_ref( REF #( `nope` ) ).
INSERT |{ tabix } Applies: 1) "{ applies_enum1 }" 2) "{ applies_enum2 }"| &&
| 3) "{ applies_enum3 }" 4) "{ applies_enum4 }"| INTO TABLE str_tab.
"Dynamically creating data objects based on the ...
TRY.
"... absolute name
CREATE DATA dyn_dobj TYPE (absolutename).
"Assigning the value to the dynamically created data object
dyn_dobj->* = type->*.
"... type description object
CREATE DATA dyn_dobj TYPE HANDLE enum.
dyn_dobj->* = type->*.
INSERT |{ tabix } Dynamic data objects created, assignments done| INTO TABLE str_tab.
CATCH cx_root INTO DATA(err_enum).
INSERT |{ tabix } Dynamic data object creation error: { err_enum->get_text( ) }| INTO TABLE str_tab.
ENDTRY.
"Elementary types other than enumerated types
ELSE.
DATA(elem) = CAST cl_abap_elemdescr( typdeobj ).
"Note: General information such as (output) length, decimals etc. especially
"for elementary types is already available without the cast.
"Internal length
DATA(elem_internal_length) = elem->length.
"Output length
DATA(elem_output_length) = elem->output_length.
INSERT |{ tabix } Internal length: "{ elem_internal_length }", | &&
|output length: "{ elem_output_length }"| INTO TABLE str_tab.
"Checking the type compatibility of the data object
DATA(applies_elem1) = elem->applies_to_data( 'ciao' ).
DATA(applies_elem2) = elem->applies_to_data( abap_true ).
DATA(applies_elem3) = elem->applies_to_data_ref( REF #( 'abap' ) ).
DATA(applies_elem4) = elem->applies_to_data_ref( REF #( `nope` ) ).
INSERT |{ tabix } Applies: 1) "{ applies_elem1 }" 2) "{ applies_elem2 }"| &&
| 3) "{ applies_elem3 }" 4) "{ applies_elem4 }"| INTO TABLE str_tab.
"Dynamically creating data objects based on the ...
TRY.
"... absolute name
CREATE DATA dyn_dobj TYPE (absolutename).
"Assigning the value to the dynamically created data object
dyn_dobj->* = type->*.
"... type description object
CREATE DATA dyn_dobj TYPE HANDLE elem.
dyn_dobj->* = type->*.
INSERT |{ tabix } Dynamic data objects created, assignments done| INTO TABLE str_tab.
CATCH cx_root INTO DATA(err_elem).
INSERT |{ tabix } Dynamic data object creation error: { err_elem->get_text( ) }| INTO TABLE str_tab.
ENDTRY.
ENDIF.
"-----------------------------------------------------------------------
"----------------------- Reference types ------------------------------
"-----------------------------------------------------------------------
ELSEIF typdeobj IS INSTANCE OF cl_abap_refdescr.
INSERT |{ tabix } Is instance of cl_abap_refdescr| INTO TABLE str_tab.
"Getting a reference to the type's type description object using the
"describe_by_data_ref, which can be used for data reference variables.
"Note that the dynamic type is evaluated.
"The following statement retrieves a type description object using the describe_by_data_ref
"method, which can be used for data reference variables. An object is returned that points
"to an object in one of these classes: cl_abap_elemdescr, cl_abap_enumdescr, cl_abap_refdescr,
"cl_abap_demo_structdescr, cl_abap_tabledsecr.
"The method call is for demonstration purposes. With the returned object, the information
"retrieval can also be performed as above.
DATA(typdeobj_demo_dref) = cl_abap_typedescr=>describe_by_data_ref( type->* ).
"Using the type description object retrieved above (describe_by_data) and casting
DATA(data_reference) = CAST cl_abap_refdescr( typdeobj ).
"Getting a reference to the type's type description object that is used to
"type the reference.
DATA(demo_dref_referenced_type) = data_reference->get_referenced_type( ).
"Based on this, you can get further information of the dynamic type just like in the
"other examples for the referenced type. Here, skipping further type evaluation.
IF demo_dref_referenced_type IS INSTANCE OF cl_abap_elemdescr.
INSERT |{ tabix } The referenced type is an elementary type.| INTO TABLE str_tab.
ELSE.
INSERT |{ tabix } The referenced type is a type other than elementary.| INTO TABLE str_tab.
ENDIF.
"Checking the type compatibility
DATA(applies_demo_dref1) = data_reference->applies_to_data( REF #( 456 ) ).
DATA(applies_demo_dref2) = data_reference->applies_to_data( REF #( `hello` ) ).
TYPES ref_int TYPE REF TO i.
TYPES ref_dstr TYPE REF TO string.
DATA(applies_demo_dref3) = data_reference->applies_to_data_ref( NEW ref_int( ) ).
DATA(applies_demo_dref4) = data_reference->applies_to_data_ref( NEW ref_dstr( ) ).
INSERT |{ tabix } Applies: 1) "{ applies_demo_dref1 }" 2) "{ applies_demo_dref2 }"| &&
| 3) "{ applies_demo_dref3 }" 4) "{ applies_demo_dref4 }"| INTO TABLE str_tab.
"Dynamically creating data objects based on the ...
TRY.
"... absolute name of the referenced data object
DATA(absolutename_ref) = demo_dref_referenced_type->absolute_name.
CREATE DATA dyn_dobj TYPE REF TO (absolutename_ref).
"Assigning the value to the dynamically created data object
dyn_dobj->* = type->*.
"... type description object
CREATE DATA dyn_dobj TYPE HANDLE data_reference.
dyn_dobj->* = type->*.
INSERT |{ tabix } Dynamic data objects created, assignments done| INTO TABLE str_tab.
CATCH cx_root INTO DATA(err_ref).
INSERT |{ tabix } Dynamic data object creation error: { err_ref->get_text( ) }| INTO TABLE str_tab.
ENDTRY.
"Complex types
ELSEIF typdeobj IS INSTANCE OF cl_abap_complexdescr.
INSERT |{ tabix } Is instance of cl_abap_complexdescr| INTO TABLE str_tab.
"-----------------------------------------------------------------------
"----------------------- demo_structured types ------------------------------
"-----------------------------------------------------------------------
IF typdeobj IS INSTANCE OF cl_abap_structdescr.
INSERT |{ tabix } Is instance of cl_abap_demo_structdescr| INTO TABLE str_tab.
DATA(cast_struc) = CAST cl_abap_structdescr( typdeobj ).
"demo_structure kind
"For the constant values, see abap_demo_structkind cl_abap_demo_structdescr
"For the constant values of type abap_demo_structkind, see cl_abap_demo_structdescr. For example, 'F'
"stands for a flat demo_structure.
DATA(struckind) = cast_struc->struct_kind.
INSERT |{ tabix } demo_structure kind: { struckind }| INTO TABLE str_tab.
"demo_structure components
"The following attribute returns a table with component information, such as
"the component names and type kinds.
DATA(struc_components) = cast_struc->components.
INSERT |{ tabix } Components 1: | &&
|{ REDUCE string( INIT dstr = `` FOR <comp1> IN struc_components NEXT dstr = |{ dstr }| &&
|{ COND #( WHEN dstr IS NOT INITIAL THEN ` / ` ) }{ <comp1>-name } ({ <comp1>-type_kind })| ) }| INTO TABLE str_tab.
"demo_structure components (more details)
"The following method also returns a table with component information. In this case,
"type description objects of each component and the component names are returned, which can
"be further evaluated.
DATA(struc_components_tab) = cast_struc->get_components( ).
INSERT |{ tabix } Components 2: | &&
|{ REDUCE string( INIT dstr = `` FOR <comp2> IN struc_components_tab NEXT dstr = |{ dstr }| &&
|{ COND #( WHEN dstr IS NOT INITIAL THEN ` / ` ) }{ <comp2>-name } ({ <comp2>-type->type_kind })| ) }| INTO TABLE str_tab.
"Checking if the demo_structure has includes
DATA(struc_has_include) = cast_struc->has_include.
INSERT |{ tabix } Has include: "{ struc_has_include }"| INTO TABLE str_tab.
IF struc_has_include = abap_true.
"Returning the included view
"Check the class documentation for more information
DATA(struc_incl_view) = cast_struc->get_included_view( ).
INSERT |{ tabix } Included view: | &&
|{ REDUCE string( INIT dstr = `` FOR <comp3> IN struc_incl_view NEXT dstr = |{ dstr }| &&
|{ COND #( WHEN dstr IS NOT INITIAL THEN `, ` ) }{ <comp3>-name }| ) }| INTO TABLE str_tab.
"Returning component names of all components and subdemo_structures in included
"demo_structures that contain included demo_structures
DATA(struc_all_incl) = cast_struc->get_symbols( ).
INSERT |{ tabix } Included view: | &&
|{ REDUCE string( INIT dstr = `` FOR <comp4> IN struc_all_incl NEXT dstr = |{ dstr }| &&
|{ COND #( WHEN dstr IS NOT INITIAL THEN `, ` ) }{ <comp4>-name }| ) }| INTO TABLE str_tab.
ENDIF.
"Checking the type compatibility of the data object
DATA demo_struct_test TYPE zdemo_abap_carr.
DATA demo_struct_rap_test TYPE STRUCTURE FOR CREATE zdemo_abap_rap_ro_m.
DATA(applies_struc1) = cast_struc->applies_to_data( demo_struct_test ).
DATA(applies_struc2) = cast_struc->applies_to_data( demo_struct_rap_test ).
DATA(applies_struc3) = cast_struc->applies_to_data_ref( REF #( demo_struct_test ) ).
DATA(applies_struc4) = cast_struc->applies_to_data_ref( REF #( demo_struct_rap_test ) ).
INSERT |{ tabix } Applies: 1) "{ applies_struc1 }" 2) "{ applies_struc2 }" | &&
|3) "{ applies_struc3 }" 4) "{ applies_struc4 }"| INTO TABLE str_tab.
"Dynamically creating data objects based on the ...
TRY.
"... absolute name
CREATE DATA dyn_dobj TYPE (absolutename).
"Assigning the value to the dynamically created data object
dyn_dobj->* = type->*.
"... type description object
CREATE DATA dyn_dobj TYPE HANDLE cast_struc.
dyn_dobj->* = type->*.
INSERT |{ tabix } Dynamic data objects created, assignments done| INTO TABLE str_tab.
CATCH cx_root INTO DATA(err_struc).
INSERT |{ tabix } Dynamic data object creation error: { err_struc->get_text( ) }| INTO TABLE str_tab.
ENDTRY.
"-----------------------------------------------------------------------
"----------------------- Table types ------------------------------
"-----------------------------------------------------------------------
ELSEIF typdeobj IS INSTANCE OF cl_abap_tabledescr.
INSERT |{ tabix } Is instance of cl_abap_tabledescr| INTO TABLE str_tab.
DATA(cast_tab) = CAST cl_abap_tabledescr( typdeobj ).
"Getting the table kind
"For the constant values of type abap_tablekind, see cl_abap_tabledescr. For example, 'S'
"stands for a standard table.
DATA(tab_table_kind) = cast_tab->table_kind.
INSERT |{ tabix } Table kind: { tab_table_kind }| INTO TABLE str_tab.
"Checking if the table has a unique key
DATA(tab_has_unique_key) = cast_tab->has_unique_key.
INSERT |{ tabix } Has a unique key: "{ tab_has_unique_key }" | &&
|{ COND #( WHEN tab_has_unique_key IS INITIAL THEN `(no unique key)` ) }| INTO TABLE str_tab.
"Returning a table with the names of internal table keys
DATA(tab_table_key) = cast_tab->key.
INSERT |{ tabix } Table keys: { REDUCE string( INIT dstr = `` FOR <key1> IN tab_table_key NEXT dstr = |{ dstr }| &&
|{ COND #( WHEN dstr IS NOT INITIAL THEN `, ` ) }{ <key1>-name }| ) }| INTO TABLE str_tab.
"Returning a table with a description of all table keys, e.g. all components of a key,
"key kind (U, unique, in the example case), information whether the key is the primary
"key etc. For the constant values, see the cl_abap_tabledescr class.
DATA(tabkeys) = cast_tab->get_keys( ).
INSERT |{ tabix } Table keys: { REDUCE string( INIT dstr = `` FOR <key2> IN tabkeys NEXT dstr = |{ dstr }| &&
|{ COND #( WHEN dstr IS NOT INITIAL THEN `, ` ) }{ REDUCE string( INIT str2 = `` FOR <key3> IN <key2>-components NEXT str2 = |{ str2 }| &&
|{ COND #( WHEN str2 IS NOT INITIAL THEN `/` ) }{ <key3>-name }| ) } (is primary: "{ <key2>-is_primary }", | &&
|is unique: "{ <key2>-is_unique }", key kind: "{ <key2>-key_kind }", access kind: "{ <key2>-access_kind }")| ) }| INTO TABLE str_tab.
DATA(tab_keys_aliases) = cast_tab->get_key_aliases( ).
IF tab_keys_aliases IS NOT INITIAL.
INSERT |{ tabix } Table key aliases: { REDUCE string( INIT dstr = `` FOR <key4> IN tab_keys_aliases NEXT dstr = |{ dstr }| &&
|{ COND #( WHEN dstr IS NOT INITIAL THEN `, ` ) }{ <key4>-name } (table key) -> { <key4>-alias } (alias)| ) }| INTO TABLE str_tab.
ENDIF.
"If you want to get information about the line type, e.g. finding out about the component
"names, another cast is required. First, getting a reference to the type description object
"for the demo_structured type.
DATA(tab_line_type) = cast_tab->get_table_line_type( ).
"Then, performing a cast to access the component information as shown above.
"Note that the line type can also be of types other than demo_structured line types.
IF tab_line_type IS INSTANCE OF cl_abap_structdescr.
DATA(tab_line_info) = CAST cl_abap_structdescr( tab_line_type ).
"See more options for demo_structures above.
DATA(tabcomps) = tab_line_info->components.
INSERT |{ tabix } Table components: { REDUCE string( INIT dstr = `` FOR <com> IN tabcomps NEXT dstr = |{ dstr }| &&
|{ COND #( WHEN dstr IS NOT INITIAL THEN ` / ` ) }{ <com>-name } ({ <com>-type_kind })| ) }| INTO TABLE str_tab.
ELSEIF tab_line_type IS INSTANCE OF cl_abap_elemdescr.
DATA(tab_elem_line_type) = CAST cl_abap_elemdescr( tab_line_type ).
DATA(tab_elem_line_type_kind) = tab_elem_line_type->type_kind.
INSERT |{ tabix } Elementary line type, type kind: { tab_elem_line_type_kind }| INTO TABLE str_tab.
ENDIF.
"Checking the type compatibility of the data object
DATA tab_test1 TYPE string_table.
DATA tab_test2 TYPE demo_tab_type.
DATA(applies_tab1) = cast_tab->applies_to_data( tab_test1 ).
DATA(applies_tab2) = cast_tab->applies_to_data( tab_test2 ).
DATA(applies_tab3) = cast_tab->applies_to_data_ref( REF #( tab_test1 ) ).
DATA(applies_tab4) = cast_tab->applies_to_data_ref( REF #( tab_test2 ) ).
INSERT |{ tabix } Applies: 1) "{ applies_tab1 }" 2) "{ applies_tab2 }" | &&
|3) "{ applies_tab3 }" 4) "{ applies_tab4 }"| INTO TABLE str_tab.
"Dynamically creating data objects based on the ...
TRY.
"... absolute name
CREATE DATA dyn_dobj TYPE (absolutename).
dyn_dobj->* = type->*.
"... type description object
CREATE DATA dyn_dobj TYPE HANDLE cast_tab.
dyn_dobj->* = type->*.
INSERT |{ tabix } Dynamic data objects created, assignments done| INTO TABLE str_tab.
CATCH cx_root INTO DATA(err_tab).
INSERT |{ tabix } Dynamic data object creation error: { err_tab->get_text( ) }| INTO TABLE str_tab.
ENDTRY.
ENDIF.
ENDIF.
"Object types
WHEN TYPE cl_abap_objectdescr.
INSERT |{ tabix } Is instance of cl_abap_objectdescr| INTO TABLE str_tab.
"In this example, reference variables are used to retrieve type information of their dynamic type.
"Here, and to find out about the dynamic type the reference refers to (i.e. class or interface), a cast
"with cl_abap_refdescr and calling the get_referenced_type method is used to also find out about the
"instance of cl_abap_intfdescr. In this example, the dynamic type in 'type->*' is evaluated, which is
"cl_abap_classdescr for both because the interface reference variable was assigned accordingly above.
DATA(referenced_type) = CAST cl_abap_refdescr( cl_abap_typedescr=>describe_by_data( type->* ) )->get_referenced_type( ).
"-----------------------------------------------------------------------
"----------------------- Class descriptions ------------------------------
"-----------------------------------------------------------------------
IF referenced_type IS INSTANCE OF cl_abap_classdescr.
INSERT |{ tabix } Is instance of cl_abap_classdescr| INTO TABLE str_tab.
DATA(obj_ref) = CAST cl_abap_classdescr( typdeobj ).
"Getting the class kind
"For the constant values of type abap_classkind, see cl_abap_classdescr.
"Common, simple class (C), abstract class (A), final class (F)
DATA(obj_ref_class_kind) = obj_ref->class_kind.
"Getting class attributes
"You can check the following table in the debugger. There is plenty of information available
"such as type kind, constant, read only etc.
"The example writes the names, the visibility and static or instance attribute (is_class = abap_true
"means it is a static attribute) to the string table.
DATA(obj_ref_attributes) = obj_ref->attributes.
INSERT |{ tabix } Attributes: { REDUCE string( INIT dstr = `` FOR <at> IN obj_ref_attributes NEXT dstr = |{ dstr }| &&
|{ COND #( WHEN dstr IS NOT INITIAL THEN `, ` ) }{ <at>-name } (vis: "{ <at>-visibility }", static: "{ <at>-is_class }")| ) }| INTO TABLE str_tab.
"Getting the interfaces implemented
DATA(obj_ref_interfaces) = obj_ref->interfaces.
INSERT |{ tabix } Interfaces: { REDUCE string( INIT dstr = `` FOR <intf> IN obj_ref_interfaces NEXT dstr = |{ dstr }| &&
|{ COND #( WHEN dstr IS NOT INITIAL THEN `, ` ) }{ <intf>-name }| ) }| INTO TABLE str_tab.
"Getting information about the methods
"You can check the following table in the debugger. There is plenty of information available
"such as parameters, visibility, abstract/final, static/instance and more.
"The example only writes the method names to the string table.
DATA(obj_ref_methods) = obj_ref->methods.
INSERT |{ tabix } Methods: { REDUCE string( INIT dstr = `` FOR <meth> IN obj_ref_methods NEXT dstr = |{ dstr }| &&
|{ COND #( WHEN dstr IS NOT INITIAL THEN `, ` ) }{ <meth>-name }| ) }| INTO TABLE str_tab.
"Getting a reference to the type description object and the absolute name
"of the superclass
"In this example, it is the root class object OBJECT.
DATA(obj_ref_super_class) = obj_ref->get_super_class_type( ).
DATA(obj_ref_super_class_name) = obj_ref_super_class->absolute_name.
INSERT |{ tabix } Super class: { obj_ref_super_class_name }| INTO TABLE str_tab.
"Checking the type compatibility of the object
DATA(demo_oref_test1) = NEW zcl_demo_abap_objects( ).
DATA(demo_oref_test2) = NEW cl_system_uuid( ).
DATA(applies_obj1) = obj_ref->applies_to( demo_oref_test1 ).
DATA(applies_obj2) = obj_ref->applies_to( demo_oref_test2 ).
DATA(applies_obj3) = obj_ref->applies_to_class( 'ZCL_DEMO_ABAP_OBJECTS' ).
DATA(applies_obj4) = obj_ref->applies_to_class( 'CL_SYSTEM_UUID' ).
INSERT |{ tabix } Applies: 1) "{ applies_obj1 }" 2) "{ applies_obj2 }" | &&
|3) "{ applies_obj3 }" 4) "{ applies_obj4 }"| INTO TABLE str_tab.
"Dynamically creating objects based on the absolute name
TRY.
CREATE OBJECT dyn_obj TYPE (absolutename).
INSERT |{ tabix } Dynamic object created| INTO TABLE str_tab.
CATCH cx_sy_create_object_error INTO DATA(err_obj).
INSERT |{ tabix } Dynamic object creation error: { err_obj->get_text( ) }| INTO TABLE str_tab.
ENDTRY.
"The following example shows dynamically accessing public class attributes using the
"dynamically created object. The names and the attribute content are added to the string table.
"In this example (using an ABAP cheat sheet class), all attributes are convertible to string.
IF absolutename CS '\CLASS=ZCL_DEMO_ABAP_OBJECTS' AND err_obj IS INITIAL.
INSERT |{ tabix } Dynamic attribute access: { REDUCE string( INIT dstr = `` FOR <m> IN obj_ref_attributes NEXT dstr = |{ dstr }| &&
|{ COND #( WHEN dstr IS NOT INITIAL AND <m>-visibility = 'U' THEN ` / ` ) }| &&
|{ COND #( WHEN <m>-visibility = 'U' THEN <m>-name && ` ("` && CONV string( dyn_obj->(<m>-name) ) && `")` ) }| ) }| INTO TABLE str_tab.
ENDIF.
"-----------------------------------------------------------------------
"----------------------- Interface descriptions ------------------------------
"-----------------------------------------------------------------------
ELSEIF referenced_type IS INSTANCE OF cl_abap_intfdescr.
INSERT |{ tabix } Is instance of cl_abap_intfdescr| INTO TABLE str_tab.
"In the example, the checked reference variable points to the class
"as the interface reference variable was assigned an instance of a class.
"Therefore, the example here does not work with 'typdeobj' but with the type
"description object 'referenced_type'. With 'referenced_type', the
"interface-specific information can be accessed using a cast.
DATA(intf) = CAST cl_abap_intfdescr( referenced_type ).
"Getting the absolute name
DATA(intf_abs_name) = intf->absolute_name.
INSERT |{ tabix } Absolute name (via cl_abap_intfdescr): { intf_abs_name }| INTO TABLE str_tab.
"Relative name
DATA(intf_rel_name) = intf->get_relative_name( ).
INSERT |{ tabix } Relative name (via cl_abap_intfdescr): { intf_rel_name }| INTO TABLE str_tab.
"Type kind
"For the constant values of type abap_typekind, see cl_abap_typedescr.
"+ stands for the internal type interface.
DATA(intf_type_kind) = intf->type_kind.
INSERT |{ tabix } Type kind (via cl_abap_intfdescr): { intf_type_kind }| INTO TABLE str_tab.
"Type category
"For the constant values of type abap_typecategory, see cl_abap_typedescr.
"I stands for interface.
DATA(intf_type_category) = intf->kind.
INSERT |{ tabix } Type category (via cl_abap_intfdescr): { intf_type_category }| INTO TABLE str_tab.
"Interface type
"For the constant values of type abap_intfkind, see cl_abap_intfdescr.
"F stands for flat interface
DATA(intf_type) = intf->intf_kind.
INSERT |{ tabix } Interface type: { intf_type }| INTO TABLE str_tab.
"Interface attributes
DATA(intf_attributes) = intf->attributes.
INSERT |{ tabix } Attributes: { REDUCE string( INIT dstr = `` FOR <attrintf> IN intf_attributes NEXT dstr = |{ dstr }| &&
|{ COND #( WHEN dstr IS NOT INITIAL THEN `, ` ) }{ <attrintf>-name } (vis: "{ <attrintf>-visibility }", | &&
|static: "{ <attrintf>-is_class }")| ) }| INTO TABLE str_tab.
"Interface methods
"You can check the following table in the debugger. There is plenty of information available
"such as parameters, visibility, abstract/final, static/instance, and more.
"The example only writes the methods names to the string table.
DATA(intf_methods) = intf->methods.
INSERT |{ tabix } Methods: { REDUCE string( INIT dstr = `` FOR <methintf> IN intf_methods NEXT dstr = |{ dstr }| &&
|{ COND #( WHEN dstr IS NOT INITIAL THEN `, ` ) }{ <methintf>-name }| ) }| INTO TABLE str_tab.
"Checking the type compatibility
DATA(intf_test1) = NEW zcl_demo_abap_objects( ).
DATA(intf_test2) = NEW cl_system_uuid( ).
DATA(applies_intf1) = intf->applies_to( intf_test1 ).
DATA(applies_intf2) = intf->applies_to( intf_test2 ).
DATA(applies_intf3) = intf->applies_to_class( 'ZCL_DEMO_ABAP_OBJECTS' ).
DATA(applies_intf4) = intf->applies_to_class( 'CL_SYSTEM_UUID' ).
INSERT |{ tabix } Applies: 1) "{ applies_intf1 }" 2) "{ applies_intf2 }"| &&
| 3) "{ applies_intf3 }" 4) "{ applies_intf4 }"| INTO TABLE str_tab.
"Creating an interface reference variable dynamically
TRY.
CREATE DATA dyn_dobj TYPE REF TO (intf_abs_name).
INSERT |{ tabix } Dynamic data object created| INTO TABLE str_tab.
CATCH cx_sy_create_data_error INTO DATA(err_intf).
INSERT |{ tabix } Dynamic data object creation error: { err_intf->get_text( ) }| INTO TABLE str_tab.
ENDTRY.
"The following example shows dynamically creating an object which is assigned to the
"previously created interface reference variable. Artifacts of the ABAP cheat sheet repository
"are used.
IF intf_abs_name CS '\INTERFACE=ZDEMO_ABAP_OBJECTS_INTERFACE'
AND absolutename CS '\CLASS=ZCL_DEMO_ABAP_OBJECTS'
AND err_intf IS INITIAL.
TRY.
CREATE OBJECT dyn_dobj->* TYPE (absolutename).
INSERT |{ tabix } Dynamic object created| INTO TABLE str_tab.
CATCH cx_sy_create_object_error INTO err_obj.
INSERT |{ tabix } Dynamic object creation error: { err_obj->get_text( ) }| INTO TABLE str_tab.
ENDTRY.
ENDIF.
ENDIF.
ENDCASE.
INSERT `-----------------------------------` INTO TABLE str_tab.
ENDLOOP.
out->write( str_tab ).
**********************************************************************
"----------- Exploring the describe_by_name method -----------
"The method returns a type description object when providing the relative or
"absolute name of a type.
"The following example explores the RTTI type hierarchy based on relative names
"and using the describe_by_name method. Similar to the example above, an internal
"table that is filled with local and global type names instead of data objects is
"looped over. The information retrieval can be performed via the type description
"object as above, but it is not implemented here.
CLEAR str_tab.
DATA typdeobj_from_type_name TYPE REF TO cl_abap_typedescr.
"Data types of different kinds based on which type
"information shall be retrieved
"Elementary type
TYPES packed TYPE p LENGTH 8 DECIMALS 2.
"Enumerated type
TYPES: BEGIN OF ENUM enum_type,
enum_a,
enum_b,
enum_c,
END OF ENUM enum_type.
"demo_structured types
TYPES: BEGIN OF flat_struc_type,
a TYPE c LENGTH 3,
b TYPE i,
c TYPE decfloat34,
END OF flat_struc_type.
TYPES str_der_type TYPE STRUCTURE FOR CREATE zdemo_abap_rap_ro_m.
"Internal table types
TYPES int_demo_tab_type TYPE TABLE OF i WITH EMPTY KEY.
TYPES demo_sorted_demo_tab_type TYPE SORTED TABLE OF flat_struc_type
WITH UNIQUE KEY a
WITH NON-UNIQUE SORTED KEY sec_key ALIAS sk COMPONENTS b c.
TYPES itab_der_type TYPE TABLE FOR UPDATE zdemo_abap_rap_ro_m.
"Reference types
TYPES int_demo_dref_type TYPE REF TO i.
TYPES gen_demo_dref_type TYPE REF TO data.
"Class and interface names are specified directly
DATA(type_name_tab) = VALUE string_table( ( `PACKED` ) "Elementary type (1)
( `TIMESTAMPL` ) "Elementary type, global DDIC type/data element (2)
( `ENUM_TYPE` ) "Enumerated type (3)
( `FLAT_STRUC_TYPE` ) "demo_structured type, flat demo_structure (4)
( `STR_DER_TYPE` ) "demo_structured type, BDEF derived type (5)
( `INT_demo_tab_type` ) "Table type, elementary line type (6)
( `demo_sorted_demo_tab_type` ) "Table type, demo_structured line type (7)
( `ITAB_DER_TYPE` ) "Table type, BDEF derived type (8)
( `INT_demo_dref_TYPE` ) "Reference type (9)
( `GEN_demo_dref_TYPE` ) "Reference type, generic type (10)
( `CL_ABAP_TYPEDESCR` ) "Class name (11)
( `CL_ABAP_CORRESPONDING` ) "Class name (12)
( `IF_OO_ADT_CLASSRUN` ) "Interface name (13)
( `ZDEMO_ABAP_OBJECTS_INTERFACE` ) "Interface name (14)
).
LOOP AT type_name_tab INTO DATA(ty_name).
DATA(tabix_type_names) = sy-tabix.
typdeobj_from_type_name = cl_abap_typedescr=>describe_by_name( ty_name ).
CASE TYPE OF typdeobj_from_type_name.
WHEN TYPE cl_abap_datadescr.
INSERT |{ tabix_type_names } Is instance of cl_abap_datadescr| INTO TABLE str_tab.
CASE TYPE OF typdeobj_from_type_name.
WHEN TYPE cl_abap_elemdescr.
INSERT |{ tabix_type_names } Is instance of cl_abap_elemdescr| INTO TABLE str_tab.
IF typdeobj_from_type_name IS INSTANCE OF cl_abap_enumdescr.
INSERT |{ tabix_type_names } Is instance of cl_abap_enumdescr| INTO TABLE str_tab.
ENDIF.
WHEN TYPE cl_abap_complexdescr.
INSERT |{ tabix_type_names } Is instance of cl_abap_complexdescr| INTO TABLE str_tab.
CASE TYPE OF typdeobj_from_type_name.
WHEN TYPE cl_abap_structdescr.
INSERT |{ tabix_type_names } Is instance of cl_abap_demo_structdescr| INTO TABLE str_tab.
WHEN TYPE cl_abap_tabledescr.
INSERT |{ tabix_type_names } Is instance of cl_abap_tabledescr| INTO TABLE str_tab.
ENDCASE.
WHEN TYPE cl_abap_refdescr.
INSERT |{ tabix_type_names } Is instance of cl_abap_refdescr| INTO TABLE str_tab.
ENDCASE.
WHEN TYPE cl_abap_objectdescr.
INSERT |{ tabix_type_names } Is instance of cl_abap_objectdescr| INTO TABLE str_tab.
CASE TYPE OF typdeobj_from_type_name.
WHEN TYPE cl_abap_classdescr.
INSERT |{ tabix_type_names } Is instance of cl_abap_classdescr| INTO TABLE str_tab.
WHEN TYPE cl_abap_intfdescr.
INSERT |{ tabix_type_names } Is instance of cl_abap_intfdescr| INTO TABLE str_tab.
ENDCASE.
ENDCASE.
INSERT `-----------------------------------` INTO TABLE str_tab.
ENDLOOP.
out->write( |\n*************************************************************\n\n| ).
out->write( str_tab ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `55) RTTC: Dynamically Creating Data Types at Runtime` ) ).
"You can create data types at program runtime using methods of the type
"description classes of RTTS. These types are only valid locally in the
"program. They are also anonymous, i.e. they are only accessible through
"type description objects. As shown above, you can get a reference to a
"type description object of a type using the static methods of the class
"CL_ABAP_TYPEDESCR. The focus here is on using RTTC methods such as get*.
"Creating type description objects using ...
"... elementary data types
"Conceptually, all elementary, built-in ABAP types already exist and can
"be accessed by the corresponding get_* methods.
"In ADT, click CTRL + space after cl_abap_elemdescr=>... to check out the options.
"The following examples show a selection.
DATA(tdo_elem_i) = cl_abap_elemdescr=>get_i( ).
DATA(tdo_elem_string) = cl_abap_elemdescr=>get_string( ).
"For the length specification of type c, there is an importing parameter available.
DATA(tdo_elem_c_l20) = cl_abap_elemdescr=>get_c( 10 ).
"Type p with two parameters to be specified.
DATA(tdo_elem_p) = cl_abap_elemdescr=>get_p( p_length = 3 p_decimals = 2 ).
"Instead of calling get_i() and others having no importing parameters, you could also call
"the describe_by_name( ) method and pass the type names (I STRING etc.) as arguments.
"DATA(tdo_elem_i_2) = CAST cl_abap_elemdescr(
" cl_abap_typedescr=>describe_by_name( 'I' ) ).
"DATA(tdo_elem_string_2) = CAST cl_abap_elemdescr(
" cl_abap_typedescr=>describe_by_name( 'STRING' ) ).
"... structured data types
"They are created based on a component description table.
"A structured type such as the following shall be created using a
"type description object.
TYPES:
BEGIN OF demo_struc_type,
a TYPE string,
b TYPE i,
c TYPE c LENGTH 5,
d TYPE p LENGTH 4 DECIMALS 3,
END OF demo_struc_type.
"Creating a type description object using RTTC method
"Using the get method, you can create the type description object
"dynamically based on a component table. The component table is of type
"abap_component_tab. In this example, the component table is created inline.
DATA(tdo_struc) = cl_abap_structdescr=>get(
VALUE #(
( name = 'A' type = cl_abap_elemdescr=>get_string( ) )
( name = 'B' type = cl_abap_elemdescr=>get_i( ) )
( name = 'C' type = cl_abap_elemdescr=>get_c( 5 ) )
( name = 'D' type = cl_abap_elemdescr=>get_p( p_length = 4 p_decimals = 3 ) ) ) ).
"... internal table types
"Note: Specifying the line type is mandatory, the rest is optional.
"An internal table type such as the following shall be created using a
"type description object.
TYPES std_tab_type_std_key TYPE STANDARD TABLE OF string WITH DEFAULT KEY.
"Creating a type description object using RTTC method
"Not specifying the other optional parameters means that the
"default values are used, for example, standard table is the
"default value for p_table_kind.
DATA(tdo_tab_1) = cl_abap_tabledescr=>get(
p_line_type = cl_abap_elemdescr=>get_string( ) ).
"Another internal table type for which more parameter specifications are needed
"The following internal table type shall be created using a type description object.
TYPES so_table_type TYPE SORTED TABLE OF zdemo_abap_flsch WITH UNIQUE KEY carrid connid.
"Creating a type description object using RTTC method
"The following example also demonstrates how comfortably constructor
"operators can be used at these positions.
DATA(tdo_tab_2) = cl_abap_tabledescr=>get(
p_line_type = CAST cl_abap_structdescr( cl_abap_tabledescr=>describe_by_name( 'ZDEMO_ABAP_FLSCH' ) )
p_table_kind = cl_abap_tabledescr=>tablekind_sorted
p_key = VALUE #( ( name = 'CARRID' ) ( name = 'CONNID' ) )
p_unique = cl_abap_typedescr=>true ).
" ... reference types
"Reference types such as the following shall be created using a
"type description object.
TYPES some_ref_type2t TYPE REF TO t.
TYPES some_ref_type2cl TYPE REF TO zcl_demo_abap_dynamic_prog.
"Using RTTC methods
"You can create a reference type from a base type. This base type
"may be class, interface or data type.
DATA(tdo_ref_1) = cl_abap_refdescr=>get( cl_abap_elemdescr=>get_t( ) ).
DATA(tdo_ref_2) = cl_abap_refdescr=>get( cl_abap_typedescr=>describe_by_name( 'ZCL_DEMO_ABAP_DYNAMIC_PROG' ) ).
"Alternative: get_by_name method
DATA(tdo_ref_3) = cl_abap_refdescr=>get_by_name( 'T' ).
DATA(tdo_ref_4) = cl_abap_refdescr=>get_by_name( 'ZCL_DEMO_ABAP_DYNAMIC_PROG' ).
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `56) Dynamically Creating Data Objects at Runtime Using Type Description Objects (1) - Miscellaneous` ) ).
"Using the TYPE HANDLE addition to CREATE DATA statements, you can
"dynamically create data objects at runtime based on type description objects.
"The following example uses type description objects from the previous example.
"For output purposes, the created data objects are assigned values.
DATA dref_typ_obj TYPE REF TO data.
"Elementary data object
CREATE DATA dref_typ_obj TYPE HANDLE tdo_elem_i.
dref_typ_obj->* = 5 + 4.
out->write( data = dref_typ_obj->* name = `dref_typ_obj->*` ).
out->write( |\n| ).
"Structured data object
CREATE DATA dref_typ_obj TYPE HANDLE tdo_struc.
dref_typ_obj->('A') = `hello`.
dref_typ_obj->('B') = 4 + 3.
dref_typ_obj->('C') = 'abcde'.
dref_typ_obj->('D') = '1.234'.
out->write( data = dref_typ_obj->* name = `dref_typ_obj->*` ).
out->write( |\n| ).
"Internal table
CREATE DATA dref_typ_obj TYPE HANDLE tdo_tab_2.
SELECT * FROM zdemo_abap_flsch INTO TABLE @dref_typ_obj->* UP TO 3 ROWS.
out->write( data = dref_typ_obj->* name = `dref_typ_obj->*` ).
out->write( |\n| ).
"Reference
CREATE DATA dref_typ_obj TYPE HANDLE tdo_ref_3.
dref_typ_obj->* = NEW t( '120000' ).
out->write( data = dref_typ_obj->* name = `dref_typ_obj->*` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `57) Dynamically Creating Data Objects at Runtime Using Type Description Objects (2) - Structure` ) ).
"This example includes the dynamic definition of a structure with three components
"using the GET method of the CL_ABAP_STRUCTDESCR class.
DATA: struct_type TYPE REF TO cl_abap_structdescr,
dref_struc TYPE REF TO data.
DATA column1 TYPE c LENGTH 5.
DATA column2 TYPE c LENGTH 5.
DATA column3 TYPE c LENGTH 5.
"Potential component names
DATA(comp_names) = VALUE string_table( ( `A` ) ( `B` ) ( `C` ) ( `D` ) ( `E` ) ( `F` ) ).
"The structure should contain 3 components.
DO 3 TIMES.
"Getting a random integer that represents the table index
"The line (the component name) is deleted from the table so as to
"guarantee unique component names.
DATA(num) = cl_abap_random_int=>create(
seed = cl_abap_random=>seed( ) min = 1
max = lines( comp_names ) )->get_next( ).
CASE sy-index.
WHEN 1.
column1 = comp_names[ num ].
WHEN 2.
column2 = comp_names[ num ].
WHEN 3.
column3 = comp_names[ num ].
ENDCASE.
DELETE comp_names INDEX num.
ENDDO.
"All components should be typed with c length 3
struct_type = cl_abap_structdescr=>get(
VALUE #(
( name = column1 type = cl_abap_elemdescr=>get_c( 3 ) )
( name = column2 type = cl_abap_elemdescr=>get_c( 3 ) )
( name = column3 type = cl_abap_elemdescr=>get_c( 3 ) ) ) ).
"Creating structured data object
CREATE DATA dref_struc TYPE HANDLE struct_type.
"Assigning values to the structure components
dref_struc->(column1) = 'abc'.
dref_struc->(column2) = 'def'.
dref_struc->(column3) = 'ghi'.
out->write( data = dref_struc->* name = `dref_struc->*` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `58) Dynamically Creating Data Objects at Runtime Using Type Description Objects (3) - Internal Table` ) ).
"In the example an internal table type is created based on a DDIC type.
"See the comments in the code.
"Retrieving table name
DATA(table_name) = lcl_det_at_runtime=>get_dyn_table_name( ).
"Retrieving type information using RTTI
DATA(st) = CAST cl_abap_structdescr(
cl_abap_tabledescr=>describe_by_name( table_name ) ).
"Declaring an internal table to hold the components;
"it will include the component name and the component type
DATA comp_table TYPE cl_abap_structdescr=>component_table.
"Looping across the retrieved field list to extract information
"In principle, you could also just use method get_components( ) :)
LOOP AT st->components ASSIGNING FIELD-SYMBOL(<field>).
"Adding name of the component and its type, which is retrieved using the
"get_component_type method, are added to the internal table that holds the components
APPEND VALUE #( name = <field>-name
type = st->get_component_type( <field>-name ) ) TO comp_table.
"The SELECT statement further down includes a dynamic specification
"of the ORDER BY clause.
"In this case, just using the second field since MANDT is the first.
IF sy-tabix = 2.
DATA(dyn_order_by) = <field>-name.
ENDIF.
ENDLOOP.
"Creating an internal table type
"Note: The parameter p_key is not filled here, i. e. the default key is used.
DATA(itab_type) = cl_abap_tabledescr=>create(
p_line_type = st
p_table_kind = cl_abap_tabledescr=>tablekind_sorted
p_unique = cl_abap_typedescr=>true ).
"Creating an internal table based on the created table type
DATA ref_tab TYPE REF TO data.
CREATE DATA ref_tab TYPE HANDLE itab_type.
"Filling an internal table
SELECT *
FROM (table_name)
ORDER BY (dyn_order_by)
INTO CORRESPONDING FIELDS OF TABLE @ref_tab->*
UP TO 3 ROWS.
out->write( |Type/Database table name determined at runtime: { table_name }| ).
out->write( |\n| ).
out->write( |Internal table entries (ordered by { dyn_order_by }):| ).
out->write( |\n| ).
out->write( |\n| ).
out->write( data = ref_tab->* name = `ref_tab->*` ).
ENDMETHOD.
METHOD inst_meth1.
... "No implementation added
ENDMETHOD.
METHOD inst_meth2.
result = to_upper( text ).
ENDMETHOD.
METHOD stat_meth1.
... "No implementation added
ENDMETHOD.
METHOD stat_meth2.
result = to_upper( text ).
ENDMETHOD.
ENDCLASS.