From e0df939ddeead621a53d9c6d55a738caebbf30a2 Mon Sep 17 00:00:00 2001 From: Daniel Reger <16720986+danrega@users.noreply.github.com> Date: Fri, 13 Jan 2023 16:51:24 +0100 Subject: [PATCH] Update Dynamic Programming --- 06_Dynamic_Programming.md | 665 ++++++------- src/zcl_demo_abap_dynamic_prog.clas.abap | 896 ++++++++++-------- ...emo_abap_dynamic_prog.clas.locals_imp.abap | 344 ++++--- 3 files changed, 1036 insertions(+), 869 deletions(-) diff --git a/06_Dynamic_Programming.md b/06_Dynamic_Programming.md index 5c445d6..fda4dec 100644 --- a/06_Dynamic_Programming.md +++ b/06_Dynamic_Programming.md @@ -2,9 +2,8 @@ # Dynamic Programming - - [Dynamic Programming](#dynamic-programming) - - [Notes on "Dynamic"](#notes-on-dynamic) + - [Notes on Dynamic Programming](#notes-on-dynamic-programming) - [Excursion: Field Symbols and Data References](#excursion-field-symbols-and-data-references) - [Field Symbols](#field-symbols) - [Data References](#data-references) @@ -13,85 +12,81 @@ - [Further Information](#further-information) - [Executable Example](#executable-example) -## Notes on "Dynamic" +## Notes on Dynamic Programming -Some considerations regarding "dynamic" in contrast to "static" aspects: +- Regarding "dynamic" in contrast to "static" aspects, [ABAP programs](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_program_glosry.htm) can include both dynamic and static parts. +- Consider a [data object](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_object_glosry.htm "Glossary Entry") in your program: + - It can be declared as a [static data object](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstatic_data_object_glosry.htm), i. e. you provide all attributes by specifying the [data type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_type_glosry.htm) and more statically in the code. -- ABAP programs can include both dynamic and static parts. -- Consider a [data - object](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_object_glosry.htm "Glossary Entry") - you declare in a program having dedicated technical properties like - the [data - type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_type_glosry.htm "Glossary Entry") - or the actual name of the data object, i. e. these properties are - already (statically) known to the program at compile time and they - do not change throughout the program execution. -- On the other hand, there can be use cases where these properties - **are not known or not yet determined at compile time** at all. -- They are **only known at a program's runtime**, i. e. the - properties are defined and passed to programs at runtime. -- Consider a UI that allows a user to make an entry in a field. By choosing a button, some action is triggered. - The underlying program must be able to work with whatever was inserted. Assume the program expects the name of a database table to be inserted. - However, the tables certainly have different properties, line types, - field names, number of rows, and so on. Nevertheless, the program - must be able to work with whatever table name was inserted and no matter what table is - processed. -- You might also need to determine information about data types and - data objects at runtime or even create them. + ```abap + "Internal table declaration + DATA itab TYPE TABLE OF zdemo_abap_carr WITH EMPTY KEY. + ``` -Dynamic programming is a powerful means to make ABAP programs more -flexible and versatile. However, as implied above, dynamic programming -techniques must be handled with care and you must be aware of some -downsides, too. For example: + - The name `itab` of the data object is determined at compile time and remains stable throughout the execution of the program. +- However, there can also be use cases where the attributes of such a data object are not statically determined. This is where dynamic aspects enter the picture: Attributes, names, types etc. are not determined at compile time but rather at runtime. +- There are ABAP statements that include these dynamic aspects in the syntax. Assume you have simple program and a UI that includes an input field storing the input in a data object named `dbtab`. As input, you expect the name of a database table to be provided. In the end, you want to retrieve all entries of the database table and store them in an internal table. This table should be displayed. So, there is random input at runtime and your program must be able deal with. + - See the following `SELECT` statement. As also shown further down, the `FROM` clause does not include a statically defined table to be selected from. Instead, there is a pair of parentheses including a data object. Assume the data object holds the name of the database table. At runtime, the data retrieval happens from the database table that was inserted in the input field. + + ```abap + SELECT * + FROM (dbtab) + INTO TABLE @DATA(some_itab). + ``` + +- Further aspects for dynamic programming in ABAP enter the picture if you want to determine information about data types and data objects at runtime ([RTTI](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrun_time_type_identific_glosry.htm)) or even create them ([RTTC](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrun_time_type_creation_glosry.htm)). + +- In general, dynamic programming also comes with some downsides. For example, the ABAP compiler cannot check the dynamic programming feature like the `SELECT` statement mentioned above. There is no syntax warning or suchlike. The checks are performed at runtime only which has an impact on the performance. Plus, the testing of [procedures](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenprocedure_glosry.htm "Glossary Entry") +that include dynamic programming features is difficult. -- Dynamic features implemented in a program cannot be checked or - analyzed by the ABAP compiler. The exact data is not known at - compile time but only when the program is executed which has also an - impact on performance since the checks must be carried out at - runtime. -- Testing - [procedures](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenprocedure_glosry.htm "Glossary Entry") - including dynamic parts is difficult.

(back to top)

## Excursion: Field Symbols and Data References -[Field -symbols](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfield_symbol_glosry.htm "Glossary Entry") -and [data -references](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_reference_glosry.htm "Glossary Entry") -support dynamic programming and working with data objects whose -properties are only known at runtime. +[Field symbols](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfield_symbol_glosry.htm "Glossary Entry") +and [data references](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_reference_glosry.htm "Glossary Entry") are dealt with here since they are supporting elements for dynamic programming. ### Field Symbols Field symbols ... -- can be considered as alias names for existing data objects. -- can only be used if they are assigned to a data object first. And if - assigned, you can access the content of variables via the field - symbol name. -- do not consume any space but act as a sort of label for the - particular memory area that is used by a data object which the field - symbol is assigned to. -- can be used in ABAP programs as if working with the actual data - object. -- can be statically typed with both [complete data - types](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomplete_data_type_glosry.htm "Glossary Entry") - and [generic data - types](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abengeneric_data_type_glosry.htm "Glossary Entry"). -- are especially helpful for accessing and editing data in structures - or internal tables at runtime without the need to copy the data - somewhere which boosts performance. +- are symbolic names for almost any data object or parts of existing data objects. +- can be assigned actual memory areas at program runtime (using `ASSIGN`). Note that you can only work with the field symbols if indeed they have been assigned before. +- can be used as placeholders for a data object at an [operand position](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenoperand_position_glosry.htm). + - Consider there is a data object in your program. A field symbol is also available that is assigned the memory area of this data object. Accessing a field symbol is like accessing the [named data object](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abennamed_data_object_glosry.htm) or part of the object itself. +- do not reserve physical space in the [data area](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_area_glosry.htm) of a program like a data object. Instead, they work as dynamic identifiers of a memory area in which a specific data object or part of an object is located. +- can be typed either with [generic data types](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abengeneric_data_type_glosry.htm "Glossary Entry") or [complete data types](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomplete_data_type_glosry.htm "Glossary Entry"). +- are declared using the statement [`FIELD-SYMBOLS`](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abapfield-symbols.htm) or the [declaration operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendeclaration_operator_glosry.htm) [`FIELD-SYMBOL`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfield-symbol_inline.htm). Their names must be included between angle brackets. **Declaring field symbols** -Field symbols are declared with the `FIELD-SYMBOLS` statement. -You provide the name of the field symbol between angle brackets. You can -either type them with a complete data type or with a generic type. +Syntax: +``` abap +"Declaring field symbols using the FIELD-SYMBOLS statement +"and providing a complete/generic type + +"Examples for complete types +FIELD-SYMBOLS: TYPE i, + TYPE zdemo_abap_fli, + TYPE LINE OF some_table_type, + LIKE some_data_object. + +"Examples for generic types +FIELD-SYMBOLS TYPE data. +FIELD-SYMBOLS TYPE any table. + +"Declaring field symbols inline +"The typing of the field symbol is determined using the statically +"known type of the assigned memory area. +"Prominent use case: Inline declaration of a field symbol for an internal table. +LOOP AT itab ASSIGNING FIELD-SYMBOL(). + ... +ENDLOOP. +``` > **💡 Note**
+>- After its declaration, a field symbol is initial, i. e. a memory area is not (yet) assigned to it (apart from the inline declaration). If you use an unassigned field symbol, an exception is raised. >- There are plenty of options for generic ABAP types. A prominent one is `data` that stands for any data type (the older generic type `any` has the same effect). See more information in the @@ -101,54 +96,37 @@ either type them with a complete data type or with a generic type. [classes](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenclass_glosry.htm "Glossary Entry") and [interfaces](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenoo_intf_glosry.htm "Glossary Entry"). ->- Untyped field symbols are not supported in object-oriented contexts. ->- Field symbols can also be [declared - inline](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfield-symbol_inline.htm). - -Syntax: -``` abap -"Complete types -FIELD-SYMBOLS: TYPE i, - TYPE zdemo_abap_fli, - TYPE LINE OF some_table_type, - LIKE some_data_object. - -"Generic types -FIELD-SYMBOLS TYPE data. "or: TYPE any -FIELD-SYMBOLS TYPE any table. -``` **Assigning data objects** -When assigning data objects to field symbols with the [`ASSIGN`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapassign.htm) -statement, field symbols are given all properties and values from the -data objects. In case of completely typed field symbols, you can only -assign data objects that have the same type. Further dynamic aspects -enter the picture with dynamic assignment. This is dealt with further -down. +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. -Syntax: ``` abap -"Data objects. +"Some data object declarations to be used DATA: number TYPE i, struc TYPE sflight, tab TYPE string_table. -"Field symbols with complete types +"Declaring field symbols with complete types FIELD-SYMBOLS: TYPE i, TYPE sflight, TYPE string_table. -"Generic type +"Declaring field symbols with generic type FIELD-SYMBOLS TYPE data. "Assigning data objects to field symbols +"Note: In this case, the field symbols have an appropriate type. ASSIGN number TO . ASSIGN struc TO . ASSIGN tab TO . ASSIGN number TO . "Could be any of the data objects -ASSIGN number TO FIELD-SYMBOL(). "Field symbol declared inline + +"Inline declaration is possible, too. The type +"is automatically derived. +ASSIGN number TO FIELD-SYMBOL(). "You can also assign a particular component of a structure. "Second component of the structure @@ -156,56 +134,50 @@ ASSIGN COMPONENT 2 OF STRUCTURE struc TO . ASSIGN COMPONENT 'CARRID' OF STRUCTURE struc TO . -"In newer ABAP releases, you can dynamically specify components -"as follows: -ASSIGN struc-(comp1) TO . +"CASTING addition for matching types of data object and field symbol +"when assigning memory areas +TYPES c_len_3 TYPE c LENGTH 3. +DATA(chars) = 'abcdefg'. + +FIELD-SYMBOLS TYPE c_len_3. + +"Implicit casting +ASSIGN chars TO CASTING. + +FIELD-SYMBOLS TYPE data. + +"Explicit casting +ASSIGN chars TO CASTING TYPE c_len_3. ``` > **💡 Note**
-> - When working with field symbols, you should make sure that they are assigned. Otherwise, a runtime error occurs. You can check the +> - 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. > ``` abap -> IF IS ASSIGNED. +> IF IS ASSIGNED. >   ... > ENDIF. > ``` ->- You can explicitly remove the assignment of the field symbol. After this, the field symbol does not point to any data object any more. - Note that a `CLEAR` statement only initializes the value. +>- Using the statement `UNASSIGN`, you can explicitly remove the assignment of the field symbol. A `CLEAR` statement only initializes the value. > ``` abap -> UNASSIGN . -> ``` ->- When assigning data objects to fields symbols, you should pay attention to compatible types of data object and field symbol. There - is also an ABAP syntax with which you can carry out type casting for incompatible types. You can cast either implicitly or explicitly by specifying the concrete type. The addition [`TYPE HANDLE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapassign_casting&sap-language=EN&sap-client=000&version=X&anchor=!ABAP_ADDITION_5@5@&tree=X) - is relevant for [Runtime Type Services - (RTTS)](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrtti.htm). -> ``` abap -> TYPES c_len_3 TYPE c LENGTH 3. -> DATA(chars) = 'abcdefg'. -> -> FIELD-SYMBOLS TYPE c_len_3. -> "Implicit casting -> ASSIGN chars TO CASTING. -> -> FIELD-SYMBOLS TYPE data. -> "Explicit casting -> ASSIGN chars TO CASTING TYPE c_len_3. +> UNASSIGN . > ``` +>- See more information on the addition `CASTING` [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapassign_casting.htm). -**Using field symbols** +**Field symbols in use** -When accessing field symbols, you address the memory area of an existing data object. After an assignment, you might assign the data object another value: ``` abap +"For example, in assignments DATA: number TYPE i VALUE 1. FIELD-SYMBOLS TYPE i. ASSIGN number TO . = 2. -"number has now the value 2 -``` -As mentioned, field symbols are often used when working with internal tables, for example, in `LOOP` statements. In this context, -field symbols are very handy. You can avoid an actual copying of content to a work area during the loop. In doing so, the loop is considerably faster especially when dealing with large tables. You can assign the field symbol using the `ASSIGNING` addition. With `ASSIGNING FIELD-SYMBOL(...)`, you can make use of a field symbol declared inline and assign the field symbol in one go. +"The data object 'number' has now the value 2. -``` abap +"Loops +"Here, field symbols are handy since you can avoid an +"actual copying of the table line to boost performance. SELECT * FROM zdemo_abap_fli INTO TABLE @DATA(itab). @@ -214,7 +186,7 @@ FIELD-SYMBOLS LIKE LINE OF itab. LOOP AT itab ASSIGNING . -carrid = ... "The field symbol represents a line of the table. -connid = ... "Components are accessed with the component selector. - "E. g. a new value is assigned. + "Here, a new value is assigned. ... ENDLOOP. @@ -233,62 +205,38 @@ ENDLOOP. [Data references](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_reference_glosry.htm "Glossary Entry") ... -- are similar to field symbols but you can do more with them compared - to field symbols. -- point to data objects in the memory, i. e. they include the data - object's address of the memory location. +- are references that point to any data object or to their parts (for example, components, lines of internal tables). - are contained in [data reference variables](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_reference_variable_glosry.htm "Glossary Entry") in ABAP programs. -[Data reference -variables](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_reference_variable_glosry.htm "Glossary Entry") +[Data reference variables](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_reference_variable_glosry.htm "Glossary Entry") ... -- contain values as every other data object. However, the direct value is here a reference (i. e. it points to the memory location of - another data object) which means you cannot work with the value directly. You must dereference the reference first. -- are, despite only pointing to other data objects, data objects themselves that can, for example, also be used as components in - structures or columns in internal tables (which is not possible with field symbols). +- are data objects that contain a reference. +- are "opaque", i. e. the contained references cannot be accessed directly. To access the content, these variables must be dereferenced first. +- are [deep](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendeep_glosry.htm "Glossary Entry") data objects like strings and internal tables. +- are typed with the addition [`REF TO`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaptypes_references.htm) followed by a [static type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstatic_type_glosry.htm). Note the [dynamic type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendynamic_type_glosry.htm) in this context: The dynamic type of such a variable is the data type to which it actually points. This concept is particularly relevant in the context of assignments (see the assignment rules [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconversion_references.htm)). +- can be typed with a complete or generic type. However, only `data` can be used as generic type. > **💡 Note**
->- Data reference variables are considered to be - [deep](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendeep_glosry.htm "Glossary Entry") - like strings and internal tables since none of them have an assigned - dedicated memory area. Internally, strings and internal tables are - addressed using references. ->- [Object - references](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenobject_reference_glosry.htm "Glossary Entry") - and [object reference - variables](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenobject_refer_variable_glosry.htm "Glossary Entry") - are not part of this cheat sheet. To get more details, refer to the - ABAP Keyword Documentation or the cheat sheet [ABAP Object Orientation](04_ABAP_Object_Orientation.md). +> [Object references](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenobject_reference_glosry.htm "Glossary Entry") +and [object reference variables](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenobject_refer_variable_glosry.htm "Glossary Entry") are not part of this cheat sheet. To get more details, refer to the ABAP Keyword Documentation or the cheat sheet [ABAP Object Orientation](04_ABAP_Object_Orientation.md). **Declaring data reference variables** -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. - -When declared, data reference variables do not yet point to a data object. - -Examples: ``` abap +"Example declarations of data reference variables +"Note that they do not yet point to a data object. DATA: ref1 TYPE REF TO i, "Complete data type ref2 TYPE REF TO some_dbtab, "Complete data type ref3 LIKE REF TO some_data_object, ref4 TYPE REF TO data. "Generic data type ``` -**Assigning data references** - -There are multiple options to assign data references: - -**Creating data references to existing data objects**: Using the -[reference -operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenreference_operator_glosry.htm "Glossary Entry") -`REF`, you can get a data reference to an existing data object. -The older syntax [`GET REFERENCE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapget_reference.htm) -has the same effect as using the newer reference operator but should not -be used anymore. +**Creating data references to existing data objects** using the +[reference operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenreference_operator_glosry.htm "Glossary Entry") +`REF`. ``` abap "Declaring a data object @@ -313,22 +261,20 @@ DATA(ref2) = REF #( num ). DATA(ref3) = REF string( `hallo` ). -"GET REFERENCE OF; do not use anymore +"The older syntax GET REFERENCE having the same effect +"should not be used anymore. "GET REFERENCE OF num INTO ref1. "GET REFERENCE OF num INTO DATA(ref4). ``` **Creating new data objects at runtime**: You create an [anonymous -data -object](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenanonymous_data_object_glosry.htm "Glossary Entry") -at runtime by placing the reference in the variable and providing the -desired type. You can use the [instance +data object](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenanonymous_data_object_glosry.htm "Glossary Entry") +at runtime by assigning the reference to the data object of a data reference variable. You can use the [instance operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninstance_operator_glosry.htm "Glossary Entry") [`NEW`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_new.htm). The older syntax [`CREATE DATA`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcreate_data.htm) has the same effect as using the newer instance operator. -Examples: ``` abap "Declaring data reference variables @@ -353,8 +299,7 @@ TYPES i_table TYPE STANDARD TABLE OF i WITH EMPTY KEY. DATA(ref3) = NEW i_table( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 5 ) ). -"Older syntax. - +"CREATE DATA statements (older syntax) DATA ref4 TYPE REF TO string. DATA ref5 TYPE REF TO data. @@ -366,63 +311,35 @@ CREATE DATA ref5 TYPE p LENGTH 6 DECIMALS 2. CREATE DATA ref5 LIKE ref4. ``` -**Assigning/Copying existing data references**: You can copy a data reference into another one. Note that static types of both data -reference variables must be compatible and that only the reference is copied and not the data object as such. That means that, when copied, both data reference variables point to the same data object. +**Assigning existing data references** to other data references. As mentioned above 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. -Notes: -- Data reference variables have both a -[static](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstatic_type_glosry.htm "Glossary Entry") -and a [dynamic -type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendynamic_type_glosry.htm "Glossary Entry"). -- When declaring a data reference variable, e. g. `DATA ref TYPE REF TO -i.`, you determine the static type. This type is either a -non-generic (`i` in the example) or a generic type (like -`data` or `any`; e. g. `DATA ref TYPE REF TO -data.`). -- The dynamic type is determined at runtime of the -program and is the data type of a referenced data object. Especially in -the context of assigning data references (and also object references), -this differentiation is relevant. -- The following basic rule applies: The -assignment of a data reference variable to another one is possible if -the static type of the target reference variable is more general than or -the same as the dynamic type of the source reference variable. -- If it can -be statically checked that an assignment is possible, the assignment is -done using the [assignment -operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenassignment_operator_glosry.htm "Glossary Entry") -`=` that triggers an -[upcast](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenup_cast_glosry.htm "Glossary Entry") -automatically. Otherwise, it is a -[downcast](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendown_cast_glosry.htm "Glossary Entry"). -Here, the assignability is not checked until runtime. The downcast must -be triggered explicitly using [casting -operators](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencasting_operator_glosry.htm "Glossary Entry"), -either with the [constructor -operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_operator_glosry.htm "Glossary Entry") -[`CAST`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_cast.htm) -(in older code you might see -[`?=`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmove_cast.htm)), -for the assignment of data reference variables. -- See more information in -the topic [Assignment Rules for Reference -Variables](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconversion_references.htm). +Excursion: Static vs. dynamic type, upcasts and downcasts +- Data reference variables have ... + - a [static type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstatic_type_glosry.htm "Glossary Entry"). This is the type you specify when declaring the variable, i. e. `i` is the static type in this example: `DATA ref TYPE REF TO i.`. The static type can also be a generic data type: `DATA ref TYPE REF TO data.`. + - a [dynamic type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendynamic_type_glosry.htm "Glossary Entry"), the type of a data object to which the reference variable actually points to at runtime. +- For an assignment to work, the differentiation is particularly relevant since the following 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. -The following example demonstrates up- and downcasts with the assignment -of data reference variables typed with a complete and generic data type: +- This is where the concepts of [upcast](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenup_cast_glosry.htm "Glossary Entry") and [downcast](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendown_cast_glosry.htm "Glossary Entry") enter the picture. + - Up and and down? It originates from the idea of moving up or down in an [inheritance tree](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninheritance_tree_glosry.htm). In an assignment between reference variables, the target variable inherits the dynamic type of the source variable. + - Upcast: If the static type of the target variables is **less specific or the same** as the static type of the source variable, an assignment is possible. This includes, for example, assignments with the [assignment operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenassignment_operator_glosry.htm) `=`. + - Downcast: If the static type of the target variable is **more specific** than the static type of the source variable, a check must be made at runtime before the assignment is done. If you indeed want to trigger such a downcast, you must do it explicitly in your code. You can do this, for example, using the + [constructor operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_operator_glosry.htm "Glossary Entry") +[`CAST`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_cast.htm). In older code, you might see +the operator [`?=`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmove_cast.htm)). + - In contrast to a downcast, an upcast does not have to be done explicitly. However, you can - but need not - use the mentioned operators for upcasts, too. -Syntax: ``` abap +"Examples demonstrating up- and downcasts + "Declaring data reference variables - DATA ref1 TYPE REF TO i. - DATA ref2 TYPE REF TO i. ref1 = NEW #( 789 ). -"Copying data reference +"Assignments ref2 = ref1. "Casting @@ -445,28 +362,15 @@ DATA ref6 TYPE REF TO data. ref6 = NEW i( 654 ). ref5 = CAST #( ref6 ). -"Alternative, yet old syntax to the CAST operator +"Old casting operator ref5 ?= ref6. ``` -**Accessing data references** +**Addressing data references** -The content of data objects a data reference refers to can only be -accessed via dereferencing data reference variables using the -[dereferencing -operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendereferencing_operat_glosry.htm "Glossary Entry") -`->*`. - -> **💡 Note**
->- When dereferencing a data reference variable that has a structured - data type, you can use the [component - selector](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomponent_selector_glosry.htm "Glossary Entry") - `->` to access individual components. ->- In older ABAP releases, you could not dereference data reference - variables typed with a generic type. You had to do an assignment to - a field symbol first. - -Syntax: +Before addressing the content of data objects a data reference points to, you must dereference data reference variables. Use the +[dereferencing operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendereferencing_operat_glosry.htm "Glossary Entry") +`->*`. To check if dereferncing works, you can use a logical expression with [`IS BOUND`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_bound.htm). ``` abap "Creating data reference variables and assign values @@ -499,44 +403,46 @@ DATA(calc) = 1 + ref_gen->*. "Complete structure DATA(struc) = ref_carr->*. -"Individual 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_carr->carrid. ref_carr->carrid = 'UA'. -"This syntax also works but it is less comfortable. +"This syntax also works but it is less "comfortable". ref_carr->*-carrname = 'United Airlines'. + +"Checking if a data reference variable can be dereferenced. +IF ref_carr IS BOUND. +  ... +ENDIF. + +"Explicitly removing a reference +"However, the garbage collector takes care of removing the references +"automatically once the data is not used any more by a reference. +CLEAR ref_carr. ``` -> **💡 Note**
-> - You can check if a data reference can be dereferenced by using a - logical expression with [`IS BOUND`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_bound.htm): -> ``` abap -> IF ref IS BOUND. ->   ... -> ENDIF. -> ``` ->- If you explicitly want to remove a reference from a data reference variable, you can use a `CLEAR` statement. However, the - [garbage collector](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abengarbage_collector_glosry.htm "Glossary Entry") - takes over the reference removal automatically once the data is not used any more by a reference. -> ``` abap -> CLEAR ref. -> ``` +**Data references in use** -**Using data references** +Some example contexts of using data references are as follows: -Some contexts of using data references are as follows: - -**Overwriting data reference variables**: A data reference variable is overwritten when a new object is created with a data reference -variable already pointing to a data object. +**Overwriting data reference variables**: ``` abap ref = NEW i( 1 ). + +"ref is overwritten here because a new object is created +"with a data reference variable already pointing to a data object ref = NEW i( 2 ). ``` -**Keeping data references**: If your use case is to retain the data references and you want to prevent that data references are overwritten -when using the same reference variable, you can put the reference variables in internal tables. The following code shows that three data -references are created with the same reference variable. + +**Retaining data references**: ``` abap +"This snippet shows that three data references are created +"with the same reference variable. Storing them in an internal table +"using the TYPE TABLE OF REF TO prevents the overwriting. + DATA: ref TYPE REF TO data, itab TYPE TABLE OF REF TO data, number TYPE i VALUE 0. @@ -554,12 +460,14 @@ DO 3 TIMES. ENDDO. ``` -**Processing internal tables**: Similar to using field symbols, you can avoid the copying of table rows into a work area, for example, in a -loop using data reference variables and a `REFERENCE INTO` statement. In doing so, the processing of internal tables is much faster -than copying table lines to a work area. In the code snippet, an inline declaration is used in the `LOOP` statement. +**Processing internal tables**: ``` abap -"Fill an internal table. +"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. + +"Filling an internal table. SELECT * FROM zdemo_abap_fli INTO TABLE @DATA(fli_tab). @@ -567,15 +475,16 @@ LOOP AT fli_tab REFERENCE INTO DATA(ref). "A component of the table line might be addressed. ref->carrid = ... - ... ENDLOOP. ``` -**Data reference variables 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. +**Data reference variables as part of structures and internal tables**: ``` abap -"Structure +"In contrast to field symbols, data reference variables can be used as +"components of structures or columns in internal tables. +"Structure DATA: BEGIN OF struc, num TYPE i, ref TYPE REF TO i, @@ -600,140 +509,123 @@ itab[ 1 ]-ref->* = 123. or a data reference variable. It depends on your use case. However, data reference variables are more powerful as far as their usage options are concerned, and they better fit into the modern (object-oriented) ABAP -world. Recommended read: [Accessing Data Objects -Dynamically](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abendyn_access_data_obj_guidl.htm "Guideline"). +world. Recommended read: [Accessing Data Objects Dynamically (F1 docu for standard ABAP)](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abendyn_access_data_obj_guidl.htm "Guideline").

(back to top)

## Dynamic ABAP Statements -Dynamic aspects particularly enter the picture when considering the -options of dynamic ABAP statements. In this context, you can make use of -[tokens](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentoken_glosry.htm "Glossary Entry") -put within parentheses and included as operands in many ABAP statements -(e. g. `SORT table BY (field_name).`). The content of the token -is character-like and should be provided in capital letters. The content -is determined at runtime, e. g. a user entry in an input field whose -content is then part of an ABAP statement. +As already touched on above, there are ABAP statements that support the dynamic specification of syntax elements. +In this context, you can use character-like data objects (or, for example, in the `SELECT` list, a standard table with a character-like row type) - the content is usually provided in capital letters - specified within a pair of parentheses. They can be included as operands in many ABAP statements +(e. g. `SORT table BY (field_name).`). -Note that especially in this context, static checks are not possible, i. -e. if you have an ABAP statement using such a token, it cannot be -determined at compile time whether the operand that is passed is valid. -This can cause runtime errors. +Note that this has downsides, too. Consider some erroneous character-like content of such data objects. There is no syntax warning. At runtime, it can lead to runtime errors. +For the rich variety of options (where dynamic specification is possible for ABAP statements), check the ABAP Keyword Documentation. The following snippets are meant to give you an idea and overview. -You can make use of the following dynamic token specification options: +- Dynamically specifying data objects -1. **Dynamic specification of data objects and fields** + ``` abap + "Here, the names of fields are stored in a character-like data object -The names of data objects and fields are determined at runtime. + "The sorting is done by a field that is determined at runtime. + DATA(field_name) = 'SOME_FIELD'. "maybe the data object/field name is changed at runtime + ... + SORT itab BY (field_name). -Examples: -``` abap -"The sorting is done by a field that is determined at runtime. + "A field symbol is assigned a data object; here, an attribute of a class -SORT itab BY (field_name). + ASSIGN class=>(attribute_name) TO FIELD-SYMBOL(). -"A field symbol is assigned a data object; here, an attribute of a class + "In newer ABAP releases, you can dynamically specify structure components using this syntax + ASSIGN struc-(comp1) TO . + ``` -ASSIGN class=>(attribute_name) TO FIELD-SYMBOL(). -``` +- Dynamically specifying data types -2. **Dynamic specification of types** + ``` abap + "Anonymous data objects are created using a type determined at runtime. + "Note that the NEW operator cannot be used here. -The name of a data or object type is determined at runtime. + CREATE DATA ref TYPE (some_type). + CREATE DATA ref TYPE TABLE OF (some_type). -Examples: -``` abap -"Anonymous data objects are created using a type determined at runtime. -"Note that the NEW operator cannot be used here. + "Assigning a data object to a field symbol casting a type -CREATE DATA ref TYPE (some_type). -CREATE DATA ref TYPE TABLE OF (some_type). + ASSIGN dobj TO CASTING TYPE (some_type). -"Assigning a data object to a field symbol casting a type + "Assigning a structure component dynamically to a field symbol that is declared inline -ASSIGN dobj TO CASTING TYPE (some_type). + DATA struct TYPE zdemo_abap_flsch. -"Assigning a structure component dynamically to a field symbol that is declared inline + ASSIGN struct-('CARRID') TO FIELD-SYMBOL(). + ``` -DATA struct TYPE zdemo_abap_flsch. +- Dynamic specification of clauses in ABAP SQL statements -ASSIGN struct-('CARRID') TO FIELD-SYMBOL(). -``` -3. **Dynamic specification of clauses in ABAP SQL statements** + ``` abap + "Dynamic SELECT list -For example, a token that includes the `WHERE` clause conditions in a `SELECT` statement. The token can also be an internal table of a character-like line type. + DATA(select_list) = `CARRID, CONNID, COUNTRYFR, COUNTRYTO`. -Examples: -``` abap -"Dynamic SELECT list + SELECT (select_list) + FROM zdemo_abap_fli + INTO TABLE @itab. -DATA(select_list) = `CARRID, CONNID, COUNTRYFR, COUNTRYTO`. + "Dynamic FROM clause -SELECT (select_list) - FROM zdemo_abap_fli - INTO TABLE @itab. + DATA(table) = `ZDEMO_ABAP_FLI`. -"Dynamic FROM clause + SELECT * + FROM (table) + INTO TABLE @itab. -DATA(table) = `ZDEMO_ABAP_FLI`. + "Dynamic WHERE clause + "This is an example for using an internal table with a character-like row type + DATA(where_clause) = VALUE string_table( ( `CARRID = 'LH'` ) + ( `OR CARRID = 'AA'` ) ). -SELECT * - FROM (table) - INTO TABLE @itab. + SELECT * + FROM zdemo_abap_fli + WHERE (where_clause) + INTO TABLE @itab. + ``` -"Dynamic WHERE clause -DATA(where_clause) = `CARRID = 'LH'`. +- Dynamic invoke: Dynamically specifying [procedure](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenprocedure_glosry.htm "Glossary Entry") calls -SELECT * - FROM zdemo_abap_fli - WHERE (where_clause) INTO TABLE @itab. -``` + ``` abap + "Note that dynamic method calls require a CALL METHOD statement. -4. **Dynamic specification of [procedures](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenprocedure_glosry.htm "Glossary Entry")** + "Note: The following 3 examples assume that there are no + "mandatory parameters defined for the method. + "Method dynamically specified + CALL METHOD class=>(meth). -Names are specified dynamically, e. g. the names of classes and methods. + "Class dynamically specified + CALL METHOD (class)=>meth. -Examples: -``` abap -"Dynamic method calls -"Note that these calls require a CALL METHOD statement. + "Class and method dynamically specified + CALL METHOD (class)=>(meth). -"Method dynamically specified. -CALL METHOD class=>(meth). + "Assigning actual parameters to the formal parameters statically + CALL METHOD class=>(meth) EXPORTING p1 = a1 p2 = a2 ... + IMPORTING p1 = a1 p2 = a2 ... -"Class dynamically specified. -CALL METHOD (class)=>meth. + "Assigning actual parameters to the formal parameters dynamically + DATA ptab TYPE abap_parmbind_tab. + ptab = ... -"Class and method dynamically specified. -CALL METHOD (class)=>(meth). + CALL METHOD class=>(meth) PARAMETER-TABLE ptab. -"Specifying parameters -CALL METHOD class=>(meth) IMPORTING param = ... . - -"Parameters and exceptions can also be specified dynamically in tables. - -CALL METHOD class=>(meth) PARAMETER-TABLE ptab. - -CALL METHOD class=>(meth) PARAMETER-TABLE ptab EXCEPTION-TABLE etab. -``` - -Regarding the addition `PARAMETER-TABLE`, you can assign [actual -parameters](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenactual_parameter_glosry.htm "Glossary Entry") -to [formal -parameters](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenformal_parameter_glosry.htm "Glossary Entry") -dynamically using the table `ptab` that is of type -`ABAP_PARMBIND_TAB`. The table must be filled and have a -line for all non-optional parameters. The line type is -`ABAP_PARMBIND`. The following fields are relevant: - -- `name`: The name of the formal parameter. -- `kind`: Specifies the kind of parameter, e. g. importing or exporting parameter. You can make use of the constants defined in class `CL_ABAP_OBJECTDESCR`. Note that if the method signature has an importing parameter, it must be specified as exporting parameter here and vice versa. -- `value`: Specifies a data reference to the actual parameter. - -Errors raise catchable exceptions of class `CX_SY_DYN_CALL_ERROR`. Using the addition `EXCEPTION-TABLE` and an internal table of type `ABAP_EXCPBIND_TAB`, you can handle non-[class-based -exceptions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenclass_based_exception_glosry.htm "Glossary Entry"). + "Notes on PARAMETER-TABLE ptab + "- The table (of type abap_parmbind_tab; line type is abap_parmbind) must + " be filled and have a line for all non-optional parameters. + "- Components: name -> formal parameter name + " kind -> kind of parameter, e. g. importing + " value -> pointer to appropriate actual parameter, + " is of type REF TO data + "The addition EXCEPTION-TABLE for exceptions is not dealt with here. + ```

(back to top)

@@ -746,7 +638,7 @@ containing methods for [Runtime Type Creation (RTTC)](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrun_time_type_creation_glosry.htm "Glossary Entry") and [Runtime Type Identification (RTTI)](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrun_time_type_identific_glosry.htm "Glossary Entry"). -Using these classes, you can +Using these classes, you can ... - get type information on data objects, data types or [instances](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninstance_glosry.htm "Glossary Entry") @@ -779,12 +671,11 @@ So, the [superclass](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensuperclass_glosry.htm "Glossary Entry") `CL_ABAP_TYPEDESCR` has multiple [subclasses](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensubclass_glosry.htm "Glossary Entry"), -for example, to deal with each kind of type. Among them, there are, for -example, structures or tables. Working with this superclass and its -subclasses means making use of +for example, to deal with each kind of type. +Working with this inheritance tree means making use of [casts](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abencast_glosry.htm "Glossary Entry"), especially -[downcasts](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendown_cast_glosry.htm "Glossary Entry"). +[downcasts](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendown_cast_glosry.htm "Glossary Entry") when retrieving information at runtime. Detailing out all the possibilities for the information retrieval and type creation is beyond scope. Check the information, options and various methods that can be used in the class documentation, e. g. using @@ -800,7 +691,7 @@ declarations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.h chaining](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenmethod_chaining_glosry.htm "Glossary Entry") comes in handy, too. ``` abap -"The properties of a type are retrieved. +"The properties of a type are retrieved using RTTI DATA(some_type) = cl_abap_typedescr=>describe_by_data( var ). @@ -819,38 +710,44 @@ DATA(attributes) = CAST cl_abap_classdescr( )->attributes. ``` -The following example demonstrates the creation of an internal table -type based on a [DDIC -type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenddic_type_glosry.htm "Glossary Entry"). -Furthermore, an internal table is created based on this type. The type -itself is a sorted table (constants can also be used here). Unique keys -are defined in a dedicated table of type -`ABAP_KEYDESCR_TAB` that is part of the -`cl_abap_tabledescr=>create` method call. - +The following example demonstrates the dynamic creation of data objects. Note the [`TYPE HANDLE`](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abapcreate_data_handle.htm) -addition as part of the `CREATE DATA` statement that is used -when referring to dynamically created data types. +addition as part of the `CREATE DATA` statement that is used when referring to dynamically created data types. ``` abap +"RTTC examples + +"Creation of an anonymous data object using a type description object for a +"dictionary structure that is obtained using RTTI + +"Declaring a data reference variable with a generic type +DATA dref TYPE REF TO data. + +"Getting type description using RTTI +DATA(type) = CAST cl_abap_datadescr( + cl_abap_typedescr=>describe_by_name( 'ZDEMO_ABAP_CARR' ) ). + +"Creating an anonymous data object using the retrieved type description +CREATE DATA dref TYPE HANDLE type. + +"Creating an internal table dynamically + +"Getting type description using RTTI DATA(line_type) = CAST cl_abap_structdescr( cl_abap_tabledescr=>describe_by_name( `ZDEMO_ABAP_CARR` ) ). "Defining primary table keys of internal table type to be created +DATA(itab_keys) = VALUE abap_keydescr_tab( ( name = 'CARRID' ) + ( name = 'CARRNAME' ) ). -DATA(key_tab) = VALUE abap_keydescr_tab( ( name = 'CARRID' ) - ( name = 'CARRNAME' ) ). - -"Creating internal table type - +"Creating internal table type using the create method of cl_abap_tabledescr DATA(table_type) = cl_abap_tabledescr=>create( p_line_type = line_type p_table_kind = cl_abap_tabledescr=>tablekind_sorted p_unique = cl_abap_typedescr=>true - p_key = key_tab ). - -"Create internal table based on the created table type + p_key = itab_keys ). +"Creating internal table based on the created table type DATA ref_tab TYPE REF TO data. CREATE DATA ref_tab TYPE HANDLE table_type. @@ -861,9 +758,9 @@ CREATE DATA ref_tab TYPE HANDLE table_type. ## Further Information - It is recommended that you also consult section [Dynamic Programming Techniques (F1 docu for standard ABAP)](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abendynamic_prog_technique_gdl.htm) in the ABAP Keyword Documentation since it provides important aspects that should be considered when dealing with dynamic programming in general (e. g. security aspects or runtime error prevention). - There are even further dynamic programming techniques in the unrestricted language scope like the -generation or execution of programs at runtime. They are not part of this cheat sheet. Find more details on the related syntax (e. g. `GENERATE SUBROUTINE POOL`, `READ REPORT` and `INSERT REPORT` in the ABAP Keyword Documentation for Standard ABAP: [Dynamic Program Development](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenabap_language_dynamic.htm) +generation or execution of programs at runtime. They are not part of this cheat sheet. Find more details on the related syntax (e. g. `GENERATE SUBROUTINE POOL`, `READ REPORT` and `INSERT REPORT` in the ABAP Keyword Documentation for Standard ABAP: [Dynamic Program Development (F1 docu for standard ABAP)](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenabap_language_dynamic.htm) ## Executable Example [zcl_demo_abap_dynamic_prog](./src/zcl_demo_abap_dynamic_prog.clas.abap) -Note the steps outlined [here](README.md#-getting-started-with-the-examples) about how to import and run the code. +Note the steps outlined [here](README.md#-getting-started-with-the-examples) about how to import and run the code. \ No newline at end of file diff --git a/src/zcl_demo_abap_dynamic_prog.clas.abap b/src/zcl_demo_abap_dynamic_prog.clas.abap index f40eae1..5a98e90 100644 --- a/src/zcl_demo_abap_dynamic_prog.clas.abap +++ b/src/zcl_demo_abap_dynamic_prog.clas.abap @@ -11,8 +11,8 @@ * and runtime type creation (RTTC) * - To provide a "real" dynamic determination at runtime, the example * includes local classes in the CCIMP include (local types tab in ADT) -* whose methods return character-like content to be used in the tokens -* of ABAP statements. The content is predefined in these classes but +* whose methods return character-like content to be used in the +* ABAP statements. The content is predefined in these classes but * the content that is actually used in the end is random. * * ----------------------- GETTING STARTED ----------------------------- @@ -56,41 +56,30 @@ CLASS zcl_demo_abap_dynamic_prog DEFINITION CLASS-METHODS: class_constructor. -protected section. + PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. - - -CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. - +CLASS zcl_demo_abap_dynamic_prog IMPLEMENTATION. METHOD class_constructor. "Filling demo database tables. zcl_demo_abap_flight_tables=>fill_dbtabs( ). ENDMETHOD. - METHOD if_oo_adt_classrun~main. DATA(output) = NEW zcl_demo_abap_display( out ). - output->display( `Demo: Dynamic Programming` ). - output->display( `Field Symbols` ). + output->display( `ABAP Cheat Sheet: Dynamic Programming` ). + +********************************************************************** + + output->display( `Excursion: Field Symbols` ). output->display( `1) Declaring Field Symbols` ). - "Field symbols are declared with the FIELD-SYMBOLS statement which is - "also possible as chained statement using a colon. You provide the name - "of the field symbol between angle brackets. You can either type them - "with a complete data type or with a generic type. - "The example includes some demo data declarations and type definitions - "to refer to. Various field symbols are declared with both complete and - "generic data types. Note: There are plenty of options for generic ABAP - "types. The most prominent is 'data' that stands for any data type (the - "older generic type 'any' has the same effect). - - "Some data declarations and type definitions + "Some data declarations and type definitions used further down DATA: str TYPE string. TYPES: BEGIN OF struc, "Structured data type @@ -99,24 +88,44 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. 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 using a colon. FIELD-SYMBOLS: TYPE i, TYPE zdemo_abap_flsch, TYPE LINE OF tab_type, LIKE str. "Generic types + "There are plenty of options for generic ABAP types. Check the + "keyword docu. + "The most prominent is 'data' that stands for any data type (the + "older generic type 'any' has the same effect). FIELD-SYMBOLS TYPE csequence. FIELD-SYMBOLS TYPE data. FIELD-SYMBOLS TYPE any. FIELD-SYMBOLS TYPE ANY TABLE. - output->display( `No output for this section.` ). + "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(). + ... + ENDLOOP. + + output->display( `No output for this section. See the code.` ). + +********************************************************************** output->next_section( `2) Assigning Data Objects to Field Symbols` ). - "When assigning data objects to field symbols with the ASSIGN statement, - "field symbols receive all properties and values from the data objects. + "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. @@ -143,11 +152,18 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. tab_a TO , tab_a TO . + "Inline declaration is possible, too. The type is automatically derived. + ASSIGN num_a TO FIELD-SYMBOL(). + "Assigning structure components to field symbols + "Component position ASSIGN COMPONENT 2 OF STRUCTURE struc_a TO . + "Component name ASSIGN COMPONENT 'CONNID' OF STRUCTURE struc_a TO . - output->display( `No output for this section.` ). + output->display( `No output for this section. See the code.` ). + +********************************************************************** output->next_section( `3) Checking Field Symbol Assignment` ). @@ -177,15 +193,15 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( `Field symbol is not assigned.` ). ENDIF. +********************************************************************** + output->next_section( `4) Unassigning Data Objects from Field Symbols` ). - "You can explicitly remove the assignment of a field symbol. After this, - "the field symbol does not point to any data object any more. Note that - "a CLEAR statement only initializes the value. - "The example includes the declaration of a data object and its - "assignment to a field symbol. It is then unassigned using an UNASSIGN - "statement. The effect is demonstrated using logical expressions with IS - "ASSIGNED. + "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. @@ -207,13 +223,13 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( `2. Field symbol is not assigned.` ). ENDIF. +********************************************************************** + output->next_section( `5) Type Casting with Field Symbols` ). - "When assigning data objects to fields symbols, you should pay attention - "to compatible types of data object and field symbol. There is also an - "ABAP syntax with which you can carry out type casting for incompatible - "types. You can cast either implicitly or explicitly by specifying the - "concrete type. + "Use the CASTING addition for matching types of data object and field symbol + "when assigning memory areas. You can cast either implicitly or explicitly + "by specifying the concrete type. "In the example, a data type and object are created. The data object is "assigned to a field symbol. In case of implicit casting, the field symbol "is typed with the created data type, so only CASTING is needed. In case @@ -227,22 +243,23 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. FIELD-SYMBOLS TYPE c_len_3. - ASSIGN chars TO CASTING. "Implicit casting + "Implicit casting + ASSIGN chars TO CASTING. FIELD-SYMBOLS TYPE data. - ASSIGN chars TO CASTING TYPE c_len_3. "Explicit casting + "Explicit casting + ASSIGN chars TO CASTING TYPE c_len_3. output->display( input = name = `` ). output->display( input = name = `` ). - output->next_section( `6) Accessing Field Symbols` ). +********************************************************************** + + output->next_section( `6) Addressing Field Symbols` ). - "When accessing field symbols, you just address the value of the - "assigned data object. You can use the field symbols as any other data - "object. "The example includes multiple data objects that are assigned to field - "symbols. It is demonstrated that field symbols are accessed in various + "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. @@ -269,7 +286,7 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. ASSIGN tab_e TO . ASSIGN tab_e TO . - "Change values + "Changing values = 789. output->display( input = name = `` ). @@ -312,7 +329,6 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. CATCH cx_sy_itab_line_not_found INTO DATA(error_e). ENDTRY. - "Note: Declarations not possible. SELECT * FROM zdemo_abap_carr ORDER BY carrid @@ -321,22 +337,19 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( input = name = `` ). +********************************************************************** + output->next_section( `7) Using Field Symbols when Processing ` && `Internal Tables` ). - "Field symbols are often used when working with internal tables, for - "example, in LOOP statements. In this context, field symbols are very - "handy. You can avoid an actual copying of content to a work area during - "the loop. In doing so, the loop is considerably faster especially when - "dealing with large tables. You can assign the field symbol using the - "ASSIGNING addition. Using ASSIGNING FIELD-SYMBOL(...), you can directly - "declare and assign the field symbol in one go. + "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 + "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 @@ -373,28 +386,29 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. -seatsocc_b, -seatsocc_f. - "Another itab is filled. + "Filling another itab = VALUE #( BASE ( ) ). ENDLOOP. output->display( input = tab_f1 ). - "Regrding the field symbol, the data type is derived automatically. + "Regarding the field symbol, the data type is derived automatically. LOOP AT ASSIGNING FIELD-SYMBOL(). -connid = '100'. -fldate = cl_abap_context_info=>get_system_date( ) + 1. -price = -price - 50. -currency = 'USD'. - "Another itab is filled. + "Filling another itab = VALUE #( BASE ( ) ). ENDLOOP. output->display( input = name = `` ). output->display( input = name = `` ). - output->next_section( `8) Using Field Symbols to Process ` && - `All Components of a Structure` ). +********************************************************************** + + output->next_section( `8) Field Symbols in the Context of Processing a Structure` ). "In this example, all components of a structure are processed using "field symbols and an ASSIGN COMPONENT ... OF STRUCTURE ... statement. @@ -433,6 +447,8 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. ENDDO. +********************************************************************** + output->next_section( `Data references` ). output->display( `9) Declaring Data References` ). @@ -453,14 +469,13 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. ref_a5 TYPE ref_type, ref_a6 TYPE REF TO data. "Generic data type - output->display( `No output for this section.` ). + output->display( `No output for this section. See the code.` ). + +********************************************************************** output->next_section( `10) Creating Data References ` && `to Existing Data Objects` ). - "Using the reference operator REF, you can get a data reference to an - "existing data object. The older syntax GET REFERENCE has the same - "effect as using the newer reference operator. "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 @@ -485,15 +500,9 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. "You can explicitly specify the data type after REF. DATA(ref_b3) = REF string( `hallo` ). - "Older syntax: GET REFERENCE - "Note: This example is only possible in unrestricted language scope. - "If you are in an environment allowing unrestricted language scope, - "you can comment the following statements in. -* GET REFERENCE OF number_b INTO ref_b1. -* GET REFERENCE OF number_b INTO DATA(ref_b4). -* GET REFERENCE OF `abcdef` INTO DATA(ref_b5). + output->display( `No output for this section. See the code.` ). - output->display( `No output for this section.` ). +********************************************************************** output->next_section( `11) Creating New Data Objects at Runtime` ). @@ -538,21 +547,24 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( input = ref_c2->* name = `ref_c2->*` ). output->display( input = ref_c3->* name = `ref_c3->*` ). - output->next_section( `12) Copying Existing Data References` ). +********************************************************************** - "You can copy a data reference into another one. Note that static types - "of both data reference variables must be compatible and that only the - "reference is copied and not the data object as such. That means that, - "when copied, both data reference variables point to the same data - "object. - "Data references also support upcasts and downcasts in case a data - "reference variable is of generic type in the assignment. - "Upcast here: The target data reference variable is of generic type, the + output->next_section( `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 here: The target data reference variable is of complete type, the + "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 '?='. + "operators, either with the constructor operator CAST or the older ?=. "Declaring data reference variables DATA ref_d1 TYPE REF TO i. @@ -560,19 +572,24 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. ref_d1 = NEW #( 789 ). - ref_d2 = ref_d1. "Copying data reference + "Assigning data reference + ref_d2 = ref_d1. "Casting - DATA(ref_d3) = NEW i( 321 ). "Complete type + "Complete type + DATA(ref_d3) = NEW i( 321 ). - DATA ref_data_d1 TYPE REF TO data. "Generic type + "Generic type + DATA ref_data_d1 TYPE REF TO data. - ref_data_d1 = ref_d3. "Upcast + "Upcast + ref_data_d1 = ref_d3. "Downcasts DATA ref_d5 TYPE REF TO i. - DATA ref_data_d2 TYPE REF TO data. "Generic type + "Generic type + DATA ref_data_d2 TYPE REF TO data. ref_data_d2 = NEW i( 654 ). @@ -584,16 +601,15 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( input = ref_data_d1->* name = `ref_data_d1->*` ). output->display( input = ref_d5->* name = `ref_d5->*` ). - output->next_section( `13) Accessing Data References ` ). +********************************************************************** - "The content of data objects a data reference refers to can only be - "accessed via dereferencing data reference variables using the - "dereferencing operator '->*'. - "Note: When dereferencing a data reference variable that has a - "structured data type, you can use the component selector '->' to access - "individual components. + output->next_section( `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 - "accessed: Changing the content of a referenced data object, the use in + "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 @@ -607,18 +623,18 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. "Copying reference ref_data_e = ref_e1. - "Accessing + "Addressing "Variable receives the content. DATA(some_num) = ref_e1->*. output->display( input = ref_e1->* name = `ref_e1->*` ). - "Content of referenced data object is changed. + "Content of referenced data object is changed ref_e1->* = 10. output->display( input = ref_e1->* name = `ref_e1->*` ). - "Data reference used in a logical expression. + "Data reference used in a logical expression IF ref_e1->* > 5. output->display( `The value of ref_e1 is greater than 5.` ). ELSE. @@ -636,6 +652,8 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( input = ref_e2->* name = `ref_e2->*` ). "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'. @@ -647,6 +665,8 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( input = ref_e2->*-carrname name = `ref_e2->*-carrname` ). +********************************************************************** + output->next_section( `14) Checking if Data Reference ` && `Can Be Dereferenced` ). @@ -669,12 +689,12 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( `ref_f2 is not bound.` ). ENDIF. - output->next_section( `15) Clearing Data References` ). +********************************************************************** - "If you explicitly want to remove a reference from a data reference - "variable, you can use a CLEAR statement. However, the garbage collector - "takes over the reference removal automatically once the data is not - "used any more by a reference. + output->next_section( `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( `hallo` ). @@ -704,10 +724,12 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( `After CLEAR: ref_g1 is not bound.` ). ENDIF. +********************************************************************** + output->next_section( `16) Overwriting Data Reference Variables` ). - "A data reference variable is overwritten when a new object is created - "with a data reference variable already pointing to a data object. + "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. @@ -719,12 +741,12 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( input = ref_h1->* name = `ref_h1->*` ). - output->next_section( `17) Keeping Data References`). +********************************************************************** - "If your use case is to retain the data references and you want to - "prevent that data references are overwritten when using the same - "reference variable, you can put the reference variables in internal - "tables. + output->next_section( `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 @@ -735,11 +757,14 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. number_i TYPE i VALUE 0. DO 3 TIMES. - number_i += 1. "Add up 1 to demonstrate a changed data object. - "Create data reference and assign value - "In the course of the loop, the variable gets overwritten. + "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 ). - "Adds the reference to internal table. + + "Adding the reference to an internal table itab_i = VALUE #( BASE itab_i ( ref_data_i ) ). ENDDO. @@ -748,13 +773,14 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. `was changed in the course of the loop - in the second table ` && `entry is ` && itab_i[ 2 ]->* && `.` ). +********************************************************************** + output->next_section( `18) Processing Internal Tables Using ` && `Data References ` ). - "Similar to using field symbols, you can avoid the copying of table rows - "into a work area, for example, in a loop using data reference variables - "and a REFERENCE INTO statement. In doing so, the processing of internal - "tables is much faster than copying table lines to a work area. + "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. @@ -776,6 +802,8 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( input = flsch_tab name = `flsch_tab` ). +********************************************************************** + output->next_section( `19) Data References as Part of ` && `Structures and Internal Tables` ). @@ -815,7 +843,9 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( input = itab_l name = `itab_l` ). - output->next_section( `Dynamic ABAP Syntax Components` ). +********************************************************************** + + output->next_section( `Dynamic ABAP Statements` ). output->display( `20) Assignment of Dynamically ` && `Determined Data Objects to Field Symbols` ). @@ -830,7 +860,9 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( |Data object name determined at runtime: { dobj_name } | ). output->display( |The content of the data object is: { } | ). - output->next_section( `21) Dynamic Specification of Field Name` ). +********************************************************************** + + output->next_section( `21) Dynamically Specifying a Data Object` ). "A field is determined at runtime on whose basis a sorting is done on an "internal table. @@ -849,34 +881,10 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. |by which the sorting was done: { field_name } | ). output->display( input = carr_itab name = `carr_itab` ). - output->next_section( `22) Dynamic Specification of Data Type: ` && - `Creating Internal Table at Runtime and Filling It` ). +********************************************************************** - "The example includes the creation of an internal table based on a data - "type that is determined at runtime. Note that when using tokens, you - "cannot use the NEW operator. The SELECT statement includes a token in - "the FROM clause, too. In this example, the database table's name from - "which to be read from is included in the dynamically determined type - "name. - - DATA(type_name) = lcl_det_at_runtime=>get_dyn_table_name( ). - - DATA ref_n TYPE REF TO data. - - CREATE DATA ref_n TYPE TABLE OF (type_name). - - "Dynamic specification of a clause in an ABAP SQL statement - SELECT * - FROM (type_name) - INTO TABLE @ref_n->* - UP TO 3 ROWS. - - output->display( |Table/type name determined at runtime: { type_name } | ). - output->display( input = ref_n->* name = `ref_n->*` ). - - output->next_section( `Dynamic Specification of Clauses ` && - `in SELECT Statements` ). - output->display( `23) SELECT List` ). + output->next_section( `Dynamically Specifying Clauses in ABAP SQL SELECT Statements` ). + output->display( `22) SELECT List` ). "In the example, the SELECT list that is used in a SELECT statement is "determined at runtime. @@ -894,7 +902,9 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( |SELECT list determined at runtime: { select_list } | ). output->display( input = sel_table name = `sel_table` ). - output->next_section( `24) FROM Clause` ). +********************************************************************** + + output->next_section( `23) 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 @@ -909,7 +919,9 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( |Table name determined at runtime: { tab_name } | ). output->display( |The table { tab_name } has { count } entries.| ). - output->next_section( `25) WHERE Clause` ). +********************************************************************** + + output->next_section( `24) 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 @@ -928,284 +940,418 @@ CLASS ZCL_DEMO_ABAP_DYNAMIC_PROG IMPLEMENTATION. output->display( input = where_clause name = `where_clause` ). output->display( input = where_tab name = `where_tab` ). - output->next_section( `26) Dynamic Specification of Classes and Methods` ). +********************************************************************** - "The example includes a dynamic method call. In this case, both class - "and method are determined at runtime. - "The implementation represents a rudimentary method test. The CALL - "METHOD statement includes the addition PARAMETER-TABLE. Here, you - "specify an internal table of type ABAP_PARMBIND_TAB for the parameter - "transfer. The filling of the table must be done with care, i. e. it - "must contain exactly one line for each non-optional formal parameter. - "Furthermore, it can include a line for each optional formal parameter. - "Actually, you would need to individually fill the table for the method - "calls. - "The implementation here only uses two demo classes and its methods from - "within the program. To really test out your methods, you would need to - "provide, for example, proper input values for importing parameters of - "your methods. Here, the methods often have only a returning - "parameter, so other kinds of parameters are of no relevance in the - "example. The example is intended to give - "you a rough idea about how to work with the syntax. - "First, the class is determined at runtime. The example anticipates - "RTTI. Using RTTI, the method names of the determined class are - "retrieved. A random method is chosen. RTTI returns a table for the - "individual method information. This table is looped across. In the - "course of the loop, the parameter table is filled. The value field - "expects a data reference. Using RTTI, the type is determined and based - "on this type, a data reference is created to fill the value field. - "After the dynamic method call, the parameters table is output as well - "as the class and method names used. + output->next_section( `25) Dynamic Invoke` ). - DATA(class) = lcl_det_at_runtime=>get_dyn_class( ). + "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. - "Data declarations for parameter table - DATA: parameter TYPE abap_parmbind, - parameters TYPE abap_parmbind_tab. + lcl_det_at_runtime=>get_dyn_class_meth( IMPORTING cl = DATA(cl_name) + meth = DATA(meth_name) + ptab = DATA(p_tab) ). - "Getting methods using RTTI - DATA(methods) = CAST cl_abap_objectdescr( - cl_abap_objectdescr=>describe_by_name( class ) - )->methods. + CALL METHOD (cl_name)=>(meth_name) PARAMETER-TABLE p_tab. - "Since the constructors are present in the result table, too, - "they are deleted here from the table. - "They cannot be called directly. - DELETE methods WHERE name = `CLASS_CONSTRUCTOR`. - DELETE methods WHERE name = `CONSTRUCTOR`. + output->display( |Class name determined at runtime: { cl_name } | ). + output->display( |Method name determined at runtime: { meth_name } | ). - "Choosing a random method - IF methods IS NOT INITIAL. - DATA(random) = cl_abap_random_int=>create( - seed = cl_abap_random=>seed( ) min = 1 max = lines( methods ) ). - DATA(idx) = random->get_next( ). + output->display( `Result of method call (text stored in a variable):` ). + output->display( input = lcl_det_at_runtime=>dyn_meth_call_result name = `lcl_det_at_runtime=>dyn_meth_call_result` ). - "Retrieving method name - DATA(meth) = to_upper( methods[ idx ]-name ). + "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. - "Looping across the parameters that are contained in a table - "to get their parameters and fill the parameter table accordingly - LOOP AT methods[ name = meth ]-parameters - ASSIGNING FIELD-SYMBOL(). - parameter-name = -name. + DATA method TYPE string VALUE `FILL_STRING`. - "Note: If the method signature has an importing parameter, - "it must be specified as exporting parameter here. - "Same is true for the exporting parameter in the signature - "that must be specified as importing parameter here. - parameter-kind = SWITCH #( -parm_kind - WHEN cl_abap_objectdescr=>exporting - THEN cl_abap_objectdescr=>importing - WHEN cl_abap_objectdescr=>importing - THEN cl_abap_objectdescr=>exporting - WHEN cl_abap_objectdescr=>returning - THEN cl_abap_objectdescr=>returning - WHEN cl_abap_objectdescr=>changing - THEN cl_abap_objectdescr=>changing - WHEN cl_abap_objectdescr=>receiving - THEN cl_abap_objectdescr=>receiving ). + "Note that 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). - "Getting the parameter types using RTTI - DATA(param) = CAST cl_abap_objectdescr( - cl_abap_objectdescr=>describe_by_name( class ) - )->get_method_parameter_type( EXPORTING - p_method_name = meth - p_parameter_name = -name ). + output->display( input = lcl_det_at_runtime=>dyn_meth_call_result name = `lcl_det_at_runtime=>dyn_meth_call_result` ). - "To fill the value field in the parameter table, - "a reference must be specified. Here, the type of the parameter - "is only known at runtime. Hence, an anonymous data object is - "created to be referred to. - DATA t TYPE REF TO data. + DATA class TYPE string VALUE `LCL_DET_AT_RUNTIME`. - CREATE DATA t TYPE (param->absolute_name). + CALL METHOD (class)=>fill_string. - "Getting the reference of dereferenced anonymous data object - "into the value field. - parameter-value = REF #( t->* ). + output->display( input = lcl_det_at_runtime=>dyn_meth_call_result name = `lcl_det_at_runtime=>dyn_meth_call_result` ). - "Inserting the line into the parameters table. - INSERT parameter INTO TABLE parameters. - ENDLOOP. +********************************************************************** - TRY. - IF parameters IS NOT INITIAL. - "Dynamic method call using a CALL METHOD statement. - CALL METHOD (class)=>(meth) PARAMETER-TABLE parameters. + output->next_section( `26) RTTI: Determining Data and Object Types at Runtime` ). - output->display( |Class determined at runtime: { class } | ). - output->display( |Method determined at runtime: { meth } | ). - output->display( |Parameters table:| ). - output->display( input = parameters name = `parameters` ).. - ENDIF. - CATCH cx_sy_dyn_call_error INTO DATA(error_params). - output->display( input = error_params->get_longtext( ) name = `error_params` ). - ENDTRY. - ENDIF. + "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. - output->next_section( `27) RTTI: Get Type Descriptions` ). - - "In the example, RTTI is used to get type descriptions based on the name - "of a random type. The CASE statement handles the following type kinds: - "elementary, structure, internal table, reference, class, interface. In - "case of an elementary type, structure an internal table, the type - "description is output. In case of a reference type, the referenced type - "is output. In case of a class and interface, their methods are output. - - "Getting random type + "Retrieving type DATA(get_type) = lcl_det_at_runtime=>get_random_type( ). output->display( |Type name determined at runtime: { get_type }| ). - "Getting type description - DATA(some_type) = cl_abap_typedescr=>describe_by_name( get_type ). + DATA: dref TYPE REF TO data. - "When referring to a concrete data object, you can use this method: - "DATA(some_type) = cl_abap_typedescr=>describe_by_data( ... ). + IF get_type <> `LCL_DET_AT_RUNTIME` + AND get_type <> `IF_OO_ADT_CLASSRUN`. + TRY. + CREATE DATA dref TYPE (get_type). - CASE some_type->kind. - "Elementary type - WHEN cl_abap_typedescr=>kind_elem. - DATA(element_descr) = CAST cl_abap_elemdescr( some_type ). - output->display( input = element_descr name = `element_descr` ). + CATCH cx_sy_create_data_error. + output->display( `Create data error!` ). + ENDTRY. - "Get components of structure - WHEN cl_abap_typedescr=>kind_struct. - DATA(struct_descr) = CAST cl_abap_structdescr( some_type ). - output->display( input = struct_descr->components name = `struct_descr->components` ). + "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->* ). - "Get components of table - WHEN cl_abap_typedescr=>kind_table. - DATA(table_descr) = CAST cl_abap_tabledescr( some_type ). - DATA(tab_struc) = CAST cl_abap_structdescr( - table_descr->get_table_line_type( ) ). - output->display( input = tab_struc->components name = `tab_struc->components` ). + "Elementary type + IF some_type->kind = cl_abap_typedescr=>kind_elem. + DATA(el) = CAST cl_abap_elemdescr( some_type ). + output->display( input = el name = `el` ). - "Get referenced type - WHEN cl_abap_typedescr=>kind_ref. + "Various attributes and methods possible + output->display( input = el->type_kind name = `el->type_kind` ). + + output->display( input = el->absolute_name name = `el->absolute_name` ). + + output->display( input = el->get_relative_name( ) name = `el->get_relative_name( )` ). + + "Structure + ELSEIF some_type->kind = cl_abap_typedescr=>kind_struct. + DATA(stru) = CAST cl_abap_structdescr( some_type ). + + output->display( input = stru->absolute_name name = `stru->absolute_name` ). + + output->display( input = stru->components name = `stru->components` ). + + output->display( input = stru->struct_kind name = `stru->struct_kind` ). + + output->display( input = stru->get_components( ) name = `stru->get_components( )` ). + + "Internal table + ELSEIF some_type->kind = cl_abap_typedescr=>kind_table. + DATA(tab) = CAST cl_abap_tabledescr( some_type ). + + output->display( input = tab->absolute_name name = `tab->absolute_name` ). + + output->display( input = tab->table_kind name = `tab->table_kind` ). + + output->display( input = tab->get_keys( ) name = `tab->get_keys` ). + + output->display( input = tab->get_table_line_type( ) name = `tab->get_table_line_type( )` ). + + "Reference + ELSEIF some_type->kind = cl_abap_typedescr=>kind_ref. DATA(ref_descr) = CAST cl_abap_refdescr( some_type ). + + output->display( input = ref_descr->absolute_name name = `ref_descr->absolute_name` ). + output->display( input = ref_descr->get_referenced_type( ) name = `ref_descr->get_referenced_type( )` ). - "Get class methods - WHEN cl_abap_typedescr=>kind_class. + ELSE. + output->display( `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 ). + + output->display( input = class_desc->absolute_name name = `class_desc->absolute_name` ). + + output->display( input = class_desc->attributes name = `class_desc->attributes` ). + output->display( input = class_desc->methods name = `class_desc->methods` ). - "Get interface methods - WHEN cl_abap_typedescr=>kind_intf. + "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 ). + + output->display( input = descr_ref->absolute_name name = `descr_ref->absolute_name` ). + + output->display( input = descr_ref->kind name = `descr_ref->kind` ). + + CATCH cx_root. + output->display( `Error` ). + + ENDTRY. + + "Interface + ELSEIF some_type->kind = cl_abap_typedescr=>kind_intf. DATA(if_descr) = CAST cl_abap_intfdescr( some_type ). + + output->display( input = if_descr->absolute_name name = `if_descr->absolute_name` ). + output->display( input = if_descr->methods name = `class_desc->methods` ). - WHEN OTHERS. + ELSE. output->display( `Others ...` ). - ENDCASE. + ENDIF. - output->next_section( `28) RTTC: Creating Data Type and Data ` && - `Object Based on this Type at Runtime` ). + ENDIF. + +********************************************************************** + + output->next_section( `27) RTTC: Dynamically Creating Elementary Data Objects` ). + + "The example demonstrates RTTC as follows: + "- The method call takes care of providing the name of a built-in data type and more + "- Depending on the type an elementary data object is created + "- For demonstration purposes, RTTI 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. + output->display( `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->* ) ). + + output->display( |Built-in type determined at runtime: { b_type-builtin_type } | ). + output->display( `Created data object at runtime:` ). + output->display( input = descr_builtin_type name = `descr_builtin_type` ). + + CATCH cx_root. + output->display( `Something went wrong.` ). + ENDTRY. + +********************************************************************** + + output->next_section( `28) RTTC: Dynamically Creating Structured Data Object (1)` ). + + "The example demonstrates RTTC as follows: + "- 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. + "- A SELECT loop is used to sequentially process the read database table lines. + " Here, the processed line is just output. + "- The integer for the UP TO clause is specified at runtime. + + "Retrieving table name + DATA(type4struc) = lcl_det_at_runtime=>get_dyn_table_name( ). + + DATA ref_dynstruc TYPE REF TO data. + + "Creating structure data object + CREATE DATA ref_dynstruc TYPE (type4struc). + + "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( ). + + output->display( |Structured data type/database table name determined at runtime: { type4struc } | ). + output->display( |At most, { random_upto } lines should have been read from the database table.| ). + + "SELECT loop + SELECT * + FROM (type4struc) + INTO @ref_dynstruc->* + UP TO @random_upto ROWS. + + output->display( input = ref_dynstruc->* ). + + ENDSELECT. + +********************************************************************** + + output->next_section( `29) RTTC: Dynamically Creating Structured Data Object (2)` ). + + "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'. + + output->display( input = dref_struc->* name = `dref_struc->*` ). + +********************************************************************** + + output->next_section( `30) RTTC: Dynamically Creating Internal Table (1)` ). + + "The example demonstrates RTTC as follows: + "- 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 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). + + "Dynamic specification of FROM clause + SELECT * + FROM (type_name) + INTO TABLE @ref_n->* + UP TO 3 ROWS. + + output->display( |Table/type name determined at runtime: { type_name } | ). + output->display( input = ref_n->* name = `ref_n->*` ). + +********************************************************************** + + output->next_section( `31) RTTC: Dynamically Creating Internal Table (2)` ). "In the example an internal table type is created based on a DDIC type. - "First, the line type is retrieved using RTTI. The primary table keys - "the dynamically created table type receives is determined using an - "internal table of type abap_keydescr_tab. Using the method - "cl_abap_tabledescr=>create, the internal table type is created. - "The parameters are filled - "accordingly. You can make use of the constants provided in class - "cl_abap_tabledescr. In this case, the table type is sorted and has - "unique keys. The keys that are passed have been specified before. Then, - "an internal table is created based on this dynamically created type. In - "the CREATE DATA statement, this type is referred to using the addition - "TYPE HANDLE. Finally, the table is filled and output. + "See the comments in the code. - "Getting the line type of a DDIC table - DATA(line_type) = CAST cl_abap_structdescr( - cl_abap_tabledescr=>describe_by_name( `ZDEMO_ABAP_CARR` ) ). + "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 ) ). + + "Retrieving primary keys of the database table + DATA(field_list) = st->get_ddic_field_list( ). + + "Declaring internal table to hold the primary keys + DATA itab_keys TYPE abap_keydescr_tab. + + "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 + LOOP AT field_list ASSIGNING FIELD-SYMBOL(). + + "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 = -fieldname + type = st->get_component_type( -fieldname ) ) TO comp_table. + + "The keyflag field determines whether a field is key field or not. + "Here, it is jsut meant to have the same keys for the internal table + "as the database table. + IF -keyflag = 'X'. + "Adding the name of the field to the internal table holding the primary keys + APPEND VALUE #( name = -fieldname ) TO itab_keys. + ENDIF. + + "Just for fun. 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) = -fieldname. + ENDIF. + + ENDLOOP. - "Defining primary table keys of internal table to be created - DATA(key_tab) = VALUE abap_keydescr_tab( ( name = 'CARRID' ) - ( name = 'CARRNAME' ) ). "Creating an internal table type - DATA(table_type_sorted) = cl_abap_tabledescr=>create( - p_line_type = line_type + 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 - p_key = key_tab ). + p_key = itab_keys ). "Creating an internal table based on the created table type DATA ref_tab TYPE REF TO data. - CREATE DATA ref_tab TYPE HANDLE table_type_sorted. + CREATE DATA ref_tab TYPE HANDLE itab_type. "Filling an internal table SELECT * - FROM zdemo_abap_carr - ORDER BY carrid - INTO TABLE @ref_tab->* + FROM (table_name) + ORDER BY (dyn_order_by) + INTO CORRESPONDING FIELDS OF TABLE @ref_tab->* UP TO 3 ROWS. + output->display( |Type/Database table name determined at runtime: { table_name }| ). output->display( |Primary table keys of the created table type:| ). - output->display( input = table_type_sorted->get_keys( ) name = `table_type_sorted->get_keys( )` ). - - output->display( |Internal table entries:| ). + output->display( input = itab_type->get_keys( ) name = `itab_type->get_keys( )` ). + output->display( |Internal table entries (ordered by { dyn_order_by }):| ). output->display( input = ref_tab->* name = `ref_tab->*` ). - output->next_section( `29) RTTC: Creating Table Type with ` && - `Custom Components at Runtime` ). - - "In the example, an internal table type is created using custom - "components. - "First, a component description table is filled as the basis of the line - "type. Here, names and types are provided similar to the demo table - "zdemo_abap_carr. The type is created using both elementary types and DDIC - "data elements whose properties are retrieved using RTTI. - "As a next step, the structure description is created using - "cl_abap_structdescr=>create. Then, based on this structure description, - "an internal table type is created as shown in the previous example. The - "table type is used to create an internal table using the addition TYPE - "HANDLE as part of a CREATE DATA statement. Finally, to have some - "content in this table, a SELECT statement is used to retrieve data - "from zdemo_abap_carr. - - "Creating custom components based on built-in types and data elements in the DDIC - DATA(custom_comp) = VALUE cl_abap_structdescr=>component_table( - ( name = 'CARRIER_ID' type = cl_abap_elemdescr=>get_c( 3 ) ) - ( name = 'CARRIER_NAME' type = cl_abap_elemdescr=>get_c( 20 ) ) - ( name = 'CURRENCY' type = cl_abap_elemdescr=>get_c( 5 ) ) - ( name = 'WEBSITE' type = cl_abap_elemdescr=>get_c( 255 ) ) ). - - "Note: If you are in an environment allowing unrestricted language scope, - "you can exchange the 'currency' and 'website' component table entries with the - "following ones. It demonstrates the type property retrieval from DDIC types. - -* ( name = 'CURRENCY' type = CAST #( -* cl_abap_elemdescr=>describe_by_name( 'S_CURRCODE' ) ) ) -* ( name = 'WEBSITE' type = CAST #( -* cl_abap_datadescr=>describe_by_name( 'S_CARRURL' ) ) ) ). - - "Getting structure description - DATA(custom_structure) = cl_abap_structdescr=>create( custom_comp ). - - "Creating an internal table type - DATA(table_type_std) = cl_abap_tabledescr=>create( - p_line_type = CAST #( custom_structure ) - p_table_kind = cl_abap_tabledescr=>tablekind_sorted - p_unique = abap_true - p_key = VALUE abap_keydescr_tab( ( name = 'CARRIER_ID' ) ) ). - - "Creating an internal table based on the created table type - DATA ref_tab_2 TYPE REF TO data. - - CREATE DATA ref_tab_2 TYPE HANDLE table_type_std. - - "Filling internal table - SELECT carrid AS carrier_id, - carrname AS carrier_name, - currcode AS currency, - url AS website - FROM zdemo_abap_carr - ORDER BY carrier_id - INTO CORRESPONDING FIELDS OF TABLE @ref_tab_2->* - UP TO 3 ROWS. - - output->display( input = ref_tab_2->* name = `ref_tab_2->*` ). - ENDMETHOD. -ENDCLASS. +ENDCLASS. \ No newline at end of file diff --git a/src/zcl_demo_abap_dynamic_prog.clas.locals_imp.abap b/src/zcl_demo_abap_dynamic_prog.clas.locals_imp.abap index d2be2d3..3c70249 100644 --- a/src/zcl_demo_abap_dynamic_prog.clas.locals_imp.abap +++ b/src/zcl_demo_abap_dynamic_prog.clas.locals_imp.abap @@ -2,23 +2,36 @@ CLASS lcl_det_at_runtime DEFINITION. PUBLIC SECTION. - CLASS-METHODS: - get_dyn_table_name RETURNING VALUE(tab) TYPE string, - get_dyn_dobj RETURNING VALUE(dobj) TYPE string, - get_dyn_field RETURNING VALUE(field) TYPE string, - get_dyn_select_list RETURNING VALUE(list) TYPE string, - get_dyn_where_clause RETURNING VALUE(clause_tab) TYPE string_table, - get_dyn_class RETURNING VALUE(cl) TYPE string, - get_random_type RETURNING VALUE(random_type) TYPE string. + CLASS-DATA: string1 TYPE string, + string2 TYPE string, + string3 TYPE string, + dyn_meth_call_result TYPE string. - CLASS-DATA: string1 TYPE string, - string2 TYPE string, - string3 TYPE string. + TYPES: type_p TYPE p LENGTH 8 DECIMALS 2, "elementary type + type_struc TYPE zdemo_abap_carr, "structure type + "internal table type + type_itab TYPE SORTED TABLE OF zdemo_abap_flsch WITH NON-UNIQUE KEY carrid connid "primary key + WITH UNIQUE SORTED KEY cities COMPONENTS cityfrom cityto, "secondary key + type_ref TYPE REF TO lcl_det_at_runtime. "reference type - TYPES: type1 TYPE p LENGTH 8 DECIMALS 2, "elementary type - type2 TYPE zdemo_abap_carr, "structure type - type3 TYPE TABLE OF zdemo_abap_flsch, "internal table type - type4 TYPE REF TO lcl_det_at_runtime. "reference type + TYPES: BEGIN OF struc_builtin, + builtin_type TYPE c LENGTH 10, + len TYPE i, + dec TYPE i, + END OF struc_builtin. + + CLASS-METHODS: + get_dyn_table_name RETURNING VALUE(tab) TYPE string, + get_dyn_dobj RETURNING VALUE(dobj) TYPE string, + get_dyn_field RETURNING VALUE(field) TYPE string, + get_dyn_select_list RETURNING VALUE(list) TYPE string, + get_dyn_where_clause RETURNING VALUE(clause_tab) TYPE string_table, + get_random_type RETURNING VALUE(random_type) TYPE string, + get_builtin_type RETURNING VALUE(builtin_type) TYPE struc_builtin, + get_dyn_class_meth EXPORTING cl TYPE string + meth TYPE string + ptab TYPE abap_parmbind_tab, + fill_string. PROTECTED SECTION. PRIVATE SECTION. @@ -32,22 +45,52 @@ CLASS lcl_det_at_runtime IMPLEMENTATION. ( `ZDEMO_ABAP_CARR` ) ( `ZDEMO_ABAP_FLSCH` ) ( `ZDEMO_ABAP_FLI` ) ). "Getting random number to determine the table index at runtime. - DATA(random) = cl_abap_random_int=>create( + DATA(idx) = cl_abap_random_int=>create( seed = cl_abap_random=>seed( ) min = 1 - max = lines( flight_tables ) ). - DATA(idx) = random->get_next( ). + max = lines( flight_tables ) )->get_next( ). "Returning parameter to receive the random table name. + tab = VALUE #( flight_tables[ idx ] DEFAULT `ZDEMO_ABAP_CARR` ). + + ENDMETHOD. + + METHOD get_builtin_type. + + "Providing the names of built-in types in a string table to be selected from. + TYPES tabtyp TYPE TABLE OF struc_builtin-builtin_type WITH EMPTY KEY. + + DATA(built) = VALUE tabtyp( + ( 'd' ) + ( 'decfloat16' ) + ( 'decfloat34' ) + ( 'f' ) + ( 'i' ) + ( 'string' ) + ( 't' ) + ( 'xstring' ) + ( 'c' ) + ( 'n' ) + ( 'x' ) + ( 'p' ) + ). + + "Getting random number to determine the table index at runtime + DATA(idx) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 1 + max = lines( built ) )->get_next( ). + + "Providing the returning parameter with a random table name TRY. - tab = flight_tables[ idx ]. - CATCH cx_sy_itab_line_not_found INTO DATA(error). + builtin_type = VALUE #( builtin_type = built[ idx ] dec = idx len = idx ). + CATCH cx_sy_itab_line_not_found. + builtin_type = VALUE #( builtin_type = `p` dec = 5 len = 5 ). ENDTRY. ENDMETHOD. METHOD get_dyn_dobj. - "Providing strings with demo content. + "Providing strings with demo content string1 = |Hallo, { sy-uname }. | && |This is string1.|. string2 = |Hallo, { sy-uname }. | && @@ -55,21 +98,17 @@ CLASS lcl_det_at_runtime IMPLEMENTATION. string3 = |Hallo, { sy-uname }. | && |This is string3.|. - "Filling table with data object names. + "Filling table with data object names DATA(str_tab) = VALUE string_table( ( `STRING1` ) ( `STRING2` ) ( `STRING3` ) ). - "Getting random number to determine the table index at runtime. - DATA(random) = cl_abap_random_int=>create( + "Getting random number to determine the table index at runtime + DATA(idx) = cl_abap_random_int=>create( seed = cl_abap_random=>seed( ) min = 1 - max = lines( str_tab ) ). - DATA(idx) = random->get_next( ). + max = lines( str_tab ) )->get_next( ). - "Returning parameter to receive the random data object name. - TRY. - dobj = str_tab[ idx ]. - CATCH cx_sy_itab_line_not_found INTO DATA(error). - ENDTRY. + "Providing the returning parameter with a random data object name + dobj = VALUE #( str_tab[ idx ] DEFAULT |Hallo, { sy-uname }. This is a string.| ). ENDMETHOD. @@ -81,24 +120,20 @@ CLASS lcl_det_at_runtime IMPLEMENTATION. 'ZDEMO_ABAP_CARR' ) )->components. - "Getting random number to determine the table index at runtime. - "Starting from 2 to exclude MANDT field - DATA(random) = cl_abap_random_int=>create( + "Getting random number to determine the table index at runtime; + "starting from 2 to exclude MANDT field + DATA(idx) = cl_abap_random_int=>create( seed = cl_abap_random=>seed( ) min = 2 - max = lines( comp ) ). - DATA(idx) = random->get_next( ). + max = lines( comp ) )->get_next( ). - "Returning parameter to receive the random component name. - TRY. - field = comp[ idx ]-name. - CATCH cx_sy_itab_line_not_found INTO DATA(error). - ENDTRY. + "Providing the returning parameter with a random component name + field = VALUE #( comp[ idx ]-name DEFAULT `CARRID` ). ENDMETHOD. METHOD get_dyn_select_list. - "Providing SELECT lists in a string table to be selected from. + "Providing SELECT lists in a string table to be selected from DATA sel_list_tab TYPE string_table. sel_list_tab = VALUE #( ( `CARRID, CONNID, COUNTRYFR, COUNTRYTO` ) @@ -108,23 +143,19 @@ CLASS lcl_det_at_runtime IMPLEMENTATION. `FLTIME, DEPTIME, ARRTIME, DISTANCE` ) ). - "Getting random number to determine the table index at runtime. - DATA(random) = cl_abap_random_int=>create( + "Getting random number to determine the table index at runtime + DATA(idx) = cl_abap_random_int=>create( seed = cl_abap_random=>seed( ) min = 1 - max = lines( sel_list_tab ) ). - DATA(idx) = random->get_next( ). + max = lines( sel_list_tab ) )->get_next( ). - "Returning parameter to receive the random SELECT list. - TRY. - list = sel_list_tab[ idx ]. - CATCH cx_sy_itab_line_not_found INTO DATA(error). - ENDTRY. + "Providing the returning parameter with a random SELECT list + list = VALUE #( sel_list_tab[ idx ] DEFAULT `CARRID, CONNID, COUNTRYFR, COUNTRYTO` ). ENDMETHOD. METHOD get_dyn_where_clause. - "Providing WHERE clauses in a table to be selected from. + "Providing WHERE clauses in a table to be selected from DATA: BEGIN OF where_struc, where_clause_tab TYPE string_table, END OF where_struc. @@ -139,90 +170,183 @@ CLASS lcl_det_at_runtime IMPLEMENTATION. ( where_clause_tab = VALUE #( ( `DISTANCE > 500 AND DISTID = 'KM'` ) ) ) ). - "Getting random number to determine the table index at runtime. - DATA(random) = cl_abap_random_int=>create( + "Getting random number to determine the table index at runtime + DATA(idx) = cl_abap_random_int=>create( seed = cl_abap_random=>seed( ) min = 1 - max = lines( where_itab ) ). - DATA(idx) = random->get_next( ). + max = lines( where_itab ) )->get_next( ). - "Returning parameter to receive the random WHERE clause. - TRY. - clause_tab = where_itab[ idx ]-where_clause_tab. - CATCH cx_sy_itab_line_not_found INTO DATA(error). - ENDTRY. - - ENDMETHOD. - - METHOD get_dyn_class. - - "Providing class names in a string table to be selected from. - DATA(class_tab) = VALUE string_table( - ( `LCL_DET_AT_RUNTIME` ) - ( `LCL_DUMMY` ) ). - - "Getting random number to determine the table index at runtime. - DATA(random) = cl_abap_random_int=>create( - seed = cl_abap_random=>seed( ) min = 1 - max = lines( class_tab ) ). - DATA(idx) = random->get_next( ). - - "Returning parameter to receive the random class name. - TRY. - cl = class_tab[ idx ]. - CATCH cx_sy_itab_line_not_found INTO DATA(error). - ENDTRY. + "Providing the returning parameter with a random WHERE clause + clause_tab = VALUE #( where_itab[ idx ]-where_clause_tab DEFAULT VALUE #( ( `CARRID = 'LH'` ) ( `OR CARRID = 'AA'` ) ) ). ENDMETHOD. METHOD get_random_type. - "Providing names of classes in a string table to be selected from. - "Note that in this example types are defined in the public section - "of a class and the program logic is included in another class. - "To be able to refer to the types, the class name is added. + "Providing names of classes in a string table to be selected from + "In this example, some types are defined in the public section + "of a local class. The class name is added here since the names + "are used in the global class. DATA(str_tab) = VALUE string_table( - ( `LCL_DET_AT_RUNTIME=>TYPE1` ) - ( `LCL_DET_AT_RUNTIME=>TYPE2` ) - ( `LCL_DET_AT_RUNTIME=>TYPE3` ) - ( `LCL_DET_AT_RUNTIME=>TYPE4` ) + ( `LCL_DET_AT_RUNTIME=>TYPE_P` ) + ( `LCL_DET_AT_RUNTIME=>TYPE_STRUC` ) + ( `LCL_DET_AT_RUNTIME=>TYPE_ITAB` ) + ( `LCL_DET_AT_RUNTIME=>TYPE_REF` ) ( `LCL_DET_AT_RUNTIME` ) - ( `IF_OO_ADT_CLASSRUN` ) ). + ( `IF_OO_ADT_CLASSRUN` ) + ). - "Getting random number to determine the table index at runtime. - DATA(random) = cl_abap_random_int=>create( + "Getting random number to determine the table index at runtime + DATA(idx) = cl_abap_random_int=>create( seed = cl_abap_random=>seed( ) min = 1 - max = lines( str_tab ) ). - DATA(idx) = random->get_next( ). + max = lines( str_tab ) )->get_next( ). - "Returning parameter to receive the random type name. - TRY. - random_type = str_tab[ idx ]. - CATCH cx_sy_itab_line_not_found INTO DATA(error). - ENDTRY. + "Providing the returning parameter with a random type name + random_type = VALUE #( str_tab[ idx ] DEFAULT `LCL_DET_AT_RUNTIME=>TYPE_STRUC` ). ENDMETHOD. + METHOD get_dyn_class_meth. + + "Providing class names in a string table to be selected from + DATA(class_tab) = VALUE string_table( + ( `LCL_DEMO1` ) + ( `LCL_DEMO2` ) ). + + "Getting random number to determine the table index at runtime + DATA(idx) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 1 + max = lines( class_tab ) )->get_next( ). + + "Provide the exporting parameter with the random class name + cl = VALUE #( class_tab[ idx ] DEFAULT `LCL_DEMO1` ). + + "Getting method names using RTTI + DATA(methods) = CAST cl_abap_classdescr( cl_abap_typedescr=>describe_by_name( cl ) )->methods. + + "Getting random number to determine the table index at runtime + idx = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 1 + max = lines( methods ) )->get_next( ). + + "Provide the exporting parameter with the random method name + meth = VALUE #( methods[ idx ]-name DEFAULT `METH_A` ). + + "Data reference objects for the value parameter in the parameter table + DATA(ref_imp) = NEW string( `hi` ). + DATA(ref_exp) = NEW string( `hallo` ). + DATA(ref_ch) = NEW string( `salut` ). + DATA(ref_ret) = NEW string( `ciao` ). + + "Filling the parameter tables + "Note: If the method signature has an importing parameter, + "it must be specified as exporting parameter here. + "Same is true for the exporting parameter in the signature + "that must be specified as importing parameter. + + CASE meth. + + WHEN `METH_A`. + + ptab = VALUE #( ( name = 'A' + kind = cl_abap_objectdescr=>exporting + value = ref_exp ) + ( name = 'B' + kind = cl_abap_objectdescr=>importing + value = ref_imp ) ). + + WHEN `METH_B`. + + ptab = VALUE #( ( name = 'C' + kind = cl_abap_objectdescr=>changing + value = ref_ch ) + ( name = 'D' + kind = cl_abap_objectdescr=>returning + value = ref_ret ) ). + + WHEN `METH_C`. + + ptab = VALUE #( ( name = 'E' + kind = cl_abap_objectdescr=>exporting + value = ref_exp ) + ( name = 'F' + kind = cl_abap_objectdescr=>importing + value = ref_imp ) ). + + WHEN `METH_D`. + + ptab = VALUE #( ( name = 'G' + kind = cl_abap_objectdescr=>changing + value = ref_ch ) + ( name = 'H' + kind = cl_abap_objectdescr=>returning + value = ref_ret ) ). + + ENDCASE. + + ENDMETHOD. + + METHOD fill_string. + dyn_meth_call_result = |Hallo { sy-uname }. The string was filled at { utclong_current( ) }.|. + ENDMETHOD. + ENDCLASS. -CLASS lcl_dummy DEFINITION. - +CLASS lcl_demo1 DEFINITION. +"Note that this is just a demo class with demo methods to work with in the example. PUBLIC SECTION. CLASS-METHODS: - meth_a IMPORTING imp TYPE i - EXPORTING exp TYPE i - RETURNING VALUE(str) TYPE string, - meth_b CHANGING ch TYPE string - RETURNING VALUE(str) TYPE string. + meth_a IMPORTING a TYPE string + EXPORTING b TYPE string, + meth_b CHANGING c TYPE string + RETURNING VALUE(d) TYPE string. + ENDCLASS. -CLASS lcl_dummy IMPLEMENTATION. +CLASS lcl_demo1 IMPLEMENTATION. METHOD meth_a. - str = |Hallo from meth_a.|. + b = |Hallo from meth_a. '{ a }' was input.|. + + "Filling an attribute for the output in the global class. + lcl_det_at_runtime=>dyn_meth_call_result = b. ENDMETHOD. METHOD meth_b. - str = |Hallo from meth_b.|. + c = `#` && c && `#`. + d = |Hallo from meth_b. Actual parameter of changing parameter: '{ c }'|. + + "Filling an attribute for the output in the global class. + lcl_det_at_runtime=>dyn_meth_call_result = d. ENDMETHOD. ENDCLASS. + +CLASS lcl_demo2 DEFINITION. +"Note that this is just a demo class with demo methods to work with in the example. + PUBLIC SECTION. + + CLASS-METHODS: + meth_c IMPORTING e TYPE string + EXPORTING f TYPE string, + meth_d CHANGING g TYPE string + RETURNING VALUE(h) TYPE string. + +ENDCLASS. + +CLASS lcl_demo2 IMPLEMENTATION. + METHOD meth_c. + f = |Hallo from meth_c. '{ e }' was input.|. + + "Filling an attribute for the output in the global class. + lcl_det_at_runtime=>dyn_meth_call_result = f. + ENDMETHOD. + + METHOD meth_d. + g = `*` && g && `*`. + h = |Hallo from meth_d. Actual parameter of changing parameter: '{ g }'|. + + "Filling an attribute for the output in the global class. + lcl_det_at_runtime=>dyn_meth_call_result = h. + ENDMETHOD. + +ENDCLASS. \ No newline at end of file