commit 75587a904bb785a537f44e8aa108a67fcfe6e82c Author: Daniel Reger <16720986+danrega@users.noreply.github.com> Date: Mon Dec 5 11:03:16 2022 +0100 Initial commit diff --git a/.abapgit.xml b/.abapgit.xml new file mode 100644 index 0000000..1e91166 --- /dev/null +++ b/.abapgit.xml @@ -0,0 +1,20 @@ + + + + + E + /src/ + FULL + + /.gitignore + /LICENSE + /README.md + /package.json + /.travis.yml + /.gitlab-ci.yml + /abaplint.json + /azure-pipelines.yml + + + + diff --git a/.reuse/dep5 b/.reuse/dep5 new file mode 100644 index 0000000..b5f4cb9 --- /dev/null +++ b/.reuse/dep5 @@ -0,0 +1,31 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: abap-cheat-sheets +Upstream-Contact: SAP Open Source Program Office ospo@sap.com +Source: https://github.com/sap-samples/abap-cheat-sheets + +Disclaimer: The code in this project may include calls to APIs (“API Calls”) of + SAP or third-party products or services developed outside of this project + (“External Products”). + “APIs” means application programming interfaces, as well as their respective + specifications and implementing code that allows software to communicate with + other software. + API Calls to External Products are not licensed under the open source license + that governs this project. The use of such API Calls and related External + Products are subject to applicable additional agreements with the relevant + provider of the External Products. In no event shall the open source license + that governs this project grant any rights in or to any External Products,or + alter, expand or supersede any terms of the applicable additional agreements. + If you have a valid license agreement with SAP for the use of a particular SAP + External Product, then you may make use of any API Calls included in this + project’s code for that SAP External Product, subject to the terms of such + license agreement. If you do not have a valid license agreement for the use of + a particular SAP External Product, then you may only make use of any API Calls + in this project for that SAP External Product for your internal, non-productive + and non-commercial test and evaluation of such API Calls. Nothing herein grants + you any rights to use or access any SAP External Product, or provide any third + parties the right to use of access any SAP External Product, through API Calls. + +Files: * +Copyright: 2022 SAP SE or an SAP affiliate company and abap-cheat-sheets contributors. +License: Apache-2.0 + diff --git a/01_Internal_Tables.md b/01_Internal_Tables.md new file mode 100644 index 0000000..a924bc3 --- /dev/null +++ b/01_Internal_Tables.md @@ -0,0 +1,1463 @@ + + +# Working with Internal Tables + +- [Working with Internal Tables](#working-with-internal-tables) + - [Internal Tables ...](#internal-tables-) + - [Declaring Internal Tables](#declaring-internal-tables) + - [Characteristics](#characteristics) + - [Excursion: Primary, Secondary and Empty Table Keys](#excursion-primary-secondary-and-empty-table-keys) + - [Working with Internal Tables](#working-with-internal-tables-1) + - [Creating Internal Tables](#creating-internal-tables) + - [Filling and Copying Internal Table Content](#filling-and-copying-internal-table-content) + - [Excursions](#excursions) + - [Reading from Internal Tables](#reading-from-internal-tables) + - [Processing Multiple Internal Table Lines Sequentially](#processing-multiple-internal-table-lines-sequentially) + - [Sorting Internal Tables](#sorting-internal-tables) + - [Modifying Internal Table Content](#modifying-internal-table-content) + - [Deleting Internal Table Content](#deleting-internal-table-content) + - [More Information](#more-information) + - [Executable Example](#executable-example) + + +## Internal Tables ... + +- are [data + objects](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_object_glosry.htm "Glossary Entry") + in ABAP. Their [data + type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_type_glosry.htm "Glossary Entry") + is a [table + type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentable_type_glosry.htm "Glossary Entry"). +- can be seen as collections of table lines. +- usually take up data from a fixed structure and store it in the + working memory in ABAP, i. e. the data is stored line by line in + memory, and each line has the same structure. +- are relevant ... + - whenever you want to process a data set with a fixed structure + within a program. + - when managing multiple related data records of the same data + type in a single variable. + - for storing and formatting data from a database table within a + program. Note: Due to their existence in memory, the + data access with internal tables is a lot faster than accessing + the data on database tables. +- are declared within ABAP source code. +- are dynamic data objects, i. e. they can be processed in many + different ways: + - table lines can, for example, be inserted, deleted, or updated. + - the way how to access the tables can vary, e. g. access by index + or key, and they can be processed sequentially in a loop. +- are only temporarily available in the memory; after the program has + been terminated, the content of an internal table is not available + any more. +- are simple to manage for developers since the runtime system is + responsible for the memory management, i. e. the runtime system + calculates an appropriate initial memory allocation for the internal + table when it is declared; when you add more data to the table, the + table grows automatically; when you empty the table, the system + automatically releases excess memory. +- are characterized by their line types, table categories and key + attributes. + +

(back to top)

+ +## Declaring Internal Tables + +The relevant syntactical element is `TABLE OF` in combination +with +[`TYPES`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaptypes.htm) +(to declare an internal table type) and +[`DATA`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapdata.htm) +(to create the internal table) and the additions +[`TYPE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapdata_simple.htm) +or +[`LIKE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapdata_referring.htm). +See more details and examples in section [Creating Internal Tables](#creating-internal-tables) further down. + +Examples +``` abap +TYPES itab_type1 TYPE STANDARD TABLE OF data_type ... +TYPES itab_type2 LIKE SORTED   TABLE OF data_object ... +DATA  itab1      TYPE          TABLE OF data_type ... +DATA  itab2      TYPE HASHED   TABLE OF data_type ... +DATA  itab3      TYPE                   table_type ... +DATA  itab4      LIKE                   table ...` +``` + +> **💡 Note**
+>- If the table category is not specified (`... TYPE TABLE OF ...`), it is automatically `... TYPE STANDARD TABLE OF ...`. +>- Internal tables can be [declared + inline](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninline_declaration_glosry.htm "Glossary Entry") in various contexts, for example, using `DATA(...)`. + + +

(back to top)

+ +### Characteristics + +Each internal table is characterized by three aspects. More details: [Internal Tables - +Overview](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenitab_oview.htm). + + +
+ Expand to view the characteristics + + +**Line Type** + +- Defines how each line of the internal table is set up, i. e. it describes what columns the table has. +- It can be any ABAP data type, e. g. a structure or an internal table. +- In most cases, the line type is a structure, which means that every line in the internal table contains a column with the name and type of the corresponding structure component. +- In a simple case, the line consists of a flat structure with elementary data objects; however, it can also be a deep structure whose components can be structures themselves or even internal tables. + +**Table Category** + +- Determines how internal tables are managed and stored internally as well as how individual table entries will be accessed. +- Why relevant? The different approaches to accessing the data can make significant performance differences. +- Note: There are two ways of accessing internal tables: + - Access by index: A line of an internal table is addressed by its line number. + - Access by key: A line of an internal table is addressed by looking for particular values in particular columns. Note: The columns in which you search may be key columns, but it can also be non-key columns. +- There are three table categories: + +| Category | Details | When to use | Hints | +|---|---|---|---| +| `STANDARD` | | | | +| `SORTED` | | | | +| `HASHED` | | | | + +**Key Attributes** + +- A table key identifies table lines. +- There are two possible key types: primary table key and secondary table key. +- A primary table key ... + - is contained in every internal table. + - is either a self-defined key or a standard key. You can make further specifications for the key, for example, whether the key is to be unique or non-unique, i. e. more than one line with the same key (duplicates) can exist in the internal table. + - can also be empty, i. e. it does not contain any key fields. + - has the predefined name `primary_key` with which it can also be addressed explicitly in various statements (but its use is optional). You can also specify an alias name for the primary key. Note that in table expressions, `primary_key` or an alias name must be specified if the primary key is to be used explicitly. + - can also be composed of the entire line of the internal table. In this case, the pseudo component `table_line` can be used to denote the primary table key. +- A secondary table key ... + - is optional. + - is either a unique or non-unique sorted key or a unique hash key. +
+ + + + + +

(back to top)

+ +### Excursion: Primary, Secondary and Empty Table Keys + +
+ Expand to view the details + + +*Primary table keys* + +Standard key: + +- The standard key is a special primary table key. +- Standard key of an internal table with a ... + - structured line type: The primary table key consists of all fields having character-like and byte-like data types. + - non-structured/elementary line type: The whole table is the key (`table_line`). +- Note: An internal table with no explicit specification of keys implicitly has the standard table key as a primary table key. +- Why respecting standard keys matters: + - A sorting of a table can lead to unexpected results. + - Since the standard key might consist of many fields, it impacts the performance when accessing the internal table via the keys. + - The key fields of the primary table key of sorted and hashed tables are always read-only, i. e. using the standard key with those table categories and then (inadvertently) modifying fields can cause unexpected runtime errors. + - An explicit specification of keys has the advantage of providing a better readability and understandability of your code and you avoid setting the standard key by mistake. + +Examples using `DATA` statements: +``` abap +"Standard table with implicit default key; all non-numeric table +"fields compose the primary table key + +DATA it1 TYPE TABLE OF zdemo_abap_fli. + +"explicitly specifying the standard table key; same as it1 + +DATA it2 TYPE STANDARD TABLE OF zdemo_abap_fli WITH DEFAULT KEY. + +"Hashed table with unique standard table key + +DATA it3 TYPE HASHED TABLE OF zdemo_abap_fli WITH UNIQUE DEFAULT KEY. + +"Sorted table with non-unique standard table key + +DATA it4 TYPE SORTED TABLE OF zdemo_abap_fli WITH NON-UNIQUE DEFAULT KEY. + +"Elementary line type; the whole table line is the standard table key + +DATA it5 TYPE TABLE OF i. +``` + +*Explicit declaration of the primary table key* + +- By specifying the uniqueness, you can explicitly declare the primary table key. +- As mentioned above, the predefined name `primary_key` can be used followed by a list of components. +- An alias name for the primary key can be specified, too. + +See the comments in the following examples for more information. + +``` abap +"Explicitly specified primary table keys +"Standard tables: only NON-UNIQUE possible + +DATA it6 TYPE TABLE OF zdemo_abap_fli WITH NON-UNIQUE KEY carrid. + +"Standard tables: only KEY specified, NON-UNIQUE is added implicitly + +DATA it7 TYPE TABLE OF zdemo_abap_fli WITH KEY carrid. + +"Sorted tables: both UNIQUE and NON-UNIQUE possible + +DATA it8 TYPE SORTED TABLE OF zdemo_abap_fli + WITH UNIQUE KEY carrid connid. + +DATA it9 TYPE SORTED TABLE OF zdemo_abap_fli + WITH NON-UNIQUE KEY carrid connid cityfrom. + +"Hashed: UNIQUE KEY must be specified + +DATA it10 TYPE HASHED TABLE OF zdemo_abap_fli + WITH UNIQUE KEY carrid. + +"Explicitly specifying primary_key and listing the components; same as it6 and it7 + +DATA it11 TYPE TABLE OF zdemo_abap_fli + WITH KEY primary_key COMPONENTS carrid. + +"Same as it9 + +DATA it12 TYPE SORTED TABLE OF zdemo_abap_fli + WITH NON-UNIQUE KEY primary_key COMPONENTS carrid connid cityfrom. + +"An alias is only possible for sorted/hashed tables + +DATA it13 TYPE SORTED TABLE OF zdemo_abap_fli + WITH NON-UNIQUE KEY primary_key + ALIAS p1 COMPONENTS carrid connid cityfrom. + +"An alias is used for the key which is composed of the entire line + +DATA it14 TYPE HASHED TABLE OF zdemo_abap_fli + WITH UNIQUE KEY primary_key + ALIAS p2 COMPONENTS table_line. +``` +> **💡 Note**
+> The specification for the primary key can only be omitted for standard tables. The primary table key is then defined automatically as a non-unique standard key. + +*Empty key* +- A standard table can be specified with an empty key, i. e. it does + not contain any key fields. +- This is not possible for sorted and hashed tables. With these table + categories, the primary table key must be specified explicitly and, + thus, cannot be empty. +- Internal tables with empty key are used if the order of the entries + based on key values is of no relevance for the filling and + accessing. +- However, they should be used with care to avoid unexpected + results e. g. when sorting those tables. +- You might want to define a table with an empty key instead of not + specifying a key definition at all since otherwise the standard key + is used which must be handled with care, too, as mentioned above. +- Declaration: + - Explicit declaration with the addition `EMPTY KEY` + - Implicit declaration when using the standard key if a structured + line type does not contain non-numeric elementary components or + if an unstructured line type is table-like. + +> **💡 Note**
+> When using an [inline + declaration](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninline_declaration_glosry.htm "Glossary Entry") + like `... INTO TABLE @DATA(itab) ...` in + `SELECT` statements, the resulting table is a standard + table and has an empty key. + +Examples: +``` abap +"Empty keys only possible for standard tables + +DATA it15 TYPE TABLE OF zdemo_abap_fli WITH EMPTY KEY. + +"The inline declaration produces a table with empty key + +SELECT * FROM zdemo_abap_fli INTO TABLE @DATA(it16) UP TO 3 ROWS. +``` + +*Secondary table keys* + +- Secondary table keys can be optionally specified for all table categories. +- There are two kind of secondary table keys: unique or non-unique sorted keys or unique hash keys. +- Secondary keys always have a self-defined name. An alias can be defined for a secondary key, too. +- A secondary table index is created internally for each sorted secondary key. This enables index access to hashed tables via the secondary key. In this case, `sy-tabix` is set. +- Use cases of secondary table keys: + - To improve the performance of data retrieval from internal tables and guarantee uniqueness when accessing data + - To enable optimized access to standard tables (huge advantage: secondary keys can be added to existing standard tables, thus, gaining the benefits of the other table types with respect to performance) + - Mainly used for very large internal tables (where only few modifications occur afterwards); not suitable for small internal tables (less than 50 lines) since each secondary key means additional administration costs (they consume additional memory) +- If you want to make use of this key in ABAP statements, for example, `READ`, `LOOP AT` or `MODIFY` statements, the key must be specified explicitly using the appropriate additions, for example, `WITH ... KEY ... COMPONENTS` or `USING KEY`. +- Find more details in the programming guidelines on secondary keys: [Secondary + Key (F1 docu for standard ABAP)](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abensecondary_key_guidl.htm "Guideline"). + +Examples: +``` abap +DATA it17 TYPE TABLE OF zdemo_abap_fli "standard table + WITH NON-UNIQUE KEY carrid connid "primary key + WITH UNIQUE SORTED KEY cities COMPONENTS cityfrom cityto. "secondary key + +DATA it18 TYPE HASHED TABLE OF zdemo_abap_fli "hashed table + WITH UNIQUE KEY carrid connid + WITH NON-UNIQUE SORTED KEY airports COMPONENTS airpfrom airpto. + +DATA it19 TYPE SORTED TABLE OF zdemo_abap_fli "sorted table + WITH UNIQUE KEY carrid connid + WITH UNIQUE HASHED KEY countries COMPONENTS countryfr countryto. + +"primary_key explicitly specified + multiple secondary keys + +DATA it20 TYPE TABLE OF zdemo_abap_fli + WITH NON-UNIQUE KEY primary_key COMPONENTS carrid connid + WITH NON-UNIQUE SORTED KEY cities COMPONENTS cityfrom cityto + WITH UNIQUE HASHED KEY airports COMPONENTS airpfrom airpto. + +"Alias names for secondary table keys (and primary table key, too) + +DATA it21 TYPE SORTED TABLE OF zdemo_abap_fli + WITH NON-UNIQUE KEY primary_key ALIAS k1 COMPONENTS carrid connid city + WITH NON-UNIQUE SORTED KEY cities ALIAS s1 COMPONENTS cityfrom cityto + WITH UNIQUE HASHED KEY airports ALIAS s2 COMPONENTS airpfrom airpto. + +"Example for key usage using a LOOP AT statement; all are possible + +LOOP AT it21 INTO DATA(wa) USING KEY primary_key. +"LOOP AT it21 INTO DATA(wa) USING KEY k1. +"LOOP AT it21 INTO DATA(wa) USING KEY cities. +"LOOP AT it21 INTO DATA(wa) USING KEY s1. +"LOOP AT it21 INTO DATA(wa) USING KEY airports. +"LOOP AT it21 INTO DATA(wa) USING KEY s2. +... +ENDLOOP. +``` +
+ + + + + +

(back to top)

+ +## Working with Internal Tables + +### Creating Internal Tables + +As a best practice for declaring internal tables, it is recommended that +an internal table with this pattern is created in a program: + +- Defining a structured data type (locally or globally; it is not + needed if you refer to a globally available type, for example, a + database table whose line type is automatically used when defining a + an internal table type or creating the variable) +- Defining an internal table type +- Creating a variable, i. e. the internal table, that refers to that + type. + +You will also see internal tables that are declared by combining the +variable creation and table type definition in one go. If the structured +data and internal table types are globally available in the DDIC, a +local definition within a program is not needed. + +Example: + +The following example shows the pattern and various examples of declaring internal tables and types by including the local definition of +structured data and internal table types for demonstration purposes. + +``` abap +"1. Defining line type locally + +TYPES: BEGIN OF ls_loc, + key_field TYPE i, + char1 TYPE c LENGTH 10, + char2 TYPE c LENGTH 10, + num1 TYPE i, + num2 TYPE i, + END OF ls_loc. + +"2. Defining internal table types +"All of the examples use the short form: +"TYPE TABLE OF instead of TYPE STANDARD TABLE OF + +TYPES: + "Standard table type based on locally defined structure type. + tt_loc_str TYPE TABLE OF ls_loc WITH NON-UNIQUE KEY key_field, + + "Based on global structure type + tt_gl_str TYPE TABLE OF demo_cs_struc WITH NON-UNIQUE KEY key_field, + + "Based on database table (could also be, e. g. a CDS view) + "In this case, the line type of the table is automatically used. + tt_gl_tab TYPE TABLE OF demo_cs_dbtab WITH NON-UNIQUE KEY key_field, + + "Based on an elementary type + tt_el_type TYPE TABLE OF i. + +"3. Creating internal tables ... +"... from locally defined table types + +DATA: itab_a1 TYPE tt_loc_str, + itab_a2 TYPE tt_gl_str, + itab_a3 TYPE tt_gl_tab, + itab_a4 TYPE tt_el_type. + +"... from global table types +DATA itab_a5 TYPE string_table. + +"Other declaration options with DATA +"To save the extra creation of the table type, you can include the table category +"and key info in the data declaration directly. +DATA itab_a6 TYPE TABLE OF ls_loc WITH NON-UNIQUE KEY key_field. + +"Internal table based on an already existing internal table using LIKE. +DATA itab_a7 LIKE TABLE OF itab_a6. +``` + +

(back to top)

+ +**Excursion: Declaring internal tables inline** + +To produce leaner and more readable code and create variables in the +place where you need them, you can make use of [inline +declarations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninline_declaration_glosry.htm "Glossary Entry"). +Such inline declarations are possible in appropriate [declaration +positions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendeclaration_positions.htm) +if the operand type can be determined completely, for example, using a +`DATA` statement (or `FINAL` for immutable variables) as shown in the following examples: + +``` abap +"Table declared inline in the context of an assignment +"The examples show the copying of a table including the content on the fly +"and creating the table in one step. The data type of the +"declared variable is determined by the right side. + +DATA(it_inline1) = it. +DATA(it_inline2) = it_inline1. + +"Using the VALUE operator and an internal table type + +DATA(it_inline3) = VALUE table_type( ( ... ) ). + +"Table declared inline in the context of a SELECT statement; +"a prior extra declaration of an internal table is not needed. + +DATA it TYPE TABLE OF zdemo_abap_fli EMPTY KEY. + +SELECT * FROM zdemo_abap_fli INTO TABLE @it. + +SELECT * FROM zdemo_abap_fli INTO TABLE @DATA(it_inline4). +``` + +

(back to top)

+ +### Filling and Copying Internal Table Content + +You can use the ABAP keywords +[`APPEND`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapappend.htm) +and +[`INSERT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapinsert_itab.htm) +to add lines to internal tables. + +
+ Notes on the use + + +- `APPEND` ... + - always adds lines at the bottom of the internal table. + - is unproblematic for standard tables for which lines are managed + via an index. When using the statement, the system field + `sy-tabix` is given the index of the recently added + line. `sy-tabix` is always set on the index with respect + to the primary table index. + - cannot be used for hashed tables. With regard to sorted tables, + lines are only appended if they match the sort order and do not + create duplicate entries if the primary table key is unique. + Hence, `INSERT` should be used when adding lines to + sorted tables. +- `INSERT` ... + - can be used to add lines at a specific position in tables (by + specifying the target index). In doing so, all the following + lines are moved down one position. + - without specifying the position adds the lines at the bottom of + the table in case of standard tables. However, when using + `INSERT`, `sy-tabix` is not set unlike + `APPEND`. In case of sorted tables, the line is + automatically inserted at the right position. + - [Note:] In case of unique primary table keys in sorted + and hashed tables, the table cannot have entries with duplicate + keys. If a duplicate is inserted, the insertion fails and the + system field `sy-subrc` is set to 4. +
+ +**Adding a line to the internal table**. The example shows both a structure that is created using the `VALUE` operator and added as well as +an existing structure that is added. + +``` abap +APPEND VALUE #( comp1 = a comp2 = b ... ) TO itab. +APPEND lv_struc TO itab. + +INSERT VALUE #( comp1 = a comp2 = b ... ) INTO TABLE itab. +INSERT lv_struc INTO itab. +``` + +**Adding an initial line** to the internal table without providing any field values. + +``` abap +APPEND INITIAL LINE TO itab. + +INSERT INITIAL LINE INTO TABLE itab. +``` + +Adding all lines from another internal table. + +``` abap +APPEND LINES OF itab2 TO itab. + +INSERT LINES OF itab2 INTO TABLE itab. +``` + +**Adding lines from another internal table with a specified index range**. +You do not need to use both `FROM` and `TO` in one +statement. You can also use just one of them. When using only +`FROM`, all lines are respected until the final table entry. +When using only `TO`, all lines are respected starting from the +first table entry. + +``` abap +"i1/i2 represent integer values + +APPEND LINES OF itab2 FROM i1 TO i2 TO itab. + +APPEND LINES OF itab2 FROM i1 TO itab. + +APPEND LINES OF itab2 TO i2 TO itab. + +INSERT LINES OF itab2 FROM i1 TO i2 INTO itab. +``` + +**Inserting one line or multiple lines from another internal table at a specific position**. `FROM` and `TO` can be used here, too. + +``` abap +INSERT lv_struc INTO itab2 INDEX i. + +INSERT LINES OF itab2 INTO itab INDEX i. +``` + +

(back to top)

+ +**Adding lines using constructor expressions** + +As already touched on above, table lines that are constructed inline as +arguments of, for example, the `VALUE` operator can be added to +internal tables. The operator allows you to fill an internal table with +a compact expression. In the cases below, internal tables are filled +using constructor expressions in the context of +[assignments](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenassignment_glosry.htm "Glossary Entry"). + +In the example below, the internal table is filled by assigning an +internal table that is constructed inline using the `VALUE` +operator. The table constructed inline has two lines. `line` +represents an existing structure having an appropriate line type. The +other line is constructed inline. + + +> **💡 Note**
+> The extra pair of brackets represents a table line. The # sign denotes that the line type can be derived from the context. The assignment clears the existing content of the internal table on the left side. + +``` abap +itab = VALUE #( ( line ) +                ( comp1 = a comp2 = b ...  ) ). +``` + +*Excursion*: **Creating a internal table by inline declaration** and adding lines using a constructor expression +``` abap +"Internal table type +TYPES it_type LIKE itab. + +"Inline declaration +"The # sign would not be possible here since the line type +"cannot be derived from the context. + +DATA(it_in) = VALUE it_type( ( comp1 = a comp2 = b ... ) + ( comp1 = c comp2 = d ... ) ). +``` +When using the assignments above (`itab = ...`), the internal table is initialized and the existing content is deleted. To add new +lines **without deleting existing content**, use the addition [`BASE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenvalue_constructor_params_itab.htm). +``` abap +itab = VALUE #( BASE itab ( comp1 = a comp2 = b ... ) +                          ( comp1 = c comp2 = d ... ) ). +``` + +**Adding lines of other tables** using the addition `LINES OF`. +> **💡 Note**
+> Without the addition `BASE` existing content is deleted. The line type of the other internal table must match the one of +the target internal table. +``` abap +itab = VALUE #( ( comp1 = a comp2 = b ...) +                ( comp1 = a comp2 = b ...) +                ( LINES OF itab2 ) +                ... ). +``` +A simple assignment without a constructor expression that **copies the content from another internal table** that has the **same line type** (note that the existing content in `itab` is deleted). +``` abap +itab = itab2. +``` +**Copying content from another internal table** that has a **different line +type** using the +[`CORRESPONDING`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expr_corresponding.htm) +operator. Note that the existing content is deleted. + +As an alternative to the `CORRESPONDING` operator, statements +with +[`MOVE-CORRESPONDING`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmove-corresponding.htm) +can be used. +``` abap +itab = CORRESPONDING #( itab3 ). + +MOVE-CORRESPONDING itab3 TO itab. +``` + +Copying content from another internal table that has a different line type using the `CORRESPONDING` operator while **keeping existing +content**. The addition `KEEPING TARGET LINES` for the `MOVE-CORRESPONDING` statement preserves the table content. + +``` abap +itab = CORRESPONDING #( BASE ( itab ) itab3 ). + +MOVE-CORRESPONDING itab3 TO itab KEEPING TARGET LINES. +``` + +Using the `MAPPING` addition of the `CORRESPONDING` operator, you can specify components of a source table that are assigned to the components of a target table in **mapping relationships**. In elementary components, the assignment is made in accordance with the associated assignment rules. + +``` abap +itab = CORRESPONDING #( itab3 MAPPING a = c b = d ). +``` +Using the `EXCEPT` addition of the `CORRESPONDING` operator, you can **exclude components from the assignment**. This is particularly handy if there are identically named components in the source and target table that are not compatible or convertible. In doing so, you can avoid syntax errors or runtime errors. Instead of a component list, `EXCEPT` can also be followed by `*` to exclude all components that are not mentioned in a preceding mapping of components. If `EXCEPT *` is used without the +`MAPPING` addition, all components remain initial. +``` abap +itab = CORRESPONDING #( itab3 EXCEPT e ). + +itab = CORRESPONDING #( itab3 EXCEPT * ). +``` +Using the `DISCARDING DUPLICATES` addition of the `CORRESPONDING` operator, you can **prevent runtime errors if duplicate lines are assigned** to the target table that are defined to only accept unique keys. In that case, the duplicate line in the source table is ignored. The addition can also be specified with `MAPPING ...`. + +``` abap +itab = CORRESPONDING #( itab2 DISCARDING DUPLICATES ). +``` + +Copying data from a **deep internal table** to another deep internal table. If used, the addition `BASE` keeps the content. See also the alternative `MOVE-CORRESPONDING` statements. +``` abap +itab_nested2 = CORRESPONDING #( DEEP itab_nested1 ). + +itab_nested2 = CORRESPONDING #( DEEP BASE ( itab_nested2 ) itab_nested1 ) + +MOVE-CORRESPONDING itab_nested1 TO itab_nested2 EXPANDING NESTED TABLES. + +MOVE-CORRESPONDING itab_nested1 TO itab_nested2 EXPANDING NESTED TABLES KEEPING TARGET LINES. +``` + +

(back to top)

+ +#### Excursions + +Adding multiple lines from a database table to an internal table using +[`SELECT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect.htm), +for example, based on a condition. In the case below, the internal table +is created inline. If the variable exists, it is `... @itab`. +In this case, it is assumed that `itab` has the same line type +as the database table. Note the `@` character before the internal +table (see [host +expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_sql_host_expressions.htm)). +There are many more syntax options for `SELECT` statements. +``` abap +SELECT FROM dbtab +  FIELDS comp1, comp2 ... +  WHERE ... +  INTO TABLE @DATA(itab_sel). +``` + +**Sequentially adding multiple rows** from a database table to an internal table using `SELECT ... ENDSELECT.`, for example, based on a +condition. In this case, the selected data is first stored in a structure which can be further processed and added to an internal table. + +``` abap +SELECT FROM dbtab +  FIELDS comp1, comp2 ... +  WHERE ... +  INTO @DATA(struc_sel). + +  IF sy-subrc = 0. +    APPEND struc_sel TO itab. +  ... +  ENDIF. +ENDSELECT. +``` +Adding multiple lines from a database table using `SELECT`, for example, based on a condition, if the database table has a different +line type as the internal table. The `*` sign means that all fields are selected. In the other examples, specific fields are defined. +The addition `APPENDING CORRESPONDING FIELDS INTO TABLE` adds the selected data to the bottom of the table without deleting existing +table entries. The addition `INTO CORRESPONDING FIELDS OF TABLE` adds lines and deletes existing table entries. +``` abap +SELECT FROM dbtab2 +  FIELDS * +  WHERE ... +  APPENDING CORRESPONDING FIELDS OF TABLE @itab. + +SELECT FROM dbtab2 +  FIELDS * +  WHERE ... +  INTO CORRESPONDING FIELDS OF TABLE @itab. +``` +Adding multiple lines **from an internal table to another internal table** using `SELECT`. Note the alias name that must be defined for the +internal table. +``` abap +SELECT comp1, comp2, ... +  FROM @itab2 AS it_alias +  INTO TABLE @DATA(itab_sel). +``` +**Combining data of multiple tables** into an internal table using an [inner +join](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninner_join_glosry.htm "Glossary Entry"). +In below example, data of an internal and a database table is joined +with a `SELECT` statement and the addition [`INNER +JOIN`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_join.htm). +Note the field list including fields from both tables. The fields are +referred to using `~`. +``` abap +SELECT it_alias~comp1, it_alias~comp2, dbtab~comp3 ... +  FROM @itab AS it_alias +  INNER JOIN dbtab ON it_alias~comp1 = dbtab~comp1 +  INTO TABLE @DATA(it_join_result). +``` + +Filling an internal table from a database table using +[subqueries](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensubquery_glosry.htm "Glossary Entry"). +In both of the following examples, an internal table is filled from a +database table. In the first example, a subquery is specified in the +`WHERE` clause with the ABAP words ` NOT IN`. A check is +made to verify whether a value matches a value in a set of values +specified in parentheses. In the second example, an internal table is +filled depending on data in another table. A subquery is specified in +the `WHERE` clause with the ABAP word ` EXISTS`. In this +case, it checks the result of the subquery that consists of another +`SELECT` statement, i. e. a check is made if an entry exists in +a table based on the specified conditions. + +``` abap +SELECT comp1, comp2, ... +  FROM dbtab +  WHERE comp1 NOT IN ( a, b, c ... ) +  INTO TABLE @DATA(it_subquery_result1). + +SELECT comp1, comp2, ... +  FROM dbtab +  WHERE EXISTS ( SELECT 'X' FROM @itab AS itab_alias +       WHERE comp1 = dbtab~comp1 ) +  INTO TABLE @DATA(it_subquery_result2). +``` + +Filling internal table from a table based on the existence of data in +another table using the addition [`FOR ALL +ENTRIES`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwhere_all_entries.htm). + +> **💡 Note**
+> Ensure that the internal table from which to read is not initial. It is therefore recommended that a subquery is used as shown above: `... ( SELECT ... FROM @itab AS itab_alias WHERE ...`). + +``` abap +IF itab IS NOT INITIAL. +SELECT dbtab~comp1, dbtab~comp2, ... +  FROM dbtab +  FOR ALL ENTRIES IN @itab +  WHERE comp1 = @itab-comp1 +  INTO TABLE @DATA(it_select_result). +ENDIF. +``` + +Creating an internal table by copying data from another internal table +filtering out lines that do not match the `WHERE` condition. +Using the [`FILTER` +operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_filter.htm) +to extract data from internal tables ... + +... by condition. +``` abap +DATA(filter1) = FILTER #( itab WHERE comp1 < i ). +``` + +... by condition with the addition `EXCEPT` that excludes data according to a condition. +``` abap +DATA(filter2) = FILTER #( itab EXCEPT WHERE comp1 < i ). +``` + +... by using a filter table. +``` abap +DATA(filter3) = FILTER #( itab IN filter_tab WHERE comp1 < i. +``` + +*Excursion:* Collecting values + +Use the +[`COLLECT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcollect.htm) +keyword, for example, to add the values of numeric components to the +corresponding values in an internal table. Use it mainly for internal +tables with a unique primary key, especially hashed tables. +``` abap +COLLECT VALUE dtype( comp1 = a comp2 = b ... ) INTO itab. +``` + +

(back to top)

+ +### Reading from Internal Tables + +There are three different ways of specifying the line to be read: + +- via index (index tables only) +- via table keys (only tables having keys defined) +- via free key + +**Reading single lines** + +*Determining the target area* + +- Copying a line to a data object using the addition `INTO`. + After the copying, the found line exists in the internal table and + in the data object separately from each other. So, if you change the + data object or the table line, the change does not affect the other. + However, you can modify the copied table line and use a + `MODIFY` statement to modify the table based on the changed + table line (see below). The addition `TRANSPORTING` + specifies which components are to be respected for the copying. If + it is not specified, all components are respected. + ``` abap + READ TABLE itab INTO dobj ... "dobj must have the table's structure type + + READ TABLE itab INTO DATA(dobj_inl) ... + + READ TABLE itab INTO ... TRANSPORTING comp1 [comp2 ... ]. + ``` + +- Assigning a line to a [field + symbol](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfield_symbol_glosry.htm "Glossary Entry"), + for example, using an inline declaration (`ASSIGNING `). If you then access the field symbol, it means + accessing the found table line. There is no actual copying of + content. Hence, modifying operations on the field symbol mean + modifying the table line directly. Note that the addition + `TRANSPORTING` is not possible since the entire table is + assigned to the field symbol. + + ``` abap + READ TABLE itab ASSIGNING ... + + READ TABLE itab ASSIGNING FIELD-SYMBOL() ... + ``` + +- Reading a line into a [data reference + variable](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_reference_variable_glosry.htm "Glossary Entry") + using `REFERENCE INTO`. In this case, no copying takes place + either. Modifications of the table are possible via the [data + reference](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_reference_glosry.htm "Glossary Entry"); + and the addition `TRANSPORTING` is not possible either. + + ``` abap + READ TABLE itab REFERNCE INTO dref ... + + READ TABLE itab REFERNCE INTO DATA(dref_inl) ... + ``` + +**What to use then?** Since all syntax options principally offer the same +functionality, it is up to you and your use case. For example, +performance or readability of the code play a role. See more information +in the programming guidelines on the [target +area (F1 docu for standard ABAP)](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abentable_output_guidl.htm "Guideline"). +One obvious use case for `INTO dobj` is when the table should +not be modified via the copied table line. However, the copying comes +with performance costs. Imagine your table contains lots of columns or +nested components. Not copying at all in such a case is more performant +(however, you can certainly restrict the fields to be copied using the +`TRANSPORTING` addition). + +

(back to top)

+ +*Reading a single line by index* + +The following example shows `READ TABLE` statements to read a single line from an internal table by specifying the index. The addition `USING +KEY` can be used to specify a table key and, thus, determine the table index to be used explicitly. If the table has a sorted secondary +key, the addition can be specified and the line to be read is then determined from its secondary table index. If the primary table key is +specified using its name `primary_key`, the table must be an index table, and the behavior is the same as if `USING KEY` was +not specified. +``` abap +READ TABLE itab INTO wa INDEX i. + +READ TABLE itab INTO wa INDEX i USING KEY primary_key. +``` + +Using a [table +expression](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentable_expressions.htm), +the read result is stored in a variable that might be declared inline. +The number in the square brackets represents the index. A line that is +not found results in an runtime error. Hence, to avoid an error, you can +use a [`TRY ... CATCH ... ENDTRY.`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaptry.htm) block. + +``` abap +DATA(lv1) = itab[ i ]. + +TRY. + DATA(lv2) = itab[ i ]. + CATCH cx_sy_itab_line_not_found. + ... +ENDTRY. + +DATA(lv3) = itab[ KEY primary_key INDEX i ]. + +"Copying a table line via table expression and embedding in constructor expression +DATA(lv4) = VALUE #( itab[ i ] ). + +"Reading into data reference variable using the REF operator +DATA(lv5_ref) = REF #( itab[ i ] ). +``` + +If you read a nonexistent line via a table expression, the exception +raising is not always desired. You can also embed the table expression +in a constructor expression using the addition `OPTIONAL`. In +doing so, an unsuccessful reading operation does not raise the +exception. The returned result is a line with initial values. +Alternatively, you can use the addition `DEFAULT` to return a +default line in case of an unsuccessful reading operation which might as +well be another table expression or constructor expression. + +``` abap +DATA(line1) = VALUE #( itab[ i ] OPTIONAL ). + +DATA(line2) = VALUE #( itab[ i ] DEFAULT itab[ i2 ] ). +``` + +

(back to top)

+ +*Reading single lines via table keys* + +Lines can be read by explicitly specifying the table keys or the alias names if available. +```abap +"Example internal table with primary and secondary key and alias names +"Assumption: all components are of type i + +DATA it TYPE SORTED TABLE OF struc + WITH NON-UNIQUE KEY primary_key ALIAS pk COMPONENTS a b + WITH NON-UNIQUE SORTED KEY sec_key ALIAS sk COMPONENTS c d. + +"Table expressions + +"key must be fully specified +line = it[ KEY primary_key COMPONENTS a = 1 b = 2 ]. + +"addition COMPONENTS is optional; same as above +line = it[ KEY primary_key a = 1 b = 2 ]. + +"primary key alias +line = it[ KEY pk a = 1 b = 2 ]. + +"secondary key +line = it[ KEY sec_key c = 3 d = 4 ]. + +"secondary key alias +line = it[ KEY sk c = 3 d = 4 ]. + +"READ TABLE statements +"primary key +READ TABLE it INTO wa WITH TABLE KEY primary_key COMPONENTS a = 1 b = 2. + +"alias +READ TABLE it INTO wa WITH TABLE KEY pk COMPONENTS a = 1 b = 2. + +"secondary key +READ TABLE it INTO wa WITH TABLE KEY sec_key COMPONENTS c = 3 d = 4. + +"alias +READ TABLE it INTO wa WITH TABLE KEY sk COMPONENTS c = 3 d = 4. + +"Reading a line based on keys specified in a work area +"Work area containing primary and secondary key values; the line type +"must be compatible to the internal table +DATA(pr_keys) = VALUE struc( a = 1 b = 2 ). + +DATA(sec_keys) = VALUE struc( c = 3 d = 4 ). + +READ TABLE it FROM pr_keys INTO wa. + +"If USING KEY is not specified, the primary table key is used. +"If it is used, the specified table key is used. +READ TABLE it FROM pr_keys USING KEY primary_key wa. + +READ TABLE it FROM sec_keys USING KEY sec_key wa. + +"alias +READ TABLE it FROM sec_keys USING KEY sk INTO wa. +``` + +

(back to top)

+ +**Reading a single line via free key** + +The specified components used as keys need not belong to a table key. +``` abap +line = it[ b = 2 ]. + +READ TABLE it INTO wa WITH KEY b = 2. +``` + +*Addressing individual components* + +When reading single lines in general, you can also address individual +components of the line using the [component +selector](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomponent_selector_glosry.htm "Glossary Entry") +`-` (or the [dereferencing +operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendereferencing_operat_glosry.htm "Glossary Entry") +`->*` in case of data reference variables). +``` abap +DATA(comp1) = it[ b = 2 ]-c. + +READ TABLE it INTO DATA(wa) WITH KEY b = 2. +DATA(comp2) = wa-c. + +READ TABLE it ASSIGNING FIELD-SYMBOL() WITH KEY b = 2. +DATA(comp3) = -c. + +READ TABLE it REFERENCE INTO DATA(dref) WITH KEY b = 2. +DATA(comp4) = dref->*-c. +"Note: dref->c, which is more comfortable, also works +``` + +

(back to top)

+ +**Checking the existence and the index of a line in an internal table** + +This is relevant if you are not interested in the content of a table +line but just want to find out if a line exists that matches to the +index or key specifications. You can do this using a [`READ TABLE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapread_table.htm) +statement with the addition `TRANSPORTING NO FIELDS`. The +addition denotes that no actual content is to be read. If the search was +successful and an entry exists, the system field `sy-subrc` is +set to 0. + +A newer way to check the existence of a line is the [predicate +function](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenpredicate_function_glosry.htm "Glossary Entry") +[`line_exists( )`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenline_exists_function.htm). +As an argument, this statement expects a [table +expression](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentable_expression_glosry.htm "Glossary Entry"). +See more on table expressions below. Note that system fields are not set +with table expressions. +``` abap +"via key + +READ TABLE it WITH KEY b = 2 TRANSPORTING NO FIELDS. + +IF sy-subrc = 0. + ... +ENDIF. + +"via index + +READ TABLE it INDEX 1 TRANSPORTING NO FIELDS. + +IF sy-subrc = 0. + ... +ENDIF. + +"via key + +IF line_exists( it[ b = 2 ] ). + ... +ENDIF. + +"via index + +IF line_exists( it[ 1 ] ). + ... +ENDIF. +``` +If you want to find out about the index of a line in an internal table, you can also make use of the `READ TABLE` statement above. If +the line is found, the system field `sy-tabix` is set with the number of the index. Apart from that, the built-in function +[`line_index( )`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenline_index_function.htm) can be used. It returns the index of the searched line or 0 if the line does not exist. +``` abap +DATA(idx) = line_index( it[ b = 2 ] ). +``` + +`lines( )` is another built-in function with which you can check how many lines exist in an internal table. The function returns an integer value. + +``` abap +DATA(number_of_lines) = lines( it ). +``` + +

(back to top)

+ +### Processing Multiple Internal Table Lines Sequentially + +If you are not only interested in single table lines but in the entire +table content or particular parts, you can use [`LOOP +AT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaploop_at_itab.htm) +statements to process table lines sequentially. Similar to above, you +can make use of the multiple target area options: work area, field +symbol, data reference. The following snippets only include the work +area. There are multiple additions available for `LOOP AT` +statements to further restrict the table content to be processed. + +Simple form: +``` abap +LOOP AT it INTO wa. "Inline declarations possible: INTO DATA(wa) + "No addition of the loop statement; all lines are processed + "Statements in this block are relevant for each individual table line. + ... +ENDLOOP. +``` + +The order in which tables are looped across depends on the table category. Index tables are looped across via the index in ascending +order. In case of hashed tables, the looping happens in the order in which the lines were added to the table. However, you can also sort the table before the loop. In the course of the loop, the system field `sy-tabix` is set to the number of the currently processed table +line. This is not true for hashed tables. There, `sy-tabix` is `0`. Note that if you want to work with the `sy-tabix` value, you +should do it right after the `LOOP` statement to not risk a potential overwriting in statements contained within the loop block. + +*Restricting the table area to be looped across* + +The additions of `LOOP` statements enter the picture if you want to restrict the table content to be respected for the loop because +you do not want to loop across the whole table. + +``` abap +"FROM/TO: Only for index tables + +"Specifying an index range +LOOP AT it INTO wa FROM 2 TO 5. + ... +ENDLOOP. + +"From specified line until the end +LOOP AT it INTO wa FROM 2. + ... +ENDLOOP. + +"From first line until the specified line +LOOP AT it INTO wa TO 5. + ... +ENDLOOP. + +"WHERE clause: Restricting lines based on logical expression + +LOOP AT it INTO wa WHERE a > 1 AND b < 4. + ... +ENDLOOP. + +"No interest in table content; only relevant system fields are filled + +"Mandatory WHERE clause +LOOP AT it TRANSPORTING NO FIELDS WHERE a < 5. + ... +ENDLOOP. + +"Table key specification (snippet uses example table from above) +"The specified table key affects the order in which the table lines +"are accessed and the evaluation of the other conditions. + +LOOP AT it INTO wa USING KEY primary_key. +"LOOP AT it INTO wa USING KEY pk. "primary key alias +"LOOP AT it INTO wa USING KEY sec_key. "secondary key +"LOOP AT it INTO wa USING KEY sk. "secondary key alias + ... +ENDLOOP. +``` + +*Iterations with* `FOR` + +Iteration expressions with [`FOR`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfor.htm) as part of particular constructor expressions allow you to create content of an internal table by evaluating one or more source tables. + +The examples below show iterations with `FOR` within a constructor expression with `VALUE`. A new table is created and +values for two fields are inserted in the new table that has the source table's internal table type. `ls` represents an iteration +variable that holds the data while looping across the table. The components, and thus the table line, that should be returned are +specified within the pair of parentheses before the closing parenthesis. Both examples set specific values for components. The second example also includes a `WHERE` clause to restrict the lines to be copied. + +In contrast to `LOOP` statements, this sequential processing cannot be debugged. +``` abap +"Internal table type +TYPES ttype like it. + +DATA(tab1) = VALUE ttype( FOR ls IN it ( a = ls-a b = 9 ) ). + +DATA(tab2) = VALUE ttype( FOR ls IN it WHERE ( a < 7 ) + ( a = ls-a b = ls-b + 5 ) ). +``` + +

(back to top)

+ +### Sorting Internal Tables + +- Sorted tables are stored in the memory in an automatically sorted + order, hence, they cannot be sorted explicitly using + [`SORT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapsort_itab.htm). +- In case of standard and hashed tables, the order can be changed. +- When using `SORT` statements, the sort order is either + derived by the primary table key ([Note:] Secondary keys + cannot be used for the sorting.) or by explicitly specifying the + fields to be sorted by. +- An explicit specification is the recommended way because it is + easier to understand and can prevent undesired sorting results + especially with tables with standard key. + +*Sorting by primary key* +``` abap +"Implicit sorting by primary key and in ascending order by default + +SORT itab. + +"Optional additions to determine the sort order +"Explicit specification of ascending sort order + +SORT itab ASCENDING. +SORT itab DESCENDING. +``` + +The effect of the sorting might have an unexpected result if you use the simple form of the statement and without an explicit specification of keys. If an internal table has a structured line type and (maybe inadvertently) the standard key as the primary table key, i. e. all character-like and byte-like components compose the primary table key, all of these components are respected when sorting the table. +``` abap +"Is basically the same as it2 +DATA it1 TYPE TABLE OF zdemo_abap_fli. + +DATA it2 TYPE STANDARD TABLE OF zdemo_abap_fli WITH DEFAULT KEY. + +"Respect the standard key when sorting. +SORT it1. +``` +Plus: Assume there are only elementary numeric components in an internal table with a structured line type. Then, the sorting has no effect because the primary table key is considered empty. This is certainly also true for tables declared with `EMPTY KEY`. + +*Sorting by explicitly specifying components* + +The sorting can be carried out using arbitrary components of the internal table. The specification of the sort order is also possible +(even component-wise). The explicit specification of components to sort by has the advantage that your code is easier to understand and you can prevent unexpected results when inadvertently using `SORT` without the `BY` addition in case of empty and standard table +keys. + +``` abap +DATA it3 TYPE TABLE OF struc WITH NON-UNIQUE KEY a. + +"Sorts by primary table key a +SORT itab. + +"Specifying the component to sort for; here, it is the same as the key; +"this way, the sorting is easier to understand + +SORT itab BY a. + +"Syntax showing multiple component sorting with component-wise sort order + +SORT itab BY a b ASCENDING c DESCENDING. + +"Sorting respecting the entire line (e. g. in the context of tables with +"empty or standard keys) + +SORT itab BY table_line. +``` + +

(back to top)

+ +### Modifying Internal Table Content + +As touched on above, you can directly modify the content of internal table lines in the context of `READ TABLE` and `LOOP AT` +statements using field symbols and data reference variables. Direct modification is also possible using table expressions. Note that the key fields of the primary table key of sorted and hashed tables are always read-only. If you try to modify a key field, a runtime error occurs. However, this is not checked until runtime. + +The following examples demonstrate the direct modification of recently read table lines: +``` abap +"Table declarations + +DATA it_st TYPE TABLE OF struc WITH NON-UNIQUE KEY a. + +DATA it_so TYPE SORTED TABLE OF struc WITH UNIQUE KEY a. + +"Reading table line into target area + +READ TABLE it_st ASSIGNING FIELD-SYMBOL() INDEX 1. + +READ TABLE it_so REFERENCE INTO DATA(dref) INDEX 2. + +"Modification examples +"Modifying the entire table line while keeping the values of other components; +"this way is not possible for it_so because of key value change. + + = VALUE #( BASE a = 1 b = 2 ). + +"Modifying a single component via field symbol + +-c = 3. + +"Modification via dereferencing + +ref->b = 4. + +"Table expressions + +it_st[ 1 ] = VALUE #( a = 1 b = 2 ). + +it_st[ 2 ]-c = 3. + +"Sorted table: no key field change + +it_so[ 2 ]-d = 4. +``` + +> **💡 Note**
+> If you choose to modify recently read lines in a work area, for example, within a loop (`LOOP AT INTO dobj`), you +might modify the line and then modify the internal table based on this line using a `MODIFY` statement. + +[`MODIFY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_itab.htm) +statements offer multiple options for changing the content of single and multiple table lines by specifying the table key or a table index +without reading lines into a target area first. + +``` abap +"Addition FROM ...; specified key values determine the line to be modified + +"line: existing line including key values + +MODIFY TABLE it FROM line. + +"line constructed inline + +MODIFY TABLE it FROM VALUE #( a = 1 b = 2 ... ). + +"Respecting only specified fields with the addition TRANSPORTING +"In case of sorted/hashed tables, key values cannot be specified. + +MODIFY TABLE it FROM line TRANSPORTING b c. + +"Modification via index +"Note that it is only MODIFY, not MODIFY TABLE. +"Example: It modifies the line with number 1 in the primary table index. + +MODIFY it FROM line INDEX 1. + +"Without the addition TRANSPORTING, the entire line is changed. +"Example: It modifies specific values. + +MODIFY it FROM line INDEX 1 TRANSPORTING b c. + +"USING KEY addition +"If the addition is not specified, the primary table key is used; +"otherwise, it is the explicitly specified table key that is used. +"Example: It is the same as MODIFY it FROM line INDEX 1. + +MODIFY it FROM line USING KEY primary_key INDEX 1. + +"The statement below uses a secondary key and an index specification +"for the secondary table index. Only specific fields are modified. + +MODIFY it FROM line USING KEY sec_key INDEX 1 TRANSPORTING c d. + +"Modifying multiple lines in internal tables +"All lines matching the logical expression in the WHERE clause are modified +"as specified in line. +"The additions TRANSPORTING and WHERE are both mandatory; USING KEY is optional. + +MODIFY it FROM line TRANSPORTING b c WHERE a < 5. +``` +> **💡 Note**
+> The system field `sy-subrc` is set to `0` if at least one line was changed. It is set to `4` if no lines were changed. + +

(back to top)

+ +### Deleting Internal Table Content + +Using [`DELETE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapdelete_itab.htm) statements, you can delete single and multiple lines in internal tables. +``` abap +"Deleting via index +"Example: The first line in the table is deleted. + +DELETE it INDEX 1. + +"If USING KEY is not used, INDEX can only be used with index tables. +"If doing so, it determines the line from the primary table index. +"If a secondary key is specified, the secondary table index is respected +"Example: same as above + +DELETE it INDEX 1 USING KEY primary_key. + +"Deleting an index range; FROM or TO alone can also be specified + +DELETE it FROM 2 TO 5. + +"Deleting via keys +"The line must have a compatible type to the tables line type and +"include key values. The first found line with the corresponding keys +"is deleted. +"If the key is empty, no line is deleted. + +DELETE TABLE it FROM line. + +"Instead of specifying the keys using a data object ("line" above), +"the keys can be specified separately. All key values must be specified. +"Example: Respects keys from primary table index. + +DELETE TABLE it WITH TABLE KEY a = 1. + +"You can also specify secondary keys. +"Example: Same as above + +DELETE TABLE it WITH TABLE KEY primary_key COMPONENTS a = 1. + +DELETE TABLE it_sec WITH TABLE KEY sec_key COMPONENTS ... + +"Deleting multiple lines based on a WHERE condition +"Specifying the additions USING KEY, FROM, TO is also possible. + +DELETE it WHERE a < 6. +``` + +`DELETE ADJACENT DUPLICATES` statements allow you to delete all neighboring lines except for the first line that have the same content +in specific components. Usually, a suitable sorting before carrying out these statements is required. +``` abap +"Implicitly uses the primary table key + +DELETE ADJACENT DUPLICATES FROM it. + +"Deletion respecting the values of the entire line + +DELETE ADJACENT DUPLICATES FROM it COMPARING ALL FIELDS. + +"Only lines are delete with matching content in specific fields + +DELETE ADJACENT DUPLICATES FROM it COMPARING a c. + +"Deletion respecting a specified table key + +"Same as first example above +DELETE ADJACENT DUPLICATES FROM it USING KEY primary_key. + +DELETE ADJACENT DUPLICATES FROM it USING KEY sec_key. +``` + +> **💡 Note**
+> The system field `sy-subrc` is set to `0` if at least one line was deleted. It is set to `4` if no lines were deleted. + +Using +[`CLEAR`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapclear.htm) +and +[`FREE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapfree_dataobject.htm) +statements, you can delete the complete table content. + +The difference between the two is the handling the initially requested +memory space for the table. When clearing a table using `CLEAR`, +the content is removed but the initially requested memory space remains +allocated. If the table is later filled again, the memory space is still +available, which is beneficial in terms of performance in contrast to +clearing an internal table using `FREE`. Such a statement also +clears the table content but, additionally, it releases the memory +space. + +``` abap +CLEAR it. + +"Additionally, it releases memory space. +FREE it. +``` +

(back to top)

+ +## More Information +Topic [Internal +Tables](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenitab.htm) in the ABAP Keyword Documentation. + +## Executable Example +[zcl_demo_abap_internal_tables](./src/zcl_demo_abap_internal_tables.clas.abap) + +Note the steps outlined [here](README.md#🎬-getting-started-with-the-examples) about how to import and run the code. diff --git a/02_Structures.md b/02_Structures.md new file mode 100644 index 0000000..70247dc --- /dev/null +++ b/02_Structures.md @@ -0,0 +1,669 @@ + +# Working with Structures + +- [Working with Structures](#working-with-structures) + - [Structures ...](#structures-) + - [Creating Structures and Structured Types](#creating-structures-and-structured-types) + - [Variants of Structures](#variants-of-structures) + - [Working with Structures](#working-with-structures-1) + - [Accessing Components of Structures](#accessing-components-of-structures) + - [Filling Structures](#filling-structures) + - [Clearing Structures](#clearing-structures) + - [Structures in Use in the Context of Tables](#structures-in-use-in-the-context-of-tables) + - [Excursion: Including Structures](#excursion-including-structures) + - [More Information](#more-information) + - [Executable Example](#executable-example) + +## Structures ... + +- (or structured [data + objects](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_object_glosry.htm "Glossary Entry")) + are ABAP variables typed with [structured + types](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstructured_type_glosry.htm "Glossary Entry"). +- are [complex data + types](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomplex_data_type_glosry.htm "Glossary Entry"). +- are composed of a sequence of other data objects which are called + [components](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomponent_glosry.htm "Glossary Entry"). +- have components that can be of any type, that is, they can + themselves be, for example, structures or [internal + tables](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninternal_table_glosry.htm "Glossary Entry"). +- summarize different pieces of information, that is, data objects, + that belong together. A typical example is the following: an address + has several components like name, street, city, and so on. +- mostly serve as line types of internal tables and [database + tables](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendatabase_table_glosry.htm "Glossary Entry"). +- are often used to process data sequentially, such as the data stored + in the rows and columns of a database table. +- and structured types can typically be found in the [ABAP Dictionary + (DDIC)](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_dictionary_glosry.htm "Glossary Entry"), + sometimes in [global + classes](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenglobal_class_glosry.htm "Glossary Entry"); + or they can be defined locally in ABAP programs. + - Most of the structures that are worked with in ABAP programs may + be globally defined structures in the DDIC. + - Why? Apart from globally defined structures in the DDIC, each + database table or + [view](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenview_glosry.htm "Glossary Entry") + constitutes a structured type. When a database table etc. is + activated, a globally available structured type of the same name + is created, too. Hence, in an ABAP program, a database table's + name is typically used as type name to declare data objects, for + example, structures or internal tables. +- can be addressed as a whole. You can also address the individual + components of structures. + +## Creating Structures and Structured Types + +> **💡 Note**
+> This cheat sheet focuses on locally defined structures and structured types. + +The typical syntactical elements are [`BEGIN OF ... END OF ...`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaptypes_struc.htm). +They are used in combination with +[`TYPES`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaptypes.htm) +to create a structured type and +[`DATA`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapdata.htm) +to create a structure. + +**Creating structured types** + +The structures' components are listed between `... BEGIN OF ...` and `... END OF ...`. +``` abap +TYPES: BEGIN OF struc_type, +         comp1 TYPE ..., +         comp2 TYPE ..., +         comp3 TYPE ..., +         ..., +       END OF struc_type. +``` +Alternatively, you could go with the following syntax, however, a chained statement with the colon `:` above is preferable due to +better readability. +``` abap +TYPES BEGIN OF struc_type. + TYPES comp1 TYPE ... . + TYPES comp2 TYPE ... . + TYPES comp3 TYPE ... . +... . +TYPES END OF struc_type. +``` +As mentioned above, the components of structures can be of any type. +They can be +[elementary](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenelementary_data_type_glosry.htm "Glossary Entry") +and also complex, that is, a structure component can be a structure or +internal table. Besides this, the components can be [reference +variables](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenreference_variable_glosry.htm "Glossary Entry"), +too. + +For the components, the additions +[`TYPE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapdata_simple.htm) +and +[`LIKE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapdata_referring.htm) +are possible. +> **💡 Note**
+> Setting default values with +[`VALUE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapdata_options.htm) +is - in contrast to the creation of structures using a `DATA` +statement as shown further down - not possible in this case. + +``` abap +TYPES: BEGIN OF struc_type, +         comp1 TYPE i,                 "elementary type +         comp2 TYPE c LENGTH 5,         "elementary type +         comp3 TYPE local_structured_type,   "local structured type + +         comp4 TYPE itab_type,         "internal table type + +         comp5 TYPE ddic_type,         "DDIC type +         comp6 TYPE REF TO i,           "reference +         comp7 LIKE data_object,       "deriving type from a data object +         ..., +       END OF struc_type. +``` + +**Creating structures** + +There are multiple ways to create structures in a program using a `DATA` statement: + +- Creating a structure either based on a local type or a globally available type from the DDIC: + + ``` abap + DATA ls_from_local TYPE struc_type, +      ls_from_global TYPE ddic_type. + ``` + +- Creating a structure by directly specifying the components using `... BEGIN OF ... END OF ...`. The syntax is similar to the `TYPES` statement. Here, default values can be set with +`VALUE`. + + ``` abap + DATA: BEGIN OF struc, +         comp1 TYPE ..., +         comp2 TYPE ... VALUE ..., +         comp3 TYPE i VALUE 99, +         comp4 TYPE i VALUE IS INITIAL, +         comp5 TYPE local_structured_type, +         ..., +       END OF struc. + ``` + + Alternatively and similar to the `TYPES` statement, you + could use the following syntax, however, a chained statement with the colon `:` as above is preferable due to better + readability. + + ``` abap + DATA BEGIN OF struc. + DATA comp1 TYPE ... . + DATA comp2 TYPE ... VALUE ... . + ... . + DATA END OF struc. + ``` +- Creating a structure by referring to a local structured data object using `LIKE` or to an internal table using `LIKE LINE OF`. + ``` abap + DATA struc1 LIKE other_struc. + + DATA struc2 LIKE LINE OF itab. + ``` +- Creating a structure by inline declaration, e. g. using `DATA( ... )`. + ``` abap + "Type is derived from the right-hand structure; + "the content of struc is assigned, too. + + DATA(struc1) = struc. + + "Using the VALUE operator; structured type is specified before the + "first parenthesis; component values can be assigned, too + + DATA(struc2) = VALUE struc_type( comp1 = ... ). + + "It is particularly handy for declaring the structure where you actually need it + "without prior extra declaration of the structure in various contexts; + "e. g. SELECT or LOOP AT statements; structured types are automatically + "derived from the context + + SELECT SINGLE * + FROM zdemo_abap_fli + WHERE carrid = 'LH' + INTO @DATA(struc3). + + LOOP AT itab INTO DATA(wa). +   ... + ENDLOOP. + ``` + +## Variants of Structures + +Depending on the component type, the structure can be a [flat +structure](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenflat_structure_glosry.htm "Glossary Entry"), +a [nested +structure](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abennested_structure_glosry.htm "Glossary Entry"), +or a [deep +structure](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendeep_structure_glosry.htm "Glossary Entry"). + +**Flat structure** + +Flat structures contain only elementary types that have a fixed length, that is, there are no internal tables, reference types or strings as components: +``` abap +DATA: BEGIN OF structure, +        comp1 TYPE i, +        comp2 TYPE c LENGTH 15, +        comp3 TYPE p LENGTH 8 DECIMALS 2, +        ..., +      END OF structure. +``` +> **💡 Note**
+> Nesting does not play a role in this context. Even a nested structure is flat unless a substructure contains a deep component. + +**Nested structure** + +At least one component of a structure is a +[substructure](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensubstructure_glosry.htm "Glossary Entry"), +that is, it refers to another structure. The following example has +multiple substructures. +``` abap +DATA: BEGIN OF address_n, +        BEGIN OF name, +          title   TYPE string VALUE `Mr.`, +          prename TYPE string VALUE `Duncan`, +          surname TYPE string VALUE `Pea`, +        END OF name, +        BEGIN OF street, +          name   TYPE string VALUE `Vegetable Lane`, +          number TYPE string VALUE `11`, +        END OF street, +        BEGIN OF city, +          zipcode TYPE string VALUE `349875`, +          name    TYPE string VALUE `Botanica`, +        END OF city, +      END OF address_n. +``` + +**Deep structure** + +A deep structure contains at least one internal table, reference type, or string as a component. +``` abap +DATA: BEGIN OF address_d, +        name    TYPE string VALUE `Mr. Duncan Pea`, +        street  TYPE string VALUE `Vegetable Lane 11`, +        city    TYPE string VALUE `349875 Botanica`, +        details TYPE TABLE OF some_table WITH EMPTY KEY, +      END OF address_d. +``` +Despite the fact that the following structure looks fairly simple, it is not to be considered a flat structure but a deep structure since it contains strings. +``` abap +DATA: BEGIN OF address, +        name   TYPE string VALUE `Mr. Duncan Pea`, +        street TYPE string VALUE `Vegetable Lane 11`, +        city   TYPE string VALUE `349875 Botanica`, +      END OF address. +``` + +## Working with Structures + +### Accessing Components of Structures + +The structure as a whole and also the individual components can be +addressed. To address the components, you use the [structure component +selector](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstructure_component_sel_glosry.htm "Glossary Entry") +`-`: +``` abap +structure-comp1 ... + +address-name ... +``` + +Nested components can be addressed via chaining: +``` +structure-substructure-comp1 ... + +address_n-name-title ... +``` + +> **✔️ Hints**
+>- You can refer to structure components when creating new data types and data objects: +> ``` abap +> TYPES: lty_1 TYPE structured_type-comp1, +>        lty_2 LIKE structure-comp1. +> +> DATA: lv_1 TYPE structured_type-comp1, +>       lv_2 LIKE structure-comp1. +> ``` +>- ADT and the ABAP Editor provide code completion for structure components after the component selector. +>- When declaring a variable with reference to a structured data object, the object component selector `->` can be used: + `...dref->comp ...` (apart from the needlessly longer syntax `... dref->*-comp ...`). + +### Filling Structures + +Filling structure components using the component selector. +``` abap +address-name   = `Mr. Duncan Pea`. + +address-street = `Vegetable Lane 11`. + +address-city   = `349875 Botanica`. +``` +Filling structure components using the +[`VALUE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_value.htm) +operator. Value assignments by addressing the structure +components individually can be very bulky. Hence, the use of the +`VALUE` operator is very handy for the value assignment, +especially for filling structure components at [operand position](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenoperand_position_glosry.htm "Glossary Entry"). +In the example below, the `#` sign is used before the +parentheses which means that the type of the operand can be implicitly +derived. This is not the case for the example further down in which a +new structure is [declared inline](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_inline.htm). +Hence, the type must be explicitly specified before the parentheses. +Note that components that are not specified and assigned a value remain +initial. +``` abap +address = VALUE #( name   = `Mr. Duncan Pea` +                   street = `Vegetable Lane 11`. +                   city   = `349875 Botanica` ). +``` +Using the `VALUE` operator and inline declarations, structures can be created and filled in one go. +``` abap +TYPES address_type LIKE address. + +DATA(add2) = VALUE address_type( name   = `Mrs. Jane Pea` +                                 street = `Vegetable Lane 11`. +                                 city   = `349875 Botanica` ). +``` +Copying the content of a structure to another one that has the same type. For value assignments, generally +bear in mind that there are special [conversion](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconversion_struc.htm) +and [comparison rules](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_rules_operands_struc.htm) +that apply to assignments involving structures.. +``` abap +address = add2. + +"When creating a new structure by inline declaration, the type of +"the right-hand structure is derived and the content is assigned. + +DATA(add3) = add2. +``` +Copying content of a structure to another one that has a different type. You can use statements with +[`MOVE-CORRESPONDING`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmove-corresponding.htm) +and the +[`CORRESPONDING`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expr_corresponding.htm) +operator. Both are used to assign identically named components of +structures to each other. This syntax also works for structures that +have the same type. + +Note the rules mentioned above and consider the result of the value +assignment when using either of the two options with +`MOVE-CORRESPONDING` and the `CORRESPONDING` operator. +See more information and syntax variants in the ABAP Keyword +Documentation: +[`MOVE-CORRESPONDING`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencorresponding_constr_arg_type.htm) +and +[`CORRESPONDING`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencorresponding_constr_arg_type.htm). +In the examples below, the focus is only on flat structures. +``` abap +"Moves identically named components; content in other components +"of the targets structure are kept. + +MOVE-CORRESPONDING struc TO diff_struc. + +"Initializes target structure; moves identically named components + +diff_struc = CORRESPONDING #( struc ). + +"Same effect as the first MOVE-CORRESPONDING statement; +"addition BASE keeps existing content + +diff_struc = CORRESPONDING #( BASE ( diff_struc ) struc ). + +"MAPPING addition: Specifying components of a source structure that are +"assigned to the components of a target structure in mapping +"relationships. + +diff_struc = CORRESPONDING #( BASE ( diff_struc ) +                           struc MAPPING comp1 = compa ). + +"EXCEPT addition: Excluding components from the assignment. + +diff_struc = CORRESPONDING #( BASE ( diff_struc ) +                           struc EXCEPT comp1 ). +``` +**Excursion**: Copying content of a deep structure to another deep structure that has a different type. You can use statements with +[`MOVE-CORRESPONDING`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmove-corresponding.htm) +and the +[`CORRESPONDING`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expr_corresponding.htm) +operator here, too. However, in the context of deep structures, more syntax variants are available. The focus of the following examples is on internal tables as structure components. See the executable example for visualizing the effect. + +> **💡 Note**
+> Bear in mind that there are special conversion and comparison rules that apply to assignments involving structures and that affect the value assignments in the internal table components. + +``` abap +"Nonidentical elementary component types are kept in target +"structure which is true for the below MOVE-CORRESPONDING statements; +"existing internal table content is replaced by content of +"the source table irrespective of identically named components + +MOVE-CORRESPONDING deep_struc TO diff_deep_struc. + +"Existing internal table content is replaced but the value +"assignment happens for identically named components only. + +MOVE-CORRESPONDING deep_struc TO diff_deep_struc +     EXPANDING NESTED TABLES. + +"Existing internal table content is kept; table content of the source +"structure are added but the value assignment happens like the first +"MOVE-CORRESPONDING statement without further syntax additions. + +MOVE-CORRESPONDING deep_struc TO diff_deep_struc +     KEEPING TARGET LINES. + +"Existing internal table content is kept; table content of the source +"structure are added; the value assignment happens like the statement +"MOVE-CORRESPONDING ... EXPANDING NESTED TABLES. + +MOVE-CORRESPONDING deep_struc TO diff_deep_struc +    EXPANDING NESTED TABLES KEEPING TARGET LINES. + +"Target structure is initialized; the value assignment for an internal +"table happens irrespective of identically named components. + +diff_deep_struc = CORRESPONDING #( deep_struc ). + +"Target structure is initialized; the value assignment for an internal +"table happens for identically named components only. + +diff_deep_struc = CORRESPONDING #( DEEP deep_struc ). + +"Nonidentical elementary component types are kept in target structure; +"internal table content is replaced; there, the value assignment +"happens like using the CORRESPONDING operator without addition. + +diff_deep_struc = CORRESPONDING #( BASE ( diff_struc ) deep_struc ). + +"Nonidentical elementary component types are kept in target structure; +"internal table content is replaced; there, the value assignment +"happens like using the CORRESPONDING operator with the addition DEEP. + +diff_deep_struc = CORRESPONDING #( DEEP BASE ( diff_struc ) deep_struc ). + +"Nonidentical elementary component types are kept in target structure; +"internal table content is kept, too, and table content of the +"source structure are added; there, the value assignment +"happens like using the CORRESPONDING operator without addition. + +diff_deep_struc = CORRESPONDING #( APPENDING BASE ( diff_struc ) deep_struc ). + +"Nonidentical elementary component types are kept in target structure; +"internal table content is kept, too, and table content of the +"source structure are added; there, the value assignment +"happens like using the CORRESPONDING operator with the addition DEEP. + +diff_deep_struc = CORRESPONDING #( DEEP APPENDING BASE ( diff_struc ) deep_struc ). +``` +                                   +## Clearing Structures +You can reset individual components to their initial value and clear the +complete structure using the keyword +[`CLEAR`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapclear.htm). +``` +CLEAR structure-component. + +CLEAR structure. +``` + +## Structures in Use in the Context of Tables +Structures are primarily used to process data from tables. In this +context, structures often assume the role of a [work +area](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwork_area_glosry.htm "Glossary Entry"). + +**Reading a line from a database table into a structure that has a matching type**. Note that, since database tables are flat, the +target structure must also be flat. In the example below, the addition +[`SINGLE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_single.htm) +reads only a single row into the structure. The first entry that is +found according to the `WHERE` condition is returned. + +> **💡 Note**
+> See more details on +[`SELECT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect.htm) +statements in the ABAP Keyword Documentation. The ABAP cheat sheet [ABAP +SQL: Working with Persisted Data in Database +Tables](03_ABAP_SQL.md) +also provides information and more code snippets when using +`SELECT` statements. +``` abap +"Creating a structure with a matching type +DATA ls_fli1 TYPE zdemo_abap_fli. + +SELECT SINGLE FROM zdemo_abap_fli +    FIELDS * +    WHERE carrid = 'LH' +    INTO @ls_fli1. + +"Target structure declared inline +SELECT SINGLE FROM zdemo_abap_fli +    FIELDS * +    WHERE carrid = 'LH' +    INTO @DATA(ls_fli2). +``` +**Reading a line from a database table into a structure that has a different type**. +``` abap +SELECT SINGLE FROM zdemo_abap_fli +    FIELDS * +    WHERE carrid = 'AA' +    INTO CORRESPONDING FIELDS OF @ls_fli_diff. +``` +**Reading a line from an internal table into a structure** ... + +... using a `SELECT` statement. Note the specified alias name and that ABAP variables like internal tables must be escaped using `@`. The addition `INTO CORRESPONDING FIELDS OF` also applies here. +``` abap +SELECT SINGLE FROM @itab AS itab_alias +    FIELDS * +    WHERE ... +    INTO @DATA(ls_struc). +    "INTO CORRESPONDING FIELDS OF @struc. +``` +... using a `READ TABLE` statement. The code snippet below shows the reading of one line +into a [work +area](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwork_area_glosry.htm "Glossary Entry"), +[field +symbol](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfield_symbol_glosry.htm "Glossary Entry") +and a [data reference +variable](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_reference_variable_glosry.htm "Glossary Entry"), +all representing structured data objects and declared inline below. In +the example below, the reading of a line is based on the line number by +specifying `INDEX`. See the section `Determining the target area` in the cheat sheet [Working with Internal Tables](01_Internal_Tables.md#) for more details. +``` abap +READ TABLE itab INTO DATA(wa) INDEX 1. + +READ TABLE itab ASSIGNING FIELD-SYMBOL() INDEX 2. + +READ TABLE itab REFERENCE INTO DATA(dref) INDEX 3. +``` +... using a [table +expression](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentable_expression_glosry.htm "Glossary Entry"). +The code snippet below shows the reading of one line into a structure +that is declared inline. The index is specified in square brackets. +``` abap +DATA(ls_table_exp) = itab[ 3 ]. +``` + +> **💡 Note**
+> See more syntax variants and code snippets for `READ TABLE` statements and statements with table expressions in the ABAP cheat sheet `Working with Internal Tables`. + +**Sequentially reading a line from** ... + +... a database table into a structure. A `SELECT` loop can be specified by using the syntax `SELECT ... ENDSELECT.`. +In the simple example below, the line that is found and returned in a structure, which is declared inline, can be further processed. +A `SELECT` loop might also have an internal table as data source. +``` abap +SELECT FROM zdemo_abap_fli +    FIELDS * +    WHERE carrid = 'AZ' +    INTO @DATA(ls_sel_loop). +      IF sy-subrc = 0. +        ... +      ENDIF. +ENDSELECT. +``` +... an internal table into a structure using a [`LOOP AT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaploop_at_itab_variants.htm) statement. There are numerous options for specifying the condition on which the loop is based. The example below covers the option of reading all lines sequentially into a field symbol which is declared inline. When using a field symbol, components can be directly modified, for example. +``` abap +LOOP AT itab ASSIGNING FIELD-SYMBOL(). +   -comp1 = ... +   ... +ENDLOOP. +``` +**Inserting an individual row from a structure into a database table** using ABAP SQL statements with +[`INSERT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapinsert_dbtab.htm). The statements below can be considered as alternatives. The third statement demonstrates that the structure might also be created and filled directly instead of inserting a line from an existing structure. Note that a line with a certain key should not be inserted into the database table if a line with the same key already exists there. +``` abap +INSERT INTO dbtab VALUES @structure. + +INSERT dbtab FROM @structure. + +INSERT dbtab FROM @( VALUE #( comp1 = ... comp2 = ... ) ). +``` +**Updating an individual row from a structure in a database table** using ABAP SQL statements with [`UPDATE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapupdate.htm). Note that with this syntax, the whole line and all components are changed. +``` abap +UPDATE dbtab FROM @structure. + +UPDATE dbtab FROM @( VALUE #( comp1 = ... comp2 = ... ) ). +``` +If you want to update a database table row from a structure by specifying components to be changed without overwriting other components, you might use the following way. First, read the intended line from the database table into a structure. Then, use the `VALUE` operator with the addition `BASE` and specify the components to be changed. +``` abap +SELECT SINGLE * + FROM dbtab + WHERE ... + INTO @DATA(wa). + +UPDATE dbtab FROM @( VALUE #( BASE wa comp2 = ... comp4 = ... ) ). +``` +**Updating or creating an individual row in a database table from a structure** using ABAP SQL statements with +[`MODIFY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_dbtab.htm). If a line in the database table already exists with the same keys as specified in the structure, the line is updated. If a line does not exist with the keys specified in the structure, a new line is created in the database table. +``` abap +MODIFY dbtab FROM @structure. + +MODIFY dbtab FROM @( VALUE #( comp1 = ... comp2 = ... ) ). +``` +**Adding rows to and updating individual rows in an internal table from a structure** using statements with `INSERT`, +`APPEND`, and `MODIFY`. Note that all statements, including `INSERT` and `MODIFY`, are ABAP statements in this context, not ABAP SQL statements. + +Both `INSERT` and `APPEND` add one line (or more) to an internal table. While `APPEND` adds at the bottom of the +internal table, `INSERT` can be used to add lines at a specific position in tables. If you go without specifying the position, then the lines are added at the bottom of the table, too. However, when using `INSERT`, `sy-tabix` is not set as compared to `APPEND`. +`MODIFY` changes the content of an internal table entry. + +Statements using the `VALUE` operator for directly creating and filling the structures are possible, too. For more information and code +snippets, refer to the cheat sheet [Working with Internal Tables](01_Internal_Tables.md#). +``` abap +INSERT structure INTO TABLE itab. + +APPEND structure TO itab. + +MODIFY TABLE itab FROM structure. +``` + +### Excursion: Including Structures + +Although their use is not recommended according to the ABAP programming +guidelines, you might stumble upon statements with [`INCLUDE TYPE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapinclude_type.htm) +and [`INCLUDE STRUCTURE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapinclude_type.htm) +in the context of local structures. Structured data objects and types +that are created with `... BEGIN OF... END OF ...` can +include the said syntax to integrate components of another structure, no +matter if it is a locally defined or global structure, without the need +to create substructures. `INCLUDE TYPE` can be used to include a +structured type. `INCLUDE STRUCTURE` can be used to include a +structure. + +> **💡 Note**
+> - They are not additions of `... BEGIN OF ... END OF ...` but individual ABAP statements. +> - If a chained statement is used for the structure declaration with the colon, the inclusion of other structures with these statements interrupts the chained statement, that is, the components of the included structures are included as direct components of the [superstructure](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensuperstructure_glosry.htm "Glossary Entry"). +>- Using the optional addition `AS` and the specification of a name, the included components can be addressed by this common name as if the components were actually components of a substructure. +>- Using the optional addition `RENAMING WITH SUFFIX` and the specification of a name, the included components are given a suffix name to avoid naming conflicts with other components. + +The example below shows how structured types and data objects are included in another structure. First, three structured types as well as a structured data object based on one of those types are created. Then, the types and the structure are included in the structured type `address_type`. The demonstration class visualizes a structure that includes other structures this way. +``` abap +TYPES: BEGIN OF name_type, +        title   TYPE string, +        prename TYPE string, +        surname TYPE string, +      END OF name_type, +      BEGIN OF street_type, +        name   TYPE string, +        number TYPE string, +      END OF street_type, +      BEGIN OF city_type, +        zipcode TYPE string, +        name    TYPE string, +      END OF city_type. + +DATA city_struc TYPE city_type. + +TYPES BEGIN OF address_type. +      INCLUDE TYPE name_type AS name. +      INCLUDE TYPE street_type AS street RENAMING WITH SUFFIX _street. +      INCLUDE STRUCTURE city_struc AS city RENAMING WITH SUFFIX _city. +TYPES END OF address_type. +``` + +## More Information +See the topic +[Structures (F1 docu for standard ABAP)](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abendata_objects_structure.htm) +for more information. + +## Executable Example +[zcl_demo_abap_structures](./src/zcl_demo_abap_structures.clas.abap) + +Note the steps outlined [here](README.md#🎬-getting-started-with-the-examples) about how to import and run the code. diff --git a/03_ABAP_SQL.md b/03_ABAP_SQL.md new file mode 100644 index 0000000..42e5311 --- /dev/null +++ b/03_ABAP_SQL.md @@ -0,0 +1,1499 @@ + + +# ABAP SQL: Working with Persisted Data in Database Tables + +- [ABAP SQL: Working with Persisted Data in Database Tables](#abap-sql-working-with-persisted-data-in-database-tables) + - [Database Tables in AS ABAP in a Nutshell](#database-tables-in-as-abap-in-a-nutshell) + - [ABAP SQL Intro](#abap-sql-intro) + - [Reading Data Using SELECT](#reading-data-using-select) + - [Basic Syntax](#basic-syntax) + - [Using SELECT for Multiple Purposes](#using-select-for-multiple-purposes) + - [Clause Variations and Additions in SELECT Statements](#clause-variations-and-additions-in-select-statements) + - [Further Clauses](#further-clauses) + - [Excursion: Operands and Expressions in ABAP SQL Statements](#excursion-operands-and-expressions-in-abap-sql-statements) + - [Excursion: SQL Conditions](#excursion-sql-conditions) + - [Using SELECT when Reading from Multiple Tables](#using-select-when-reading-from-multiple-tables) + - [Excursion: Using Common Table Expressions (CTE)](#excursion-using-common-table-expressions-cte) + - [Changing Data in Database Tables](#changing-data-in-database-tables) + - [Using INSERT](#using-insert) + - [Using UPDATE](#using-update) + - [Using MODIFY](#using-modify) + - [Using DELETE](#using-delete) + - [Further Information](#further-information) + - [Executable Example](#executable-example) + + +## Database Tables in AS ABAP in a Nutshell + +Database tables in [AS +ABAP](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenas_abap_glosry.htm "Glossary Entry") +... + +- are objects of the [ABAP Dictionary + (DDIC)](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_dictionary_glosry.htm "Glossary Entry") +- consist of table rows and columns; each row represents a data record + whose components (or fields) are available in columns; each + component has a data type. +- are [relational + database](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrelational_database_glosry.htm "Glossary Entry") + tables, i. e. information can be stored in multiple database tables + that are related to each other. + - For example, there might be a table containing information on + flight connections, flight destinations and times, another table + is related to this one and includes further details on the + flights like occupied seats in the plane or price details. + - Such tables define a relationship using [foreign + key](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenddic_database_tables_forkeyrel.htm) + relations. +- have at least one key, i.e. the [primary + key](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenprimary_key_glosry.htm "Glossary Entry"), + to uniquely identify table rows; this might be one or more columns + at the beginning of each database table. +- are either + cross-[client](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenclient_glosry.htm "Glossary Entry") + or client-specific to keep the data separated; client-specific + tables, which are the vast majority of database tables, include a + client field (often named `MANDT`) as their first key + field. + - Note: ABAP SQL ensures that a statement only + manipulates data from the current client. +- have a [flat + structure](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenflat_structure_glosry.htm "Glossary Entry") + type. +- are physically created on the database when activated - in contrast + to [internal + tables](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninternal_table_glosry.htm "Glossary Entry"). + Plus, a globally available [structured + type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstructured_type_glosry.htm "Glossary Entry") + of the same name is created, too. Hence, in an ABAP program, a + database table's name can be used to declare data objects, for + example, internal tables. These can be accessed by ABAP SQL, too. +- are primarily processed through ABAP SQL statements that use + [structures](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstructure_glosry.htm "Glossary Entry") + for single rows and internal tables for multiple rows to be + processed. + +
+ Excursion: Views +
+ +**Views ...** + +- are further ABAP Dictionary objects for grouping particular data. +- combine columns of one or more database tables. +- usually realize a + [join](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenjoin_glosry.htm "Glossary Entry") + with defined [join + conditions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenjoin_condition_glosry.htm "Glossary Entry"). +- Note: + - Similar to database tables, the columns of such a view form a + flat structure. Hence, the view's name can be used to declare + data objects, too. + - The views can be accessed by ABAP SQL, especially for reading + purposes using `SELECT`. + +**"Classic"** [DDIC +Views](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenddic_view_glosry.htm "Glossary Entry") ... + +- are the oldest form of views and are not available in SAP BTP ABAP environments. +- can be accessed by ABAP SQL for read and write operations, however, writing is only supported if the view is created with only one database table. +- can only be created in the [ABAP Workbench](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenabap_workbench_glosry.htm). + +**"Modern" Views (since release 7.40)** + +- [External + views](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenexternal_view_glosry.htm "Glossary Entry") + as proxies for [SAP HANA + views](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenhana_view_glosry.htm "Glossary Entry") + (attribute view, analytic view, calculation view) + - SAP HANA Views are entities of the SAP HANA database that are + defined using the [SAP HANA + Studio](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenhana_studio_glosry.htm "Glossary Entry"). + - They are based on HANA-specific data types. + - Using external views of the ABAP dictionary, you can make those + SAP HANA views "known" to the ABAP program. In doing so, the + external views can be used like classic DDIC views as structured + data types and as a source for reading operations with ABAP SQL. + - To be used only if the central database of the AS ABAP is an + [SAP HANA + database](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenhana_database_glosry.htm "Glossary Entry"). +- [ABAP Core Data Services (ABAP + CDS)](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencds_view_glosry.htm "Glossary Entry") + ... + - serve the purpose of defining semantically rich data models. + - have a lot more options than classic views, for example, they + support annotations (provide information about views or + individual fields), data sources can be combined using + associations, unions are possible, or views can be defined with + input parameters. + - are used like a classic database view as structured data types + and used as a source for reading operations with ABAP SQL (using + [`SELECT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect.htm)). + - are created using [Data Definition + Language (DDL)](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenddl_glosry.htm "Glossary Entry") + in the + [ADT](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenadt_glosry.htm "Glossary Entry") + (that is, a source code editor, in contrast to a form-based + editor) + - are, in contrast to External Views, supported by all database + systems (that support the ABAP CDS characteristics). + +> **💡 Note**
+> The code snippets below focus on database tables as [data +source](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_source_glosry.htm "Glossary Entry") +for ABAP SQL statements. +
+ +

(back to top)

+ +## ABAP SQL Intro + +- ABAP-specific form of standard [Structured Query Language + (SQL)](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_glosry.htm "Glossary Entry") + which is the common language to access database tables. +- What happens behind the scenes when using an ABAP SQL statement? + - Generally speaking, tables in relational database systems have a + programming interface allowing table access using standard SQL, + however, these interfaces are not entirely uniform and can have + individual characteristics. + - To make AS ABAP independent of the database used, the ABAP SQL + statements are converted to the corresponding [Native + SQL](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abennative_sql_glosry.htm "Glossary Entry") + statements of the current database system. In doing so, ABAP SQL + allows a hassle-free and uniform access to the database tables + no matter what database system is used. +- The main ABAP SQL keywords to read and change data are the + following: + + | Keyword | Purpose | + | -------- | ------------------------------------------------------------------------- | + | `SELECT` | Reads data from database tables | + | `INSERT` | Adds rows to database tables | + | `UPDATE` | Changes the content of rows of database tables | + | `MODIFY` | Inserts rows into database tables or changes the content of existing rows | + | `DELETE` | Deletes rows from database tables | + +- For a good level of performance of your ABAP programs when using + ABAP SQL, you should follow the rules in the performance notes + outlined + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_sql_perfo.htm). + The considerations there are not relevant for this cheat sheet since + the focus is on syntactical options. + +## Reading Data Using SELECT + +### Basic Syntax + +You use ABAP SQL `SELECT` statements to read records from the +database, either by accessing a database table directly or via a view. +The `SELECT` statement includes several clauses that serve +different purposes. The following code snippet shows the basic syntax: +``` abap +SELECT FROM source "What db table or view to read from +  FIELDS field_list "What columns should be read +  WHERE condition "Specifies conditions on which a row/rows should be read +  INTO target. "Data object into which data should be read +``` +> **💡 Note**
+>- There are further clauses available of which some are dealt with + further down. +>- Especially in older ABAP programs, you will see other forms of the + `SELECT` syntax that you should no longer use. Depending on + the ABAP release in your on-premise system, strict syntax check modes might enforce the use + of specific ABAP SQL syntax. For example, the `INTO` clause + should be placed after the other clauses. This was not possible for + older statements. Furthermore, [host + variables](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenhost_variable_glosry.htm "Glossary Entry") + or [host + expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenhost_expression_glosry.htm "Glossary Entry") + are required for variables and expressions, i. e. variables and + expressions must be preceded by `@` or `@( ... )`. + This is also true for other ABAP SQL statements further down. + Further information: [Release-Dependent Syntax Check + Modes](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenabap_sql_strict_modes.htm). +>- The list of fields can also directly follow the `SELECT` + keyword and be positioned before the `FROM` clause. In this + case, a separate `FIELDS` clause cannot be specified. The + following two code snippets are basically the same: +> ``` abap +> SELECT FROM dbtab +>   FIELDS comp1, comp2, comp3 +>   ... +> +> SELECT comp1, comp2, comp3 +>   FROM dbtab +>   ... +> ``` +>- Regarding the target into which data is read: Instead of using a + variable that is (extra) declared beforehand, you can also make use + of [inline + declarations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninline_declaration_glosry.htm "Glossary Entry"), + for example `... INTO TABLE @DATA(itab).`, to comfortably + create an appropriate variable in place. Note that in case of + internal tables as targets, the resulting table is a standard table + and has an empty key which might have an impact when further + processing the internal table entries. Find more information in the + ABAP cheat sheet [Working with Internal Tables](01_Internal_Tables.md). + +

(back to top)

+ +### Using SELECT for Multiple Purposes + +**Reading a single row into a structure**. The read result can, for +example, be stored in an existing structure (`struc`) or a +structure that is declared inline. Specifying an asterisk (`*`) indicates +that all fields are to be read. Alternatively, you can list all the +fields separated by comma. +``` abap +"Reading all fields of a single row + +SELECT SINGLE FROM dbtab +  FIELDS * +  WHERE ... +  INTO @struc. "Existing structure of dbtab's row type + +"Reading a selected set of fields of a single row + +SELECT SINGLE FROM dbtab +  FIELDS comp1, comp2, comp3 +  WHERE ... +  INTO @DATA(struc2). "Structure declared inline + +"Alternative syntax without the FIELDS addition +"When reading into an existing target variable on the basis of a selected +"set of fields, use a CORRESPONDING addition in the INTO clause + +SELECT SINGLE comp1, comp2, comp3   "Selected set of fields +  FROM dbtab +  WHERE ... +  INTO CORRESPONDING FIELDS OF @struc. "Existing structure +``` +> **💡 Note**
+>- When listing the fields, only those fields that are really of + interest should be read as a rule for performance reasons. +>- It makes a lot of sense to further restrict the read result, for + example and although it is optional, a `WHERE` clause should + always be specified for performance reasons too to restrict the read + result. +>- The addition `CORRESPONDING FIELDS OF` in the `INTO` + clause is required when using an existing variable as target and + listing the fields, otherwise a type compatibility issue might arise + because the variable is filled from left to right beginning with the + first field in the list of fields. + +**Reading multiple rows into an internal table**. The read result +can, for example, be stored in an existing internal table +(`itab`) or an internal table that is declared inline. +``` abap +SELECT FROM dbtab +  FIELDS *   "All fields +  WHERE ... +  INTO TABLE @itab. "itab has an appropriate row type + +"Alternative syntax without the FIELDS addition + +SELECT comp1, comp2, comp3   "Selected set of fields +  FROM dbtab +  WHERE ... +  INTO TABLE @DATA(lv_itab). "Internal table declared inline + +"Reading a selected set of fields into an existing variable + +SELECT FROM dbtab +  FIELDS comp1, comp2, comp3   "Selected set of fields +  WHERE ... +  INTO CORRESPONDING FIELDS OF TABLE @itab. +``` + +`SELECT` **loop: Sequentially reading multiple rows into a structure**. If the row is found, the system field `sy-subrc` is set to `0`. +``` abap +SELECT FROM dbtab +  FIELDS * +  WHERE ... +  INTO @struc. +    IF sy-subrc = 0. +      ...  "For example, making changes on data and adding the row to an internal table. +    ENDIF. +ENDSELECT. +``` + +**Reading into an existing target variable that does not have a matching type**. If you choose to store the result in a variable that has +not a matching type, the `CORRESPONDING FIELDS OF` addition +should be used so as not to mess up the result. Note that this addition +is also valid for `SELECT` statements that use an existing +target variable and only a selected set of fields should be read into +the target variable. + +``` abap +"Reading a single row into an existing structure that does not have a matching type + +SELECT SINGLE FROM dbtab +  FIELDS comp1, comp2, comp3 +  WHERE ... +  INTO CORRESPONDING FIELDS OF @diff_struc. + +"Reading multiple rows into an existing internal table that does not +"have a matching type. Note that the target table is initialized +"with this addition. + +SELECT FROM dbtab +  FIELDS comp1, comp2, comp3 +  WHERE ... +  INTO CORRESPONDING FIELDS OF TABLE @diff_itab. +``` + +> **💡 Note**
+>- If only `INTO` is used, the selected columns must be in the + correct order fitting to the structure type of the target variable. + Only the content of columns for which there are components of the + same name in the structure of the target is read from the result + set. +>- If identically named components have different types, the system + tries to convert the content of source fields into the type of the + target field. In this case, there is a risk of data loss and runtime + errors due to conversion errors. +>- Find more information regarding the addition + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapinto_clause.htm). + +

(back to top)

+ +### Clause Variations and Additions in SELECT Statements + +`SELECT`/`FROM` clause: + +**Checking the existence of a row in a database table** +``` abap +"Instead of @abap_true, you could use 'X' + +SELECT SINGLE @abap_true +  FROM dbtab +  WHERE ... +  INTO @DATA(exists). + +IF exists = abap_true. + ... +ENDIF. +``` + +**Reading multiple rows into an internal table by excluding duplicate rows from the multiline result set** using `DISTINCT`. +The duplicate entries might occur due to a non-unique `WHERE` clause. +``` abap +SELECT DISTINCT comp1 +  FROM dbtab +  WHERE ... +  INTO TABLE @itab. +``` +**Setting new field names by specifying an alias name** with [`AS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_list.htm)]. + +The alias name can be helpful for a situation like this: Data from a +database table is to be read into an existing table but the line type +does not match, some fields might have different names. Using an alias +name, you can read the data into the corresponding field names of the +target table (provided that there will not be an issue regarding the +type). +``` abap +SELECT FROM dbtab +  FIELDS comp1 AS alias1, comp2 AS alias2, comp3 AS alias3 +  WHERE ... +  INTO CORRESPONDING FIELDS OF TABLE @itab. +``` + +**Getting data from a database table in another client** (not available in SAP BTP ABAP environments). Note that there are several variants of the `USING CLIENT` addition, for example, you can also specify `ALL CLIENTS` to select from database tables in all clients. Furthermore, the `USING CLIENT` addition is also available for the ABAP SQL statements that modify database table entries further down. +``` abap +"Not available in SAP BTP ABAP environments +SELECT * +  FROM dbtab USING CLIENT '000'             +  WHERE ... +  INTO TABLE @itab. + +SELECT * +  FROM dbtab USING ALL CLIENTS +  WHERE ... +  INTO TABLE @itab. +``` + +`INTO` **clause**: + +**Restricting the absolute number of returned table rows** using the addition [`UP TO n +ROWS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_up_to_offset.htm). +In the example below, only five rows are to be returned at most. +``` abap +SELECT * FROM dbtab +  WHERE ... +  INTO TABLE @DATA(itab_upto) +  UP TO 5 ROWS. +``` +**Appending the result set to an existing internal table**. By appending, you avoid the deletion of existing lines in internal tables. +``` abap +"itab has a matching line type +SELECT * FROM dbtab +  WHERE ... +  APPENDING TABLE @itab. +``` + +If the target table does not have a matching type, you can use the addition `CORRESPONDING FIELDS OF`. +``` abap +SELECT * FROM dbtab +  WHERE ... +  APPENDING CORRESPONDING FIELDS OF TABLE @diff_itab. +``` +**Reading single fields into individual variables**. Note that the number of columns specified (here, in the `FIELDS` clause) must match the number of elements in the `INTO` clause. +``` abap +SELECT FROM dbtab +  FIELDS comp1, comp2, comp3 +  WHERE ... +  INTO (@res1,@res2,@res3). +``` +**Reading into packages when reading into internal tables**. The +[package +size](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapinto_clause.htm) +defines how many rows should be selected in one iteration. This is handy +in case a very large amount of data has to be processed that might be +too large for the memory capacity of an internal table, thus avoiding +program termination. The package size is specified by an integer value. +``` abap +SELECT FROM dbtab +  FIELDS comp1, comp2, comp3 +  WHERE ... +  INTO TABLE @DATA(itab_pack) PACKAGE SIZE i. +... +ENDSELECT. +``` + +

(back to top)

+ +### Further Clauses + +[`GROUP BY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapgroupby_clause.htm) +clause: Combining groups of table rows in the result set. You +might also use [SQL expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_expression_glosry.htm "Glossary Entry") +here. Multiple clause elements are separated by a comma. Find more +information on SQL expressions further down. + +Note that the `GROUP BY` clause requires all columns that are +directly specified in the `SELECT` list or specified there as an +argument of an SQL expression to be specified. An exception to this is +[aggregate +functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenaggregate_function_glosry.htm "Glossary Entry") +in [aggregate +expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenaggregate_expression_glosry.htm "Glossary Entry") +(except [grouping +functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abengrouping_glosry.htm "Glossary Entry")) +as shown in the following example. + +In the example below, the database table rows that have the same content in column `comp1` are combined. The lowest and highest values in column `comp2` are determined for each of these groups and placed into the combined row. +``` abap +SELECT FROM dbtab +  FIELDS comp1, MIN( comp2 ) AS min, MAX( comp2 ) AS max +  WHERE ... +  GROUP BY comp1 +  INTO ... +``` + +[`HAVING`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaphaving_clause.htm) +clause: Limiting the number of table rows in groups in the +result by setting conditions on these rows. The rows for which a +logical expression is true are inserted in the target variable. Note +that `HAVING` can only be used together with `GROUP BY`. +``` abap +SELECT FROM dbtab +  FIELDS comp1, MIN( comp2 ) AS min, MAX( comp3 ) AS max +  WHERE ... +  GROUP BY comp1 +  HAVING comp1 LIKE '%XYZ%' AND SUM( comp4 ) > 100 +  INTO ... +``` + +[`ORDER BY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaporderby_clause.htm) +clause: Sorting the result set by specified columns. + +The following example shows the ordering of the result set based on the +content of the primary key of the [data +source](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_source_glosry.htm "Glossary Entry"). +You can also order by any columns and by explicitly specifying the sort +order. There are more ordering options, for example, by using SQL +expressions. +``` abap +SELECT FROM dbtab +  FIELDS comp1, comp2, comp3 +  WHERE ... +  ORDER BY PRIMARY KEY +           "comp2 ASCENDING +           "comp2 DESCENDING +  INTO ... +``` + +> **💡 Note**
+>- Not specifying `ORDER BY` means that the order of entries in the result set is undefined. +>- If `ORDER BY` and `GROUP BY` clauses are used, all columns specified after `ORDER BY` must also be specified after `GROUP BY`. +>- If aggregate functions are specified after `SELECT`, all columns that are specified after `ORDER BY` and that do not have an alias name for an aggregate function must also be specified after `SELECT` and after the `GROUP BY` clause which is required in this case, too. + +

(back to top)

+ +### Excursion: Operands and Expressions in ABAP SQL Statements + +ABAP offers plenty of [SQL +operands](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_operand_glosry.htm "Glossary Entry") +and +[expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_expression_glosry.htm "Glossary Entry") +that are possible in ABAP SQL statements, not only in the context of +`SELECT` statements and the `SELECT` lists which are +mainly used for the following demonstration examples. Questions about +when to use what, what is possible in which contexts and positions, is +beyond the scope of this cheat sheet. Check the details in the +respective topics in the ABAP Keyword Documentation. Find a general +overview of important operand positions in ABAP SQL +[here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_operand_positions_oview.htm). +Due to the rich variety of options, the cheat sheet covers a selection. + +**SQL operands** + +- Are elementary operands in an ABAP SQL statement +- Can be database table or view columns, a + [literal](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenliteral_glosry.htm "Glossary Entry"), + [host + variables](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenhost_variable_glosry.htm "Glossary Entry") + (i. e. global or local data objects escaped using `@`: + `@dobj`) or [host + expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenhost_expression_glosry.htm "Glossary Entry") + (`@( ... )`) + - Regarding literals: They are not prefixed with the escape + character `@`. The literals can be + [typed](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentyped_literal_glosry.htm "Glossary Entry") + (using the type name and content within a pair of backquotes: + char\`abc\`) with [built-in ABAP Dictionary + types](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenddic_builtin_types.htm) + or untyped. Typed literals are preferable for the following + reasons: Using untyped literals means extra cost in terms of + performance since they must be converted by the compiler. Plus, + their use can result in errors at runtime whereas typed literals + guarantee type compatibility at once. + - Regarding host expressions: Structures and internal tables are + possible as host expressions for statements modifying the + content of database tables as shown further down. +- See more information + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_operands.htm). + +Example demonstrating possible operands: +``` abap +DATA upto TYPE i VALUE 3. + +SELECT FROM zdemo_abap_flsch +  FIELDS +  "Specifies a column of a data source directly using its name +  cityfrom, + +  "Column selector ~ can be used to prefix every specified column. +  "Here, it is optional. It is non-optional, e. g., if multiple data +  "sources in an ABAP SQL statement are edited and the column name +  "is not unique. ] +   zdemo_abap_flsch~cityto, + +  'Lufthansa' AS name, "Untyped literal + +  char`X` AS flag, "Typed literal + +  @upto as num, "Host variable + +  @( cl_abap_context_info=>get_system_date( ) ) as date "Host expression + +  WHERE carrid = 'LH'         "Untyped literal +    AND countryfr = char`DE` "Typed literal + +  "Data object created inline and escaped with @ +  INTO TABLE @DATA(it) + +  "The following clause shows all options having the same effect +  UP TO 3 ROWS.         "Untyped numeric literal +  "UP TO int4`3` ROWS.   "Typed numerice literal +  "UP TO @upto ROWS.     "Host variable +  "UP TO @( 10 - 7 ) ROWS. "Host expression +``` + +**SQL Expressions** + +- Expressions in an ABAP SQL statement that are passed to the database + system for evaluation. +- For example, SQL expressions can be specified as columns in the + `SELECT` as demonstrated in most of the following examples. + Find information on more possible positions and general information + on SQL expressions + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapsql_expr.htm) + and the subtopics there. + +**Elementary expressions** + +- An elementary expression represents one of the four mentioned + operands above: A value from the database (the column name) or + values from an ABAP program passed to the database (literal, host + variable or host expression). +- As an example, see the `SELECT` list in the example above. +- See more information + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_elem.htm). + +**SQL functions** + +- You can use built-in functions in ABAP SQL. +- Result: Value with the associated dictionary type. +- Arguments of the functions: Cover one or more SQL expressions. +- See more information + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_sql_builtin_functions.htm). + +Example: [Numeric functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_arith_func.htm) +``` abap +SELECT SINGLE +   carrname, + +   "Division, result rounded to an integer +   "Result: 2 +   div( 4, 2 ) AS div, + +   "Division, 3rd argument: result is rounded to the specified +   "number of decimals +   "Result: 0.33 +   division( 1, 3, 2 ) AS division, + +   "Result is rounded to first greater integer +   "Result: 2 +   ceil( decfloat34`1.333` ) AS ceil, + +   "Result is the remainder of division +   "Result: 1 +   mod( 3, 2 ) AS mod, + +   "Result: Largest integer value not greater than the specified value +   "Result: 1 +   floor( decfloat34`1.333` ) AS floor, + +   "Returns the absolute number +   "Result: 2 +   abs( int4`-2` ) AS abs, + +   "Result is rounded to the specified position after the decimal separator +   "Result: 1.34 +   round( decfloat34`1.337`, 2 ) AS round + + FROM zdemo_abap_carr +   WHERE carrid = 'AA' +   INTO @DATA(numeric_functions). +``` + +Example: [String functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_string_func.htm) + +``` abap +SELECT SINGLE + carrid,   "LH + carrname, "Lufthansa + url, "http://www.lufthansa.com + + "Concatenates strings, ignores trailing blanks + "Result: LHLufthansa + concat( carrid, carrname ) AS concat, + + "Concatenates strings, number denotes the blanks that are inserted + "Result: LH Lufthansa + concat_with_space( carrid, carrname, 1 ) AS concat_with_space, + + "First letter of a word -> uppercase, all other letters -> lowercase; + "note that a space and other special characters means a new word. + "Result: Http://Www.Lufthansa.Com + initcap( url ) AS initcap, + + "Position of the first occurrence of the substring specified + "Result: 6 + instr( carrname,'a' ) AS instr, + + "String of length n starting from the left of an expression; + "trailing blanks are ignored + "Result: Luft + left( carrname, 4 ) AS left, + + "Number of characters in an expression, trailing blanks are ignored + "Result: 24 + length( url ) AS length, + + "Checks if expression contains a PCRE expression; + "case-sensitive by default (case_sensitive parameter can be specified) + "Notes on the result: 1 = found, 0 = not found + "Result: 1 + like_regexpr( pcre  = '..', "Period that is followed by any character +                value = url ) AS like_regex, + + "Returns position of a substring in an expression, + "3rd parameter = specifies offset (optional) + "4th parameter = determines the number of occurrences (optional) + "Result: 9 + locate( carrname, 'a', 0, 2 ) AS locate, + + "Searches a PCRE pattern, returns offset of match; + "many optional parameters: occurrence, case_sensitive, start, group + "Result: 21 + locate_regexpr( pcre = '..', "Period followed by any character +                  value = url, +                  occurrence = 2 ) "2nd occurrence in the string +                 AS locate_regexpr, + + "Searches a PCRE pattern, returns offset of match + 1; + "many optional parameters: occurrence, case_sensitive, start, group + "Result: 2 + locate_regexpr_after( pcre = '.',  "Any character +                        value = url, +                        occurrence = 1 ) AS locate_regexpr_after, + + "Removes leading characters as specified in the 2nd argument, + "trailing blanks are removed + "Result: ufthansa + ltrim( carrname, 'L' ) AS ltrim, + + "Counts all occurrences of found PCRE patterns + "Result: 2 + occurrences_regexpr( pcre = '..', +                       value = url ) AS occ_regex, + + "Replaces the 2nd argument with the 3rd in an expression + "Result: Lufth#ns# + replace( carrname, 'a', '#' ) AS replace, + + "Replaces a found PCRE expression; + "more parameters possible: occurrence, case_sensitive, start + "Result: http://www#ufthansa#om + replace_regexpr( pcre = '..', +                   value = url, +                   with = '#' ) AS replace_regex, + + "Extracts a string with the length specified starting from the right + "Result: hansa + right( carrname, 5 ) AS right, + + "Expands string to length n (2nd argument); trailing blanks produced + "are replaced by the characters from the (3rd) argument + "Note that if n is less than the string, the expression is truncated + "on the right. + "Result: Lufthansa### + rpad( carrname, 12, '#' ) AS rpad, + + "All trailing characters that match the character of the 2nd argument + "are removed; trailing blanks are removed, too + "Result: Lufthans + rtrim( carrname, 'a' ) AS rtrim, + + "Returns a substring; 2nd argument = position from where to start; + "3rd argument: length of the extracted substring + "Result: fth + substring( carrname, 3, 3 ) AS substring, + + "Searches for a PCRE expression and returns the matched substring + "More parameters possible: occurrence, case_sensitive, start, group + "Result:.lu + substring_regexpr( pcre = '...', +                     value = url ) AS substring_regexpr, + + "All lower case letters are transformed to upper case letters + "Result: LUFTHANSA + upper( carrname ) AS upper + + FROM zdemo_abap_carr + WHERE carrid = 'LH' + INTO @FINAL(string_functions). +``` + +Example: [Special functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_sql_special_functions.htm) + +``` abap +SELECT SINGLE +  carrid, + +  "Conversion functions +  "When used: Special conversions that cannot be handled in a general +  "CAST expression + +  "Type conversion: string of fixed length (e.g. of type c) to variable +  "length string of type string +  to_clob( carrid ) AS clob, + +  "Byte string -> character string +  bintohex( raw`3599421128650F4EE00008000978B976` ) AS bintohex, + +  "Character string -> byte string +  hextobin( char`3599421128650F4EE00008000978B976` ) AS hextobin, + +  "Byte field of type RAW to a byte string (BLOB) of type RAWSTRING +  to_blob( raw`3599421128650F4EE00008000978B976` ) AS blob, + +  "Unit and currency conversion functions +  "More parameters are available. + +  "Converts miles to kilometers +  unit_conversion( quantity = d34n`1`, +                   source_unit = unit`MI`, +                   target_unit = unit`KM` ) AS miles_to_km, + +  "Converts Euro to US dollars using today's rate +  currency_conversion( +    amount = d34n`1`, +    source_currency = char`EUR`, +    target_currency = char`USD`, +    exchange_rate_date = @( cl_abap_context_info=>get_system_date( ) ) +                     ) AS eur_to_usd, + +  "Date and time functions + add_days( @( cl_abap_context_info=>get_system_date( ) ), 4 ) AS add_days, + add_months( @( cl_abap_context_info=>get_system_date( ) ), 2 ) AS add_months, + is_valid( @( cl_abap_context_info=>get_system_date( ) ) ) AS date_is_valid, + is_valid( @( cl_abap_context_info=>get_system_time( ) ) ) AS time_is_valid + +FROM zdemo_abap_carr +INTO @FINAL(special_functions). +``` + +[Aggregate expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_aggregate.htm) + +- Consist of [aggregate + functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenaggregate_function_glosry.htm "Glossary Entry") + and aggregate the values of multiple rows of the result set of a + [query](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenquery_glosry.htm "Glossary Entry") + into a single value +- See more information + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_aggregate.htm). + +Example: +``` abap +"The example shows a selection of available functions +SELECT + carrid, + + "Average value of the content of a column in a row set + AVG( fltime ) AS fltime1, + + "AVG with data type specification for the result + AVG( fltime AS DEC( 14,4 ) ) AS fltime2, + + "Maximum value of the results in a row set + MAX( fltime ) AS max, + + "Minimum value + MIN( fltime ) AS min, + + "Sum of the results in a row set. + SUM( fltime ) AS sum, + + "Returns the number of rows in a row set. + "The following two have the same meaning. + COUNT( * ) AS count2, + COUNT(*) AS count3, + + "Chains the results in a row set. + "An optional separator can be specified + STRING_AGG( airpfrom, ', ' ) AS string_agg + + FROM zdemo_abap_flsch + WHERE carrid = 'LH' + GROUP BY carrid + INTO TABLE @FINAL(agg_exp). +``` + +**More SQL Expressions** + +- [Arithmetic + expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_arith.htm) + to perform arithmetic calculations using the operators `+`, + `-`, [`*]`, `/`, +- [Cast + expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_cast.htm) + to convert the value of operands to a dedicated dictionary type. + Note that there are special [conversion + rules](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_cast_rules.htm). +- [String + expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_string.htm) + using the operator `&&` to concatenate character strings. +- [Case + distinctions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_case.htm) + to carry out either a simple (comparison of the values of a + dedicated operand) or complex (searched case; evaluation of multiple + logical expressions) case distinction. + +The following example demonstrates the expressions mentioned above: +``` abap +SELECT SINGLE +  carrid, + +  "Arithmethic expressions +  "operators + - * +  "Note that / is not allowed in integer expressions as the one below +  ( 1 + 2 ) * 3 AS calc, + +  "/ used in an expression using type adjustment in ABAP SQL. +  "A cast expression converts the value of the operands to the +  "specified dictionary type. The result is a representation of the +  "source value in the specified type. +  CAST( 1 AS D34N ) / CAST( 2 AS D34N ) AS ratio, + +  "String expression using && to concatenate two character strings; +  "the result of the concatenation must not be longer than +  "255 characters. +  carrid && carrname AS concat, + +  "Case distinction +  "Simple case distinction +  "The expression compares the values of an operand with other +  "operands. Result: The first operand after THEN for which the +  "comparison is true. If no matches are found, the result specified +  "after ELSE is selected. +  CASE currcode +       WHEN 'EUR' THEN 'A' +       WHEN 'USD' THEN 'B' +       ELSE 'C' +  END AS case_simple, + +  "Complex case distinction +  "The expression evaluates logical expressions. Result: The first +  "operand after THEN for which the logical expression is true. If no +  "logical expressions are true, the result specified after ELSE is +  "selected. +  CASE WHEN length( carrname ) <= 5 THEN 'small' +       WHEN length( carrname ) BETWEEN 6 AND 10 THEN 'mid' +       WHEN length( carrname ) BETWEEN 11 AND 15 THEN 'large' +       ELSE 'huge' +  END AS case_complex + +FROM zdemo_abap_carr +WHERE carrid = 'AA' +INTO @DATA(more_sql_expr). +``` + +[Window expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwindow_expression_glosry.htm "Glossary Entry") + +How they work: + +- Defines a subset of the result set (i. e. the + "[window](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwindow_glosry.htm "Glossary Entry")") + of a database query that implements ABAP SQL +- Applies a [window + function](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwindow_function_glosry.htm "Glossary Entry") - + which evaluates the rows of the window and which can, for example, + be an [aggregate + function](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenaggregate_function_glosry.htm "Glossary Entry") + like + [`AVG`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_agg_func&sap-language=EN&sap-client=000&version=X&anchor=!ABAP_VARIANT_1@1@&tree=X) + to determine the average value - to the result set +- I. e. a window is constructed by the rows of the result set for + which all the window functions have the same result; a value is then + determined for the rows of a window + +Setup of a statement with window expressions: + +- Window function, e. g. an aggregate function like `AVG`, + followed by `OVER( ... )` (the content in the parentheses + defines the "window") +- The content in the parentheses can contain the following additions: + - Optional `PARTITION BY`: Defines the windows using a + comma-separated list of [SQL + expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapsql_expr.htm); + the window function is calculated for the rows of this window; + note that if the addition is not specified, the window comprises + all rows of the result set + - Optional `ORDER BY`: Introduces both an order (you can + use `ASCENDING` and `DESCENDING`) and a frame + (as outlined below) within the current window, which further + restricts the rows for which the window function is calculated + - A window frame, which stands for a subset of rows inside a + window, can optionally be defined if `ORDER BY` is + specified; there are 3 options to define the starting and ending + frame boundaries (see the example) + +See more information on window expressions and the syntax +[here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_over.htm). + +Examples: +``` abap +"Example 1: A simple window is constructed in the OVER clause; +"window functions - here aggregate functions - are applied +SELECT carrid, currency, +    SUM( paymentsum ) OVER( PARTITION BY carrid ) AS sum, +    AVG( price AS DEC( 14,2 ) ) OVER( PARTITION BY carrid ) AS avg, +    MAX( price ) OVER( PARTITION BY carrid ) AS max +    FROM zdemo_abap_fli +    ORDER BY carrid +    INTO TABLE @DATA(win). + +"Example 2: +SELECT carrid, currency, fldate, +  "Sorts the rows by some columns and counts the number of rows from +  "the first row of the window to the current row. +  COUNT( * ) OVER( ORDER BY currency, fldate +                    ROWS BETWEEN +                     "UNBOUNDED PRECEDING: frame starts at the +                     "first row of the window +                    UNBOUNDED PRECEDING +                     "CURRENT ROW: determines starting or ending +                     "at the current row; here, it ends +                    AND CURRENT ROW ) AS count1, + +  "If no window frame is used, the default window frame is +  "BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, +  "i. e. the result of count1 equals the result of count2. +  COUNT( * ) OVER( ORDER BY currency, fldate ) AS count2, + +  "Sorts the rows by some columns and counts the number of rows from +  "the current row to the last row of the window. +  "The result is reverse numbering. +  COUNT( * ) OVER( ORDER BY currency, fldate +                   ROWS BETWEEN CURRENT ROW +                    "UNBOUND FOLLOWING: +                    "Determines the ending frame boundary, +                    "this addition specifies the last row of the window +                   AND UNBOUNDED FOLLOWING ) AS count_reverse, + +  "Sorts the rows by some columns and calculates the rolling averages +  "of a subset of rows from column price. The subset consists of the +  "current row plus one preceding and one following row. Another use +  "case as below example that uses prices would be that, for example, +  "you can calculate the 3-day-average temperature for every day from +  "a list of temperature data. +  AVG( price AS DEC( 14,2 ) ) OVER( ORDER BY currency, fldate +       ROWS BETWEEN +        "n PRECEDING: for both start and end of frame; +        "frame to start/end n rows above the current row +       1 PRECEDING +        "n FOLLOWING: for both start and end of frame; +        "frame to start/end n rows beneath the current row +       AND 1 FOLLOWING ) AS avg + +  FROM zdemo_abap_fli +  INTO TABLE @DATA(result). +``` + +### Excursion: SQL Conditions + +You can formulate conditions in ABAP SQL statements, i. e. [logical +expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogical_expression_glosry.htm "Glossary Entry"), +especially in the `WHERE` clause to restrict the result. Note +that without a `WHERE` clause, all rows are respected for the +operation. + +See below a selection of the operators that are possible when specifying +conditions. For more information, see the subtopics of the [SQL +Conditions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenasql_cond.htm) +topic. + + +| Operator | Meaning | +|----------|:-------------:| + | `=`, `EQ` | The content of two operands is equal.| +| `<>`, `NE` | The content of two operands is not equal.| + | `<`, `LT` | The content of one operand is less than the content of the other operand.| + | `>`, `GT` | The content of one operand is greater than the content of the other operand.| + | `<=`, `LE` | The content of one operand is less than or equal to the content of the other operand.| + | `>=`, `GE` | The content of one operand is greater than or equal to the content of the other operand.| + | `... [NOT] BETWEEN ... AND ...` | The value of an operand is (not) between the value of the two other operands.| + | `... [NOT] LIKE ...` | The content of an operand matches (does not match) a specified pattern. The pattern can be specified by using wildcard characters. `%` stands for any character string, including an empty string.◾`_` stands for any character.| + | `... IS [NOT] INITIAL ...` | The value of an operand is (not) the initial value of its built-in dictionary type.| + | `... EXISTS ...` | Checks the result set of a [subquery](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensubquery_glosry.htm "Glossary Entry"). The expression is true if the result set contains at least one row. See more information [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwhere_logexp_subquery.htm).| + | `... [NOT] IN ( ... )` | Checks whether the operands on the left side match a value from a set of values specified in parentheses. On the left side, a single operand or an operand list are possible. On the right side, a comma-separated lists or subqueries can be specified.| + +> **💡 Note**
+>You can combine multiple logical expressions into one +logical expression using `AND` or `OR`. To further +detail out the desired condition, expressions within parentheses are +possible. + +The clause parts that are commented out in the following code snippet +just demonstrate how the `WHERE` clause might look like. + +``` abap +SELECT FROM dbtab +  FIELDS comp1, comp2, comp3 +  WHERE comp1 = 'abc' "Equals some value + +        "More example WHERE conditions: +        "comp2 > 100 "Greater than some value; alternatively GT is possible + +        "Not equals plus an additional condition that must be respected +        "comp2 <> 100 AND comp4 = 'xyz' + +        "(Not) between a value range +        "comp1 BETWEEN 1 AND 10 + +        "A character literal has a certain pattern, preceded and +        "followed by any string. +        "comp1 LIKE '%XYZ%' + +        "The second character is not Y. _ stands for any character. +        "comp1 NOT LIKE '_Y%' + +        "Contains one of the values specified in the parentheses +        "comp1 IN ( 'ABC', 'DEF', 'GHI' ) + +        "Does not contain one of the values specified in the parentheses +        "comp1 NOT IN ( 'JKL', 'MNO' ) + +         "Checking if an operand has an initial value +         "comp1 IS INITIAL + +        "Combination of logical expression using AND, OR and parentheses +        "( comp1 = a AND comp2 < b ) OR ( comp3> c AND comp4 <> d ) + +  INTO TABLE @DATA(itab_where). +``` + + +### Using SELECT when Reading from Multiple Tables + +[`FOR ALL ENTRIES`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwhere_all_entries.htm) +addition: Reading data from a database table depending on the +content of an internal table. +``` abap +"Checking that table is not initial +IF ( 0 < lines( itab2 ) ). + +  SELECT comp1, comp2, comp3 +    FROM dbtab +    FOR ALL ENTRIES IN @itab2   "Host variable before internal table +    WHERE comp1 = @itab2-comp1 ... "Relational expression on the right side of a comparison +    INTO TABLE @itab1 + +ENDIF. +``` + + +> **💡 Note**
+>- The entire logical expression after `WHERE` is evaluated for each individual line in the internal table. +>- There must be at least one comparison with a column of the internal +table in the `WHERE` clause. +>- Ensure that the internal table from which to read is not initial. It is recommended that you use a subquery, which is shown in the next example, and a `SELECT` statement that reads from the internal table (`... ( SELECT ... FROM itab2 WHERE ...`). + +**Using a subquery** with the addition `EXISTS` to read data from a database table depending +on data of another database table. More information: +[`EXISTS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwhere_logexp_exists.htm). The components of the table are referenced by `~`. +``` abap +SELECT comp1, comp2, comp3 +  FROM dbtab AS tab1 +  WHERE EXISTS +   ( SELECT comp1 FROM dbtab2 +     WHERE comp1 = tab1~comp1 AND comp2 = tab1~comp2 ) +  INTO ... +``` + +**Combining data of multiple database tables ...** + +**... using an inner join**. In this kind of join, columns with rows of the left-hand side and those of the right-hand side are only joined if the rows meet join conditions (`ON ...`). If there are no equivalent entries in the first or second table, the rows are not joined. + +If the same column name appears in multiple data sources of a single +join expression, these sources must be identified in all other additions +of the `SELECT` statement using the [column +selector](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentable_comp_selector_glosry.htm "Glossary Entry") +`~`. +``` abap +SELECT a~comp1, a~comp2, b~comp3, c~comp4 +  FROM dbtab1 AS a +  INNER JOIN dbtab2 AS b +   ON a~comp1 = b~comp1 AND a~comp2 = b~comp2 +  INNER JOIN dbtab3 AS c +   ON a~comp1 = c~comp1 +  WHERE ... +  INTO ... +``` + +**... using a left outer join**. The columns of each row on the right-hand side that do not meet the `ON` condition are filled with initial values and linked with the columns of the left-hand side. If the conditions of the `WHERE` clause are met, each row on the left-hand side of the left outer join produces at least one row in the selection, irrespective of the `ON` condition. +``` abap +SELECT a~comp1, a~comp2, b~comp3, +  FROM dbtab1 AS a +  LEFT OUTER JOIN dbtab2 AS b +   ON a~comp1 = b~comp1 +  WHERE ... +  INTO ... +``` + +**... using a union**. The columns of the result set keep the names defined in the statement on the left of `UNION`. The result set of rows of the `SELECT` statement on the right of `UNION` are inserted into the results set of the `SELECT` statement on the left of `UNION`. +``` abap +SELECT FROM dbtab1 +  FIELDS ... +  WHERE ... +UNION +  SELECT FROM dbtab2 +   FIELDS ... +   WHERE ... +  INTO ... +``` +> **💡 Note**
+> There are more join variants available. See the ABAP +Keyword Documentation on +[joins](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_join.htm) +for more information. + + +#### Excursion: Using Common Table Expressions (CTE) + +When used: + +- Whenever you need intermediate results in a `SELECT` + statement and especially if you need them more than once. +- You get the option of selecting directly from a subquery [SELECT + FROM subquery], which is not possible in ABAP SQL. + +How it works: + +- The ABAP SQL keyword + [`WITH`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapwith.htm) + introduces the definition of CTEs. +- Each CTE creates a tabular result set in a + [subquery](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensubquery_glosry.htm "Glossary Entry"). +- The result set of such a CTE can then be used in subsequent queries + as data source; CTEs can be considered as temporary views, which + only exist for the duration of the database access. +- The CTEs (at least one) are then used in a final [main + query](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenmainquery_glosry.htm "Glossary Entry"), i. + e. a `SELECT` statement accesses the result of the + expressions. + +Setup of a statement with CTEs: + +- Introductory keyword `WITH` +- A comma-separated list with at least one definition of a CTE + - Each CTE has a unique name with an initial `+` character + - An optional list of column names, which should be used in the + result set, within parentheses + - `AS` followed by a subquery with `SELECT` which + creates the tabular result set of the CTE +- A closing main query with `SELECT` in which the previous + CTEs are to be used as data source +- If a `SELECT` loop is opened and data is written into a work + area in the closing main query, the loop must be closed with + `ENDWITH.` (which fulfills the same task as + `ENDSELECT.`). + +> **💡 Note**
+>- Each CTE must be used at least once, either in another CTE or in the + main query. The main query must access at least one CTE. +>- The result set of a CTE never has a [client + column](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenclient_column_glosry.htm "Glossary Entry"). +>- See more information in [this topic](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapwith.htm) +and further options and additions when using CTEs in the subtopics. + +Example: The result sets of both common table expressions +`+connections` and `+sum_seats` are merged in the +subquery of the CTE `+result` in a join expression. An explicit +name list assigns names to the resulting columns. These names are used +in the main query to sort the results. For each flight connection of the +selected airline, the total number of occupied seats is stored in the +internal table. +``` abap +WITH ++connections AS ( + SELECT zdemo_abap_flsch~carrid, carrname, connid, cityfrom, cityto + FROM zdemo_abap_flsch + INNER JOIN zdemo_abap_carr + ON zdemo_abap_carr~carrid = zdemo_abap_flsch~carrid + WHERE zdemo_abap_flsch~carrid BETWEEN 'AA' AND 'JL' ), ++sum_seats AS ( + SELECT carrid, connid, SUM( seatsocc ) AS sum_seats + FROM zdemo_abap_fli + WHERE carrid BETWEEN 'AA' AND 'JL' + GROUP BY carrid, connid ), ++result( name, connection, departure, arrival, occupied ) AS ( + SELECT carrname, c~connid, cityfrom, cityto, sum_seats + FROM +connections AS c + INNER JOIN +sum_seats AS s + ON c~carrid = s~carrid AND + c~connid = s~connid ) +SELECT * + FROM +result + ORDER BY name, connection + INTO TABLE @DATA(result). +``` + + +

(back to top)

+ +## Changing Data in Database Tables + +### Using INSERT + +Using the ABAP SQL statement +[`INSERT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapinsert_dbtab.htm), +you can insert one or more rows into a database table (or a [DDIC table +view](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abentable_view_glosry.htm "Glossary Entry")). +As mentioned above, structures and internal tables from which to insert +content should be specified as host variables (with `@`) or host +expressions (with `@( ... )`) depending on your ABAP release +and strict syntax enforcement. The examples below all use the preceding +`@`. The system fields `sy-subrc` (0 = single row or all +rows inserted successfully, 4 = row not or not all rows inserted) and +`sy-dbcnt` (number of rows that are inserted) are set. + +``` abap +"Inserting a single row into a database table + +INSERT dbtab FROM @row. +INSERT INTO dbtab VALUES @row. "Alternative syntax, same effect + +"Line is created inline using the VALUE operator as part of a host expression + +INSERT dbtab FROM @( VALUE #( comp1 = ... comp2 = ... ) ). + +"Inserting multiple lines from an internal table into a database table. +"Make sure that the internal table does not contain a line having the same key +"as an existing row in the database table. Otherwise, a runtime error occurs. + +INSERT dbtab FROM TABLE @itab. + +"Inserting lines from a table declared inline using the VALUE operator +"as part of a host expression + +INSERT dbtab FROM TABLE @( VALUE #( ( comp1 = ... comp2 = ... ) +                                    ( comp1 = ... comp2 = ... ) ) ). + +"ACCEPTING DUPLICATE KEYS addition: To avoid the runtime error mentioned above, +"all lines that would produce duplicate entries in the database table +"regarding the keys are discarded and sy-subrc is set to 4. + +INSERT dbtab FROM TABLE @itab ACCEPTING DUPLICATE KEYS. + +"Inserting the result set of an embedded subquery +"Here, multiple result sets can be joined, e. g. using UNION. + +INSERT dbtab FROM ( SELECT ... ). +``` + +

(back to top)

+ +### Using UPDATE + +Using the ABAP SQL statement +[`UPDATE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapupdate.htm), +you can update one or more rows in a database table. Similar to +`INSERT`, `sy-subrc` and `sy-dbcnt` are set. +After `FROM`, you can specify a structure or an internal table +as host variable (with `@`) or a host expression (with `@( ... )`). + + +``` abap +"Changing content by overwriting entire rows based on a work area + +UPDATE dbtab FROM @row. +UPDATE dbtab FROM @( VALUE #( comp1 = ... comp2 = ... ) ). "Using a host expression + +"Changing content by overwriting entire rows based on rows in an internal table + +UPDATE dbtab FROM TABLE @itab. + +"Using a host expression + +UPDATE dbtab FROM TABLE @( VALUE #( ( comp1 = ... comp2 = ... ) +                                    ( comp1 = ... comp2 = ... ) ) ). + +"INDICATORS addition: Changing content of specific fields without overwriting +"existing values of other fields +"Example: +"- Structured type is created with WITH INDICATORS addition +"- Internal table from which to update dbtab is created; +" it includes the indicator structure comp_ind +"- Internal table is filled; only one component is flagged as to be updated +"- Other fields remain unchanged; note that key fields must be included +" in ind_tab (indicator setting for key fields has no effect) + +TYPES ind_wa TYPE dbtab WITH INDICATORS comp_ind TYPE abap_bool. + +DATA ind_tab TYPE TABLE OF ind_wa. + +ind_tab = VALUE #( +       ( comp1 = ... comp2 = ... comp_ind-comp2 = abap_true ) +       ( comp1 = ... comp2 = ... comp_ind-comp2 = abap_true ) ). + +UPDATE dbtab FROM TABLE @ind_tab +             INDICATORS SET STRUCTURE comp_ind. + +"Reverses the logic + +UPDATE dbtab FROM TABLE @ind_tab +             INDICATORS NOT SET STRUCTURE comp_ind. + +"SET addition: Changing values of specific fields in all table rows +"There are mutliple options for the value assignment. E. g. you can use +"a literal, host variable/expression, SQL function, and so on. + +UPDATE dbtab SET comp2 = ... . +``` + +

(back to top)

+ +### Using MODIFY + +Using the ABAP SQL statement +[`MODIFY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_dbtab.htm), +you can insert one or more rows in a database table or overwrite +existing ones. Similar to the statements above, `sy-subrc` and +`sy-dbcnt` are set. After `FROM`, you can specify a +structure or an internal table as host variable (with `@`) or a +host expression (with `@( ... )`). + +``` abap +"Inserting a single row into a database table or changing an existing row + +MODIFY dbtab FROM @row. + +"Using a host expression + +MODIFY dbtab FROM @( VALUE #( comp1 = ... comp2 = ... ) ). + +"Inserting/Changing multiple rows + +MODIFY dbtab FROM TABLE @itab. + +"Using a host expression + +MODIFY dbtab FROM TABLE @( VALUE #( ( comp1 = ... comp2 = ... ) +                                    ( comp1 = ... comp2 = ... ) ) ). + +"Inserting/Changing multiple rows based on a result set of an embedded subquery + +MODIFY dbtab FROM ( SELECT ... ). +``` + +

(back to top)

+ +### Using DELETE + +Using the ABAP SQL statement +[`DELETE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapdelete_dbtab.htm), +you can delete one or more rows in a database table. Similar to the +statements above, `sy-subrc` and `sy-dbcnt` are set. + +``` abap +"Variant DELETE FROM ...: Either all rows are deleted or restricted + +"All rows are deleted + +DELETE FROM dbtab. + +"Rows are deleted based on a condition + +DELETE FROM dbtab WHERE .... + +"Note that there are further options available, e. g. ORDER BY, UP TO +"Variant DELETE ... FROM ...: Deleting a single row or multiple row + +DELETE dbtab FROM @row. + +"Using a host expression + +DELETE dbtab FROM @( VALUE #( comp1 = ... ) ). + +DELETE dbtab FROM TABLE @itab. + +"Using a host expression + +DELETE dbtab FROM TABLE @( VALUE #( ( comp1 = ... ) +                                    ( comp1 = ... ) ) ). +``` + +

(back to top)

+ +## Further Information +Find more information on the topics covered here or not (e. g. topics like [table buffering](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensap_puffering.htm)) in the respective sections in the ABAP Keyword Documentation. + + +## Executable Example +[zcl_demo_abap_sql](./src/zcl_demo_abap_sql.clas.abap) + +Note the steps outlined [here](README.md#🎬-getting-started-with-the-examples) about how to import and run the code. diff --git a/04_ABAP_Object_Orientation.md b/04_ABAP_Object_Orientation.md new file mode 100644 index 0000000..d5cb13a --- /dev/null +++ b/04_ABAP_Object_Orientation.md @@ -0,0 +1,1213 @@ + + +# ABAP Object Orientation + +> **💡 Note**
+> This cheat sheet provides an overview on selected syntax options and concepts related to ABAP object orientation. It is supported by code snippets and an executable example. They are **not** suitable as role models for object-oriented design. Their primary focus is on the syntax and functionality. For more details, refer to the respective topics in the ABAP Keyword Documentation. Find an overview in the topic [ABAP Objects - Overview](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenabap_objects_oview.htm). + +- [ABAP Object Orientation](#abap-object-orientation) + - [Classes and Objects](#classes-and-objects) + - [Creating Classes](#creating-classes) + - [Creating a Local Class](#creating-a-local-class) + - [Creating a Global Class](#creating-a-global-class) + - [Visibility of Components](#visibility-of-components) + - [Creating the Visibility Sections](#creating-the-visibility-sections) + - [Defining Components](#defining-components) + - [Working with Objects and Components](#working-with-objects-and-components) + - [Notes on Inheritance](#notes-on-inheritance) + - [Notes on Polymorphism and Casting](#notes-on-polymorphism-and-casting) + - [Working with Interfaces](#working-with-interfaces) + - [Further Concepts](#further-concepts) + - [Factory Methods](#factory-methods) + - [Friendship](#friendship) + - [Events](#events) + - [Executable Example](#executable-example) + +## Classes and Objects + +Object-oriented programming in ABAP means dealing with +[classes](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenclass_glosry.htm "Glossary Entry") +and +[objects](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenobject_glosry.htm "Glossary Entry"). + +Objects ... + +- are + [instances](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninstance_glosry.htm "Glossary Entry") + of a + [type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentype_glosry.htm "Glossary Entry"). + In this context, they are instances of a + [class](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenclass_glosry.htm "Glossary Entry"). + The terms object and instance are used synonymously. +- exist in the [internal + session](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninternal_session_glosry.htm "Glossary Entry") + of an [ABAP + program](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_program_glosry.htm "Glossary Entry") + + +Classes ... +- are templates for objects, i. e. they determine the appearance of + all instances of a class. All instances are created based on this + template (this is what is called + [instantiation](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninstantiation_glosry.htm "Glossary Entry")). + - If, for example, a vehicle represents a class, then the + instances of the class `vehicle` have the same setup. + That means they all share the same kind of data like the brand, + model and color or the same functionality like the acceleration. + However, the values are different from instance to instance. + Hence, an instance has a unique identity. For example, one + instance is a red sedan of brand A having a certain + acceleration; another instance is a black SUV of brand B and so + on. You create an object (or instance respectively) that stands + for an actual vehicle to work with in your ABAP program. + - Basically, you might create any number of objects that are based + on a class. +- contain + [components](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomponent_glosry.htm "Glossary Entry"): + - [Attributes](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenattribute_glosry.htm "Glossary Entry") + of the objects (the data declarations) + - [Methods](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenmethod_glosry.htm "Glossary Entry") + that typically operate on this data + - [Events](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenevent_glosry.htm "Glossary Entry") + +

(back to top)

+ +### Creating Classes + +You can either create local or global classes: + + + + + + + + + + +
Local classes
  • can be defined within an ABAP program (or a CCIMP include respectively)
  • can only be used in the program (or global class/CCIMP include respectively) in which the class is defined
Global +classes
  • are defined as global type; hence, they can be used by all ABAP programs or global classes respectively since they are globally visible
  • are declared in class pools
+ +> **💡 Note**
+> - Regarding the names of global and local classes and usage of classes in ABAP programs when, for example, calling methods, the system searches for a local class with the specific name at first. Then, if a local class with that name is not found, the systems searches for a global class. +> - The class design must be done with care. If a class is only used in one program (or class), choosing a local class is enough. However, global +classes must be prepared to be used anywhere. A later change of that class, especially regarding the visibility of components (see +further down) or the data types of attributes that are used in other programs might cause problems. +> - Apart from ADT, global classes can also be created in the ABAP Workbench (`SE80`) or with transaction `SE24` in on-premise systems. + +The basic structure of classes consists of a declaration and an implementation part that are both introduced by `CLASS` and ended by `ENDCLASS`. + +#### Creating a Local Class + +``` abap +CLASS local_class DEFINITION. +     + "Here go the declaration for all components and visibility sections. +    "You should place the declaration at the beginning of the program. + +ENDCLASS. + +CLASS local_class IMPLEMENTATION. + +    "Here go the method implementations. +    "Only required if you declare methods in the DEFINITION part. + +ENDCLASS. +``` + +#### Creating a Global Class +The code snippet shows a basic skeleton of a global class. There are [further +additions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapclass_options.htm) +possible. + +``` abap +CLASS global_class DEFINITION +  PUBLIC   "Makes the class a global class in the class library. + FINAL     "Means that no subclasses can be derived from this class. +  CREATE PUBLIC. "This class can be instantiated anywhere it is visible. + +    ... + +    "Here go the declaration for all components and visibility sections. + +ENDCLASS. + +CLASS global_class IMPLEMENTATION. + +    "Here go the method implementations. +    "Only required if you declare methods in the DEFINITION part. + +ENDCLASS. +``` +> **💡 Note**
+> The addition `... CREATE PROTECTED.` of the class declaration part means that the class can only be instantiated in methods of its +[subclasses](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensubclass_glosry.htm "Glossary Entry"), +of the class itself, and of its +[friends](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfriend_glosry.htm "Glossary Entry"). +The addition `... CREATE PRIVATE` means that the class can only +be instantiated in methods of the class itself or of its friends. Hence, +it cannot be instantiated as an inherited component of subclasses. + +

(back to top)

+ +### Visibility of Components + +In the class declaration part, you must specify (at least one of the) +[visibility +sections](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenvisibility_section_glosry.htm "Glossary Entry") +to determine how to interact with the class. For example, you want to +hide and thus disallow the usage of certain data. The visibility +sections are as follows: + + + + + + + + + + + + + + +
PUBLIC.
Components declared in this section can be accessed from within the class and from outside including subclasses.
PROTECTED.
Components declared in this section can be + accessed from within the class and from subclasses and friends + - topics related to inheritance.
PRIVATE.
Components declared in this section can only be accessed from within the class in which they are declared and its friends.
+ +#### Creating the Visibility Sections +At least one section must be specified. +``` abap +CLASS local_class DEFINITION. +    PUBLIC. +      "Here go the components. +    PROTECTED. +      "Here go the components. +    PRIVATE. +      "Here go the components. +ENDCLASS. +``` + +

(back to top)

+ +### Defining Components + +All components - attributes (using `TYPES`, `DATA`, +`CLASS-DATA`, and `CONSTANTS` for data types and data +objects), methods (using `METHODS` and `CLASS-METHODS`), +events `EVENTS` and `CLASS-EVENTS` as well as interfaces - are declared in the declaration part of the class. There, they must be +assigned to a visibility section. + +Regarding, for example, `DATA` and `CLASS-DATA`, two +kind of components are to be distinguished: + +- [Instance + components](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninstance_component_glosry.htm "Glossary Entry"): + Components that exist separately for each instance and can only be + accessed in instances of a class. +- [Static + components](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstatic_component_glosry.htm "Glossary Entry"): + Components that are not specific for instances. They exist only once + per class and can be accessed using the name of the class. + +**Attributes** + +- The attributes of a class mean the data objects declared within a + class (or interface). +- [Static + attributes](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstatic_attribute_glosry.htm "Glossary Entry") + (`CLASS-DATA`): Their content is independent of instances of + a class and, thus, valid for all instances. As shown further down, + static attributes can be accessed by using the class name without a + prior creation of an instance. Note that changing an instance + attribute means the change is visible in all instances. +- [Instance + attributes](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninstance_attribute_glosry.htm "Glossary Entry") + (`DATA`): Determine the instance-dependent state. The data + is only valid in the context of an instance. As shown further down, + instance attributes can only be accessed via an [object reference + variable](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenobject_refer_variable_glosry.htm "Glossary Entry"). + +> **💡 Note**
+> You can declare static attributes that should not be +changed using +[`CONSTANTS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapconstants.htm) +statements. You specify the values for the constants when you declare +them. Furthermore, the addition +[`READ-ONLY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapdata_options&sap-language=EN&sap-client=000&version=X&anchor=!ABAP_ADDITION_2@2@&tree=X) +can be used in the public visibility section as a means of securing data +objects against undesired changes from outside. A change is only +possible via the methods of the class or it subclasses. + +Declaring attributes in visibility sections. In the snippet below, all attributes are declared in the `PUBLIC.` section of a local class. +``` abap +CLASS local_class DEFINITION. + +    PUBLIC. +      TYPES: some_type TYPE c LENGTH 3.             "Type declaration + +      DATA: inst_number TYPE i,                     "Instance attributes +            inst_string TYPE string, +            dobj_r_only TYPE c LENGTH 5 READ-ONLY. "Read-only attribute + +      CLASS-DATA: stat_number TYPE i,           "Static attributes +                  stat_char   TYPE c LENGTH 3. + +      CONSTANTS: const_num TYPE i VALUE 123.   "Non-changeable constant + +    PROTECTED. +      "Here go more attributes. + +    PRIVATE. +      "Here go more attributes. + +ENDCLASS. + +CLASS local_class IMPLEMENTATION. + +      "Here go all method implementations. + +ENDCLASS. +``` + +

(back to top)

+ +**Methods** + +- Are internal + [procedures](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenprocedure_glosry.htm "Glossary Entry") + determining the behavior of the class. +- Can access all of the attributes of a class and, if not defined + otherwise, change their content. +- Have a [parameter + interface](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenparameter_interface_glosry.htm "Glossary Entry") + (also known as + [signature](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensignature_glosry.htm "Glossary Entry")) + with which methods can get values when being called and pass values + back to the caller. +- [Static + methods](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstatic_method_glosry.htm "Glossary Entry") + can only access static attributes of a class and trigger static + events. You declare them using `CLASS-METHODS` statements in + a visibility section. +- [Instance + methods](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninstance_method_glosry.htm "Glossary Entry") + can access all of the attributes of a class and trigger all events. + You declare them using `CLASS` statements in a visibility + section. Since instance methods are bound to instances, an instance + of the class must first be created before using them. + +**Parameter Interface** + +In the simplest form, methods can have no parameter at all. Apart from that, methods can be defined with the following parameters: + +| Addition | Details | +|---|---| +|`IMPORTING`|Defines one or multiple parameters that are imported (or input) by the method (e. g. from another object or an ABAP program) with which the method can work. | +|`EXPORTING`|Defines one or multiple parameters that are exported (or output) by the method (e. g. to another object or an ABAP program). | +|`CHANGING`|Defines one or multiple parameters that can be both imported and exported. | +|`RETURNING`|Only one `RETURNING` parameter can be defined for methods. These methods are called functional methods. Like `EXPORTING` parameters, `RETURNING` parameters pass back values (note that the formal parameters of returning parameters must be passed by value), i. e. they are output parameters. The difference is that there can be multiple `EXPORTING` parameters in a method. However, `RETURNING` parameters simplify the syntax if there is only one value to be passed back. It shortens the method call and enables method chaining. Furthermore, functional methods can, for example, be used in arithmetic or logical expressions. In case of standalone method calls, the returned value can be accessed using the addition `RECEIVING`. | +|`RAISING` | Used to declare the class-based exceptions to handle errors. | + + +> **💡 Note**
+> - You may find the addition `EXCEPTIONS` especially in definitions of older classes. They are for non-class-based exceptions. The exceptions are based on the system field `sy-subrc` and raised according to setting this field. You can then check the value of `sy-subrc` and act accordingly. This addition should not be used in ABAP for Cloud Development. +> - [Formal + parameter](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenformal_parameter_glosry.htm "Glossary Entry") + versus [actual + parameter](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenactual_parameter_glosry.htm "Glossary Entry"): + You define method parameters by specifying a name with a type which + can be a + [generic](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abengeneric_data_type_glosry.htm "Glossary Entry") + or + [complete](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomplete_data_type_glosry.htm "Glossary Entry") + type. This formal parameter includes the specification of how the + value passing should happen. Parameters can be [passed by + reference](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenpass_by_reference_glosry.htm "Glossary Entry") + (`... REFERENCE(param) ...`; note that just specifying the + parameter name `... param ...` - as a shorter syntax - + means passing by reference by default) or [by + value](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenpass_by_value_glosry.htm "Glossary Entry") + (`... VALUE(param) ...`). The actual parameter represents + the data object whose content is passed to or copied from a formal + parameter as an argument when a procedure is called. If + pass-by-reference is used, a local data object is not created for + the actual parameter. Instead, the procedure is given a + [reference](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenreference_glosry.htm "Glossary Entry") + to the actual parameter during the call and works with the actual + parameter itself. Note that parameters that are input and passed by + reference cannot be modified in the procedure. However, the use of a + reference is beneficial regarding the performance compared to + creating a local data object. +>- Parameters can be defined as optional using the + [`OPTIONAL`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmethods_parameters&sap-language=EN&sap-client=000&version=X&anchor=!ABAP_ONE_ADD@1@&tree=X) + addition. In doing so, it is not mandatory to pass an actual + parameter. The + [`DEFAULT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmethods_parameters&sap-language=EN&sap-client=000&version=X&anchor=!ABAP_ONE_ADD@1@&tree=X) + addition also makes the passing of an actual parameter optional. + However, when using this addition, as the name implies, a default + value is set. + +

(back to top)

+ +**Constructors** + +- [Constructors](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_glosry.htm "Glossary Entry") + are special methods that are usually used for setting a defined + initial state of objects, e. g. for setting a particular starting + value for attributes in an object. +- A class can only have one [instance + constructor](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninstance_constructor_glosry.htm "Glossary Entry") + and one [static + constructor](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstatic_constructor_glosry.htm "Glossary Entry"). +- Constructors always exist implicitly in classes. However, their + declaration and use is optional. If they are declared explicitly, + they must consequently be implemented. Note that they are always + called automatically even if not declared and implemented. +- Static constructor: Automatically called when calling a class for + the first time in an internal session. This constructor is declared + using the predefined name `class_constructor` as part of a + `CLASS-METHODS` statement in the public visibility section. + Static constructors cannot have any parameters. +- Instance constructor: Automatically called when a class is + instantiated and an object is created. The constructor is declared + using the predefined name `constructor` as part of a + `METHODS` statement. In contrast to local classes, instance + constructors must be declared in the public visibility section of + global classes. They can only have `IMPORTING` parameters + and exceptions. In case of exceptions, make sure that you use a + [`TRY ... ENDTRY.`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaptry.htm) + block in the implementation since otherwise the instance is not + created if uncaught errors occur. + +Example for method definitions: The following snippet shows +multiple method definitions in the public section. Most of the formal +parameters of the demo methods below are defined by just using the +parameter name. This means passing by reference (returning parameters +require to be passed by value). +``` abap +CLASS local_class DEFINITION. +    PUBLIC. +      METHODS: inst_meth1 IMPORTING a TYPE i   "instance methods +                          EXPORTING b TYPE i, + +               inst_meth2 IMPORTING c TYPE string + +               inst_meth2 IMPORTING d TYPE string +                          RETURNING VALUE(e) TYPE string, + +               inst_meth3 IMPORTING f TYPE i +                          EXPORTING g TYPE i +                          CHANGING  h TYPE string_table +                          RETURNING VALUE(i) TYPE i +                          RAISING   cx_sy_zerodivide, + +             constructor IMPORTING j TYPE i. "instance constructor with importing parameter + +      CLASS-METHODS: stat_meth1 IMPORTING k TYPE i    "static methods +                                EXPORTING l TYPE i, +                     stat_meth2,         "no formal parameters +                     class_constructor,   "static constructor + +                     "Formal parameter definitions +                     stat_meth3 IMPORTING VALUE(m) TYPE i "pass by value +                                          REFERENCE(n) TYPE i "pass by reference +                                          o TYPE i, "same as n + +                     "OPTIONAL/DEFAULT additions +                     stat_meth4 IMPORTING p TYPE i DEFAULT 123 +                                       q TYPE i OPTIONAL. + +ENDCLASS. + +CLASS local_class IMPLEMENTATION. +   METHOD inst_meth1. +      ...            "Here goes the method implementation. +   ENDMETHOD. + + ...                  "Note that all declared methods must be implemented. +ENDCLASS. +``` + +

(back to top)

+ +## Working with Objects and Components + +**Declaring reference variables**: To create an object, a +[reference +variable](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenreference_variable_glosry.htm "Glossary Entry") +must be declared. Such an [object reference +variable](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenobject_refer_variable_glosry.htm "Glossary Entry") +is also necessary for accessing objects and their components, i. e. +objects are not directly accessed but only via references that point to +those objects. This reference is stored in the reference variables. +``` abap +DATA: ref1 TYPE REF TO local_class, +      ref2 TYPE REF TO global_class, +      ref3 LIKE ref1. +``` + +**Creating objects**: You create an object by using the instance +operator +[`NEW`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_new.htm). +In doing so, a new instance of a class is created and the reference to +it is assigned to an object reference variable. The `#` sign +means that the type (`TYPE REF TO ...`) can be derived from the +context (in this case from the type of the reference variable). You can +also omit the explicit declaration of a reference variable by declaring +a new reference variable +[inline](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_inline.htm), +for example, using `DATA`. In this case, the name of the class +must be placed after `NEW` and before the first parenthesis. The `NEW` operator replaces the older +[`CREATE OBJECT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcreate_object.htm) +statements. +``` abap +ref1 = NEW #( ). "Type derived from already declared ref1 + +DATA(ref2) = NEW local_class( ). "Reference variable declared inline, explicit type + "(class) specification + +"Old syntax. Do not use. +"CREATE OBJECT ref3. "Type derived from already declared ref3 +``` + +**Assigning or copying reference variables**: To assign or copy +reference variables, use the [assignment +operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenassignment_operator_glosry.htm "Glossary Entry") +`=`. In the example below, both reference variables have the same +type. + +``` abap +DATA: ref1 TYPE REF TO local_class, +      ref2 TYPE REF TO local_class. + +ref1 = NEW #( ). +ref2 = ref1. +``` + +**Overwriting reference variables**: An [object +reference](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenobject_reference_glosry.htm "Glossary Entry") +is overwritten when a new object is created with a reference variable +already pointing to an instance. +``` abap +ref1 = NEW #( ). +ref1 = NEW #( ). +``` + +**Keeping object references in internal tables**: If your use case is to retain the object references, for example, if you create a series of objects and you want to prevent object references to be overwritten when using the same reference variable, you can put the reference variables in internal tables. The following code shows that three objects are created with the same reference variable. The internal table includes all object references and, thus, their values are retained. +``` abap +DATA: ref TYPE REF TO local_class, +      itab TYPE TABLE OF REF TO local_class. + +DO 3 TIMES. +  ref = NEW #( ). +  itab = VALUE #( BASE itab ( ref ) ). "Adding the reference to itab +ENDDO. +``` +**Clearing object references**: Use +[`CLEAR`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapclear.htm) +statements to explicitly clear a reference variable. +``` +CLEAR ref. +``` +> **💡 Note**
+> Since objects use up space in the memory, they should be +cleared if they are no longer needed. 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 this task automatically, i. e. all objects without any +reference are cleared and the memory space is released. + +**Accessing attributes**: Instance attributes are accessed using +the [object component +selector](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenobject_component_select_glosry.htm "Glossary Entry") +`->` via a reference variable. Visible static attributes are +accessed using the [class component +selector](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenclass_component_select_glosry.htm "Glossary Entry") +`=>` via the class name. You can also declare data objects and +types by referring to the static attributes. +``` abap +"Accessing instance attribute via a reference variable + +... ref->some_attribute ... + +"Accessing static attributes via the class name + +... local_class=>static_attribute ... + +"Without the class name only within the class +... static_attribute ... + +"Type and data object declarations + +TYPES some_type LIKE local_class=>some_attribute. +DATA dobj1      TYPE local_class=>some_type. +DATA dobj2      LIKE local_class=>some_attribute. +``` + +**Calling methods**: Similar to accessing attributes, instance +methods are called using `->` via a reference variable. Static +methods are called using `=>` via the class name. When used +within the class in which it is declared, the static method can also be +called without `class_name=>...`. You might also see method +calls with [`CALL METHOD`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcall_method_static.htm) +statements which are not used here (however, these statements are the +only option in the context of dynamic programming. +When methods are called, the parameters must be specified within the +parentheses. + + +Examples for instance method calls and static method calls: +``` abap +"Calling instance methods via reference variable + +ref->inst_meth( ... ). + +"Calling static methods via/without the class name + +class_name=>stat_meth( ... ). + +"Only within the program in which it is declared. +stat_meth( ... ).   + +"Calling (static) methdod having no parameter + +class_name=>stat_meth( ). + +"Calling (static) methods having a single importing parameter: + +"Note that in the method call, the caller exports values to the +"method having importing parameters defined; hence, the ABAP word +"EXPORTING is relevant. The three following method calls are the same + +"Explicit use of EXPORTING. +class_name=>meth( EXPORTING a = b ). + +"Only importing parameters in the signature: explicit EXPORTING not needed + +class_name=>meth( a = b ). + +"Only a single value must be passed: parameter name (a) and EXPORTING not needed + +stat_meth( b ). + +"Calling (static) methods having importing/exporting parameters +"Parameters must be specified if they are not marked as optional + +class_name=>meth( EXPORTING a = b c = d "a/c: importing parameters +                  IMPORTING e = f ). "e: exporting parameter + +"If f is not yet available, you could also declare it inline. + +class_name=>meth( EXPORTING a = b c = d +                  IMPORTING e = DATA(f) ). "f receives type of e + +"Calling (static) methods having a changing parameter; +"should be reserved for changing an existing local variable and value + +DATA h TYPE i VALUE 123. +class_name=>meth( CHANGING g = h ). + +"Calling (static) methods having a returning parameter. +"Basically, they do the same as methods with exporting parameters +"but they are way more versatile and you save lines of code. + +"They do not need temporary variables. +"In the example, the return value is stored in a variable declared inline. + +"i and k are importing parameters +DATA(result) = class_name=>meth( i = j k = l ) + +"They can be used with other statements, e. g. logical expressions. +IF class_name=>meth( i = j k = l ) > 100. +  ... +ENDIF. + +"They enable method chaining. +"The example shows a method to create random integer values. +"The methods have a returning parameter. + +DATA(random_no) = cl_abap_random_int=>create( )->get_next( ). + +"Receiving parameter: Available in methods defined with a returning parameter; +"used in standalone method calls only. +"In the snippet, m is the returning parameter; n stores the result. + +class_name=>meth( EXPORTING i = j k = l RECEIVING m = DATA(n) ). +``` + +**Self-Reference me** + +When implementing instance methods, you can make use of the implicitly available object reference variable `me` which is always available and points to the respective object itself. You can use it to refer to components of the instance of a particular class but it is not needed: +``` abap +... some_method( ... ) ... + +... me->some_method( ... ) ... +``` + +However, if you want to access attributes of the particular class and these attributes have identical names as local attributes within the method, you can make use of `me` to access the attributes that are outside of the method and within the class. The following example demonstrates the use of `me` in a method implementation. + +``` abap +METHOD me_ref. + + DATA str TYPE string VALUE `Local string`. + + DATA(local_string) = str. + + "Assuming there is a variable str declared in the class declaration part. + DATA(other_string) = me->str. + +ENDMETHOD. +``` + +

(back to top)

+ +## Notes on Inheritance + +- Concept: Deriving a new class (i. e. a + [subclass](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensubclass_glosry.htm "Glossary Entry")) + from an existing one + ([superclass](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensuperclass_glosry.htm "Glossary Entry")) + to share common components between classes. In doing so, you create + hierarchies of classes (an [inheritance + tree](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninheritance_tree_glosry.htm "Glossary Entry")) + while a superclass includes components that are shared by all + subclasses to provide a better structure for your code. +- Subclasses ... + - inherit all components from superclasses + - can access instance components from superclasses + - can be made more specific by declaring new components and + redefining instance methods (i. e. replacing the implementations + of inherited methods). + - can access static components but not redefine them. + - can only handle components in the `PROTECTED` and + `PUBLIC` section of superclasses. + - know their direct superclass but they do not know which classes + inherit from them. [Note:] Handle component definitions + and implementations in the superclass with great care since a + change might have undesired consequences for the subclasses. +- Components ... + - that are changed and added to subclasses are not visible to + superclasses, hence, these changes are only relevant for this + class itself and its subclasses. + - that are added should have a different name than those of the + superclass. +- A class ... + - can only inherit from one superclass, i. e. a subclass can only + have one superclass, however, a superclass can have any number + of subclasses. + - must enable derivation, i. e. classes cannot inherit from + classes that are specified with the addition + [`FINAL`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapclass_options&sap-language=EN&sap-client=000&version=X&anchor=!ABAP_ADDITION_4@4@&tree=X) + (e. g. `CLASS global_class DEFINITION PUBLIC FINAL CREATE PUBLIC. ...`). + +

(back to top)

+ +**Excursion: `ABSTRACT` and `FINAL`** +- A global class declared with the addition `FINAL` rules out + inheritance. The addition `FINAL` is also available for + [method + declarations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmethods_abstract_final&sap-language=EN&sap-client=000&version=X&anchor=!ABAP_ADDITION_2@2@&tree=X) + in classes which rules out that such a method is redefined in + subclasses. In classes that are declared with `FINAL`, all + methods are implicitly final. Instance constructors are always final + by default. +- The addition + [`ABSTRACT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmethods_abstract_final.htm) + is meant for abstract classes. +- The addition `ABSTRACT` enters the picture in the context of + abstract classes. These classes are used if you want to have a + template for subclasses and you do not need instances of such + classes. Instances are only possible for their subclasses. Instance + components of an abstract class can then be accessed via an + instantiated subclass. `ABSTRACT` is available for [class + declarations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapclass_options&sap-language=EN&sap-client=000&version=X&anchor=!ABAP_ADDITION_3@3@&tree=X) + (e. g. `CLASS cl DEFINITION ABSTRACT. ...`) and [method + declarations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmethods_abstract_final&sap-language=EN&sap-client=000&version=X&anchor=!ABAP_ADDITION_1@1@&tree=X) + (e. g. `METHODS meth ABSTRACT. ...`). Abstract methods can + only be declared within abstract classes. They are not implemented + in the implementation part of the abstract class. Instead, they must + be redefined in subclasses. Note that in abstract classes, + non-abstract methods can also be declared and that private methods + cannot be redefined, i. e. methods in this section cannot be + declared as abstract. See a high-level comparison of abstract + classes and interfaces further down. + +

(back to top)

+ +**Redefining Methods** + +- The non-final method from the superclass that is redefined must be + specified in the declaration part of the subclass as follows: + `METHODS meth REDEFINITION.` + + It must be specified with the same method name and in the same + visibility section as in the superclass. Specifying or changing the + signature is not possible. + +- If you want to access the original method in the superclass within + the method implementation of the subclass, use the [pseudo + reference](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenpseudo_reference_glosry.htm "Glossary Entry") + `super->...`. + +> **💡 Note**
+> If the instance constructor is implemented in a subclass, the instance constructor of the superclass must be called explicitly using `super->constructor`, even if the latter is not explicitly declared. An exception to this: Direct subclasses of the root node object. + +

(back to top)

+ +## Notes on Polymorphism and Casting + +The object orientation concept +[polymorphism](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenpolymorphism_glosry.htm "Glossary Entry") +means accessing different methods in different objects and with +different behavior via the same interface, i. e. you can use one and the +same reference variable to access various objects, for example, +references to a superclass can point to objects of a subclass. + +Note the concept of static and dynamic type in this context: + +- Object reference variables (and also interface reference variables + as outlined further down) 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 an object reference variable, e. g. `DATA oref TYPE + REF TO cl`, you determine the static type, i. e. + `cl` is used to declare the reference variable that is + statically defined in your program. The dynamic type is determined + at runtime of the program and is the class of an object. Especially + in the context of assigning object or interface references (and also + [data + references](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_reference_glosry.htm "Glossary Entry")), + this differentiation enters the picture. +- The following basic rule applies: The assignment of an object or + interface 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 + although the types are different, 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) + or the older + [`?=`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmove_cast.htm), + for the assignment of object or interface 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). + +> **✔️ Hints**
+> - If the static type is a class, the dynamic type must be the same class or one of its subclasses. +> - If the static type is an interface, the dynamic type must implement the interface. + +As an example, assume there is an inheritance tree with `lcl_super` as the superclass and `lcl_sub` as a direct subclass. `lcl_sub2` is a direct subclass of `lcl_sub`. + +In the following code snippet, the rule is met since the superclass is either the same as or more generic than the subclass (the subclass has, for example, redefined methods and is, thus, more specific). Hence, the assignment of an object reference variable pointing to the subclass to a variable pointing to a superclass works. An upcast is triggered. After this casting, the type of `oref_super` has changed and the methods of `lcl_sub` can be accessed via `oref_super`. + +``` abap +oref_super = NEW lcl_super( ). + +oref_sub = NEW lcl_sub( ). + +"Upcast +oref_super = oref_sub. + +"The casting might be done when creating the object. +DATA super_ref TYPE REF TO lcl_super. + +super_ref = NEW lcl_sub( ). +``` + +Such upcasts frequently occur if you want to access objects via an +object reference variable pointing to the superclass. However, there +might also be situations when you want to do the assignment the other +way round, i. e. going from specific to more generic. In this case, a +more generic reference variable is assigned to a specific variable which +can be depicted as moving downwards in the inheritance tree concerning +the assignment. As mentioned above, a downcast must be triggered +manually. Just an assignment like `oref_sub = oref_super.` +does not work. A syntax error occurs saying the right-hand variable's +type cannot be converted to the left-hand variable's type. + +If you indeed want to carry out this casting, you must use +`CAST` or `?=` to overcome this syntax error (but just +the syntax error!). Note: You might also use these two +operators for the upcasts. That means, `oref_super = +oref_sub.` has the same effect as `oref_super = CAST #( +oref_sub ).`. This syntax is usually not necessary. + +At runtime, the assignment is checked and if the conversion does not +work, you face a (catchable) runtime error. Even more so, the assignment +`oref_sub ?= oref_super.` does not throw a syntax error but it +does not work in this example either because it violates the rule +mentioned above (`oref_sub` is more specific than +`oref_super`). To check whether such an assignment is possible +on specific classes, you can use the predicate expression [`IS INSTANCE +OF`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_instance_of.htm) +or the case distinction [`CASE TYPE +OF`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcase_type.htm). +Carrying out an upcast before the downcast ensures that the left-hand +variable's type is compatible to the right-hand variable's type. + +``` abap +oref_super = NEW lcl_super( ). +oref_sub = NEW lcl_sub( ). +oref_sub2 = NEW lcl_sub2( ). + +"Downcast resulting in an error; error is caught + +TRY. +  oref_sub = CAST #( oref_super ). +  CATCH CX_SY_MOVE_CAST_ERROR INTO DATA(e). +    ... +ENDTRY. + +"Working downcast with a prior upcast + +oref_super = oref_sub2. + +"Due to the prior upcast, the following check is actually not necessary. + +IF oref_super IS INSTANCE OF lcl_sub. +  oref_sub = CAST #( oref_super ). +  ... +ENDIF. +``` + +

(back to top)

+ +## Working with Interfaces + +Interfaces ... + +- represent a template for the components in the public visibility + section of classes and thus enhance the components of classes by + adding components of interfaces. +- represent a means to deal with multiple inheritance in ABAP Object + Orientation. +- serve the concept of + [polymorphism](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenpolymorphism_glosry.htm "Glossary Entry"). + Any number of classes can implement the same interface. +- are beneficial if you want to share and reuse common components + across classes especially if those classes are not in an inheritance + relationship. +- are possible as both + [local](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlocal_interface_glosry.htm "Glossary Entry") + and [global + interfaces](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenglobal_interface_glosry.htm "Glossary Entry"). +- are different from classes in the following ways: + - They only consist of a part declaring the components without an + implementation part. The classes using the interfaces are + responsible for the implementation. + - They do not include visibility sections. All interface + components are public. + - No instances can be created from interfaces. + - Declarations as mentioned for classes, e. g. `DATA`, + `CLASS-DATA`, `METHODS`, + `CLASS-METHODS`, are possible. Constructors are not + possible. + +

(back to top)

+ +**Defining Interfaces** + +> **💡 Note**
+> The addition `DEFINITION` is not relevant here since there is no implementation part. + +``` abap +INTERFACE intf. +"The addition PUBLIC is for global interfaces: +"INTERFACE intf_g PUBLIC. + +    DATA ... + CLASS-DATA ... + METHODS ... + CLASS-METHODS ... + +ENDINTERFACE. +``` + +

(back to top)

+ +**Using Interfaces in Classes** + +- A class can implement multiple interfaces. +- As a prerequisite, the interfaces must be specified in the + declaration part of a class using the statement + [`INTERFACES`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapinterfaces.htm). +- Since all interface components are public, you must include this + statement and the interfaces in the public section of a class. +- In doing so, the interface components become part of the class + itself. Methods that are specified in interfaces must be implemented + in the class unless the methods are marked as optional in the + interface using the additions [`DEFAULT + IGNORE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmethods_default.htm) + or [`DEFAULT FAIL`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmethods_default.htm). + Furthermore, you can specify the addition [`ABSTRACT + METHODS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapinterfaces_class.htm) + for the `INTERFACES` statement in the declaration part of + classes followed by method names. In this case, the class need not + implement the methods of the interface. The implementation is then + relevant for a subclass inheriting from a superclass that includes + such an interface declaration. Find more information + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapinterfaces_class.htm). +- You can specify alias names for the interface components using the + statement [`ALIASES ... FOR + ...`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapaliases.htm). + The components can then be addressed using the alias name everywhere + (since the alias is public). + +Syntax for using interfaces in classes: +``` abap +CLASS class DEFINITION. +  PUBLIC SECTION. +    INTERFACES intf1. +    INTERFACES intf2 ABSTRACT METHODS meth1. "No implementation required for meth1 +    INTERFACES intf3 ALL METHODS ABSTRACT. "All methods abstract + +    ALIASES meth_alias FOR intf1~some_method. +ENDCLASS. + +CLASS class IMPLEMENTATION. +  METHOD intf1~some_meth. "Method implementation using the original name +   ... +  ENDMETHOD. + + "Just for demo purposes: Method implementation using the alias name   + "METHOD meth_alias.   +  " ... +  "ENDMETHOD. + +  ... +ENDCLASS. +``` + +

(back to top)

+ +**Accessing Interface Components** + +- Interface components can be addressed using the [interface component + selector](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninterface_comp_selector_glosry.htm "Glossary Entry"): + `... intf~comp ...`. + - Note: Due to the unique interface name and the use of + the name as prefix (e. g. `intf~`) that is followed by + a component name, there is no problem regarding the component + naming within a class, i. e. the class can have components with + the same name, and other interfaces can have components with the + same name, too. +- You can then access them either using class references or interface + references: + - Class references: `... class_ref->intf~comp ...` + - Interface references: `... i_ref->comp ...`. In this + case, the [object component + selector](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenobject_component_select_glosry.htm "Glossary Entry") + is used to access the components without the interface name. + +Before making use of interface references, an [interface reference +variable](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninterface_ref_variable_glosry.htm "Glossary Entry") +must be created: `DATA i_ref TYPE REF TO intf.` Interfaces +cannot be instantiated, i. e. an object cannot be created, however, +interface references can point to the objects of any class that includes +the interface so that interface components (and only them) can be +accessed via the variable. Accessing an object via an interface +reference variable is basically the same as accessing a subclass object +via a superclass reference variable. + +

(back to top)

+ +**Assigning Interface Reference Variables** + +As mentioned above, interface references can point to the objects of any +class that includes the interface. This is true when object references +are assigned to interface references. Here, as touched on before, the +concept of +[upcasting](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenup_cast_glosry.htm "Glossary Entry") +enters the picture. +``` abap +DATA i_ref TYPE REF TO intf. + +DATA cl_ref TYPE REF TO class. + +cl_ref = NEW #( ). + +"Upcast +i_ref = cl_ref. + +"Method call using the interface reference variable +i_ref->some_method( ... ). +``` + +The other way round, i. e. the assignment of an interface reference to +an object reference, is also possible +([downcast](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendown_cast_glosry.htm "Glossary Entry") +or narrowing cast). However, as mentioned before, this assignment can be +problematic since a successful assignment is dependent on whether the +object the interface reference points to is actually an object of the +implementing class. If this is not the case, a runtime error occurs. You +can carry out a downcast using the casting operator +[`CAST`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_cast.htm) +or the operator +[`?=`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmove_cast.htm). +The example shows the use of an [`IS INSTANCE +OF`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_instance_of.htm) +expression to prevent a runtime error as also shown before. + +``` abap +DATA i_ref TYPE REF TO intf. + +DATA cl_ref TYPE REF TO class. + +cl_ref = NEW #( ). + +IF i_ref IS INSTANCE OF class. +  cl_ref = CAST #( i_ref ). +... +ENDIF. +``` + +> **✔️ Hints**
+> Interfaces versus abstract classes
+> Coming back to +[abstract](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabstract_glosry.htm "Glossary Entry") +classes in the context of interfaces. Like interfaces, abstract classes +cannot be instantiated - only their subclasses. The following list +includes differences between abstract classes and interfaces that should +be considered when creating them: +>- Abstract classes can have components (including abstract and + non-abstract methods) in other visibility sections than only public. +>- Multiple inheritance is impossible with abstract classes since there + can be only one abstract class as superclass. +>- Non-abstract methods in abstract classes can be implemented whereas + interfaces do not allow any method implementations. + +

(back to top)

+ +## Further Concepts + +### Factory Methods + +A factory method is relevant if you want to restrict and control the +instantiation of a class by external users of this class. Still, the +users should be able to work with objects of the class. This is true for +cases when, for example, there must be only a single object of a class +(a singleton) or certain checks must be carried out before a class can +be instantiated so as to guarantee a consistent creation of all objects. + +A (static) factory method implemented in such a class does the trick: It +creates an object of the class and returns a reference to the object. + +Example for a class and factory method: +``` abap +"Addition CREATE PRIVATE +CLASS class DEFINITION CREATE PRIVATE. + +  PUBLIC SECTION. +  CLASS-METHODS factory_method +         IMPORTING ... +         RETRUNING VALUE(obj) TYPE REF TO class. "Returns an object + +ENDCLASS. +... + +"Calling a factory method. +DATA obj_factory TYPE REF TO class. + +obj_factory = class=>factory_method( ... ). +``` + +

(back to top)

+ +### Friendship + +Classes can grant friendship to other classes and interfaces to enable +the access to protected and private components. However, the friendship +is not reciprocal. If class `a` grants friendship to class `b`, class `b` must +also explicitly grant friendship to class `a` if the component should be +made accessible also the other way round. + +Friends of a class can create instances of the class without any +restrictions. They are not automatically made friends of the subclasses +of the class. + +Friendship prevents that the components are made available to all users. +A typical use case for friendship between classes is [unit +tests](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenunit_test_glosry.htm "Glossary Entry"), +i. e. friendship is granted to test classes so that they can access and +test private components, too. + +You specify the befriended class in the definition part: +``` abap +CLASS class DEFINITION FRIENDS other_class. +... +``` + +

(back to top)

+ +### Events + +[Events](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenevent_glosry.htm "Glossary Entry") +are components of classes that can be triggered by methods. If an event +is raised (by a [`RAISE EVENT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapraise_event.htm) +statement), specific [event handler +methods](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenevent_handler_glosry.htm "Glossary Entry") +are called to react on the event. The following points are relevant for +raising events: + +- Defining events + - Events must be defined in a visibility section of the + declaration part of a class or in an interface, e. g. as + instance (using an + [`EVENTS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapevents.htm) + statement) or static events + ([`CLASS-EVENTS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapclass-events.htm)). + - Similar to methods, static events can be triggered by instance + and static methods, instance events can only be triggered by + instance methods. + - Events allow exporting parameters to be defined. They must be + passed by value. Each instance event also includes the implicit + output parameter `sender` representing an object + reference variable for the instance for which the event is + defined. +- Defining and implementing [event handler + methods](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenevent_handler_glosry.htm "Glossary Entry"). + These methods are defined with a special syntax: + +``` abap +CLASS-METHODS handler_meth FOR EVENT evt OF class +... +``` + +- Registering event handler methods + + - Event handler methods must be registered using a [`SET + HANDLER`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapset_handler.htm) + statement at runtime so that they can handle a raised event at + all and react accordingly. + - You can register instance events for a specific instance or for + all instances of a class. Static events are registered to the + whole class without any addition to the `SET HANDLER` + statement. + +```abap +SET HANDLER handler_meth FOR ref. "Specific instance + +SET HANDLER handler_meth FOR ALL INSTANCES. "All instances + +SET HANDLER handler_meth. "For static events +``` + +

(back to top)

+ +## Executable Example +[zcl_demo_abap_objects](./src/zcl_demo_abap_objects.clas.abap) + +Note the steps outlined [here](README.md#🎬-getting-started-with-the-examples) about how to import and run the code. diff --git a/05_Constructor_Expressions.md b/05_Constructor_Expressions.md new file mode 100644 index 0000000..9f39ff0 --- /dev/null +++ b/05_Constructor_Expressions.md @@ -0,0 +1,899 @@ + + +# Working with Constructor Expressions + +- [Working with Constructor Expressions](#working-with-constructor-expressions) + - [Introduction](#introduction) + - [`VALUE`](#value) + - [`CORRESPONDING`](#corresponding) + - [`NEW`](#new) + - [`CONV`](#conv) + - [`EXACT`](#exact) + - [`REF`](#ref) + - [`CAST`](#cast) + - [`COND`](#cond) + - [`SWITCH`](#switch) + - [`FILTER`](#filter) + - [`REDUCE`](#reduce) + - [Iteration Expressions with `FOR`](#iteration-expressions-with-for) + - [`LET Expressions`](#let-expressions) + - [Executable Example](#executable-example) + +## Introduction + +- [Constructor + expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_glosry.htm "Glossary Entry") + include a [constructor + operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_operator_glosry.htm "Glossary Entry") + followed by the specification of a [data + type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_type_glosry.htm "Glossary Entry") + or [object + type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenobject_type_glosry.htm "Glossary Entry") + (or a `#` character that stands for such a type) and + specific parameters specified within parentheses. Example using the + `VALUE` operator: + + ``` abap + ... VALUE string( ... ) ... + ... VALUE #( ... ) ... + ``` + +- As the name implies, these expressions construct results of a + specific type and their content. Either the type is specified + explicitly before the first parenthesis or the said `#` + character can be specified if the type can be derived implicitly + from the [operand + position](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenoperand_position_glosry.htm "Glossary Entry"). + The `#` character symbolizes the [operand + type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenoperand_type_glosry.htm "Glossary Entry"). + If no type can be derived from the operand position, for some + constructor operators, the type can also be derived from the + arguments in the parentheses. +- Why use them? Constructor expressions can make your code leaner and + more readable since you can achieve the same with fewer statements. +- Apart from the concept of deriving types from the context, another + concept is very handy particularly in this context: [Inline + declaration](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninline_declaration_glosry.htm "Glossary Entry"). + - This means that you can declare a variable using + `DATA(var)` (or an immutable variable + [`FINAL(var)`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfinal_inline.htm)) + as an operand in the current [write + position](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwrite_position_glosry.htm "Glossary Entry"). + In doing so, such a variable declared inline can be given the + appropriate type and result of the constructor expression in one + go: `DATA(dec) = VALUE decfloat34( '1.23' )`. + +> **✔️ Hint**
+> The construction of a result, i. e. a target [data +object](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_object_glosry.htm "Glossary Entry"), +implies that the data object is initialized. However, for some +constructor operators, there is an addition with which the +initialization can be avoided. + +

(back to top)

+ +## `VALUE` + +- Expressions with the + [`VALUE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_value.htm) + operator construct a result in place based on a data type. +- This result can be initial values for any non-generic data types, + structures or internal tables. +> **💡 Note**
+> Elementary data types and reference types cannot be + explicitly specified for the construction of values here. +- Regarding the type specifications before and parameters within the + parentheses: + - No parameter specified within the parentheses: The return value + is set to its type-specific initial value. This is possible for + any non-generic data types. See more information + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenvalue_constructor_params_init.htm). + - Structured and internal table type before the parentheses or + `#` stands for such types: Individual components of + structures can be specified as named arguments while each + component of the return value can be assigned a data object that + has the same data type as the component, or whose data type can + be converted to this data type. See more information + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenvalue_constructor_params_struc.htm). + To construct internal tables, you have multiple options, for + example, you can add individual table lines using an inner pair + of parentheses. More syntax options, for example, using the + additions `BASE` and `FOR` are possible, too. + See more information + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenvalue_constructor_params_itab.htm). + +Example: Structure + +``` abap +"Creating a structured type +TYPES: BEGIN OF struc_type, +          a   TYPE i, +          b   TYPE c LENGTH 3, +        END OF struc_type. + +DATA struc TYPE struc_type. "Structured data object + +struc = VALUE #( a = 1 b = 'aaa' ). "Deriving the type using # +``` + + +As mentioned above, the concept of [inline +declarations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninline_declarations.htm) +enters the picture here, which simplifies ABAP programming. You can +construct a new data object (for example, using `DATA(...)`), +provide the desired type with the constructor expression and assign +values in one go. + +``` abap +"Explicit type specification needed +DATA(structure) = VALUE struc_type( a = 2 b = 'bbb' ). +``` + +Note that initial values can be created by omitting the specification of +components or by providing no content within the parentheses. + +``` abap +"Component b not specified, b remains initial +struc = VALUE #( a = 2 ). + +"Explicit setting of initial value for a component +struc = VALUE #( a = 1 b = value #( ) ). + +"The whole structure is initial +struc = VALUE #( ). + +"Creating initial values for an elementary data type +DATA num1 TYPE i. + +num1 = VALUE #( ). + +"Inline declaration +DATA(num2) = VALUE i( ). +``` + +Regarding internal tables, the line specifications are enclosed in an +inner pair of parentheses `( ... )`. In the following examples, +three lines are added to a table. + +``` abap +"Creating an internal table type and an internal table +TYPES tab_type TYPE TABLE OF struc_type WITH EMPTY KEY. +DATA itab TYPE tab_type. + +"Filling the internal table using the VALUE operator with # +itab = VALUE #( ( a = 1 b = 'aaa' ) +                ( a = 2 b = 'bbb' ) +                ( a = 3 b = 'ccc' ) ). + +"Internal table declared inline, explicit type specification +DATA(itab2) = VALUE tab_type( ( a = 1 b = 'aaa' ) +                              ( a = 2 b = 'bbb' ) +                              ( a = 3 b = 'ccc' ) ). + +"Unstructured line types work without component names. +"Here, the internal table type is a string table. +DATA(itab3) = VALUE string_table( ( `abc` ) ( `def` ) ( `ghi` ) ). +``` + +In case of +[deep](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendeep_structure_glosry.htm "Glossary Entry") +and [nested +structures](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abennested_structure_glosry.htm "Glossary Entry") +or [deep +tables](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abendeep_table_glosry.htm "Glossary Entry"), +the use of `VALUE` expressions is handy. The following example +demonstrates a nested structure. +``` abap +"Creating a nested structure +DATA: BEGIN OF nested_struc, +        a TYPE i, +        BEGIN OF struct, +          b TYPE i, +          c TYPE c LENGTH 3, +        END OF struct, +      END OF nested_struc. + +"Filling the deep structure +nested_struc = VALUE #( a = 1 struct = VALUE #( b = 2 c = 'abc' ) ). +``` + +`BASE` addition: A constructor expression without the +`BASE` addition initializes the target variable. Hence, you can +use the addition if you do not want to construct a structure or internal +table from scratch but keep existing content. + +``` abap +"Filling structure +struc = VALUE #( a = 1 b = 'aaa' ). + +"struc is not initialized, only component b is modified, value of a is kept +struc = VALUE #( BASE struc b = 'bbb' ). + +"Filling internal table with two lines +itab = VALUE #( ( a = 1 b = 'aaa' ) +                ( a = 2 b = 'bbb' ) ). + +"Two more lines are added instead of initializing the internal table +itab = VALUE #( BASE itab +                ( a = 3 b = 'ccc' ) +                ( a = 4 b = 'ddd' ) ). +``` + +`LINES OF` addition: All or some lines of another table can be included in the target internal table (provided that they have +appropriate line types): +``` abap +itab = VALUE #( ( a = 1 b = 'aaa' ) +                ( a = 2 b = 'bbb' ) +                ( LINES OF itab2 ) "All lines of itab2 +                ( LINES OF itab3 FROM 2 TO 5 ) ). "Specific lines of itab3 +``` + +Using the inline construction of structures and internal tables, you can +avoid the declaration of extra variables in many contexts, for example, +ABAP statements like +[`MODIFY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_itab.htm) +for modifying internal tables or [ABAP +SQL](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_sql_glosry.htm "Glossary Entry") +statements like +[`MODIFY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_dbtab.htm) +(which is not to be confused with the ABAP statement having the same +name) for modifying database tables. + +Examples: +``` abap +"ABAP statements +"Modifiying individual internal table entries based on a structure created inline + +"Modifying a table line +MODIFY TABLE some_itab FROM VALUE #( a = 1 ... ). + +"Inserting a table line +INSERT VALUE #( a = 2 ... ) INTO TABLE some_itab. + +"Deleting a table line +DELETE TABLE some_itab FROM VALUE #( a = 3 ). + +"ABAP SQL statement +"Modifying multiple database table entries based on an internal table +"constructed inline within a host expression +MODIFY zdemo_abap_carr FROM TABLE @( VALUE #( + ( carrid = 'XY' + carrname = 'XY Airlines' + currcode = 'USD' + url =  'some_url' ) + ( carrid = 'ZZ' + carrname = 'ZZ Airways' + currcode = 'EUR' + url =  'some_url' ) ) ). +``` + +> **💡 Note**
+> Some of the additions and concepts mentioned here are +also valid for other constructor expressions further down but not +necessarily mentioned explicitly. See the details on the syntactical +options of the constructor operators in the ABAP Keyword Documentation. + +

(back to top)

+ +## `CORRESPONDING` + +- Expressions with the + [`CORRESPONDING`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expr_corresponding.htm) + operator construct structures and internal tables based on a data + type (i. e. a [table + type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentable_type_glosry.htm "Glossary Entry") + or [structured + type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstructured_type_glosry.htm "Glossary Entry")). +- The components or columns of the target data object are filled using + assignments of the parameters specified within the parentheses. +- The assignments are made using identical names or based on [mapping + relationships](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencorresponding_constr_mapping.htm) +- Note: Pay attention to the [assignment and conversion + rules](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconversion_rules.htm) + to avoid errors when using the operator. Consider, for example, the + impact of assigning the values of identically named fields having + different types (e. g. one field is of type `c` and another + field is of type `string`). + +The following table includes a selection of various possible additions to +this constructor operator. There are more variants available like the +addition `EXACT`, using a lookup table, the option of discarding +duplicates or +[RAP](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_glosry.htm "Glossary Entry")-specific +variants that are not part of this cheat sheet. Find the details in +[this +topic](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expr_corresponding.htm). + +| Addition | Details | +|---|---| +| `BASE` | Keeps original values. Unlike, for example, the operator `VALUE`, a pair of parentheses must be set around `BASE`. | +| `MAPPING` | Enables the mapping of component names, i. e. a component of a source structure or source table can be assigned to a differently named component of a target structure or target table (e. g. `MAPPING c1 = c2`). | +| `EXCEPT` | You can specify components that should not be assigned content in the target data object. They remain initial. In doing so, you exclude identically named components in the source and target object that are not compatible or convertible from the assignment to avoid syntax errors or runtime errors. | +| `DEEP` | Relevant for deep tabular components. They are resolved at every hierarchy level and identically named components are assigned line by line. | +| `[DEEP] APPENDING` | Relevant for (deep) tabular components. It ensures that the nested target tables are not deleted. The effect without `DEEP` is that lines of the nested source table are added using `CORRESPONDING` without addition. The effect with `DEEP` is that lines of the nested source table are added using `CORRESPONDING` with the addition `DEEP`. | + +See the executable example for demonstrating the effect of the variants: +``` abap +"Assignment of a structure/internal table to another one having a different type +struc2 = CORRESPONDING #( struc1 ). + +tab2 = CORRESPONDING #( tab1 ). + +"BASE keeps original content, does not initialize the target +struc2 = CORRESPONDING #( BASE ( struc2 ) struc1 ). + +tab2 = CORRESPONDING #( BASE ( tab2 ) tab1 ). + +"MAPPING/EXACT are used for mapping/excluding components in the assignment +struc2 = CORRESPONDING #( struc1 MAPPING comp1 = comp2 ). + +tab2 = CORRESPONDING #( tab1 EXCEPT comp1 ). + +"Complex assignments with deep components using further additions +st_deep2 = CORRESPONDING #( DEEP st_deep1 ). + +st_deep2 = CORRESPONDING #( DEEP BASE ( st_deep2 ) st_deep1 ). + +st_deep2 = CORRESPONDING #( APPENDING ( st_deep2 ) st_deep1 ). + +st_deep2 = CORRESPONDING #( DEEP APPENDING ( st_deep2 ) st_deep1 ). +``` +> **✔️ Hint**
+> `CORRESPONDING` operator versus +[`MOVE-CORRESPONDING`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmove-corresponding.htm): +Although the functionality is the same, note that, as the name implies, +constructor operators construct and - without the addition +`BASE` - target objects are initialized. Hence, the following +two statements are not the same: +>``` abap +>struc2 = CORRESPONDING #( struc1 ). +> +>"Not matching components are not initialized +>MOVE-CORRESPONDING struc1 TO struc2. +>``` + +

(back to top)

+ +## `NEW` + +- Using the instance operator + [`NEW`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_new.htm), + you can create [anonymous data + objects](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenanonymous_data_object_glosry.htm "Glossary Entry") + or + [instances](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninstance_glosry.htm "Glossary Entry") + of a class and also assign values to the new object. As a result, + you get a [reference + variable](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenreference_variable_glosry.htm "Glossary Entry") + that points to the created object. In doing so, the operator + basically replaces [CREATE + DATA](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcreate_data.htm) + and [CREATE + OBJECT](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcreate_object.htm). +- For the type specification preceding the parentheses, you can use + - non-generic data types which creates a [data reference + variable](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_reference_variable_glosry.htm "Glossary Entry") + pointing to the anonymous data object. + - classes which creates objects of these classes. The result is a + [object reference + variable](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenobject_refer_variable_glosry.htm "Glossary Entry") + pointing to an object. +- Regarding the created object reference variables, you can use the + [object component + selector](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenobject_component_select_glosry.htm "Glossary Entry") + `->` in certain contexts to ... + - point to a class attribute: `... NEW class( ... )->attr` + - introduce + [standalone](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcall_method_static_short.htm) + and + [functional](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcall_method_functional.htm) + method calls, including [chained method + calls](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenchained_method_call_glosry.htm "Glossary Entry") + which is a big advantage because you do not need to declare an + extra variable: `... NEW class( ... )->meth( ... ) ...` +- Regarding the type specifications before and parameters within the + parentheses: + - No parameter specified within the parentheses: An anonymous data + object retains its type-specific initial value. In case of + classes, no parameter specification means that no values are + passed to the instance constructor of an object. However, in + case of mandatory [input + parameters](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninput_parameter_glosry.htm "Glossary Entry"), + the parameters must be specified. + - Single parameter specified: If the type specified before the + parentheses is a non-generic elementary, structured, table, or a + reference type (or such a type can be derived using + `#`), a single data object can be specified as an + unnamed argument. Note the [assignment + rules](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconversion_rules.htm) + regarding the value assignments within the parentheses and that + a constructor expression itself can be specified there. + - Structures and internal tables specified: If the type specified + before the parentheses is a structured data type or `#` + stands for it, you can specify the individual components as + named arguments (`comp1 = 1 comp2 = 2 ...`; see more + information + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abennew_constructor_params_struct.htm)). + For the construction of anonymous internal tables, multiple + options are available. Among them, there is the use of + `LET` and `FOR` expressions and others. See more + details + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abennew_constructor_params_itab.htm). + - Classes: As mentioned, non-optional input parameters of the + instance constructor of the instantiated class must be filled. + No parameters are passed for a class without an explicit + instance constructor. See more information: + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abennew_constructor_params_class.htm). + +Examples: +``` abap +"Data references +"Declaring data reference variables +DATA dref1 TYPE REF TO i.    "Complete type +DATA dref2 TYPE REF TO data. "Generic type + +"Creating anonymous data objects +"Here, no parameters are specified within the parentheses meaning the +"data objects retain their initial values. +dref1 = NEW #( ). +dref2 = NEW string( ). + +"Assigning single values; specified as unnamed data objects +dref1 = NEW #( 123 ). +dref2 = NEW string( `hallo` ). + +"Using inline declarations to omit a prior declaration of a variable +DATA(dref3) = NEW i( 456 ). + +DATA text TYPE string VALUE `world`. + +"Another constructor expression specified within the parentheses +dref2 = NEW string( `Hello ` && text && CONV string( '!' ) ). + +DATA dref4 TYPE REF TO string_table. +dref4 = NEW #( VALUE string_table( ( `a` ) ( `b` ) ) ). + +"Structured type; named arguments within the parentheses +DATA(dref5) = NEW scarr( carrid = 'AA' carrname = 'American Airlines' ). + +"Object references +"Declaring object reference variables +DATA oref1 TYPE REF TO cl1. "Assumption: class without constructor implementation +DATA oref2 TYPE REF TO cl2. "Assumption: class with constructor implementation + +"Creating instances of classes +oref1 = NEW #( ). + +"Listing the parameter bindings for the constructor method +"If there is only one parameter, the explicit specification of the +"parameter name is not needed and the value can be specified directly +oref2 = NEW #( p1 = ... p2 = ... ). + +"Using inline declaration +DATA(oref3) = NEW cl2( p1 = ... p2 = ... ). + +"Method chaining +... NEW some_class( ... )->meth( ... ). + +"Chained attribute accesses +... NEW some_class( ... )->attr ... +``` + +

(back to top)

+ +## `CONV` + +- The + [`CONV`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_conv.htm) + operator enforces conversions from one type to another and creates + an appropriate result. +- Note that the conversion is carried out according to [conversion + rules](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconversion_rules.htm). + - Further [special + rules](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconv_constructor_inference.htm) + apply if the constructor expression is passed to an [actual + parameter](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenactual_parameter_glosry.htm "Glossary Entry") + with a generically typed [formal + parameter](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenformal_parameter_glosry.htm "Glossary Entry"). +- The operator is particularly suitable for avoiding the declaration + of helper variables. + +Examples: +``` abap +"Result: 0.2 +DATA(a) = CONV decfloat34( 1 / 5 ). + +"Comparison with an expression without CONV; the result is 0 +DATA(b) = 1 / 5. +``` + +Excursion: As outlined above, you can construct structures and internal +tables using the `VALUE` operator. Using this operator for +constructing [elementary data +objects](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenelementary_data_object_glosry.htm "Glossary Entry") +is not possible apart from creating a data object with an initial value, +for example `DATA(str) = VALUE string( ).`. The `CONV` +operator closes this gap. However, in some cases, the use of +`CONV` is redundant. + +``` abap +DATA(c) = CONV decfloat34( '0.4' ). + +"Instead of +DATA d TYPE decfloat34 VALUE '0.4'. + +"Redundant conversion +"Derives the string type automatically +DATA(e) = `hallo`. + +"Produces a syntax warning +"DATA(f) = CONV string( `hallo` ). +``` + + +

(back to top)

+ +## `EXACT` + +- The + [`EXACT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_exact.htm) + operator enforces either a [lossless + assignment](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlossless_move.htm) + or a [lossless + calculation](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlossless_calculation.htm) + depending on the data object specified within the parentheses and + creates an appropriate result. +- In case of calculations, [rules of lossless + assignments](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmove_exact.htm) + apply. In other cases, the result is created according to the + [conversion + rules](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconversion_rules.htm) + mentioned above and an additional check is performed in accordance + with the [rules of lossless + assignments](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmove_exact.htm). + +Examples: +``` abap +"Leads to a data loss when converting to a data object accepting only a single character +TRY. +  DATA(exact1) = EXACT abap_bool( 'XY' ). +  CATCH CX_SY_CONVERSION_DATA_LOSS INTO DATA(error1). +ENDTRY. + +"The calculation cannot be executed exactly; a rounding is necessary +TRY. +  DATA(exact2) = EXACT decfloat34( 1 / 3 ). +  CATCH CX_SY_CONVERSION_ROUNDING INTO DATA(error2). +ENDTRY. +``` + +

(back to top)

+ +## `REF` + +- The + [`REF`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_ref.htm) + operator creates a [data reference + variable](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_reference_variable_glosry.htm "Glossary Entry") + pointing to a specified data object. +- The type specified after `REF` and directly before the first + parenthesis determines the [static + type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstatic_type_glosry.htm "Glossary Entry") + of the result. +- The operator replaces [`GET + REFERENCE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapget_reference.htm) + and is particularly useful for avoiding the declaration of helper + variables that are only necessary, for example, to specify data + reference variables as actual parameters. + +Examples: +``` abap +"Data references +"Declaring data object and assign value + +DATA number TYPE i VALUE 5. + +"Declaring data reference variable + +DATA dref_a TYPE REF TO i. + +"Getting references + +dref_a = REF #( number ). + +"Inline declaration and explicit type specification +DATA(dref_b) = REF string( `hallo` ). + +"Object references + +DATA(oref_a) = NEW some_class( ). + +DATA(oref_b) = REF #( oref_a ). +``` + +

(back to top)

+ +## `CAST` + +- Using the + [`CAST`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_cast.htm) + operator, you can carry out + [upcasts](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenup_cast_glosry.htm "Glossary Entry") + and + [downcasts](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendown_cast_glosry.htm "Glossary Entry") + and create a reference variable of a static type as a result. +- It replaces the + [`?=`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmove_cast.htm) + operator and enables [chained method + calls](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenchained_method_call_glosry.htm "Glossary Entry"). +- The operator is particularly helpful for avoiding the declaration of + helper variables and more contexts. +- Similar to the `NEW` operator, constructor expressions with + `CAST` can be followed by the object component selector + `->` to point to a class or interface attribute (`... CAST class( ... )->attr`) and methods (`... CAST class( ... + )->meth( ... )`). Method chaining, standalone and + functional method calls are possible, too. See more information + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_cast.htm). + +[Run Time 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") +examples: +``` abap +"Getting component information +DATA(components) = +  CAST cl_abap_structdescr( +    cl_abap_typedescr=>describe_by_data( some_object ) )->components. + +"Getting method information +DATA(methods) = +  CAST cl_abap_objectdescr( +    cl_abap_objectdescr=>describe_by_name( 'LOCAL_CLASS' ) )->methods. +``` + +

(back to top)

+ +## `COND` + +- The + [`COND`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconditional_expression_cond.htm) + operator is used for either creating a result depending on [logical + expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogical_expression_glosry.htm "Glossary Entry") + or raising a [class-based + exception](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenclass_based_exception_glosry.htm "Glossary Entry") + (which is specified within the parentheses after the addition + [`THROW`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconditional_expression_result.htm)). +- There can be multiple logical expressions initiated by + `WHEN` followed by the result specified after + `THEN`. If none of the logical expressions are true, you can + specify an `ELSE` clause at the end. If this clause is not + specified, the result is the initial value of the specified or + derived data type. +- Note that all operands specified after `THEN` must be + convertible to the specified or derived data type. + +Example: +``` abap +DATA(b) = COND #( WHEN a BETWEEN 1 AND 3 THEN w +                  WHEN a > 4 THEN x +                  WHEN a IS INITIAL THEN y +                  ELSE z ). +``` + +

(back to top)

+ +## `SWITCH` + +The +[`SWITCH`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconditional_expression_switch.htm) +operator is fairly similar to the `COND` operator and works in +the style of +[`CASE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcase.htm) +statements, i. e. it uses the value of only a single variable that is +checked in the case distinction. + +``` abap +DATA(b) = SWITCH #( a +                    WHEN 1 THEN w +                    WHEN 2 THEN x +                    WHEN 3 THEN y +                    ELSE z ). +``` + +

(back to top)

+ +## `FILTER` + +- The + [`FILTER`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_filter.htm) + operator constructs an internal table line by line based on an + existing table and conditions specified in a `WHERE` clause. +- The conditions can either be based on single values or a [filter + table](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expr_filter_table.htm). +- Additions: + +|Addition |Details | +|---|---| +|`USING KEY` | Specifies the [table key](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentable_key_glosry.htm "Glossary Entry") with which the `WHERE` condition is evaluated, i. e. a [sorted key](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensorted_key_glosry.htm "Glossary Entry") or a [hash key](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenhash_key_glosry.htm "Glossary Entry"). If the internal table has neither of them, a [secondary table key](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensecondary_table_key_glosry.htm "Glossary Entry") must be available and specified. | +| `EXCEPT` | The specification of `EXCEPT` means that those lines of the existing table are used that do not meet the condition specified in the `WHERE` clause. Hence, if `EXCEPT` is not specified, the lines of the existing table are used that meet the condition. | + + +Examples: +``` abap +DATA(f1) = FILTER #( tab WHERE comp > 5 ). + +DATA(f2) = FILTER #( tab EXCEPT WHERE comp < 3 ). + +DATA(f3) = FILTER #( tab USING KEY x WHERE comp = 4 ). + +"Filtering based on another table +DATA(f3) = FILTER #( tab IN filter_tab +                     WHERE comp = 3 ). +``` + +

(back to top)

+ +## `REDUCE` + +- The + [`REDUCE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_reduce.htm) + operator creates a result of a specified or derived type from one or + more [iteration + expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeniteration_expression_glosry.htm "Glossary Entry"). +- It basically reduces sets of data objects to a single data object. + For example, the numeric values of a table column are summed up. As + a result, the total number is constructed. + +The following example calculates the total of the numbers from 1 to 10 +using the `REDUCE` operator: +``` abap +DATA(sum) = REDUCE i( INIT s = 0 +                      FOR  i = 1 UNTIL i > 10 +                      NEXT s += i ) ). "sum: 55 +``` + +> **💡 Note**
+> - `INIT ...`: A temporary variable is specified that sets an + initial value for the result variable. +>- `FOR ...`: Represents a loop. The loop is carried out until + the condition is met after `UNTIL`. +>- `NEXT ...`: Represents the assignment to the temporary + variable after every iteration. +>- Once the loop has finished, the target variable is assigned the + resulting value. + +

(back to top)

+ +## Iteration Expressions with `FOR` + +- Using [iteration + expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeniteration_expression_glosry.htm "Glossary Entry") + with the language element + [`FOR`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfor.htm), + you can carry out [conditional + iterations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfor_conditional.htm) + (including the ABAP words `UNTIL` and `WHILE` which + have the semantics of ABAP statements + [`DO`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapdo.htm) + and + [`WHILE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapwhile.htm)) + or [table + iterations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentable_iteration_glosry.htm "Glossary Entry") + (having the semantics of [[LOOP + AT]](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaploop_at_itab_variants.htm); + the expressions include the ABAP word `IN`). +- Such expressions are possible in the following contexts: + - `REDUCE`: The reduction result is created in the + iteration steps. + - `NEW` and `VALUE`: Used in the context of + looping across internal tables. New table lines are created in + the iteration steps and inserted into a target table. + +`FOR ... WHILE`: The following example with `REDUCE` +has the same effect as the example using `UNTIL` shown above. + +``` abap +DATA(sum) = REDUCE i( INIT y = 0 +                      FOR n = 1 THEN n + 1 WHILE n < 11 +                      NEXT y += n ). +``` + +`FOR ... UNTIL`: See the example in the `REDUCE` +section. + +`FOR ... IN`: + +- The operand specified after `FOR` represents an iteration + variable, i. e. a [work + area](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwork_area_glosry.htm "Glossary Entry") + that contains the data while looping across the table. +- This variable is only visible within the `FOR` + expression, i. e. it cannot be used outside of the expression. +- The type of the variable is determined by the type of the internal + table specified after `IN`. +- One or more iteration expressions can be specified using + `FOR`. +- The components or the whole table line that is to be returned are + specified within the pair of parentheses before the closing + parenthesis. +- In contrast to `LOOP` statements, the sequential processing + cannot be debugged. + +Some examples for looping across tables and storing values in target +tables: +``` abap +"Looping across table and storing the whole line in a new table; +"the target table must have the same table type as the source table itab; +"without the WHERE condition, all lines are respected + +TYPES t_type LIKE itab. + +... = VALUE t_type( FOR wa IN itab +                    "WHERE ( comp1 > 2 ) +                    ( wa ) ). + +"Storing specific components having different names by specifying the assignment +"individually; assumption: the target type is not compatible to the type of itab; +"a field mapping is provided; pay attention to potential type conversion + +... = VALUE t_type( FOR wa IN itab +                    "WHERE ( comp1 > 2 ) +                    ( compX = wa-comp1 +                      compY = wa-comp2 ) ). + +"Storing specific components having the same names; +"assumption: Target type is not compatible to the type of itab; +"if there are identically named components in the table types, you might +"also use CORRESPONDING + +... = VALUE t_type( FOR wa IN itab +                    "WHERE ( comp1 > 2 ) +                    ( CORRESPONDING #( wa ) ) ). + +"Multiple iteration expressions + +... = VALUE t_type( FOR wa1 IN itab1 WHERE ( comp1 = 4 ) +                    FOR wa2 IN itab2 WHERE ( comp2 > 5 ) +                    FOR wa3 IN itab3 WHERE ( comp3 < 3 ) +                    ( compX = wa1-comp1 +                      compY = wa2-comp2 +                      compZ = wa3-comp3 ) ). +``` + +

(back to top)

+ +## `LET Expressions` + +- [`LET`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaplet.htm) + expressions allow you to declare local helper fields (variables or + fields symbols) and assign values (the type is derived from the + defined value) to be used in constructor expressions, for example, + in iteration expressions using `FOR` or results specified in + the conditional expressions of `COND` and `SWITCH`. +- Note that the helper field is only valid in the context in which the + `LET` expression is specified. + +Examples: +``` abap +"Creating a string table using a LET expression + +DATA(str_tab) = VALUE string_table( LET it = `be` IN +                    ( |To { it } is to do| ) +                    ( |To { it } or not to { it }| ) +                    ( |To do is to { it }| ) +                    ( |Do { it } do { it } do| ) ). + +"Conditional expressions + +DATA(a) = COND #( LET b = c IN +                  WHEN b > x THEN ... +                  WHEN b < y THEN ... +                  ... +                  ELSE ... ). +``` + +

(back to top)

+ +## Executable Example +[zcl_demo_abap_constructor_expr](./src/zcl_demo_abap_constructor_expr.clas.abap) + +Note the steps outlined [here](README.md#🎬-getting-started-with-the-examples) about how to import and run the code. diff --git a/06_Dynamic_Programming.md b/06_Dynamic_Programming.md new file mode 100644 index 0000000..482aa6d --- /dev/null +++ b/06_Dynamic_Programming.md @@ -0,0 +1,866 @@ + + +# Dynamic Programming + + +- [Dynamic Programming](#dynamic-programming) + - [Notes on "Dynamic"](#notes-on-dynamic) + - [Excursion: Field Symbols and Data References](#excursion-field-symbols-and-data-references) + - [Field Symbols](#field-symbols) + - [Data References](#data-references) + - [Dynamic ABAP Statements](#dynamic-abap-statements) + - [Runtime Type Services (RTTS)](#runtime-type-services-rtts) + - [Further Information](#further-information) + - [Executable Example](#executable-example) + +## Notes on "Dynamic" + +Some considerations regarding "dynamic" in contrast to "static" aspects: + +- 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 program that does not work with a specific kind of table + but should be able to work with any kind of table, for example, a + user must input the table name first in a UI. The tables to be used + in the program certainly have different properties, line types, + field names, number of rows, and so on. Nevertheless, the program + must be able to work with all of them, 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. + +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: + +- 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 + +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. + +**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. + +> **💡 Note**
+>- 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 + topic [Generic ABAP + Types](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenbuilt_in_types_generic.htm). +>- Field symbols cannot be declared in the declaration part of + [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. + +Syntax: +``` abap +"Data objects. +DATA: number TYPE i, + struc TYPE sflight, + tab TYPE string_table. + +"Field symbols with complete types +FIELD-SYMBOLS: TYPE i, + TYPE sflight, + TYPE string_table. + +"Generic type +FIELD-SYMBOLS TYPE data. + +"Assigning data objects to field symbols +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 + +"You can also assign a particular component of a structure. +"Second component of the structure +ASSIGN COMPONENT 2 OF STRUCTURE struc TO . + +ASSIGN COMPONENT 'CARRID' OF STRUCTURE struc TO . +``` + +> **💡 Note**
+> - When working with field symbols, you should make sure that they are assigned. Otherwise, a runtime error occurs. You can check the + assignment with the following logical expression. The statement is true if the field symbol is assigned. +> ``` abap +> 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. +> ``` 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. +> ``` + +**Using field symbols** + +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 +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. + +``` abap +SELECT * FROM zdemo_abap_fli + INTO TABLE @DATA(itab). + +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. + ... +ENDLOOP. + +"Inline declaration of field symbol +LOOP AT itab ASSIGNING FIELD-SYMBOL(). + -carrid = ... + -connid = ... + ... +ENDLOOP. +``` + +

(back to top)

+ +### Data References + +[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 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") +... + +- 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). + +> **💡 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). + +**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 +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. +``` abap +"Declaring a data object + +DATA num TYPE i VALUE 5. + +"Declaring data reference variables + +DATA ref1 TYPE REF TO i. +DATA ref_gen TYPE REF TO data. + +"Creating data references to data objects. +"The # sign means that the type is derived from the context. + +ref1 = REF #( num ). +ref_gen = REF #( num ). + +"You can also use inline declarations to omit the explicit declaration. + +DATA(ref2) = REF #( num ). + +"You can explicitly specify the data type after REF. + +DATA(ref3) = REF string( `hallo` ). + +"GET REFERENCE OF; do not use anymore +"GET REFERENCE OF num INTO ref1. +"GET REFERENCE OF num INTO DATA(ref4). +``` + +**Creating new data objects at runtime**: You create a [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 +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 + +DATA ref1 TYPE REF TO i. "Complete type +DATA ref_gen TYPE REF TO data. "Generic type + +"Creating anonymous data objects +"Using the # sign and the explicit type: see REF #( ) above. + +ref1 = NEW #( ). +ref_gen = NEW string( ). + +"For directly assigning values, insert the values within the parentheses. + +ref1 = NEW #( 123 ). + +"Using inline declarations to omit a prior declaration of a variable. + +DATA(ref2) = NEW i( 456 ). + +TYPES i_table TYPE STANDARD TABLE OF i WITH EMPTY KEY. + +DATA(ref3) = NEW i_table( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 5 ) ). + +"Older syntax. + +DATA ref4 TYPE REF TO string. +DATA ref5 TYPE REF TO data. + +CREATE DATA ref4. + +"Note: TYPE ... needed because of generic type data +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. + +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) +or the older +[`?=`](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). + +The following example demonstrates up- and downcasts with the assignment +of data reference variables typed with a complete and generic data type: + +Syntax: + +``` abap +"Declaring data reference variables + +DATA ref1 TYPE REF TO i. + +DATA ref2 TYPE REF TO i. + +ref1 = NEW #( 789 ). + +"Copying data reference +ref2 = ref1. + +"Casting + +"Complete type +DATA(ref3) = NEW i( 321 ). + +"Generic type +DATA ref4 TYPE REF TO data. + +"Upcast +ref4 = ref3. + +"Downcasts +DATA ref5 TYPE REF TO i. + +"Generic type +DATA ref6 TYPE REF TO data. + +ref6 = NEW i( 654 ). +ref5 = CAST #( ref6 ). + +"Alternative syntax to the CAST operator +ref5 ?= ref6. +``` + +**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](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: + +``` abap +"Creating data reference variables and assign values + +DATA(ref_i) = NEW i( 1 ). +DATA(ref_carr) = NEW zdemo_abap_carr( carrid = 'LH' carrname = 'Lufthansa' ). + +"Generic type + +DATA ref_gen TYPE REF TO data. +ref_gen = ref_i. "Copying reference + +"Accessing + +"Variable number receives the content. +DATA(number) = ref_i->*. + +"Content of referenced data object is changed. +ref_i->* = 10. + +"Data reference used in a logical expression. +IF ref_i->* > 5. + ... +ENDIF. + +"Dereferenced generic type +DATA(calc) = 1 + ref_gen->*. + +"Structure +"Complete structure +DATA(struc) = ref_carr->*. + +"Individual component +DATA(carrid) = ref_carr->carrid. +ref_carr->carrid = 'UA'. + +"This syntax also works but it is less comfortable. +ref_carr->*-carrname = 'United Airlines'. +``` + +> **💡 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. +> ``` + +**Using data references** + +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. +``` abap +ref = NEW i( 1 ). +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. + +``` abap +DATA: ref TYPE REF TO data, + itab TYPE TABLE OF REF TO data, + number TYPE i VALUE 0. + +DO 3 TIMES. + "Adding up 1 to demonstrate a changed data object. + number += 1. + + "Creating data reference and assigning value. + "In the course of the loop, the variable gets overwritten. + ref = NEW i( number ). + + "Adding the reference to itab + itab = VALUE #( BASE itab ( ref ) ). +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. + +``` abap +"Fill an internal table. +SELECT * FROM zdemo_abap_fli + INTO TABLE @DATA(fli_tab). + +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. +``` abap +"Structure + +DATA: BEGIN OF struc, + num TYPE i, + ref TYPE REF TO i, + END OF struc. + +"Some value assignment + +struc2 = VALUE #( num = 1 ref = NEW #( 2 ) ). + +"Internal table + +DATA itab LIKE TABLE OF struc WITH EMPTY KEY. + +"Some value assignment in the first table line +"assuming the table is filled and a line is available. + +itab[ 1 ]-ref->* = 123. +``` + +> **✔️ Hint**
+> The question might now arise when to actually use either a field symbol +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"). + +

(back to top)

+ +## Dynamic ABAP Statements + +Dynamic aspects come particularly into 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. + +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. + +You can make use of the following dynamic token specification options: + +1. **Dynamic specification of data objects and fields** + +The names of data objects and fields are determined at runtime. + +Examples: +``` abap +"The sorting is done by a field that is determined at runtime. + +SORT itab BY (field_name). + +"A field symbol is assigned a data object; here, an attribute of a class + +ASSIGN class=>(attribute_name) TO FIELD-SYMBOL(). +``` + +2. **Dynamic specification of types** + +The name of a data or object type is determined at runtime. + +Examples: +``` abap +"Anonymous data objects are created using a type determined at runtime. +"Note that the NEW operator cannot be used here! + +CREATE DATA ref TYPE (some_type). +CREATE DATA ref TYPE TABLE OF (some_type). + +"Assigning a data object to a field symbol casting a type + +ASSIGN dobj TO CASTING TYPE (some_type). + +"Assigning a structure component dynamically to a field symbol that is declared inline + +DATA struct TYPE zdemo_abap_flsch. + +ASSIGN struct-('CARRID') TO FIELD-SYMBOL(). +``` + +3. **Dynamic specification of clauses in ABAP SQL statements** + +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. + +Examples: +``` abap +"Dynamic SELECT list + +DATA(select_list) = `CARRID, CONNID, COUNTRYFR, COUNTRYTO`. + +SELECT (select_list) + FROM zdemo_abap_fli + INTO TABLE @itab. + +"Dynamic FROM clause + +DATA(table) = `ZDEMO_ABAP_FLI`. + +SELECT * + FROM (table) + INTO TABLE @itab. + +"Dynamic WHERE clause +DATA(where_clause) = `CARRID = 'LH'`. + +SELECT * + FROM zdemo_abap_fli + WHERE (where_clause) INTO TABLE @itab. +``` + +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")** + +Names are specified dynamically, e. g. the names of classes and methods. + +Examples: +``` abap +"Dynamic method calls +"Note that these calls require a CALL METHOD statement. + +"Method dynamically specified. +CALL METHOD class=>(meth). + +"Class dynamically specified. +CALL METHOD (class)=>meth. + +"Class and method dynamically specified. +CALL METHOD (class)=>(meth). + +"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"). + +

(back to top)

+ +## Runtime Type Services (RTTS) + +[RTTS](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrun_time_type_services_glosry.htm "Glossary Entry") +represent a hierarchy of [type description +classes](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentype_class_glosry.htm "Glossary Entry") +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 + +- 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") + at runtime. +- define and create new data types at runtime. + +The hierarchy of type description classes is as follows. + +
+CL_ABAP_TYPEDESCR
+  |
+  |--CL_ABAP_DATADESCR
+  |   |
+  |   |--CL_ABAP_ELEMDESCR
+  |   |   |
+  |   |   |--CL_ABAP_ENUMDESCR
+  |   |
+  |   |--CL_ABAP_REFDESCR
+  |   |--CL_ABAP_COMPLEXDESCR
+  |       |
+  |       |--CL_ABAP_STRUCTDESCR
+  |       |--CL_ABAP_TABLEDESCR
+  |
+  |--CL_ABAP_OBJECTDESCR
+     |
+     |--CL_ABAP_CLASSDESCR
+     |--CL_ABAP_INTFDESCR
+
+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 +[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"). +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 +F2 help information in +[ADT](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenadt_glosry.htm "Glossary Entry"), +for more details. + +The following examples show the retrieval of information. Instead of the +cumbersome extra declaration of data reference variables, you can use +[inline +declarations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninline_declaration_glosry.htm "Glossary Entry"). +[Method +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. + +DATA(some_type) = cl_abap_typedescr=>describe_by_data( var ). + +"The components of a structure are retrieved. +"Like above, the describe_by_data method is used together with a variable. + +DATA(components) = CAST cl_abap_structdescr( + cl_abap_typedescr=>describe_by_data( some_struc ) + )->components. + +"The attributes of a global class are retrieved. In contrast to the +"example above the describe_by_name method is used together with the actual name. + +DATA(attributes) = CAST cl_abap_classdescr( + cl_abap_classdescr=>describe_by_name( 'CL_SOME_CLASS' ) + )->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. + +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. + +``` abap +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(key_tab) = VALUE abap_keydescr_tab( ( name = 'CARRID' ) + ( name = 'CARRNAME' ) ). + +"Creating internal table type + +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 + +DATA ref_tab TYPE REF TO data. + +CREATE DATA ref_tab TYPE HANDLE table_type. +``` + +

(back to top)

+ +## 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) + +## 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. diff --git a/07_String_Processing.md b/07_String_Processing.md new file mode 100644 index 0000000..5eb331c --- /dev/null +++ b/07_String_Processing.md @@ -0,0 +1,1178 @@ + + +# String Processing + +- [String Processing](#string-processing) + - [Introduction](#introduction) + - [Variable Length and Fixed Length Character Strings](#variable-length-and-fixed-length-character-strings) + - [Declaring Character-Like Data Types and Objects](#declaring-character-like-data-types-and-objects) + - [Assigning Values](#assigning-values) + - [String Templates](#string-templates) + - [Determining the Length of Strings](#determining-the-length-of-strings) + - [Concatenating Strings](#concatenating-strings) + - [Splitting Strings](#splitting-strings) + - [Modifying Strings](#modifying-strings) + - [Processing Substrings](#processing-substrings) + - [Searching and Replacing](#searching-and-replacing) + - [Searching for Specific Characters](#searching-for-specific-characters) + - [Replacing Specific Characters in Strings](#replacing-specific-characters-in-strings) + - [Searching for Substrings in Strings](#searching-for-substrings-in-strings) + - [Replacing Substrings in Strings](#replacing-substrings-in-strings) + - [Pattern-Based Searching and Replacing in Strings](#pattern-based-searching-and-replacing-in-strings) + - [Simple Pattern-Based Searching Using Comparison Operators](#simple-pattern-based-searching-using-comparison-operators) + - [Complex Searching and Replacing Using Regular Expressions](#complex-searching-and-replacing-using-regular-expressions) + - [Searching Using Regular Expressions](#searching-using-regular-expressions) + - [Replacing Using Regular Expressions](#replacing-using-regular-expressions) + - [Executable Example](#executable-example) + + +## Introduction + +ABAP offers plenty of options for processing [character +strings](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencharacter_string_glosry.htm "Glossary Entry"). +The options include ABAP statements (e. g. +[`FIND`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapfind.htm)), +[character string +expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstring_expression_glosry.htm "Glossary Entry") +(e. g. [string +templates](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstring_template_glosry.htm "Glossary Entry")) +and built-in [string +functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstring_function_glosry.htm "Glossary Entry") +(e. g. +[`strlen`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlength_functions.htm)). + +> **💡 Note**
+>- Expressions and string functions can help make your ABAP code more + concise and straightforward. For example, string operations can be + done directly in [operand + position](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenoperand_position_glosry.htm "Glossary Entry") + and, thus, you can avoid temporary variables. +>- In ABAP statements, modify operations on strings are frequently done + on the source field which is the target field at the same time. + String functions never modify the source field. Instead, the + modified string is provided as a [return + value](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenreturn_value_glosry.htm "Glossary Entry"). +>- In most cases, string functions offer the same functionality as the + corresponding ABAP statements. The return value of string functions + that return character strings is always of type `string`. + +

(back to top)

+ +### Variable Length and Fixed Length Character Strings + +Built-in character-like types in ABAP are as follows: + +| Type | Length | Value Range | Initial Value | +|---|---|---|---| +| `string` | Variable, i. e. the length of [data objects](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_object_glosry.htm "Glossary Entry") of this type can change during the execution during the execution of ABAP programs (hence, they are also referred to as [dynamic data objects](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendynamic_data_object_glosry.htm "Glossary Entry")); no standard length | Any characters | Empty string with length 0 | +| `c` | Data objects of this type contain a string of fixed length (between 1 and 262143 characters); standard length: 1 | Any characters | blank for every position | +| `n` | Same as for c | Any characters; valid values are only the digits 0 to 9.

Note that the restrictions for this type to only accept digits are not enforced, hence, fields may contain invalid data. The type is especially used for digits that are not meant for arithmetic calculations like zip codes or article numbers. | "0" for every position | + +> **💡 Note**
+> [Byte-like data +types](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenbyte_like_data_typ_glosry.htm "Glossary Entry") +(`x`, `xstring`) and types that are specializations of +the type `c` (`n`, `d` referring to date and +`t` referring to time) are not covered in this cheat sheet. + +> **⚡ Differences between variable length and fixed length strings**
+>- **Initial value**: The initial value of variable length strings is an + empty string with length 0. Fixed length strings have a standard + length of 1 character. +>- **Performance**: Data objects of both type `c` and + `string` are considered as [elementary data + objects](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenelementary_data_object_glosry.htm "Glossary Entry"). + However, data objects of type `string` are internally + managed as + [references](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenreference_glosry.htm "Glossary Entry") + and are, thus, considered as + [deep](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendeep_glosry.htm "Glossary Entry"). + This fact enables the performance boosting concept of + [sharing](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensharing_glosry.htm "Glossary Entry") + for data objects of this type when making value assignments. +>- **Length**: Theoretically, a variable length string can use up to 2 GB. + The maximum length of a fixed length string is 262143 characters. +>- **Handling trailing blanks**: Usually, the [operand + position](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenoperand_position_glosry.htm "Glossary Entry") + determines whether trailing blanks are respected or not. Fixed + length strings usually ignore trailing blanks; variable length + strings respect them. For example, if a fixed length string is + assigned to a variable length string, the target string does not + contain any trailing blanks. Note in this context the section + *Condensing Strings*. +>- **Flexibility**: Variable length strings are more flexible than fixed + length strings because you can easily shorten or extend them without + worrying that, for example, parts of the character string are + truncated when processing. +>- **Specifying values** for character strings in the ABAP source code + ([literals](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenliteral_glosry.htm "Glossary Entry")) + with the types `c` and `string`: With quotes + (`'...'`), you create [text field + literals](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentext_field_literal_glosry.htm "Glossary Entry") + of type `c`, with backquotes (\`...\`), you create [text + string + literals](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentext_string_literal_glosry.htm "Glossary Entry") + of type `string`. + +So, when to actually use what? Fixed length strings make sense when +actually determining a maximum or mandatory length, e. g. a country code +that must consist of a maximum of two characters or input fields in +forms that should not exceed a certain length. If restricting a string +is not relevant, variable length strings are a good choice. + +

(back to top)

+ +## Declaring Character-Like Data Types and Objects + +Character-like data types and objects are declared like other types and +objects using +[`TYPES`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaptypes.htm) +and +[`DATA`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapdata.htm) +statements. This cheat sheet focuses on the built-in types `c` +and especially on `string` in most examples. + +Syntax examples: +``` abap +"Type declarations using built-in types + +TYPES: c_type TYPE c LENGTH 3, "Explicit length specification + str_type TYPE string. + +"Data object declarations using built-in, local and DDIC types + +DATA: flag TYPE c LENGTH 1, "Built-in type + str1 TYPE string, "Built-in type + char1 TYPE c_type, "Local type + str2 LIKE str1, "Deriving type from a local data object + str3 TYPE str_type, "Local type + char2 TYPE s_toairp, "DDIC type (used e. g. for a field in a demo table) + char3 TYPE spfli-carrid. "Using the type of a DDIC table component + +"You might also stumble upon declarations with type c and the length +"specified in parentheses. It is not recommended so as not to confuse +"with the use of parentheses in dynamic programming. + +DATA char(4) TYPE c. +``` + +

(back to top)

+ +## Assigning Values + +When declaring character-like data objects, you can directly provide +default values. For the assignment of values, you can, for example, use +the [assignment +operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenassignment_operator_glosry.htm "Glossary Entry") +`=`. To do both data object declaration and value assignment in +one go, you can make use of [inline +declaration](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_inline.htm) +that supports declarations in [write +positions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwrite_position_glosry.htm "Glossary Entry"). +In doing so, a variable specified in parentheses preceded by +`DATA` on the left side of the assignment operator automatically +derives a data type from the operand on the right. This helps make your +programs leaner. + +Syntax examples: +``` abap +"Data object declarations including default values with VALUE +DATA: flag TYPE c LENGTH 1 VALUE 'X', +      str1 TYPE string VALUE `Hallo!`. + +"Examples for type n +DATA zip_code TYPE n LENGTH 5 VALUE '12345'. +DATA isbn_number TYPE n LENGTH 13 VALUE '1234567890123'. + +"More data object declarations +DATA: char1  TYPE c LENGTH 5, +      html   TYPE string, +      str2   LIKE html. + +"Value assignments +char1 = 'ab123'. +html  = `

hallo

`. + +"Escaping backquotes in text string literals with another one +str1  = `This is a backquote: ``.`. + +"If possible, avoid unnecessary type conversion; in principle, every +"convertible type can be specified +str2 = 'abc'. "Fixed length string assigned to data object of type string +DATA str3 TYPE string VALUE 'X'. "type c length 1 +DATA str4 TYPE string VALUE -1. "type i + +"Inline declarations +DATA(char2) = 'abcd'. "Type c length 4 +DATA(str5)  = `efgh`. + +"Since char2 is of type c length 4 (the length is also derived), +"characters are truncated in the following example assignment +char2 = 'ijklmnopq'. "ijkl + +"Trailing blanks after assigning fixed length to variable length string +DATA(char3) = 'ab   '. +DATA(str6)  = `cdefgh`. +str6 = char3. "'ab' (trailing blanks are not respected due to conversion rule) +``` + +When assigning strings, not only data objects can be placed on the right +side. Various expressions and strings can be chained using the +[concatenation +operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconcatenation_operator_glosry.htm "Glossary Entry") +`&&`. Alternatively, you might chain strings using [string +templates](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstring_template_glosry.htm "Glossary Entry") +and as outlined in the section [Concatenating Strings]. `Concatenating Strings`. +``` abap +str5 = str3 && ` ` && str4 && `!`. "X 1-! +"Note the output for str4 that includes the conversion of type i to +"string above demonstrating a possibly inadvertent specification +"of an integer value for str4 that is of type string. +``` + +Note that there is also the [literal +operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenliteral_operator_glosry.htm "Glossary Entry") +`&` that joins [text string +literals](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentext_string_literal_glosry.htm "Glossary Entry"), +however, with [significant +differences](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenliteral_operator.htm) +to `&&`. + +

(back to top)

+ +## String Templates +Using string templates, you can construct strings very elegantly from +literal text and - which is the primary use case - by also including +embedded ABAP +[expression](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenexpression_glosry.htm "Glossary Entry") +within a pair of delimiter characters: `|...|` if these expressions can be converted to `string`. To embed expressions, you include them within curly brackets: `{ ... }`. + +> **💡 Note**
+> String templates form a [string +expression](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstring_expression_glosry.htm "Glossary Entry") +that is compiled at runtime. Hence, a string template that only contains +literal text is handled like an expression which has impact on the +performance. In such a case, using a text string literal with backquotes +is preferable. + +Syntax examples: +``` abap +"Value assignment with string templates +"The expression must be convertible to a string. A blank (not within the curly brackets) +"means a blank in the resulting string. +DATA(s1) = |Hallo { cl_abap_context_info=>get_user_technical_name( ) }!|. + +DATA(s2) = `How are you?`. "Literal text only with backquotes +DATA(s3) = |{ s1 } { s2 }|. "Hallo ...! How are you? + +"Chaining of string templates using && +DATA(s4) = |{ s1 }| && ` ` && |{ s2 }|. "Hallo ...! How are you? +``` + +String templates interpret certain character combinations as [control +characters](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstring_templates_separators.htm). +For example, `n` is interpreted as line feed. A new line is +set. Plus, string templates support various [formatting +options](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcompute_string_format_options.htm). +Check the ABAP Keyword Documentation for all options. + +Syntax examples: +``` abap +"Control characters +s4 = |{ s1 }\n{ s2 }\nSee you.|. "\n is interpreted as a line feed + +"Various formatting options +"Time and date +"Formatting according to the user master data +DATA(d) = |The date is { cl_abap_context_info=>get_system_date( ) DATE = USER }.|. + +"Formatting in accordance with ISO 8601 +DATA(tm) = |The time is { cl_abap_context_info=>get_system_time( ) TIME = ISO }.|. + +"Formatting to UTC; date and time are represented in ISO 8601 +DATA(ts) = |Timestamp: { utclong_current( ) TIMESTAMP = SPACE }.|. + +"Lowercase and uppercase +s1 = |AbCdEfG|. +s2 = |{ s1 CASE = LOWER }|. "abcdefg +s2 = |{ s1 CASE = UPPER }|. "ABCDEFG + +"Width and alignment +s1 = `##`. +s2 = |{ s1 WIDTH = 10 ALIGN = LEFT }<---|. "'## <---' +s2 = |{ s1 WIDTH = 10 ALIGN = CENTER }<---|. "' ## <---' + +"PAD: Used to pad any surplus places in the result with the specified character. +s2 = |{ s1 WIDTH = 10 ALIGN = RIGHT PAD = `.` }<---|. "'........##<---' + +"Numbers +s1 = |{ CONV decfloat34( - 1 / 3 ) DECIMALS = 3 }|. "'-0.333' +``` + +> **💡 Note**
+> Escape `|{}` in string templates using `\`, i. e. `\\` means `\`. + +

(back to top)

+ +## Determining the Length of Strings + +To determine the length of a string you can use the string function +[`strlen`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlength_functions.htm). +Note that the result depends on the type of the string, i. e. the result +for a data object of type `string` includes trailing blanks. A +fixed length string does not include them. To exclude trailing blanks in +all cases irrespective of the data type, you can use the built-in +function +[`numofchar`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlength_functions.htm). + +Syntax examples: +``` abap +"strlen +DATA(len_c) = strlen( 'abc ' ). "3 +DATA(len_str) = strlen( `abc ` ). "6 + +"numofchar +len_c = numofchar( 'abc ' ). "3 +len_str = numofchar( `abc ` ). "3 +``` + +

(back to top)

+ +## Concatenating Strings + +Two or more strings can be concatenated using the concatenation operator +`&&` and string templates. Alternatively, you can use +[`CONCATENATE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapconcatenate.htm) +statements. It is also possible to concatenate lines of internal tables +into a string to avoid a loop. In a more modern way, you can make use of +the string function +[`concat_lines_of`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconcatenation_functions.htm). + +Syntax examples: +``` abap +"&& and string template +s1 = `AB` && `AP`. "ABAP +s2 = `ab` && `ap` && ` ` && s1. "abap ABAP +s3 = |{ s1 }. { s2 }!|. "ABAP. abap ABAP! + +"CONCATENATE statements +CONCATENATE s1 s2 INTO s3. "ABAPabap ABAP + +"Multiple data objects and target declared inline +CONCATENATE s1 ` ` s2 INTO DATA(s5). "ABAP abap ABAP + +CONCATENATE s1 s2 s5 INTO DATA(s6). "ABAPabap ABAPABAP abap ABAP + +"You can also add a separation sign using the addition SEPARATED BY +CONCATENATE s1 s2 INTO s3 SEPARATED BY ` `. "ABAP abap ABAP + +CONCATENATE s1 s2 INTO s3 SEPARATED BY `#`. "ABAP#abap ABAP + +"Keeping trailing blanks in the result when concatenating fixed length +"strings. The ones of variable length strings are respected by default. +CONCATENATE 'a ' 'b ' 'c ' INTO DATA(ch) RESPECTING BLANKS. "'a b c ' + +"Concatenating lines of internal tables into a string +CONCATENATE LINES OF itab INTO t SEPARATED BY ` `. + +"Using concat_lines_of +s1 = concat_lines_of( table = itab ). "Without separator +s1 = concat_lines_of( table = itab sep = ` ` ). "With separator +``` + +

(back to top)

+ +## Splitting Strings + +You can use +[`SPLIT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapsplit.htm) +statements to split strings in multiple segments. The result of the +splitting can be stored in separate data objects or internal tables that +have a character-like line type. Note that if the specified targets are +fewer than the segments retrieved by the splitting, the last target is +given the rest of the segments that have not yet been split. If there +are more targets specified, the targets not given a segment are +initialized. Hence, specifying individual targets with `SPLIT` +statements is useful if the number of expected segments is known. +Otherwise, splitting into tables is a good choice. + +If you want to get the value of a specific segment, you can use the +string function +[`segment`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensegment_functions.htm). + +Syntax examples: +``` abap +s1 = `Hallo,world,123`. + +SPLIT s1 AT `,` INTO s2 s3 s4. "s2 = Hallo / s3 = world / s4 = 123 + +"Less data objects than possible splittings +SPLIT s1 AT `,` INTO s2 s3. "s2 = Hallo / s3 = world,123 + +"Splitting into internal table +DATA itab TYPE TABLE OF string. +SPLIT s1 AT ',' INTO TABLE itab. "Strings are added to itab in individual lines without comma + +"String function segment returning the occurrence of a segment +"index parameter: number of segment +s2 = segment( val = s1 index = 2 sep = `,` ). "world +``` + +

(back to top)

+ +## Modifying Strings +**Transforming to Lowercase and Uppercase** + +The string functions +[`to_lower`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencase_functions.htm) +and +[`to_upper`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencase_functions.htm) +transform characters of a string to either lowercase or uppercase and +store the result in a target variable. If the transformation should be +applied to the source directly, you can use +[`TRANSLATE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaptranslate.htm) +statements. + +Syntax examples: +``` abap +"String functions +s1 = to_upper( `abap` ). "ABAP +s1 = to_lower( `SOME_FILE.Txt` ). "some_file.txt + +"TRANSLATE statements +s1 = `Hallo`. +TRANSLATE s1 TO UPPER CASE. "HALLO +TRANSLATE s1 TO LOWER CASE. "hallo +``` + +**Shifting Content** + +You can shift content within a string to a specific position on the left +or right of a string. +[`SHIFT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapshift.htm) +statements have different additions for specific use cases. + +In a more modern way, you can use the string functions +[`shift_left`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenshift_functions.htm) +and +[`shift_right`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenshift_functions.htm) +that store the result in a variable. These functions offer an additional +functionality. The `sub` parameter can be used to specify a +substring. All substrings in the string that match the specified value +in `sub` either on the left or right side of the string are +removed. + +Syntax examples: +``` abap +"SHIFT statements +"Note that all results below refer to s1 = `hallo`. +s1 = `hallo`. "Type string + +SHIFT s1. "No addition; string shifted one place to the left: allo +SHIFT s1 BY 2 PLACES. "Without direction, left by default: llo +SHIFT s1 BY 3 PLACES RIGHT. "With direction, variable length strings are extended: ' hallo' + +"Note that all results below refer to ch4 = 'hallo'. +DATA(ch4) = 'hallo'. "Type c length 5 + +SHIFT ch4 BY 3 PLACES RIGHT. "Fixed length string: ' ha' + +"CIRCULAR addition: characters that are moved out of the string are +"added at the other end again +SHIFT ch4 BY 3 PLACES LEFT CIRCULAR. "lohal +SHIFT ch4 UP TO `ll`. "Shift characters up to a specific character set: llo + +"Deleting leading and trailing characters +s2 = ` hallo `. +s3 = s2. + +SHIFT s2 LEFT DELETING LEADING ` `. "'hallo ' +SHIFT s3 RIGHT DELETING TRAILING ` `. "' hallo' (length is kept) + +"Removing trailing blanks for strings without leading blanks with this sequence +s4 = `hallo `. +SHIFT s4 RIGHT DELETING TRAILING ` `. "' hallo' +SHIFT s4 LEFT DELETING LEADING ` `. "'hallo' + +"String functions with parameters +s1 = `hallo`. + +s2 = shift_left( val = s1 places = 3 ). "lo +s2 = shift_left( val = s1 circular = 2 ). "lloha + +"Note that shift_right does not extend a variable length string. +s2 = shift_right( val = s1 places = 3 ). "ha +s2 = shift_right( val = `abc ` sub = ` ` ). "'abc' +s2 = shift_right( val = `abc ` ). "'abc' (same result as above) +``` + +**Condensing Strings** + +You can use +[`CONDENSE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcondense.htm) +statements or the string function +[`condense`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencondense_functions.htm) +to remove blanks in strings. The advantage of using the string function +is that you can also specify any characters to be removed and not only +blanks. + +Syntax examples: +``` abap +"CONDENSE statements +s1 = ` ab cd `. +s2 = ` ef gh ij `. +s3 = ` kl mn op `. + +CONDENSE s1. "Trailing and leading blanks are removed: 'ab cd' +CONDENSE s2. "It also replaces sequences of multiple blanks with a single blank: 'ef gh ij' +CONDENSE s3 NO-GAPS. "Removes all blanks: 'klmnop' + +"String function condense +s1 = ` ab cd `. + +"No parameters specified, i. e. their default values are provided. +"Works like CONDENSE statement without the NO-GAPS addition. +s2 = condense( s1 ). "ab cd + +"Parameters del/to not specified. from parameter with initial string +"(could also be a text field literal: from = ' '). This way, leading and +"trailing blanks are removed. +s2 = condense( val = s1 from = `` ). "ab cd + +"Parameter to specified with an initial string. No other parameters. +"Works like CONDENSE statement with the NO-GAPS addition. +s2 = condense( val = s1 to = `` ). "abcd + +"Parameter del specifies the leading/trailing characters to be removed. +s2 = condense( val = `##see###you##` del = `#` ). "see###you + +"If from and to are specified along with del, leading/trailing +"characters specified in del are first removed. Then, in the remaining string, all +"substrings composed of characters specified in from are replaced with +"the first character of the string specified in the to parameter +s2 = condense( val = ` Rock'xxx'Roller` del = `re ` + from = `x` to = `n` ). "Rock'n'Roll +``` + +**Reversing Strings** + +The string function +[`reverse`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenreverse_functions.htm) +reverses a string: +``` abap +"Result: 'abap' +s1 = reverse( `paba` ). +``` + +**Inserting Content** + +The string function +[`insert`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninsert_functions.htm) +inserts a substring into any position within a given string. Using +various parameters, you can construct your desired string: + +- `val`: Original string. +- `sub`: Substring. +- `off`: Optionally sets the offset, i. e. the position where to add the substring. The default value is 0. When using the function with the default value, the result is like concatenating a string with `&&` (like `res = sub && text`). + +Syntax examples: +``` abap +"Result: 'abcdefghi' +s1 = insert( val = `abcghi` sub = `def` off = 3 ). + +"Result: 'defabcghi' +s1 = insert( val = `abcghi` sub = `def` ). +``` + +

(back to top)

+ +## Processing Substrings + +Using the string function +[`substring`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensubstring_functions.htm), +you can specify the position (parameter `off`) and the length +(`len`) of a substring that should be extracted from a given +string (`val`). At least one of the two parameters `off` +or `len` must be specified. The default value of `off` +is 0, i. e. when using the default value, the substring is extracted +from the beginning of the string. Not specifying `len` means +that the rest of the remaining characters are respected. If the offset +and length are greater than the actual length of the string, the +exception `CX_SY_RANGE_OUT_OF_BOUNDS` is raised. + +You might also stumble on the syntax for accessing substrings via offset +and length specification using the `+` character after a variable. Here, the +length is specified within parentheses. Providing an asterisk (`*`) means +that the rest of the remaining string is respected. This syntax option +even enables write access on substrings for fixed length strings. Read +access is possible for both fixed length and variable length strings. +However, this syntax might be confused with the use of tokens in the +context of dynamic programming. + +There are further string functions available to deal with substrings, +for example, +[`substring_after`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensubstring_functions.htm), +[`substring_before`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensubstring_functions.htm), +[`substring_from`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensubstring_functions.htm) +and +[`substring_to`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensubstring_functions.htm). + +These functions offer more options in terms of parameters, for example, +a [PCRE regular +expression](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenpcre_regex_glosry.htm "Glossary Entry") +which are dealt with further down. + +Syntax examples: +``` abap +s1 = `Lorem ipsum dolor sit amet`. "Type string + +"Extracting substring starting at a specific position +"'len' not specified means the rest of the remaining characters are +"respected +s2 = substring( val = s1 off = 6 ). "ipsum dolor sit amet + +"Extracting substring with a specific length +"'off' is not specified and has the default value 0. +s2 = substring( val = s1 len = 5 ). "Lorem + +"Specifying both off and len parameters +s2 = substring( val = s1 off = 6 len = 5 ). "ipsum + +DATA(ch5) = 'Lorem ipsum dolor sit amet'. "Type c + +"Offset and length specification using the + sign after a variable +DATA(ch6) = ch2+0(5). "Lorem + +"* means respecting the rest of the remaining string +DATA(ch7) = ch2+12(*). "dolor sit amet + +CLEAR ch5+11(*). "Lorem ipsum + +ch5+0(5) = 'Hallo'. "Hallo ipsum dolor sit amet + +"Further string functions +s1 = `aa1bb2aa3bb4`. + +"Extracting a substring ... +"... after a specified substring +s2 = substring_after( val = s1 sub = `aa` ). "1bb2aa3bb4 (only the first occurrence is respected) + +"... after a specified substring specifying the occurence in a string +"and restricting the length +s2 = substring_after( val = s1 sub = `aA` occ = 2 len = 4 case = abap_false ). "3bb4 + +"... before a specified substring +s2 = substring_before( val = s1 sub = `b2` ). "aa1b + +"... from a specified substring on. It includes the substring specified +"in sub. len/off and other parameters are possible. +s2 = substring_from( val = s1 sub = `a3` ). "a3bb4 + +"... up to a specified substring. It includes the substring specified +"in sub. len/off and other parameters are possible. +s2 = substring_to( val = s1 sub = `3b` ). "aa1bb2aa3b +``` + +

(back to top)

+ +## Searching and Replacing + +In ABAP, there are a lot of options to carry out search and replace +operations with strings. This includes the use of [comparison +operators](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomp_operator_glosry.htm "Glossary Entry") +or the prominent ABAP statements +[`FIND`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapfind.htm) +and +[`REPLACE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapreplace.htm), +or the more modern built-in string functions +[`find`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensearch_functions.htm) +and +[`replace`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenreplace_functions.htm), +among others, with their considerable amount of additions or parameters +respectively. Many of these options support rather simple operations +with respect to only single characters or more complex, pattern-based +operations on character sequences using [PCRE regular +expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenpcre_regex_glosry.htm "Glossary Entry"). + +

(back to top)

+ +### Searching for Specific Characters + +- You can use the [comparison + operators](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomp_operator_glosry.htm "Glossary Entry") + [`CA`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_strings.htm) + (contains any) or its negation + [`NA`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_strings.htm) + (contains not any) in [comparison + expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomparison_expression_glosry.htm "Glossary Entry") + to determine if any character of a given character set is contained + in a string. Such an expression is true if at least one character is + found. + - The search is case-sensitive. + - The system variable `sy-fdpos` contains the offset of + the first found character. If nothing is found, + `sy-fdpos` contains the length of the string. + - Note that offset 0 stands for the very first position. +- The string functions + [`find_any_of`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensearch_functions.htm) + or its negation + [`find_any_not_of`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensearch_functions.htm) + return the offset of the occurrence of any character contained in a + substring. + - If nothing is found, the value -1 is returned + - There are further optional parameters possible. For example, the + specification of `occ` determines the search + direction, i. e. a positive value means the search is performed + from left to right. A negative value means searching from right + to left. +- If the position of characters is not of interest but rather how + often characters occur in a string, you can use the string functions + [`count_any_of`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensearch_functions.htm) + or its negation + [`count_any_not_of`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensearch_functions.htm). +- To determine if a string only contains a specific set of characters, + you can use the comparison operators + [`CO`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_strings.htm) + (contains only) or its negation + [`CN`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_strings.htm) + (contains not only) in comparison expressions. + - Regarding `CO`, a comparison is true if the left operand + only contains characters that are also contained in the right + operand. If the comparison is not true, you can get the position + of the first character from text that is not contained in the + character set via `sy-fdpos`. Regarding `CN`, a + comparison is true if a string not only contains characters from + the character set. + +Syntax examples: +``` abap +s1 = `cheers`. +IF s1 CA `aeiou` ... "true, sy-fdpos = 2 +IF s1 NA `xyz`... "true, sy-fdpos = 6 + +s2 = find_any_of( val = s1 sub = `aeiou` ). "2 +s2 = find_any_not_of( val = s1 sub = `c` ). "1 + +s2 = count_any_of( val = s1 sub = `e` ). "2 +s2 = count_any_not_of( val = s1 sub = `s` ). "5 + +IF s1 CO `rs` ... "not true, sy-fdpos = 0 +IF s1 CN `cheers` ... "not true, sy-fdpos = 6 +``` + +

(back to top)

+ +### Replacing Specific Characters in Strings + +You can use the string function +[`translate`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentranslate_functions.htm) +to replace specific characters by others. Here, the parameter +`from` denotes the characters to be placed in a string and +`to` specifies the target characters. Note: The +replacement is done as follows: Each character specified in +`from` is replaced by the character in `to` that is on +the same position, i. e. the second character in `from` is +replaced by the second character specified in `to`. If there is +no equivalent in `to`, the character in `from` is +removed from the result. + +Using +[`TRANSLATE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaptranslate.htm) +statements, you can carry out replacements directly on the source field. + +Syntax examples: +``` abap +s1 = `___abc_def_____ghi_`. +s2 = translate( val = s1 from = `hi_` to = `##` ). "abcdefg## +s2 = translate( val = s1 from = `_` to = `##` ). "###abc#def#####ghi# + +"TRANSLATE statement. The value after USING is interpreted as a string composed of character pairs. +"Starting with the first pair, a search is performed in text for the +"first character in every pair and each occurrence is replaced with the +"second character of the pair. +TRANSLATE s1 USING `_.a#g+`. "...#bc.def.....+hi. +``` + +

(back to top)

+ +### Searching for Substrings in Strings + +- For simple substring searches, you can use the [comparison + operators](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomp_operator_glosry.htm "Glossary Entry") + [`CS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_strings.htm) + (contains string) or its negation + [`NS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_strings.htm) + (contains no string) in [comparison + expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomparison_expression_glosry.htm "Glossary Entry"). + Here, the search is not case-sensitive. The system variable + `sy-fdpos` contains the offset of the found substring. If + the substring is not found, `sy-fdpos` contains the length + of the searched string. +- For more complex and iterating search operations, it can be + beneficial to use `FIND` statements or the string function + [`find`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensearch_functions.htm). + If you are only interested in the offset of a substring within a + string, the string function offers more options than using the + logical operator, for example, you can specify if the search should + be case-sensitive or not. You can also further restrict the search + using parameters. +- Find out how often a substring occurs using the string function + [`count`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencount_functions.htm). + Special variants are available: + [`count_any_of`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencount_functions.htm), + [`count_any_not_of`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencount_functions.htm). +- Since the string function `find` is derived from the ABAP + statement `FIND`, `FIND` covers the same + functionality as mentioned above and beyond using the many addition + options. + +Syntax examples: +``` abap +s3 = `cheers`. + +IF s3 CS `rs` ... "true, sy-fdpos = 4 + +IF s3 NA `xyz`... "not true, sy-fdpos = 6 + +"String function find +s1 = `Pieces of cakes.`. + +s2 = find( val = s1 sub = `OF` case = abap_false ). "7 + +s2 = find( val = s1 sub = `hallo` ). "-1 (no occurrence returns -1) + +s2 = find( val = s1 sub = `ce` off = 1 len = 7 ). "3 + +"Parameter occ: Positive value means the nth position from the left, +"a negative value the nth position from the right +s2 = find( val = s1 sub = `es` occ = -1 ). "13 + +"String function count +s2 = count( val = s1 sub = `es` ). "2 + +"FIND statements with selected additions +s1 = `abc def ghi abc`. + +FIND `def` IN s1. +IF sy-subrc = 0. "If there is an occurrence, sy-subrc is set to 0. + ... "Some action +ENDIF. +FIND SUBSTRING `abc` IN s1. "Addition SUBSTRING is optional + +FIND `aBC` IN s1 IGNORING CASE. "Case-insensitive search + +"MATCH additions can be specified individually or combined +FIND ALL OCCURRENCES OF `abc` IN s1 + MATCH COUNT DATA(fcnt) "2 (number of occurrences) + MATCH OFFSET DATA(foff) "12 (offset of last occurrence) + MATCH LENGTH DATA(flen). "3 (length of last occurrence) + +"Finding the first occurrence +FIND FIRST OCCURRENCE OF `abc` IN s1 MATCH OFFSET foff. "0 + +"Returning all of these pieces of information in a table for all occurrences +FIND ALL OCCURRENCES OF `abc` IN s1 RESULTS DATA(fres). + +"Restricting the search area (OFFSET/LENGTH can be specified individually) +FIND `abc` IN SECTION OFFSET 4 LENGTH 11 OF s1 + MATCH OFFSET foff. "12 + +"Searching in internal tables; search results are returned in an internal table +DATA(str_table) = VALUE string_table( ( `ZxZ` ) ( `yZ` ) ( `Zz` ) ). + +FIND ALL OCCURRENCES OF `Z` IN TABLE str_table RESULTS + DATA(findings) RESPECTING CASE. +``` + +

(back to top)

+ +### Replacing Substrings in Strings + +Using the string function +[`replace`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenreplace_functions.htm), +you can store the result of a substring replacement in a separate +variable. What makes it very powerful in particular is the fact that it +returns a value and can, thus, be used in almost all [read +positions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenread_position_glosry.htm "Glossary Entry"). +Using +[`REPLACE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapreplace.htm) +statements, you can carry out replacements on strings directly +(including substrings, which is not possible with the string function). +Regarding the multiple additions `REPLACE` offers, +`REPLACE` is syntactically similar to `FIND`. Regarding +the parameters of `replace`: +- `with`: The replacement text. +- `sub`: Specifies the substring to be replaced. +- `case`: Sets the case sensitivity. +- `occ`: Specifies the number of occurrences of a substring. The default value is `1`, i. e. the first occurrence starting from the left. Setting `occ` to `0` means that all occurrences are respected for the replacement. + +Syntax examples: +``` abap +s1 = `abc def ghi abc`. + +s2 = replace( val = s1 sub = `def` with = `###` ). "abc ### ghi abc + +s2 = replace( val = s1 sub = `ABC` with = `###` case = abap_false occ = 2 ). "abc def ghi ### + +s2 = replace( val = s1 sub = `abc` with = `###` occ = 0 ). "### def ghi ### + +"REPLACE statements with selected additions +"Note that all results below refer to s1 = `abc def ghi abc`. +REPLACE `def` IN s1 WITH `###`. "abc ### ghi abc + +REPLACE FIRST OCCURRENCE OF `abc` IN s1 WITH `###`. "### def ghi abc + +REPLACE `abc` IN s1 WITH `###`. "### def ghi abc (first found is replaced) + +REPLACE SUBSTRING `abc` IN s1 WITH `###`. "### def ghi abc (SUBSTRING is optional) + +REPLACE ALL OCCURRENCES OF `abc` IN s1 WITH `###`. "### def ghi ### + +REPLACE `aBC` IN s1 WITH `###` IGNORING CASE. "### def ghi abc + +"REPLACEMENT additions; can be specified individually or combined +REPLACE ALL OCCURRENCES OF `abc` IN s1 WITH `###` "### def ghi ### + REPLACEMENT COUNT DATA(cnt) "2 (number of replacements) + REPLACEMENT OFFSET DATA(off) "12 (offset of last replacement) + REPLACEMENT LENGTH DATA(len). "3 (length of last substring inserted) + +"Returning all of these pieces of information in a table for all replacements +REPLACE ALL OCCURRENCES OF `abc` IN s1 WITH `###` + RESULTS DATA(res). "### def ghi ### + +"Position-based replacement (OFFSET/LENGTH can be specified individually ) +REPLACE SECTION OFFSET 4 LENGTH 7 OF s1 WITH `###`. "abc ### abc + +"Replacements in internal tables +DATA(str_tab) = VALUE string_table( ( `ZxZ` ) ( `yZ` ) ( `Zz` ) ). + +REPLACE ALL OCCURRENCES OF `Z` + IN TABLE str_tab WITH `` + RESPECTING CASE. "x / y / z +``` + +## Pattern-Based Searching and Replacing in Strings + +You can carry out complex search and replace operations based on +patterns. [PCRE regular +expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenpcre_regex_glosry.htm "Glossary Entry") +help you process strings effectively. +> **💡 Note**
+> Do not use [POSIX +regular +expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenposix_regex_glosry.htm "Glossary Entry") +any more since they are obsolete. + +

(back to top)

+ +### Simple Pattern-Based Searching Using Comparison Operators + +For simple patterns, you can use the [comparison +operators](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomp_operator_glosry.htm "Glossary Entry") +[`CP`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_strings.htm) +(conforms to pattern) or its negation +[`NP`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_strings.htm) +(does not conform to pattern) in [comparison +expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomparison_expression_glosry.htm "Glossary Entry") +to determine if a set of characters is contained in a string that +matches a certain pattern. For the patterns, you can use the following +special characters: + +| Special Character | Details | +|---|---| +| `*` | Any character sequence (including blanks). | +| `+` | Any character (only one character, including blanks). | +| `#` | Escape character. The following character is marked for an exact comparison. | + +Patterns are not case-sensitive except for characters marked by +`#`. If a pattern is found, the system variable +`sy-fdpos` returns the offset of the first occurrence. +Otherwise, it contains the length of the searched string. +``` abap +s1 = `abc_def_ghi`. + +"Pattern: f is preceded by any character sequence, must be followed +"by '_' and then followed by any character sequence +IF s1 CP `*f#_*`. ... "true; sy-fdpos = 6 + +"Pattern: 'i' is not followed by another character +IF s1 NP `i+`. ... "true; sy-fdpos = 11 (length of searched string) +``` + +

(back to top)

+ +### Complex Searching and Replacing Using Regular Expressions + +**Excursion: Common Regular Expressions** + +There are various options to carry out complex searching in strings using PCRE expressions. They can be fairly complex. The following overview shows common PCRE expressions with simple examples. + +Characters and character types + +| Expression | Represents | Example | Matches | Does not Match | +|---|---|---|---|---| +| `.` | Any single character | `.` | a, 9, Ä, # | aa, empty | +| `\d` | Any digit (0-9) | `\d` | 1, 3, 7 | A, b, c | +| `\D` | Any character that is not a digit, equivalent to `[^0-9]` | `\D` | D, e, f | 4, 5, 8 | +| `\s` | Any whitespace character such as a blank, tab and new line | `\s`
(example string: hi there) | the blank in between | hi | +| `\S` | Any character that is not a whitespace | `\S`
(example string: a 1) | a, 1 | the blank in between | +| `\w` | Any word character (letter, digit or the underscore), equivalent to `[a-zA-Z0-9_]` | `\w`
(example string: ab 12) | a, b, 1, 2 | the blank in between | +| `\W` | Any character that is not a word character, equivalent to `[^a-zA-Z0-9_]` | `\W`
(example string: cd 34) | the blank in between | c, d, 3, 4 | +| `\...` | To include special characters like [] \ / ^, use `\` to escape them. Use `\.` to match a period ("."). | `\\` | `\` | `/` | + + +Repetitions and Alternatives + +| Expression | Represents | Example | Matches | Does not Match | +|---|---|---|---|---| +| `r*` | Zero or more repetitions of `r` | `ab*` | a, ab, abb, abbb | b, aba | +| `r+` | One or more repetitions of `r` | `ab+` | ab, abb, abbb | a, b, aba | +| `r{m,n}` | Between `m` and `n` repetitions | `a{2,4}` | aa, aaa, aaaa | a, aaaaa, aba | +| `r{m}` | Exactly `m` repetitions | `a{3}` | aaa | a, aa, aaaa, bbb | +| `r?` | Optional `r` | `ab?a` | aa, aba | abba, aca | +| `r\|s` | Matching alternatives, i. e. `r` or `s` | `a+\|b+` | a, b, aa, bb, aaa | ab, aabb | + + +Character Sets, Ranges, Subgroups and Lookarounds +| Expression | Represents | Example | Matches | Does not Match | +|---|---|---|---|---| +| `[aA1-]` | Character set, matches a single character present in the list | `[aA1-]` | a, A, 1, - | b, B, cc, 3 | +| `[a-z0-9]` | Character range, matches a single character in the specified range, note that ranges may be locale-dependent | `[a-c0-5]` | b, c, 2, 4 | d, Z, x, 9 | +| `[^aA1]` | Negation, matches any single character not present in the list | `[^aA1]` | b, C, 3, - | a, A, 1 | +| `[^0-9]` | Negation, matches any single character not within the range | `[^0-9]` | a, B, c | 1, 2, 3 | +| `(...)` | Capturing group to group parts of patterns together | `a(b\|c)a` | aba, aca | aa, abca | +| `(?=...)` | Positive lookahead, returns characters that are followed by a specified pattern without including this pattern | `a(?=b)`
(example string: abc ade) | the first a | the second a | +| `(?!...)` | Negative lookahead, returns characters that are not followed by a specified pattern without including this pattern | `a(?!b)`
(example string: abc ade) | the second a | the first a | +| `(?<=...)` | Positive lookbehind, returns characters that are preceded by a specified pattern without including this pattern | `(?<=\s)c`
(example string: ab c abcd) | the first c since it is preceded by a blank | the second c | +| `(?example string: ab c abcd) | the second c since it is not preceded by a blank | the first c | + +> **💡 Note**
+> Subgroups are handy in replacements. Using an expression with `$` and a number, e. g. `$1`, you can refer to a particular group. For example, you have a string `abcde`. A PCRE expression might be +`(ab|xy)c(d.)`, i. e. there are two subgroups specified within two pairs of parentheses. In a replacement pattern, you can refer to the first group using `$1` and the second group using `$2`. Hence, the replacement pattern `$2Z$1` results in `deZab`. + +Anchors and Positions + +| Expression | Represents | Example | Matches | Does not Match | +|---|---|---|---|---| +| `^` | Beginning of line, alternative: `\A` | `^a.`
(example string: abcde) | ab | bc | +| `$` | End of line, alternative: `\Z` | `$`
(example string: abcde) | the position right after e | any other position | +| `\b` | beginning and end of word | 1) `\ba\d`
2) `\Dd\b`
(example string: abcd a12d) | 1) `a1`
2) `cd` | 1) `ab`
2) `2d` | + +

(back to top)

+ +### Searching Using Regular Expressions + +Multiple string functions support PCRE expressions by offering the +parameter `pcre` with which you can specify such an expression. +`FIND` and `REPLACE` statements support regular +expressions with the `PCRE` addition. + +The string function +[`match`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenmatch_functions.htm) +exclusively works with regular expressions. It returns a substring that +matches a regular expression within a string. For comparisons, you could +also use the [predicate +function](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenpredicate_function_glosry.htm "Glossary Entry") +[`matches`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenmatches_functions.htm) +that returns true or false if a string matches a given pattern or not. + +Syntax examples: +``` abap +s1 = `Cathy's black cat on the mat played with Matt.`. + +"Determining the position of the first occurrence +"Here, the parameter occ is 1 by default. +s2 = find( val = s1 pcre = `at.` ). "1 + +"Determining the number of all occurrences. +"Respects all 'a' characters not followed by 't', all 'at' plus 'att' +s2 = count( val = s1 pcre = `at*` ). "6 + +"Respects all 'at' plus 'att' +s2 = count( val = s1 pcre = `at+` ). "4 + +"Extracting a substring matching a given pattern +s1 = `The email address is jon.doe@email.com.`. +s2 = match( val = s1 + pcre = `\w+(\.\w+)*@(\w+\.)+(\w{2,4})` ). "jon.doe@email.com + +"Predicate function matches +"Checking the validitiy of an email address +s1 = `jon.doe@email.com`. +IF matches( val = s1 + pcre = `\w+(\.\w+)*@(\w+\.)+(\w{2,4})` ). "true +... +ENDIF. + +"Examples with the FIND statement +"Storing submatches in variables. +"Pattern: anything before and after ' on ' +FIND PCRE `(.*)\son\s(.*)` IN s1 IGNORING CASE SUBMATCHES DATA(a) DATA(b). +"a = 'Cathy's black cat' / b = 'the mat played with Matt'. + +"Determinging the number of letters in a string +FIND ALL OCCURRENCES OF PCRE `[A-Za-z]` IN s1 MATCH COUNT DATA(c). "36 + +"Searching in an internal table and retrieving line, offset, length information +DATA(itab) = value string_table( ( `Cathy's black cat on the mat played with the friend of Matt.` ) ). +"Pattern: 't' at the beginning of a word followed by another character +FIND FIRST OCCURRENCE OF PCRE `\bt.` IN TABLE itab + IGNORING CASE MATCH LINE DATA(d) MATCH OFFSET DATA(e) MATCH LENGTH DATA(f). "d: 1, e: 21, f: 2 +``` +

(back to top)

+ +### Replacing Using Regular Expressions + +To carry out replacement operations using regular expressions both +string function `replace` and `REPLACE` statements can +be used with the `pcre` parameter or the `PCRE` addition +respectively. Similar to the `find` function, among others, and +`FIND` statements, the `replace` function and +`REPLACE` statements offer a variety of parameters or additions +respectively to further restrict the area to be replaced. Check the ABAP +Keyword Documentation for a more detailed insight. The executable +example covers numerous PCRE expressions listed above with the +`replace` function. + +Syntax examples: +``` abap +s1 = `ab apppc app`. + +"Replaces 'p' with 2 - 4 repetitions, all occurences +s2 = replace( val = s1 pcre = `p{2,4}` with = `#` occ = 0 ). "ab a#c a# + +"Replaces any single character not present in the list, all occurences) +s2 = replace( val = s1 pcre = `[^ac]` with = `#` occ = 0 ). " "a##a###c#a## + +"Replaces first occurence of a blank +s2 = replace( val = s1 pcre = `\s` with = `#` ). "ab#apppc app + +"Greedy search +"The pattern matches anything before 'p'. The matching is carried out as +"often as possible. Hence, in this example the search stretches until the +"end of the string since 'p' is the final character, i. e. this 'p' and +"anything before is replaced. +s2 = replace( val = s1 pcre = `(.*)p` with = `#` ). "# + +"Non-greedy search (denoted by '?' below) +"The pattern matches anything before 'p'. The matching proceeds until +"the first 'p' is found and does not go beyond. It matches as few as +"possible. Hence, the first found 'p' including the content before +"is replaced. +s2 = replace( val = s1 pcre = `(.*?)p` with = `#` ). "#ppc app + +"Replacements with subgroups +"Replaces 'pp' (case-insensitive here) with '#', the content before and after 'pp' is switched +s2 = replace( val = s1 pcre = `(.*?)PP(.*)` + with = `$2#$1` case = abap_false ). "pc app#ab a + +"Changing the source field directly with a REPLACE statement; same as above +REPLACE PCRE `(.*?)PP(.*)` IN s1 WITH `$2#$1` IGNORING CASE. "pc app#ab a +``` + +

(back to top)

+ +## Executable Example +[zcl_demo_abap_string_proc](./src/zcl_demo_abap_string_proc.clas.abap) + +Note the steps outlined [here](README.md#🎬-getting-started-with-the-examples) about how to import and run the code. diff --git a/08_EML_ABAP_for_RAP.md b/08_EML_ABAP_for_RAP.md new file mode 100644 index 0000000..f245fdd --- /dev/null +++ b/08_EML_ABAP_for_RAP.md @@ -0,0 +1,1183 @@ + + +# ABAP for RAP: Entity Manipulation Language (ABAP EML) + +- [ABAP for RAP: Entity Manipulation Language (ABAP EML)](#abap-for-rap-entity-manipulation-language-abap-eml) + - [Excursion: EML in the Context of RAP](#excursion-eml-in-the-context-of-rap) + - [ABAP Behavior Pools (ABP)](#abap-behavior-pools-abp) + - [RAP Handler Classes and Methods](#rap-handler-classes-and-methods) + - [RAP Saver Class and Saver Methods](#rap-saver-class-and-saver-methods) + - [BDEF Derived Types](#bdef-derived-types) + - [Components of BDEF Derived Types](#components-of-bdef-derived-types) + - [EML Syntax](#eml-syntax) + - [EML Syntax for Modifying Operations](#eml-syntax-for-modifying-operations) + - [EML Syntax for Reading Operations](#eml-syntax-for-reading-operations) + - [Persisting to the Database](#persisting-to-the-database) + - [EML Statements in ABAP Behavior Pools](#eml-statements-in-abap-behavior-pools) + - [Excursion: Using Keys and Identifying RAP BO Instances in a Nutshell / RAP Concepts](#excursion-using-keys-and-identifying-rap-bo-instances-in-a-nutshell--rap-concepts) + - [Further Information](#further-information) + - [Executable Examples](#executable-examples) + +## Excursion: EML in the Context of RAP + +[ABAP Entity Manipulation Language](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenaeml_glosry.htm) (or EML for short) is a subset of ABAP that allows you to access the data of [RAP](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenarap_glosry.htm) business objects in an ABAP program. +The following points are meant to touch on RAP-related buzzwords like +*RAP business objects* and others for setting the context: + +- RAP business objects (RAP BO) + - A RAP BO is based on a special, tree-like hierarchical structure + of [CDS + entities](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencds_entity_glosry.htm "Glossary Entry") + of a data model + - Such a structure of entities consists of [parent + entities](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenparent_entity_glosry.htm "Glossary Entry") + and [child + entities](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenchild_entity_glosry.htm "Glossary Entry") + that are themselves defined using [CDS + compositions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencds_composition_glosry.htm "Glossary Entry") + and [to-parent + associations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abento_parent_association_glosry.htm "Glossary Entry"). + - The top parent entity of a [CDS composition + tree](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencds_composition_tree_glosry.htm "Glossary Entry") + is the root entity that represents the business object. With a + large composition tree, RAP BOs can be fairly complex. Or they + can be very simple by just consisting of one root entity alone. + - Note: There is a special syntax for the CDS root view + entity of a RAP BO: [`define root view + entity`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencds_define_root_view_v2.htm) +- [CDS behavior + definition](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencds_behavior_definition_glosry.htm "Glossary Entry") + (BDEF) + - RAP BOs are described by the definitions specified in a special + [DDIC](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenddic_glosry.htm "Glossary Entry") + artifact: CDS behavior definition (BDEF) + - A BDEF defines the [RAP business object + behavior](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_bo_behavior_glosry.htm "Glossary Entry") + (i. e. the transactional behavior of a RAP BO) + - Transactional behavior means a BDEF defines [behavior + characteristics](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencds_entity_properties_glosry.htm "Glossary Entry") + and [RAP BO + operations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_bo_operation_glosry.htm "Glossary Entry") i. + e. [RAP BO standard + operations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_standard_operation_glosry.htm "Glossary Entry") + ([CRUD + operations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencrud_glosry.htm "Glossary Entry")), + [non-standard + operations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_nstandard_operation_glosry.htm "Glossary Entry") + like specific [RAP + actions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_action_glosry.htm "Glossary Entry") + and + [functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_function_glosry.htm "Glossary Entry"), + and more. + - There are many other things that can be included impacting the + RAP BO behavior like [RAP feature + control](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_feature_control_glosry.htm "Glossary Entry"), + for example, defining which data is mandatory and which is + read-only, or + [determinations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_determination_glosry.htm "Glossary Entry") + and + [validations](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_validation_glosry.htm "Glossary Entry"). + - BDEFs use [Behavior Definition + Language](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenbdl_glosry.htm "Glossary Entry") + (BDL) for the definitions. Find more information on the topic + and various options to define the transactional behavior in + section [BDL for Behavior + Definitions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenbdl.htm) + in the ABAP Keyword Documentation. +- Transactional buffer and implementation types + - A BDEF defines the behavior of a RAP BO and, thus, how to handle + its data. This data is available in the [RAP transactional + buffer](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentransactional_buffer_glosry.htm "Glossary Entry"). + - It is a storage for a RAP BO's data that is used and worked on + during a [RAP + LUW](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_luw_glosry.htm "Glossary Entry"). + - This data includes [RAP BO + instances](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_bo_instance_glosry.htm "Glossary Entry") + (i. e. concrete data sets of an entity). This is where EML + enters the picture: EML is used to access this data in the + transactional buffer. + - Currently, there are two kinds of RAP BOs: + [managed](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenmanaged_rap_bo_glosry.htm "Glossary Entry") + and [unmanaged RAP + BOs](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenunmanaged_rap_bo_glosry.htm "Glossary Entry"). + - Managed and unmanaged are implementation types that are also + specified in the BDEF. + - The implementation type determines the [RAP BO + provider](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_bo_provider_glosry.htm "Glossary Entry"), i. + e. how the transactional buffer is provided and how the behavior + of a RAP BO is implemented. +- Managed RAP BOs: + - The managed RAP BO provider fully or partly provides the + transactional buffer and RAP BO behavior (for standard + operations only). In this case, the developers need not cater + for the transactional buffer and implement the standard + operations. This implementation is mostly relevant for + greenfield scenarios when starting from scratch. + - Example: Regarding CRUD operations in managed RAP BOs, + developers need not cater for an implementation at all. The + standard operations work out of the box. For example, in case of + an update operation, RAP BO instance data that is to be updated + is automatically read into the transactional buffer, and then + updated accordingly there. Finally, when triggering the saving, + the updated instance in the transactional buffer is + automatically saved to the database without any custom + development needed. + - The transactional buffer is provided, too. You do not need to + create the buffer yourself. + - Note: Usually, the behavior of a RAP BO requires some + additional implementations also in the context of managed RAP + BOs. For example, non-standard operations or feature controls + must be self-implemented in [ABAP behavior + pools](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenbehavior_pool_glosry.htm "Glossary Entry") + (see the details further down). +- Unmanaged RAP BOs: + - Everything must be provided by the [unmanaged RAP BO + provider](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenunmanaged_rap_bo_prov_glosry.htm "Glossary Entry"), i. + e. the transactional buffer and all RAP BO operations must be + provided or self-implemented by developers in an ABAP behavior + implementation + - Unmanaged RAP BOs are, for example, relevant for brownfield + scenarios, i. e. in scenarios in which business logic is already + available and should be embedded in the RAP world. +- ABAP behavior implementation in an ABAP behavior pool (ABP) + - An [ABAP behavior + pool](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenbehavior_pool_glosry.htm "Glossary Entry") + is a special [class + pool](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenclass_pool_glosry.htm "Glossary Entry") + for an ABAP behavior implementation that implements the + unmanaged RAP BO provider based on definitions in a BDEF. The + class pool's name is specified in the BDEF. + - The [global + class](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenglobal_class_glosry.htm "Glossary Entry") + of a behavior pool does not implement the behavior itself. It is + basically empty. The behavior implementation is coded in local + [RAP handler + classes](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabp_handler_class_glosry.htm "Glossary Entry") + and a [RAP saver + class](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabp_saver_class_glosry.htm "Glossary Entry") + in the [CCIMP + include](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenccimp_glosry.htm "Glossary Entry") + of the behavior pool. These classes are called by the [RAP + runtime + engine](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_runtime_engine_glosry.htm "Glossary Entry") + when the RAP BO is accessed. This is touched on in more detail + further down. + - Usually, saver classes are not needed in managed RAP BOs (except + for special variants of managed RAP BOs which are not touched on + here). Local handler classes are, as mentioned above, usually + needed in managed RAP BOs if implementations are required that + go beyond standard operations. + - Note: In more complex scenarios, with RAP BOs that + consist of many entities, you can define behavior pools for + individual entities by adding the syntax to the [`define + behavior + for`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenbdl_define_beh.htm) + notation. There is not a saver class for each entity but only + one saver class for the BO as a whole. Any number of behavior + pools can be assigned to a BDEF allowing applications a + structuring into multiple units. + +There are more artifacts and concepts related to RAP that go way beyond +the scope of this cheat sheet. For example, a RAP BO can be exposed as a +[business +service](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenbusiness_service_glosry.htm "Glossary Entry") +to be accessed from outside [AS +ABAP](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenas_abap_sys_environ_glosry.htm "Glossary Entry") +and consumed. A [RAP BO +consumer](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_bo_consumer_glosry.htm "Glossary Entry") +is either the [RAP transactional +engine](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_transac_engine_glosry.htm "Glossary Entry") +that handles requests from outside the AS ABAP or, from inside AS ABAP, +an ABAP program using ABAP EML (which this cheat sheet and the examples +focus on). + +

(back to top)

+ +## ABAP Behavior Pools (ABP) + +As mentioned above, you can access RAP BO data from inside AS ABAP using +EML. Among other things, EML allows you to read or modify RAP BOs by +accessing the RAP BO data (the RAP BO instances) in the transactional +buffer and trigger the persistent storage or reset changes. More +precisely, when EML statements are executed, the calling of [RAP handler +methods](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabp_handler_method_glosry.htm "Glossary Entry") +is triggered to access the transactional buffer of a RAP BO. As +mentioned, for unmanaged RAP BOs or unmanaged parts of managed RAP BOs, +the handler methods that are called are part of an ABAP behavior pool. + +The global class of an ABP has the addition [`FOR BEHAVIOR OF +bdef`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapclass_for_behavior_of.htm) +to the definition while `bdef` stands for the name of the BDEF. +This class is usually empty. + +The implementation is done in local classes in the CCIMP include. There, +two kinds of local classes are to be defined and implemented that are +related to the RAP BO's runtime: one or more [handler +classes](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabp_handler_class_glosry.htm "Glossary Entry") +to implement the RAP BO behavior (in RAP handler methods) during the +[RAP interaction +phase](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_int_phase_glosry.htm "Glossary Entry") +(the data reading and modification phase) and a [saver +class](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabp_saver_class_glosry.htm "Glossary Entry") +to implement the [RAP save +sequence](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_save_seq_glosry.htm "Glossary Entry") +(in [saver +methods](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabp_saver_method_glosry.htm "Glossary Entry") +to save data from the transactional buffer to the database). + +

(back to top)

+ +### RAP Handler Classes and Methods + +- One or more handler classes implement the RAP interaction phase. For + modularization purposes, one behavior pool can define multiple + handler classes. For example, each entity can have its own handler + class, or individual handler classes can be defined to distinguish + between reading and changing RAP BO entities. +- A handler class inherits from class + `CL_ABAP_BEHAVIOR_HANDLER`. +- These classes are implicitly `ABSTRACT` and `FINAL` + since instantiating and calling only happens through the RAP runtime + engine. +- [ADT](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenadt_glosry.htm "Glossary Entry") + helps you create the classes and methods (and basically the ABP as + such) when creating the BDEF. A quick fix is available that creates + the method definitions and a skeleton of the implementations + automatically. + +Example: Handler class definition +``` abap +CLASS lhc_root DEFINITION INHERITING FROM cl_abap_behavior_handler. +... +ENDCLASS. +``` +- Handler method definitions include the additions `... FOR ... FOR + ...` followed by the kind of operations. There are various + options depending on the RAP BO operation. +- Depending on the definition in the BDEF, there might be more ABAP + words with dedicated method parameters. For example, an action might + be defined with a result parameter, hence, the method must be + defined with the addition `RESULT` and a parameter. +- The `FOR MODIFY` handler method can handle multiple entities + and operations, i. e. not only create but also update or delete + might be integrated in the method definition. However, it might be + useful to split the handler method into separate methods for better + readability. +- See more details on the handler method definitions in the topic + [`METHODS, FOR`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmethods_for_rap_behv.htm). + +Example: Handler method definitions +``` abap +"Create +METHODS create FOR MODIFY + IMPORTING entities FOR CREATE bdef. + +"Read: Specifying a read result is mandatory. +METHODS read FOR READ + IMPORTING keys FOR READ bdef RESULT result. + +"Action: action name is preceded by the BDEF name and a tilde after FOR ACTION +METHODS some_action FOR MODIFY + IMPORTING keys FOR ACTION bdef~some_action. +``` + +**Parameters of Handler Methods** + +- The handler method definition contains RAP-specific additions like + `FOR MODIFY`, `FOR CREATE` or `FOR READ` as + well as mandatory or optional additions like `RESULT` that + are followed by parameters. +- Nearly all parameters are typed with [BDEF derived + types](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_derived_type_glosry.htm "Glossary Entry") + that have special RAP-related components as touched on further down. +- The parameters' names can be chosen freely. This is also true for + the method names except for some predefined names. +- Each handler method must have at least one importing parameter. The + addition `IMPORTING` is optional since it is used + implicitly. In most cases, the whole instance or just the key values + of an instance are imported. +- All handler methods have changing parameters that are usually not + explicitly specified in the definition but implicitly used. The + addition `CHANGING` is not needed. In most cases, these are + [RAP response + parameters](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_response_param_glosry.htm "Glossary Entry"). + The following image shows the F2 information in ADT for the create + handler method. + ![RAP_handler_method_parameters](./files/rap_handler_method_parameters.png) +- The response parameters `mapped`, `failed` and + `reported` (the names are predefined) can be considered as + containers for information - information a RAP BO consumer is + provided with by a RAP BO provider, for example, an SAP Fiori app + displays an error message if something went wrong. The availability + of the parameters depends on the handler method used (e. g. + `mapped` is only available for operations creating + instances). + - `mapped`: Used to provide mapping information on RAP BO + instances, for example, which key values were created for given + content IDs ( + [`%cid`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_cid.htm)). + - `failed`: Information for identifying the data set for + which an error occurred in a RAP operation + - `reported`: Used to exchange error messages for each + entity defined in the BDEF and [not related to a specific + entity](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_other.htm). + - Example: Technically, the `reported` parameter is a + [deep + structure](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendeep_structure_glosry.htm "Glossary Entry") + containing, for example, the messages of the root entity and + child entities. For example, if a create operation fails for a + RAP BO instance of the root entity, a message, information about + the instance key and other things can be included in this + parameter which is passed to a RAP BO consumer. You could + imagine that such an error message is displayed on an SAP Fiori + UI if something goes wrong to inform the user. + + +

(back to top)

+ +### RAP Saver Class and Saver Methods + +- A RAP saver class implements the [RAP save + sequence](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_save_seq_glosry.htm "Glossary Entry"). + A saver class is usually only available in unmanaged RAP BOs (except + for special variants of managed RAP BOs that are not outlined here). +- The saver class is implicitly `ABSTRACT` and `FINAL` + since the instantiating and calling only happens through the RAP + runtime engine. +- A saver class can be defined in the CCIMP include of an ABAP + behavior pool. It includes the definitions and implementations of + RAP saver methods. +- The saver methods consist of a set of predefined methods having + predefined names. Some of them are mandatory to implement, some are + optional. The + [`adjust_numbers`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensaver_adjust_numbers.htm) + method is only available in [late + numbering](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_late_numbering_glosry.htm "Glossary Entry") + scenarios. +- A saver class inherits from class + `CL_ABAP_BEHAVIOR_SAVER`. The saver methods are + declared by redefining predefined methods of the superclass. They + implicitly have response parameters. +- In contrast to RAP handler methods, saver methods do not have data + of RAP BO instances as import parameter. Therefore, instance data + must be handled via the transactional buffer when self-implementing + the saver methods. +- All saver methods are called after at least one successful + modification of data in the current RAP LUW, for example, by + triggering the save sequence using a [`COMMIT + ENTITIES`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcommit_entities.htm) + statement. +- Find more information on RAP saver methods + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabp_saver_class.htm). + +Example: Definition of a RAP saver class and saver methods +``` abap +CLASS lsc_bdef DEFINITION INHERITING FROM cl_abap_behavior_saver. + PROTECTED SECTION. + + "For final calculations and data modifications involving all + "BOs in the current RAP LUW + METHODS finalize REDEFINITION. + + "Checks the consistency of the transactional buffer before + "the save method saves data to the database + METHODS check_before_save REDEFINITION. + + "Preliminary IDs are mapped to final keys. Only for late numbering. + METHODS adjust_numbers REDEFINITION. + + "Saves the current state of the transactional buffer to the database + METHODS save REDEFINITION. + + "Clear the transactional buffer + METHODS cleanup REDEFINITION. + METHODS cleanup_finalize REDEFINITION. + +ENDCLASS. +``` + +

(back to top)

+ +## BDEF Derived Types + +The operands of EML statements and parameters of handler and saver +methods are mainly special messenger tables for passing data and +receiving results or messages, i. e. the communication between a RAP BO +consumer and the RAP BO provider using EML consists (in most cases) of +exchanging data stored in internal tables that have special ABAP types - +[BDEF derived +types](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_derived_type_glosry.htm "Glossary Entry"). +These types are tailor-made for RAP purposes. + +As the name implies, the types are derived by the [ABAP runtime +framework](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_runtime_frmwk_glosry.htm "Glossary Entry") +from CDS entities and their behavior definition in the BDEF. With these +special types, a type-safe access to RAP BOs is guaranteed. + +You can create internal tables (using [`TYPE TABLE +FOR`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaptype_table_for.htm)), +structures (using [`TYPE STRUCTURE +FOR`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaptype_structure_for.htm)) +and data types with BDEF derived types. For all operations and behavior +characteristics defined in the BDEF, types can be derived. + +The syntax uses - similar to the method definitions mentioned before - +the addition `FOR` followed by the operation and the name of an +entity (and, if need be, the concrete name, e. g. in case of an action +defined in the BDEF). + +Each BDEF derived type can be categorized as +[input](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_input_der_type_glosry.htm "Glossary Entry") +or [output derived +type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_output_der_type_glosry.htm "Glossary Entry") +according to its use as importing or exporting parameters in methods of +RAP BO providers. In most cases, structures of type `TYPE STRUCTURE +FOR` can be considered as serving as [work +area](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwork_area_glosry.htm "Glossary Entry") +and line type of the internal tables. However, there are also structured +derived types that do serve as types for handler method parameters. + +The response parameters `mapped`, `failed` and +`reported` have dedicated derived types: [`TYPE RESPONSE +FOR`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaptype_response_for.htm). +They are deep structures containing the information for the individual +entities of the RAP BO. The components of these structures are internal +tables of appropriate types with `TYPE TABLE FOR`. + +Examples for BDEF derived types: + +``` abap +"Data objects with input derived types (entity = name of a root entity) + +"For an EML create operation + +DATA create_tab TYPE TABLE FOR CREATE entity. + +"For an update operation + +DATA update_tab TYPE TABLE FOR UPDATE entity. + +"Type for create-by-association operations specifying the name of the entity +"and the association + +DATA cba_tab TYPE TABLE FOR CREATE entity\_child. + +"For an action execution; the name of the action is preceded by a tilde + +DATA action_imp TYPE TABLE FOR ACTION IMPORT entity~action1. + +"Data objects with output derived types + +"For a read operation + +DATA read_tab TYPE TABLE FOR READ RESULT entity. + +"For an action defined with a result + +DATA action_res TYPE TABLE FOR ACTION RESULT entity~action2. + +"Examples for structures and types + +DATA create_wa TYPE STRUCTURE FOR CREATE entity. + +"For permission retrieval + +DATA perm_req TYPE STRUCTURE FOR PERMISSIONS REQUEST entity. + +"For retrieving global features + +DATA feat_req TYPE STRUCTURE FOR GLOBAL FEATURES RESULT entity. + +"Type declaration using a BDEF derived type + +TYPES der_typ TYPE TABLE FOR DELETE entity. + +"Response parameters + +DATA map TYPE RESPONSE FOR MAPPED entity. +DATA fail TYPE RESPONSE FOR FAILED entity. +DATA rep TYPE RESPONSE FOR REPORTED entity. +``` +> **💡 Note**
+> Some of the derived types can only be created and accessed in implementation classes. + +

(back to top)

+ +### Components of BDEF Derived Types + +Many of the BDEF derived types contain components of CDS entities like +key and data fields that retain their original line type, for example, a +messenger table typed with `TYPE TABLE FOR CREATE`. Certainly, +if an instance is to be created, key and field values of a RAP BO +instance are of relevance. + +Yet, all BDEF derived types contain special RAP components serving a +dedicated purpose. The names of these RAP components begin with +`%` to avoid naming conflicts with components of the CDS +entities. The following image shows the F2 information of a BDEF derived +type containing the `%` components and fields from the CDS +entity. + +![BDEF_derived_types](./files/bdef_derived_types.png) + +Some of the `%` components are [component +groups](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomponent_group_glosry.htm "Glossary Entry") +summarizing groups of table columns under a single name. In doing so, +they simplify the handling of derived types for developers. For example, +the component group +[`%data`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_data.htm) +contains all primary key and data fields of a RAP BO entity (actually, +by containing the keys, it also contains the component group +[`%key`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_key.htm) +in the case above). The F2 information in ADT helps you find out about +the available components in a variable. The image below shows the +details of `%data` when clicking the `derived type` +link in the first ADT F2 information screen. + +![BDEF_derived_type_components](./files/bdef_derived_type_components.png) + +The availability of `%` components depends on definitions in the +BDEF. Their availability also depends on more criteria, for example, the +scenario. For example, the component +[`%pid`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_pid.htm) +that represents a preliminary ID for a RAP BO instance is only available +in [late +numbering](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_late_numbering_glosry.htm "Glossary Entry") +scenarios. The draft indicator +[`%is_draft`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_is_draft.htm) +is only available in the context of +[draft](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenbdl_with_draft.htm). + +Find more details on the available components in section [Components of +BDEF Derived +Types](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_comp.htm). + +Bullet points on selected `%` components: + +- [`%cid`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_cid.htm) + - A string to define a content ID. + - Content IDs are used as a unique and preliminary identifier for + RAP BO operations in which instances are created and especially + in cases where the key values of RAP BO instances are not yet + determined + - Assume that you create a RAP BO instance with an EML create + request and the key value has not yet been determined. In the + same request - a save has not yet been triggered - an update is + requested for this RAP BO instance. Using the content ID, it is + guaranteed that the update operation happens for the desired + instance. For this purpose, derived types for operations like + update or delete include the component + [`%cid_ref`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_cid_ref.htm) + to refer to the content ID `%cid` as the name implies. + - Note: You should always fill `%cid` even if not + needed. The specified content ID is only valid within one ABAP + EML request. +- [`%key`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_key.htm)/[`%tky`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_tky.htm) + - Both are component groups summarizing all primary keys of a RAP + BO instance. + - Where possible, it is recommended that you use `%tky` + instead of `%key`. `%tky` includes + `%key` and also the draft indicator + `%is_draft`. When using `%tky` in non-draft + scenarios, you are prepared for a later, potential switch to a + draft scenario. In doing so, you can avoid lots of adaptations + in your code by manually adding the indicator. +- [`%control`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_control.htm) + - Component group that, for example, contains the names of all key + and data fields of a RAP BO instance which indicate flags. + - Used to get information on which fields are provided or set a + flag for which fields are requested by RAP BO providers or RAP + BO consumers respectively during the current transaction. + - For this purpose, the value of each field in the + `%control` structure is of type + `ABP_BEHV_FLAG`. For the value setting, + you can use the structured constant `mk` of interface + `IF_ABAP_BEHV`. Note that the technical + type is `x length 1`. + - Example: If you want to read data from a RAP BO instance and + particular non-key fields in `%control` are set to + `if_abap_behv=>mk-off`, the values of these fields + are not returned in the result. + +

(back to top)

+ +## EML Syntax + +The focus is here on selected EML statements. These statements can be +fairly long and various additions are possible. Find more information on +the EML statements +[here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeneml.htm). + +### EML Syntax for Modifying Operations + +The modifying operations covered include the standard operations (using +the additions +[`CREATE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_entity_entities_op.htm), +[`CREATE +BY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_entity_entities_op.htm), +[`UPDATE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_entity_entities_op.htm), +and +[`DELETE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_entity_entities_op.htm) +and non-standard operations (actions) using the addition +[`EXECUTE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_entity_entities_op.htm). +All EML statements for the mentioned operations begin with +[`MODIFY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_entity_entities.htm). +The following commented code snippets demonstrate the +[short](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_entity_short.htm) +and [long +form](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_entities_long.htm) +of EML `MODIFY` statements. + +Create operation for creating new instances of a RAP BO entity: + +``` abap +"Declaration of data objects using BDEF derived types + +DATA: cr_tab TYPE TABLE FOR CREATE root_ent, "input derived type + mapped_resp TYPE RESPONSE FOR MAPPED root_ent, "response parameters + failed_resp TYPE RESPONSE FOR FAILED root_ent, + reported_resp TYPE RESPONSE FOR REPORTED root_ent. + +"Input derived type for the EML statement is filled using the VALUE operator +"Assumption: key_field is the key field having type i, +"field1 and field2 are data fields with character-like data type. +"Specify %cid even if not used or of interest; it must be unique within a RAP LUW + +cr_tab = VALUE #( + ( %cid = 'cid1' key_field = 1 + field1 = 'A' field2 = 'B' ) + ( %cid = 'cid2' + "Just to demo %data/%key. You can specify fields with or without + "the derived type components + %data = VALUE #( %key-key_field = 2 + field1 = 'C' + field2 = 'D' ) ) ). + +"EML statement, short form +"root_ent must be the full name of the root entity, it is basically the name of the BDEF + +MODIFY ENTITY root_ent + CREATE "determines the kind of operation + FIELDS ( key_field field1 field2 ) WITH cr_tab "Fields to be respected for the + "input derived type and the input + "derived type itself + MAPPED mapped_resp "mapping information + FAILED failed_resp "information on failures with instances + REPORTED reported_resp. "messages +``` + +> **💡 Note**
+> - Addition [`FIELDS ( ... ) WITH`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_entity_entities_fields.htm): + This field selection option specifies which fields are to be + respected for the operation. The derived type, i. e. an internal + table containing the concrete RAP BO instance values, follows + `WITH`. If a field is specified in the field list within the + pair of parentheses after `FIELDS`, the `%control` + flag for this field is automatically set to + `if_abap_behv=>mk-on`. Likewise, if a field is not + contained in the list, the flag in `%control` is set to + `if_abap_behv=>mk-off`. Assume `field2` is not + specified in the list. The value for `field2` will not be + respected (even if a value is specified in the internal table). The + initial value will be used for the field. +>- Retrieving the responses and specifying the parameters is optional. + Assuming a data set with the value 2 for `key_field` + already exists on the database for this BO, you should expect an + entry for this particular instance in the `failed_resp` + operand and potentially an error message in + `reported_resp`, too. Nevertheless, especially in ABP + implementations and depending on the context, you should implement + and fill these parameters according to the [RAP BO + contract](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_bo_contract_glosry.htm "Glossary Entry") + to meet the variety of implementation rules. +>- `%cid` should be provided even if you are not interested in + it and subsequent operations do not require the reference. + +Long form of an EML `MODIFY` statement: +``` abap +MODIFY ENTITIES OF root_ent "full name of root entity + ENTITY root "root or child entity (alias name if available) + CREATE FROM "FROM as further field selection variant + VALUE #( ( %cid = 'cid' "Input derived type created inline + key_field = 3 + field1 = 'E' + field2 = 'F' + %control = VALUE #( "Must be filled when using FROM + key_field = if_abap_behv=>mk-on + field1 = if_abap_behv=>mk-on + field2 = if_abap_behv=>mk-on ) ) ) + MAPPED DATA(m) "Target variables declared inline + FAILED DATA(f) + REPORTED DATA(r). +``` + +> **💡 Note**
+>- The entity specified after `ENTITY` can be either the root + entity itself or a child entity. If an alias is defined, the alias + should be used. +>- The addition `FIELDS ( ... ) WITH` from the previous + snippet is basically a shortcut for the addition + [`FROM`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_entity_entities_fields.htm) + that is used here. When using `FROM`, the values of the + `%control` structure must be specified explicitly. +>- The BDEF derived types can also be created + [inline](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_inline.htm) + as shown in the example using a [constructor + expression](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconstructor_expression_glosry.htm "Glossary Entry") + for the input derived type and with `DATA` or + [`FINAL`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfinal_inline.htm) + for the responses. +>- The long form allows you to bundle several operations in one + statement, either different operations on the same entity (for + example, deleting some instances and updating some others) or + operations on different entities of the same RAP BO (for example, + creating a root entity instance and related instances of a child + entity in one EML request). Long and short forms are also available + for other EML statements. + +The following EML statement combines multiple operations in one EML +request. It demonstrates the use of `%cid` and +`%cid_ref`. First, two instances are created by specifying +`%cid`. An update operation in the same request only specifies a +certain field within the parentheses of the `FIELDS ( ... ) +WITH` addition which denotes that only this particular field +should be updated. The other field values remain unchanged. The +reference to the instance is made via `%cid_ref`. Consider an +EML request in which no instance to refer to using `%cid_ref` +exists, e. g. for an update operation. You can also make the reference +using the unique key. A delete operation is available in the same +request, too. `DELETE` can only be followed by the addition +`FROM`. In contrast to other derived types, the derived type +that is expected here (`TYPE TABLE FOR DELETE`) only has +`%cid_ref` and the key as components. +``` abap +MODIFY ENTITIES OF root_ent + ENTITY root + CREATE FIELDS ( key_field field1 field2 ) WITH + VALUE #( ( %cid = 'cid4' key_field = 4 + field1 = 'G' field2 = 'H' ) + ( %cid = 'cid5' key_field = 5 + field1 = 'I' field2 = 'J' ) ) + + UPDATE FIELDS ( field2 ) WITH + VALUE #( ( %cid_ref = 'cid4' field2 = 'Z' ) ) + + DELETE FROM + VALUE #( ( %cid_ref = 'cid5' ) "Instance referenced via %cid_ref + ( key_field = 9 ) ) "Instance referenced via the key +... +``` + +EML statement including the execution of an action: +``` abap +MODIFY ENTITIES OF root_ent + ENTITY root + EXECUTE some_action + FROM action_tab + RESULT DATA(action_result) "Assumption: The action is defined with a + "result parameter. + ... +``` + +The following code snippet shows a deep create. First, an instance is +created for the root entity. Then, in the same request, instances are +created for the child entity based on the root instance. In the example +below, the assumption is that a composition is specified in the root +view entity like `composition [1..*] of root_ent as _child` and `key_field` and +`key_field_child` are the keys of the child view entity. The +[`%target`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_target.htm) +component group enters the picture here which contains the target's +primary key and data fields. +``` abap +MODIFY ENTITIES OF root_ent + ENTITY root_ent + CREATE FIELDS ( key_field field1 field2 ) WITH + VALUE #( ( %cid = 'cid6' key_field = 6 + field1 = 'I' field2 = 'J' ) ) + CREATE BY \_child + FIELDS ( key_field_child field1_child field2_child ) WITH + VALUE #( ( %cid_ref = 'cid6' + %target = VALUE #( ( %cid = 'cid_child_1' + key_field_child = 1 + field1_child = 'aa' + field2_child = 'bb' ) + ( %cid = 'cid_child_2' + key_field_child = 2 + field1_child = 'cc' + field2_child = 'dd' ) ) ) ) +... +``` + +

(back to top)

+ +### EML Syntax for Reading Operations + +- Read-only operations always return a result, i.e. the syntax of the + EML statement requires the addition `RESULT` and an operand. +- When RAP BO instances are read, the returned data include the + current status of instances in the transactional buffer which + includes unsaved modifications on instances. If an instance is not + yet available in the transactional buffer, the currently persisted + data set is automatically read into the transactional buffer. +- Note that read operations are always implicitly enabled for each + entity listed in a BDEF, i. e. there is no extra definition in the + BDEF in contrast to, for example, create or update. + +The following code snippet shows the long form of the EML +[`READ`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapread_entity_entities_op.htm) +statement for reading instances from the root entity. In `READ` +statements, the additions `FIELDS ( ... ) WITH` and +`FROM` can also be used to specify the fields that you intend to +read. Here, the addition [`ALL FIELDS +WITH`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapread_entity_entities_fields.htm) +is available for reading all field values. + +``` abap +READ ENTITIES OF root_ent + ENTITY root_ent + ALL FIELDS WITH + VALUE #( ( key_field = 1 ) "Derived type TYPE TABLE FOR READ IMPORT + "only includes the keys + ( key_field = 2 ) ) + RESULT DATA(result) + FAILED DATA(f) + REPORTED DATA(r). +``` + +Read-by-association operations include the optional addition +[`LINK`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapread_entity_entities_op&sap-language=EN&sap-client=000&version=X&anchor=!ABAP_ONE_ADD@1@&tree=X) +with which you can retrieve the keys of the source and target (i. e. the +associated entity). The by-association operations work reciprocally, i. +e. you can, for example, read a child instance via the parent and a +parent instance via the child, too. + +``` abap +"Read-by association operation: parent to child +READ ENTITIES OF root_ent + ENTITY root_ent + BY \_child + ALL FIELDS WITH VALUE #( ( key_field = 1 ) ) + RESULT DATA(rba_res1) + LINK DATA(links1). + ... + +"Read-by association operation: child to parent +READ ENTITIES OF root_ent + ENTITY child_ent + BY \_parent + ALL FIELDS WITH VALUE #( ( key_field = 1 key_field_child = 1 ) ) + RESULT DATA(rba_res2) + LINK DATA(links2). + ... +``` +

(back to top)

+ +### Persisting to the Database + +- A [`COMMIT + ENTITIES`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcommit_entities.htm) + statement triggers the RAP save sequence. Without such a statement, + the modified RAP BO instances that are available in the + transactional buffer are not persisted to the database. +- `COMMIT ENTITIES` implicitly includes [`COMMIT + WORK`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcommit.htm). +- Note: `COMMIT ENTITIES` statements cannot be used + in behavior implementations. In case of a natively supported RAP + scenario (for example, when using OData), the `COMMIT + ENTITIES` request is executed automatically. +- There a multiple variants available for the statement as described + in the ABAP Keyword Documentation + [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcommit_entities.htm). +- `COMMIT ENTITIES` statements set the system field + `sy-subrc`. When using `COMMIT ENTITIES`, it is not + guaranteed that `COMMIT WORK` is carried out successfully. + Hence, you should include a check for `sy-subrc` after + `COMMIT ENTITIES` so that you can react to failures + accordingly. + +The following snippet shows a create operation. This operation has only +an impact on the database with the `COMMIT ENTITIES` statement. +Triggering the save sequence means that the execution of the statement +triggers the calling of the saver methods available in the saver class +of a behavior implementation. In managed scenarios (except for some +special variants), the saving is done automatically without implementing +a dedicated saver method. +``` abap +MODIFY ENTITIES OF root_ent + ENTITY root_ent + CREATE FIELDS ( key_field field1 field2 ) WITH + VALUE #( ( %cid = 'cid' key_field = 7 + field1 = 'K' field2 = 'L' ) ). + +COMMIT ENTITIES. + +IF sy-subrc <> 0. + ... +ENDIF. +``` + +

(back to top)

+ +### EML Statements in ABAP Behavior Pools + +- There are a [special additions when using EML in behavior + pools](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeneml_in_abp.htm). + One of them is [`IN LOCAL MODE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapin_local_mode.htm). +- This addition can be used to exclude feature controls and + authorization checks. +- Consider the following use case: There is a field to display the + booking status of a trip on a UI. In the BDEF, this field is + specified as read-only. Hence, it cannot be modified by a user on + the UI. However, there is a button on the UI to book the trip. This + button might trigger an action to book the trip so that the value of + the field changes from open to booked. To enable this, the + underlying handler method for the modify operation with the action + to be executed has the addition `IN LOCAL MODE` that ignores + the feature control. + +Syntax: + +``` abap +MODIFY ENTITIES OF root_ent IN LOCAL MODE + ENTITY root + EXECUTE book + FROM action_tab + ... +``` + +

(back to top)

+ + +## Excursion: Using Keys and Identifying RAP BO Instances in a Nutshell / RAP Concepts + +
+ Using Keys and Identifying RAP BO Instances in a Nutshell + +
+ +The following bullet points outline important aspects regarding + keys and identifying [RAP BO +instances](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_bo_instance_glosry.htm "Glossary Entry") in ABAP EML statements. + +**Why is it important?** + +- The [primary + key](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenprimary_key_glosry.htm "Glossary Entry") + of a [RAP BO entity instance](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_bo_entity_inst_glosry.htm "Glossary Entry") + is composed of one or more [key fields](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenkey_field_glosry.htm "Glossary Entry"). +- These key fields stand for the fields that are specified with + `key` in the underlying [CDS view entity](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencds_v2_view_glosry.htm "Glossary Entry") + of the RAP BO. +- The primary key uniquely identifies each RAP BO entity instance. +- After the creation of an instance and the primary key during a [RAP create operation](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_create_operation_glosry.htm "Glossary Entry"), + the primary key cannot be changed. + - Note the concept of [late numbering](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlate_numbering_glosry.htm "Glossary Entry") + where newly created entity instances are given their final key + only shortly before saving in the database. Until then, the + business logic uses a temporary key that has to be replaced. +- If a data set with a particular primary key already exists in the + persistent database table, the saving of a RAP BO instance with a + duplicate primary key is rejected. + +**How can a RAP BO instance be uniquely identified?** + +- It can be done by using a [RAP instance identifier](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_inst_identifier_glosry.htm "Glossary Entry") + or [RAP content identifier](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_cont_identifier_glosry.htm "Glossary Entry") + or both of them. +- RAP instance identifier: + - It consists of the primary key fields and all relevant [BDEF derived type](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_derived_type_glosry.htm "Glossary Entry") + components. + - To ease the reference to all of these components, special + [component groups](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomponent_group_glosry.htm "Glossary Entry") + are available to summarize the components and make them + addressable via one single name. + - [`%key`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_key.htm): + Contains the primary key fields of a RAP BO instance + - [`%tky`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_tky.htm): + Specifies the transactional key. Comprises `%key` (and, + thus, the primary key fields of a RAP BO instance) and more + components that are relevant to uniquely identify a RAP BO + instance. Among them, + [`%pid`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_pid.htm) + (relevant for late numbering scenarios) and the draft indicator + [`%is_draft`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_is_draft.htm) + (relevant for + [draft](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenbdl_with_draft.htm) + scenarios). In non-late numbering or non-draft scenarios, these + extra components are just blank. However, it is recommended that + you use `%tky` in all scenarios since it simplifies a + possible later switch, for example, to a draft scenario. In + doing so, lots of adaptations to the code regarding the keys and + the inclusion of `%is_draft` can be avoided. +- RAP content identifier: + - Reflected in the component + [`%cid`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_cid.htm) + which is a string of type + `ABP_BEHV_CID` to define a content ID. + - Used as a unique and preliminary identifier for RAP BO instances + in RAP create operations, especially where no primary key exists + for the particular instance. + - For newly created instances, the ID can then be used for + performing further, referencing modifications on those instances + using + [`%cid_ref`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_cid_ref.htm) + (which has the same value as `%cid` is then used, for + example, in RAP operations using [`CREATE BY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_entity_entities_op.htm), [`UPDATE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_entity_entities_op.htm) + and + [`DELETE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_entity_entities_op.htm), + as well as + [actions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenbdl_action.htm) + with + [`EXECUTE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapmodify_entity_entities_op.htm)). + - In contrast to the primary key and the preliminary ID + `%pid` for late numbering scenarios, `%cid` (and + `%cid_ref`) are only available on a short-term basis + for the current ABAP EML request within the [RAP interaction phase](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_int_phase_glosry.htm "Glossary Entry") in one [RAP LUW](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_luw_glosry.htm "Glossary Entry"). + - **Note:** The specification of `%cid` should be done even if there are no further operations referring to it. +- Special case: Late numbering + - As mentioned above, in late numbering scenarios newly created + entity instances are given their final key only shortly before + saving in the database, i. e. you deal with preliminary keys in + the RAP interaction phase and the early phase of the [RAP save sequence](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_save_seq_glosry.htm "Glossary Entry"). + - In this case, you can use `%key` to hold the preliminary + keys or use a preliminary ID in the dedicated component + [`%key`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_pid.htm) + which is of type `ABP_BEHV_PID` and + only available in late numbering scenarios. + - Similar to above, to uniquely identify RAP BO instances in late + numbering scenarios, you can use either `%key` or + `%pid` or both of them. In any case, the use of + `%tky` is handy because it includes both components. You + must ensure that `%tky` in total uniquely identifies the + instances. + - **Note:** A further component group to refer to the keys is available: [`%pky`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_pky.htm). `%pky` contains `%pid` and `%key` in late numbering scenarios. In non-late numbering scenarios, it just contains `%key`. `%pky` itself is contained in `%tky`. There are contexts, for example, particular actions, where `%tky` is not available but `%pky` is. This way, there is still the option to summarize `%pid` and `%key` in one component group in the absence of `%tky`. + +**General rule**: A RAP BO instance must always be uniquely +identifiable by its transactional key (`%tky`) for internal +processing during the RAP interaction phase. `%tky` always +contains all relevant components for the chosen scenario. + +
+ + +
+ RAP Concepts + +
+ +**RAP numbering** + +- A concept that deals with setting values for primary key fields. +- There are multiple options to handle the numbering for primary key + fields depending on when (early in the RAP interaction phase or late + in the RAP save sequence) and by whom (RAP BO consumer, behavior + pool, or framework) the primary key values are set. +- When: + - [Early numbering](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_early_numbering_glosry.htm "Glossary Entry"): + The final key values are assigned during a RAP create operation + in the interaction phase. + - [Late numbering](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_late_numbering_glosry.htm "Glossary Entry"): + The final key values are assigned during the RAP save sequence + (and here only in the RAP saver method + [`adjust_numbers`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensaver_adjust_numbers.htm)). +- By whom + - [External numbering](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_ext_numbering_glosry.htm "Glossary Entry"): + Key values are provided by the RAP BO consumer. For example, in + a create operation, the key values are specified by the RAP BO + consumer like other non-key field values. Basically, this is the + concept with which the snippets above are tailored. + - [Internal numbering](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_int_numbering_glosry.htm "Glossary Entry"): + Key values are provided by the RAP BO provider. For example, in + a create operation, the key values are not specified in an EML + create request by the RAP BO consumer but rather by the RAP BO + provider. In case of a managed RAP BO, the key is automatically + created by the framework which only works if the key is of a + certain type (16-character byte-like + [UUID](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenuuid_glosry.htm "Glossary Entry")). + In case of an unmanaged RAP BO, the key values are provided in a + dedicated handler method which must be self-implemented. Note + that late numbering is internal by default since no further RAP + BO consumer interaction is possible in the late phase of the RAP + save sequence. + +**Draft** + +- The draft concept in RAP allows the content of the transactional + buffer to be stored in intermediate storages ([draft tables](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendraft_table_glosry.htm "Glossary Entry")) + in order to allow transactions to expand over different ABAP + sessions. +- Like the concepts mentioned above, a RAP BO can be draft-enabled in + the BDEF. This concept enters the picture, for example, if you have + an application allowing data modifications and the temporary storage + of modifications but does not yet persist them to the database. You + can continue modifying this data later and you might even use a + different device from the one where you modified the data + previously. +- The draft indicator `%is_draft` enters the picture here for + RAP BO instance identification. It is used to indicate if a RAP BO + instance is a [draft instance](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_draft_instance_glosry.htm "Glossary Entry") + or an [active instance](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_active_instance_glosry.htm "Glossary Entry"). + Conveniently, the component group `%tky` contains + `%is_draft`. `%is_draft` can then be addressed via + `%tky`. + +> **💡 Note**
+> Late numbering and identification in the late phase of the RAP save sequence +>- Context: RAP saver method +> [`adjust_numbers`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensaver_adjust_numbers.htm) +> in which the final key values are assigned; the preliminary keys can +> be included in `%key` or `%pid` or both of them. +>- `%pid` and the preliminary key values in `%key` are +> automatically assigned to the following component groups when +> reaching the `adjust_numbers` method: +> - [`%tmp`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_tmp.htm): +> A component group that is assigned the preliminary key values +> contained in `%key`. In doing so, `%tmp` takes +> over the role that `%key` has had in the RAP interaction +> phase to hold the preliminary key values. +> - `%pid` remains as is. The component group +> [`%pre`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapderived_types_pre.htm) +> contains `%pid` and `%tmp` and, thus, all +> preliminary identifiers. +>- In the `adjust_numbers` method, the preliminary keys are +> transformed into the final keys, i. e. the preliminary keys are +> mapped to `%key` (which holds the final keys in this +> context) in the `mapped` [response parameter](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrap_response_param_glosry.htm "Glossary Entry"). +>- Depending on your use case to use either `%pid` or (the +> preliminary key values in) `%key` (which is `%tmp` +> here in this method) during the interaction phase or both of them, +> you must ensure that `%pre` in total (since it contains both +> `%pid` and `%tmp`) is unique and mapped to the final +> keys that are to be contained in `%key`. + +
+ +

(back to top)

+ +## Further Information + +- Section [ABAP for RAP Business + Objects](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_for_rap_bos.htm) + in the ABAP Keyword Documentation including EML +- [Development guide for the ABAP RESTful Application Programming + Model](https://help.sap.com/http.svc/ahp2/DRAFT/SAP_S4HANA_ON-PREMISE/2022.000/EN/28/9477a81eec4d4e84c0302fb6835035/frameset.htm) +- [RAP + Contract](https://help.sap.com/http.svc/ahp2/DRAFT/SAP_S4HANA_ON-PREMISE/2022.000/EN/3a/402c5cf6a74bc1a1de080b2a7c6978/frameset.htm): + Rules for the RAP BO provider and consumer implementation to ensure + consistency and reliability + + +

(back to top)

+ +## Executable Examples +This cheat sheet is supported by different executable examples demonstrating various scenarios: +- Demo RAP scenario with a managed RAP BO, external numbering: [zcl_demo_abap_rap_ext_num_m](./src/zcl_demo_abap_rap_ext_num_m.clas.abap) +- Demo RAP scenario with an unmanaged RAP BO, external numbering: [zcl_demo_abap_rap_ext_num_u](./src/zcl_demo_abap_rap_ext_num_u.clas.abap) +- Demo RAP scenario ("RAP calculator") with a managed, draft-enabled RAP BO, late numbering [zcl_demo_abap_rap_draft_ln_m](./src/zcl_demo_abap_rap_draft_ln_m.clas.abap) + +> **💡 Note**
+> - To reduce the complexity, the executable examples are purposely kept simple and only focus on the technical side. ABAP classes play the role of a RAP BO consumer here. +>- The examples do not represent real life scenarios and are not suitable as role models for proper RAP scenarios. They rather focus on the technical side by giving an idea how the communication and data exchange between a RAP BO consumer and RAP BO provider can work. Additionally, the examples show how the methods for non-standard RAP BO operations might be self-implemented in an ABAP behavior pool. +>- Due to the simplification, the examples might not fully meet the requirements of the [RAP BO contract](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenrap_bo_contract_glosry.htm) in many respects. +>- The "RAP calculator" example can be checked out using the preview version of an SAP Fiori Elements UI. See the comments in the class for more information. +>- See the steps outlined [here](README.md#🎬-getting-started-with-the-examples) about how to import and run the code. diff --git a/09_Bits_and_Bytes.md b/09_Bits_and_Bytes.md new file mode 100644 index 0000000..4fa405e --- /dev/null +++ b/09_Bits_and_Bytes.md @@ -0,0 +1,182 @@ + + +# Excursion Down to Bits and Bytes + +This sheet goes a bit into the technical background of data types and +data objects. It may be helpful for a better understanding of how to +handle data in ABAP including a glance on casting and conversions. + +After its declaration, a [data +object](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_object_glosry.htm "Glossary Entry") +is usable in its context (procedure, class, program) according to its +type. For example, a numeric data object can be assigned the result of a +calculation: + +> **💡 Note**
+> For checking out the code snippets in an SAP BTP ABAP environment, you can use the interface `if_oo_adt_classrun` in a class by implementing the method `if_oo_adt_classrun~main`. + +``` abap +DATA num TYPE i. + +num = 2 * 3 * 5 * 53 * 2267. + +cl_demo_output=>display( num ). +``` + +After this assignment, the data object `num` contains the +calculated value 3604530, which is also displayed accordingly with a +type-compliant output as, for example, +`cl_demo_output=>display`. And of course the same can be seen +in the display of the variable in the ABAP Debugger when setting a +breakpoint at the last statement. + +The ABAP Debugger also shows the hexadecimal value 32003700 of the data +object. This directly represents the binary value `0011 0010 0000 0000 +0011 0111 0000 0000` stored in the 4 bytes allocated to the 4-byte +integer number in the memory. This value is platform dependent and for +numeric types is defined by the [byte +order](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenbyte_order_glosry.htm "Glossary Entry") +order, where either the most significant (big endian) or least +significant (little endian) byte is written to the first memory +location. The decimal value of the hexadecimal value `32003700` shown here +would be `838874880` and is not the integer value +`3604530` that ABAP deals with. This shows the meaning of +data types. A data object is a sequence of bytes stored in memory at its +address, which is interpreted by the ABAP runtime framework according to +the data type. The hexadecimal value is in most cases irrelevant to the +programmer. + +Let us now consider a character-like field text with a length of two +characters: + +``` abap +DATA text TYPE c LENGTH 2. + +text = '2' && '7'. + +cl_demo_output=>display( text ). +``` +This field can be assigned the result of a string operation as shown and +the result shown by `cl_demo_output=>display` as well as in +the ABAP Debugger is the character string `27`. Again, it is +the data type, that derives the value `27` from the actual hexadecimal +content, which is `32003700` as in the previous example! In +this case, `32003700` is the encoding of the string +`27` in the Unicode character representation +[UCS-2](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenucs2_glosry.htm "Glossary Entry"), +which is supported by ABAP in Unicode systems. The Unicode character +representation also depends on the platform-dependent byte order. + +Only for a byte-type data type does the value as interpreted by ABAP +directly correspond to the hexadecimal content. The following lines +modify the bits of a byte string with a bit-operation: + +``` abap +DATA hex TYPE x LENGTH 4 VALUE 'CDFFC8FF'. + +hex = BIT-NOT hex. + +cl_demo_output=>display( hex ). +``` + +Here, the output with `cl_demo_output=>display`, the value +display of the ABAP Debugger as well as the hexadecimal value are the +same, namely `32003700`. + +In the above examples, we presented three data objects that all occupy 4 +bytes in memory that have the same binary values, but are handled +differently by ABAP due to their data type. The data type is also +responsible for the fact, that different kind of operations (numeric +calculation, string concatenation, bit-operation) can be applied to the +respective data objects. Using these examples we can have now look at +the basic concepts of +[casting](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencast_casting_glosry.htm "Glossary Entry") +and [type +conversion](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentype_conversion_glosry.htm "Glossary Entry") +and their relation to bits and bytes. + +In ABAP, the term casting means nothing more than treating a data object +according to a different data type than the one that is permanently +assigned to it. This can be done using [field +symbols](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfield_symbol_glosry.htm "Glossary Entry"), +with which a new (symbolic) name and a new type can be defined for the +memory area of a data object. When the memory area is accessed using a +field symbol, it is handled according to the type of the field symbol. +The following lines show an example. + +``` abap +DATA hex TYPE x LENGTH 4 VALUE '32003700'. +FIELD-SYMBOLS:   TYPE i, +               TYPE c. +ASSIGN hex TO   CASTING. +ASSIGN hex TO CASTING. + +cl_demo_output=>new( +  )->write_data( hex +  )->write_data( +  )->write_data( )->display( ). +``` + +The bit string in `hex` is cast to a numeric field when accessed +using the name `` and to a text field when accessed using +the name ``. The outputs are `32003700`, +`3604530` and `27`, clearly showing the effect of +the data type on handling one and the same hexadecimal content. + +In contrast, in a type conversion (or conversion for short), the actual +binary content of a data object is converted so that it fits another +data type. Type conversions usually occur in assignments between data +objects of different data types. The goal of such a conversion is to +preserve the type-specific meaning of the content in the source field as +far as possible for the data type of the target field. For this purpose, +ABAP contains a large set of [conversion +rules](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconversion_rules.htm). +A simple example is shown here: + +``` abap +TYPES hex TYPE x LENGTH 4. +FIELD-SYMBOLS: +   TYPE hex, +    TYPE hex. + +DATA: text TYPE c LENGTH 2 VALUE '27', +      num  TYPE i. + +num = text. + +ASSIGN text TO CASTING. +ASSIGN num  TO   CASTING. + +cl_demo_output=>new( +  )->write_data( text +  )->write_data( +  )->write_data( num +  )->write_data( )->display( ). +``` + +We are assigning the character-like field `text` to the numeric +field `num` and display the result that can also be checked in +the ABAP Debugger. The ABAP runtime framework recognizes that the +character string `27` in text can be interpreted as the +integer number `27`, generates the hexadecimal value 1B000000 +in which this number is encoded for the numeric type of `num`, +and assigns it to the memory location of `num`. Thus, the actual +conversion takes place for the original hexadecimal content +`32003700` of `text` to the new hexadecimal content +`1B000000` of `num`. For character strings in text +fields, for which no such meaningful conversion is possible, an +exception occurs. The field symbols `` and +`` are used to show the hexadecimal content of the +fields `text` and `num` by casting them to a byte-like +type. + +> **✔️ Hint**
+> For reasons of simplicity this sheet is restricted to named elementary +variables. Note that in particular the same holds for +[literals](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_literal_glosry.htm "Glossary Entry") +that are handled internally in such a way as if they were constants of +the data type assigned to the literal. In the preceding example, +`text` can be replaced by a literal `'27'` yielding +the same results. + + diff --git a/10_ABAP_SQL_Hierarchies.md b/10_ABAP_SQL_Hierarchies.md new file mode 100644 index 0000000..9febe82 --- /dev/null +++ b/10_ABAP_SQL_Hierarchies.md @@ -0,0 +1,703 @@ + + +# ABAP SQL: Working with Hierarchies + +This cheat sheet summarizes the functions ABAP SQL offers together with +ABAP CDS for working with [hierarchical +data](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenhierarchy_glosry.htm "Glossary Entry") +that is stored in database tables. Hierarchical data in database tables +means that lines of one or more database tables are connected by +[parent-child +relationships](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenpcr_glosry.htm "Glossary Entry"). +There are many use cases where hierarchical data plays a role and where +accessing information about the hierarchical relationship is important. +For example, a common task can be to find out the descendants or +ancestors of a given hierarchy node or to aggregate values of subtrees. + +- [ABAP SQL: Working with Hierarchies](#abap-sql-working-with-hierarchies) + - [Overview](#overview) + - [SQL Hierarchies](#sql-hierarchies) + - [Creating SQL Hierarchies](#creating-sql-hierarchies) + - [ABAP CDS Hierarchies](#abap-cds-hierarchies) + - [ABAP SQL Hierarchy Generator HIERARCHY](#abap-sql-hierarchy-generator-hierarchy) + - [ABAP CTE Hierarchies](#abap-cte-hierarchies) + - [Hierarchy Navigators](#hierarchy-navigators) + - [Hierarchy Node Navigator HIERARCHY\_DESCENDANTS](#hierarchy-node-navigator-hierarchy_descendants) + - [Hierarchy Node Navigator HIERARCHY\_ANCESTORS](#hierarchy-node-navigator-hierarchy_ancestors) + - [Hierarchy Node Navigator HIERARCHY\_SIBLINGS](#hierarchy-node-navigator-hierarchy_siblings) + - [Hierarchy Aggregate Navigators](#hierarchy-aggregate-navigators) + - [Further Information](#further-information) + + +## Overview + +In former times you had to load the data from the database into internal +tables and program it all by yourself (if you did not find an +appropriate API). In between, +[meshes](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenmesh_glosry.htm "Glossary Entry") +offered some features for working with hierarchies, as shown in this +[example](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenmesh_for_reflex_sngl_abexa.htm), +but have not found wide distribution. + +Meanwhile, the standard AS ABAP database is a SAP HANA database that +offers a lot of helpful features. Among other things, you will find a +set of hierarchy functions there that allow you to deal with +hierarchical data directly on the database and that you can look up in +the [SAP HANA +documentation](https://help.sap.com/http.svc/ahp2/DRAFT/SAP_S4HANA_ON-PREMISE/2022.000/EN/20/ff532c751910148657c32fe3431a9/frameset.htm). +Now you might expect that you must use +[AMDP](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenamdp.htm) +in order to access these functions from your ABAP programs, but no need +to do so! ABAP SQL and ABAP CDS support hierarchies directly by wrapping +the HANA built-in functions without any loss of performance. You can +stay in the comfortable ABAP world and nevertheless have access to most +modern features. All you have to do, is to understand some concepts and +learn some additional syntax and then you can start right away. + +> **💡 Note**
+> The examples in this cheat sheet are only relevant for [standard ABAP](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenstandard_abap_glosry.htm), i. e. the unrestricted ABAP language scope. Find the artifacts used in the code snippets in your on-premise system. + +## SQL Hierarchies + +With [SQL +hierarchy](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_hierarchy_glosry.htm "Glossary Entry") +we denote a special [hierarchical data +source](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenselect_hierarchy_data.htm) +that you can use in the `FROM` clause of ABAP SQL queries. A SQL +hierarchy is a tabular set of rows which form the hierarchy nodes of a +hierarchy and which contains additionally [hierarchy +columns](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenhierarchy_column_glosry.htm "Glossary Entry") +that contain hierarchy attributes with hierarchy-specific information +for each row. For creating a SQL hierarchy, you need the following: + +- Data Source + + This can be any data source you can access normally in an ABAP SQL + query, as most commonly a database table or a CDS view, but also a + CTE (common table expression). The structure and content of the data + source should be able to represent hierarchical data. + +- Parent-Child Relation + + A parent-child relation must be defined between two or more columns + of the data source. From the parent-child relation and the actual + data of the data source, the SQL hierarchy consisting of parent + nodes and child nodes can be created. The parent-child relation must + be defined by a self-association which we call hierarchy + association. This can be achieved with CDS associations or CTE + associations. A data source exposing a hierarchy association can be + used as a hierarchy source for creating a SQL hierarchy. + +- Hierarchy Creation + + From a hierarchy source, that is a data source exposing a hierarchy + association, a SQL hierarchy can be created. This can be done either + by defining a CDS hierarchy outside an ABAP program or with the + hierarchy generator of ABAP SQL directly in the `FROM` + clause of an ABAP SQL query inside an ABAP program. + +The following topics show you step-by-step how SQL hierarchies can be +created and accessed. + +## Creating SQL Hierarchies + +### ABAP CDS Hierarchies +With [CDS +hierarchies](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenhierarchy_column_glosry.htm "Glossary Entry"), +you outsource the hierarchy data source and the creation of the SQL +hierarchy from your ABAP program to ABAP CDS. Here the hierarchy is a +fully fledged CDS entity, it is reusable in different programs or in +other CDS entities (views), and can be part of your data model including +access control using CDS DCL. For a CDS hierarchy, the hierarchy source +cannot be anything else but a CDS view that exposes a [hierarchy +association](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenhierarchy_association_glosry.htm "Glossary Entry"). +Here is a very simple example for that: + +``` abap +@AccessControl.authorizationCheck: #NOT_REQUIRED +define view entity DEMO_CDS_SIMPLE_TREE_VIEW +  as select from demo_simple_tree +  association [1..1] to DEMO_CDS_SIMPLE_TREE_VIEW as _tree   +    on $projection.parent = _tree.id +{ +      _tree, +   key id, +      parent_id as parent, +      name +} +``` + +This CDS view entity accesses the database table +`DEMO_SIMPLE_TREE`, where the actual data is +stored, and exposes a +[self-association](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenself_association_glosry.htm "Glossary Entry") +`_tree`. The `ON` condition of the association +defines a parent-child relation between the elements `id` and +`parent`. It simply means that a row of the result set where +column `parent` has the same value as the column +`id` of another row is a child of the latter row in the +hierarchy that is constructed from that view. The CDS view exposes also +another column `name` of the database table that represents +the remaining data content. Note that you can define such CDS views for +any available data source and that the `ON` condition can be +more complex than shown in the simple example here. + +Now we can use the above CDS view as the hierarchy source of a CDS +hierarchy that can be defined as follows: + +``` abap +define hierarchy DEMO_CDS_SIMPLE_TREE +  with parameters +    p_id : abap.int4 +  as parent child hierarchy( +    source +      DEMO_CDS_SIMPLE_TREE_SOURCE +      child to parent association _tree +      start where +        id = :p_id +      siblings order by +        id ascending +    ) +    { +      id, +      parent, +      name +    } +``` + +The CDS DDL statement [`DEFINE +HIERARCHY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencds_f1_define_hierarchy.htm) +that can be used in the DDL source code editor of ADT defines a CDS +hierarchy as a CDS entity that can be accessed in CDS views or ABAP SQL +as a SQL hierarchy. The most important additions of the statement are: + +- `SOURCE` for specifying the hierarchy source, here our + `DEMO_CDS_SIMPLE_TREE_VIEW`. +- `CHILD TO PARENT ASSOCIATION` for specifying the + hierarchy association, here `_tree`. +- `START WHERE` for defining the root nodes of the SQL + hierarchy, here represented by an input parameter `p_id` + that must be passed when accessing the CDS hierarchy. +- `SIBLINGS ORDER BY` to define also a sort order for + sibling nodes besides the sort order that comes from the + parent-child relationship anyhow. +- An element list `{ ... }` that defines the columns of + the SQL hierarchy, here simply all elements of the hierarchy source. + +For a full description and all other additions see [`DEFINE +HIERARCHY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencds_f1_define_hierarchy.htm). + +When you access the CDS hierarchy, all lines are selected from the +original data source, in our case the database table +`DEMO_SIMPLE_TREE`, that fulfill the `START WHERE` condition. Those make up the root node set of the SQL +hierarchy. In the simplest case we have exactly one root node, but more +are possible. Then, for each root node, its descendants are retrieved. +That means each line from the database table that fulfills the +`ON`-condition of the hierarchy association is added to the +SQL hierarchy. And for each descendant this is done again and again +until all descendants are found. And that is basically all! Further +additions to `DEFINE HIERARCHY` allow you to control the +creation of the SQL hierarchy, for example, whether multiple parents are +allowed or how orphans or cycles should be handled. + +Besides the elements of the hierarchy, the element list can also contain +the hierarchy attributes listed under [Hierarchy +Attributes](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencds_hierarchy_attributes.htm). +Then the SQL hierarchy is enriched with columns containing information +about the role, the current line plays as a hierarchy node, as, for +example, the hierarchy rank or the hierarchy level. In our example, we +did not add such elements, because ABAP SQL does that implicitly for you +when accessing the CDS hierarchy! + +The SQL hierarchy can be used in an ABAP SQL query by using the CDS +hierarchy directly as a data source of the `FROM` clause: + +``` abap +DATA root_id type demo_cds_simple_tree_view-id. + +... + +SELECT FROM demo_cds_simple_tree( p_id = @root_id ) +       FIELDS id, +              parent, +              name, +              hierarchy_rank, +              hierarchy_tree_size, +              hierarchy_parent_rank, +              hierarchy_level, +              hierarchy_is_cycle, +              hierarchy_is_orphan, +              node_id, +              parent_id +           INTO TABLE @FINAL(cds_result). +``` + +And although we did not define any hierarchy attributes in the element +list of the CDS hierarchy, we can add all the hierarchy columns listed +under [Hierarchy +Columns](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenddddl_hierarchy.htm) +to the `SELECT` list of our ABAP SQL statement! This is always +possible when a SQL hierarchy is accessed in ABAP SQL. We can pass any +ID to the CDS hierarchy now and see what happens. If such a line is +found in the database table, the respective hierarchical data will be +retrieved and delivered. Execute class +`CL_DEMO_SQL_HIERARCHIES` for filling the +database table with randomly generated data and inspect the tabular +result. As expected, all elements of the `SELECT` list appear as +columns. Note that the content of column `NAME` could be +anything. It is filled here with a string representation of the path +from the root node to the current node for demonstration purposes only. + +From the ABAP language point of view, CDS hierarchies are the most +convenient way of using SQL hierarchies. Now let us turn to other ways, +involving more ABAP, until we do not use any CDS more in the end. + +### ABAP SQL Hierarchy Generator HIERARCHY +The ABAP SQL [hierarchy +generator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenhierarchy_generator_glosry.htm "Glossary Entry") +is a ABAP SQL function named +[`HIERARCHY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenselect_hierarchy_generator.htm), +that allows you to define a SQL hierarchy in the ABAP program itself. +Let us look directly at an example: + +``` abap +DATA root_id TYPE demo_cds_simple_tree_view-id. + +... + +SELECT FROM HIERARCHY( SOURCE demo_cds_simple_tree_view +                       CHILD TO PARENT ASSOCIATION _tree +                       START WHERE id = @root_id +                       SIBLINGS ORDER BY id +                       MULTIPLE PARENTS NOT ALLOWED ) "hierarchy +] +       FIELDS id, +              parent, +              name, +              hierarchy_rank, +              hierarchy_tree_size, +              hierarchy_parent_rank, +              hierarchy_level, +              hierarchy_is_cycle, +              hierarchy_is_orphan, +              node_id, +              parent_id +       INTO TABLE @FINAL(asql_cds_result). + +ASSERT asql_cds_result = cds_result. +``` + +Looks familiar? Well, almost the same syntax used for defining the CDS +hierarchy is used in the brackets `HIERARCHY( ... )` and it +does exactly the same! The difference is the same as it is between ABAP +SQL joins and joins in CDS views: + +- If you code it in ABAP SQL, it is for usage in one program only. +- If you code it in ABAP CDS, it is for usage in many programs or + whole data models. + +And, as you can see, we dare to prove this with an `ASSERT` +statement. Also note that we use the hierarchy columns again. They exist +implicitly when an SQL hierarchy, here created by the hierarchy +generator, is accessed. + +The above hierarchy generator of ABAP SQL accesses the same hierarchy +source as the CDS hierarchy, namely the CDS view +`DEMO_CDS_SIMPLE_TREE_VIEW` that exposes the necessary +hierarchy association `_tree`. In the following code +snippet, we replace the CDS hierarchy source with a CTE: + +``` abap +DATA root_id type demo_cds_simple_tree_view-id. + +... + +WITH +  +cte_simple_tree_source AS +     ( SELECT FROM demo_simple_tree +              FIELDS id, +                     parent_id AS parent, +                     name ) +        WITH ASSOCIATIONS ( +          JOIN TO MANY +cte_simple_tree_source AS _tree +            ON +cte_simple_tree_source~parent = _tree~id ) +  SELECT FROM HIERARCHY( SOURCE +cte_simple_tree_source +                         CHILD TO PARENT ASSOCIATION _tree +                         START WHERE id = @root_id +                         SIBLINGS ORDER BY id +                         MULTIPLE PARENTS NOT ALLOWED ) "hierarchy +] +         FIELDS id, +                parent, +                name, +                hierarchy_rank, +                hierarchy_tree_size, +                hierarchy_parent_rank, +                hierarchy_level, +                hierarchy_is_cycle, +                hierarchy_is_orphan, +                node_id, +                parent_id +         INTO TABLE @FINAL(asql_cte_result). ] + +ASSERT asql_cte_result = cds_result. +``` + +Common table expressions (CTEs) are a very powerful tool for defining +subqueries that can be used in subsequent queries of the same +[`WITH`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapwith.htm) +statement. They can be regarded as an internal ABAP SQL definition of +data sources that fulfill the same functionality as program external +data sources, especially CDS views. As you see above, the CTE +`cte_simple_tree_source` does the same as the CDS view +`DEMO_CDS_SIMPLE_TREE_VIEW`: + +- It accesses the database table `DEMO_SIMPLE_TREE`. +- It exposes an association `_tree` by using the addition + [`WITH ASSOCIATIONS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapwith_associations.htm). + +The main query of the `WITH` statement uses the hierarchy +generator in the same way as the `SELECT` above, just with the +CTE as a data source instead of the CDS view and the result is - of +course - the same. + +For a full description of the hierarchy generator and all other +additions see [`SELECT, FROM HIERARCHY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenselect_hierarchy_generator.htm). + +We managed to create a SQL hierarchy with ABAP SQL means only. Last but +not least we will use CTEs as hierarchies themselves. You might skip the +following section and turn directly to the hierarchy navigators if you +are not too interested in this syntactic gimmicks. + +### ABAP CTE Hierarchies + +A CTE that produces hierarchical data can declare itself as a SQL +hierarchy of a freely defined name with the addition [`WITH HIERARCHY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapwith_hierarchy.htm). +That simply means that subsequent queries of the same `WITH` +statement can use the CTE as a hierarchy with its implicit hierarchy +columns or - more important - in hierarchy navigators. + +The following code snippets show the three ways in which a CTE can +produce hierarchical data: + +``` abap +DATA root_id TYPE demo_cds_simple_tree_view-id. + +... + +WITH +      +tree AS +        ( SELECT FROM demo_cds_simple_tree( p_id = @root_id ) +                 FIELDS * ) +          WITH HIERARCHY demo_cds_simple_tree +      SELECT FROM  +tree "hierarchy ] +             FIELDS id, +                    parent, +                    name, +                    hierarchy_rank, +                    hierarchy_tree_size, +                    hierarchy_parent_rank, +                    hierarchy_level, +                    hierarchy_is_cycle, +                    hierarchy_is_orphan, +                    node_id, +                    parent_id +             INTO TABLE @FINAL(cte_cds_result). + +... + +WITH +      +tree AS +        ( SELECT FROM HIERARCHY( +            SOURCE demo_cds_simple_tree_view +            CHILD TO PARENT ASSOCIATION _tree +            START WHERE id = @root_id +            SIBLINGS ORDER BY id +            MULTIPLE PARENTS NOT ALLOWED ) AS asql_hierarchy +            FIELDS id, +                   parent, +                   name ) +          WITH HIERARCHY asql_hierarchy +      SELECT FROM +tree "hierarchy ] +             FIELDS id, +                    parent, +                    name, +                    hierarchy_rank, +                    hierarchy_tree_size, +                    hierarchy_parent_rank, +                    hierarchy_level, +                    hierarchy_is_cycle, +                    hierarchy_is_orphan, +                    node_id, +                    parent_id +             INTO TABLE @FINAL(cte_asql_result). + +... + +WITH +      +cte_simple_tree_source AS +        ( SELECT FROM demo_simple_tree +                 FIELDS id, +                        parent_id AS parent, +                        name ) +           WITH ASSOCIATIONS ( +             JOIN TO MANY +cte_simple_tree_source AS _tree +               ON +cte_simple_tree_source~parent = _tree~id ), +      +tree AS +        ( SELECT FROM HIERARCHY( +            SOURCE +cte_simple_tree_source +            CHILD TO PARENT ASSOCIATION _tree +            START WHERE id = @root_id +            SIBLINGS ORDER BY id +            MULTIPLE PARENTS NOT ALLOWED ) AS cte_hierarchy +            FIELDS id, +                   parent, +                   name  ) +            WITH HIERARCHY cte_hierarchy +      SELECT FROM +tree "hierarchy ] +             FIELDS id, +                    parent, +                    name, +                    hierarchy_rank, +                    hierarchy_tree_size, +                    hierarchy_parent_rank, +                    hierarchy_level, +                    hierarchy_is_cycle, +                    hierarchy_is_orphan, +                    node_id, +                    parent_id +             INTO TABLE @FINAL(cte_cte_result). + +ASSERT cte_cds_result  = cds_result. +ASSERT cte_asql_result = cds_result. +ASSERT cte_cte_result  = cds_result. +``` + +A CTE that is exposed as a SQL hierarchy must access a SQL hierarchy +itself and in the end these are always based on a CDS hierarchy or the +ABAP SQL hierarchy generator as shown above. Again, the hierarchy source +of the hierarchy generator can be a CDS view or a CTE exposing the +hierarchy association. Running +`CL_DEMO_SQL_HIERARCHIES` shows that all +assertions are fulfilled. + +## Hierarchy Navigators + +[Hierarchy +navigators](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenhierarchy_navigator_glosry.htm "Glossary Entry") +are an additional set of [hierarchy +functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenhierarchy_function_glosry.htm "Glossary Entry") +in ABAP SQL that allow you to work on existing SQL hierarchies instead +of creating them. Hierarchy navigators can work on SQL hierarchies +created as shown above, namely on CDS hierarchies, the hierarchy +generator or a CTE hierarchy. They can be used as data sources in ABAP +SQL queries. If you need a SQL hierarchy multiple times, from a +performance point of view it is best to create it once with a given set +of root nodes and then access it with hierarchy navigators. Furthermore, +each hierarchy navigator can add further hierarchy columns to the result +set that offer additional options for the evaluation. + +In the following examples, we access our CDS hierarchy with hierarchy +navigators. But you could also replace it with the hierarchy generator +or a CTE hierarchy. Check the examples of the +[documentation](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenselect_hierarchy_navigators.htm), +where this is also shown. + +### Hierarchy Node Navigator HIERARCHY_DESCENDANTS + +As the name says, +[`HIERARCHY_DESCENDANTS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenselect_hierarchy_node_navis.htm) +fetches all descendants for any nodes from a SQL hierarchy. It adds +`HIERARCHY_DISTANCE` as an additional hierarchy column to +the result set. Let us look at an example. All examples are code +snippets from `CL_DEMO_SQL_HIERARCHIES` again. + +``` abap +DATA root_id TYPE demo_cds_simple_tree_view-id. + +DATA sub_id TYPE demo_cds_simple_tree_view-id. + +... + +SELECT FROM HIERARCHY_DESCENDANTS( +              SOURCE demo_cds_simple_tree( p_id = @root_id ) +              START WHERE id = @sub_id  ) +  FIELDS id, +         parent_id, +         name, +         hierarchy_distance +  INTO TABLE @FINAL(descendants). +``` + +Our CDS hierarchy `DEMO_CDS_SIMPLE_TREE_VIEW` is used to +create a SQL hierarchy with a start node passed to parameter +`p_id` and for a node `sub_id` all descendants +are fetched. Running the program shows the result including the +additional column `HIERARCHY_DISTANCE` that contains the +distance to the respective start node. A further parameter +`DISTANCE` - not shown here - allows you to restrict the +distance to the respective start node. + +### Hierarchy Node Navigator HIERARCHY_ANCESTORS + +Now the other way around: ABAP SQL function +[`HIERARCHY_ANCESTORS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenselect_hierarchy_node_navis.htm) +returns the ancestors of any given node of an existing hierarchy: + +``` abap +DATA root_id TYPE demo_cds_simple_tree_view-id. + +DATA max_id TYPE demo_cds_simple_tree_view-id. + +... + +SELECT FROM HIERARCHY_ANCESTORS( +              SOURCE demo_cds_simple_tree( p_id = @root_id ) +              START WHERE id = @max_id ) +  FIELDS id, +          parent_id, +          name, +          hierarchy_distance +  INTO TABLE @FINAL(ancestors). +``` + +Looking at the result when running +`CL_DEMO_SQL_HIERARCHIES`, you see that the +value of column `HIERARCHY_DISTANCE` is negative now. Using +aggregate functions or evaluating the internal result table, you can now +easily extract further information like the number of ancestors and so +on. + +### Hierarchy Node Navigator HIERARCHY_SIBLINGS + +Besides descendants and ancestors, hierarchy nodes also can have +siblings, that is nodes that have the same parent node. You can find +these by looking for all nodes with the same value in hierarchy column +`HIERARCHY_PARENT_RANK`. But there is also +[`HIERARCHY_SIBLINGS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenselect_hierarchy_node_navis.htm) +as a hierarchy function for that: + +``` abap +DATA root_id TYPE demo_cds_simple_tree_view-id. + +DATA sibl_id TYPE demo_cds_simple_tree_view-id. + +... + +SELECT FROM HIERARCHY_SIBLINGS( +              SOURCE demo_cds_simple_tree( p_id = @root_id ) +              START WHERE id = @sibl_id ) +  FIELDS id, +          parent_id, +          name, +          hierarchy_sibling_distance +  INTO TABLE @FINAL(siblings). +``` + +You see that this function adds another hierarchy column +`HIERARCHY_SIBLING_DISTANCE` that contains the distance to +the respective start node. Running +`CL_DEMO_SQL_HIERARCHIES`, where we start with +a node that definitely has some siblings, shows the result. + +### Hierarchy Aggregate Navigators + +Finally let us turn to the [hierarchy aggregate +navigators](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenhierarchy_agg_navi_glosry.htm "Glossary Entry") +that allow you to apply some aggregate functions to descendants and +ancestors of any node of a SQL hierarchy: + +- [`HIERARCHY_DESCENDANTS_AGGREGATE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenselect_hierarchy_desc_agg.htm) +- [`HIERARCHY_ANCESTORS_AGGREGATE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenselect_hierarchy_ancs_agg.htm) + +We will show an example for the descendants case and refer to the +[documentation](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenselect_hierarchy_ancs_agg.htm) +for the ancestors. + +Applying aggregate functions to columns normally means that you have +some data there for which this makes sense. In our simplistic SQL +hierarchy tree we do not have such meaningful data. On the other hand, +this can also be a use case: You can have the administrative data for +the parent-child relation in one database table and the real data in +another one. And for that use case, the hierarchy aggregate navigator +`HIERARCHY_DESCENDANTS_AGGREGATE` gives you the option of +joining such data to your SQL hierarchy: + +``` abap +TYPES: +  BEGIN OF value, +    id     TYPE i, +    amount TYPE p LENGTH 16 DECIMALS 2, +  END OF value. + +DATA value_tab TYPE SORTED TABLE OF value WITH UNIQUE KEY id. + +DATA root_id TYPE demo_cds_simple_tree_view-id. + +DATA sub_id TYPE demo_cds_simple_tree_view-id. + +... + +SELECT FROM HIERARCHY_DESCENDANTS_AGGREGATE( +              SOURCE demo_cds_simple_tree( p_id = @sub_id  ) AS h +              JOIN @value_tab AS v +                ON  v~id = h~id +              MEASURES SUM( v~amount ) AS amount_sum +              WHERE hierarchy_rank > 1 +              WITH SUBTOTAL +              WITH BALANCE ) +  FIELDS id, +         amount_sum, +         hierarchy_rank, +         hierarchy_aggregate_type +  INTO TABLE @FINAL(descendants_aggregate). +``` + +In our example, we join an internal table `value_tab` of the +same program to the SQL hierarchy. In a real life example you would join +another database table, of course. On the other hand the example shows +ABAP SQL's capability of using internal tables as data sources. You +even can go so far to evaluate hierarchical data in internal tables with +ABAP SQL by using an internal table as data source for a CTE hierarchy! + +The example does the following: + +- We use the hierarchy aggregate navigator + `HIERARCHY_DESCENDANTS_AGGREGATE` as a data source of a + `FROM` clause. +- Our CDS hierarchy `DEMO_CDS_SIMPLE_TREE_VIEW` joined + with internal table `value_tab` is used as the data source. +- The ABAP SQL function returns a tabular result of nodes of the data + source. +- The aggregate function `SUM` behind `MEASURES` sums + up the values of the column amounts of the joined internal table for + all descendants of each node returned by the ABAP SQL function. +- The `WHERE` condition restricts the result set by a freely + programmable condition. +- The `WITH` additions add further rows to the result set that + can be recognized by values in an additional hierarchy column + `HIERARCHY_AGGREGATE_TYPE`: + + - `WITH SUBTOTAL` + + In the row where `HIERARCHY_AGGREGATE_TYPE` has + value 1, column `AMOUNT_SUM` contains the sum of the + values of all hierarchy nodes that meet the `WHERE` + condition. + + - `WITH BALANCE` + + In the row where `HIERARCHY_AGGREGATE_TYPE` has + value 2, column `AMOUNT_SUM` contains the sum of the + values of all hierarchy nodes that do not meet the + `WHERE` condition. + + For more `WITH` additions see the + [documentation](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenselect_hierarchy_desc_agg.htm). + +Running `CL_DEMO_SQL_HIERARCHIES` shows the +result. It also shows the result of the joined data source, where you +can check that the calculated values are correct. + +## Further Information +For the complete reference documentation about SQL hierarchies, see [`SELECT, FROM hierarchy_data`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenselect_hierarchy_data.htm). diff --git a/11_ABAP_SQL_Grouping_Internal_Tables.md b/11_ABAP_SQL_Grouping_Internal_Tables.md new file mode 100644 index 0000000..f7087f2 --- /dev/null +++ b/11_ABAP_SQL_Grouping_Internal_Tables.md @@ -0,0 +1,171 @@ + + +# ABAP SQL: Grouping Internal Tables + +- [ABAP SQL: Grouping Internal Tables](#abap-sql-grouping-internal-tables) + - [Introduction](#introduction) + - [Grouping by One Column](#grouping-by-one-column) + - [Grouping by More than One Column](#grouping-by-more-than-one-column) + - [Group Key Binding when Grouping by One Column](#group-key-binding-when-grouping-by-one-column) + - [Group Key Binding when Grouping by More than One Column](#group-key-binding-when-grouping-by-more-than-one-column) + - [Executable Example](#executable-example) + + +## Introduction + +Similar to SQL's [`GROUP BY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapgroupby_clause.htm), +there is also a [`GROUP BY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaploop_at_itab_group_by.htm) +for working with internal tables that can be used behind [`LOOP AT itab`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaploop_at_itab_variants.htm) +or in the form [`IN GROUP`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfor_in_group.htm) +in a table iteration with +[`FOR`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfor_itab.htm). +It replaces the clumsy group level processing with statements [`AT NEW ...`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapat_itab.htm) +that relies on the order of table columns and content that is sorted +respectively. + +Thi cheat sheet explains the grouping of internal tables step by step +using a very simple case of an internal table `spfli_tab` that +is filled with data from the database table `SPFLI`. The +following steps show how the content of the internal table can be +grouped using [`LOOP AT GROUP BY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaploop_at_itab_group_by.htm). + +## Grouping by One Column + +The simplest form of grouping is by one column without explicitly +specifying the output behavior of the group loop: + +``` abap +LOOP AT spfli_tab INTO wa +                  GROUP BY wa-carrid. +       ... wa-carrid ... +ENDLOOP. +``` + +Within the loop, there is access to the work area `wa`, in +particular to the component `wa-carrid` that is used for +grouping. The work area `wa` contains the first line of each +group and represents the group in the loop. This is called +representative binding. + +To access the members of a group, a member loop can be inserted into the +group loop: +``` abap +LOOP AT spfli_tab INTO wa +                  GROUP BY wa-carrid. +  ... +  LOOP AT GROUP wa INTO DATA(member). +    ... member-... ... +  ENDLOOP. +  ... +ENDLOOP. +``` + +The member loop is executed using the group represented by `wa` +and its members are assigned to `member` and are available in +the member loop. + +## Grouping by More than One Column + +To group by more than just one criterion, a structured group key is +defined as follows. In the simplest case, the grouping criteria are +columns of the internal table: + +``` abap +LOOP AT spfli_tab INTO wa +                  GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom ). +  ... wa-carrid ... wa-airpfrom ... +ENDLOOP. +``` + +This is also a representative binding in which the work area +`wa` is reused in the group loop to access the group key. + +To access the members of the groups, the exact same member loop can be +inserted as when grouping by one column. + +## Group Key Binding when Grouping by One Column + +By explicitly specifying an [output +area](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaploop_at_itab_group_by_binding.htm) +for the group key, a group key binding can be defined explicitly instead +of the representative binding in which the output area of the group loop +is reused: + +``` abap +LOOP AT spfli_tab INTO wa +                  GROUP BY wa-carrid +                  INTO DATA(key). +  ... key ... +ENDLOOP. +``` + +The difference to the example with representative binding is the +`INTO` addition after `GROUP BY`. Instead of reusing +`wa`, an elementary data object `key` represents the +group. This can be generated inline. The additions [`GROUP +SIZE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaploop_at_itab_group_by_key.htm), +[`GROUP +INDEX`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaploop_at_itab_group_by_key.htm), +and [`WITHOUT +MEMBERS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaploop_at_itab_group_by.htm) +can only be used in the group key binding, which gives it more functions +than the representative binding. If these are not required, the +representative binding can be used. The group key binding can also be +used to make the use of the group key in the loop more explicit. + +Inserting a member loop works in the same way as in the representative +binding, with the difference that a group is now addressed by +`key` instead of `wa`. + +``` abap +LOOP AT spfli_tab INTO wa +                  GROUP BY wa-carrid +                  INTO key. +  ... +  LOOP AT GROUP key INTO member. +    ... members ... +  ENDLOOP. +  ... +ENDLOOP. +``` + +## Group Key Binding when Grouping by More than One Column +Finally, the group key binding for structured group keys: + +``` abap +LOOP AT spfli_tab INTO wa +                  GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom ) +                  INTO DATA(key). +  ... key-key1 ... key-key2 ... +ENDLOOP. +``` + +Here, `key` is a structure with the components `key1` +and `key2`. A member loop can be inserted in exactly the same +way as when grouping by one column. + +If the group members are not relevant, the addition [`NO +MEMBERS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaploop_at_itab_group_by.htm) +can be used to save time and memory. + +``` abap +LOOP AT spfli_tab INTO wa +                  GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom +                             index = GROUP INDEX size = GROUP SIZE ) +                  WITHOUT MEMBERS +                  INTO DATA(key). +  ... key-key1 ... key-key2 ... key-index ... key-size ... +ENDLOOP. +``` + +It is no longer possible to use a member loop here. Instead, the group +key was enriched with optional components for further information using +[`GROUP +INDEX`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaploop_at_itab_group_by_key.htm) +[`GROUP +SIZE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaploop_at_itab_group_by_key.htm). + +## Executable Example +[zcl_demo_abap_sql_group_by](./src/zcl_demo_abap_sql_group_by.clas.abap) + +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/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSES/Apache-2.0.txt b/LICENSES/Apache-2.0.txt new file mode 100644 index 0000000..137069b --- /dev/null +++ b/LICENSES/Apache-2.0.txt @@ -0,0 +1,73 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d19e2c5 --- /dev/null +++ b/README.md @@ -0,0 +1,206 @@ +
+
+ + ABAP Keyword Documentation + + +

ABAP Cheat Sheets

+ +

+ Explore ABAP syntax in a nutshell & executable examples +
+ +
+ How to Use + · + Cheat Sheets + · + Examples +

+
+
+
+
+ +ABAP cheat sheets[^1] ... +- provide a **collection of information on selected ABAP topics** in a nutshell for your reference. +- focus on **ABAP syntax**. +- include **code snippets**. +- are supported by easy-to-consume **demonstration examples** that you can import into your [SAP BTP ABAP environment](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensap_btp_abap_env_glosry.htm) or on-premise ABAP system using [abapGit](https://abapgit.org/) to run and check out ABAP syntax in action in simple contexts. +- are enriched by links to glossary entries and chapters of the **ABAP Keyword Documentation** (the *F1 help*) for you to deep dive into the respective ABAP topics and get more comprehensive information. + +
+💡 Note +
+ +- Since the ABAP cheat sheets provide information in a nutshell, they do not claim to be exhaustive as far as the described syntax and concepts are concerned. If you want to go more into the details, just consult the ABAP Keyword Documentation, for example, by choosing `F1` for a keyword in your code or directly researching via the online version or the system-internal version. +- If not stated otherwise in the cheat sheets and examples, the content of this repository is relevant for these ABAP language versions: + - [ABAP for Cloud Development](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenabap_for_sap_cloud_glosry.htm): Restricted ABAP language scope for developments in the [SAP BTP ABAP environment](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abensap_btp_abap_env_glosry.htm) → [Online version of the documentation](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm) + - [Standard ABAP](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenstandard_abap_glosry.htm): Unrestricted ABAP language scope, for example, for developments in an on-premise ABAP system → [Online version of the documentation (latest version)](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenabap.htm) +- Check the [Known Issues](#⚡-known-issues) and [Disclaimer](#⚠️-disclaimer). +- In the cheat sheets, links are available to glossary entries and topics of the ABAP Keyword Documentation. Note that these links refer to the ABAP for Cloud Development version in most cases. +
+ +
+ +## 🏗️ How to Use + +1. **ABAP syntax info**: Get info in a nutshell on ABAP syntax and concepts related to various ABAP topics in the [ABAP cheat sheets](#📝-abap-cheat-sheets-overview). +2. **Demo examples**: Import the ABAP development objects of this repository into your system using [abapGit](https://abapgit.org/) as described [here](#🎬-getting-started-with-the-examples) and run the demo classes by choosing `F9` in the [ABAP Development Tools (ADT)](https://tools.eu1.hana.ondemand.com/) for checking out the ABAP syntax in action. + + +## 📝 ABAP Cheat Sheets Overview + +| Cheat Sheet | Topics Covered | Demo Example | +| ------------- | ------------- | ----- | +|[Working with Internal Tables](01_Internal_Tables.md)| Creating, filling, reading from, sorting, modifying internal tables | [zcl_demo_abap_internal_tables](./src/zcl_demo_abap_internal_tables.clas.abap) | +|[Working with Structures](02_Structures.md)| Creating structures and structured types, variants of structures, accessing components of structures, filling structures, clearing structures, structures in use in the context of tables | [zcl_demo_abap_structures](./src/zcl_demo_abap_structures.clas.abap) | +|[ABAP SQL: Working with Persisted Data in Database Tables](03_ABAP_SQL.md)| Reading from database tables using `SELECT`, changing data in database tables using `INSERT`, `UPDATE`, `MODIFY` and `DELETE` | [zcl_demo_abap_sql](./src/zcl_demo_abap_sql.clas.abap) | +|[ABAP Object Orientation](04_ABAP_Object_Orientation.md)| Working with objects and components, concepts like inheritance, interfaces, and more | [zcl_demo_abap_objects](./src/zcl_demo_abap_objects.clas.abap) | +|[Working with Constructor Expressions](05_Constructor_Expressions.md)| Operators `VALUE`, `CORRESPONDING`, `NEW`, `CONV`, `EXACT`, `REF`, `CAST`, `COND`, `SWITCH`, `FILTER`, `REDUCE`, iteration expressions with `FOR`, `LET` expressions | [zcl_demo_abap_constructor_expr](./src/zcl_demo_abap_constructor_expr.clas.abap) | +|[Dynamic Programming](06_Dynamic_Programming.md)| Touches on field symbols and data references as supporting elements for dynamic programming, dynamic ABAP syntax components, runtime type services (RTTS), i. e. runtime type identification (RTTI) and runtime type creation (RTTC) | [zcl_demo_abap_dynamic_prog](./src/zcl_demo_abap_dynamic_prog.clas.abap) | +|[String Processing](07_String_Processing.md)| Creating strings and assigning values, chaining strings, string templates, concatenating, splitting, modifying strings, searching and replacing, regular expressions | [zcl_demo_abap_string_proc](./src/zcl_demo_abap_string_proc.clas.abap) | +|[ABAP for RAP: Entity Manipulation Language (ABAP EML)](08_EML_ABAP_for_RAP.md)| Setting EML in the context of RAP, standard (create, read, update, delete) and non-standard operations (actions) |
  • [Demo RAP scenario with a managed RAP BO, external numbering (zcl_demo_abap_rap_ext_num_m)](./src/zcl_demo_abap_rap_ext_num_m.clas.abap)

  • [Demo RAP scenario with an unmanaged RAP BO, external numbering (zcl_demo_abap_rap_ext_num_u)](./src/zcl_demo_abap_rap_ext_num_u.clas.abap)

  • [Demo RAP scenario ("RAP calculator") with a managed, draft-enabled RAP BO, late numbering (zcl_demo_abap_rap_draft_ln_m)](./src/zcl_demo_abap_rap_draft_ln_m.clas.abap)
    Note that this example can also be checked out using the preview version of an SAP Fiori UI. Check the comments in the class for the steps.
| +|[Excursion Down to Bits and Bytes](09_Bits_and_Bytes.md)|Touches on the technical background of data types and data objects|-| +|[ABAP SQL: Working with Hierarchies](10_ABAP_SQL_Hierarchies.md)|Summarizes the functions ABAP SQL offers together with ABAP CDS for working with hierarchical data that is stored in database tables|-| +|[ABAP SQL: Grouping Internal Tables](11_ABAP_SQL_Grouping_Internal_Tables.md)|Touches on the `GROUP BY` clause in ABAP SQL|[zcl_demo_abap_sql_group_by](./src/zcl_demo_abap_sql_group_by.clas.abap) + +## 🎬 Getting Started with the Examples + +The executable examples are especially targeted at being imported into the SAP BTP ABAP environment, however, they are basically fit for both on-premise systems and the SAP BTP ABAP environment (that's why there are no ABAP programs included). Hence, check the info in the following collapsible sections for your system environment and carry out the prerequisite steps. + +
+ 1) General info +
+ +- A few **DDIC artifacts**, for example, database tables, are part of the repository. They are used by the examples to ensure self-contained examples. For all examples to work, all artifacts must be imported. +- All examples are designed to **display some output in the ADT console**. Once successfully imported, you can **execute** the examples in ADT by choosing `F9` to display the output in the ADT console. +- The examples **include descriptions and comments** in the code for providing explanations and setting the context. +
+ +
+ 2a) SAP BTP ABAP environment +
+ +**Prerequisites** +- [x] Before importing the code, you have made a system-wide search for, for example, classes named `ZCL_DEMO_ABAP*` so as not to run into issues when you try to import the code. If someone has already imported the content in the system, you can simply check out that imported version and proceed with the step *3) Run the code*. +- [x] You have access to an SAP BTP ABAP Environment instance (see [here](https://blogs.sap.com/2018/09/04/sap-cloud-platform-abap-environment) for additional information). +- [x] You have downloaded and installed ABAP Development Tools (ADT). Make sure that you use the most recent version as indicated on the [installation page](https://tools.hana.ondemand.com/#abap). +- [x] You have created an ABAP cloud project in ADT that allows you to access your SAP BTP ABAP Environment instance (see [here](https://help.sap.com/viewer/5371047f1273405bb46725a417f95433/Cloud/en-US/99cc54393e4c4e77a5b7f05567d4d14c.html) for additional information). Your logon language is English. +- [x] You have installed the [abapGit](https://github.com/abapGit/eclipse.abapgit.org) plug-in for ADT from the [update site](http://eclipse.abapgit.org/updatesite/). + +**Import Code** + +Use the abapGit plug-in to install the ABAP Cheat Sheets by carrying out the following steps: + +1. In your ABAP cloud project, create a package, for example, `ZABAP_CHEAT_SHEETS` as target package. It is recommended that you assign the package to a transport request suitable for demo content. +2. Add the package to the *Favorite Packages* in the *Project Explorer* view in ADT. +3. To add the abapGit Repositories view to the ABAP perspective, choose `Window` → `Show View` → `Other...` from the menu bar and choose `abapGit Repositories`. +4. On the abapGit Repositories view, click the `+` icon in the top right corner of the ADT tab to link a new abapGit repository. +
![ADT](./files/abapGit_Repositories.png) + +5. The *Link abapGit Repository* pop-up is displayed. Insert the following URL: + +``` +https://github.com/SAP-samples/abap-cheat-sheets.git +``` + +6. Choose `Next`. + +7. On the *Branch and Package Selection* screen, insert the name of the created package (e. g. `ZABAP_CHEAT_SHEETS`) in the `Package` field. +8. Choose `Next`. +9. On the *Select Transport Request* screen, select the created transport request suitable for demo content and choose `Finish` to link the Git repository to your ABAP cloud project. If the created package is already assigned to a transport request for the demo content and a message that an object is already locked in a transport request is displayed, choose `Finish`, too. +10. In the *abapGit Repositories* view, filter for your package. The repository appears in the *abapGit Repositories* view with status Linked. +11. Right-click the new abapGit repository and choose `Pull...` to start the cloning of the repository contents. +12. On the *Branch and Package Selection* screen, choose `Next`. +13. If the *Locally Modified Object* screen is displayed, select the objects (for example, the package to automatically select all artifacts) in the list and choose `Next`. +14. On the next screen, select a transport request and choose `Finish`. Same as above, if an *object already locked* message is displayed, choose `Finish`, too. The status in the *abapGit Repositories* view changes to Pull running.... Note that the pull run may take a few minutes. +15. Once the cloning has finished, the status is set to `Pulled Successfully`. You might need to refresh the `abapGit Repositories` view to see the progress of the import. To do so, choose the icon for refreshing (`Refresh`) in the top right corner of the view. +16. Refresh your project tree. E. g. in ADT, right-click the package and choose `Refresh` or `F5`. The package should contain all artifacts from the GitHub repository. +17. Make sure that all artifacts are active. To activate all inactive development objects, choose the `Activate all inactive ABAP development objects` button from the menu (or choose `CTRL+Shift+F3`). +
+ +
+ 2b) Application Server ABAP On-Premise +
+ +**Prerequisites** +- [x] The assumption is that you are on the latest [ABAP release](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abennews-75.htm). The content was also tested with release 7.56. +- [x] Before importing the code, you have made a system-wide search for, for example, classes named `ZCL_DEMO_ABAP*` so as not to run into issues when you try to import the code. If someone has already imported the content in the system, you can simply check out that imported version and proceed with the step *3) Run the code*. +- [x] You have downloaded and installed ABAP Development Tools (ADT). Make sure that you use the most recent version as indicated on the [installation page](https://tools.hana.ondemand.com/#abap). +- [x] You have created an ABAP Project in ADT that allows you to access your Application Server as mentioned above. Your logon language is English. +- [x] You have downloaded and installed the standalone version of the abapGit report. Make sure that you use the most recent version as indicated on the [installation page](https://docs.abapgit.org/). You can create a report, for example, `zabapgit_standalone` and copy and paste [this code](https://raw.githubusercontent.com/abapGit/build/main/zabapgit_standalone.prog.abap) into the program. +- [x] You have installed the certificate files for github.com, see [abapGit Documentation](https://docs.abapgit.org/guide-ssl-setup.html). + +**Import Code** + +Use the standalone version of the abapGit report to import the demo examples of the ABAP Cheat Sheets by carrying out the following steps: +1. In your ABAP project, create a package, for example, `TEST_ABAP_CHEAT_SHEETS` as target package suitable for demo content (e. g. by using `LOCAL` as software component). +2. Add the package to the *Favorite Packages* in the *Project Explorer* view in ADT. +3. Run the standalone version of the abapGit report. +4. Choose the `New Online` button. If the button is not available, for example, if another repository is already opened, choose the `Repository List` button. +5. On the *New Online Repository* screen, make the following entries: + - `Git Repository URL`: + + ``` + https://github.com/SAP-samples/abap-cheat-sheets.git + ``` + + - `Package`: Your demo package, for example, `TEST_ABAP_CHEAT_SHEETS` +6. Leave the other fields unchanged and choose `Create Online Repo`. +7. On the *Repository* screen, you will see the available ABAP artifacts to be imported into your ABAP system. +8. Choose the `Pull` button. The import of the artifacts is triggered. It might take some minutes. +9. If the `Inactive Objects` pop-up is displayed, select all artifacts and choose `Continue` (✔️). +10. Once the cloning has finished, refresh your project tree. E. g. in ADT, right-click the package and choose `Refresh` or `F5`. The package should contain all artifacts from the GitHub repository. +11. Make sure that all artifacts are active. To activate all inactive development objects, choose the `Activate all inactive ABAP development objects` button from the (or choose `CTRL+Shift+F3`). + +
+ +
+ 3) Run the code +
+ +- Open your created package containing the imported ABAP artifacts in the ABAP Development Tools (ADT). +- Open an ABAP cheat sheet example class as listed in the [ABAP Cheat Sheets Overview](#📝-abap-cheat-sheets-overview) section, for example, `zcl_demo_abap_string_proc`. The classes are contained in folder `Source Code Library` → `Classes`. +- Choose `F9` to run the class. Alternatively, choose `Run` → `Run As` → `2 ABAP Application (Console)` from the menu. +- Check the console output. + +> **💡 Note**
+>- Check notes on the context and the ABAP syntax used included in the class as comments. +>- Due to the amount of output in the console, the examples include numbers (e. g. 1) ..., 2) ..., 3) ...) representing the header of the individual example code sections. Plus, the variable name is displayed in the console in most cases. Hence, to easier and faster find the relevant output in the console, just search in the console for the number (e. g. search for `3)` for the particular output) or variable name (`STRG+F` in the console) or use break points in the code to check variables in the debugger. +>- You might want to clear the console by making a right-click within the console and choosing `Clear` before running another demo class so as not to confuse the output of multiple classes. +
+ +
+ +## ⚡ Known Issues +- Only one user on the system may import this repository as all object names must be globally unique. If you receive an error that the objects already exist when trying to import, search the system for classes named `ZCL_DEMO_ABAP*`. Someone has already imported the content in the system and you can simply check out that imported version. +- Since the repository contains self-contained examples, i. e. they work, for example, with demo database tables included in the repository (note that these tables are filled in the course of method executions), all demo artifacts must be imported so that all examples work. +- For the import into an on-premise system, note the following: The demos cover ABAP syntax irrespective of the ABAP release so as not to scatter information and to have the info in one go. Hence, there may be syntax that is not yet available with the ABAP version of your on-premise system. In that case, you might want to comment out affected code sections if an activation fails. +- Regarding potential code check warnings, for example, for the many strings in the code, not using an `ORDER BY` clause or messages regarding using `SELECT *`, the code purposely renounces pragmas and pseudo comments to keep the code fairly simple and to focus on the available ABAP syntax. See also the [Disclaimer](#⚠️-disclaimer). + +## 🛈 Further Information +- Regarding the system-internal version of the ABAP Keyword Documentation in your + - ... **on-premise system**: Access the documentation in SAP GUI via the transactions `ABAPDOCU` (opens the documentation directly) and `ABAPHELP` (opens an input field with which you can search the documentation content, for example, you can search for a keyword like `SELECT`). Or, certainly, in your code, choose `F1` for a keyword. If you are in SAP GUI (e. g. in `SE80`), the system-internal version is opened. If you are in ADT, the documentation is opened in the *ABAP Language Help* view. + - ... **SAP BTP ABAP environment**: In ADT, you find the documentation in the *ABAP Language Help* view where you can also search. When choosing `F1` for a keyword in your code, the documentation is opened there accordingly. +- Links to the online version of the ABAP Keyword Documentation for: + - **Standard ABAP**: Unrestricted ABAP language scope, for example, for developments in an on-premise ABAP system → [Online version of the documentation (latest version)](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenabap.htm) + - **ABAP for Cloud Development**: Restricted ABAP language scope for developments in the SAP BTP ABAP environment → [Online version of the documentation](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm) +- Regarding demonstration examples of the ABAP Keyword Documentation in your on-premise system: Have you ever checked out the package `SABAPDEMOS`? This package contains all the examples used in the ABAP Keyword Documentation. To get the context, program names etc., check out the [example page](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenabap_examples.htm) (which is also available in the system-internal SAP GUI version as node in the topic tree) summarizing executable examples. Certainly, you also find the example topics in the context of the individual ABAP Keyword Documentation topic. The example topics have a ⚙️ sign: + + ![](./files/example_topics.png) + +- See [this blog](https://blogs.sap.com/2021/04/28/video-tutorials-on-how-to-use-the-abap-keyword-documentation-abap-f1-help/) for videos about the ABAP Keyword Documentation on the [Help Portal](https://www.youtube.com/watch?v=a4ckF1XkfG8), in [SAP GUI](https://www.youtube.com/watch?v=fsX-085MlD8) and in [ADT](https://www.youtube.com/watch?v=hNGEYFpWwh0). + +## ⚠️ Disclaimer +The code examples presented in this repository are only syntax examples and are not intended for direct use in a production system environment. The code examples are primarily intended to provide a better explanation and visualization of the syntax and semantics of ABAP statements and not to solve concrete programming tasks. For production application programs, a dedicated solution should therefore always be worked out for each individual case. +SAP does not guarantee either the correctness or the completeness of the code. In addition, SAP takes no legal responsibility or liability for possible errors or their consequences, which occur through the use of the example programs. + +## 📟 How to Obtain Support +This project is provided "as-is": there is no guarantee that raised issues will be answered or addressed in future releases. + +## 📜 License +Copyright (c) 2022 SAP SE or an SAP affiliate company. All rights reserved. This project is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](LICENSE) file. + +[^1]: "A written [...] aid (such as a sheet of notes) that can be referred to for help in understanding or remembering something complex" (Definition for "cheat sheet" in Merriam-Webster Dictionary). diff --git a/files/ABAP_Keyword_Documentation.png b/files/ABAP_Keyword_Documentation.png new file mode 100644 index 0000000..e0eb0a6 Binary files /dev/null and b/files/ABAP_Keyword_Documentation.png differ diff --git a/files/abapGit_Repositories.png b/files/abapGit_Repositories.png new file mode 100644 index 0000000..ea145f4 Binary files /dev/null and b/files/abapGit_Repositories.png differ diff --git a/files/bdef_derived_type_components.png b/files/bdef_derived_type_components.png new file mode 100644 index 0000000..c8a55ea Binary files /dev/null and b/files/bdef_derived_type_components.png differ diff --git a/files/bdef_derived_types.png b/files/bdef_derived_types.png new file mode 100644 index 0000000..b48b0a9 Binary files /dev/null and b/files/bdef_derived_types.png differ diff --git a/files/example_topics.png b/files/example_topics.png new file mode 100644 index 0000000..98e9d44 Binary files /dev/null and b/files/example_topics.png differ diff --git a/files/rap_handler_method_parameters.png b/files/rap_handler_method_parameters.png new file mode 100644 index 0000000..aa61074 Binary files /dev/null and b/files/rap_handler_method_parameters.png differ diff --git a/src/ezdemo_abap_lock.enqu.xml b/src/ezdemo_abap_lock.enqu.xml new file mode 100644 index 0000000..8d677f1 --- /dev/null +++ b/src/ezdemo_abap_lock.enqu.xml @@ -0,0 +1,44 @@ + + + + + + EZDEMO_ABAP_LOCK + E + E + ZDEMO_ABAP_RAPT1 + Lock on demo table + 5 + + + + EZDEMO_ABAP_LOCK + ZDEMO_ABAP_RAPT1 + 0001 + ZDEMO_ABAP_RAPT1 + E + + + + + EZDEMO_ABAP_LOCK + 0001 + CLIENT + ZDEMO_ABAP_RAPT1 + CLIENT + X + E + + + EZDEMO_ABAP_LOCK + 0002 + KEY_FIELD + ZDEMO_ABAP_RAPT1 + KEY_FIELD + X + E + + + + + diff --git a/src/package.devc.xml b/src/package.devc.xml new file mode 100644 index 0000000..e77fc01 --- /dev/null +++ b/src/package.devc.xml @@ -0,0 +1,11 @@ + + + + + + ABAP Cheat Sheets + X + + + + diff --git a/src/zbp_demo_abap_rap_draft_m.clas.abap b/src/zbp_demo_abap_rap_draft_m.clas.abap new file mode 100644 index 0000000..4614e93 --- /dev/null +++ b/src/zbp_demo_abap_rap_draft_m.clas.abap @@ -0,0 +1,20 @@ +*********************************************************************** +* +* RAP BO provider (i. e. ABAP behavior pool/ABP) +* for a RAP demo scenario +* +* See more information in the CCIMP include (local types tab in ADT). +* +********************************************************************** +"!

Behavior implementation for RAP demo scenario (draft BO)

+"! The class represents a RAP BO provider (i. e. an ABAP behavior pool/ABP) for a RAP demo scenario +"! (managed, draft-enabled RAP BO with late numbering). +CLASS zbp_demo_abap_rap_draft_m DEFINITION PUBLIC ABSTRACT FINAL FOR BEHAVIOR OF zdemo_abap_rap_draft_m. + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + + +CLASS zbp_demo_abap_rap_draft_m IMPLEMENTATION. +ENDCLASS. diff --git a/src/zbp_demo_abap_rap_draft_m.clas.locals_imp.abap b/src/zbp_demo_abap_rap_draft_m.clas.locals_imp.abap new file mode 100644 index 0000000..3293cf6 --- /dev/null +++ b/src/zbp_demo_abap_rap_draft_m.clas.locals_imp.abap @@ -0,0 +1,234 @@ +*********************************************************************** +* +* RAP BO provider (i. e. ABAP behavior pool/ABP) +* for a RAP demo scenario +* +* - RAP scenario: "RAP calculator" (managed, draft-enabled RAP BO with +* late numbering) +* - Data model: Consists of a root entity alone. +* The BDEF defines the behavior for this entity. The definitions in the +* BDEF determine which methods must be implemented in the ABAP behavior +* pool (ABP). Note that the view contains many annotations for the +* SAP Fiori UI. +* +* ----------------------------- NOTE ----------------------------------- +* This simplified example is not a real life scenario and rather +* focuses on the technical side by giving an idea how the communication +* and data exchange between a RAP BO consumer, which is a class +* in this case, and RAP BO provider can work. Additionally, it shows +* how the methods for non-standard RAP BO operations might be +* self-implemented in an ABP. The example is is intentionally kept +* short and simple and focuses on specific RAP aspects. For this reason, +* the example might not fully meet the requirements of the RAP BO contract. +* +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +*********************************************************************** + +CLASS lhc_calc DEFINITION INHERITING FROM cl_abap_behavior_handler. + PRIVATE SECTION. + + METHODS delete_all FOR MODIFY + IMPORTING keys FOR ACTION calc~delete_all. + + METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION + IMPORTING REQUEST requested_authorizations FOR calc RESULT result. + + METHODS validate FOR VALIDATE ON SAVE + IMPORTING keys FOR calc~validate. + + METHODS det_modify FOR DETERMINE ON MODIFY + IMPORTING keys FOR calc~det_modify. + + METHODS calculation FOR MODIFY + IMPORTING keys FOR ACTION calc~calculation. + +ENDCLASS. + +CLASS lhc_calc IMPLEMENTATION. + + METHOD delete_all. + "Purpose: The method deletes all persisted database entries. + + DATA all_keys TYPE TABLE FOR DELETE zdemo_abap_rap_draft_m. + + SELECT id FROM zdemo_abap_tabca INTO CORRESPONDING FIELDS OF TABLE @all_keys. + + READ ENTITIES OF zdemo_abap_rap_draft_m IN LOCAL MODE + ENTITY calc + ALL FIELDS WITH CORRESPONDING #( all_keys ) + RESULT DATA(lt_del). + + IF lt_del IS NOT INITIAL. + + MODIFY ENTITY IN LOCAL MODE zdemo_abap_rap_draft_m + DELETE FROM CORRESPONDING #( lt_del ). + + APPEND VALUE #( %msg = new_message_with_text( text = 'All persisted calculations were deleted.' + severity = if_abap_behv_message=>severity-information ) + ) TO reported-calc. + + ELSE. + APPEND VALUE #( %msg = new_message_with_text( text = 'No persisted calculations available.' + severity = if_abap_behv_message=>severity-information ) + ) TO reported-calc. + + ENDIF. + + ENDMETHOD. + + METHOD get_global_authorizations. + "Purposely kept without implementation. + ENDMETHOD. + + METHOD validate. + + "Retrieving instances based on requested keys + READ ENTITIES OF zdemo_abap_rap_draft_m IN LOCAL MODE + ENTITY calc + ALL FIELDS + WITH CORRESPONDING #( keys ) + RESULT DATA(result_validate) + FAILED DATA(f). + + CHECK result_validate IS NOT INITIAL. + + "Various calculation errors are handled. + LOOP AT result_validate ASSIGNING FIELD-SYMBOL(). + + APPEND VALUE #( %tky = -%tky + %state_area = 'VALIDATE_CALCULATION' + ) TO reported-calc. + + IF -calc_result = `Wrong operator`. + APPEND VALUE #( %tky = -%tky ) TO failed-calc. + + APPEND VALUE #( %tky = -%tky + %state_area = 'VALIDATE_CALCULATION' + %msg = new_message_with_text( text = 'Only + - * / P allowed as operators.' + severity = if_abap_behv_message=>severity-error ) + "%element highlights the input field + %element-arithm_op = if_abap_behv=>mk-on + ) TO reported-calc. + + ELSEIF -calc_result = `Division by 0`. + + APPEND VALUE #( %tky = -%tky ) TO failed-calc. + + APPEND VALUE #( %tky = -%tky + %state_area = 'VALIDATE_CALCULATION' + %msg = new_message_with_text( text = 'Zero division not possible.' + severity = if_abap_behv_message=>severity-error ) + %element-arithm_op = if_abap_behv=>mk-on + %element-num2 = if_abap_behv=>mk-on + ) TO reported-calc. + + ELSEIF -calc_result = `Overflow error`. + + APPEND VALUE #( %tky = -%tky ) TO failed-calc. + + APPEND VALUE #( %tky = -%tky + %state_area = 'VALIDATE_CALCULATION' + %msg = new_message_with_text( text = 'Check the numbers. Try smaller ones.' + severity = if_abap_behv_message=>severity-error ) + %element-num1 = if_abap_behv=>mk-on + %element-num2 = if_abap_behv=>mk-on + ) TO reported-calc. + ENDIF. + + ENDLOOP. + + ENDMETHOD. + + METHOD det_modify. + + MODIFY ENTITIES OF zdemo_abap_rap_draft_m IN LOCAL MODE + ENTITY calc + EXECUTE calculation + FROM CORRESPONDING #( keys ). + + ENDMETHOD. + + METHOD calculation. + + READ ENTITIES OF zdemo_abap_rap_draft_m IN LOCAL MODE + ENTITY calc + FIELDS ( num1 num2 arithm_op ) WITH CORRESPONDING #( keys ) + RESULT DATA(lt_calc) + FAILED DATA(f). + + LOOP AT lt_calc ASSIGNING FIELD-SYMBOL(). + + TRY. + -calc_result = SWITCH #( -arithm_op + WHEN `+` THEN -num1 + -num2 + WHEN `-` THEN -num1 - -num2 + WHEN `*` THEN -num1 * -num2 + WHEN `/` THEN -num1 / -num2 + WHEN `P` THEN ipow( base = -num1 exp = -num2 ) + ELSE `Wrong operator` ). + "Bringing "-" to the front in case of negative values in the string + IF -calc_result CA `-`. + -calc_result = shift_right( val = -calc_result circular = 1 ). + ENDIF. + + "Removing trailing .0 from the string + REPLACE PCRE `\.0+\b` IN -calc_result WITH ``. + + "Handling the fact that ABAP allows division by zero if the dividend itself is zero. + IF -num1 = 0 AND -num2 = 0 AND -arithm_op = `/`. + -calc_result = `Division by 0`. + ENDIF. + + CATCH cx_sy_zerodivide. + -calc_result = `Division by 0`. + + CATCH cx_sy_arithmetic_overflow. + -calc_result = `Overflow error`. + + ENDTRY. + + ENDLOOP. + + MODIFY ENTITY IN LOCAL MODE zdemo_abap_rap_draft_m + UPDATE FIELDS ( calc_result ) + WITH CORRESPONDING #( lt_calc ). + + ENDMETHOD. + +ENDCLASS. + +CLASS lsc_zdemo_abap_rap_draft_m DEFINITION INHERITING FROM cl_abap_behavior_saver. + PROTECTED SECTION. + + METHODS adjust_numbers REDEFINITION. + +ENDCLASS. + +CLASS lsc_zdemo_abap_rap_draft_m IMPLEMENTATION. + + METHOD adjust_numbers. + + "The newly created entity instances are given their final key + "only shortly before saving in the database in the adjust_numbers method. + "Until then, the business logic uses a temporary key that has to be replaced. + "In this very simplified example, the key 'id' is purposely typed with the + "type sysuuid_x16 which can accept the value used in %pid to finally ensure + "that there is a unique key and the instance can be stored in the database. + "Hence, the final key 'id' is in this example just the value used for %pid. + LOOP AT mapped-calc ASSIGNING FIELD-SYMBOL(). + -%key-id = -%pid. + ENDLOOP. + + ENDMETHOD. + +ENDCLASS. diff --git a/src/zbp_demo_abap_rap_draft_m.clas.xml b/src/zbp_demo_abap_rap_draft_m.clas.xml new file mode 100644 index 0000000..86c9a76 --- /dev/null +++ b/src/zbp_demo_abap_rap_draft_m.clas.xml @@ -0,0 +1,18 @@ + + + + + + ZBP_DEMO_ABAP_RAP_DRAFT_M + E + Behavior implementation for RAP demo scenario (draft BO) + 06 + 1 + X + X + X + ZDEMO_ABAP_RAP_DRAFT_M + + + + diff --git a/src/zbp_demo_abap_rap_ro_m.clas.abap b/src/zbp_demo_abap_rap_ro_m.clas.abap new file mode 100644 index 0000000..332c226 --- /dev/null +++ b/src/zbp_demo_abap_rap_ro_m.clas.abap @@ -0,0 +1,20 @@ +*********************************************************************** +* +* RAP BO provider (i. e. ABAP behavior pool/ABP) +* for a RAP demo scenario +* +* See more information in the CCIMP include (local types tab in ADT). +* +********************************************************************** +"!

Behavior implementation for RAP demo scenario (managed BO)

+"! The class represents a RAP BO provider (i. e. an ABAP behavior pool/ABP) for a RAP demo scenario +"! (managed RAP BO with external numbering). +CLASS zbp_demo_abap_rap_ro_m DEFINITION PUBLIC ABSTRACT FINAL FOR BEHAVIOR OF zdemo_abap_rap_ro_m. + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + + +CLASS zbp_demo_abap_rap_ro_m IMPLEMENTATION. +ENDCLASS. diff --git a/src/zbp_demo_abap_rap_ro_m.clas.locals_imp.abap b/src/zbp_demo_abap_rap_ro_m.clas.locals_imp.abap new file mode 100644 index 0000000..fac796e --- /dev/null +++ b/src/zbp_demo_abap_rap_ro_m.clas.locals_imp.abap @@ -0,0 +1,123 @@ +*********************************************************************** +* +* RAP BO provider (i. e. ABAP behavior pool/ABP) +* for a RAP demo scenario +* +* - RAP scenario: managed RAP BO, external numbering +* - Data model: Consists of a root entity and one child entity. The BDEF +* defines the behavior for these two entities which are connected via +* a CDS composition relation. The definitions in the BDEF determine +* which methods must be implemented in this ABAP behavior pool (ABP). +* +* ----------------------------- NOTE ----------------------------------- +* This simplified example is not a real life scenario and rather +* focuses on the technical side by giving an idea how the communication +* and data exchange between a RAP BO consumer, which is a class +* in this case, and RAP BO provider can work. Additionally, it shows +* how the methods for non-standard RAP BO operations might be +* self-implemented in an ABP. The example is is intentionally kept +* short and simple and focuses on specific RAP aspects. For this reason, +* the example might not fully meet the requirements of the RAP BO contract. +* +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +*********************************************************************** + +CLASS lhc_root DEFINITION INHERITING FROM cl_abap_behavior_handler. + PRIVATE SECTION. + + METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION + IMPORTING REQUEST requested_authorizations FOR root RESULT result. + + METHODS multiply_by_2 FOR MODIFY + IMPORTING keys FOR ACTION root~multiply_by_2. + + METHODS det_add_text FOR DETERMINE ON SAVE + IMPORTING keys FOR root~det_add_text. + + METHODS val FOR VALIDATE ON SAVE + IMPORTING keys FOR root~val. + +ENDCLASS. + +CLASS lhc_root IMPLEMENTATION. + + METHOD get_global_authorizations. + ENDMETHOD. + + METHOD multiply_by_2. + + "Retrieving instances based on requested keys + READ ENTITIES OF zdemo_abap_rap_ro_m IN LOCAL MODE + ENTITY root + FIELDS ( field3 field4 ) WITH CORRESPONDING #( keys ) + RESULT DATA(result) + FAILED failed. + + "If read result is initial, stop further method execution. + CHECK result IS NOT INITIAL. + + "Multiply integer values by 2 + MODIFY ENTITIES OF zdemo_abap_rap_ro_m IN LOCAL MODE + ENTITY root + UPDATE FIELDS ( field3 field4 ) WITH VALUE #( FOR key IN result ( %tky = key-%tky + field3 = key-field3 * 2 + field4 = key-field4 * 2 ) ). + ENDMETHOD. + + METHOD det_add_text. + + READ ENTITIES OF zdemo_abap_rap_ro_m IN LOCAL MODE + ENTITY root + FIELDS ( field2 ) WITH CORRESPONDING #( keys ) + RESULT DATA(lt_res). + + "If read result is initial, stop further method execution. + CHECK lt_res IS NOT INITIAL. + + "field2 is changed + MODIFY ENTITIES OF zdemo_abap_rap_ro_m IN LOCAL MODE + ENTITY root + UPDATE FIELDS ( field2 ) + WITH VALUE #( FOR key IN lt_res ( %tky = key-%tky + field2 = |{ key-field2 }_#| ) ). + + ENDMETHOD. + + METHOD val. + + READ ENTITIES OF zdemo_abap_rap_ro_m IN LOCAL MODE + ENTITY root + FIELDS ( field3 ) WITH CORRESPONDING #( keys ) + RESULT DATA(lt_res). + + "If read result is initial, stop further method execution. + CHECK lt_res IS NOT INITIAL. + + LOOP AT lt_res ASSIGNING FIELD-SYMBOL(). + IF -field3 > 1000. + APPEND VALUE #( %tky = -%tky + %fail-cause = if_abap_behv=>cause-disabled + ) + TO failed-root. + + APPEND VALUE #( %tky = -%tky + %msg = new_message_with_text( + severity = if_abap_behv_message=>severity-error + text = 'Validation failed!' ) + ) TO reported-root. + ENDIF. + ENDLOOP. + + ENDMETHOD. + +ENDCLASS. diff --git a/src/zbp_demo_abap_rap_ro_m.clas.xml b/src/zbp_demo_abap_rap_ro_m.clas.xml new file mode 100644 index 0000000..919f460 --- /dev/null +++ b/src/zbp_demo_abap_rap_ro_m.clas.xml @@ -0,0 +1,18 @@ + + + + + + ZBP_DEMO_ABAP_RAP_RO_M + E + Behavior implementation for RAP demo scenario (managed BO) + 06 + 1 + X + X + X + ZDEMO_ABAP_RAP_RO_M + + + + diff --git a/src/zbp_demo_abap_rap_ro_u.clas.abap b/src/zbp_demo_abap_rap_ro_u.clas.abap new file mode 100644 index 0000000..b290a60 --- /dev/null +++ b/src/zbp_demo_abap_rap_ro_u.clas.abap @@ -0,0 +1,20 @@ +*********************************************************************** +* +* RAP BO provider (i. e. ABAP behavior pool/ABP) +* for a RAP demo scenario +* +* See more information in the CCIMP include (local types tab in ADT). +* +********************************************************************** +"!

Behavior implementation for RAP demo scenario (unmanaged BO)

+"! The class represents a RAP BO provider (i. e. an ABAP behavior pool/ABP) for a RAP demo scenario +"! (unmanaged RAP BO with external numbering). +CLASS zbp_demo_abap_rap_ro_u DEFINITION PUBLIC ABSTRACT FINAL FOR BEHAVIOR OF zdemo_abap_rap_ro_u. + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + + +CLASS zbp_demo_abap_rap_ro_u IMPLEMENTATION. +ENDCLASS. diff --git a/src/zbp_demo_abap_rap_ro_u.clas.locals_imp.abap b/src/zbp_demo_abap_rap_ro_u.clas.locals_imp.abap new file mode 100644 index 0000000..b0ed153 --- /dev/null +++ b/src/zbp_demo_abap_rap_ro_u.clas.locals_imp.abap @@ -0,0 +1,1023 @@ +*********************************************************************** +* +* RAP BO provider (i. e. ABAP behavior pool/ABP) +* for a RAP demo scenario +* +* - RAP scenario: unmanaged RAP BO, external numbering +* - Data model: Consists of a root entity and one child entity. The BDEF +* defines the behavior for these two entities which are connected via +* a CDS composition relation. The definitions in the BDEF determine +* which methods must be implemented in this ABAP behavior pool (ABP). +* +* ----------------------------- NOTE ----------------------------------- +* This simplified example is not a real life scenario and rather +* focuses on the technical side by giving an idea how the communication +* and data exchange between a RAP BO consumer, which is a class +* in this case, and RAP BO provider can work. Additionally, it shows +* how the methods for non-standard RAP BO operations might be +* self-implemented in an ABP. The example is is intentionally kept +* short and simple and focuses on specific RAP aspects. For this reason, +* the example might not fully meet the requirements of the RAP BO contract. +* +* For demonstration purposes, some of the operations are +* impacted by feature controls and instance authorization as specified +* in the BDEF. +* +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +*********************************************************************** + +*********************************************************************** +* Class lcl_buffer +* +* To have a self-contained and simple scenario, the transactional +* buffer is realized by internal tables here. These tables - one for +* the root entity, one for the child entity - are designed in a way +* to include RAP BO instance data as well content IDs and flags to +* specify if an instance is to be changed or deleted (which is relevant +* for the save method in this example). +* +* The purpose of this class is to create these internal tables and +* provide a method implementation for the preparation of the tables, +* i. e. to prepare the content of the transactional buffer which is +* accessed throughout the handler and saver method implementations. +* The method/s is/are called in the context of each EML request. +* +*********************************************************************** +"Class that constitutes the transactional buffer +CLASS lcl_buffer DEFINITION. + PUBLIC SECTION. + + "Structure and internal table types for the internal table serving + "as transactional buffers for the root and child entities + TYPES: BEGIN OF gty_buffer, + instance TYPE zdemo_abap_rap_ro_u, + cid TYPE string, + changed TYPE abap_bool, + deleted TYPE abap_bool, + END OF gty_buffer. + + TYPES: BEGIN OF gty_buffer_child, + instance TYPE zdemo_abap_rap_ch_u, + cid_ref TYPE string, + cid_target TYPE string, + changed TYPE abap_bool, + END OF gty_buffer_child. + + TYPES gtt_buffer TYPE TABLE OF gty_buffer + WITH EMPTY KEY. + + TYPES gtt_buffer_child TYPE TABLE OF gty_buffer_child + WITH EMPTY KEY. + + CLASS-DATA: + "Internal tables serving as transactional buffers for the root and child entities + root_buffer TYPE STANDARD TABLE OF gty_buffer WITH EMPTY KEY, + child_buffer TYPE STANDARD TABLE OF gty_buffer_child WITH EMPTY KEY. + + "Structure and internal table types to include the keys for buffer preparation methods + TYPES: BEGIN OF root_keys, + key_field TYPE zdemo_abap_rap_ro_u-key_field, + END OF root_keys, + BEGIN OF child_keys, + key_field TYPE zdemo_abap_rap_ch_u-key_field, + key_ch TYPE zdemo_abap_rap_ch_u-key_ch, + full_key TYPE abap_bool, + END OF child_keys, + tt_root_keys TYPE TABLE OF root_keys WITH EMPTY KEY, + tt_child_keys TYPE TABLE OF child_keys WITH EMPTY KEY. + + "Buffer preparation methods + CLASS-METHODS: prep_root_buffer IMPORTING keys TYPE tt_root_keys, + prep_child_buffer IMPORTING keys TYPE tt_child_keys. + +ENDCLASS. + + +CLASS lcl_buffer IMPLEMENTATION. + + "Buffer preparation for the root entity based on the requested key values + METHOD prep_root_buffer. + + LOOP AT keys ASSIGNING FIELD-SYMBOL(). + "Logic: + "- Line with the specific key values exists in the buffer for the root entity + "- If it is true: Do nothing, buffer is prepared for the specific instance. + "- Note: If the line is marked as deleted, the buffer should not be filled anew with the data. + IF line_exists( lcl_buffer=>root_buffer[ instance-key_field = -key_field ] ). + "Do nothing, buffer is prepared for the specific instance. + ELSE. + "Checking if entry exists in the database table of the root entity based on the key value + SELECT SINGLE @abap_true + FROM zdemo_abap_rapt1 + WHERE key_field = @-key_field + INTO @DATA(exists). + + IF exists = abap_true. + "If entry exists, retrieve it based on the shared key value + DATA line TYPE zdemo_abap_rap_ro_u. + + SELECT SINGLE * FROM zdemo_abap_rapt1 + WHERE key_field = @-key_field + INTO CORRESPONDING FIELDS OF @line. + + IF sy-subrc = 0. + "Adding line to the root buffer + APPEND VALUE #( instance = line ) TO lcl_buffer=>root_buffer. + ENDIF. + + ENDIF. + ENDIF. + ENDLOOP. + ENDMETHOD. + + "Buffer preparation for the child entity based on the requested key values + METHOD prep_child_buffer. + + LOOP AT keys ASSIGNING FIELD-SYMBOL(). + + "The full_key flag is in this example only relevant if a read operation is executed on the child entity directly + "and all key values should be considered for the data retrieval from the database table. + IF -full_key = abap_true. + "Logic: + "- Line with specific key values exists in the buffer for the child entity + "- If it is true: Do nothing, buffer is prepared for the specific instance. + IF line_exists( lcl_buffer=>child_buffer[ instance-key_field = -key_field + instance-key_ch = -key_ch ] ). + "Buffer is prepared for the instance. + ELSE. + "Checking if entry exists in the database table of the child entity based on the shared key value + SELECT SINGLE @abap_true + FROM zdemo_abap_rapt2 + WHERE key_field = @-key_field + AND key_ch = @-key_ch + INTO @DATA(exists). + + "If entry exists, retrieve all entries based on the key values + IF exists = abap_true. + + DATA line_ch TYPE zdemo_abap_rap_ch_u. + SELECT SINGLE * FROM zdemo_abap_rapt2 + WHERE key_field = @-key_field + AND key_ch = @-key_ch + INTO CORRESPONDING FIELDS OF @line_ch. + + IF sy-subrc = 0. + "Adding line to the child buffer if no line exists with all key values + APPEND VALUE #( instance = line_ch ) TO lcl_buffer=>child_buffer. + ENDIF. + + ENDIF. + ENDIF. + + ELSE. + + "Logic: + "- Line with specific keys exists in the buffer for the root entity and is marked for deletion + "- If all is true: Doing nothing, buffer is prepared for the specific instance. + "- Else: Retrieving all lines from the database table of the child entity having the shared key + IF line_exists( lcl_buffer=>root_buffer[ instance-key_field = -key_field ] ) + AND VALUE #( lcl_buffer=>root_buffer[ instance-key_field = -key_field ]-deleted OPTIONAL ) IS NOT INITIAL. + "Buffer is prepared for the instance. + ELSE. + "Checking if entry exists in the database table of the child entity based on the shared key value + SELECT SINGLE @abap_true + FROM zdemo_abap_rapt2 + WHERE key_field = @-key_field + INTO @DATA(exists_ch). + + "If entry exists, retrieve all entries based on the shared key value + IF exists_ch = abap_true. + + DATA ch_tab TYPE TABLE OF zdemo_abap_rap_ch_u WITH EMPTY KEY. + SELECT * FROM zdemo_abap_rapt2 + WHERE key_field = @-key_field + INTO CORRESPONDING FIELDS OF TABLE @ch_tab. + + IF sy-subrc = 0. + + LOOP AT ch_tab ASSIGNING FIELD-SYMBOL(). + "Adding line to the child buffer if no line exists with all key values + IF NOT line_exists( lcl_buffer=>child_buffer[ instance-key_field = -key_field + instance-key_ch = -key_ch ] ). + APPEND VALUE #( instance = ) TO lcl_buffer=>child_buffer. + ENDIF. + ENDLOOP. + ENDIF. + + ENDIF. + ENDIF. + ENDIF. + ENDLOOP. + ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Local handler class lhc_root +* +* Contains handler method definitions and implementations as defined +* in the CDS behavior definition (BDEF). +* +*********************************************************************** + +CLASS lhc_root DEFINITION INHERITING FROM cl_abap_behavior_handler. + + PRIVATE SECTION. + + METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION + IMPORTING REQUEST requested_authorizations FOR root RESULT result. + + METHODS create FOR MODIFY + IMPORTING entities FOR CREATE root. + + METHODS read FOR READ + IMPORTING keys FOR READ root RESULT result. + + METHODS lock FOR LOCK + IMPORTING keys FOR LOCK root. + + METHODS update FOR MODIFY + IMPORTING entities FOR UPDATE root. + + METHODS delete FOR MODIFY + IMPORTING keys FOR DELETE root. + + METHODS rba_child FOR READ + IMPORTING keys_rba FOR READ root\_child FULL result_requested RESULT result LINK association_links. + + METHODS cba_child FOR MODIFY + IMPORTING entities_cba FOR CREATE root\_child. + + METHODS multiply_by_2 FOR MODIFY + IMPORTING keys FOR ACTION root~multiply_by_2. + + METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION + IMPORTING keys REQUEST requested_authorizations FOR root RESULT result. + + METHODS get_instance_features FOR INSTANCE FEATURES + IMPORTING keys REQUEST requested_features FOR root RESULT result. + + METHODS multiply_by_3 FOR MODIFY + IMPORTING keys FOR ACTION root~multiply_by_3. + + METHODS get_global_features FOR GLOBAL FEATURES + IMPORTING REQUEST requested_features FOR root RESULT result. + + METHODS set_z FOR MODIFY + IMPORTING keys FOR ACTION root~set_z. + +ENDCLASS. + +CLASS lhc_root IMPLEMENTATION. + + METHOD get_global_authorizations. + "No implementation on purpose. No global authorization restriction. + ENDMETHOD. + + METHOD create. + "Preparing the transactional buffer based on the input BDEF derived type. + lcl_buffer=>prep_root_buffer( CORRESPONDING #( entities ) ). + + "Processing requested entities sequentially + LOOP AT entities ASSIGNING FIELD-SYMBOL(). + "Logic: + "- Line with the specific key does not exist in the buffer for the root entity + "- Line with the specific key exists in the buffer but it is marked as deleted + "- If it is true: Add new instance to the buffer and, if needed, remove the instance marked as deleted beforehand + IF NOT line_exists( lcl_buffer=>root_buffer[ instance-key_field = -key_field ] ) + OR line_exists( lcl_buffer=>root_buffer[ instance-key_field = -key_field deleted = abap_true ] ). + + "If it exists, removing instance that is marked for deletion from the transactional buffer since it gets replaced by a new one. + DELETE lcl_buffer=>root_buffer WHERE instance-key_field = VALUE #( + lcl_buffer=>root_buffer[ instance-key_field = -key_field ]-instance-key_field OPTIONAL ) AND deleted = abap_true. + + "Adding new instance to the transactional buffer by considering %control values + APPEND VALUE #( cid = -%cid + instance-key_field = -key_field + instance-field1 = COND #( WHEN -%control-field1 NE if_abap_behv=>mk-off + THEN -field1 ) + instance-field2 = COND #( WHEN -%control-field2 NE if_abap_behv=>mk-off + THEN -field2 ) + instance-field3 = COND #( WHEN -%control-field3 NE if_abap_behv=>mk-off + THEN -field3 ) + instance-field4 = COND #( WHEN -%control-field4 NE if_abap_behv=>mk-off + THEN -field4 ) + changed = abap_true + deleted = abap_false ) TO lcl_buffer=>root_buffer. + + "Filling the MAPPED response parameter for the root entity + INSERT VALUE #( %cid = -%cid + %key = -%key ) INTO TABLE mapped-root. + + ELSE. + + "Filling FAILED and REPORTED response parameters + APPEND VALUE #( %cid = -%cid + %key = -%key + %create = if_abap_behv=>mk-on + %fail-cause = if_abap_behv=>cause-unspecific + ) TO failed-root. + + APPEND VALUE #( %cid = -%cid + %key = -%key + %create = if_abap_behv=>mk-on + %msg = new_message_with_text( + severity = if_abap_behv_message=>severity-error + text = 'Create operation failed.' ) + ) TO reported-root. + + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD update. + "Preparing the transactional buffer based on the input BDEF derived type. + lcl_buffer=>prep_root_buffer( CORRESPONDING #( entities ) ). + + "Processing requested entities sequentially + "Note: + "The example is implemented in a way that instances that failed in methods called before this method are not handled. + "The instances that have failed before this method call are not available in this method's input parameter. + "Hence, an adding to FAILED and REPORTED is not implemented here. + LOOP AT entities ASSIGNING FIELD-SYMBOL(). + + "Logic: + "- Line with the specific key exists in the buffer for the root entity and it is not marked as deleted + "- If it is true: Updating the buffer based on the input BDEF derived type and considering %control values + READ TABLE lcl_buffer=>root_buffer + WITH KEY instance-key_field = -key_field + deleted = abap_false + ASSIGNING FIELD-SYMBOL(). + + IF sy-subrc = 0. + -instance-field1 = COND #( WHEN -%control-field1 NE if_abap_behv=>mk-off + THEN -field1 + ELSE -instance-field1 ). + + -instance-field2 = COND #( WHEN -%control-field2 NE if_abap_behv=>mk-off + THEN -field2 + ELSE -instance-field2 ). + + -instance-field3 = COND #( WHEN -%control-field3 NE if_abap_behv=>mk-off + THEN -field3 + ELSE -instance-field3 ). + + -instance-field4 = COND #( WHEN -%control-field4 NE if_abap_behv=>mk-off + THEN -field4 + ELSE -instance-field4 ). + + -changed = abap_true. + -deleted = abap_false. + ENDIF. + + + ENDLOOP. + ENDMETHOD. + + METHOD read. + "Preparing the transactional buffer based on the input BDEF derived type. + lcl_buffer=>prep_root_buffer( CORRESPONDING #( keys ) ). + + "Processing requested keys sequentially + LOOP AT keys ASSIGNING FIELD-SYMBOL() GROUP BY -%tky. + "Logic: + "- Line exists in the buffer and it is not marked as deleted + "- If it is true: Adding the entries to the buffer based on the input BDEF derived type and considering %control values + READ TABLE lcl_buffer=>root_buffer + WITH KEY instance-key_field = -key_field + deleted = abap_false + ASSIGNING FIELD-SYMBOL(). + + IF sy-subrc = 0. + + APPEND VALUE #( %tky = -%tky + field1 = COND #( WHEN -%control-field1 NE if_abap_behv=>mk-off + THEN -instance-field1 ) + field2 = COND #( WHEN -%control-field2 NE if_abap_behv=>mk-off + THEN -instance-field2 ) + field3 = COND #( WHEN -%control-field3 NE if_abap_behv=>mk-off + THEN -instance-field3 ) + field4 = COND #( WHEN -%control-field4 NE if_abap_behv=>mk-off + THEN -instance-field4 ) + ) TO result. + + ELSE. + + "Filling FAILED and REPORTED response parameters + APPEND VALUE #( %tky = -%tky + %fail-cause = if_abap_behv=>cause-not_found + ) TO failed-root. + + APPEND VALUE #( %tky = -%tky + %msg = new_message_with_text( + severity = if_abap_behv_message=>severity-error + text = 'Read operation failed.' ) + ) TO reported-root. + + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD lock. + + TRY. + "Instantiating lock object + DATA(lo_lock) = cl_abap_lock_object_factory=>get_instance( + iv_name = 'EZDEMO_ABAP_LOCK' ). + + "Processing requested keys sequentially + LOOP AT keys REFERENCE INTO DATA(lr_key). + TRY. + lo_lock->enqueue( it_parameter = VALUE #( + ( name = 'KEY_FIELD' value = REF #( + lr_key->key_field ) ) ) ). + CATCH cx_abap_foreign_lock. + APPEND VALUE #( %key = lr_key->* + %fail-cause = if_abap_behv=>cause-locked ) + TO failed-root. + CATCH cx_abap_lock_failure. + APPEND VALUE #( %key = lr_key->* + %fail-cause = if_abap_behv=>cause-unspecific ) + TO failed-root. + ENDTRY. + ENDLOOP. + CATCH cx_abap_lock_failure. + APPEND VALUE #( %fail-cause = if_abap_behv=>cause-unspecific ) + TO failed-root. + ENDTRY. + ENDMETHOD. + + METHOD delete. + "Preparing the transactional buffer based on the input BDEF derived type. + lcl_buffer=>prep_root_buffer( CORRESPONDING #( keys ) ). + + "Processing requested keys sequentially + "Note: + "The example is implemented in a way that instances that failed in methods called before this method are not handled. + "The instances that have failed before this method call are not available in this method's input parameter. + "Hence, an adding to FAILED and REPORTED is not implemented here. + LOOP AT keys ASSIGNING FIELD-SYMBOL(). + "Logic: + "- Line exists in the buffer and it is not marked as deleted + "- If it is true: Flagging the instance as deleted + READ TABLE lcl_buffer=>root_buffer + WITH KEY instance-key_field = -key_field + deleted = abap_false + ASSIGNING FIELD-SYMBOL(). + + IF sy-subrc = 0. + + -changed = abap_false. + -deleted = abap_true. + + ENDIF. + + ENDLOOP. + ENDMETHOD. + + METHOD rba_Child. + "Preparing the transactional buffers for both the root and child entity based on the input BDEF derived type. + lcl_buffer=>prep_root_buffer( CORRESPONDING #( keys_rba ) ). + lcl_buffer=>prep_child_buffer( CORRESPONDING #( keys_rba ) ). + + "Processing requested keys sequentially + LOOP AT keys_rba ASSIGNING FIELD-SYMBOL() GROUP BY -key_field. + "Logic: + "- Line with the shared key value exists in the buffer for the root entity and it is not marked as deleted + "- Line with the shared key value exists in the child buffer + "- If it is true: Sequentially processing the child buffer entries (the example is set up in a way that there can be multiple entries) + IF line_exists( lcl_buffer=>root_buffer[ instance-key_field = -key_field deleted = abap_false ] ) + AND line_exists( lcl_buffer=>child_buffer[ instance-key_field = -key_field ] ). + + LOOP AT lcl_buffer=>child_buffer ASSIGNING FIELD-SYMBOL() WHERE instance-key_field = -key_field. + + "Filling the table for the LINK parameter + INSERT VALUE #( source-%tky = -%tky + target-%tky = VALUE #( key_field = -instance-key_field + key_ch = -instance-key_ch ) ) INTO TABLE association_links. + + "Filling the table for the RESULT parameter based on the FULL parameter + "Note: If the FULL parameter is initial, only the LINK parameter should be provided + IF result_requested = abap_true. + APPEND VALUE #( %tky = -%tky + key_ch = COND #( WHEN -%control-key_ch NE if_abap_behv=>mk-off + THEN -instance-key_ch ) + field_ch1 = COND #( WHEN -%control-field_ch1 NE if_abap_behv=>mk-off + THEN -instance-field_ch1 ) + field_ch2 = COND #( WHEN -%control-field_ch2 NE if_abap_behv=>mk-off + THEN -instance-field_ch2 ) + ) TO result. + ENDIF. + ENDLOOP. + + ELSE. + + "Filling FAILED and REPORTED response parameters + APPEND VALUE #( %tky = -%tky + %fail-cause = if_abap_behv=>cause-not_found + %assoc-_child = if_abap_behv=>mk-on + ) TO failed-root. + + + APPEND VALUE #( %tky = -%tky + %msg = new_message_with_text( + severity = if_abap_behv_message=>severity-error + text = 'RBA (parent to child) operation failed.' ) + ) TO reported-root. + + ENDIF. + ENDLOOP. + + "Removing potential duplicate entries + SORT association_links BY target ASCENDING. + DELETE ADJACENT DUPLICATES FROM association_links COMPARING ALL FIELDS. + + SORT result BY %tky ASCENDING. + DELETE ADJACENT DUPLICATES FROM result COMPARING ALL FIELDS. + + ENDMETHOD. + + METHOD cba_Child. + "Preparing the transactional buffers for both the root and child entity based on the input BDEF derived type + lcl_buffer=>prep_root_buffer( CORRESPONDING #( entities_cba ) ). + lcl_buffer=>prep_child_buffer( CORRESPONDING #( entities_cba ) ). + + "Processing requested entities sequentially + LOOP AT entities_cba ASSIGNING FIELD-SYMBOL() GROUP BY -key_field. + "Logic: + "- Line with the shared key value exists in the buffer for the root entity and it is not marked as deleted + "- If it is true: Sequentially processing the instances in the %target table + IF line_exists( lcl_buffer=>root_buffer[ instance-key_field = -key_field deleted = abap_false ] ). + + LOOP AT -%target ASSIGNING FIELD-SYMBOL(). + + "Adding instance to child buffer if it does not exist there and considering %control values + "The example is implemented in a way that the RAP BO consumer need not specify the common key with the root entity. + "Plus, the keys of the child entity should not be initial. + IF NOT line_exists( lcl_buffer=>child_buffer[ instance-key_field = -key_field + instance-key_ch = -key_ch ] ) + AND -key_ch IS NOT INITIAL. + + APPEND VALUE #( cid_ref = -%cid_ref + cid_target = -%cid + instance-key_field = -key_field + instance-key_ch = -key_ch + instance-field_ch1 = COND #( WHEN -%control-field_ch1 NE if_abap_behv=>mk-off + THEN -field_ch1 ) + instance-field_ch2 = COND #( WHEN -%control-field_ch2 NE if_abap_behv=>mk-off + THEN -field_ch2 ) + changed = abap_true + ) TO lcl_buffer=>child_buffer. + + "Filling MAPPED response parameter + INSERT VALUE #( %cid = -%cid + %key = VALUE #( key_field = -key_field + key_ch = -key_ch ) ) INTO TABLE mapped-child. + + ELSE. + + "Filling FAILED and REPORTED response parameters + APPEND VALUE #( %cid = -%cid_ref + %tky = -%tky + %assoc-_child = if_abap_behv=>mk-on + %fail-cause = if_abap_behv=>cause-unspecific + ) TO failed-root. + + APPEND VALUE #( %cid = -%cid_ref + %tky = -%tky + %msg = new_message_with_text( + severity = if_abap_behv_message=>severity-error + text = 'Create-by-association (root to child) operation failed.' ) + ) TO reported-root. + + APPEND VALUE #( %cid = -%cid + %key = VALUE #( key_field = -key_field key_ch = -key_ch ) + %fail-cause = if_abap_behv=>cause-dependency + ) TO failed-child. + + APPEND VALUE #( %cid = -%cid + %key = VALUE #( key_field = -key_field key_ch = -key_ch ) + %msg = new_message_with_text( + severity = if_abap_behv_message=>severity-error + text = 'Create-by-association (root to child) operation failed.' ) + ) TO reported-child. + + ENDIF. + ENDLOOP. + + ELSE. + + "Filling FAILED and REPORTED response parameters + APPEND VALUE #( %cid = -%cid_ref + %tky = -%tky + %assoc-_child = if_abap_behv=>mk-on + %fail-cause = if_abap_behv=>cause-not_found + ) TO failed-root. + + APPEND VALUE #( %cid = -%cid_ref + %tky = -%tky + %msg = new_message_with_text( + severity = if_abap_behv_message=>severity-error + text = 'Create-by-association (root to child) operation failed.' ) + ) TO reported-root. + + LOOP AT -%target ASSIGNING FIELD-SYMBOL(). + APPEND VALUE #( %cid = -%cid + %key = -%key + %fail-cause = if_abap_behv=>cause-dependency + ) TO failed-child. + + APPEND VALUE #( %cid = -%cid + %key = -%key + %msg = new_message_with_text( + severity = if_abap_behv_message=>severity-error + text = 'Create-by-association (root to child) operation failed.' ) + ) TO reported-child. + ENDLOOP. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD multiply_by_2. + + "Retrieving instances based on requested keys + READ ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE + ENTITY root + FIELDS ( field3 field4 ) WITH CORRESPONDING #( keys ) + RESULT DATA(result) + FAILED failed. + + "If read result is initial, stop further method execution. + CHECK result IS NOT INITIAL. + + "Setting %action value in failed response parameter + LOOP AT failed-root ASSIGNING FIELD-SYMBOL(). + -%action-multiply_by_2 = if_abap_behv=>mk-on. + ENDLOOP. + + "Multiply integer values by 2 + MODIFY ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE + ENTITY root + UPDATE FIELDS ( field3 field4 ) WITH VALUE #( FOR key IN result ( %tky = key-%tky + field3 = key-field3 * 2 + field4 = key-field4 * 2 ) ). + ENDMETHOD. + + METHOD get_instance_authorizations. + + "Retrieving instances based on requested keys + READ ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE + ENTITY root + FIELDS ( field1 ) WITH CORRESPONDING #( keys ) + RESULT DATA(status) + FAILED failed. + + "If the read result is initial, stop further method execution. + CHECK status IS NOT INITIAL. + + LOOP AT status ASSIGNING FIELD-SYMBOL(). + + "If a specific field has a certain value, the deletion should be disallowed. + IF requested_authorizations-%delete = if_abap_behv=>mk-on. + APPEND VALUE #( %tky = -%tky + %op = VALUE #( %delete = COND #( WHEN -field1 = 'X' + THEN if_abap_behv=>auth-unauthorized + ELSE if_abap_behv=>auth-allowed ) ) + ) TO result. + + ENDIF. + ENDLOOP. + + ENDMETHOD. + + METHOD get_instance_features. + + READ ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE + ENTITY root + FIELDS ( field3 field4 ) WITH CORRESPONDING #( keys ) + RESULT DATA(numbers) + FAILED failed. + + "If the read result is initial, stop further method execution. + CHECK numbers IS NOT INITIAL. + + LOOP AT numbers ASSIGNING FIELD-SYMBOL(). + + "If two fields have certain values, the execution of an action should be disabled for the instance. + IF requested_features-%action-multiply_by_3 = if_abap_behv=>mk-on. + APPEND VALUE #( %tky = -%tky + %features-%action-multiply_by_3 = COND #( WHEN -field3 = 0 OR -field4 = 0 + THEN if_abap_behv=>fc-o-disabled + ELSE if_abap_behv=>fc-o-enabled ) + ) TO result. + + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD multiply_by_3. + "Retrieving instances based on requested keys + READ ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE + ENTITY root + FIELDS ( field3 field4 ) WITH CORRESPONDING #( keys ) + RESULT DATA(result) + FAILED failed. + + "Setting %action value in failed response parameter + LOOP AT failed-root ASSIGNING FIELD-SYMBOL(). + -%action-multiply_by_3 = if_abap_behv=>mk-on. + ENDLOOP. + + "Multiply integer values by 2 + MODIFY ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE + ENTITY root + UPDATE FIELDS ( field3 field4 ) WITH VALUE #( FOR key IN result ( %tky = key-%tky + field3 = key-field3 * 3 + field4 = key-field4 * 3 ) ). + ENDMETHOD. + + METHOD get_global_features. + "The execution of an action should be disabled based on a certain time frame. + DATA(time1) = CONV t( '070000' ). + DATA(time2) = CONV t( '120000' ). + + result = VALUE #( %action-set_z = COND #( WHEN cl_abap_context_info=>get_system_time( ) BETWEEN time1 AND time2 + THEN if_abap_behv=>fc-o-enabled + ELSE if_abap_behv=>fc-o-disabled ) + ). + + IF result-%action-set_z = if_abap_behv=>fc-o-disabled. + APPEND VALUE #( %msg = new_message_with_text( text = 'Execution of action currently not allowed.' + severity = if_abap_behv_message=>severity-error ) + %global = if_abap_behv=>mk-on ) TO reported-root. + ENDIF. + ENDMETHOD. + + METHOD set_z. + + "Retrieving instances based on requested keys + READ ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE + ENTITY root + FIELDS ( field3 field4 ) WITH CORRESPONDING #( keys ) + RESULT DATA(result) + FAILED failed. + + "Setting %action value in failed response parameter + LOOP AT failed-root ASSIGNING FIELD-SYMBOL(). + -%action-set_z = if_abap_behv=>mk-on. + ENDLOOP. + + "Setting a field value + MODIFY ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE + ENTITY root + UPDATE FIELDS ( field2 ) WITH VALUE #( FOR key IN result ( %tky = key-%tky + field2 = 'Z' ) ). + + ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Local saver class lsc_zdemo_abap_rap_ro_u +* +* Contains saver method definitions and implementations. The only +* methods that are implemented in this example are the save method (to +* persist the data to the database) and cleanup methods (to clear the +* transactional buffer). +* +* The save method is implemented in a way, that the database tables +* are modified based on the entries in the transactional buffer tables +* and the flags for changed and deleted. +* If the flag for changed is not initial, the instance gets created +* or updated respectively in the database table. +* If the flag for deleted is not initial, the database table entry +* is deleted. +* +*********************************************************************** + +CLASS lsc_zdemo_abap_rap_ro_u DEFINITION INHERITING FROM cl_abap_behavior_saver. + PROTECTED SECTION. + + METHODS finalize REDEFINITION. + + METHODS check_before_save REDEFINITION. + + METHODS save REDEFINITION. + + METHODS cleanup REDEFINITION. + + METHODS cleanup_finalize REDEFINITION. + +ENDCLASS. + +CLASS lsc_zdemo_abap_rap_ro_u IMPLEMENTATION. + + METHOD finalize. + ENDMETHOD. + + METHOD check_before_save. + ENDMETHOD. + + METHOD save. + + "Processing the saving of create and update operations + "Only those entries should be saved to the database table whose flag for "changed" is not initial. + DATA mod_tab TYPE TABLE OF zdemo_abap_rap_ro_u. + + IF line_exists( lcl_buffer=>root_buffer[ changed = abap_true ] ). + LOOP AT lcl_buffer=>root_buffer ASSIGNING FIELD-SYMBOL() WHERE changed = abap_true AND deleted = abap_false. + + APPEND CORRESPONDING #( -instance ) TO mod_tab. + + ENDLOOP. + + MODIFY zdemo_abap_rapt1 FROM TABLE @( CORRESPONDING #( mod_tab ) ). + ENDIF. + + "Processing the saving of delete operations + "Only those entries should be deleted from the database table whose flag "deleted" is not initial. + DATA del_tab TYPE lcl_buffer=>tt_root_keys. + + IF line_exists( lcl_buffer=>root_buffer[ deleted = abap_true ] ). + + LOOP AT lcl_buffer=>root_buffer ASSIGNING FIELD-SYMBOL() WHERE deleted = abap_true. + + APPEND CORRESPONDING #( -instance ) TO del_tab. + + ENDLOOP. + + DELETE zdemo_abap_rapt1 FROM TABLE @( CORRESPONDING #( del_tab ) ). + ENDIF. + + "Processing the saving of create-by-association operations. + DATA cba_tab TYPE TABLE OF zdemo_abap_rap_ch_u. + + IF line_exists( lcl_buffer=>child_buffer[ changed = abap_true ] ). + LOOP AT lcl_buffer=>child_buffer ASSIGNING FIELD-SYMBOL() WHERE changed = abap_true. + + APPEND CORRESPONDING #( -instance ) TO cba_tab. + + ENDLOOP. + + MODIFY zdemo_abap_rapt2 FROM TABLE @( CORRESPONDING #( cba_tab ) ). + ENDIF. + ENDMETHOD. + + METHOD cleanup. + "Clearing the transactional buffer. + CLEAR lcl_buffer=>root_buffer. + CLEAR lcl_buffer=>child_buffer. + ENDMETHOD. + + METHOD cleanup_finalize. + CLEAR lcl_buffer=>root_buffer. + CLEAR lcl_buffer=>child_buffer. + ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Local handler class for the child entity lhc_child +* +* Contains handler method definitions and implementations for read +* and read-by-association operations. +* +*********************************************************************** + +CLASS lhc_child DEFINITION INHERITING FROM cl_abap_behavior_handler. + + PRIVATE SECTION. + + METHODS read FOR READ + IMPORTING keys FOR READ child RESULT result. + + METHODS rba_Parent FOR READ + IMPORTING keys_rba FOR READ child\_Parent FULL result_requested RESULT result LINK association_links. + +ENDCLASS. + +CLASS lhc_child IMPLEMENTATION. + + METHOD read. + "Preparing the transactional buffer for child entity based on the input BDEF derived type. + "Here, the full_key flag is set to consider all key values. + "Purpose: The preparation method is set up to also consider the entries in the root buffer + "when dealing with by-association operations which is not relevant in this case. + lcl_buffer=>prep_child_buffer( VALUE #( FOR wa IN keys ( key_field = wa-key_field + key_ch = wa-key_ch + full_key = abap_true ) ) ). + + "Processing the requested keys sequentially + LOOP AT keys ASSIGNING FIELD-SYMBOL() GROUP BY -%tky. + + "Logic: + "- Line with the requested key values exists in the child buffer + "- If it is true: Adding the line to the RESULT parameter considering %control values. + READ TABLE lcl_buffer=>child_buffer + WITH KEY instance-key_field = -key_field + instance-key_ch = -key_ch + ASSIGNING FIELD-SYMBOL(). + + IF sy-subrc = 0. + + APPEND VALUE #( %tky = -%tky + field_ch1 = COND #( WHEN -%control-field_ch1 NE if_abap_behv=>mk-off + THEN -instance-field_ch1 ) + field_ch2 = COND #( WHEN -%control-field_ch2 NE if_abap_behv=>mk-off + THEN -instance-field_ch2 ) + ) TO result. + + ELSE. + + "Filling FAILED and REPORTED response parameters + APPEND VALUE #( %tky = -%tky + %fail-cause = if_abap_behv=>cause-not_found + ) TO failed-child. + + APPEND VALUE #( %tky = -%tky + %msg = new_message_with_text( + severity = if_abap_behv_message=>severity-error + text = 'Read operation failed (child entity).' ) + ) TO reported-child. + + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD rba_Parent. + "Preparing the transactional buffers for both the root and child entity based on the input BDEF derived type. + lcl_buffer=>prep_root_buffer( CORRESPONDING #( keys_rba ) ). + lcl_buffer=>prep_child_buffer( CORRESPONDING #( keys_rba ) ). + + LOOP AT keys_rba ASSIGNING FIELD-SYMBOL() GROUP BY -%tky. + "Logic: + "- Line with the shared key value exists in buffer for the root entity and is not marked as deleted + "- Line with the full key exists in the child buffer + "- If it is true: Adding the instance to the RESULT parameter considering %control values + IF line_exists( lcl_buffer=>root_buffer[ instance-key_field = -key_field deleted = abap_false ] ) + AND line_exists( lcl_buffer=>child_buffer[ instance-key_field = -key_field instance-key_ch = -key_ch ] ). + + "Filling the LINK parameter + INSERT VALUE #( target-%tky = -%tky + source-%tky = VALUE #( key_field = -key_field + key_ch = -key_ch ) + ) INTO TABLE association_links. + + IF result_requested = abap_true. + READ TABLE lcl_buffer=>root_buffer + WITH KEY instance-key_field = -key_field + ASSIGNING FIELD-SYMBOL(). + + IF sy-subrc = 0. + + APPEND VALUE #( %tky = -%tky + field1 = COND #( WHEN -%control-field1 NE if_abap_behv=>mk-off + THEN -instance-field1 ) + field2 = COND #( WHEN -%control-field2 NE if_abap_behv=>mk-off + THEN -instance-field2 ) + field3 = COND #( WHEN -%control-field3 NE if_abap_behv=>mk-off + THEN -instance-field3 ) + field4 = COND #( WHEN -%control-field4 NE if_abap_behv=>mk-off + THEN -instance-field4 ) + ) TO result. + ENDIF. + ENDIF. + + ELSE. + + "Filling FAILED and REPORTED response parameters + APPEND VALUE #( %tky = -%tky + %assoc-_parent = if_abap_behv=>mk-on + %fail-cause = if_abap_behv=>cause-not_found + ) TO failed-child. + + + APPEND VALUE #( %tky = -%tky + %msg = new_message_with_text( + severity = if_abap_behv_message=>severity-error + text = 'RBA (child to parent) failed.' ) + ) TO reported-child. + + ENDIF. + ENDLOOP. + + "Removing potential duplicate entries. + SORT association_links BY target ASCENDING. + DELETE ADJACENT DUPLICATES FROM association_links COMPARING ALL FIELDS. + + SORT result BY %tky ASCENDING. + DELETE ADJACENT DUPLICATES FROM result COMPARING ALL FIELDS. + + ENDMETHOD. + +ENDCLASS. diff --git a/src/zbp_demo_abap_rap_ro_u.clas.xml b/src/zbp_demo_abap_rap_ro_u.clas.xml new file mode 100644 index 0000000..e3f8ebd --- /dev/null +++ b/src/zbp_demo_abap_rap_ro_u.clas.xml @@ -0,0 +1,18 @@ + + + + + + ZBP_DEMO_ABAP_RAP_RO_U + E + Behavior implementation for RAP demo scenario (unmanaged BO) + 06 + 1 + X + X + X + ZDEMO_ABAP_RAP_RO_U + + + + diff --git a/src/zcl_demo_abap_constructor_expr.clas.abap b/src/zcl_demo_abap_constructor_expr.clas.abap new file mode 100644 index 0000000..93242c3 --- /dev/null +++ b/src/zcl_demo_abap_constructor_expr.clas.abap @@ -0,0 +1,1277 @@ +*********************************************************************** +* +* ABAP cheat sheet: Constructor expressions +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate the use of constructor expressions as outlined +* in the respective ABAP cheat sheet. +* - Topics covered: Operators VALUE, CORRESPONDING, NEW, CONV, EXACT, REF, +* CAST, COND, SWITCH, FILTER, REDUCE, Iteration Expressions with FOR, +* LET Expressions +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP Development Tools (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, check the notes +* included in the class as comments or refer to the respective topic +* in the ABAP Keyword Documentation. +* - Due to the amount of output in the console, the examples include +* numbers (e. g. 1) ..., 2) ..., 3) ...) for the individual example +* sections. Plus, the variable name is displayed in most cases. Hence, +* to easier and faster find the relevant output in the console, just +* search in the console for the number/variable name (STRG+F in the +* console) or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** +"!

ABAP cheat sheet: Constructor expressions

+"! Example to demonstrate the use of constructor expressions.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_constructor_expr DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + +protected section. + PRIVATE SECTION. + TYPES: BEGIN OF line1, + col1 TYPE i, + col2 TYPE i, + END OF line1, + BEGIN OF line2, + col2 TYPE i, + col3 TYPE i, + col4 TYPE i, + END OF line2, + BEGIN OF s1_type, + comp1 TYPE c LENGTH 1, + comp2 TYPE string, + comp3 TYPE i, + END OF s1_type, + BEGIN OF s2_type, + comp1 TYPE string, + comp2 TYPE c LENGTH 1, + comp3 TYPE i, + comp4 TYPE i, + END OF s2_type. + + CLASS-DATA: + "Deep structures as examples to demonstrate the CORRESPONDING operator + BEGIN OF struc1, + comp1 TYPE c LENGTH 1 VALUE 'W', + BEGIN OF struc_nested, + comp1 TYPE c LENGTH 1 VALUE 'X', + BEGIN OF comp2, + col1 TYPE c LENGTH 1 VALUE 'Y', + col2 TYPE c LENGTH 1 VALUE 'Z', + END OF comp2, + END OF struc_nested, + itab TYPE TABLE OF line1 WITH EMPTY KEY, + END OF struc1, + BEGIN OF struc2, + BEGIN OF struc_nested, + comp1 TYPE string, + comp2 TYPE string, + comp3 TYPE string, + END OF struc_nested, + itab TYPE TABLE OF line2 WITH EMPTY KEY, + comp4 TYPE i, + END OF struc2, + s1 TYPE s1_type, + s2 TYPE s2_type, + tab1 TYPE TABLE OF s1_type WITH EMPTY KEY, + tab2 TYPE TABLE OF s2_type WITH EMPTY KEY, + tab3 TYPE TABLE OF s2_type WITH EMPTY KEY, + tab4 TYPE SORTED TABLE OF s2_type WITH NON-UNIQUE KEY comp3. + + CLASS-METHODS: + fill_deep_structures, + fill_struc_and_tab. + +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_CONSTRUCTOR_EXPR IMPLEMENTATION. + + + METHOD fill_deep_structures. + "Clearing all contents of struc2 + CLEAR struc2. + "Filling nested tables in deep structures + struc2-struc_nested = VALUE #( comp1 = `aaa` + comp2 = `bbb` + comp3 = `ccc`). + + struc1-itab = VALUE #( + ( col1 = 111 col2 = 222 ) + ( col1 = 333 col2 = 444 + ) ). + + struc2-itab = VALUE #( + ( col2 = 1 col3 = 2 col4 = 3 ) + ( col2 = 4 col3 = 5 col4 = 6 ) + ( col2 = 7 col3 = 8 col4 = 9 ) + ). + + "Filling individual component that is not shared by both structures + struc2-comp4 = 999. + ENDMETHOD. + + + METHOD fill_struc_and_tab. + CLEAR: s1, s2, tab1, tab2, tab3. + + s1 = VALUE #( comp1 = 'A' comp2 = `bbb` comp3 = 1 ). + + s2 = VALUE #( comp1 = `ccc` comp2 = 'D' comp3 = 2 comp4 = 3 ). + + tab1 = VALUE #( + ( comp1 = 'A' comp2 = `bbb` comp3 = 1 ) + ( comp1 = 'B' comp2 = `ccc` comp3 = 2 ) + ( comp1 = 'C' comp2 = `ddd` comp3 = 3 ) ). + + tab2 = VALUE #( + ( comp1 = `eee` comp2 = 'F' comp3 = 4 comp4 = 5 ) + ( comp1 = `ggg` comp2 = 'H' comp3 = 6 comp4 = 7 ) + ( comp1 = `iii` comp2 = 'J' comp3 = 8 comp4 = 9 ) ). + + tab3 = VALUE #( + ( comp1 = `aaa` comp2 = 'B' comp3 = 1 comp4 = 2 ) + ( comp1 = `ccc` comp2 = 'D' comp3 = 3 comp4 = 4 ) + ( comp1 = `eee` comp2 = 'F' comp3 = 5 comp4 = 6 ) + ( comp1 = `ggg` comp2 = 'H' comp3 = 7 comp4 = 8 ) + ( comp1 = `iii` comp2 = 'J' comp3 = 9 comp4 = 10 ) ). + + tab4 = tab3. + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `Demo: Constructor Expressions` ). + + + output->display( `1. VALUE` ). + output->display( `1.1 VALUE: Structures` ). + + "1) A flat structure is created based on a data type defined with a + "TYPES statement. The structure is then filled using a constructor + "expression with VALUE by specifying the components and assigning + "values. Here, the type can be inferred, hence, a # symbol can be used. + + output->display( `1.1.1 Flat structure` ). + + TYPES: BEGIN OF struc_type, + num TYPE i, + char1 TYPE c LENGTH 3, + char2 TYPE c LENGTH 3, + END OF struc_type. + + DATA struc TYPE struc_type. + + "Filling structure + struc = VALUE #( num = 1 char1 = 'aaa' char2 = 'abc' ). + + output->display( input = struc name = `struc` ). + + "2) The same structure is then filled purposely omitting components, i. + "e. these components remain initial. + + output->next_section( `1.1.2 Omitting value assignment to components` ). + + struc = VALUE #( char1 = 'bbb' ). + + output->display( input = struc name = `struc` ). + + "3) The example demonstrates a variable that is declared inline. Here, + "the result is a structure which is filled using a constructor + "expression with VALUE and by specifying the components and assigning + "values in parentheses. The structure type is specified explicitly. + "The # symbol would not work since no type can be inferred from the + "specified parameters. + + output->next_section( `1.1.3 Structure declared inline, explicit type specification` ). + + DATA(struc_inl) = VALUE struc_type( num = 3 + char1 = 'ccc' + char2 = 'def' ). + + output->display( input = struc_inl name = `struc_inl` ). + + + output->next_section( `1.2 VALUE: Internal tables` ). + + "1) The example demonstrates the declaration of an internal table. The + "internal table is then filled using a constructor expression with + "VALUE. + "The type can be inferred here and need not be specified explicitly. + "Note the extra pair of parentheses in which the components are + "specified and assigned values. In the example, 3 lines are added to + "the table. For one line, some components are purposely not assigned. + + output->display( `1.2.1 Declaration and filling of an internal table` ). + + DATA itab TYPE TABLE OF struc_type WITH EMPTY KEY. + + itab = VALUE #( ( num = 1 char1 = 'aaa' char2 = 'abc' ) + ( num = 2 char1 = 'bbb' char2 = 'def' ) + ( num = 3 char1 = 'ccc' ) ). + + output->display( input = itab name = `itab` ). + + "2) The example demonstrates an internal table declared inline that is + "filled using a constructor expression with VALUE by specifying the + "internal table type explicitly. Note that the internal table type + "cannot be generic in this context. + + output->next_section( `1.2.2 Table declared inline, explicitly specified table type` ). + + TYPES: itab_type TYPE STANDARD TABLE OF struc_type + WITH NON-UNIQUE KEY num. + + DATA(itab2) = VALUE itab_type( + ( num = 4 char1 = 'ddd' char2 = 'ghi' ) + ( num = 5 char1 = 'eee' char2 = 'jkl' ) ). + + output->display( input = itab2 name = `itab2` ). + + "3) Using the LINES OF addition, you can add lines of other tables. + "Note: The line type of the other internal table must match the one of + "the target internal table. Using FROM/TO, the table line selection can + "be further restricted. Without FROM/TO, all lines of the table are + "respected. + + output->next_section( `1.2.3 LINES OF addition` ). + + itab2 = VALUE #( ( num = 6 char1 = 'fff' char2 = 'mno' ) + ( LINES OF itab ) + ( LINES OF itab FROM 1 TO 2 ) ). + + output->display( input = itab2 name = `itab2` ). + + "4) Using the BASE addition, you can keep existing content of the source + "internal table. + + output->next_section( `1.2.4 BASE addition for keeping existing data` ). + + itab2 = VALUE #( BASE itab2 ( num = 7 char1 = 'ggg' + char2 = 'pqr' ) ). + + output->display( input = itab2 name = `itab2` ). + + "5) Various examples using ABAP (SQL) statements in which table lines + "are constructed inline. + + output->next_section( `ABAP statements and ABAP SQL statements` ). + output->display( `1.2.5 Modifying internal table from a structure created inline` ). + + MODIFY TABLE itab2 FROM VALUE #( num = 7 + char1 = 'hhh' + char2 = 'stu' ). + + output->display( input = itab2 name = `itab2` ). + + + output->next_section( `1.2.6 Inserting a table line that is created inline into an internal table` ). + + INSERT VALUE #( num = 8 char1 = 'iii' char2 = 'vwx' ) + INTO TABLE itab2. + + output->display( input = itab2 name = `itab2` ). + + + output->next_section( `1.2.7 Deleting a table entry based on a line created inline` ). + + DELETE TABLE itab2 FROM VALUE #( num = 3 ). + + output->display( input = itab2 name = `itab2` ). + + "Delete demo database table entries for demo below + DELETE FROM zdemo_abap_carr. + + output->next_section( `1.2.8 Modifying a database table based on an internal table created inline` ). + + MODIFY zdemo_abap_carr FROM TABLE @( VALUE #( + ( carrid = 'CO' + carrname = 'Continental Airlines' + currcode = 'USD' + url = 'http://www.continental.com' ) + ( carrid = 'SQ' + carrname = 'Singapore Airlines' + currcode = 'SGD' + url = 'http://www.singaporeair.com' ) + ) ). + + "Retrieving table entries for display purposes + SELECT FROM zdemo_abap_carr + FIELDS carrid, carrname, currcode, url + ORDER BY carrid + INTO TABLE @DATA(itab_carr). + + output->display( input = itab_carr name = `itab_carr` ). + + output->next_section( `1.3 Excursion: Deep structures and tables` ). + + "1) The example demonstrates the use of constructor expressions with + "VALUE in the context of a deep structure. Here, a structure is declared + "inline and filled. The structure type includes a nested structure. The + "nested structure is filled using a nested VALUE expression. + + output->display( `1.3.1 Deep structure` ). + + TYPES: BEGIN OF deep_struc_ty, + num TYPE i, + char1 TYPE c LENGTH 3, + BEGIN OF substruc, + int TYPE i, + str TYPE string, + END OF substruc, + END OF deep_struc_ty. + + DATA(deep_struc) = VALUE deep_struc_ty( num = 1 char1 = 'aaa' + + substruc = VALUE #( int = 123 str = `hallo` ) ). + + output->display( input = deep_struc name = `deep_struc` ). + + "2) A deep internal table is created. Also here, nested VALUE + "expressions are demonstrated. + + output->next_section( `1.3.2 Deep internal table` ). + + TYPES: BEGIN OF deep_struc_ty2, + char TYPE c LENGTH 3, + tab TYPE TABLE OF i WITH EMPTY KEY, + END OF deep_struc_ty2. + + TYPES: itab_deep_type TYPE STANDARD TABLE OF deep_struc_ty2 + WITH NON-UNIQUE KEY char. + + DATA(deep_itab) = VALUE itab_deep_type( + ( char = 'aaa' tab = VALUE #( ( 1 ) ( 2 ) ( 3 ) ) ) + ( char = 'bbb' tab = VALUE #( ( 4 ) ( 5 ) ( 6 ) ) ) ). + + output->display( input = deep_itab name = `deep_itab` ). + + output->next_section( `2. CORRESPONDING` ). + + output->display( `2.1 CORRESPONDING: Simple Examples with ` && + `Structures and Internal Tables` ). + + "1) Displaying the original structures and tables that are filled in the + "course of a method call. The structures and tables are filled anew + "throughout the examples so that all CORRESPONDING expressions are based + "on the same values. + + "Method to fill demo structures and internal tables + "with values to work with + fill_struc_and_tab( ). + + output->display( `2.1.1 Original structure and table content` ). + output->display( input = s1 name = `s1` ). + output->display( input = s2 name = `s2` ). + output->display( input = tab1 name = `tab1` ). + output->display( input = tab2 name = `it_st` ). + + "2) The target structure and table have a different type but identically + "named components. The identically named components are filled. Note + "that the target variables are initialized here. Also note the effect + "of an automatic conversion of a variable-length string to a + "fixed-length string (one component is typed with c, the other, + "identically named component, is of type string). + + output->next_section( `2.1.2 CORRESPONDING without addition` ). + + s2 = CORRESPONDING #( s1 ). + + tab2 = CORRESPONDING #( tab1 ). + + output->display( input = s2 name = `s2` ). + output->display( input = tab2 name = `tab2` ). + + fill_struc_and_tab( ). + + "3) The BASE addition keeps the original content. Structure: The non- + "identical component name retains its value. Internal table: Existing + "table lines are kept. + + output->next_section( `2.1.3 BASE addition for keeping original content` ). + + s2 = CORRESPONDING #( BASE ( s2 ) s1 ). + + tab2 = CORRESPONDING #( BASE ( tab2 ) tab1 ). + + output->display( input = s2 name = `s2` ). + output->display( input = tab2 name = `tab2` ). + + fill_struc_and_tab( ). + + "4) The example demonstrates the additions MAPPING and EXCEPT. MAPPING: + "One component of the target structure is assigned the value of a + "dedicated component of the source structure. EXCEPT: All corresponding + "components are assigned except a specific one. + + output->next_section( `2.1.4 MAPPING/EXCEPT additions` ). + + s2 = CORRESPONDING #( s1 MAPPING comp4 = comp3 ). + + tab2 = CORRESPONDING #( tab1 EXCEPT comp1 ). + + output->display( input = s2 name = `s2` ). + output->display( input = tab2 name = `tab2` ). + + output->next_section( `2.2 CORRESPONDING: Demonstrating Various` && + ` Additions Using Deep Structures` ). + + "Displaying the original deep structures and tables that are filled in + "the course of a method call. The deep structures and tables are filled + "anew throughout the examples so that all CORRESPONDING expressions are + "based on the same values. + + "Method to fill demo deep structures and internal tables + "with values to work with + fill_deep_structures( ). + + output->display( `2.2.1 Original content of deep structures` ). + output->display( input = struc1 name = `struc1` ). + output->display( input = struc2 name = `struc2` ). + + "CORRESPONDING operator without addition + "Existing contents of identically named components are replaced. + "Existing contents of components in the target structure that are not + "available in the source structure are initialized. + "Contents of the nested structure struc_nested is converted to + "string. Note that the two components of the nested structure in + "component struc_nested-comp2 of struc1 are drawn together when being + "converted to string. + "Contents of struc2-itab are replaced by table contents of struc1- + "itab. Note the value assignment, for example, for col2 in struc2-itab. + "Despite the fact that there is no identically named component comp1 in + "the target structure, values are assigned starting with the first + "column of the source structure. Check the conversion rules for + "internal tables. + + output->next_section( `2.2.2 CORRESPONDING without addition` ). + + struc2 = CORRESPONDING #( struc1 ). + + output->display( input = struc2 name = `struc2` ). + + fill_deep_structures( ). + + "CORRESPONDING operator with the addition DEEP + "Existing contents of identically named components are replaced. + "Existing contents of components in the target structure that are not + "available in the source structure are initialized. + "Contents of the nested structure struc_nested is converted to + "string. Note that the two components of the nested structure in + "component struc_nested-comp2 of struc1 are drawn together when being + "converted to string. + "Contents of struc2-itab are replaced by table contents of struc1- + "itab. Due to the addition DEEP, the value assignment happens for + "identically named components in the nested table. Hence, only col2 as + "the only shared and identically named component is filled. + + output->next_section( `2.2.3 DEEP addition` ). + + struc2 = CORRESPONDING #( DEEP struc1 ). + + output->display( input = struc2 name = `struc2` ). + + fill_deep_structures( ). + + "CORRESPONDING operator with the addition BASE + "Existing contents of identically named components are replaced. + "Existing contents of components in the target structure that are not + "available in the source structure are kept. + "Contents of the nested structure struc_nested is converted to + "string. Note that the two components of the nested structure in + "component struc_nested-comp2 of struc1 are drawn together when being + "converted to string. + "Contents of struc2-itab are replaced by table contents of struc1- + "itab. The value assignment in the nested table happens like using the + "CORRESPONDING operator without addition. Note the value assignment, for + "example, for col2 in struc2-itab. Despite the fact that there is no + "identically named component col1 in the target structure, values are + "assigned starting with the first column of the source structure. Check + "the conversion rules for internal tables. + + output->next_section( `2.2.4 BASE addition` ). + + struc2 = CORRESPONDING #( BASE ( struc2 ) struc1 ). + + output->display( input = struc2 name = `struc2` ). + + fill_deep_structures( ). + + "CORRESPONDING operator with the additions DEEP BASE + "Existing contents of identically named components are replaced. + "Existing contents of components in the target structure that are not + "available in the source structure are kept. + "Contents of the nested structure struc_nested is converted to + "string. Note that the two components of the nested structure in + "component struc_nested-comp2 of struc1 are drawn together when being + "converted to string. + "Contents of struc2-itab are replaced by table contents of struc1- + "itab. The value assignment in the nested table happens like using the + "CORRESPONDING operator with the addition DEEP. That is, the value + "assignment happens for identically named components in the nested table. + "Hence, only col2 as the only shared and identically named component is filled. + + output->next_section( `2.2.5 DEEP BASE addition` ). + + struc2 = CORRESPONDING #( DEEP BASE ( struc2 ) struc1 ). + + output->display( input = struc2 name = `struc2` ). + + fill_deep_structures( ). + + "CORRESPONDING operator with the addition APPENDING + "Existing contents of identically named components are replaced. + "Existing contents of components in the target structure that are not + "available in the source structure are kept. + "Contents of the nested structure struc_nested is converted to + "string. Note that the two components of the nested structure in + "component struc_nested-comp2 of struc1 are drawn together when being + "converted to string. + "Contents of struc2-itab are kept and contents of struc1-itab are + "added. The value assignment concerning the added lines happens like + "using the CORRESPONDING operator without addition. Note the value + "assignment, for example, for col2 in struc2-itab. Despite the fact that + "there is no identically named component col1 in the target structure, + "values are assigned starting with the first column of the source + "structure. Check the conversion rules for internal tables. + + output->next_section( `2.2.6 APPENDING addition` ). + + struc2 = CORRESPONDING #( APPENDING ( struc2 ) struc1 ). + + output->display( input = struc2 name = `struc2` ). + + fill_deep_structures( ). + + "CORRESPONDING operator with the additions DEEP APPENDING + "Existing contents of identically named components are replaced. + "Existing contents of components in the target structure that are not + "available in the source structure are kept. + "Contents of the nested structure struc_nested is converted to + "string. Note that the two components of the nested structure in + "component struc_nested-comp2 of struc1 are drawn together when being + "converted to string. + "Contents of struc2-itab are kept and contents of struc1-itab are + "added. The value assignment concerning the added lines happens like + "using the CORRESPONDING operator with the addition DEEP. That is, the + "value assignment happens for identically named components in the nested + "table. Hence, only col2 as the only shared and identically named + "component is filled. + + output->next_section( `2.2.7 DEEP APPENDING` ). + + struc2 = CORRESPONDING #( DEEP APPENDING ( struc2 ) struc1 ). + + output->display( input = struc2 name = `struc2` ). + + output->next_section( `3. NEW` ). + output->display( `3.1 NEW: Creating Anonymous Data Objects` ). + + "The examples show the creation of anonymous data objects. + "First, data reference variables are declared using a DATA statement. + "Here, one variable is declared with a complete type, the other one with + "a generic type. + "Then, anonymous data objects are created using the NEW operator. For + "one example, the type can be inferred. In the other example, the type + "is specified explicitly. + "The next examples show the direct assigning of values. + "Furthermore, inline declarations can be used to avoid the prior + "declaration of a variable. + + "Declaring data reference variables + DATA dref1 TYPE REF TO i. "Complete type + DATA dref2 TYPE REF TO data. "Generic type + + "Creating anonymous data objects + "Here, no parameters are specified within the parentheses meaning the + "data objects retain their initial values. + dref1 = NEW #( ). + dref2 = NEW string( ). + + IF dref1->* = 0 AND dref2->* = ``. + DATA(val) = `Initial values!`. + ELSE. + val = `No initial values!`. + ENDIF. + + "Directly assigning values within the parentheses. + dref1 = NEW #( 123 ). + dref2 = NEW string( `hallo` ). + + "Inline declaration, explicit type specification + DATA(dref3) = NEW i( 456 ). + + "Another constructor expression specified within the parentheses + DATA tx TYPE string VALUE `world`. + DATA(dref4) = NEW string( `Hello ` && tx && CONV string( '!' ) ). + + DATA dref5 TYPE REF TO string_table. + dref5 = NEW #( VALUE string_table( ( `a` ) ( `b` ) ) ). + + "Structured type; named arguments within the parentheses + DATA(dref6) = NEW zdemo_abap_carr( carrid = 'AA' + carrname = 'American Airlines' ). + + output->display( input = val name = `val` ). + output->display( input = dref1 name = `dref1` ). + output->display( input = dref2 name = `dref2` ). + output->display( input = dref3 name = `dref3` ). + output->display( input = dref4 name = `dref4` ). + output->display( input = dref5 name = `dref5` ). + output->display( input = dref6 name = `dref6` ). + + output->next_section( `3.2 NEW: Creating Instances of Classes` ). + + "The example demonstrates the creation of instances of classes. + "First, an object reference variable is declared with a DATA statement. + "As a next step, an instance of a class is created. The type can be + "inferred here. The class has a constructor method defined. Hence, the + "parentheses contain the parameter binding for the constructor method. + "Here, it is only one parameter. That means the explicit specification + "of the parameter name is not needed and the value can be specified + "directly: oref1 = NEW #( `Hallo` ). + "The next examples show object reference variables that are declared + "inline. Here, the type (i. e. the class name) must be specified + "explicitly. + "The last example shows the method chaining that is possible with + "expressions using the NEW operator. The demo class has a method that + "has a returning parameter. In this case, the parameter of the method + "is of type REF TO i. + + "Creating an object reference variable + DATA oref1 TYPE REF TO local_class. + + "Creating an instance of a class; + "providing parameter bindings for the constructor method + "in the parentheses + oref1 = NEW #( txt = `Hallo` ). + + output->display( input = oref1 name = `oref1` ). + + "Creating an instance of a class, object reference variable + "is declared inline, explicit type specification + DATA(oref2) = NEW local_class( `Salut` ). + + output->display( input = oref2 name = `oref2` ). + + "Method chaining + DATA(result) = NEW local_class( `Ciao` )->double( + int = NEW #( 5 ) ). + + output->display( input = result name = `result` ). + + output->next_section( `4. CONV` ). + + "The examples show the effect of the CONV operator. + "A variable of type i is declared and assigned a value. Then, + "calculations are carried out. The result is stored in a variable + "declared inline. The first result is 0 because the derived type is i. + "The second calculation returns the precise value resulting from the + "division because the CONV + "expression triggers the conversion of the result (decfloat34). + "The next examples demonstrate logical expressions using character-like + "data types. + "A variable is of type abap_bool, i. e. a single character is expected. + "In this case, the variable is initial, i. e. the content is a blank. + "Another variable is of type string. A blank is assigned to this + "variable. + "A logical expression compares the two variables. Without the conversion + "using the CONV operator, the two are not equal due to the comparison + "rules for character-like data types (trailing blanks are not respected + "in variable-length strings). When the string is converted, the + "comparison results to true. + + "Declaring data object and assign value + DATA num TYPE i VALUE 1. + + "Effect of a calculation ... + "... without conversion + DATA(i) = num / 4. + + "... with conversion using an appropriate type + DATA(dec_num) = CONV decfloat34( num / 4 ). + + output->display( input = i name = `i` ). + output->display( input = dec_num name = `dec_num` ). + + "Declaring data objects + DATA(txt) = VALUE abap_bool( ). + + DATA(str) = ` `. + + "Comparing the data objects with and without conversion + output->display( `Without conversion:` ). + + IF txt = str. + output->display( `txt is equal to str.` ). + ELSE. + output->display( `txt is not equal to str.` ). + ENDIF. + + output->display( `With conversion:` ). + + IF txt = CONV abap_bool( str ). + output->display( `txt is equal to converted str.` ). + ELSE. + output->display( `txt is not equal to converted str.` ). + ENDIF. + + output->next_section( `5. EXACT` ). + + "The examples show the effect of the EXACT operator that enforces either + "a lossless assignment or a lossless calculation. + "1) Demonstrates data loss when converting to a data object that expects + "only a single character. + "2) Demonstrates an impossible lossless calculation. A rounding is + "necessary in this case. + "3) The example compares the effect of the EXACT and CONV operator. + + "Example 1 + TRY. + DATA(ex1) = EXACT abap_bool( 'XY' ). + CATCH cx_sy_conversion_data_loss INTO DATA(e1). + DATA(ex2) = e1->value. + DATA(t1) = e1->get_text( ). + ENDTRY. + + "Example 2 + TRY. + DATA(ex3) = EXACT decfloat34( 1 / 3 ). + CATCH cx_sy_conversion_rounding INTO DATA(e2). + DATA(ex4) = e2->value. + DATA(t2) = e2->get_text( ). + ENDTRY. + + "Example 3 + "Comparing the effect of CONV and EXACT + TYPES numtext TYPE n LENGTH 20. + + TRY. + DATA(ex5) = EXACT numtext( '2 Apples + 5 Oranges' ). + CATCH cx_sy_conversion_error INTO DATA(e3). + DATA(t3) = e3->get_text( ). + ENDTRY. + + DATA(conv_comp) = CONV numtext( '2 Apples + 5 Oranges' ). + + IF ex1 IS INITIAL. + output->display( |ex2: { ex2 }; { t1 }| ). + ELSE. + output->display( ex1 ). + ENDIF. + + IF ex3 IS INITIAL. + output->display( |ex4: { ex4 }; { t2 }| ). + ELSE. + output->display( input = ex3 name = `ex3` ). + ENDIF. + + IF ex5 IS INITIAL. + output->display( input = t3 name = `t3` ). + ELSE. + output->display( input = ex5 name = `ex5` ). + ENDIF. + + output->display( input = conv_comp name = `conv_comp` ). + + output->next_section( `6. REF` ). + + "The example includes the declaration of a data object and some data + "reference variables. One data reference variable is typed with a + "complete type, the other one is typed with a generic type. Then, data + "references to the data object declared before are created. Using the # + "symbol means that the type can be automatically derived. + "You can also use inline declarations to omit the explicit declaration. + "Another example shows the explicit specification of the data type after + "REF. + "Furthermore, an object reference is created using inline declaration. + + "Declaring data object + DATA number TYPE i VALUE 5. + + "Declaring data reference variables + DATA dref_a TYPE REF TO i. "Complete type + DATA dref_b TYPE REF TO data. "Generic type + + "Creating data references to data objects + dref_a = REF #( number ). + dref_b = REF #( number ). + + "Data reference variable declared inline + DATA(dref_c) = REF #( number ). + + "Type specified explicitly + DATA(dref_d) = REF string( `hallo` ). + + "Object references + DATA(oref_a) = NEW local_class( `Ciao` ). + DATA(oref_b) = REF #( oref_a ). + + output->display( input = dref_a name = `dref_a` ). + output->display( input = dref_b name = `dref_b` ). + output->display( input = dref_c name = `dref_c` ). + output->display( input = dref_d name = `dref_d` ). + output->display( input = oref_b name = `oref_b` ). + + output->next_section( `7. CAST` ). + + "The example demonstrates the CAST operator in the context of Run Time + "Type Identification (RTTI). + "First, the components of a structure are retrieved. Secondly, the + "method information of a local class is retrieved. The syntax + "particularly shows the advantages of downcasts carried out with the + "CAST operator that also enables method chaining. An example is added + "that demonstrates the use of the older ?= operator with which extra + "variables are needed. Furthermore, simple downcasts are demonstrated + "using data references. + + "RTTI examples + "Using CAST + "Retrieving structure components + DATA(components_s2) = + CAST cl_abap_structdescr( + cl_abap_typedescr=>describe_by_data( s2 ) )->components. + + "Retrieving the methods of a local class + DATA(methods) = + CAST cl_abap_objectdescr( + cl_abap_objectdescr=>describe_by_name( 'LOCAL_CLASS' ) + )->methods. + + "Using ?= + "Retrieving structure components + "Note: More lines of code, helper variables needed + DATA structdescr TYPE REF TO cl_abap_structdescr. + + structdescr ?= cl_abap_typedescr=>describe_by_data( s1 ). + + DATA(components_s1) = structdescr->components. + + "Casting with data references + DATA dref_i TYPE REF TO i. "Complete type + + DATA dref_data TYPE REF TO data. "Generic type + + dref_data = NEW i( 123 ). + + dref_i = CAST #( dref_data ). + + output->display( input = components_s2 name = `components_s2` ). + output->display( input = methods name = `methods` ). + output->display( input = components_s1 name = `components_s1` ). + output->display( input = dref_i name = `dref_i` ). + + output->next_section( `8. COND` ). + + "The example demonstrates the use of the COND operator. The syntax + "includes several WHEN and THEN expressions. + "The example returns a string based on the current time. + + DATA(syst_time) = cl_abap_context_info=>get_system_time( ). + + DATA(greets) = + COND #( WHEN syst_time BETWEEN '050001' AND '120000' + THEN |It's { syst_time TIME = ISO }. | && + |Good morning, { sy-uname }.| + WHEN syst_time BETWEEN '120001' AND '170000' + THEN |It's { syst_time TIME = ISO }. | && + |Good afternoon, { sy-uname }.| + WHEN syst_time BETWEEN '170001' AND '210000' + THEN |It's { syst_time TIME = ISO }. | && + |Good evening, { sy-uname }.| + WHEN syst_time BETWEEN '210001' AND '050000' + THEN |It's { syst_time TIME = ISO }. | && + |Good night, { sy-uname }.| + ELSE |Hallo, { sy-uname }.| + ). + + output->display( input = greets name = `greets` ). + + output->next_section( `9. SWITCH` ). + + "The example demonstrates the use of the SWITCH operator. Here, + "calculations are carried out. For this + "purpose, a string table is created that includes arithmetic operators + "and, purposely, one entry that does not fit. The internal table is looped + "across. Based on the current arithmetic operator, the calculation is + "carried out. A string is displayed accordingly. The code is embedded in + "a TRY ... CATCH ... ENDTRY block to take care of arithmetic errors. For + "example, you might insert the value 0 for the variable num2 to provoke 0 + "division. The CONV operator is also used in the example to respect + "decimals. + + DATA operator_tab TYPE TABLE OF string. + + operator_tab = VALUE #( ( `+` ) ( `-` ) ( `*` ) ( `/` ) ( `#` ) ). + + DATA(num1) = 2. + DATA(num2) = 4. + + LOOP AT operator_tab ASSIGNING FIELD-SYMBOL(). + TRY. + DATA(calc_result) = + SWITCH string( + WHEN '+' THEN CONV decfloat34( num1 + num2 ) + WHEN '-' THEN CONV decfloat34( num1 - num2 ) + WHEN '*' THEN CONV decfloat34( num1 * num2 ) + WHEN '/' THEN CONV decfloat34( num1 / num2 ) + ELSE `That doesn't work.` ). + output->display( |{ num1 } { } { num2 } = { calc_result }| ). + CATCH cx_sy_arithmetic_error INTO DATA(error). + output->display( |Arithmetic error. { error->get_text( ) }| ). + ENDTRY. + ENDLOOP. + + output->next_section( `10. FILTER` ). + + "The examples demonstrate the use of the FILTER operator. + "1) Method to fill internal table with demo values. The tables are + "displayed to show the original values of the internal table that is to + "be filtered. + "2) Filters an internal table based on a condition specified after + "WHERE. A new internal table is created that is declared inline. + "3) A new table is created based on filtering a table. Here, the + "addition EXCEPT is used that excludes data according to a condition + "specified after WHERE. + "4) The filtering is based on entries in a filter table. + + "1) Method to fill demo internal table with values to work with. + "The table is displayed showing the values. + fill_struc_and_tab( ). + + "2) Filtering an internal table based on a condition. + DATA(filter1) = FILTER #( tab4 WHERE comp3 > 5 ). + + "3) Filtering an internal table using EXCEPT addition + DATA(filter2) = FILTER #( tab4 EXCEPT WHERE comp3 < 3 ). + + "4) Filtering an internal table using a filter table. + "Creating and filling a filter table. + DATA extract_tab TYPE SORTED TABLE OF i + WITH NON-UNIQUE KEY table_line. + + extract_tab = VALUE #( ( 3 ) ( 5 ) ). + + DATA(filter3) = FILTER #( tab4 IN extract_tab + WHERE comp3 = table_line ). + + output->display( input = tab4 name = `tab4` ). + output->display( input = filter1 name = `filter1` ). + output->display( input = filter2 name = `filter2` ). + output->display( input = filter3 name = `filter3` ). + + output->next_section( `11. Iteration Expressions with FOR` ). + + "The examples demonstrate iteration expressions with FOR. The examples + "are based on demo internal tables that are filled using a method. The + "tables are displayed to show the original content of the internal + "tables that are to be processed. + + "Method to fill demo internal tables with values to work with. + "Tables are displayed showing the values. + fill_struc_and_tab( ). + + output->display( `11.1 Original table content` ). + output->display( input = tab1 name = `tab1` ). + output->display( input = tab2 name = `tab2` ). + output->display( input = tab3 name = `tab3` ). + + output->next_section( `11.2 FOR ... IN ... (LOOP Semantics)` ). + + "Examples demonstrating FOR ... IN ... that has the semantics of LOOP. + "1) An internal table is looped across. The whole line is stored in a + "new table which is declared inline. The target table must have the + "same table type as the internal table that is looped across. The + "example is without a WHERE condition, i. e. all lines of the source + "table are considered. + "2) A new table is created that has a different table type. However, + "both table types have identically named components. The example shows + "how you can fill all identically named components of the target table. + "The example includes a WHERE condition to restrict the lines to be + "considered. Pay attention to potential type conversions. + "3) A new table is created that has a different table type. Here, it is + "shown that the components and their assignment might be specified + "individually. A WHERE clause is included. Pay attention to potential + "type conversions. + + "1) Storing the whole line in a new table. + "The target table must have the same table type as the source table. + "Without the WHERE condition, all lines are considered. + + TYPES t_type1 LIKE tab3. + + DATA(for1) = VALUE t_type1( FOR wa IN tab3 ( wa ) ). + + "2) Storing specific components having the same names. + "The target type is not compatible to the type of the source table. + "Identically named components exist. + + TYPES t_type2 LIKE tab1. + + DATA(for2) = VALUE t_type2( FOR wa IN tab3 + WHERE ( comp4 > 7 ) + ( CORRESPONDING #( wa ) ) ). + + "3) Specify components individually and providing a mapping + + DATA(for3) = VALUE t_type2( FOR wa IN tab3 + WHERE ( comp4 > 7 ) + ( comp1 = wa-comp1 + comp2 = `hallo` + comp3 = wa-comp4 ) ). + + output->display( input = for1 name = `for1` ). + output->display( input = for2 name = `for2` ). + output->display( input = for3 name = `for3` ). + + "The example demonstrates multiple iteration expressions with FOR. Here, + "a new table is created that is declared inline. Three tables are + "respected. The lines to be considered are restricted by a WHERE + "clause. + "A component name mapping takes care of assigning specific values to + "non-identically named components. + + "Declaring an internal table type + TYPES: BEGIN OF line_type2, + compX TYPE c LENGTH 1, + compY TYPE string, + compZ TYPE i, + END OF line_type2, + t_type3 TYPE STANDARD TABLE OF line_type2 WITH EMPTY KEY. + + "Nested iteration expressions with FOR + DATA(for4) = VALUE t_type3( + FOR wa1 IN tab1 WHERE ( comp1 = 'A' ) + FOR wa2 IN tab2 WHERE ( comp4 > 6 ) + FOR wa3 IN tab3 WHERE ( comp3 < 4 ) + ( compX = wa1-comp1 + compY = wa2-comp1 + compZ = wa3-comp3 ) ). + + output->display( input = for4 name = `for4` ). + + output->next_section( `11.2 FOR ... WHILE/UNTIL ... ` && + `(DO/WHILE Semantics)` ). + + "Examples demonstrating FOR ... WHILE/UNTIL ... that has the semantics + "of DO/WHILE. + "The example demonstrates the construction of internal tables using + "condition iterations with a constructor expression + "and the corresponding variant of the value operator VALUE. Two internal + "tables with different iterations, one using FOR ... WHILE ..., the + "other FOR ... UNTIL ..., are created. + + "Declaring and internal table type + TYPES: + BEGIN OF line_type3, + col1 TYPE i, + col2 TYPE i, + col3 TYPE i, + END OF line_type3, + t_type4 TYPE STANDARD TABLE OF line_type3 WITH EMPTY KEY. + + "FOR ... WHILE ... + DATA(for5) = VALUE t_type4( + FOR x = 11 THEN x + 10 WHILE x < 40 + ( col1 = x col2 = x + 1 col3 = x + 2 ) ). + + "FOR ... UNTIL ... + DATA(for6) = VALUE t_type4( + FOR y = 31 THEN y - 10 UNTIL y < 10 + ( col1 = y col2 = y + 1 col3 = y + 2 ) ). + + output->display( input = for5 name = `for5` ). + output->display( input = for6 name = `for6` ). + + output->next_section( `12. REDUCE` ). + + "The examples demonstrate the REDUCE operator using values contained in + "an internal table column. Here, the table is of type string. + "1) The values of the columns are sequentially concatenated into a + "string. + "INIT ...: A temporary variable is specified that sets an initial + "value for the result variable + "FOR ...: Represents a loop, the loop is carried out for all table + "entries. + "NEXT ...: Represents the assignment to the temporary variable after + "every iteration. + "Once the loop has finished, the target variable is assigned the + "resulting value. + "2) Also here, the table rows are reduced to a text string using a + "chaining after NEXT. The auxiliary variable sep declared after + "INIT is initial for the first read row and is filled with a blank here + "for the evaluation of further rows. + + "Creating and filling a string table + DATA tab TYPE STANDARD TABLE OF string WITH EMPTY KEY. + + tab = VALUE #( ( `h` ) ( `a` ) ( `l` ) ( `l` ) ( `o` ) ). + + "Example 1 + DATA(a_word) = + REDUCE string( INIT text = `` + FOR word IN tab + NEXT text = |{ text }{ word }| ). + + "Example 2 + tab = VALUE #( ( `Some` ) ( `cool` ) ( `stuff` ) + ( `using` ) ( `REDUCE` ) ). + + DATA(sentence) = + REDUCE string( INIT text = `` sep = `` + FOR word IN tab + NEXT text = |{ text }{ sep }{ word }| sep = ` ` ) && '.'. + + output->display( input = a_word name = `a_word` ). + output->display( input = sentence name = `sentence` ). + + "The examples demonstrate summations using the REDUCE operator. + "1) Example using FOR ... UNTIL .... It calculates the total of the + "numbers from 1 to 10. The resulting number is stored in a variable that + "is declared inline. + "2) The example has the same output as the first example. Here, a table + "column is reduced. The table that is of type i is filled with numbers + "from 1 to 10 (using a constructor expression with FOR ... WHILE ...). + "The reduction is then carried out based on the numbers contained in + "this table. + + "Example 1 + DATA(sum1) = REDUCE i( INIT b = 0 + FOR n = 1 UNTIL n > 10 + NEXT b += n ). + + "Example 2 + DATA itab_i TYPE STANDARD TABLE OF i WITH EMPTY KEY. + + itab_i = VALUE #( FOR j = 1 WHILE j <= 10 ( j ) ). + + DATA(sum2) = REDUCE i( INIT x = 0 + FOR z IN itab_i + NEXT x = x + z ). + + output->display( input = sum1 name = `sum1` ). + output->display( input = sum2 name = `sum2` ). + + "The examples demonstrate the concatenation of strings + "1) without the addition THEN + "2) with the addition THEN + "3) in the context of a non-arithmetic expression. + + "1) Concatenation without THEN + DATA(conc1) = REDUCE string( INIT text = `Count up:` + FOR n = 1 UNTIL n > 10 + NEXT text &&= | { n }| ). + + "2) Concatenation with THEN + DATA(conc2) = REDUCE string( INIT text = `Count down:` + FOR n = 10 THEN n - 1 WHILE n > 0 + NEXT text &&= | { n }| ). + + "3) Non-arithmetic expression + DATA(conc3) = REDUCE string( INIT text = `` + FOR t = `x` THEN t && `y` + UNTIL strlen( t ) > 10 + NEXT text &&= |{ t } | ). + + output->display( input = conc1 name = `conc1` ). + output->display( input = conc2 name = `conc2` ). + output->display( input = conc3 name = `conc3` ). + + output->next_section( `13. LET Expressions` ). + + "The examples demonstrate LET expressions in different contexts. + + "1) LET within a constructor expression with VALUE: The temporary + "variable is assigned a value of type string and all lines of the + "resulting table (a table of type string) receive the content of this + "variable in the specified position. + + DATA(str_tab) = VALUE string_table( LET it = `be` IN + ( |To { it } is to do| ) + ( |To { it } or not to { it }| ) + ( |To do is to { it }| ) + ( |Do { it } do { it } do| ) ). + + output->display( input = str_tab name = `str_tab` ). + + "2) LET within a constructor expression with COND: 12 o'clock is + "specified as value for the LET expression. Based on this value, checks + "are carried out and an appropriate result is returned. + + DATA(system_time) = cl_abap_context_info=>get_system_time( ). + + DATA(time) = + COND #( LET tm = '120000' IN + WHEN system_time < tm THEN + |{ system_time TIME = ISO } AM| + WHEN system_time > tm AND system_time < '240000' THEN + |{ CONV t( system_time - 12 * 3600 ) TIME = ISO } PM| + WHEN system_time = tm THEN |High Noon| + ELSE |?| ). + + output->display( input = time name = `time` ). + + "3) An internal table that includes three components is created and + "filled. A loop across this table is carried out. The purpose of the + "constructor expression is to construct a string by concatenating the + "values in the table columns. Here, the LET expression includes a field + "symbol representing the table line. The field symbol receives the + "concatenation result which is then stored in the variable that is + "declared inline. The content is then added to an internal table of type + "string which is then output to show the result of the loop. + + TYPES: + BEGIN OF date, + year TYPE c LENGTH 4, + month TYPE c LENGTH 2, + day TYPE c LENGTH 2, + END OF date, + dates TYPE TABLE OF date WITH EMPTY KEY. + + DATA stringtab TYPE TABLE OF string WITH EMPTY KEY. + + DATA(dates) = VALUE dates( + ( year = '2020' month = '07' day = '16' ) + ( year = '2021' month = '08' day = '31' ) + ( year = '2022' month = '09' day = '07' ) ). + + DO lines( dates ) TIMES. + DATA(isodate) = CONV string( + LET = dates[ sy-index ] + separator = '-' + IN -year && separator && -month && + separator && -day ). + + "Adding line to table + stringtab = VALUE #( BASE stringtab ( isodate ) ). + ENDDO. + + output->display( input = stringtab name = `stringtab` ). + + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_constructor_expr.clas.locals_imp.abap b/src/zcl_demo_abap_constructor_expr.clas.locals_imp.abap new file mode 100644 index 0000000..280d1a9 --- /dev/null +++ b/src/zcl_demo_abap_constructor_expr.clas.locals_imp.abap @@ -0,0 +1,27 @@ +CLASS local_class DEFINITION. + PUBLIC SECTION. + METHODS: constructor IMPORTING txt TYPE string, + double IMPORTING int TYPE + REF TO i RETURNING VALUE(res) TYPE i. + + DATA: timestamp TYPE string, + text TYPE string. + + CLASS-DATA: no_of_instances TYPE i READ-ONLY. +ENDCLASS. + +CLASS local_class IMPLEMENTATION. + METHOD constructor. + "Number of instances of the class are counted. + no_of_instances = no_of_instances + 1. + "Set a time stamp. + DATA: ts TYPE timestampl. + GET TIME STAMP FIELD ts. + timestamp = |{ ts TIMESTAMP = SPACE }|. + + text = |{ txt }, { sy-uname }.|. + ENDMETHOD. + METHOD double. + res = int->* * 2. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_constructor_expr.clas.xml b/src/zcl_demo_abap_constructor_expr.clas.xml new file mode 100644 index 0000000..45764df --- /dev/null +++ b/src/zcl_demo_abap_constructor_expr.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_CONSTRUCTOR_EXPR + E + ABAP cheat sheet: Constructor expressions + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_display.clas.abap b/src/zcl_demo_abap_display.clas.abap new file mode 100644 index 0000000..26ce5e4 --- /dev/null +++ b/src/zcl_demo_abap_display.clas.abap @@ -0,0 +1,201 @@ +*********************************************************************** +* +* Class for ABAP cheat sheet examples +* +* -------------------------- NOTE ------------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** +"!

Class for ABAP cheat sheet examples

+"! The class supports the displaying of output of the ABAP cheat sheet examples. +CLASS zcl_demo_abap_display DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + METHODS: + constructor + IMPORTING + io_out TYPE REF TO if_oo_adt_classrun_out, + display + IMPORTING + input TYPE data + name TYPE string DEFAULT `` + RETURNING + VALUE(output) TYPE string, + next_section + IMPORTING + heading TYPE string. + +protected section. + PRIVATE SECTION. + DATA: + mo_out TYPE REF TO if_oo_adt_classrun_out, + offset TYPE i. + +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_DISPLAY IMPLEMENTATION. + + + METHOD constructor. + mo_out = io_out. + ENDMETHOD. + + + METHOD display. + + "Checking data type + DATA(type_descr) = cl_abap_typedescr=>describe_by_data( input ). + + CASE type_descr->kind. + + WHEN cl_abap_typedescr=>kind_struct. + + DATA(struct_descr) = CAST cl_abap_structdescr( type_descr ). + + "Checking for complex output + IF struct_descr->struct_kind = cl_abap_structdescr=>structkind_nested + OR line_exists( struct_descr->components[ type_kind = cl_abap_typedescr=>typekind_table ] ) + OR line_exists( struct_descr->components[ type_kind = cl_abap_typedescr=>typekind_dref ] ) + OR line_exists( struct_descr->components[ type_kind = cl_abap_typedescr=>typekind_oref ] ). + + DATA(to_be_serialized) = abap_true. + + ELSE. + + DATA(display) = mo_out->get( data = input name = name ). + + ENDIF. + + WHEN cl_abap_typedescr=>kind_table. + + DATA(table_descr) = CAST cl_abap_tabledescr( type_descr ). + + TRY. + DATA(line_type_struct_descr) = CAST cl_abap_structdescr( table_descr->get_table_line_type( ) ). + + "Checking for complex output + IF line_type_struct_descr->struct_kind = cl_abap_structdescr=>structkind_nested + OR line_exists( line_type_struct_descr->components[ type_kind = cl_abap_typedescr=>typekind_table ] ) + OR line_exists( line_type_struct_descr->components[ type_kind = cl_abap_typedescr=>typekind_dref ] ) + OR line_exists( line_type_struct_descr->components[ type_kind = cl_abap_typedescr=>typekind_oref ] ). + + to_be_serialized = abap_true. + + ELSE. + + display = mo_out->get( data = input name = name ). + + ENDIF. + + CATCH cx_sy_move_cast_error. + + to_be_serialized = abap_true. + + ENDTRY. + + WHEN cl_abap_typedescr=>kind_class. + + to_be_serialized = abap_true. + + WHEN cl_abap_typedescr=>kind_intf. + + to_be_serialized = abap_true. + + WHEN cl_abap_typedescr=>kind_elem. + + display = mo_out->get( data = COND string( WHEN name IS INITIAL THEN input ELSE `"` && name && `":` && cl_abap_char_utilities=>newline && input ) ). + + WHEN cl_abap_typedescr=>kind_ref. + + "Checking for data references + IF type_descr->type_kind = cl_abap_typedescr=>typekind_dref. + + "Checking type of dereferenced data object + DATA(type_check_dref) = cl_abap_typedescr=>describe_by_data( input->* ). + + "Processing (non-)elementary types + IF type_check_dref->kind = type_descr->kind_elem. + + display = mo_out->get( data = COND string( WHEN name IS INITIAL THEN input->* ELSE `"` && name && `":` && cl_abap_char_utilities=>newline && input->* ) ). + + ELSE. + + to_be_serialized = abap_true. + + ENDIF. + + ELSE. + to_be_serialized = abap_true. + ENDIF. + + ENDCASE. + + "Processing complex output by serializiation + FIND SUBSTRING `Data type not yet supported ...` IN display MATCH OFFSET DATA(off) MATCH LENGTH DATA(len). + + IF sy-subrc = 0 OR to_be_serialized = abap_true. + + "ABAP JSON serializing + DATA(json) = /ui2/cl_json=>serialize( data = input + pretty_name = /ui2/cl_json=>pretty_mode-low_case + compress = abap_false + hex_as_base64 = abap_false + format_output = abap_true + assoc_arrays = abap_true + assoc_arrays_opt = abap_true ). + + IF to_be_serialized = abap_true. + + IF name IS INITIAL. + REPLACE PCRE `^` IN display WITH json && cl_abap_char_utilities=>newline. + ELSE. + REPLACE PCRE `^` IN display WITH `"` && name && `":` && cl_abap_char_utilities=>newline && json && cl_abap_char_utilities=>newline. + + ENDIF. + + "substring found + ELSE. + IF name IS INITIAL. + REPLACE SECTION OFFSET off LENGTH len OF display WITH json && cl_abap_char_utilities=>newline. + ELSE. + REPLACE SECTION OFFSET off LENGTH len OF display WITH `"` && name && `":` && cl_abap_char_utilities=>newline && json && cl_abap_char_utilities=>newline. + ENDIF. + + ENDIF. + + mo_out->write( display ). + + ELSE. + + mo_out->write( display ). + + ENDIF. + + ENDMETHOD. + + + METHOD next_section. + + mo_out->write( `_________________________________________________________________________________` + && cl_abap_char_utilities=>newline + && cl_abap_char_utilities=>newline + && heading + && cl_abap_char_utilities=>newline ). + + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_display.clas.xml b/src/zcl_demo_abap_display.clas.xml new file mode 100644 index 0000000..32954f8 --- /dev/null +++ b/src/zcl_demo_abap_display.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_DISPLAY + E + Class for ABAP cheat sheet examples + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_dynamic_prog.clas.abap b/src/zcl_demo_abap_dynamic_prog.clas.abap new file mode 100644 index 0000000..4bacc94 --- /dev/null +++ b/src/zcl_demo_abap_dynamic_prog.clas.abap @@ -0,0 +1,1211 @@ +*********************************************************************** +* +* ABAP cheat sheet: Dynamic programming +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntactical options and concepts related +* to dynamic programming as outlined in the respective ABAP cheat sheet. +* - Topics covered: field symbols and data references (both as supporting +* elements for dynamic programming), dynamic ABAP syntax components, +* runtime type services (RTTS), i. e. runtime type identification (RTTI) +* and runtime type creation (RTTC) +* - 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 +* the content that is actually used in the end is completely random. +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP Development Tools (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, check the notes +* included in the class as comments and refer to the ABAP Keyword +* Documentation. +* - Due to the amount of output in the console, the examples include +* numbers (e. g. 1) ..., 2) ..., 3) ...) for the individual example +* sections. Plus, the variable name is displayed in most cases. Hence, +* to easier and faster find the relevant output in the console, just +* search in the console for the number/variable name (STRG+F in the +* console) or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** +"!

ABAP cheat sheet: Dynamic programming

+"! Example to demonstrate concepts related to dynamic programming.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_dynamic_prog DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + + CLASS-METHODS: + class_constructor. + +protected section. + PRIVATE SECTION. + +ENDCLASS. + + + +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( `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 + DATA: str TYPE string. + + TYPES: BEGIN OF struc, "Structured data type + num1 TYPE i, + num2 TYPE i, + END OF struc, + tab_type TYPE TABLE OF struc. "Internal table type + + "Complete types + FIELD-SYMBOLS: TYPE i, + TYPE zdemo_abap_flsch, + TYPE LINE OF tab_type, + LIKE str. + + "Generic types + FIELD-SYMBOLS TYPE csequence. + FIELD-SYMBOLS TYPE data. + FIELD-SYMBOLS TYPE any. + FIELD-SYMBOLS TYPE ANY TABLE. + + output->display( `No output for this section.` ). + + 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. + "You can also assign a particular component of a structure. Either you + "specify the position of the component or the name of the component. + + "Data objects. + DATA: num_a TYPE i, + struc_a TYPE zdemo_abap_fli, + tab_a TYPE string_table. + + "Field symbols with complete types + FIELD-SYMBOLS: TYPE i, + TYPE zdemo_abap_fli, + TYPE string_table. + + "Field symbols with complete types + FIELD-SYMBOLS TYPE data. + FIELD-SYMBOLS TYPE ANY TABLE. + + "Assigning data objects to field symbols + ASSIGN num_a TO . + ASSIGN struc_a TO . + ASSIGN tab_a TO . + ASSIGN: num_a TO , + struc_a TO , + tab_a TO , + tab_a TO . + + "Assigning structure components to field symbols + ASSIGN COMPONENT 2 OF STRUCTURE struc_a TO . + ASSIGN COMPONENT 'CONNID' OF STRUCTURE struc_a TO . + + output->display( `No output for this section.` ). + + output->next_section( `3) Checking Field Symbol Assignment` ). + + "When working with field symbols, you should make sure that they are + "assigned. Otherwise, a runtime error occurs. + "You can make use of a logical expression with IS [NOT] ASSIGNED. + "The example includes data object declarations. One data object is + "assigned, the other is not. Consequently, the expression is + "true for the one and false for the other. + + DATA num_b TYPE i VALUE 123. + + FIELD-SYMBOLS: TYPE i, + TYPE string. + + ASSIGN num_b TO . + + IF IS ASSIGNED. + output->display( `Field symbol is assigned.` ). + ELSE. + output->display( `Field symbol not assigned.` ). + ENDIF. + + IF IS ASSIGNED. + output->display( `Field symbol is assigned.` ). + ELSE. + 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. + + DATA num_c TYPE i VALUE 123. + + FIELD-SYMBOLS: TYPE i. + + ASSIGN num_c TO . + + IF IS ASSIGNED. + output->display( `1. Field symbol is assigned.` ). + ELSE. + output->display( `1. Field symbol is not assigned.` ). + ENDIF. + + UNASSIGN . + + IF IS ASSIGNED. + output->display( `2. Field symbol is assigned.` ). + ELSE. + 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. + "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 + "of explicit casting, the field symbol is typed with a generic type. + "Here, just having CASTING would not be sufficient. + "As a result, the type that only accepts 3 characters is respected. + + TYPES c_len_3 TYPE c LENGTH 3. + + DATA(chars) = 'abcdefg'. + + FIELD-SYMBOLS TYPE c_len_3. + + ASSIGN chars TO CASTING. "Implicit casting + + FIELD-SYMBOLS TYPE data. + + ASSIGN chars TO CASTING TYPE c_len_3. "Explicit casting + + output->display( input = name = `` ). + output->display( input = name = `` ). + + output->next_section( `6) Accessing 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 + "occasions. Among them: Changing the value of data objects assigned to + "field symbols, the use of field symbols in expressions, structures, and + "internal tables. + + DATA: num_e TYPE i VALUE 456, + struc_e TYPE zdemo_abap_carr, + tab_e TYPE TABLE OF zdemo_abap_carr WITH EMPTY KEY. + + SELECT SINGLE * + FROM zdemo_abap_carr + WHERE carrid = 'LH' + INTO @struc_e. + + FIELD-SYMBOLS: TYPE i, + TYPE zdemo_abap_carr, + LIKE tab_e, + TYPE ANY TABLE. + + "Without an assignment, this would result in a runtime error: + " = 1. + + ASSIGN num_e TO . + ASSIGN struc_e TO . + ASSIGN tab_e TO . + ASSIGN tab_e TO . + + "Change values + = 789. + + output->display( input = name = `` ). + output->display( input = num_e name = `num_e` ). + + "Use in expressions + DATA(calc_e) = + 211. + + output->display( input = calc_e name = `calc_e` ). + + IF < 1000. + output->display( `The value of is less than 1000` ). + ELSE. + output->display( `The value of is greater than 1000` ). + ENDIF. + + "Structure + output->display( input = name = `` ). + + DATA(comp_e1) = -carrid. + + output->display( input = comp_e1 name = `comp_e1` ). + + -url = 'www.lh.com'. + + output->display( input = -url name = `-url` ). + + "Internal table + SELECT * + FROM zdemo_abap_carr + ORDER BY carrid + INTO TABLE @ + UP TO 3 ROWS. + + output->display( input = name = `` ). + + TRY. + DATA(comp_e2) = [ 2 ]-carrname. + output->display( input = comp_e2 name = `comp_e2` ). + CATCH cx_sy_itab_line_not_found INTO DATA(error_e). + ENDTRY. + + "Note: Declarations not possible. + SELECT * + FROM zdemo_abap_carr + ORDER BY carrid + INTO TABLE @ + UP TO 3 ROWS. + + 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. + "The example includes multiple loops. First, internal tables are + "declared. One of them is filled. Then, field symbols are declared to + "which data objects are assigned. In the first loop, a previously + "declared field symbol is used as target area to hold the table line + "that + "is processed. In the course of the loop, some values are changed. The + "components are accessed using the component selector '-'. At the end of + "the loop, another internal table is filled using the currently + "processed line for the second loop. The second loop (the loop is + "carried out based on + "an internal table a field symbol points to) uses a directly declared + "field symbol using ASSIGNING FIELD-SYMBOL(<...>). Also here, some + "values are changed and another internal table is filled. This table + "is of a generic type. At the end, this internal table is output, too. + + DATA: tab_f1 TYPE TABLE OF zdemo_abap_fli WITH EMPTY KEY, + tab_f2 LIKE tab_f1, + tab_f3 LIKE tab_f1. + + SELECT * + FROM zdemo_abap_fli + ORDER BY carrid + INTO TABLE @tab_f1 + UP TO 3 ROWS. + + FIELD-SYMBOLS: LIKE LINE OF tab_f1, + LIKE tab_f1, + TYPE ANY TABLE. + + ASSIGN tab_f2 TO . + ASSIGN tab_f3 TO . + + LOOP AT tab_f1 ASSIGNING . + -connid = '99'. + -fldate = cl_abap_context_info=>get_system_date( ). + -price = -price + 100. + -currency = 'EUR'. + CLEAR: -paymentsum, + -seatsocc, + -seatsocc_b, + -seatsocc_f. + + "Another itab is filled. + = VALUE #( BASE ( ) ). + ENDLOOP. + + output->display( input = tab_f1 ). + + "Regrding 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. + = 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` ). + + "In this example, all components of a structure are processed using + "field symbols and an ASSIGN COMPONENT ... OF STRUCTURE ... statement. + "First, a field symbol is declared with a generic type. A structure is + "filled with values from a demo table. The structure is assigned to the + "field symbol. Using a DO loop, all components are processed. The + "sy-index value represents the position of the component in the + "structure. Once all components have been processed (i. e. if sy-subrc + "does not return '0' for a sy-index value), the loop is exited. The output + "shows all components and their values. + + FIELD-SYMBOLS: TYPE data. + + DATA: comp_tab TYPE string_table. + + SELECT SINGLE carrid, carrname, currcode, url + FROM zdemo_abap_carr + WHERE carrid = 'LH' + INTO @DATA(struct). + + FIELD-SYMBOLS: TYPE data. + + ASSIGN struct TO . + + DO. + "sy-index represents the position of a structure component + ASSIGN COMPONENT sy-index OF STRUCTURE TO . + + IF sy-subrc <> 0. + "If all components are processed, the loop is exited. + EXIT. + ELSE. + output->display( |sy-index: { sy-index }, component content:| ). + output->display( ). + ENDIF. + + ENDDO. + + output->next_section( `Data references` ). + output->display( `9) Declaring Data References` ). + + "Like field symbols, data reference variables can be declared with both + "a complete and a generic data type using DATA statements and the + "addition REF TO. The type after REF TO represents the static data type. + "The example shows multiple ways of declaring a data reference variable + "using both complete and generic data types. + + DATA: some_string TYPE string. + + TYPES: ref_type TYPE REF TO zdemo_abap_flsch. + + DATA: ref_a1 TYPE REF TO i, "Complete data type + ref_a2 TYPE REF TO zdemo_abap_carr, "Complete data type + ref_a3 LIKE REF TO some_string, + ref_a4 LIKE ref_a1, + ref_a5 TYPE ref_type, + ref_a6 TYPE REF TO data. "Generic data type + + output->display( `No output for this section.` ). + + 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 + "the data type after REF before the parenthesis. Within the parentheses, + "you can provide a value. + + "Declaring data object + DATA number_b TYPE i VALUE 5. + + "Declaring data reference variables + DATA ref_b1 TYPE REF TO i. + DATA ref_data_b TYPE REF TO data. + + "Creating data references to data objects. + "The '#' sign means that the type is derived from the data object. + ref_b1 = REF #( number_b ). + ref_data_b = REF #( number_b ). + + "You can also use inline declarations to omit the explicit declaration. + DATA(ref_b2) = REF #( number_b ). + + "You can explicitly specify the data type after REF. + DATA(ref_b3) = REF 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.` ). + + output->next_section( `11) Creating New Data Objects at Runtime` ). + + "You create a so-called anonymous data object at runtime by putting the + "reference into the variable and providing the desired type. Use the + "instance operator NEW. The older syntax CREATE DATA has the same effect + "as using the newer instance operator. + "The example includes various anonymous data objects. Note: To output + "the content of the data reference variables, they must be dereferenced + "first. The details are shown further down. + + "Declaring data reference variables + DATA ref_c1 TYPE REF TO i. "Complete type + DATA ref_data_c TYPE REF TO data. "Generic type + + "Creating anonymous data objects + "Using the '#' sign and the explicit type: see REF #( ) above. + ref_c1 = NEW #( ). + ref_data_c = NEW string( ). + + "For directly assigning values, insert the values within the parentheses. + ref_c1 = NEW #( 123 ). + + "Using inline declarations to omit a prior declaration of a variable. + DATA(ref_c2) = NEW i( 456 ). + + "Internal table type + TYPES i_table TYPE STANDARD TABLE OF i WITH EMPTY KEY. + + "Filling internal table + DATA(ref_c3) = NEW i_table( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 5 ) ). + + "Older syntax + DATA ref_c4 TYPE REF TO string. + DATA ref_c5 TYPE REF TO data. + + CREATE DATA ref_c4. + CREATE DATA ref_c5 TYPE p LENGTH 6 DECIMALS 2. + CREATE DATA ref_c5 LIKE ref_c4. + + output->display( input = ref_c1->* name = `ref_c1->*` ). + 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 + "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 + "source variable is of generic type. The assignment is done with casting + "operators, either with the constructor operator CAST or the older '?='. + + "Declaring data reference variables + DATA ref_d1 TYPE REF TO i. + DATA ref_d2 TYPE REF TO i. + + ref_d1 = NEW #( 789 ). + + ref_d2 = ref_d1. "Copying data reference + + "Casting + DATA(ref_d3) = NEW i( 321 ). "Complete type + + DATA ref_data_d1 TYPE REF TO data. "Generic type + + ref_data_d1 = ref_d3. "Upcast + + "Downcasts + DATA ref_d5 TYPE REF TO i. + + DATA ref_data_d2 TYPE REF TO data. "Generic type + + ref_data_d2 = NEW i( 654 ). + + ref_d5 = CAST #( ref_data_d2 ). + + ref_d5 ?= ref_data_d2. + + output->display( input = ref_d2->* name = `ref_d2->*` ). + 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. + "The example includes multiple occasions in which data reference are + "accessed: Changing the content of a referenced data object, the use in + "logical expressions, structures, and internal tables. + + "Creating data reference variables and assigning values + DATA(ref_e1) = NEW i( 1 ). + DATA(ref_e2) = NEW zdemo_abap_carr( carrid = 'LH' + carrname = 'Lufthansa' ). + + "Generic type + DATA ref_data_e TYPE REF TO data. + + "Copying reference + ref_data_e = ref_e1. + + "Accessing + "Variable receives the content. + DATA(some_num) = ref_e1->*. + + output->display( input = ref_e1->* name = `ref_e1->*` ). + + "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. + IF ref_e1->* > 5. + output->display( `The value of ref_e1 is greater than 5.` ). + ELSE. + output->display( `The value of ref_e1 is lower than 5.` ). + ENDIF. + + "Dereferenced generic type + DATA(calc) = 1 + ref_data_e->*. + + output->display( input = calc name = `calc` ). + + "Complete structure + DATA(struc) = ref_e2->*. + + output->display( input = ref_e2->* name = `ref_e2->*` ). + + "Individual structure component + DATA(carrid) = ref_e2->carrid. + + ref_e2->carrid = 'UA'. + + output->display( input = ref_e2->carrid name = `ref_e2->carrid` ). + + "This syntax also works but it's less comfortable. + ref_e2->*-carrname = 'United Airlines'. + + output->display( input = ref_e2->*-carrname name = `ref_e2->*-carrname` ). + + output->next_section( `14) Checking if Data Reference ` && + `Can Be Dereferenced` ). + + "You can check if a data reference can be dereferenced by using + "a logical expression with IS [NOT] BOUND. + "The example shows both a data reference that is bound and not bound. + + DATA(ref_f1) = NEW string( `hallo` ). + DATA ref_f2 TYPE REF TO i. + + IF ref_f1 IS BOUND. + output->display( `ref_f1 is bound.` ). + ELSE. + output->display( `ref_f1 is not bound.` ). + ENDIF. + + IF ref_f2 IS BOUND. + output->display( `ref_f2 is bound.` ). + ELSE. + 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. + + DATA(ref_g1) = NEW string( `hallo` ). + + IF ref_g1 IS INITIAL. + output->display( `Before CLEAR: ref_g1 is initial.` ). + ELSE. + output->display( `Before CLEAR: ref_g1 is not intial.` ). + ENDIF. + + IF ref_g1 IS BOUND. + output->display( `Before CLEAR: ref_g1 is bound.` ). + ELSE. + output->display( `Before CLEAR: ref_g1 is not bound.` ). + ENDIF. + + CLEAR ref_g1. + + IF ref_g1 IS INITIAL. + output->display( `After CLEAR: ref_g1 is initial.` ). + ELSE. + output->display( `After CLEAR: ref_g1 is not initial.` ). + ENDIF. + + IF ref_g1 IS BOUND. + output->display( `After CLEAR: ref_g1 is bound.` ). + ELSE. + 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. + + DATA ref_h1 TYPE REF TO data. + + ref_h1 = NEW i( 111 ). + + output->display( input = ref_h1->* name = `ref_h1->*` ). + + ref_h1 = NEW string( `ref_h1 overwritten.` ). + + 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. + "The example demonstrates that three data references are created with + "the same reference variable in the course of a DO loop. There, the data + "reference is overwritten. However, due to saving the data reference + "variable in an internal table, the content is preserved. + + DATA: ref_data_i TYPE REF TO data, + itab_i TYPE TABLE OF REF TO data, + number_i TYPE i VALUE 0. + + DO 3 TIMES. + 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. + ref_data_i = NEW i( number_i ). + "Adds the reference to internal table. + itab_i = VALUE #( BASE itab_i ( ref_data_i ) ). + ENDDO. + + output->display( input = itab_i name = `itab_i` ). + output->display( input = `The derefenced value of the data reference - which ` && + `was changed in the course of the loop - in the second table ` && + `entry is ` && itab_i[ 2 ]->* && `.` ). + + 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. + "In the example, an internal table is created including values of + "database table. A data reference variable is declared as target area of + "the loop. In the course of the loop, some values are changed. + + SELECT * + FROM zdemo_abap_flsch + WHERE distid = 'MI' + ORDER BY carrid + INTO TABLE @DATA(flsch_tab) + UP TO 3 ROWS. + + "The table line is written into a data reference variable + "that is declared inline. + LOOP AT flsch_tab REFERENCE INTO DATA(ref_k). + ref_k->connid = '123'. + ref_k->distance = ref_k->distance * '1.609344'. + ref_k->distid = 'KM' . + ENDLOOP. + + output->display( input = flsch_tab name = `flsch_tab` ). + + output->next_section( `19) Data References as Part of ` && + `Structures and Internal Tables` ). + + "In contrast to field symbols, data reference variables can be used as + "components of structures or columns in internal tables. + "The example includes the declaration of a structure that contains a + "data reference variable. + "The structure is filled. Based on this structure, an + "internal table is created. In a DO loop, the internal table is filled. + + "Declaring a structure + DATA: BEGIN OF struc_l, + number_l TYPE i, + ref_l TYPE REF TO i, + END OF struc_l, + some_int TYPE i VALUE 0. + + "Filling structure + struc_l = VALUE #( number_l = 1 ref_l = NEW #( 2 ) ). + + output->display( input = struc_l ). + + "Declaring an internal table + DATA itab_l LIKE TABLE OF struc_l WITH EMPTY KEY. + + "Filling internal table. + DO 3 TIMES. + some_int += 1. + + "Filling structure + struc_l = VALUE #( number_l = some_int + ref_l = NEW #( some_int ) ). + + "Filling internal table + itab_l = VALUE #( BASE itab_l ( struc_l ) ). + ENDDO. + + output->display( input = itab_l name = `itab_l` ). + + output->next_section( `Dynamic ABAP Syntax Components` ). + output->display( `20) Assignment of Dynamically ` && + `Determined Data Objects to Field Symbols` ). + + "A dynamically determined data object is assigned to a field symbol. + "In this case, data objects that are declared in the public section of + "the local demo class are assigned to a field symbol. + + DATA(dobj_name) = lcl_det_at_runtime=>get_dyn_dobj( ). + + ASSIGN lcl_det_at_runtime=>(dobj_name) TO FIELD-SYMBOL(). + + 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` ). + + "A field is determined at runtime on whose basis a sorting is done on an + "internal table. + + DATA(field_name) = lcl_det_at_runtime=>get_dyn_field( ). + + SELECT * + FROM zdemo_abap_carr + ORDER BY carrid + INTO TABLE @DATA(carr_itab) + UP TO 3 ROWS. + + SORT carr_itab BY (field_name). + + output->display( |Field name determined at runtime | && + |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` ). + + "In the example, the SELECT list that is used in a SELECT statement is + "determined at runtime. + + DATA(select_list) = lcl_det_at_runtime=>get_dyn_select_list( ). + + DATA sel_table TYPE TABLE OF zdemo_abap_flsch WITH EMPTY KEY. + + SELECT (select_list) + FROM zdemo_abap_flsch + ORDER BY carrid + INTO CORRESPONDING FIELDS OF TABLE @sel_table + UP TO 3 ROWS. + + output->display( |SELECT list determined at runtime: { select_list } | ). + output->display( input = sel_table name = `sel_table` ). + + output->next_section( `24) FROM Clause` ). + + "In the example, the FROM clause that is used in a SELECT statement is + "determined at runtime. Here, the number of entries of a database table + "is counted. + + DATA(tab_name) = lcl_det_at_runtime=>get_dyn_table_name( ). + + SELECT COUNT(*) + FROM (tab_name) + INTO @DATA(count). + + output->display( |Table name determined at runtime: { tab_name } | ). + output->display( |The table { tab_name } has { count } entries.| ). + + output->next_section( `25) WHERE Clause` ). + + "In the example, the WHERE clause that is used in a SELECT statement is + "determined at runtime. Here, the WHERE clause is based on a string + "table. + + DATA(where_clause) = lcl_det_at_runtime=>get_dyn_where_clause( ). + + SELECT * + FROM zdemo_abap_flsch + WHERE (where_clause) + ORDER BY carrid + INTO TABLE @DATA(where_tab) + UP TO 5 ROWS. + + output->display( |WHERE clause determined at runtime:| ). + 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. + + DATA(class) = lcl_det_at_runtime=>get_dyn_class( ). + + "Data declarations for parameter table + DATA: parameter TYPE abap_parmbind, + parameters TYPE abap_parmbind_tab. + + "Getting methods using RTTI + DATA(methods) = CAST cl_abap_objectdescr( + cl_abap_objectdescr=>describe_by_name( class ) + )->methods. + + "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`. + + "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( ). + + "Retrieving method name + DATA(meth) = to_upper( methods[ idx ]-name ). + + "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. + + "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 ). + + "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 ). + + "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. + + CREATE DATA t TYPE (param->absolute_name). + + "Getting the reference of dereferenced anonymous data object + "into the value field. + parameter-value = REF #( t->* ). + + "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->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. + + 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 + 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 ). + + "When referring to a concrete data object, you can use this method: + "DATA(some_type) = cl_abap_typedescr=>describe_by_data( ... ). + + 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` ). + + "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` ). + + "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` ). + + "Get referenced type + WHEN cl_abap_typedescr=>kind_ref. + DATA(ref_descr) = CAST cl_abap_refdescr( some_type ). + output->display( input = ref_descr->get_referenced_type( ) name = `ref_descr->get_referenced_type( )` ). + + "Get class methods + WHEN cl_abap_typedescr=>kind_class. + DATA(class_desc) = CAST cl_abap_classdescr( some_type ). + output->display( input = class_desc->methods name = `class_desc->methods` ). + + "Get interface methods + WHEN cl_abap_typedescr=>kind_intf. + DATA(if_descr) = CAST cl_abap_intfdescr( some_type ). + output->display( input = if_descr->methods name = `class_desc->methods` ). + + WHEN OTHERS. + output->display( `Others ...` ). + ENDCASE. + + output->next_section( `28) RTTC: Creating Data Type and Data ` && + `Object Based on this Type at Runtime` ). + + "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. + + "Getting the line type of a DDIC table + DATA(line_type) = CAST cl_abap_structdescr( + cl_abap_tabledescr=>describe_by_name( `ZDEMO_ABAP_CARR` ) ). + + "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 + p_table_kind = cl_abap_tabledescr=>tablekind_sorted + p_unique = cl_abap_typedescr=>true + p_key = key_tab ). + + "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. + + "Filling an internal table + SELECT * + FROM zdemo_abap_carr + ORDER BY carrid + INTO TABLE @ref_tab->* + UP TO 3 ROWS. + + 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 = 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. diff --git a/src/zcl_demo_abap_dynamic_prog.clas.locals_imp.abap b/src/zcl_demo_abap_dynamic_prog.clas.locals_imp.abap new file mode 100644 index 0000000..d2be2d3 --- /dev/null +++ b/src/zcl_demo_abap_dynamic_prog.clas.locals_imp.abap @@ -0,0 +1,228 @@ +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. + + 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 + + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + +CLASS lcl_det_at_runtime IMPLEMENTATION. + METHOD get_dyn_table_name. + + "Providing DDIC table names in a string table to be selected from. + DATA(flight_tables) = VALUE string_table( + ( `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( + seed = cl_abap_random=>seed( ) min = 1 + max = lines( flight_tables ) ). + DATA(idx) = random->get_next( ). + + "Returning parameter to receive the random table name. + TRY. + tab = flight_tables[ idx ]. + CATCH cx_sy_itab_line_not_found INTO DATA(error). + ENDTRY. + + ENDMETHOD. + + METHOD get_dyn_dobj. + + "Providing strings with demo content. + string1 = |Hallo, { sy-uname }. | && + |This is string1.|. + string2 = |Hallo, { sy-uname }. | && + |This is string2.|. + string3 = |Hallo, { sy-uname }. | && + |This is string3.|. + + "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( + seed = cl_abap_random=>seed( ) min = 1 + max = lines( str_tab ) ). + DATA(idx) = random->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. + + ENDMETHOD. + + METHOD get_dyn_field. + + "Getting list of components of DDIC type zdemo_abap_carr + DATA(comp) = CAST cl_abap_structdescr( + cl_abap_typedescr=>describe_by_name( + '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( + seed = cl_abap_random=>seed( ) min = 2 + max = lines( comp ) ). + DATA(idx) = random->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. + + ENDMETHOD. + + METHOD get_dyn_select_list. + + "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` ) + ( `CARRID, CONNID, CITYFROM, CITYTO` ) + ( `CARRID, CONNID, AIRPFROM, AIRPTO` ) + ( `CARRID, CONNID, AIRPFROM, AIRPTO, ` && + `FLTIME, DEPTIME, ARRTIME, DISTANCE` ) + ). + + "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( sel_list_tab ) ). + DATA(idx) = random->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. + + ENDMETHOD. + + METHOD get_dyn_where_clause. + + "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. + + DATA where_itab LIKE TABLE OF where_struc WITH EMPTY KEY. + + where_itab = VALUE #( + ( where_clause_tab = VALUE #( ( `CARRID = 'LH'` ) + ( `OR CARRID = 'AA'` ) ) ) + ( where_clause_tab = VALUE #( ( `CONNID BETWEEN 0 AND 300` ) ) ) + ( where_clause_tab = VALUE #( ( `CITYFROM LIKE '%FRA%'` ) ) ) + ( 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( + seed = cl_abap_random=>seed( ) min = 1 + max = lines( where_itab ) ). + DATA(idx) = random->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. + + 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. + 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` ) + ( `IF_OO_ADT_CLASSRUN` ) ). + + "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( str_tab ) ). + DATA(idx) = random->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. + + ENDMETHOD. + +ENDCLASS. + +CLASS lcl_dummy DEFINITION. + + 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. +ENDCLASS. + +CLASS lcl_dummy IMPLEMENTATION. + METHOD meth_a. + str = |Hallo from meth_a.|. + ENDMETHOD. + + METHOD meth_b. + str = |Hallo from meth_b.|. + ENDMETHOD. + +ENDCLASS. diff --git a/src/zcl_demo_abap_dynamic_prog.clas.xml b/src/zcl_demo_abap_dynamic_prog.clas.xml new file mode 100644 index 0000000..397ccf8 --- /dev/null +++ b/src/zcl_demo_abap_dynamic_prog.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_DYNAMIC_PROG + E + ABAP cheat sheet: Dynamic programming + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_flight_tables.clas.abap b/src/zcl_demo_abap_flight_tables.clas.abap new file mode 100644 index 0000000..2b5c41d --- /dev/null +++ b/src/zcl_demo_abap_flight_tables.clas.abap @@ -0,0 +1,711 @@ +*********************************************************************** +* +* Class for clearing and filling demo database tables used in the +* context of ABAP cheat sheets +* +* -------------------------- NOTE ------------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** +"!

Class for ABAP cheat sheet examples

+"! The class is meant to clear and fill demo database tables used in the context of ABAP cheat sheet examples. +"! The demo database tables contain airline and flight information. +CLASS zcl_demo_abap_flight_tables DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + CLASS-METHODS: clear_dbtabs, + fill_dbtabs. + +protected section. +private section. +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_FLIGHT_TABLES IMPLEMENTATION. + + + METHOD clear_dbtabs. + DELETE FROM zdemo_abap_flsch. + DELETE FROM zdemo_abap_carr. + DELETE FROM zdemo_abap_fli. + ENDMETHOD. + + + METHOD fill_dbtabs. + + "Clearing db tables before filling + clear_dbtabs( ). + + "Filling db table + MODIFY zdemo_abap_flsch FROM TABLE @( VALUE #( + ( carrid = 'AA' + connid = 0017 + countryfr = 'US' + cityfrom = 'NEW YORK' + airpfrom = 'JFK' + countryto = 'US' + cityto = 'SAN FRANCISCO' + airpto = 'SFO' + fltime = 361 + deptime = '110000' + arrtime = '140100' + distance = 2572 + distid = 'MI' + fltype = '' + period = 0 ) + ( carrid = 'AA' + connid = 0064 + countryfr = 'US' + cityfrom = 'SAN FRANCISCO' + airpfrom = 'SFO' + countryto = 'US' + cityto = 'NEW YORK' + airpto = 'JFK' + fltime = 321 + deptime = '090000' + arrtime = '172100' + distance = 2572 + distid = 'MI' + fltype = '' + period = 0 ) + ( carrid = 'AZ' + connid = 0555 + countryfr = 'IT' + cityfrom = 'ROME' + airpfrom = 'FCO' + countryto = 'DE' + cityto = 'FRANKFURT' + airpto = 'FRA' + fltime = 125 + deptime = '190000' + arrtime = '210500' + distance = 845 + distid = 'MI' + fltype = '' + period = 0 ) + ( carrid = 'AZ' + connid = 0788 + countryfr = 'IT' + cityfrom = 'ROME' + airpfrom = 'FCO' + countryto = 'JP' + cityto = 'TOKYO' + airpto = 'TYO' + fltime = 775 + deptime = '120000' + arrtime = '085500' + distance = 6130 + distid = 'MI' + fltype = '' + period = 1 ) + ( carrid = 'AZ' + connid = 0789 + countryfr = 'JP' + cityfrom = 'TOKYO' + airpfrom = 'TYO' + countryto = 'IT' + cityto = 'ROME' + airpto = 'FCO' + fltime = 940 + deptime = '114500' + arrtime = '192500' + distance = 6130 + distid = 'MI' + fltype = '' + period = 0 ) + ( carrid = 'AZ' + connid = 0790 + countryfr = 'IT' + cityfrom = 'ROME' + airpfrom = 'FCO' + countryto = 'JP' + cityto = 'OSAKA' + airpto = 'KIX' + fltime = 815 + deptime = '103500' + arrtime = '081000' + distance = 6030 + distid = 'MI' + fltype = 'X' + period = 1 ) + ( carrid = 'DL' + connid = 0106 + countryfr = 'US' + cityfrom = 'NEW YORK' + airpfrom = 'JFK' + countryto = 'DE' + cityto = 'FRANKFURT' + airpto = 'FRA' + fltime = 475 + deptime = '193500' + arrtime = '093000' + distance = 3851 + distid = 'MI' + fltype = '' + period = 1 ) + ( carrid = 'DL' + connid = 1699 + countryfr = 'US' + cityfrom = 'NEW YORK' + airpfrom = 'JFK' + countryto = 'US' + cityto = 'SAN FRANCISCO' + airpto = 'SFO' + fltime = 382 + deptime = '171500' + arrtime = '203700' + distance = 2572 + distid = 'MI' + fltype = '' + period = 0 ) + ( carrid = 'DL' + connid = 1984 + countryfr = 'US' + cityfrom = 'SAN FRANCISCO' + airpfrom = 'SFO' + countryto = 'US' + cityto = 'NEW YORK' + airpto = 'JFK' + fltime = 325 + deptime = '100000' + arrtime = '182500' + distance = 2572 + distid = 'MI' + fltype = '' + period = 0 ) + ( carrid = 'JL' + connid = 0407 + countryfr = 'JP' + cityfrom = 'TOKYO' + airpfrom = 'NRT' + countryto = 'DE' + cityto = 'FRANKFURT' + airpto = 'FRA' + fltime = 725 + deptime = '133000' + arrtime = '173500' + distance = 9100 + distid = 'KM' + fltype = '' + period = 0 ) + ( carrid = 'JL' + connid = 0408 + countryfr = 'DE' + cityfrom = 'FRANKFURT' + airpfrom = 'FRA' + countryto = 'JP' + cityto = 'TOKYO' + airpto = 'NRT' + fltime = 675 + deptime = '202500' + arrtime = '154000' + distance = 9100 + distid = 'KM' + fltype = 'X' + period = 1 ) + ( carrid = 'LH' + connid = 0400 + countryfr = 'DE' + cityfrom = 'FRANKFURT' + airpfrom = 'FRA' + countryto = 'US' + cityto = 'NEW YORK' + airpto = 'JFK' + fltime = 444 + deptime = '101000' + arrtime = '113400' + distance = 6162 + distid = 'KM' + fltype = '' + period = 0 ) + ( carrid = 'LH' + connid = 0401 + countryfr = 'US' + cityfrom = 'NEW YORK' + airpfrom = 'JFK' + countryto = 'DE' + cityto = 'FRANKFURT' + airpto = 'FRA' + fltime = 435 + deptime = '183000' + arrtime = '074500' + distance = 6162 + distid = 'KM' + fltype = '' + period = 1 ) + ( carrid = 'LH' + connid = 0402 + countryfr = 'DE' + cityfrom = 'FRANKFURT' + airpfrom = 'FRA' + countryto = 'US' + cityto = 'NEW YORK' + airpto = 'JFK' + fltime = 455 + deptime = '133000' + arrtime = '150500' + distance = 6162 + distid = 'KM' + fltype = 'X' + period = 0 ) + ( carrid = 'LH' + connid = 2402 + countryfr = 'DE' + cityfrom = 'FRANKFURT' + airpfrom = 'FRA' + countryto = 'DE' + cityto = 'BERLIN' + airpto = 'SXF' + fltime = 65 + deptime = '103000' + arrtime = '113500' + distance = 555 + distid = 'KM' + fltype = '' + period = 0 ) ) ). + + "Filling db table + MODIFY zdemo_abap_carr FROM TABLE @( VALUE #( + ( carrid = 'AA' + carrname = 'American Airlines' + currcode = 'USD' + url = 'http://www.aa.com' ) + ( carrid = 'LH' + carrname = 'Lufthansa' + currcode = 'EUR' + url = 'http://www.lufthansa.com' ) + ( carrid = 'JL' + carrname = 'Japan Airlines' + currcode = 'JPY' + url = 'http://www.jal.co.jp' ) + ( carrid = 'DL' + carrname = 'Delta Airlines' + currcode = 'USD' + url = 'http://www.delta-air.com' ) + ( carrid = 'AZ' + carrname = 'ITA Airways' + currcode = 'EUR' + url = 'http://www.ita-airways.com' ) ) ). + + "Filling db table + MODIFY zdemo_abap_fli FROM TABLE @( VALUE #( + ( carrid = 'AA' + connid = 0017 + fldate = '20230923' + price = '464.35' + currency = 'USD' + planetype = '747-400' + seatsmax = 385 + seatsocc = 369 + paymentsum = '191993.87' + seatsmax_b = 31 + seatsocc_b = 31 + seatsmax_f = 21 + seatsocc_f = 19 ) + ( carrid = 'AA' + connid = 0017 + fldate = '20230929' + price = '464.35' + currency = 'USD' + planetype = '747-400' + seatsmax = 385 + seatsocc = 372 + paymentsum = '193537.52' + seatsmax_b = 31 + seatsocc_b = 30 + seatsmax_f = 21 + seatsocc_f = 20 ) + ( carrid = 'AA' + connid = 0017 + fldate = '20231111' + price = '464.35' + currency = 'USD' + planetype = '747-400' + seatsmax = 385 + seatsocc = 374 + paymentsum = '193651.77' + seatsmax_b = 31 + seatsocc_b = 29 + seatsmax_f = 21 + seatsocc_f = 21 ) + ( carrid = 'AA' + connid = 0064 + fldate = '20220131' + price = '464.35' + currency = 'USD' + planetype = 'A340-600' + seatsmax = 330 + seatsocc = 313 + paymentsum = '168469.88' + seatsmax_b = 30 + seatsocc_b = 30 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'AA' + connid = 0064 + fldate = '20220215' + price = '464.35' + currency = 'USD' + planetype = 'A340-600' + seatsmax = 330 + seatsocc = 157 + paymentsum = '84846.15' + seatsmax_b = 30 + seatsocc_b = 15 + seatsmax_f = 20 + seatsocc_f = 10 ) + ( carrid = 'AZ' + connid = 0555 + fldate = '20230721' + price = '226.41' + currency = 'EUR' + planetype = 'A319-100' + seatsmax = 120 + seatsocc = 114 + paymentsum = '26519.75' + seatsmax_b = 8 + seatsocc_b = 8 + seatsmax_f = 8 + seatsocc_f = 8 ) + ( carrid = 'AZ' + connid = 0555 + fldate = '20230728' + price = '226.41' + currency = 'EUR' + planetype = 'A319-100' + seatsmax = 120 + seatsocc = 115 + paymentsum = '16695.50' + seatsmax_b = 8 + seatsocc_b = 8 + seatsmax_f = 8 + seatsocc_f = 8 ) + ( carrid = 'AZ' + connid = 0788 + fldate = '20230922' + price = '1071.41' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 456 + paymentsum = '548722.20' + seatsmax_b = 30 + seatsocc_b = 30 + seatsmax_f = 20 + seatsocc_f = 20 ) + ( carrid = 'AZ' + connid = 0788 + fldate = '20230722' + price = '1071.41' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 455 + paymentsum = '544674.30' + seatsmax_b = 30 + seatsocc_b = 28 + seatsmax_f = 20 + seatsocc_f = 20 ) + ( carrid = 'AZ' + connid = 0789 + fldate = '20231025' + price = '1071.41' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 455 + paymentsum = '545704.30' + seatsmax_b = 30 + seatsocc_b = 30 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'AZ' + connid = 0789 + fldate = '20230221' + price = '1071.41' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 459 + paymentsum = '549226.90' + seatsmax_b = 30 + seatsocc_b = 30 + seatsmax_f = 20 + seatsocc_f = 20 ) + ( carrid = 'AZ' + connid = 0790 + fldate = '20231228' + price = '1055.41' + currency = 'EUR' + planetype = '747-400' + seatsmax = 385 + seatsocc = 370 + paymentsum = '462373.86' + seatsmax_b = 31 + seatsocc_b = 30 + seatsmax_f = 21 + seatsocc_f = 21 ) + ( carrid = 'AZ' + connid = 0790 + fldate = '20231201' + price = '1055.41' + currency = 'EUR' + planetype = '747-400' + seatsmax = 385 + seatsocc = 367 + paymentsum = '463661.64' + seatsmax_b = 31 + seatsocc_b = 31 + seatsmax_f = 21 + seatsocc_f = 21 ) + ( carrid = 'DL' + connid = 0106 + fldate = '20230209' + price = '652.42' + currency = 'USD' + planetype = 'A340-600' + seatsmax = 330 + seatsocc = 178 + paymentsum = '136750.33' + seatsmax_b = 30 + seatsocc_b = 17 + seatsmax_f = 20 + seatsocc_f = 10 ) + ( carrid = 'DL' + connid = 0106 + fldate = '20240102' + price = '652.42' + currency = 'USD' + planetype = 'A340-600' + seatsmax = 330 + seatsocc = 16 + paymentsum = '12892.33' + seatsmax_b = 30 + seatsocc_b = 2 + seatsmax_f = 20 + seatsocc_f = 10 ) + ( carrid = 'DL' + connid = 1699 + fldate = '20230921' + price = '464.35' + currency = 'USD' + planetype = '767-200' + seatsmax = 260 + seatsocc = 250 + paymentsum = '126636.91' + seatsmax_b = 21 + seatsocc_b = 20 + seatsmax_f = 11 + seatsocc_f = 11 ) + ( carrid = 'DL' + connid = 1699 + fldate = '20230511' + price = '464.35' + currency = 'USD' + planetype = '767-200' + seatsmax = 260 + seatsocc = 251 + paymentsum = '126493.06' + seatsmax_b = 21 + seatsocc_b = 20 + seatsmax_f = 11 + seatsocc_f = 11 ) + ( carrid = 'DL' + connid = 1984 + fldate = '20230719' + price = '464.35' + currency = 'USD' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 460 + paymentsum = '225427.35' + seatsmax_b = 30 + seatsocc_b = 29 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'DL' + connid = 1984 + fldate = '20230213' + price = '464.35' + currency = 'USD' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 458 + paymentsum = '225088.83' + seatsmax_b = 30 + seatsocc_b = 30 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'JL' + connid = 0407 + fldate = '20231128' + price = '1102.77' + currency = 'JPY' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 458 + paymentsum = '563231.65' + seatsmax_b = 30 + seatsocc_b = 27 + seatsmax_f = 20 + seatsocc_f = 20 ) + ( carrid = 'JL' + connid = 0407 + fldate = '20231019' + price = '1102.77' + currency = 'JPY' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 452 + paymentsum = '553552.12' + seatsmax_b = 30 + seatsocc_b = 28 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'JL' + connid = 0408 + fldate = '20231128' + price = '1102.77' + currency = 'JPY' + planetype = '747-400' + seatsmax = 385 + seatsocc = 365 + paymentsum = '470129.20' + seatsmax_b = 31 + seatsocc_b = 28 + seatsmax_f = 21 + seatsocc_f = 20 ) + ( carrid = 'JL' + connid = 0408 + fldate = '20230123' + price = '1102.77' + currency = 'JPY' + planetype = '747-400' + seatsmax = 385 + seatsocc = 372 + paymentsum = '487715.90' + seatsmax_b = 31 + seatsocc_b = 31 + seatsmax_f = 21 + seatsocc_f = 20 ) + ( carrid = 'LH' + connid = 0400 + fldate = '20230628' + price = '1184.54' + currency = 'EUR' + planetype = 'A340-600' + seatsmax = 330 + seatsocc = 319 + paymentsum = '270822.24' + seatsmax_b = 30 + seatsocc_b = 30 + seatsmax_f = 20 + seatsocc_f = 20 ) + ( carrid = 'LH' + connid = 0400 + fldate = '20230323' + price = '1184.54' + currency = 'EUR' + planetype = 'A340-600' + seatsmax = 330 + seatsocc = 312 + paymentsum = '262597.14' + seatsmax_b = 30 + seatsocc_b = 28 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'LH' + connid = 0401 + fldate = '20231128' + price = '669.20' + currency = 'EUR' + planetype = '767-200' + seatsmax = 260 + seatsocc = 246 + paymentsum = '195417.72' + seatsmax_b = 21 + seatsocc_b = 19 + seatsmax_f = 11 + seatsocc_f = 10 ) + ( carrid = 'LH' + connid = 0401 + fldate = '20231229' + price = '669.20' + currency = 'EUR' + planetype = '767-200' + seatsmax = 260 + seatsocc = 252 + paymentsum = '199300.50' + seatsmax_b = 21 + seatsocc_b = 19 + seatsmax_f = 11 + seatsocc_f = 11 ) + ( carrid = 'LH' + connid = 0402 + fldate = '20230617' + price = '669.20' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 461 + paymentsum = '353526.12' + seatsmax_b = 30 + seatsocc_b = 29 + seatsmax_f = 20 + seatsocc_f = 18 ) + ( carrid = 'LH' + connid = 0402 + fldate = '20230313' + price = '669.20' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 450 + paymentsum = '349223.76' + seatsmax_b = 30 + seatsocc_b = 29 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'LH' + connid = 2402 + fldate = '20231028' + price = '245.20' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 451 + paymentsum = '127197.62' + seatsmax_b = 30 + seatsocc_b = 29 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'LH' + connid = 2402 + fldate = '20231223' + price = '245.20' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 458 + paymentsum = '18944.86' + seatsmax_b = 30 + seatsocc_b = 30 + seatsmax_f = 20 + seatsocc_f = 20 ) ) ). + + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_flight_tables.clas.xml b/src/zcl_demo_abap_flight_tables.clas.xml new file mode 100644 index 0000000..84a3247 --- /dev/null +++ b/src/zcl_demo_abap_flight_tables.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_FLIGHT_TABLES + E + Class for ABAP cheat sheet examples + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_internal_tables.clas.abap b/src/zcl_demo_abap_internal_tables.clas.abap new file mode 100644 index 0000000..adf142e --- /dev/null +++ b/src/zcl_demo_abap_internal_tables.clas.abap @@ -0,0 +1,1497 @@ +*********************************************************************** +* +* ABAP cheat sheet: Working with internal tables +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntactical options for working with +* internal tables as outlined in the respective ABAP cheat sheet. +* - Topics covered: creating, filling, reading from, sorting, modifying +* internal tables +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP Development Tools (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, check the notes +* included in the class as comments or refer to the respective topic +* in the ABAP Keyword Documentation. +* - Due to the amount of output in the console, the examples include +* numbers (e. g. 1) ..., 2) ..., 3) ...) for the individual example +* sections. Plus, the variable name is displayed in most cases. Hence, +* to easier and faster find the relevant output in the console, just +* search in the console for the number/variable name (STRG+F in the +* console) or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** +"!

ABAP cheat sheet: Internal tables

+"! Example to demonstrate working with internal tables.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_internal_tables DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + + CLASS-METHODS: class_constructor. + +protected section. + PRIVATE SECTION. + + "Creating structured data types. + TYPES: "Line types for internal tables + BEGIN OF struc1, + a TYPE i, + b TYPE c LENGTH 3, + c TYPE c LENGTH 3, + d TYPE c LENGTH 3, + END OF struc1, + BEGIN OF struc2, + a TYPE i, + b TYPE c LENGTH 3, + e TYPE c LENGTH 3, + f TYPE string, + END OF struc2, + "Types for demonstrating nested internal tables + BEGIN OF l_type1, + key_field TYPE i, + char1 TYPE c LENGTH 10, + char2 TYPE c LENGTH 10, + num1 TYPE i, + num2 TYPE i, + END OF l_type1, + BEGIN OF l_type2, + key_field TYPE i, + num1 TYPE i, + num2 TYPE i, + END OF l_type2, + BEGIN OF nested1, + key_field TYPE i, + char1 TYPE c LENGTH 10, + tab TYPE STANDARD TABLE OF l_type2 WITH EMPTY KEY, + END OF nested1, + BEGIN OF nested2, + key_field TYPE i, + char2 TYPE c LENGTH 10, + tab TYPE STANDARD TABLE OF l_type1 WITH EMPTY KEY, + END OF nested2, + "Declaring internal table types. + "Types for demonstrating nested internal tables + ty_nested1 TYPE STANDARD TABLE OF nested1 WITH EMPTY KEY, + ty_nested2 TYPE STANDARD TABLE OF nested2 WITH EMPTY KEY. + + CLASS-DATA: + "Declaring internal tables. + "Internal tables for demonstrating nested internal tables + itab_nested1 TYPE ty_nested1, + itab_nested2 TYPE ty_nested2, + "Internal tables for CORRESPONDING/MOVE-CORRESPONDING demo + tab1 TYPE TABLE OF struc1 WITH NON-UNIQUE KEY a, + tab2 TYPE TABLE OF struc2 WITH NON-UNIQUE KEY a, + tab3 TYPE SORTED TABLE OF struc1 WITH UNIQUE KEY a, + tab4 TYPE SORTED TABLE OF struc2 WITH UNIQUE KEY a. + + CLASS-METHODS: + fill_dbtabs, + fill_itabs_for_corresponding. + +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_INTERNAL_TABLES IMPLEMENTATION. + + + METHOD class_constructor. + fill_dbtabs( ). + ENDMETHOD. + + + METHOD fill_dbtabs. + "Initializing and filling of database tables to have data to work with + + DELETE FROM zdemo_abap_tab1. + DELETE FROM zdemo_abap_tab2. + + MODIFY zdemo_abap_tab1 FROM TABLE @( VALUE #( + ( key_field = 100 char1 = 'aaa' char2 = 'bbb' num1 = 1 num2 = 2 ) + ( key_field = 200 char1 = 'ccc' char2 = 'ddd' num1 = 3 num2 = 4 ) + ( key_field = 300 char1 = 'eee' char2 = 'fff' num1 = 5 num2 = 6 ) + ( key_field = 400 char1 = 'ggg' char2 = 'hhh' num1 = 7 num2 = 8 ) + ) ). + MODIFY zdemo_abap_tab2 FROM TABLE @( VALUE #( + ( key_field = 500 char1 = 'iii' num1 = 10 numlong = 1000 ) + ( key_field = 600 char1 = 'kkk' num1 = 12 numlong = 2000 ) + ( key_field = 700 char1 = 'mmm' num1 = 14 numlong = 3000 ) + ( key_field = 800 char1 = 'ooo' num1 = 15 numlong = 4000 ) + ) ). + + ENDMETHOD. + + + METHOD fill_itabs_for_corresponding. + tab1 = VALUE #( ( a = 1 b = 'aaa' c = 'aaa' d = 'A' ) + ( a = 2 b = 'bbb' c = 'bbb' d = 'B' ) ). + + tab2 = VALUE #( ( a = 3 b = 'ccc' e = 'ccc' f = `CCC` ) + ( a = 4 b = 'ddd' e = 'ddd' f = `DDD` ) ). + + tab3 = VALUE #( ( LINES OF tab1 ) ). + + tab4 = VALUE #( ( a = 1 b = 'xxx' e = 'yyy' f = `ZZZ` ) + ( LINES OF tab2 ) ). + + itab_nested1 = VALUE #( + ( key_field = 1 char1 = 'aaa' + tab = VALUE #( ( key_field = 1 num1 = 2 num2 = 3 ) + ( key_field = 2 num1 = 3 num2 = 4 ) + ( key_field = 3 num1 = 4 num2 = 5 ) ) ) + ( key_field = 2 char1 = 'bbb' + tab = VALUE #( ( key_field = 4 num1 = 5 num2 = 6 ) + ( key_field = 5 num1 = 6 num2 = 7 ) + ( key_field = 6 num1 = 7 num2 = 8 ) ) ) ). + + itab_nested2 = VALUE #( + ( key_field = 99 char2 = 'yyy' tab = VALUE #( + ( key_field = 10 char1 = 'aaa' + char2 = 'bbb' num1 = 100 num2 = 200 ) + ( key_field = 20 char1 = 'ccc' + char2 = 'ddd' num1 = 300 num2 = 400 ) + ( key_field = 30 char1 = 'eee' + char2 = 'fff' num1 = 500 num2 = 600 ) ) ) + ( key_field = 100 char2 = 'zzz' tab = VALUE #( + ( key_field = 40 char1 = 'ggg' + char2 = 'hhh' num1 = 700 num2 = 800 ) + ( key_field = 50 char1 = 'iii' + char2 = 'jjj' num1 = 900 num2 = 1000 ) + ( key_field = 60 char1 = 'kkk' + char2 = 'lll' num1 = 1100 num2 = 1200 ) ) ) ). + + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `Demo: Working with Internal Tables` ). + output->display( `Filling and Copying Internal Table Content` ). + output->display( `1) Adding single lines using APPEND/INSERT` ). + + "Two internal tables, a standard and sorted internal table. + "Both have the same line type and one field as (non-)unique key. + DATA it_st TYPE TABLE OF struc1 WITH NON-UNIQUE KEY a. + DATA it_so TYPE SORTED TABLE OF struc1 WITH UNIQUE KEY a. + + "APPEND + "Standard table + APPEND VALUE #( a = 1 b = 'aaa' c = 'bbb' d = 'ccc' ) TO it_st. + + "A line is created and filled to be used for the APPEND statement. + "The line type matches the line type of the internal table. + DATA(line) = VALUE struc1( a = 2 b = 'd' c = 'e' d = 'f' ). + + "Sorted table + "APPEND works here with a sorted table. At this stage, the + "internal table is empty, so there is no issue (lines are only + "appended if they match the sort order and do not create + "duplicate entries if the primary table key is unique). + APPEND line TO it_so. + + "INSERT + "INSERT has same effect as APPEND with standard tables + INSERT VALUE #( a = 2 b = 'ddd' c = 'eee' d = 'fff' ) + INTO TABLE it_st. + + INSERT VALUE #( a = 1 b = 'a' c = 'b' d = 'c' ) INTO TABLE it_so. + + output->display( input = it_st name = `it_st` ). + output->display( input = it_so name = `it_so` ). + + output->next_section( `2) Adding initial line` ). + + APPEND INITIAL LINE TO it_st. + + INSERT INITIAL LINE INTO TABLE it_so. + + output->display( input = it_st name = `it_st` ). + output->display( input = it_so name = `it_so` ). + + output->next_section( `3) Adding mutliple lines of an internal table to` && + ` another one` ). + + "No additions: All lines are added to the target internal table + APPEND LINES OF it_so TO it_st. + + "Creating a new itab and filling it. + DATA it_so2 LIKE it_so. + + INSERT VALUE #( a = 3 b = 'g' c = 'h' d = 'i' ) INTO TABLE it_so2. + + INSERT VALUE #( a = 4 b = 'j' c = 'k' d = 'l' ) INTO TABLE it_so2. + + "Inserting all lines of previously created internal table. + INSERT LINES OF it_so2 INTO TABLE it_so. + + output->display( input = it_st name = `it_st` ). + output->display( input = it_so name = `it_so` ). + + output->next_section( `4) Adding lines of an internal table to` && + ` another one by specifying the index range.` ). + + "When using only FROM, all lines are respected until the final + "table entry. When using only TO, all lines are respected + "starting from the first table entry. + APPEND LINES OF it_so FROM 2 TO 3 TO it_st. + + INSERT LINES OF it_so FROM 3 INTO TABLE it_st. + + APPEND LINES OF it_so TO 2 TO it_st. + + output->display( input = it_st name = `it_st` ). + + output->next_section( `5) Inserting lines of an internal table` && + ` into another one at a specific position` ). + + "Inserting a single line + INSERT VALUE #( a = 10 b = 'ggg' c = 'hhh' d = 'iii' ) + INTO it_st INDEX 1. + + "Inserting multiple lines + INSERT LINES OF it_so2 INTO it_st INDEX 2. + + output->display( input = it_st name = `it_st` ). + + output->next_section( `6) Adding lines using constructor expressions` ). + + "Creating a line to be added to an internal table. + line = VALUE #( a = 1 b = 'aaa' c = 'bbb' d = 'ccc' ). + + "Table on the right is constructed inline using VALUE and assigned + "Note: This way, existing table content is cleared. + it_st = VALUE #( ( line ) + ( a = 2 b = 'ddd' c = 'eee' d = 'fff' ) ). + + output->display( input = it_st name = `it_st` ). + + output->next_section( `7) Creating a new table inline and adding lines` && + ` using a constructor expression` ). + + "Internal table type + TYPES it_type LIKE it_st. + + "Creating an internal table inline and filling in one go + DATA(it_st2) = VALUE it_type( ( a = 3 b = 'ggg' + c = 'hhh' d = 'iii' ) + ( a = 4 b = 'jjj' + c = 'kkk' d = 'lll' ) ). + + output->display( input = it_st2 name = `it_st2` ). + + output->next_section( `8) Adding lines using constructor expressions ` && + `and keeping existing table content` ). + + "BASE addition: existing table content is not removed + it_st = VALUE #( BASE it_st ( a = 5 b = 'mmm' c = 'nnn' d = 'ooo' ) + ( a = 6 b = 'ppp' c = 'qqq' d = 'rrr' ) + ). + + output->display( input = it_st name = `it_st` ). + + output->next_section( `9) Adding lines from other internal tables using` && + ` constructor expressions` ). + + "With LINES OF itab specified within the pair of parentheses, + "all lines of the internal table are added; here, in the same + "expression another line is added as well + it_st = VALUE #( BASE it_st ( LINES OF it_st2 ) + ( a = 7 b = 'sss' c = 'ttt' d = 'uuu' ) + ). + + output->display( input = it_st name = `it_st` ). + + output->next_section( `10) Copying table content (without constructor ` && + `expression)` ). + + "Assignment of a table to another one having a matching line type + it_st = it_st2. + + output->display( input = it_st name = `it_st` ). + + output->next_section( `11) CORRESPONDING Operator and MOVE-CORRESPONDING` ). + output->display( `Internal table content before assignments` ). + + "Note: Before the following statements, the table content is reset + "to this state to work with the same set of values. + fill_itabs_for_corresponding( ). + + output->display( input = tab1 name = `tab1` ). + output->display( input = tab2 name = `tab2` ). + output->display( input = tab3 name = `tab3` ). + output->display( input = tab4 name = `tab4` ). + + output->next_section( `Copying content from another table that has ` && + `a different line type ...` ). + output->display( `11a) ... and deleting existing table content ` && + `using the CORRESPONDING operator` ). + + tab1 = CORRESPONDING #( tab2 ). + + output->display( input = tab1 name = `tab1` ). + + fill_itabs_for_corresponding( ). + + output->next_section( `11b) ... and deleting existing table content ` && + `using MOVE-CORRESPONDING` ). + + MOVE-CORRESPONDING tab2 TO tab1. + + output->display( input = tab1 name = `tab1` ). + + fill_itabs_for_corresponding( ). + + output->next_section( `11c) ... and keeping existing table ` && + `content using the CORRESPONDING operator` ). + + tab1 = CORRESPONDING #( BASE ( tab1 ) tab2 ). + + output->display( input = tab1 name = `tab1` ). + + fill_itabs_for_corresponding( ). + + output->next_section( `11d) ... and keeping existing table ` && + `content using MOVE-CORRESPONDING` ). + + MOVE-CORRESPONDING tab2 TO tab1 KEEPING TARGET LINES. + + output->display( input = tab1 name = `tab1` ). + + fill_itabs_for_corresponding( ). + + output->next_section( `11e) ... respecting component ` && + `mapping` ). + + "Specifying components of a source table that are assigned to the + "components of a target table in mapping relationships + tab1 = CORRESPONDING #( tab2 MAPPING c = e d = f ). + + output->display( input = tab1 name = `tab1` ). + + fill_itabs_for_corresponding( ). + + output->next_section( `11f) ... excluding components` ). + + "Excluding components from the assignment + tab1 = CORRESPONDING #( tab2 EXCEPT b ). + + output->display( input = tab1 name = `tab1` ). + + "EXCEPT * means that all components remain initial not specified + "for mapping + tab1 = CORRESPONDING #( tab2 MAPPING d = f EXCEPT * ). + + output->display( input = tab1 name = `tab1` ). + + fill_itabs_for_corresponding( ). + + output->next_section( `11h) ... discarding duplicates` ). + + "Preventing runtime errors if duplicate lines are assigned to + "target table that is defined to only accept unique keys. + "Note: Other tables than above are used here. + tab3 = CORRESPONDING #( BASE ( tab3 ) tab4 DISCARDING DUPLICATES ). + + output->display( input = tab3 name = `tab3` ). + + fill_itabs_for_corresponding( ). + + tab3 = CORRESPONDING #( BASE ( tab3 ) tab4 DISCARDING DUPLICATES + MAPPING d = f EXCEPT b ). + + output->display( input = tab3 name = `tab3` ). + + output->next_section( `11i) Copying data from a deep ` && + `internal table to another deep internal table` ). + output->display( `Original table content` ). + + output->display( input = itab_nested1 name = `itab_nested1` ). + output->display( input = itab_nested2 name = `itab_nested2` ). + + output->next_section( `11j) ... deleting ` && + `existing content (CORRESPONDING operator)` ). + + itab_nested2 = CORRESPONDING #( DEEP itab_nested1 ). + + output->display( input = itab_nested2 name = `itab_nested2` ). + + fill_itabs_for_corresponding( ). + + output->next_section( `11k) ... deleting ` && + `existing content (MOVE-CORRESPONDING)` ). + + MOVE-CORRESPONDING itab_nested1 TO itab_nested2 + EXPANDING NESTED TABLES. + + output->display( input = itab_nested2 name = `itab_nested2` ). + + fill_itabs_for_corresponding( ). + + output->next_section( `11l) ... keeping ` && + `existing content (CORRESPONDING operator)` ). + + itab_nested2 = CORRESPONDING #( DEEP BASE ( itab_nested2 ) + itab_nested1 ). + + output->display( input = itab_nested2 name = `itab_nested2` ). + + fill_itabs_for_corresponding( ). + + output->next_section( `11m) ... keeping ` && + `existing content (MOVE-CORRESPONDING)` ). + + MOVE-CORRESPONDING itab_nested1 TO itab_nested2 + EXPANDING NESTED TABLES KEEPING TARGET LINES. + + output->display( input = itab_nested2 name = `itab_nested2` ). + + output->next_section( `Filling internal tables: Excursions` ). + output->display( `12) Selecting multiple rows from a database ` && + `table into an internal table` ). + + SELECT FROM zdemo_abap_tab1 + FIELDS key_field, char1, char2, num1, num2 + WHERE num1 > 3 + INTO TABLE @DATA(itab_select1). + + output->display( input = itab_select1 name = `itab_select1` ). + + output->next_section( `13) Sequentially adding multiple rows from ` && + `a database table to an internal table` ). + + DATA itab TYPE TABLE OF zdemo_abap_tab1 WITH NON-UNIQUE KEY client key_field. + + SELECT FROM zdemo_abap_tab1 + FIELDS * + WHERE num1 > 3 + INTO @DATA(struc_select). + + IF sy-subrc = 0. + "Some modifications on the read lines (capitalizing letters) + struc_select-char1 = to_upper( struc_select-char1 ). + struc_select-char2 = to_upper( struc_select-char2 ). + + "Adding modified line to an internal table + APPEND struc_select TO itab. + ENDIF. + ENDSELECT. + + output->display( input = itab name = `itab` ). + + output->next_section( `14) Adding multiple rows from a database table ` && + `to an internal table that has a different line type than the ` && + `database table and keeping existing table content` ). + + SELECT FROM zdemo_abap_tab2 + FIELDS * + WHERE num1 > 10 + APPENDING CORRESPONDING FIELDS OF TABLE @itab. + + output->display( input = itab name = `itab` ). + + output->next_section( `15) Adding multiple rows from a database table ` && + `to an internal table that has a different line type than the ` && + `database table and deleting existing table content` ). + + SELECT FROM zdemo_abap_tab2 + FIELDS * + WHERE num1 > 10 + INTO CORRESPONDING FIELDS OF TABLE @itab. + + output->display( input = itab name = `itab` ). + + output->next_section( `16) Adding multiple rows from an internal table ` && + `to an internal table using SELECT` ). + + SELECT key_field, char1, char2, num1, num2 + FROM @itab AS itab_alias + INTO TABLE @DATA(itab_clone). + + output->display( input = itab_clone name = `itab_clone` ). + + output->next_section( `17) Combining data of multiple tables into an` && + ` internal table using an inner join` ). + + "Filling table to be selected from + itab = VALUE #( ( key_field = 500 char1 = 'uuu' char2 = 'vvv' + num1 = 501 num2 = 502 ) + ( key_field = 600 char1 = 'www' char2 = 'xxx' + num1 = 601 num2 = 602 ) ). + + "SELECT list includes fields from both tables + "If there are no equivalent entries in the first or second table, + "the rows are not joined. + SELECT itab_alias1~key_field, itab_alias1~char2, + zdemo_abap_tab2~numlong + FROM @itab AS itab_alias1 + INNER JOIN zdemo_abap_tab2 + ON itab_alias1~key_field = zdemo_abap_tab2~key_field + INTO TABLE @DATA(join_result). + + output->display( input = join_result name = `join_result` ). + + output->next_section( `18) Filling internal table ` && + `using a subquery (1)` ). + + "A subquery is specified in the WHERE clause + "Here, data is selected from a database table depending on + "whether the value of a certain field is not among the + "values specified in parentheses. + SELECT key_field, char1, numlong + FROM zdemo_abap_tab2 + WHERE char1 NOT IN ( 'iii', 'mmm', 'ooo', 'ppp' ) + INTO TABLE @DATA(subquery_result1). + + output->display( input = subquery_result1 name = `subquery_result1` ). + + output->next_section( `19) Filling internal table ` && + `using a subquery (2)` ). + + "A subquery using EXISTS in the WHERE clause. + "In the example, data is selected from a database table depending + "on the existence of data in an internal table. Only if a line + "with a matching value of the specified field exists in both + "database and internal table, data is read. + SELECT key_field, numlong + FROM zdemo_abap_tab2 + WHERE EXISTS + ( SELECT 'X' FROM @itab AS itab_alias2 + WHERE key_field = zdemo_abap_tab2~key_field ) + INTO TABLE @DATA(subquery_result2). + + output->display( input = subquery_result2 name = `subquery_result2` ). + + output->next_section( `20) Filling an internal table from a table ` && + `depending on the existence of data in another internal table ` && + `using the addition FOR ALL ENTRIES` ). + + "In the example, data is selected from a database table depending + "on the existence of data in an internal table. Only if a line + "with a matching value of the specified field exists in both + "database and internal table, data is read. + "Ensure that the internal table from which to read is not initial. + IF ( 0 < lines( itab ) ). + SELECT key_field, char1, numlong + FROM zdemo_abap_tab2 + FOR ALL ENTRIES IN @itab + WHERE key_field = @itab-key_field + INTO TABLE @DATA(select_result). + ENDIF. + + output->display( input = select_result name = `select_result` ). + + output->next_section( `21) Adding content from a database to internal` && + ` table by using alias names in the SELECT list` ). + + DATA itab2 TYPE TABLE OF zdemo_abap_tab2 WITH EMPTY KEY. + + "Specifying alias names can help fill an existing internal + "table that has not a matching line type to the database table. + "Here, two fields are specified with an alias name to match the + "names of components contained in the existing internal table. + "The individual types of the fields match, too. + SELECT key_field, char2 AS char1, num2 AS num1 + FROM zdemo_abap_tab1 + INTO CORRESPONDING FIELDS OF TABLE @itab2 UP TO 3 ROWS. + + output->display( input = itab2 name = `itab2` ). + + output->next_section( `22) FILTER: Filtering internal table by condition` ). + + TYPES t_itab2filter TYPE SORTED TABLE OF zdemo_abap_tab1 + WITH NON-UNIQUE KEY key_field. + + DATA itab2filter TYPE t_itab2filter. + + "Retrieving data for the example in an internal table + SELECT key_field, char1, char2, num1, num2 + FROM zdemo_abap_tab1 + INTO CORRESPONDING FIELDS OF TABLE @itab2filter UP TO 4 ROWS. + + "New internal table is constructed by filtering out lines from + "another one. + DATA(filtered1) = FILTER #( itab2filter WHERE key_field < 400 ). + + output->display( input = filtered1 name = `filtered1` ). + + output->next_section( `23) FILTER: Filtering internal table by ` && + `condition with the addition EXCEPT that excludes data ` && + `according to condition` ). + + DATA(filtered2) = FILTER #( itab2filter + EXCEPT WHERE key_field < 300 ). + + output->display( input = filtered2 name = `filtered2` ). + + output->next_section( `24) FILTER: Filtering internal table by ` && + `using another filter table` ). + + "Filling the filter table. + DATA(extract) = VALUE t_itab2filter( ( key_field = 100 ) + ( key_field = 300 ) ). + + DATA(filtered3) = FILTER #( itab2filter IN extract + WHERE key_field = key_field ). + + output->display( input = filtered3 name = `filtered3` ). + + + output->next_section( `25) Inserting data into an internal table ` && + `using a COLLECT statement` ). + + "Internal table to work with + DATA itab_num TYPE SORTED TABLE OF l_type2 + WITH UNIQUE KEY key_field. + + itab_num = VALUE #( ( key_field = 1 num1 = 2 num2 = 3 ) + ( key_field = 2 num1 = 4 num2 = 5 ) + ( key_field = 3 num1 = 6 num2 = 7 ) ). + + "Values of numeric components are added to the + "corresponding values in an internal table + COLLECT VALUE l_type2( key_field = 1 num1 = 10 num2 = 10 ) + INTO itab_num. + + output->display( input = itab_num name = `itab_num` ). + + output->next_section( `Reading from internal tables` ). + + "Filling internal tables + it_st = VALUE #( ( a = 1 b = 'aaa' c = 'bbb' d = 'ccc' ) + ( a = 2 b = 'ddd' c = 'eee' d = 'fff' ) + ( a = 3 b = 'ggg' c = 'hhh' d = 'iii' ) + ( a = 4 b = 'jjj' c = 'kkk' d = 'lll' ) ). + + "Declaring demo sorted/hashed tables having primary and + "secondary keys as well as alias names defined + DATA it_so_sec TYPE SORTED TABLE OF struc1 + WITH NON-UNIQUE KEY primary_key ALIAS pk COMPONENTS a + WITH NON-UNIQUE SORTED KEY sec_key ALIAS sk COMPONENTS b. + + DATA it_ha_sec TYPE HASHED TABLE OF struc1 + WITH UNIQUE KEY primary_key ALIAS pkh COMPONENTS a + WITH NON-UNIQUE SORTED KEY sec_key_h ALIAS skh COMPONENTS b. + + "Filling internal table + it_so_sec = VALUE #( ( a = 1 b = 'bbb' c = '###' d = '###' ) + ( a = 2 b = 'ccc' c = '###' d = '###' ) + ( a = 3 b = 'aaa' c = 'zzz' d = '###' ) + ( a = 4 b = 'ddd' c = '###' d = '###' ) ). + + "Filling internal table with the content above + it_ha_sec = it_so_sec. + + output->display( `Original table content` ). + + output->display( input = it_so_sec name = `it_so_sec` ). + output->display( input = it_ha_sec name = `it_ha_sec` ). + + output->next_section( `26) Reading a single line into target area` ). + + "The examples anticipate the reading of a line by index since the + "syntax requires to specify the reading via index or key. Both + "inline declarations and existing target areas are demonstrated. + + "Work area + READ TABLE it_so_sec INTO DATA(wa1) INDEX 1. + DATA wa2 LIKE LINE OF it_so_sec. + + "The addition TRANSPORTING specifies which components are to be + "respected for the copying. If it is not specified, all components + "are respected. + READ TABLE it_so_sec INTO wa2 INDEX 2 TRANSPORTING a b c. + + "Field symbol + READ TABLE it_so_sec ASSIGNING FIELD-SYMBOL() INDEX 3. + + FIELD-SYMBOLS LIKE LINE OF it_so_sec. + READ TABLE it_st ASSIGNING INDEX 1. + + "Data reference variable + READ TABLE it_so_sec REFERENCE INTO DATA(dref) INDEX 4. + + DATA dref2 LIKE REF TO wa2. + READ TABLE it_so_sec REFERENCE INTO dref2 INDEX 2. + + output->display( input = wa1 name = `wa1` ). + output->display( input = wa2 name = `wa2` ). + output->display( input = name = `` ). + output->display( input = name = `` ). + output->display( input = dref->* name = `dref->*` ). + output->display( input = dref2->* name = `dref2->*` ). + + output->next_section( `Reading a single line via index ...` ). + output->display( `27) ... using READ TABLE` ). + + "Primary table index used implicitly + READ TABLE it_so_sec INTO DATA(wa3) INDEX 1. + + "Primary table index used implicitly; result here: same as above + READ TABLE it_so_sec INTO DATA(wa4) INDEX 1 USING KEY primary_key. + + "Primary table key alias; result here: same as above + READ TABLE it_so_sec INTO DATA(wa5) INDEX 1 USING KEY pk. + + "Secondary table key; secondary table index used + READ TABLE it_so_sec INTO DATA(wa6) INDEX 1 USING KEY sec_key. + + "Secondary table key alias; secondary table index used + "result here: same as above + READ TABLE it_so_sec INTO DATA(wa7) INDEX 1 USING KEY sk. + + "Index access for hashed tables using secondary table index + READ TABLE it_ha_sec INTO DATA(wa8) INDEX 1 USING KEY sec_key_h. + + output->display( input = wa3 name = `wa3` ). + output->display( input = wa4 name = `wa4` ). + output->display( input = wa5 name = `wa5` ). + output->display( input = wa6 name = `wa6` ). + output->display( input = wa7 name = `wa7` ). + output->display( input = wa8 name = `wa8` ). + + output->next_section( `28) ... table expressions (1)` ). + + "Reading via index; primary table index is used implicitly + DATA(lv1) = it_so_sec[ 2 ]. + + "Note: A line that is not found results in an runtime error. + DATA(idx) = 10. + + TRY. + DATA(lv2) = it_so_sec[ idx ]. + CATCH cx_sy_itab_line_not_found. + DATA(error) = |Line with index { idx } does not exist.|. + ENDTRY. + + "Reading via index and specifying the table index (via the key) + "to be read from + DATA(lv3) = it_so_sec[ KEY primary_key INDEX 1 ]. + + DATA(lv4) = it_so_sec[ KEY sec_key INDEX 4 ]. + + "Hashed table example (secondary table index) + DATA(lv5) = it_ha_sec[ KEY sec_key_h INDEX 3 ]. + + output->display( input = lv1 name = `lv1` ). + + IF lv2 IS NOT INITIAL. + output->display( input = lv2 name = `lv2` ). + ENDIF. + + IF error IS NOT INITIAL. + output->display( input = error name = `error` ). + ENDIF. + + output->display( input = lv3 name = `lv3` ). + output->display( input = lv4 name = `lv4` ). + output->display( input = lv5 name = `lv5` ). + + output->next_section( `29) ... table expressions (2)` ). + + "Copying a table line via table expression and embedding in + "a constructor expression + DATA(lv6) = VALUE #( it_so_sec[ 2 ] ). + + "Reading into data reference variable using the REF operator + DATA(dref3) = REF #( it_so_sec[ 4 ] ). + + "OPTIONAL/DEFAULT additions: An unsuccessful reading operation + "does not raise the exception; returns either an initial or + "default line in case of an unsuccessful reading operation + DATA(lv7) = VALUE #( it_so_sec[ 10 ] OPTIONAL ). + + DATA(lv8) = VALUE #( it_so_sec[ 10 ] DEFAULT it_so_sec[ 2 ] ). + + output->display( input = lv6 name = `lv6` ). + output->display( input = dref3->* name = `dref3->*` ). + output->display( input = lv7 name = `lv7` ). + output->display( input = lv8 name = `lv8` ). + + output->next_section( `Reading a single line via table keys ...` ). + output->display( `30) ... using READ TABLE (1)` ). + + "Primary table key (COMPONENTS addition is optional) + READ TABLE it_so_sec INTO DATA(wa9) + WITH TABLE KEY primary_key COMPONENTS a = 1. + + READ TABLE it_so_sec INTO DATA(wa10) WITH TABLE KEY a = 2. + + "Primary table key alias + READ TABLE it_so_sec INTO DATA(wa11) + WITH TABLE KEY pk COMPONENTS a = 3. + + "Secondary table key + READ TABLE it_so_sec INTO DATA(wa12) + WITH TABLE KEY sec_key COMPONENTS b = 'ddd'. + + "Secondary table key alias + READ TABLE it_so_sec INTO DATA(wa13) + WITH TABLE KEY sk COMPONENTS b = 'ccc'. + + output->display( input = wa9 name = `wa9` ). + output->display( input = wa10 name = `wa10` ). + output->display( input = wa11 name = `wa11` ). + output->display( input = wa12 name = `wa12` ). + output->display( input = wa13 name = `wa13` ). + + output->next_section( `31) ... using READ TABLE (2)` ). + + "Reading a line based on keys specified in a work area + "Here, the work area contains primary and secondary key values. + "The line type is compatible to the internal table. + DATA(pr_keys) = VALUE struc1( a = 2 ). + + DATA(sec_keys) = VALUE struc1( b = 'aaa' ). + + "Primary table key is used implicitly + READ TABLE it_so_sec FROM pr_keys INTO DATA(wa14). + + "If USING KEY is not specified, the primary table key is used. + "If it is used, the specified table key is used. + "Secondary table key + READ TABLE it_so_sec FROM sec_keys + USING KEY sec_key INTO DATA(wa15). + + "Primary table key; result: same as wa14 + READ TABLE it_so_sec FROM pr_keys + USING KEY primary_key INTO DATA(wa16). + + output->display( input = wa14 name = `wa14` ). + output->display( input = wa15 name = `wa15` ). + output->display( input = wa16 name = `wa16` ). + + output->next_section( `32) ... using table expressions` ). + "Primary table key (COMPONENTS addition is optional) + DATA(lv9) = it_so_sec[ KEY primary_key COMPONENTS a = 1 ]. + + DATA(lv10) = it_so_sec[ KEY primary_key a = 1 ]. + + DATA(lv11) = it_so_sec[ KEY pk a = 2 ]. "Primary table key alias + + "Secondary table key (COMPONENTS mandatory) + DATA(lv12) = it_so_sec[ KEY sec_key COMPONENTS b = 'aaa' ]. + + DATA(lv13) = it_so_sec[ KEY sk COMPONENTS b = 'ddd' ]. "Alias + + output->display( input = lv9 name = `lv9` ). + output->display( input = lv10 name = `lv10` ). + output->display( input = lv11 name = `lv11` ). + output->display( input = lv12 name = `lv12` ). + output->display( input = lv13 name = `lv13` ). + + output->next_section( `33) Reading a single line via free key` ). + "Note: If there a multiple matching entries, the first found + "is returned. + READ TABLE it_so_sec INTO DATA(wa17) WITH KEY c = '###'. + + DATA(lv14) = it_so_sec[ c = 'zzz' ]. + + output->display( input = wa17 name = `wa17` ). + output->display( input = lv14 name = `lv14` ). + + output->next_section( `34) Excursion: Addressing individual components` ). + "Addressing a component using the component selector + DATA(comp1) = it_so_sec[ 1 ]-b. + + READ TABLE it_so_sec ASSIGNING FIELD-SYMBOL() INDEX 2. + + DATA(comp2) = -c. + + READ TABLE it_so_sec REFERENCE INTO DATA(dref4) INDEX 3. + + DATA(comp3) = dref->*-a. + + "Same effect as above but less to write + DATA(comp4) = dref->b. + + output->display( input = comp1 name = `comp1` ). + output->display( input = comp2 name = `comp2` ). + output->display( input = comp3 name = `comp3` ). + output->display( input = comp4 name = `comp4` ). + + output->next_section( `35) Checking if a line exists in an internal table` ). + + "Defining the key + DATA(key1) = 2. + + "Internal table function + IF line_exists( it_so_sec[ a = key1 ] ). + output->display( |Line { key1 } exists in internal table.| ). + ELSE. + output->display( |Line { key1 } does not exist in internal table.| ). + ENDIF. + + "Alternative using READ TABLE (sy-subrc is checked) + "When using the addition TRANSPORTING NO FIELDS, no field values + "are read. Only the system fields are filled. + READ TABLE it_so_sec WITH KEY a = key1 TRANSPORTING NO FIELDS. + + IF sy-subrc = 0. + output->display( |Line { key1 } exists in internal table.| ). + ELSE. + output->display( |Line { key1 } does not exist in internal table.| ). + ENDIF. + + output->next_section( `36) Checking the index of a ` && + `specific line` ). + + DATA(key2) = 4. + + DATA(idx_of_line1) = line_index( it_so_sec[ a = key2 ] ). + + DATA(key3) = 10. + + DATA(idx_of_line2) = line_index( it_so_sec[ a = key3 ] ). + + "Alternative using READ TABLE + "The table index is written to the sy-tabix system field + READ TABLE it_so_sec WITH KEY a = key2 TRANSPORTING NO FIELDS. + + IF sy-subrc = 0. + DATA(tab_idx1) = sy-tabix. + ENDIF. + + READ TABLE it_so_sec WITH KEY a = key3 TRANSPORTING NO FIELDS. + + IF sy-subrc = 0. + DATA(tab_idx2) = sy-tabix. + ENDIF. + + IF idx_of_line1 <> 0. + output->display( |The index of the line with key = { key2 } | && + |is { idx_of_line1 } in the internal table.| ). + ELSE. + output->display( |The line with key = { key2 } does not exist | && + |in the internal table.| ). + ENDIF. + + IF idx_of_line2 <> 0. + output->display( |The index of the line with key = { key3 } | && + |is { idx_of_line2 } in the internal table.| ). + ELSE. + output->display( |The line with key = { key3 } does not exist | && + |in the internal table.| ). + ENDIF. + + IF tab_idx1 <> 0. + output->display( |The index of the line with key = { key2 } | && + |is { tab_idx1 } in the internal table.| ). + ELSE. + output->display( |The line with key = { key2 } does not exist | && + |in the internal table.| ). + ENDIF. + + IF tab_idx2 <> 0. + output->display( |The index of the line with key = { key3 } | && + |is { tab_idx2 } in the internal table.| ). + ELSE. + output->display( |The line with key = { key3 } does not exist | && + |in the internal table.| ). + ENDIF. + + output->next_section( `37) Checking how many lines are in an` && + ` internal table` ). + DATA(itab_lines) = lines( it_so_sec ). + + output->display( |The internal table consists of { itab_lines } | && + |lines.| ). + + output->next_section( `Processing multiple internal table lines ` && + `sequentially` ). + output->display( `38) Reading a complete table by sequentially ` && + `reading all lines` ). + + "No further addition: All lines are respected. + LOOP AT it_so_sec ASSIGNING FIELD-SYMBOL(). + "Modifying a component to visualize the reading of all lines. + -b = 'ZZZ'. + ENDLOOP. + + output->display( input = it_so_sec name = `it_so_sec` ). + + output->next_section( `39) Reading multiple lines by an index range` ). + + "Specific lines in an index range are respected + "Note: FROM/TO alone can specified, too. + LOOP AT it_so_sec ASSIGNING FIELD-SYMBOL() FROM 2 TO 3. + "Modifying a component to visualize the reading of specific lines. + -c = 'YYY'. + ENDLOOP. + + output->display( input = it_so_sec name = `it_so_sec` ). + + output->next_section( `40) Reading multiple lines by condition` ). + + LOOP AT it_so_sec ASSIGNING FIELD-SYMBOL() WHERE a < 3. + "Modifying a component to visualize the reading of specific lines. + -d = 'XXX'. + ENDLOOP. + + output->display( input = it_so_sec name = `it_so_sec` ). + + output->next_section( `41) Looping across a table without an interest` && + ` in the table content` ). + + "Here, only the system fields are set. + LOOP AT it_so_sec TRANSPORTING NO FIELDS WHERE a < 3. + DATA(num) = sy-tabix. + ENDLOOP. + + output->display( |There are { num } lines in the table | && + |fulfilling the condition.| ). + + output->next_section( `42) Loop with table key specification` ). + + DATA it_st_em TYPE TABLE OF struc1 WITH EMPTY KEY. + + "Looping across hashed table using a secondary key. The loop starts + "according to the secondary table index. The lines are added to + "another internal table having a matching type. It basically + "visualizes the order of the table lines in the secondary table + "index. + LOOP AT it_ha_sec ASSIGNING FIELD-SYMBOL() USING KEY sec_key_h. + APPEND TO it_st_em. + ENDLOOP. + + output->display( input = it_st_em name = `it_st_em` ). + + output->next_section( `Creating and filling tables using table ` && + `iterations with FOR and VALUE` ). + output->display( `43) Retrieving values of one column in ` && + `an internal table.` ). + + "Creating internal table type + TYPES ty_numbers TYPE TABLE OF i WITH EMPTY KEY. + + "Table comprehension: Content of an internal table is created by + "evaluating a table using a table iteration with an iteration + "expressions within a constructor expression. + DATA(lv_num_a) = VALUE ty_numbers( FOR ls1 IN it_ha_sec + ( ls1-a ) ). + + output->display( input = lv_num_a name = `lv_num_a` ). + + output->next_section( `44) Retrieving values of one column in ` && + `an internal table based on conditions` ). + + DATA(lv_num_b) = VALUE ty_numbers( FOR ls2 IN it_ha_sec + WHERE ( a < 3 ) ( ls2-a ) ). + + output->display( input = lv_num_b name = `lv_num_b` ). + + + output->next_section( `45) Looping across 2 tables ` && + `and retrieving values based on conditions` ). + "Internal table type + TYPES tabtype LIKE it_so_sec. + + DATA(itab_for_2tab) = + VALUE tabtype( + FOR ls3 IN it_ha_sec + FOR ls4 IN it_so_sec WHERE ( a = ls3-a ) + ( a = ls3-a b = ls4-b c = ls3-c d = ls4-d ) ). + + output->display( input = itab_for_2tab name = `itab_for_2tab` ). + + output->next_section( `46) Retrieving and changing values from an ` && + `internal tables sequentially` ). + DATA(it_changed) = VALUE tabtype( FOR ls5 IN it_so_sec + ( a = ls5-a b = 'WWW' c = 'VVV' d = 'UUU' ) ). + + output->display( input = it_changed name = `it_changed` ). + + + output->next_section( `Sorting internal tables` ). + + "Creating structured data types + TYPES: BEGIN OF s1, + a TYPE i, + b TYPE string, + c TYPE c LENGTH 1, + d TYPE i, + END OF s1. + + TYPES: BEGIN OF s2, + a TYPE i, + b TYPE i, + END OF s2. + + "Creating internal tables + DATA it1 TYPE TABLE OF s1 WITH NON-UNIQUE KEY a. + DATA it2 TYPE TABLE OF s1 WITH DEFAULT KEY. + + "Filling internal tables + it1 = VALUE #( ( a = 1 b = `c` c = 'z' d = 4 ) + ( a = 3 b = `b` c = 'f' d = 3 ) + ( a = 2 b = `d` c = 'r' d = 9 ) + ( a = 4 b = `a` c = 'p' d = 3 ) + ( a = 5 b = `b` c = 'x' d = 2 ) + ( a = 5 b = `a` c = 'x' d = 0 ) + ( a = 1 b = `c` c = 'y' d = 8 ) ). + + it2 = it1. + + output->display( `Original internal table content ` && + `(it1 and it2 have the same content)` ). + + output->display( input = it1 name = `it1` ). + output->display( input = it2 name = `it2` ). + + output->next_section( `47) Sorting by primary table key` ). + + "Primary key: component a + SORT it1. + + output->display( input = it1 name = `it1` ). + + output->next_section( `48) Sorting by primary table key in ascending` && + ` order` ). + + "The sorting result is the same as above (where ASCENDING is used + "implicitly). Here, it is explicitly specified. + SORT it1 ASCENDING. + + output->display( input = it1 name = `it1` ). + + output->next_section( `49) Sorting by primary table key respecting all ` && + `non-numeric fields` ). + + "Primary key: standard table key (all non-numeric fields) + SORT it2. + + output->display( input = it2 name = `it2` ). + + "The following code is commented out on purpose because it + "produces a syntax warning. The primary table key is empty. + "A sorting has no effect. + "SORT it3. + "output->display( input = it3 name = `it3` ). + + output->next_section( `50) Sorting by primary table key in ` && + `descending order` ). + + "Sorting in descending order and by primary table key + SORT it1 DESCENDING. + + output->display( input = it1 name = `it1` ). + + output->next_section( `51) Sorting by explicitly specified component (1)` ). + "Here, the component is the primary table key. + "The sorting result is the same as above. + SORT it1 BY a DESCENDING. + + output->display( input = it1 name = `it1` ). + + output->next_section( `52) Sorting by explicitly specified component (2)` ). + + "Sorting by arbitrary, non-key field + SORT it1 BY d DESCENDING. + + output->display( input = it1 name = `it1` ). + + output->next_section( `53) Sorting by multiple explicitly specified` && + ` components` ). + + "Sorting by multiple components and specifying the sort order + SORT it1 BY b ASCENDING c DESCENDING. + + output->display( input = it1 name = `it1` ). + + output->next_section( `54) Sorting by respecting the values of all` && + ` components` ). + + "Sorting by considering the values of each field of the table line + SORT it1 BY table_line. + + output->display( input = it1 name = `it1` ). + + output->next_section( `Modifying internal table content` ). + output->display( `Internal table content before modifications` ). + + "Standard table + output->display( input = it_st name = `it_st` ). + + "Sorted table + output->display( input = it_so_sec name = `it_so_sec` ). + + "Hashed table + output->display( input = it_ha_sec name = `it_ha_sec` ). + + output->next_section( `55) Directly modifying recently read table lines` ). + + "READ TABLE + "Reading table line into target area (field symbol) + READ TABLE it_so_sec ASSIGNING FIELD-SYMBOL() INDEX 1. + "Directly modifying an individual component value and + "the entire line (except the key values in sorted/hashed tables) + -c = 'ABC'. + = VALUE #( BASE d = 'DEF' ). + + "Table expressions + it_st[ 1 ]-c = 'GHI'. "Individual component + it_st[ 1 ] = VALUE #( BASE it_st[ 1 ] b = 'JKL' d = 'MNO' ). + + output->display( input = it_so_sec[ 1 ] name = `it_so_sec[ 1 ]` ). + output->display( input = it_st[ 1 ] name = `it_st[ 1 ]` ). + + output->next_section( `56) Modifying internal table content using MODIFY` ). + "Modifying table lines via key values + "Line that is used to modify internal table + line = VALUE #( a = 2 b = 'zzz' c = 'yyy' ). + + "Standard table + "With the addition FROM wa, the key values in wa determine the line + "to be modified. + "Note: Component d is not specified in "line". The value is + "initialized. + MODIFY TABLE it_st FROM line. + + "Example in which the work area is constructed inline. + "Components b and c not specified. The values are initialized. + MODIFY TABLE it_st FROM VALUE #( a = 3 d = 'xxx' ). + + "Addition TRANSPORTING: Only specified fields are respected + "Note: In case of sorted/hasehd tables, key values cannot be + "specified. + MODIFY TABLE it_st + FROM VALUE #( a = 4 b = '###' c = '###' d = '###' ) + TRANSPORTING b c. + + "Modifying table lines via index + "Note: It is only MODIFY, not MODIFY TABLE as above. + "The following statement modifies the line with number 1 in the + "primary table index. Without the addition TRANSPORTING, the + "entire line is changed. + MODIFY it_st + FROM VALUE #( a = 1 b = 'aaa' c = 'aaa' d = 'aaa' ) + INDEX 1. + + "USING KEY: Determines the table key and thus which table index + "to respect + MODIFY it_so_sec + FROM VALUE #( a = 1 b = 'EEE' c = 'EEE' d = 'EEE' ) + INDEX 1 + USING KEY primary_key + TRANSPORTING c d. + + "Note: Without TRANSPORTING, the statement would overwrite the + "secondary key which is not allowed. + MODIFY it_ha_sec + FROM VALUE #( a = 1 b = 'FFF' c = 'FFF' d = 'FFF' ) + INDEX 1 + USING KEY sec_key_h + TRANSPORTING d. + + output->display( input = it_st name = `it_st` ). + output->display( input = it_so_sec name = `it_so_sec` ). + output->display( input = it_ha_sec name = `it_ha_sec` ). + + output->next_section( `57) Deleting internal table content using DELETE` ). + "Deleting via index + "Primary table index is used implicitly. + DELETE it_st INDEX 1. + + "If USING KEY is not used, INDEX can only be used with index + "tables. If a secondary key is specified, the secondary table + "index is respected. + "The following example has the same effect as above. + DELETE it_st INDEX 1 USING KEY primary_key. + + "Hashed table. The secondary table index is respected. + DELETE it_ha_sec INDEX 1 USING KEY sec_key_h. + + "Deleting multiple lines by specifying an index range + "FROM or TO alone can also be specified + DELETE it_so_sec FROM 2 TO 3. + + "Deleting via keys + "When using the addition FROM wa, the line wa must have a + "compatible type to the table's line type and include key values. + "The first found line with the corresponding keys is deleted. + "If the key is empty, no line is deleted. + DELETE TABLE it_so_sec FROM VALUE #( a = 4 ). + + "Explicitly specifying the table key + DELETE TABLE it_so_sec WITH TABLE KEY a = 1. + + DELETE TABLE it_ha_sec + WITH TABLE KEY sec_key_h COMPONENTS b = 'bbb'. + + "Deleting multiple lines based on conditions + "Note: Specifying the additions USING KEY/FROM/TO is also possible + DELETE it_st WHERE a > 3. + + output->display( input = it_st name = `it_st` ). + output->display( input = it_so_sec name = `it_so_sec` ). + output->display( input = it_ha_sec name = `it_ha_sec` ). + + output->next_section( `Deleting adjacent duplicate entries` ). + output->display( `Original table content (restored before` && + ` each of the following examples)` ). + + it_st = VALUE #( ( a = 1 b = 'BBB' c = '###' d = '###' ) + ( a = 2 b = '###' c = '###' d = '###' ) + ( a = 1 b = '###' c = '###' d = '###' ) + ( a = 3 b = '###' c = '###' d = '###' ) + ( a = 4 b = '###' c = 'CCC' d = '###' ) + ( a = 1 b = 'BBB' c = '###' d = '###' ) + ( a = 2 b = 'BBB' c = '###' d = '###' ) + ( a = 4 b = 'BBB' c = '###' d = '###' ) + ( a = 2 b = 'BBB' c = '###' d = '###' ) + ( a = 3 b = '###' c = '###' d = '###' ) ). + + SORT it_st BY table_line. + + "Filling another table so that the same content above + "is available for the examples below. + it_st2 = it_st. + + output->display( input = it_st2 name = `it_st2` ). + + + output->display( `58) Deleting adjacent duplicates based on` && + ` primary table key` ). + + "Note: Using the primary table key can have unexpected consequences + "if the primary table key is the standard key or if it is empty. + DELETE ADJACENT DUPLICATES FROM it_st2. + + output->display( input = it_st2 name = `it_st2` ). + + it_st2 = it_st. + + output->next_section( `59) Deleting adjacent duplicates by comparing ` && + `all field values` ). + + DELETE ADJACENT DUPLICATES FROM it_st2 COMPARING ALL FIELDS. + + output->display( input = it_st2 name = `it_st2` ). + + it_st2 = it_st. + + output->next_section( `60) Deleting adjacent duplicates by comparing ` && + `specific field values` ). + + DELETE ADJACENT DUPLICATES FROM it_st2 COMPARING a c. + + output->display( input = it_st2 name = `it_st2` ). + + it_st2 = it_st. + + output->next_section( `61) Deleting adjacent duplicates by using a` && + ` table key` ). + + "In this case, the result is the same as in the first example. + DELETE ADJACENT DUPLICATES FROM it_st2 USING KEY primary_key. + + output->display( input = it_st2 name = `it_st2` ). + + output->next_section( `62) Deleting the entire internal table content` ). + + CLEAR it_st. + + "Additionally, FREE releases memory space. + FREE it_st2. + + output->display( input = it_st name = `it_st` ). + output->display( input = it_st2 name = `it_st2` ). + + output->next_section( `Excursions` ). + output->display( `63) Secondary table keys and hashed tables` ). + + "Declaring a hashed table + DATA hashed_tab + TYPE HASHED TABLE OF zdemo_abap_tab1 + WITH UNIQUE KEY primary_key COMPONENTS key_field + WITH NON-UNIQUE SORTED KEY sec_key COMPONENTS char1 char2. + + "Retrieving data to work with + SELECT * FROM zdemo_abap_tab1 INTO TABLE @hashed_tab UP TO 3 ROWS. + + "Integer table to display the table index + DATA int_itab TYPE TABLE OF i. + + "Note: There is no primary table index in hashed tables. + LOOP AT hashed_tab INTO DATA(hwa) USING KEY primary_key. + APPEND sy-tabix TO int_itab. + ENDLOOP. + + output->display( input = int_itab name = `int_itab` ). + + CLEAR int_itab. + + "Demonstrating the secondary table index when using + "the secondary key + LOOP AT hashed_tab INTO DATA(hwa2) USING KEY sec_key. + APPEND sy-tabix TO int_itab. + ENDLOOP. + + output->display( input = int_itab name = `int_itab` ). + + "Retrieving a table line via index access to the secondary index + "of the sorted secondary key + DATA(line_of_ht) = hashed_tab[ KEY sec_key INDEX 2 ]. + + output->display( input = line_of_ht name = `line_of_ht` ). + + output->next_section( `64) Empty keys in internal table created inline` ). + "This example visualizes the fact that when using an inline + "construction like INTO TABLE @DATA(itab) in SELECT statements, the + "resulting table has an empty table key. Here, the key information + "is retrieved using RTTI. The output shows the key information: + "the information on the first internal table includes the key as + "specified (key_field as the primary key, non-unique - since + "key_kind is U and is_unique is not flagged. The result for the + "other internal table shows that there is no key name at all and + "key_kind is E (= empty). + + "An internal table representing an existing table having table keys + "defined in contrast to an internal table created inline. + DATA it_with_key TYPE TABLE OF zdemo_abap_tab1 + WITH NON-UNIQUE KEY key_field. + + "Retrieving data to work with + SELECT * FROM zdemo_abap_tab1 INTO TABLE @it_with_key UP TO 3 ROWS. + SELECT * FROM zdemo_abap_tab1 INTO TABLE @DATA(it_inline) + UP TO 3 ROWS. + + "Using RTTI to retrieve the key information + DATA(k1) = CAST cl_abap_tabledescr( + cl_abap_typedescr=>describe_by_data( + it_with_key ) + )->get_keys( ). + + + output->display( input = k1 name = `k1` ). + + DATA(k2) = CAST cl_abap_tabledescr( + cl_abap_typedescr=>describe_by_data( + it_inline ) + )->get_keys( ). + + output->display( input = k2 name = `k2` ). + + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_internal_tables.clas.xml b/src/zcl_demo_abap_internal_tables.clas.xml new file mode 100644 index 0000000..5448460 --- /dev/null +++ b/src/zcl_demo_abap_internal_tables.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_INTERNAL_TABLES + E + ABAP cheat sheet: Internal tables + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_objects.clas.abap b/src/zcl_demo_abap_objects.clas.abap new file mode 100644 index 0000000..64a20f8 --- /dev/null +++ b/src/zcl_demo_abap_objects.clas.abap @@ -0,0 +1,1001 @@ +*********************************************************************** +* +* ABAP cheat sheet: ABAP object orientation +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntactical options and concepts related +* to ABAP object orientation as outlined in the related ABAP cheat sheet. +* - The CCIMP include (local types tab in ADT) includes multiple local +* classes to support the example. +* - A separate global interface is used to demonstrate working with +* interfaces: zdemo_abap_objects_interface +* - Another global class is used to demonstrate the concept of friendship: +* zcl_demo_abap_objects_friend +* - Topics covered: working with objects and components, method redefinition +* in inheritance, working with interfaces, upcast and downcast, concepts +* like factory methods, singleton and abstract classes, using events +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP Development Tools (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, check the notes +* included in the class as comments or refer to the respective topic +* in the ABAP Keyword Documentation. +* - Due to the amount of output in the console, the examples include +* numbers (e. g. 1) ..., 2) ..., 3) ...) for the individual example +* sections. Plus, the variable name is displayed in most cases. Hence, +* to easier and faster find the relevant output in the console, just +* search in the console for the number/variable name (STRG+F in the +* console) or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** +"!

ABAP cheat sheet: ABAP Object Orientation

+"! Example to demonstrate concepts related to ABAP object orientation.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_objects DEFINITION + PUBLIC + FINAL + CREATE PUBLIC + GLOBAL FRIENDS zcl_demo_abap_objects_friend. + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun, + zdemo_abap_objects_interface. + ALIASES triple FOR zdemo_abap_objects_interface~triple. + + METHODS: hallo_instance_method, + "Demo method for self-reference me + me_ref_meth EXPORTING e1 TYPE string e2 TYPE string. + + DATA: another_string TYPE string VALUE `I'm just a public string.`. + + CLASS-METHODS: + hallo_static_method. + + CLASS-DATA: string TYPE string, + public_string TYPE string VALUE `I'm a string from a friend's public section. I'm accessible anyway.`. + + PROTECTED SECTION. + CLASS-DATA: protected_string TYPE string VALUE `I'm a string from a friend's protected section.`. + + PRIVATE SECTION. + + CLASS-DATA: + private_string TYPE string VALUE `I'm a string from a friend's private section.`. + +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_OBJECTS IMPLEMENTATION. + + + METHOD hallo_instance_method. + string = |Hallo { sy-uname }. | && + |I'm an instance method of class zcl_demo_abap_objects.|. + ENDMETHOD. + + + METHOD hallo_static_method. + string = |Hallo { sy-uname }. | && + |I'm a static method of class zcl_demo_abap_objects.|. + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `Demo: ABAP Object Orientation` ). + output->display( `Working with objects and components` ). + output->display( `1) Declaring reference variables` ). + + "To create an object, a reference variable must be declared. This + "variable is also necessary for accessing objects, i. e. objects + "are not directly accessed but only via references that "point to" + "those objects. And this reference is stored in the reference + "variables. The example below demonstrate multiple reference + "variables that are created using statements with TYPE REF TO. + "LIKE is also possible. You can also create a type with + "TYPE REF TO. + + DATA: ref1a TYPE REF TO local_class, + ref1b TYPE REF TO local_class, + ref1c LIKE ref1a. + + TYPES: ref_type TYPE REF TO local_class. + DATA: ref1d TYPE ref_type. + + + IF ref1a IS INITIAL + AND ref1b IS INITIAL + AND ref1c IS INITIAL + AND ref1d IS INITIAL. + output->display( `The declared reference variables are initial.` ). + ELSE. + output->display( `One or more of the declared reference ` && + `variables are not initial.` ). + ENDIF. + + output->next_section( `2) Creating objects` ). + + "You create an object in the memory of an application by using the + "instance operator NEW. In doing so, a new instance of a + "class is created and the "address" of the instance is put into the + "reference variable. The # sign means to use the type (TYPE REF TO) + "of the reference variable. You can also omit the explicit + "declaration of a reference variable by declaring a new reference + "variable inline. In this case, the name of the class must be + "placed after NEW. As an alternative to the NEW operator, you can + "also use the older CREATE OBJECT statements. + + DATA ref2a TYPE REF TO local_class. + + ref2a = NEW #( ). + DATA(ref2b) = NEW local_class( ). + + "Alternative syntax. + CREATE OBJECT ref2a. + + + IF ref2a IS INSTANCE OF local_class + AND ref2b IS INSTANCE OF local_class. + output->display( `ref2a and ref2b point to instances ` && + `of the class local_class.` ). + ELSE. + output->display( `One or more of the reference variables ` && + `do not point to instances of the class local_class.` ). + ENDIF. + + + output->next_section( `3) Assigning object references` ). + + "Without an assignment, the reference variable is empty. + "To assign or copy reference variable, use the assignment operator + "=. In the example below, both reference variables have the same + "type. + + DATA: ref3a TYPE REF TO local_class, + ref3b TYPE REF TO local_class. + + ref3a = ref3b. + + IF ref3a = ref3b. + output->display( `ref3b has been assigned to ref3a.` ). + ELSE. + output->display( `ref3b has not been assigned to ref3a.` ). + ENDIF. + + output->next_section( `4) Overwriting object references` ). + + "An object reference is overwritten when a new object is created + "with a reference variable already pointing to an instance. + "The class is implemented in a way that the number of instances + "that are created is counted. In this example, the output is + "just to visualize that the first ref4 is indeed overwritten. + + DATA ref4 TYPE REF TO local_class. + + ref4 = NEW #( ). + + output->display( input = ref4->no_of_instances name = `ref4->no_of_instances` ). + + ref4 = NEW #( ). + + output->display( input = ref4->no_of_instances name = `ref4->no_of_instances` ). + + output->next_section( `5) Keeping references variables in internal tables` ). + + "The following code shows that the reference variable is + "overwritten in the course of the loop multiple times. + "Since the reference variables are stored in an internal table, the + "current state of the object is not lost once the reference + "variable is overwritten. The difference in the retained state of + "the object is visible in the timestamp und uuid field. The values + "are created in the constructor when an instance is instantiated. + + DATA: ref5 TYPE REF TO local_class, + itab5 TYPE TABLE OF REF TO local_class. + + DO 3 TIMES. + ref5 = NEW #( ). + itab5 = VALUE #( BASE itab5 ( ref5 ) ). + ENDDO. + + output->display( input = itab5 name = `itab5` ). + + output->next_section( `6) Clearing object references` ). + + "Use CLEAR statements to explicitly clear a reference variable. + "Since objects use up space in the memory, they should be cleared + "if they are no longer needed. Actually, the garbage collector + "takes over this task automatically, i. e. all objects without any + "reference are cleared and the memory space is released. + + DATA ref6 TYPE REF TO local_class. + + ref6 = NEW #( ). + + CLEAR ref6. + + IF ref6 IS INITIAL. + output->display( `ref6 is initial.` ). + ELSE. + output->display( `ref6 is not initial.` ). + ENDIF. + + output->next_section( `7) Accessing and using attributes` ). + + "Instance attributes are accessed using the object component + "selector -> via a reference variable. Visible static attributes + "are accessed using the class component selector => via the class + "name. You can also declare data objects and types by referring + "to those attributes. + + DATA ref7 TYPE REF TO local_class. + ref7 = NEW #( ). + + "Instance + static attribute from individual objects. + DATA(obj_instance_attr) = ref7->num_inst. + DATA(obj_static_attr_obj) = ref7->num_stat. + + "Static attributes + DATA(class_static_attr) = local_class=>num_stat. + + "Data objects and types whose type definitions can be based on + "static class attributes + DATA some_int LIKE local_class=>num_stat. + DATA some_other_int TYPE local_class=>type_i. + TYPES int_type TYPE local_class=>type_i. + + output->display( input = obj_instance_attr name = `obj_instance_attr` ). + output->display( input = obj_static_attr_obj name = `obj_static_attr_obj` ). + output->display( input = class_static_attr name = `class_static_attr` ). + + output->next_section( `8) Calling static and instance methods` ). + + "Similar to accessing attributes, instance methods are called + "using -> via a reference variable. Static methods are called + "using => via the class name. When used within the class in which + "it is declared, the static method can also be called without + "class_name=>.... You might also see method calls with CALL + "METHOD statements which are not used here. When methods are + "called, the parameters must be specified within the parentheses. + "If methods are within the class where they are called, a class + "specification is not needed. In the example below, the methods + "have no parameters defined, hence, there is no specification + "within the parentheses. The methods just change the value of + "a public static variable. + + "Instance methods + DATA(ref8) = NEW zcl_demo_abap_objects( ). + ref8->hallo_instance_method( ). + + output->display( input = string name = `string` ). + + "Static methods + lcl_demo=>hallo_static_ext( ). + + output->display( input = lcl_demo=>string name = `lcl_demo=>string` ). + + "If methods are within the class where they are called, + "the class name can be omitted. + zcl_demo_abap_objects=>hallo_static_method( ). + + hallo_static_method( ). + + output->display( input = string name = `string` ). + + output->next_section( `9) Calling methods: Examples` && + ` with importing parameters` ). + + "The example shows method calls. The methods used have only one or + "two importing parameters. + "One importing parameter: + "- Note that you export the values to the method (which has + " importing parameters), hence, the method call includes + " EXPORTING. + "- If the method has only one importing parameter, you can omit the + " explicit assignment of the value to the parameter and just specify + " the value that you want to pass. The specification of EXPORTING + " can be omitted, too. + "- Hence, the first three method calls do all the same. + "Two importing parameters: + "- All mandatory parameters must be specified. + "- Also here, the specification of EXPORTING can be omitted. + "- The last method includes an optional parameter. In this case, it + " is of type i. Hence, its value remains initial ('0') since it is + " not specified. + "To keep the code lean, only static methods are covered. + + "Method with one importing parameter. + lcl_demo=>powers_of_two( 4 ). + + output->display( input = lcl_demo=>calc_result name = `lcl_demo=>calc_result` ). + + lcl_demo=>powers_of_two( i_pow = 5 ). + + output->display( input = lcl_demo=>calc_result name = `lcl_demo=>calc_result` ). + + lcl_demo=>powers_of_two( EXPORTING i_pow = 6 ). + + output->display( input = lcl_demo=>calc_result name = `lcl_demo=>calc_result` ). + + "Method with two importing parameters + lcl_demo=>addition( i_add1 = 1 i_add2 = 4 ). + + output->display( input = lcl_demo=>calc_result name = `lcl_demo=>calc_result` ). + + lcl_demo=>addition_optional( i_add_mand = 1 ). + + output->display( input = lcl_demo=>calc_result name = `lcl_demo=>calc_result` ). + + output->next_section( `10) Calling methods: Examples ` && + `with exporting parameters` ). + + "Note: The methods have exporting parameters defined in the signature, + "hence, when calling the method, the ABAP word IMPORTING must be used to + "address the values. + "In the first method call below, the variable that holds the imported + "value is declared inline. It receives the type automatically. + "The second method below has two importing parameters and one exporting + "parameter (which is actually the result of a calculation). If a method + "has, for example, importing and exporting parameters but you do not + "want to take the exporting parameters into your program, you can write + "the method call as though the method had only importing parameters. + "Likewise, if a method has a single obligatory importing parameter and + "several optional parameters and you do not want to specify the optional + "parameters, you can write the method call as if the method had only one + "importing parameter. + + lcl_demo=>exporting_hallo( IMPORTING text = DATA(hallo) ). + + lcl_demo=>subtraction( EXPORTING i_sub1 = 10 i_sub2 = 7 + IMPORTING e_sub_result = DATA(subtraction_result) ). + + output->display( input = hallo name = `hallo` ). + output->display( input = subtraction_result name = `subtraction_result` ). + + output->next_section( `11) Calling methods: Example with changing parameter` ). + + "Changing parameters define one or multiple parameters that can + "be both imported and exported. They should be reserved for + "changing an existing local variable and value. + + DATA num TYPE decfloat34 VALUE '144'. + + lcl_demo=>square_root( CHANGING i_sqr = num ). + + output->display( input = num name = `num` ). + + output->next_section( `12) Calling methods: Examples with returning parameters` ). + + "Methods having a returning parameter are called functional methods. + "Returning parameters are preferable to exporting parameters since they + "not only make the call shorter, they also allow method chaining and + "they do not require the use of temporary variables because they can be + "used in conjunction with other statements. + "Functional methods can be called directly from within various + "expressions (e. g. logical expressions with IF) without temporarily + "storing values. + "The use of receiving parameters is only possible for standalone method + "calls and not for functional method calls. + "The code below also includes an example for method chaining. Here, the + "global class cl_abap_random_int is used with which random integers can + "be created. + + DATA(mult_result) = lcl_demo=>multiplication( i_mult1 = 4 + i_mult2 = 5 ). + + output->display( input = mult_result name = `mult_result` ). + + "Comparing a method having exporting parameters doing the same. + lcl_demo=>multiplication_exp_param( EXPORTING i_multa = 5 + i_multb = 6 + IMPORTING e_mult_result = DATA(mult_res_exp) ). + + output->display( input = mult_res_exp name = `mult_res_exp` ). + + "Example with a logical expression + IF lcl_demo=>multiplication( i_mult1 = 5 i_mult2 = 3 ) < 20. + output->display( |The value is lower than 20.| ). + ELSE. + output->display( |The value is greater than 20.| ). + ENDIF. + + "Receiving parameter + lcl_demo=>multiplication( EXPORTING i_mult1 = 10 + i_mult2 = 11 + RECEIVING r_mult_result = DATA(res_received) ). + + output->display( input = res_received name = `res_received` ). + + "Example for method chaining using a global class. + DATA(random_no1) = cl_abap_random_int=>create( )->get_next( ). + + "Specifying the optional min and max importing parameters. + DATA(random_no2) = cl_abap_random_int=>create( seed = cl_abap_random=>seed( ) + min = 1 + max = 10 )->get_next( ). + + "Using method chaining as above saves the extra declaration + "of variables. + DATA(ref_randnom_no) = cl_abap_random_int=>create( seed = cl_abap_random=>seed( ) + min = 20 + max = 30 ). + + DATA(random_no3) = ref_randnom_no->get_next( ). + + output->display( input = random_no1 name = `random_no1` ). + output->display( input = random_no2 name = `random_no2` ). + output->display( input = random_no3 name = `random_no3` ). + + output->next_section( `13) Calling methods: Examples with error handling` ). + + "The examples show two method calls for a method that includes a + "raising parameter. For this method, a class-based exception is + "specified. The exception is raised for the second method + "call. The third method call just gives + "a rough idea on raising exceptions: The current time is checked + "and if it is currently a certain time of the day, an exception + "is raised. + + "Method with raising parameter (class-based exception) + DATA(div_result1) = lcl_demo=>division( i_div1 = 5 + i_div2 = 2 ). + + IF lcl_demo=>string IS INITIAL. + output->display( input = div_result1 name = `div_result1` ). + ELSE. + output->display( |Calculation error: { lcl_demo=>string }| ). + ENDIF. + + DATA(div_result2) = lcl_demo=>division( i_div1 = 1 i_div2 = 0 ). + IF lcl_demo=>string IS INITIAL. + output->display( input = div_result2 name = `div_result2` ). + ELSE. + output->display( |Calculation error: { lcl_demo=>string }| ). + ENDIF. + + "Method with RAISING addition (class-based exceptions) + TRY. + lcl_demo=>check_daytime( + EXPORTING time = cl_abap_context_info=>get_system_time( ) + IMPORTING greetings = DATA(greets) ). + CATCH cx_afternoon. + DATA(subrc) = 11. + CATCH cx_night. + subrc = 33. + ENDTRY. + + output->display( input = greets name = `greets` ). + + output->next_section( `14) Constructors` ). + + "Constructors cannot be explicitly called like other methods. + "The examples demonstrate instance and static constructors. + "The first three method calls show instance constructors. The + "implementation of the instance constructor includes several things. + "Among them, getting a time stamp and a uuid, counting the number of + "instances, updating a string, and carrying out a division. The output + "also shows the effect of calling the static constructor. Check the + "string in the field stat_text and see that it has not changed compared + "to the instance attribute in_text. The third method call shows that an + "instance cannot be created if an error occurs in the instance + "constructor method (initial reference variable). + "The effect of the static constructor is demonstrated by the fourth + "method call. The value of the variable stat_text has not changed + "compared to the other method calls before when outputting it. The + "value of variable stat_number only changes when calling this particular + "method (it explicitly changes the value of the variable). + + "Instance constructor + TRY. + DATA(ref14a) = NEW lcl_constructors( num1 = 10 num2 = 5 ). + CATCH cx_sy_zerodivide INTO DATA(error). + output->display( input = error->get_text( ) name = `error->get_text( )` ). + ENDTRY. + + output->display( input = ref14a name = `ref14a` ). + + TRY. + DATA(ref14b) = NEW lcl_constructors( num1 = 18 num2 = 6 ). + CATCH cx_sy_zerodivide INTO error. + output->display( input = error->get_text( ) name = `error->get_text( )` ). + ENDTRY. + + output->display( input = ref14b name = `ref14b` ). + + TRY. + DATA(ref14c) = NEW lcl_constructors( num1 = 1 num2 = 0 ). + CATCH cx_sy_zerodivide INTO error. + output->display( |Error with ref14c: { error->get_text( ) }| ). + ENDTRY. + + output->display( input = ref14c name = `ref14c` ). + + "Static constructor + lcl_constructors=>add_1( ). + + output->display( input = lcl_constructors=>stat_text name = `lcl_constructors=>stat_text` ). + output->display( input = lcl_constructors=>stat_number name = `lcl_constructors=>stat_number` ). + + output->next_section( `15) Parameters: Generic types` ). + + "The use of generic types in method signatures is particularly relevant + "for dynamic programming. The code shows various examples of parameters + "typed with DATA and ANY TABLE. In the method implementation, all values + "of the variables are stored in a data reference variable that is + "displayed. + + DATA(int) = 4. + + lcl_demo=>generic_data( EXPORTING i_data = int ). + + output->display( input = lcl_demo=>some_data->* name = `lcl_demo=>some_data->*` ). + + DATA strtab TYPE TABLE OF string. + + strtab = VALUE #( ( `I'm a ` ) ( `string table.` ) ). + + lcl_demo=>generic_data( EXPORTING i_data = strtab ). + + output->display( input = lcl_demo=>some_data->* name = `lcl_demo=>some_data->*` ). + + DATA int_tab TYPE TABLE OF i. + + int_tab = VALUE #( ( 1 ) ( 2 ) ( 3 ) ). + + DATA c_tab TYPE TABLE OF c. + + c_tab = VALUE #( ( 'a' ) ( 'b' ) ( 'c' ) ). + + lcl_demo=>generic_tab( EXPORTING i_anytab = int_tab ). + + output->display( input = lcl_demo=>some_data->* name = `lcl_demo=>some_data->*` ). + + lcl_demo=>generic_tab( EXPORTING i_anytab = c_tab ). + + output->display( input = lcl_demo=>some_data->* name = `lcl_demo=>some_data->*` ). + + output->next_section( `16) Inheritance: Method redefinition` ). + + "The example demonstrates inheritance in a very rudimentary way. + "Class 1 is the superclass of class 2 that inherits from class 1. The + "same is true for class 2 and class 3. Class 3 is defined with the + "addition FINAL, so another class cannot inherit from this one. All + "classes implement or redefine respectively a certain method. In this + "case, it is a method that adapts a string. The redefined methods + "access the method of the superclass by specifying super->.... + + "Class 1 + DATA(ref_inh1) = NEW lcl_class1( ). + + DATA(first_string) = ref_inh1->get_string( ). + + "Class 2 + DATA(ref_inh2) = NEW lcl_class2a( ). + + DATA(second_string) = ref_inh2->get_string( ). + + "Class 3 + DATA(ref_inh3) = NEW lcl_class3a( ). + + DATA(third_string) = ref_inh3->get_string( ). + + output->display( input = first_string name = `first_string` ). + output->display( input = second_string name = `second_string` ). + output->display( input = third_string name = `third_string` ). + + output->next_section( `17) Polymorphism and Casting` ). + + "The ref_pol1 object reference variable is created and points to class + "lcl_class1, i. e. the superclass. The ref_pol2 object reference + "variable points to class lcl_class2, i. e. the subclass of lcl_class1. + "At this stage, both the static type and dynamic type of the object + "reference variable ref_pol1 are the same. Then, the object reference + "variable with the type of the subclass is assigned to this reference + "variable, i. e. an upcast is triggered. The output shows the outcome of + "a method call using this object reference variable before and after the + "upcast. The first method call before the upcast demonstrates that the + "method from the superclass is called. The second method call after the + "upcast shows the polymorphism concept since the method call happens via + "the same object reference variable as before. However, at this stage, + "the reference variable points to another object, i. e. the dynamic type + "of the reference variable is now lcl_class2. Hence, the redefined + "method in the subclass having the same name as the method in the + "superclass is called. It is also shown that the casting might be done + "when creating the object. + + DATA(ref_pol1) = NEW lcl_class1( ). + + DATA(ref_pol2) = NEW lcl_class2a( ). + + DATA(str1) = ref_pol1->get_string( ). + + "Upcast + ref_pol1 = ref_pol2. + + DATA(str2) = ref_pol1->get_string( ). + + "The casting might be done when creating the object + DATA ref_pol_super TYPE REF TO lcl_class1. + + ref_pol_super = NEW lcl_class2a( ). + + output->display( input = str1 name = `str1` ). + output->display( input = str2 name = `str2` ). + + output->next_section( `18) Downcast` ). + + "In this example, the possibility of downcasts are checked, i. e. the + "assignment of a more generic object reference variable to a specific + "one. At the beginning, an internal table is just created for displaying + "purposes. The classes lcl_class2a and lcl_class2b are both subclasses + "of lcl_class1. Various objects with reference to these subclasses are + "created providing a "name" for the objects. Here, some of the objects + "are created separately, some are directly declared when adding them to + "the table. An internal table with reference to the superclass + "lcl_class1 is created. All of the objects are then inserted into this + "internal table. In doing so, an implicit upcast takes place here (it is + "basically the assignment of an object reference variable pointing to + "the subclass to a variable pointing to a superclass). As a next step, + "all objects in the internal table are looped across. In each iteration, + "checks are implemented to find out if downcasts are possible. First, a + "check is implemented using a TRY ... ENDTRY block. This statement checks if + "an object reference variable of lcl_class1 can be cast down to one of + "lcl_class2a. If it is possible, a message is written into a dedicated + "field of the display table. Plus, a method is called that is only + "available in lcl_class2a. Note: The method just returns a random + "number. The return value is written to the display table, too. If the + "downcast is not possible, a message is written to the table, too. The + "second check is implemented in a similar way, however, the check is + "implemented using the predicate expression IS INSTANCE OF. Using this + "syntax, the code gets leaner while achieving the same as using a TRY ... + "ENDTRY block without handling the cx_sy_move_cast_error error + "separately. + + "Creating an internal table for displaying purposes + TYPES: BEGIN OF dc_check_struc, + object_name TYPE string, + a_check TYPE string, + b_check TYPE string, + a_number TYPE i, + b_number TYPE i, + END OF dc_check_struc. + + DATA dc_check TYPE TABLE OF dc_check_struc. + + "Creating internal table to hold various objects + DATA: obj_itab TYPE TABLE OF REF TO lcl_class1. + + "Creating various objects ... + DATA(oref1) = NEW lcl_class2a( `Object A1` ). + DATA(oref2) = NEW lcl_class2a( `Object A2` ). + + "... and adding them to the internal table. + "Some of the objects are directly declared when assigning them. + obj_itab = VALUE #( ( oref1 ) + ( oref2 ) + ( NEW lcl_class2a( `Object A3` ) ) + ( NEW lcl_class2b( `Object B1` ) ) + ( NEW lcl_class2b( `Object B2` ) ) + ( NEW lcl_class2b( `Object B3` ) ) ). + + "Looping across all objects in the internal table. + LOOP AT obj_itab ASSIGNING FIELD-SYMBOL(). + "Adding an entry for the display table. + "Here, only the name of the object. + dc_check = VALUE #( BASE dc_check + ( object_name = ->get_obj_name( ) ) ). + + "First check if downcasts are possible using TRY ENDTRY block. + TRY. + "lcl_class1 to be cast down to lcl_class2a + DATA(o_dc_a) = CAST lcl_class2a( ). + + "If downcast works, write a message into a table field. + "Plus, return the number received via the method available + "in lcl_class2a only. + dc_check[ object_name = ->get_obj_name( ) ]-a_check = + `Downcast works.`. + dc_check[ object_name = ->get_obj_name( ) ]-a_number = + o_dc_a->get_number_2a( ). + + CATCH cx_sy_move_cast_error. + "If downcast does not work, write a message into a table field. + dc_check[ object_name = ->get_obj_name( ) ]-a_check = + `Downcast does not work.`. + ENDTRY. + + "Second check if downcasts are possible using IS INSTANCE OF + IF IS INSTANCE OF lcl_class2b. + "If downcast works, write a message into a table field. + "Plus, return the number received via the method available + "in lcl_class2b only. + DATA(o_dc_b) = CAST lcl_class2b( ). + dc_check[ object_name = ->get_obj_name( ) ]-b_check = + `Downcast works.`. + dc_check[ object_name = ->get_obj_name( ) ]-b_number = + o_dc_b->get_number_2b( ). + ELSE. + "If downcast does not work, write a message into a table field. + dc_check[ object_name = ->get_obj_name( ) ]-b_check = + `Downcast does not work.`. + ENDIF. + ENDLOOP. + + output->display( input = dc_check name = `dc_check` ). + + output->next_section( `19) Working with interfaces` ). + + "The code demonstrates multiple things when working with interfaces. + "1. Accessing static methods and attributes using the interface + "component selector ~. + "2. Accessing instance methods and attributes via class references. + "3. Accessing instance methods and attributes via class references and + "using an alias name. + "4. Accessing instance methods and attributes via interface references. + "In this case, the object component selector is used to access the + "components without the interface name. Before making use of interface + "references, an interface reference variable must be created. Interfaces + "cannot be instantiated, i. e. an object cannot be created, however, + "interface references can point to the objects of any class that + "includes the interface so that interface components (and only them) can + "be accessed via the variable. The assignment of an object reference to + "an interface references called upcast (or widening cast). + "The other way round, i. e. the assignment of an interface reference to + "an object reference, is also possible. This concept is called downcast + "(or narrowing cast). However, this assignment can be problematic since + "a successful assignment is dependent on whether the object the + "interface reference points to is actually an object of the implementing + "class. If this is not the case, a runtime error occurs. A downcast can + "be done using the casting operator CAST. In older code, you might + "stumble on the operator ?= for downcasting. To prevent a runtime + "error, you can implement a check using the statement IS INSTANCE OF. + + "Accessing static method and attribute + DATA(res_halve) = zdemo_abap_objects_interface~halve( 10 ). + + DATA(intro_static) = zdemo_abap_objects_interface~stat_str. + + "Access instance method and attribute ... + "... via class reference. + DATA(ref_cl) = NEW zcl_demo_abap_objects( ). + + DATA(res_double1) = ref_cl->zdemo_abap_objects_interface~double( 5 ). + + DATA(intro_inst1) = ref_cl->zdemo_abap_objects_interface~in_str. + + "Using alias name + DATA(res_triple) = ref_cl->triple( 100 ). + + DATA(intro_inst4) = ref_cl->zdemo_abap_objects_interface~in_str. + + "... via interface reference. + DATA ref_if2 TYPE REF TO zdemo_abap_objects_interface. + + DATA ref_cl2 TYPE REF TO zcl_demo_abap_objects. + + "Upcast (1): Convert object references to an interface reference + ref_if2 = NEW zcl_demo_abap_objects( ). + + DATA(res_double2) = ref_if2->double( 10 ). + + DATA(intro_inst2) = ref_if2->in_str. + + ref_cl2 = NEW #( ). + + "Upcast (2) + ref_if2 = ref_cl2. + + DATA(res_upcast) = ref_if2->double( 50 ). + + DATA(intro_inst_up) = ref_if2->in_str. + + "Downcast + DATA(ref_cl_down) = NEW zcl_demo_abap_objects( ). + + IF ref_if2 IS INSTANCE OF zcl_demo_abap_objects. + ref_cl_down = CAST #( ref_if2 ). + + "Old syntax: + "ref_if2 ?= ref_cl_down . + + DATA(res_downcast) = ref_cl_down->zdemo_abap_objects_interface~double( 100 ). + + DATA(intro_inst_down) = ref_cl_down->zdemo_abap_objects_interface~in_str. + ELSE. + output->display( `Downcast not possible.` ). + ENDIF. + + output->display( input = intro_static name = `intro_static` ). + output->display( input = res_halve name = `res_halve` ). + output->display( input = intro_inst1 name = `intro_inst1` ). + output->display( input = res_double1 name = `res_double1` ). + output->display( input = intro_inst4 name = `intro_inst4` ). + output->display( input = res_triple name = `res_triple` ). + output->display( input = intro_inst2 name = `intro_inst2` ). + output->display( input = res_double2 name = `res_double2` ). + output->display( input = intro_inst_up name = `intro_inst_up` ). + output->display( input = res_upcast name = `res_upcast` ). + output->display( input = intro_inst_down name = `intro_inst_down` ). + output->display( input = res_downcast name = `res_downcast` ). + + output->next_section( `20) Using a factory method in a singleton class` ). + + "The example demonstrates a factory method in a singleton class. An + "instance is tried to be created three times. The factory method is + "implemented in a way that prevents the creation of more than one + "instance of the class. Hence, the result of all three method call shows + "the same values (the time stamp that is set initially and the number of + "instances that is always one). + + DATA: obj1 TYPE REF TO lcl_singleton, + obj2 LIKE obj1. + + "Getting an instance of the class lcl_singleton + obj1 = lcl_singleton=>factory_method( ). + + "Setting a time stamp + obj1->set_timestamp( ). + + "Getting time stamp and the overall number of instances of the class + DATA(timestamp) = obj1->get_timestamp( ). + DATA(no_of_instances) = lcl_singleton=>no_of_instances. + + output->display( input = timestamp name = `timestamp` ). + output->display( input = no_of_instances name = `no_of_instances` ). + + "Trying to get another instance + obj2 = lcl_singleton=>factory_method( ). + + "Getting time stamp and the overall number of instances of the class + timestamp = obj2->get_timestamp( ). + no_of_instances = lcl_singleton=>no_of_instances. + + output->display( input = timestamp name = `timestamp` ). + output->display( input = no_of_instances name = `no_of_instances` ). + + "Trying to get another instance + DATA(obj3) = lcl_singleton=>factory_method( ). + + "Getting time stamp and the overall number of instances of the class + timestamp = obj2->get_timestamp( ). + + no_of_instances = lcl_singleton=>no_of_instances. + + output->display( input = timestamp name = `timestamp` ). + output->display( input = no_of_instances name = `no_of_instances` ). + + output->next_section( `21) Factory method in an abstract class` ). + + "The example demonstrates a factory method in an abstract class. An + "instance is tried to be created two times. The factory method is + "implemented in a way that prevents the creation of an instance if a + "certain condition is not met. In this simplistic example, the creation + "is only allowed if the value '1' is passed to the factory method. The + "second instance creation fails on purpose. + + output->display( `First try: inst_1` ). + + TRY. + DATA(inst_1) = lcl_abstract=>factory_method( 1 ). + DATA(str_1) = inst_1->return_string( `inst_1` ). + output->display( input = str_1 name = `str_1` ). + CATCH cx_sy_ref_is_initial INTO DATA(error1). + output->display( |Error message: { error1->get_text( ) }| ). + ENDTRY. + + output->display( input = lcl_abstract=>message name = `lcl_abstract=>message` ). + + output->display( `Second try: inst_2` ). + + TRY. + DATA(inst_2) = lcl_abstract=>factory_method( 2 ). + DATA(str_2) = inst_2->return_string( `inst_2` ). + output->display( input = str_2 name = `str_2` ). + CATCH cx_sy_ref_is_initial INTO DATA(error2). + output->display( |Error message: { error2->get_text( ) }| ). + ENDTRY. + + output->display( input = lcl_abstract=>message name = `lcl_abstract=>message` ). + + output->next_section( `22) Friendship: Accessing components of friends` ). + + "Classes can grant friendship to other classes and interfaces to enable + "the access to protected and private components. However, the friendship + "is not reciprocal. If class a grants friendship to class b, class b + "must also explicitly grant friendship to class a if the components + "should be made accessible also the other way round. In this simple example, + "this class has a class declared as a friend. To visualize the concept, + "strings are available in the public, protected and private section + "here. The befriended class can access the strings not only in the public section + "but of all other sections, too. The strings are stored in a string table. The content + "of this string table is retrieved from the befriended class via a method. + + DATA(string_table) = zcl_demo_abap_objects_friend=>get_strings( ). + + output->display( input = string_table name = `string_table` ). + + output->next_section( `23) Self-reference me` ). + + "This example demonstrates the use of the self-reference 'me' in an + "instance method. The method implementation includes a variable of type + "string that has the same name as a variable that is declared in the + "public section of the class. The method has two exporting parameters to + "include both the value of the local variable and the value of the + "equally named variable from the public section. The latter one is + "referred to using the self-reference me within the method implementation. + + DATA(ref_var) = NEW zcl_demo_abap_objects( ). + + ref_var->me_ref_meth( IMPORTING e1 = DATA(string_without_me) + e2 = DATA(string_with_me) ). + + output->display( input = string_without_me name = `string_without_me` ). + output->display( input = string_with_me name = `string_with_me` ). + + output->next_section( `24) Events` ). + + "The example covers the use of instance events. Event handler methods + "are registered for a particular instance. Events are raised in a method + "based on the daytime. Various event handler methods are implemented + "which return a string. + + DATA(ref_events) = NEW lcl_events( ). + + "Registering event handler methods. + SET HANDLER: ref_events->morning_greets + ref_events->afternoon_greets + ref_events->evening_greets + ref_events->night_greets + FOR ref_events. + + "Calling method that raises an event + ref_events->greetings( ). + + output->display( input = ref_events->greets name = `ref_events->greets` ). + + ENDMETHOD. + + + METHOD me_ref_meth. + DATA another_string TYPE string VALUE `I'm a local string.`. + "e1 gets assigned the local string. + e1 = another_string. + "e2 gets assigned the variable from the public section. + e2 = me->another_string. + ENDMETHOD. + + + METHOD triple. + zdemo_abap_objects_interface~in_str = `The result of calling triple (i. e. zdemo_abap_objects_interface~triple) is: `. + r_triple = i_op * 3. + ENDMETHOD. + + + METHOD zdemo_abap_objects_interface~double. + zdemo_abap_objects_interface~in_str = `The result of calling zdemo_abap_objects_interface~double is: `. + r_double = i_op * 2. + ENDMETHOD. + + + METHOD zdemo_abap_objects_interface~halve. + zdemo_abap_objects_interface~stat_str = `The result of calling zdemo_abap_objects_interface~halve is: `. + r_halve = i_op / 2. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_objects.clas.locals_imp.abap b/src/zcl_demo_abap_objects.clas.locals_imp.abap new file mode 100644 index 0000000..ab5f299 --- /dev/null +++ b/src/zcl_demo_abap_objects.clas.locals_imp.abap @@ -0,0 +1,556 @@ +*&--------------------------------------------------------------------* +*& Custom exception classes +*&--------------------------------------------------------------------* + +CLASS cx_afternoon DEFINITION INHERITING FROM cx_static_check. +ENDCLASS. + +CLASS cx_night DEFINITION INHERITING FROM cx_static_check. +ENDCLASS. + +*&--------------------------------------------------------------------* +*& Class to demonstrate various method parameters +*& All formal parameters are passed by reference except the +*& returning parameter. +*&--------------------------------------------------------------------* +CLASS lcl_demo DEFINITION. + PUBLIC SECTION. + CLASS-METHODS: + "No parameters + hallo_static_ext, + + "One importing parameter + powers_of_two IMPORTING i_pow TYPE i, + + "Two importing parameters + "Specifying REFERENCE(p) is optional; a formal parameter + "without VALUE(p) or REFERENCE(p) is REFERENCE(p) by default + addition IMPORTING i_add1 TYPE i + REFERENCE(i_add2) TYPE i, + + "Two importing parameters, one of them is optional. + addition_optional IMPORTING i_add_mand TYPE i + i_add_opt TYPE i OPTIONAL, + + "Importing and exporting parameters + subtraction IMPORTING i_sub1 TYPE i + i_sub2 TYPE i + EXPORTING e_sub_result TYPE i, + + "One exporting parameter + exporting_hallo EXPORTING text TYPE string, + + "Changing parameter + square_root CHANGING i_sqr TYPE decfloat34, + + "Importing and returning parameters + multiplication IMPORTING i_mult1 TYPE i + i_mult2 TYPE i + RETURNING VALUE(r_mult_result) TYPE i, + + "Importing and exporting parameters + "for comparing the signature with method 'multiplication' + multiplication_exp_param IMPORTING i_multa TYPE i + i_multb TYPE i + EXPORTING e_mult_result TYPE i, + + "Includes RAISING + division IMPORTING i_div1 TYPE i + i_div2 TYPE i + RETURNING VALUE(r_div_result) TYPE decfloat34 + RAISING cx_sy_arithmetic_error, + + check_daytime IMPORTING time TYPE t + EXPORTING greetings TYPE string + RAISING cx_afternoon cx_night, + + "Include parameters with generic types + generic_data IMPORTING i_data TYPE data, + generic_tab IMPORTING i_anytab TYPE ANY TABLE. + + CLASS-DATA: calc_result TYPE i, + string TYPE string, + some_data TYPE REF TO data. + +ENDCLASS. + +CLASS lcl_demo IMPLEMENTATION. + + METHOD hallo_static_ext. + string = |Hallo { sy-uname }. | && + |I'm a static method of class lcl_demo.|. + ENDMETHOD. + + METHOD square_root. + i_sqr = sqrt( i_sqr ). + ENDMETHOD. + + METHOD powers_of_two. + calc_result = i_pow * i_pow. + ENDMETHOD. + + METHOD addition. + calc_result = i_add1 + i_add2. + ENDMETHOD. + + METHOD addition_optional. + calc_result = i_add_mand + i_add_opt. + ENDMETHOD. + + METHOD subtraction. + e_sub_result = i_sub1 - i_sub2. + ENDMETHOD. + + METHOD exporting_hallo. + text = |Hallo { sy-uname }. | && |I'm a static method of class lcl_demo with one exporting parameter.|. + ENDMETHOD. + + METHOD multiplication. + r_mult_result = i_mult1 * i_mult2. + ENDMETHOD. + + METHOD multiplication_exp_param. + e_mult_result = i_multa * i_multb. + ENDMETHOD. + + METHOD division. + CLEAR string. + + TRY. + r_div_result = i_div1 / i_div2. + CATCH cx_sy_arithmetic_error INTO DATA(exc). + string = exc->get_text( ). + ENDTRY. + + ENDMETHOD. + + METHOD check_daytime. + CLEAR string. + + "Morning: 5 am to 12 pm + IF time BETWEEN '050001' AND '120000'. + DATA(subrc) = 0. + ENDIF. + + "Afternoon: 12 pm to 5 pm. + IF time BETWEEN '120001' AND '170000'. + subrc = 11. + ENDIF. + + "Evening 5 pm to 9 pm. + "Commented out on purpose to have a time range for OTHERS :) + "IF time BETWEEN '170001' AND '210000'. + " subrc = 22. + "ENDIF. + + "Night: 9 pm to 4 am. + IF time BETWEEN '210001' AND '050000'. + subrc = 33. + ENDIF. + + IF subrc <> 0. + CASE subrc. + WHEN 11. + greetings = |Good afternoon.|. + WHEN 33. + greetings = |Good night.|. + WHEN OTHERS. + greetings = |It's neither morning, afternoon or night. | && + |Hence, wishing you a good evening.|. + ENDCASE. + ELSE. + greetings = |Good morning.|. + ENDIF. + + ENDMETHOD. + + METHOD generic_data. + "A data reference variable is created that has the type of the + "imported variable. Its content is store in the variable + "some_data in the public section to be able to access the content. + CREATE DATA some_data LIKE i_data. + some_data->* = i_data. + ENDMETHOD. + + METHOD generic_tab. + "See implementation of generic_data. + "Here, an internal table is handled. + CREATE DATA some_data LIKE i_anytab. + some_data->* = i_anytab. + ENDMETHOD. + +ENDCLASS. + +*&--------------------------------------------------------------------* +*& Class to demonstrate basics in the global class +*&--------------------------------------------------------------------* + +CLASS local_class DEFINITION. + PUBLIC SECTION. + + METHODS: constructor. + + DATA: num_inst TYPE i, + uuid TYPE sysuuid_x16, + timestamp TYPE timestampl. + + CLASS-DATA: no_of_instances TYPE i READ-ONLY, + num_stat TYPE i VALUE 33. + + CONSTANTS: const_number TYPE i VALUE 11. + + TYPES type_i TYPE i. + +ENDCLASS. + +CLASS local_class IMPLEMENTATION. + METHOD constructor. + "Number of instances of the class are counted. + no_of_instances = no_of_instances + 1. + "Set a time stamp. + GET TIME STAMP FIELD timestamp. + "Increase the number. + num_inst = num_inst + 1. + "Get a random UUID. + TRY. + uuid = cl_system_uuid=>create_uuid_x16_static( ) . + CATCH cx_uuid_error. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +*&--------------------------------------------------------------------* +*& Class to demonstrate events +*&--------------------------------------------------------------------* + +CLASS lcl_events DEFINITION. + PUBLIC SECTION. + DATA: greets TYPE string. + + "Events declaration. + EVENTS: morning, afternoon, evening, night. + + "Event handler methods + METHODS: morning_greets FOR EVENT morning OF lcl_events, + afternoon_greets FOR EVENT afternoon OF lcl_events, + evening_greets FOR EVENT evening OF lcl_events, + night_greets FOR EVENT night OF lcl_events. + + "Method to raise events + METHODS: greetings. +ENDCLASS. + +CLASS lcl_events IMPLEMENTATION. + + METHOD greetings. + + DATA(syst_time) = cl_abap_context_info=>get_system_time( ). + + "Morning: 5 am to 12 pm + IF syst_time BETWEEN '050001' AND '120000'. + RAISE EVENT morning. + + "Afternoon: 12 pm to 5 pm. + ELSEIF syst_time BETWEEN '120001' AND '170000'. + RAISE EVENT afternoon. + + "Evening 5 pm to 9 pm. + ELSEIF syst_time BETWEEN '170001' AND '210000'. + RAISE EVENT evening. + + "Night: 9 pm to 5 am. + ELSEIF syst_time BETWEEN '210001' AND '050000'. + RAISE EVENT night. + ENDIF. + + ENDMETHOD. + + METHOD morning_greets. + greets = |Good morning, { sy-uname }.|. + ENDMETHOD. + + METHOD afternoon_greets. + greets = |Good afternoon, { sy-uname }.|. + ENDMETHOD. + + METHOD evening_greets. + greets = |Good evening, { sy-uname }.|. + ENDMETHOD. + + METHOD night_greets. + greets = |Good night, { sy-uname }.|. + ENDMETHOD. + +ENDCLASS. + +*&--------------------------------------------------------------------* +*& Class to demonstrate constructors +*&--------------------------------------------------------------------* + +CLASS lcl_constructors DEFINITION. + PUBLIC SECTION. + + METHODS: constructor IMPORTING num1 TYPE i + num2 TYPE i RAISING cx_sy_zerodivide. + + DATA: uuid TYPE sysuuid_x16, + in_div_result TYPE i, + in_text TYPE string. + + + CLASS-METHODS: class_constructor, + add_1. + + CLASS-DATA: no_of_instances TYPE i READ-ONLY, + stat_number TYPE i, + stat_text TYPE string. + +ENDCLASS. + +CLASS lcl_constructors IMPLEMENTATION. + + METHOD constructor. + "Get time stamp. + DATA(ts1) = utclong_current( ). + "Provide message. + in_text = |The instance constructor of the class | && + |lcl_constructors was called on { ts1 }.|. + + "Count number of instances. + no_of_instances = no_of_instances + 1. + "Get random UUID. + TRY. + uuid = cl_system_uuid=>create_uuid_x16_static( ) . + CATCH cx_uuid_error. + ENDTRY. + + CLEAR in_div_result. + "Do calculation. + in_div_result = num1 / num2. + ENDMETHOD. + + METHOD class_constructor. + "Set a number. + stat_number = 999. + "Get time stamp. + DATA(ts2) = utclong_current( ). + "Provide message. + stat_text = |The static constructor of the class | && + |lcl_constructors was called on { ts2 } and the | && + |value for the variable 'stat_number' was set to | && + |{ stat_number }.|. + ENDMETHOD. + + METHOD add_1. + stat_number += 1. + ENDMETHOD. + +ENDCLASS. + +*&--------------------------------------------------------------------* +*& Classes to demonstrate inheritance, polymorphism and casting +*&--------------------------------------------------------------------* + +"Class 1 +CLASS lcl_class1 DEFINITION. + + PUBLIC SECTION. + "Note: All methods are purposely included in the public section. + "Otherwise, it cannot be called in the demo's main class. + METHODS: constructor IMPORTING i_obj TYPE string OPTIONAL, + get_string RETURNING VALUE(str) TYPE string, + get_obj_name RETURNING VALUE(obj) TYPE string. + + PRIVATE SECTION. + DATA: obj_name TYPE string. + +ENDCLASS. + +CLASS lcl_class1 IMPLEMENTATION. + + METHOD constructor. + obj_name = i_obj. + ENDMETHOD. + + METHOD get_obj_name. + obj = obj_name. + ENDMETHOD. + + METHOD get_string. + str = `Hallo`. + ENDMETHOD. + +ENDCLASS. + +"Class 2a +CLASS lcl_class2a DEFINITION INHERITING FROM lcl_class1. + + PUBLIC SECTION. + + METHODS: get_string REDEFINITION, + get_number_2a RETURNING VALUE(num) TYPE i.. + +ENDCLASS. + +CLASS lcl_class2a IMPLEMENTATION. + + METHOD get_string. + str = |{ super->get_string( ) }, { sy-uname }!|. + ENDMETHOD. + + METHOD get_number_2a. + num = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 1 max = 100 )->get_next( ). + ENDMETHOD. + +ENDCLASS. + +"Class 2b +CLASS lcl_class2b DEFINITION INHERITING FROM lcl_class1 FINAL. + + PUBLIC SECTION. + + METHODS: get_string REDEFINITION, + get_number_2b RETURNING VALUE(num) TYPE i. + +ENDCLASS. + +CLASS lcl_class2b IMPLEMENTATION. + + METHOD get_string. + str = |{ super->get_string( ) } from lcl_class2b, { sy-uname }!|. + ENDMETHOD. + + METHOD get_number_2b. + num = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 1 max = 100 )->get_next( ). + ENDMETHOD. + +ENDCLASS. + +"Class 3a +CLASS lcl_class3a DEFINITION INHERITING FROM lcl_class2a FINAL. + + PUBLIC SECTION. + + METHODS: get_string REDEFINITION. + +ENDCLASS. + +CLASS lcl_class3a IMPLEMENTATION. + + METHOD get_string. + str = |{ super->get_string( ) } How are you doing?|. + ENDMETHOD. + +ENDCLASS. + +*&--------------------------------------------------------------------* +*& Classes to demonstrate a factory method in a singleton +*& and an abstract class. +*&--------------------------------------------------------------------* + +CLASS lcl_singleton DEFINITION CREATE PRIVATE. + PUBLIC SECTION. + + METHODS: constructor, + "Methods for setting and getting a time stamp. + get_timestamp RETURNING VALUE(res_timestamp) + TYPE timestampl, + set_timestamp. + + CLASS-METHODS: + "Factory method that returns an instance of the class. + factory_method RETURNING VALUE(res_instance) + TYPE REF TO lcl_singleton. + + CLASS-DATA: "Holds the number of overall instances. + no_of_instances TYPE i READ-ONLY. + + PRIVATE SECTION. + CLASS-DATA: obj TYPE REF TO lcl_singleton. + + DATA: timestamp TYPE timestampl. + +ENDCLASS. + +CLASS lcl_singleton IMPLEMENTATION. + + METHOD factory_method. + "Checking if an instance of the class already exists. + "An instance should only be created if no instance exists + "to make sure that there is only a single instance overall. + IF obj IS INITIAL. + CREATE OBJECT obj. + ENDIF. + "In case an instance already exists, the existing one is + "always returned. + res_instance = obj. + ENDMETHOD. + + METHOD constructor. + "Counts the number of instances of the class. + no_of_instances = no_of_instances + 1. + ENDMETHOD. + + METHOD get_timestamp. + res_timestamp = timestamp. + ENDMETHOD. + + METHOD set_timestamp. + GET TIME STAMP FIELD timestamp. + ENDMETHOD. + +ENDCLASS. + +CLASS lcl_sub DEFINITION DEFERRED. + +CLASS lcl_abstract DEFINITION ABSTRACT. + + PUBLIC SECTION. + + CLASS-METHODS: factory_method IMPORTING check_num TYPE i + RETURNING VALUE(obj) TYPE REF TO lcl_abstract. + + CLASS-DATA: message TYPE string. + + "Abstract method: There's no implementation in this class. + METHODS: return_string ABSTRACT + IMPORTING i_str TYPE string + RETURNING VALUE(res_string) TYPE string. + +ENDCLASS. + +CLASS lcl_sub DEFINITION INHERITING FROM lcl_abstract. + + PUBLIC SECTION. + + METHODS: return_string REDEFINITION. + +ENDCLASS. + + +CLASS lcl_abstract IMPLEMENTATION. + + METHOD factory_method. + "Purpose of factory method: An instance can only be created + "if a certain condition is met. + CASE check_num. + WHEN 1. + obj = NEW lcl_sub( ). + message = `Great! I was able to create an instance.`. + WHEN OTHERS. + message = `What a pity. I'm not allowed to create an instance.`. + ENDCASE. + ENDMETHOD. + +ENDCLASS. + +CLASS lcl_sub IMPLEMENTATION. + + METHOD return_string. + res_string = |I'm a returned string. | && + |The object reference variable is { i_str }.|. + ENDMETHOD. + +ENDCLASS. diff --git a/src/zcl_demo_abap_objects.clas.xml b/src/zcl_demo_abap_objects.clas.xml new file mode 100644 index 0000000..e770ca1 --- /dev/null +++ b/src/zcl_demo_abap_objects.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_OBJECTS + E + ABAP cheat sheet: ABAP Object Orientation + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_objects_friend.clas.abap b/src/zcl_demo_abap_objects_friend.clas.abap new file mode 100644 index 0000000..8f9e424 --- /dev/null +++ b/src/zcl_demo_abap_objects_friend.clas.abap @@ -0,0 +1,40 @@ +*********************************************************************** +* +* Class for ABAP cheat sheet example +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets in this repository. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** + "!

Class for ABAP cheat sheet example

+ "! The class supports the ABAP cheat sheet on object orientation and demonstrates the concept of friendship. + CLASS zcl_demo_abap_objects_friend DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + CLASS-METHODS get_strings RETURNING VALUE(res_string) TYPE string_table. + + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + +CLASS ZCL_DEMO_ABAP_OBJECTS_FRIEND IMPLEMENTATION. + + METHOD get_strings. + "Getting the strings and put them in the string table + APPEND zcl_demo_abap_objects=>public_string TO res_string. + APPEND zcl_demo_abap_objects=>protected_string TO res_string. + APPEND zcl_demo_abap_objects=>private_string TO res_string. + ENDMETHOD. + +ENDCLASS. diff --git a/src/zcl_demo_abap_objects_friend.clas.xml b/src/zcl_demo_abap_objects_friend.clas.xml new file mode 100644 index 0000000..f0de8f5 --- /dev/null +++ b/src/zcl_demo_abap_objects_friend.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_OBJECTS_FRIEND + E + Class for ABAP cheat sheet example + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_rap_draft_ln_m.clas.abap b/src/zcl_demo_abap_rap_draft_ln_m.clas.abap new file mode 100644 index 0000000..8a9978f --- /dev/null +++ b/src/zcl_demo_abap_rap_draft_ln_m.clas.abap @@ -0,0 +1,506 @@ +*********************************************************************** +* +* RAP BO consumer for a RAP demo scenario +* ABAP EML in use: RAP calculator (managed, draft-enabled RAP BO with +* late numbering +* +* -------------------------- PURPOSE ---------------------------------- +* - This class is the RAP BO consumer for a RAP demo scenario that +* represents a calculator using RAP concepts, i. e. using ABAP EML in +* the context of a managed and draft-enabled RAP business object with +* RAP late numbering to carry out simple calculations. Here, a RAP BO +* instance consists of a calculation ID (which is the key that is finally +* set not until the RAP save sequence), two operands (having integer +* values), the arithmetic operator and the result plus other +* draft-related fields. +* - Underlying data model: Consists of a root entity alone. +* The BDEF defines the behavior for this entity. The definitions in the +* BDEF determine which methods must be implemented in the ABAP behavior +* pool (ABP). Note that the view consists many annotations for the SAP +* Fiori UI. +* - ABP for this scenario: zbp_demo_abap_rap_draft_m +* +* ----------------------- GETTING STARTED (1) ------------------------- +* ----------------- Using this class as RAP BO consumer --------------- +* +* - Open the class with the ABAP Development Tools (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, check the notes +* included in the class as comments or refer to the respective topic +* in the ABAP Keyword Documentation. +* - Due to the amount of output in the console, the examples include +* numbers (e. g. 1) ..., 2) ..., 3) ...) for the individual example +* sections. Plus, the variable name is displayed in most cases. Hence, +* to easier and faster find the relevant output in the console, just +* search in the console for the number/variable name (STRG+F in the +* console) or use the debugger. +* +* ----------------------- GETTING STARTED (2) ------------------------- +* Using the preview version of an SAP Fiori Elements as RAP BO consumer +* +* Create a service binding: +* 1. Find the service definition ZDEMO_ABAP_RAP_CALC_SD in the imported +* package in Business Services -> Service Definitions. +* 2. Right-click the service definition and choose New Service Binding. +* 3. In the New Service Binding pop-up, make the following entries: +* - Name: ZDEMO_ABAP_RAP_CALC_SB +* - Description: Service binding for demo +* - Binding type: OData V2 - UI +* - Service Definition: ZDEMO_ABAP_RAP_CALC_SD (should be already filled) +* 4. Choose Next. +* 5. Assign a transport request and choose Finish. +* 6. The service binding ZDEMO_ABAP_RAP_CALC_SB is opened. Activate the +* service binding. +* 7. In the Service Version Details section, choose the Publish button +* for the Local Service Endpoint. Once the service has been published, +* you should see ZDEMO_ABAP_RAP_DRAFT_M in the Entity Set and Association +* section. +* 8. Activate the service binding once the service has been published. +* 9. Select ZDEMO_ABAP_RAP_DRAFT_M and choose the Preview button. +* 10. The preview version of an SAP Fiori Elements app is displayed. If +* prompted, provide your credentials. +* 11. The app and the managed, draft-enabled RAP BO can be explored. If no +* columns are displayed, choose the 'Settings' button and select the +* desired columns. +* Choosing the 'Go' button refreshes the list. At first use, there +* might not any entry. You can create an entry choosing the 'Create' +* button. +* The late numbering aspects comes into the picture when you, for +* example, create a new instance, i. e. create a new calculation, and +* you keep a draft version of it instead of saving it to the database. +* The calculation ID which represents the key of the instance has an +* initial value. Only when you save the instance to the database, the +* final key is set. +* +* ----------------------------- NOTE ----------------------------------- +* This simplified example is not a real life scenario and rather +* focuses on the technical side by giving an idea how the communication +* and data exchange between a RAP BO consumer, which is a class +* in this case, and RAP BO provider can work. Additionally, it shows +* how the methods for non-standard RAP BO operations might be +* self-implemented in an ABP. The example is intentionally kept +* short and simple and focuses on specific RAP aspects. For this reason, +* the example might not fully meet the requirements of the RAP BO contract. +* +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +*********************************************************************** +"!

ABAP cheat sheet: ABAP EML in a RAP scenario (draft BO)

+"! Example to demonstrate ABAP EML in the context of a RAP demo scenario (managed and draft-enabled RAP business object with RAP late numbering). +"! The class represents a RAP BO consumer.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_rap_draft_ln_m DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + + CLASS-METHODS: + class_constructor. + +protected section. + PRIVATE SECTION. + CLASS-DATA: + activate_tab TYPE TABLE FOR ACTION IMPORT + zdemo_abap_rap_draft_m~activate, + activate_tab2 TYPE TABLE FOR ACTION IMPORT + zdemo_abap_rap_draft_m~activate, + activate_tab3 TYPE TABLE FOR ACTION IMPORT + zdemo_abap_rap_draft_m~activate, + edit_tab TYPE TABLE FOR ACTION IMPORT + zdemo_abap_rap_draft_m~edit, + read_tab TYPE TABLE FOR READ IMPORT zdemo_abap_rap_draft_m, + f TYPE RESPONSE FOR FAILED zdemo_abap_rap_draft_m, + r TYPE RESPONSE FOR REPORTED zdemo_abap_rap_draft_m, + m TYPE RESPONSE FOR MAPPED zdemo_abap_rap_draft_m. + + CLASS-METHODS: + initialize_dbtabs. + +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_RAP_DRAFT_LN_M IMPLEMENTATION. + + + METHOD class_constructor. + initialize_dbtabs( ). + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `RAP Demo: RAP Calculator Using Managed, ` && + `Draft-Enabled RAP BO (Late Numbering)` ). + output->display( `1) Creating Instances and ` && + `Saving to the database` ). + + "Creating instances; draft indicator %is_draft is enabled + MODIFY ENTITY zdemo_abap_rap_draft_m + CREATE AUTO FILL CID + FIELDS ( num1 arithm_op num2 ) + WITH VALUE #( + ( %is_draft = if_abap_behv=>mk-on + num1 = 1 arithm_op = '+' num2 = 2 ) + ( %is_draft = if_abap_behv=>mk-on + num1 = 2 arithm_op = '*' num2 = 4 ) + ( %is_draft = if_abap_behv=>mk-on + num1 = 3 arithm_op = '-' num2 = 5 ) + ( %is_draft = if_abap_behv=>mk-on + num1 = 1 arithm_op = '/' num2 = 4 ) + ( %is_draft = if_abap_behv=>mk-on + num1 = 2 arithm_op = 'P' num2 = 5 ) ) + FAILED f + REPORTED r + MAPPED m. + + "Displaying responses only if FAILED and REPORTED + "response parameters are not initial + IF f IS NOT INITIAL OR r IS NOT INITIAL. + output->display( `Responses after MODIFY operation` ). + + IF m IS NOT INITIAL. + output->display( input = m name = `m` ). + ENDIF. + + IF f IS NOT INITIAL. + output->display( input = f name = `f` ).. + ENDIF. + + IF r IS NOT INITIAL. + output->display( input = r name = `r` ). + ENDIF. + + ENDIF. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving draft table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time, draftentitycreationdatetime, + draftentitylastchangedatetime + FROM zdemo_abap_draft + ORDER BY id + INTO TABLE @DATA(draft_parent_before_act). + + "Retrieving database table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time + FROM zdemo_abap_tabca + ORDER BY id + INTO TABLE @DATA(db_tab_root_before_act). + + "Filling the derived type for the ACTIVATE method by + "getting %pid values + LOOP AT m-calc + ASSIGNING FIELD-SYMBOL(). + APPEND VALUE #( %pid = -%pid ) + TO activate_tab. + ENDLOOP. + + MODIFY ENTITY zdemo_abap_rap_draft_m + EXECUTE activate AUTO FILL CID WITH activate_tab + MAPPED m + FAILED f + REPORTED r. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving draft table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time, draftentitycreationdatetime, + draftentitylastchangedatetime + FROM zdemo_abap_draft + ORDER BY id + INTO TABLE @DATA(draft_parent_afer_act). + + "Retrieving database table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time + FROM zdemo_abap_tabca + ORDER BY id + INTO TABLE @DATA(db_tab_root_after_act). + + "Displaying entries + output->display( `1a) Draft and database tables before ` && + `ACTIVATE action` ). + output->display( `Draft table before activation` ). + output->display( input = draft_parent_before_act name = `draft_parent_before_act` ). + + + output->display( `Database table before activation` ). + output->display( input = db_tab_root_before_act name = `db_tab_root_before_act` ). + + + output->next_section( `1b) Draft and database tables after ` && + `ACTIVATE action` ). + output->display( `Draft table after activation` ). + output->display( input = draft_parent_afer_act name = `draft_parent_afer_act` ). + + + output->display( `Database table after activation` ). + output->display( input = db_tab_root_after_act name = `db_tab_root_after_act` ). + + +********************************************************************** + + output->next_section( `2) Creating Invalid Instances` ). + + "Purposely creating invalid instances; + "draft indicator %is_draft is enabled + MODIFY ENTITY zdemo_abap_rap_draft_m + CREATE AUTO FILL CID + FIELDS ( num1 arithm_op num2 ) + WITH VALUE #( + ( %is_draft = if_abap_behv=>mk-on + num1 = 1 arithm_op = 'a' num2 = 1 ) "wrong operator + ( %is_draft = if_abap_behv=>mk-on + num1 = 1 arithm_op = '/' num2 = 0 ) "0 division + ( %is_draft = if_abap_behv=>mk-on + num1 = 2 arithm_op = 'P' num2 = 12345 ) ) "arithmetic overflow + FAILED f + REPORTED r + MAPPED m. + + "Displaying responses only if FAILED and REPORTED + "response parameters are not initial. + IF f IS NOT INITIAL OR r IS NOT INITIAL. + output->display( input = `Responses after MODIFY operation` ). + + IF m IS NOT INITIAL. + output->display( input = m name = `m` ). + ENDIF. + + IF f IS NOT INITIAL. + output->display( input = f name = `f` ).. + ENDIF. + + IF r IS NOT INITIAL. + output->display( input = r name = `r` ). + ENDIF. + + ENDIF. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving draft table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time, draftentitycreationdatetime, + draftentitylastchangedatetime + FROM zdemo_abap_draft + ORDER BY id + INTO TABLE @draft_parent_before_act. + + "Retrieving database table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time + FROM zdemo_abap_tabca + ORDER BY id + INTO TABLE @db_tab_root_before_act. + + + "Filling the derived type for the ACTIVATE method by + "getting %pid values; here, another table is filled for later use + LOOP AT m-calc + ASSIGNING FIELD-SYMBOL(). + + APPEND VALUE #( %pid = -%pid ) + TO activate_tab2. + + APPEND VALUE #( %pid = -%pid ) + TO activate_tab3. + ENDLOOP. + + MODIFY ENTITY zdemo_abap_rap_draft_m + EXECUTE activate AUTO FILL CID WITH activate_tab2 + MAPPED m + FAILED f + REPORTED r. + + "Displaying responses only if FAILED and REPORTED + "response parameters are not initial. + IF f IS NOT INITIAL OR r IS NOT INITIAL. + output->display( input = `Responses after MODIFY operation` ). + + IF m IS NOT INITIAL. + output->display( input = m name = `m` ). + ENDIF. + + IF f IS NOT INITIAL. + output->display( input = f name = `f` ).. + ENDIF. + + IF r IS NOT INITIAL. + output->display( input = r name = `r` ). + ENDIF. + + ENDIF. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving draft table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time, draftentitycreationdatetime, + draftentitylastchangedatetime + FROM zdemo_abap_draft + ORDER BY id + INTO TABLE @draft_parent_afer_act. + + "Retrieving database table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time + FROM zdemo_abap_tabca + ORDER BY id + INTO TABLE @db_tab_root_after_act. + + "Displaying entries + output->next_section( `2a) Draft and database tables before ` && + `ACTIVATE action` ). + output->display( `Draft table before activation` ). + output->display( input = draft_parent_before_act name = `draft_parent_before_act` ). + + + output->display( `Database table before activation` ). + output->display( input = db_tab_root_before_act name = `db_tab_root_before_act` ). + + + output->next_section( `2b) Draft and database tables after ` && + `ACTIVATE action` ). + output->display( `Draft table after activation` ). + output->display( input = draft_parent_afer_act name = `draft_parent_afer_act` ). + + + output->display( `Database table after activation` ). + output->display( input = db_tab_root_after_act name = `db_tab_root_after_act` ). + + +********************************************************************** + + output->next_section( `3) Correcting and Updating Invalid Instances` ). + + "Preparing the derived type for the read operation to + "retrieve the field values; the draft indicator is enabled + LOOP AT activate_tab3 ASSIGNING FIELD-SYMBOL(). + + APPEND VALUE #( %pky = -%pky + %is_draft = if_abap_behv=>mk-on + %control-id = if_abap_behv=>mk-on + %control-num1 = if_abap_behv=>mk-on + %control-arithm_op = if_abap_behv=>mk-on + %control-num2 = if_abap_behv=>mk-on + %control-calc_result = if_abap_behv=>mk-on + %control-crea_date_time = if_abap_behv=>mk-on + %control-lchg_date_time = if_abap_behv=>mk-on + ) TO read_tab. + ENDLOOP. + + "Retrieving the entries of the invalid instances + READ ENTITY zdemo_abap_rap_draft_m + ALL FIELDS WITH read_tab + RESULT DATA(result). + + "Correcting and updating the invalid instances + MODIFY ENTITY zdemo_abap_rap_draft_m + UPDATE FROM VALUE #( + FOR wa IN result ( + %pid = wa-%pid + %is_draft = if_abap_behv=>mk-on + num2 = SWITCH #( wa-calc_result + WHEN `Division by 0` THEN 2 + WHEN `Overflow error` THEN 3 ) + arithm_op = SWITCH #( wa-calc_result + WHEN `Wrong operator` THEN '+' ) + %control-num2 = SWITCH #( wa-calc_result + WHEN `Division by 0` THEN if_abap_behv=>mk-on + WHEN `Overflow error` THEN if_abap_behv=>mk-on + ELSE if_abap_behv=>mk-off ) + %control-arithm_op = SWITCH #( wa-calc_result + WHEN `Wrong operator` THEN if_abap_behv=>mk-on + ELSE if_abap_behv=>mk-off ) ) ) + FAILED f + REPORTED r + MAPPED m. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + MODIFY ENTITY zdemo_abap_rap_draft_m + EXECUTE activate AUTO FILL CID WITH activate_tab3 + MAPPED m + FAILED f + REPORTED r. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving draft table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time, draftentitycreationdatetime, + draftentitylastchangedatetime + FROM zdemo_abap_draft + ORDER BY id + INTO TABLE @draft_parent_afer_act. + + "Retrieving database table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time + FROM zdemo_abap_tabca + ORDER BY id + INTO TABLE @db_tab_root_after_act. + + "Displaying entries + output->display( input = `Draft and database tables after ` && + `ACTIVATE action` ). + + output->display( `Draft table after activation` ). + output->display( input = draft_parent_afer_act name = `draft_parent_afer_act` ). + + + output->display( `Database table after activation` ). + output->display( input = db_tab_root_after_act name = `db_tab_root_after_act` ). + + + ENDMETHOD. + + + METHOD initialize_dbtabs. + DELETE FROM zdemo_abap_tabca. + DELETE FROM zdemo_abap_draft. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_rap_draft_ln_m.clas.xml b/src/zcl_demo_abap_rap_draft_ln_m.clas.xml new file mode 100644 index 0000000..9cacfbe --- /dev/null +++ b/src/zcl_demo_abap_rap_draft_ln_m.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_RAP_DRAFT_LN_M + E + ABAP cheat sheet: ABAP EML in a RAP scenario (draft BO) + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_rap_ext_num_m.clas.abap b/src/zcl_demo_abap_rap_ext_num_m.clas.abap new file mode 100644 index 0000000..932e3e6 --- /dev/null +++ b/src/zcl_demo_abap_rap_ext_num_m.clas.abap @@ -0,0 +1,975 @@ +*********************************************************************** +* +* RAP BO consumer for a RAP demo scenario: +* ABAP EML in use: Managed RAP BO with external numbering +* +* -------------------------- PURPOSE ---------------------------------- +* - This class is the RAP BO consumer for a RAP demo scenario that +* demonstrates various RAP BO standard operations and non-standard +* operations using ABAP EML in the context of a managed RAP business +* object with RAP external numbering. +* See also the ABAP for RAP (EML) ABAP cheat sheet. +* - Topics covered: RAP BO operations like create (including a +* determination on save), update, delete, executing an action, validation, +* create-by-association (parent to child), read (root entity), +* read-by-association (parent to child), read (child entity), +* read-by-association (child to parent) +* - Underlying data model: Consists of a root entity and one child entity. +* The BDEF defines the behavior for these two entities which are connected +* via a CDS composition relation. The definitions in the BDEF determine +* which methods must be implemented in the ABAP behavior pool (ABP). +* - ABP for this scenario: zbp_demo_abap_rap_ro_m +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP Development Tools (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, check the notes +* included in the class as comments or refer to the respective topic +* in the ABAP Keyword Documentation. +* - Due to the amount of output in the console, the examples include +* numbers (e. g. 1) ..., 2) ..., 3) ...) for the individual example +* sections. Plus, the variable name is displayed in most cases. Hence, +* to easier and faster find the relevant output in the console, just +* search in the console for the number/variable name (STRG+F in the +* console) or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* This simplified example is not a real life scenario and rather +* focuses on the technical side by giving an idea how the communication +* and data exchange between a RAP BO consumer, which is a class +* in this case, and RAP BO provider can work. Additionally, it shows +* how the methods for non-standard RAP BO operations might be +* self-implemented in an ABP. The example is intentionally kept +* short and simple and focuses on specific RAP aspects. For this reason, +* the example might not fully meet the requirements of the RAP BO contract. +* +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +*********************************************************************** +"!

ABAP cheat sheet: ABAP EML in a RAP scenario (managed BO)

+"! Example to demonstrate ABAP EML in the context of a RAP demo scenario (managed RAP BO with external numbering). +"! The class represents a RAP BO consumer.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_rap_ext_num_m DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + + CLASS-METHODS: + class_constructor. + +protected section. + PRIVATE SECTION. + CLASS-DATA: + failed TYPE RESPONSE FOR FAILED zdemo_abap_rap_ro_m, + reported TYPE RESPONSE FOR REPORTED zdemo_abap_rap_ro_m, + mapped TYPE RESPONSE FOR MAPPED zdemo_abap_rap_ro_m. + CLASS-METHODS: + initialize_dbtabs, + "If there are entries in the response parameters following EML + "requests, they should be processed for displaying purposes. + extract_from_reported RETURNING VALUE(messages) TYPE string_table, + extract_from_failed RETURNING VALUE(errors) TYPE string_table, + fill_db_tab. + +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_RAP_EXT_NUM_M IMPLEMENTATION. + + + METHOD class_constructor. + initialize_dbtabs( ). + ENDMETHOD. + + + METHOD extract_from_failed. + CLEAR errors. + + LOOP AT failed-root ASSIGNING FIELD-SYMBOL(). + + DATA op TYPE string. + + CASE if_abap_behv=>mk-on. + WHEN -%op-%create. + op = `create operation`. + WHEN -%op-%update. + op = `update operation`. + WHEN -%op-%delete. + op = `delete operation`. + WHEN -%op-%assoc-_child. + op = `operation involving the child entity`. + WHEN -%op-%action-multiply_by_2. + op = `executing action multiply_by_2`. + WHEN OTHERS. op = `operation`. + ENDCASE. + + APPEND `Error for instance with ` && + COND #( WHEN -%cid IS NOT INITIAL THEN `%cid = ` + && -%cid + ELSE `key = ` && -key_field ) && + `: Fail cause ` && -%fail-cause && ` for ` && op + && `.` TO errors. + + ENDLOOP. + + IF failed-child IS NOT INITIAL. + LOOP AT failed-child ASSIGNING FIELD-SYMBOL(). + APPEND `Error for child instance with key_field = ` && + -key_field && ` and key_ch = ` && + -key_ch && `: Fail cause ` + && -%fail-cause && `.` TO errors. + ENDLOOP. + ENDIF. + + ENDMETHOD. + + + METHOD extract_from_reported. + CLEAR messages. + + LOOP AT reported-root ASSIGNING FIELD-SYMBOL(). + IF -%global = if_abap_behv=>mk-on. + APPEND -%msg->m_severity && + -%msg->if_t100_dyn_msg~msgv1 TO messages. + ELSE. + APPEND `Message for instance with ` && + COND #( WHEN -%cid IS NOT INITIAL + THEN `%cid = ` && -%cid + ELSE `key = ` && -key_field ) && + `: ` && -%msg->m_severity && ` ` && + -%msg->if_t100_dyn_msg~msgv1 TO messages. + + ENDIF. + ENDLOOP. + + IF reported-child IS NOT INITIAL. + LOOP AT reported-child ASSIGNING FIELD-SYMBOL(). + APPEND `Message for child instance with key_field = ` && + -key_field && ` and key_ch = ` + && -key_ch && `: ` && -%msg->m_severity && + ` ` && -%msg->if_t100_dyn_msg~msgv1 TO messages. + ENDLOOP. + ENDIF. + + ENDMETHOD. + + + METHOD fill_db_tab. + + MODIFY zdemo_abap_rapt1 FROM TABLE @( VALUE #( + ( key_field = 4 + field1 = 'ggg' + field2 = 'hhh' + field3 = 40 + field4 = 41 ) ) ). + + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `RAP Demo: RAP BO Operations Using a Managed ` && + `RAP BO` ). + +********************************************************************** +* +* Create operation +* +********************************************************************** + + output->display( `1) Create operation` ). + + "Adding an entry to the database table to provoke an error for the + "EML create request. + fill_db_tab( ). + +********************************************************************** +* Notes: +* - field4 is purposely not included in the FIELDS list +* - Effect: +* - %control value for field4 is set to if_abap_behv=>mk-off +* - Although the derived type (created inline here) includes a +* value assignment for field4 in an instance, the field value is +* not saved. The initial value is used. +* - The instance with key_field = 4 will not be saved since an entry +* already exists in the database table with the same key. +* - Response parameters are specified to receive information. +* - A COMMIT ENTITIES statement triggers the saving of the instances. +* - The example BDEF includes the definition of a determination on +* save for create operations. In this case, the determination +* adds some text to the value in field2. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_m + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 ) + WITH VALUE #( ( %cid = 'cid1' + key_field = 1 + field1 = 'aaa' + field2 = 'bbb' + field3 = 10 + field4 = 11 ) "Value not considered + ( %cid = 'cid2' + key_field = 2 + field1 = 'ccc' + field2 = 'ddd' + field3 = 20 ) + ( %cid = 'cid3' + key_field = 3 + field1 = 'eee' + field2 = 'fff' + field3 = 30 ) + ( %cid = 'cid4' "Instance to fail + key_field = 4 + field1 = 'error' + field2 = 'error' + field3 = 99 ) ) + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @DATA(tab_root). + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Update operations +* +********************************************************************** + + output->next_section( `2) Update operation` ). + +********************************************************************** +* Notes: +* - The EML request includes a create and an update request. The +* create request is included to have a %cid to refer to for demo +* purposes. This instance has not yet been persisted. +* - The EML statement for the create operation includes the ABAP +* FROM ... (instead of FIELDS ( ... ) WITH ...) for demo purposes. +* Here, the %control values must be set explicitly. +* - The update request purposely excludes field2 so as not to update +* the value of this particular field. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_m + ENTITY root + CREATE FROM VALUE #( + %control-key_field = if_abap_behv=>mk-on + %control-field1 = if_abap_behv=>mk-on + %control-field2 = if_abap_behv=>mk-on + %control-field3 = if_abap_behv=>mk-on + %control-field4 = if_abap_behv=>mk-on + ( %cid = 'cid5' + key_field = 5 + field1 = 'iii' + field2 = 'jjj' + field3 = 50 + field4 = 51 ) ) + UPDATE FIELDS ( field1 field3 field4 ) + WITH VALUE #( + "Update via cid_ref + ( %cid_ref = 'cid5' + field1 = 'up_kkk' + field2 = 'up_lll' "Value not considered + field3 = 500 + field4 = 501 ) + "Updates via key + ( key_field = 1 + field1 = 'up_mmm' + field3 = 100 + field4 = 101 ) + ( key_field = 2 + field1 = 'up_ooo' + field3 = 200 + field4 = 201 ) + ( key_field = 99 "Instance to fail + field1 = 'error' + field3 = 99 + field4 = 99 ) ) + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Delete operation +* +********************************************************************** + + output->next_section( `3) Delete operation` ). + +********************************************************************** +* Notes: +* - The EML request includes a create and an delete request. The +* create request is included to have a %cid to refer to for demo +* purposes. This instance has not yet been persisted. +* - EML statements for delete operations can only be used with the +* ABAP word FROM .... +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_m + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 field4 ) + WITH VALUE #( + ( %cid = 'cid_del' + key_field = 6 + field1 = 'mmm' + field2 = 'nnn' + field3 = 60 + field4 = 61 ) ) + DELETE FROM VALUE #( + "Deletion via %cid_ref + ( %cid_ref = 'cid_del' ) + "Deletions via key + ( key_field = 4 ) + ( key_field = 5 ) + "Instance to fail + ( key_field = 100 ) ) "Key not available + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Action multiply_by_2 +* +********************************************************************** + + output->next_section( `4) Action execution: mutliply_by_2` ). + +********************************************************************** +* Notes: +* - The EML request includes a create request and a request to execute +* an action. The create request is included to have a %cid to refer +* to for demo purposes. This instance has not yet been persisted. +* - EML statements for executing actions can only be used with the +* ABAP word FROM .... +* - As the name implies, the action multiplies field +* values (field3 and field4) by 2 for requested instances. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_m + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 field4 ) + WITH VALUE #( + ( %cid = 'cid_x2' + key_field = 7 + field1 = 'ooo' + field2 = 'ppp' + field3 = 70 + field4 = 71 ) ) + EXECUTE multiply_by_2 FROM VALUE #( + "Executing action via %cid_ref + ( %cid_ref = 'cid_x2' ) + "Executing action via key + ( key_field = 1 ) + ( key_field = 2 ) + ( key_field = 1234 ) ) "Instance to fail + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Create-by-association operation (from root to child entity) +* +********************************************************************** + + output->next_section( `5) Create-by-association operation (from parent to child)` ). + +********************************************************************** +* Notes: +* - The EML request includes a create and create-by-association +* request, i. e. a "deep create". An instance is created for the +* parent entity and, in the same request and based on this +* instance, instances are created for the child entity, too. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_m + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 field4 ) + WITH VALUE #( + ( %cid = 'cid_cba' + key_field = 9 + field1 = 'qqq' + field2 = 'rrr' + field3 = 90 + field4 = 91 ) ) + CREATE BY \_child + FIELDS ( key_ch field_ch1 field_ch2 ) WITH VALUE #( + "CBA operation via %cid_ref + ( %cid_ref = 'cid_cba' + %target = VALUE #( ( %cid = 'cid_ch1' + key_ch = 9 + field_ch1 = 'aaa_ch' + field_ch2 = 99 ) + ( %cid = 'cid_ch2' + key_ch = 10 + field_ch1 = 'bbb_ch' + field_ch2 = 100 ) ) ) + "CBA operation via root key + ( key_field = 1 + %target = VALUE #( ( %cid = 'cid_ch3' + key_ch = 1 + field_ch1 = 'ccc_ch' + field_ch2 = 11 ) + ( %cid = 'cid_ch4' + key_ch = 2 + field_ch1 = 'ddd_ch' + field_ch2 = 22 ) ) ) + ( key_field = 2 + %target = VALUE #( ( %cid = 'cid_ch5' + key_ch = 3 + field_ch1 = 'ccc_ch' + field_ch2 = 33 ) + ( %cid = 'cid_ch6' + key_ch = 4 + field_ch1 = 'ddd_ch' + field_ch2 = 44 ) ) ) + "Instance to fail + ( key_field = 123 + %target = VALUE #( ( %cid = 'cid_ch7' + key_ch = 1 + field_ch1 = 'error' + field_ch2 = 2 ) + ( %cid = 'cid_ch8' + key_ch = 2 + field_ch1 = 'error' + field_ch2 = 3 ) ) ) + ) + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + SELECT FROM zdemo_abap_rapt2 + FIELDS key_field, key_ch, field_ch1, field_ch2 + ORDER BY key_field, key_ch + INTO TABLE @DATA(tab_child). + + output->display( input = tab_root name = `tab_root` ). + output->display( input = tab_child name = `tab_child` ). + + "Displaying response information + IF mapped IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root and child entity)` ). + + output->display( input = mapped name = `mapped` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Validation val +* +********************************************************************** + + output->next_section( `6) Validation val` ). + +********************************************************************** +* Notes: +* - The EML request includes a create request. The validation's +* handler method is implementation in a way that the saving of +* instances is disabled if a field value is not allowed. In this +* example, the value of the integer in field3 shall not exceed 1000. +* Here, the third instance will fail for the validation. +* Consequently, all instances of this request are not saved to the +* database. Either all is ok and will be saved or nothing. +* - Note that the response information for the validation is only +* available in the response parameters of the COMMIT ENTITIES +* statement. Here, the BDEF derived type is +* ... TYPE RESPONSE FOR ... LATE .... +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_m + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 field4 ) + WITH VALUE #( + ( %cid = 'cid_val1' + key_field = 123 + field1 = 'sss' + field2 = 'ttt' + field3 = 1 + field4 = 2 ) + ( %cid = 'cid_val2' + key_field = 456 + field1 = 'uuu' + field2 = 'vvv' + field3 = 3 + field4 = 4 ) + ( %cid = 'cid_val3' + key_field = 789 + field1 = 'www' + field2 = 'xxx' + field3 = 1001 + field4 = 5 ) ) + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES RESPONSES + FAILED DATA(failed_late) + REPORTED DATA(reported_late). + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root and child entity)` ). + + output->display( input = mapped name = `mapped` ). + ENDIF. + + IF failed_late IS NOT INITIAL. + output->display( `Entries in FAILED LATE response parameter` ). + + output->display( input = failed_late name = `failed_late` ). + ENDIF. + + IF reported_late IS NOT INITIAL. + output->display( `Entries in REPORTED LATE response parameter` ). + + output->display( input = reported_late name = `reported_late` ). + ENDIF. + +********************************************************************** +* +* Read operation (root entity) +* +********************************************************************** + + output->next_section( `7) Read operation (root entity)` ). + +********************************************************************** +* Notes: +* - The EML request includes a read request. The EML statement uses +* the ABAP words ALL FIELDS WITH. In this case, as the name implies, +* all field values are retrieved. The %control values for all fields +* are set to if_abap_behv=>mk-on. +* - When using the ABAP words FIELDS ( ... ) WITH and specifying the +* concrete fields to be read, only for those fields %control is +* set accordingly. +* - Filling the parameter for RESULT is mandatory. +********************************************************************** + + READ ENTITIES OF zdemo_abap_rap_ro_m + ENTITY root + ALL FIELDS WITH VALUE #( + ( key_field = 1 ) + ( key_field = 2 ) + ( key_field = 7 ) + ( key_field = 5 ) ) "Instance to fail + RESULT DATA(result) + FAILED failed + REPORTED reported. + + "Displaying the read result + output->display( input = result name = `result` ). + + "Displaying response information + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************* +* +* Read operation (child entity) +* +********************************************************************** + + output->next_section( `8) Read operation (child entity)` ). + +********************************************************************** +* Notes: +* - The EML request includes a read request. The read operation is +* executed on the child entity directly by specifying the alias, as +* it is defined in the BDEF, following the ABAP word ENTITY. +* - All field values are read using the addition ALL FIELDS WITH. +********************************************************************** + + READ ENTITIES OF zdemo_abap_rap_ro_m + ENTITY child + ALL FIELDS WITH VALUE #( + ( key_field = 1 key_ch = 1 ) + ( key_field = 2 key_ch = 4 ) + "Instances to fail + ( key_field = 9 ) + ( key_field = 9 key_ch = 11 ) ) + RESULT DATA(read_ch) + FAILED failed + REPORTED reported. + + "Displaying read result + output->display( input = read_ch name = `read_ch` ). + + + "Displaying response information + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Read-by-association operation (from parent to child) +* +********************************************************************** + + output->next_section( `9) Read-by-association operation (from parent to child)` ). + +********************************************************************** +* Notes: +* - The EML request includes a read-by-association request from the +* parent to the child. +* - All field values are read using the addition ALL FIELDS WITH. +* - Specifying the parameter for RESULT is mandatory. +* - Additionally, the optional association links are retrieved. +********************************************************************** + + READ ENTITIES OF zdemo_abap_rap_ro_m + ENTITY root + BY \_child + ALL FIELDS WITH VALUE #( + ( key_field = 2 ) + ( key_field = 9 ) + ( key_field = 999 ) ) "Instance to fail + RESULT DATA(rba_result) + LINK DATA(association_links) + FAILED failed + REPORTED reported. + + "Displaying read result and association links + output->display( input = rba_result name = `rba_result` ). + output->display( input = association_links name = `association_links` ). + + "Displaying response information + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Read-by-association operation (from child to parent) +* +********************************************************************** + + output->next_section( `10) Read-by-association operation (from child to parent)` ). + +********************************************************************** +* Notes: +* - The EML request includes a read-by-association request from the +* child to the parent. +* - All field values are read using the addition ALL FIELDS WITH. +* - Specifying the parameter for RESULT is mandatory. +* - Additionally, the optional association links are retrieved. +********************************************************************** + + READ ENTITIES OF zdemo_abap_rap_ro_m + ENTITY child + BY \_parent ALL FIELDS WITH VALUE #( + ( key_field = 1 key_ch = 1 ) + ( key_field = 2 key_ch = 4 ) + "Instances to fail + ( key_field = 1 key_ch = 3 ) + ( key_field = 543 key_ch = 1 ) ) + RESULT DATA(rba_parent) + LINK DATA(association_links_parent) + FAILED failed + REPORTED reported. + + "Displaying read result and association links + output->display( input = rba_parent name = `rba_parent` ). + output->display( input = association_links_parent name = `association_links_parent` ). + + "Displaying response information + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Excursion: Read and read-by-association operation using dynamic +* EML statements +* +********************************************************************** + +* output->next_section( `11) Excursion: Read and read-by-association ` && +* `operations using dynamic EML` ). +* DATA: +* op_tab TYPE abp_behv_retrievals_tab, +* read_dyn TYPE TABLE FOR READ IMPORT zdemo_abap_rap_ro_m, +* read_dyn_result TYPE TABLE FOR READ RESULT zdemo_abap_rap_ro_m, +* rba_dyn TYPE TABLE FOR READ IMPORT +* zdemo_abap_rap_ro_m\_child, +* rba_dyn_result TYPE TABLE FOR READ RESULT +* zdemo_abap_rap_ro_m\_child, +* rba_dyn_link TYPE TABLE FOR READ LINK zdemo_abap_rap_ro_m\_child. +* +* read_dyn = VALUE #( +* ( %key-key_field = 1 +* %control = VALUE #( +* field1 = if_abap_behv=>mk-on +* field2 = if_abap_behv=>mk-on +* field3 = if_abap_behv=>mk-on +* field4 = if_abap_behv=>mk-on ) ) +* ( %key-key_field = 2 +* %control = VALUE #( +* field1 = if_abap_behv=>mk-on +* field2 = if_abap_behv=>mk-on +* field3 = if_abap_behv=>mk-on +* field4 = if_abap_behv=>mk-on ) ) ). +* +* rba_dyn = VALUE #( +* ( %key-key_field = 1 +* %control = VALUE #( +* key_field = if_abap_behv=>mk-on +* key_ch = if_abap_behv=>mk-on +* field_ch1 = if_abap_behv=>mk-on +* field_ch2 = if_abap_behv=>mk-on ) ) +* ( %key-key_field = 2 +* %control = VALUE #( +* key_field = if_abap_behv=>mk-on +* key_ch = if_abap_behv=>mk-on +* field_ch1 = if_abap_behv=>mk-on +* field_ch2 = if_abap_behv=>mk-on ) ) ). +* +* op_tab = VALUE #( +* ( op = if_abap_behv=>op-r-read +* entity_name = 'ZDEMO_ABAP_RAP_RO_M' +* instances = REF #( read_dyn ) +* results = REF #( read_dyn_result ) ) +* ( op = if_abap_behv=>op-r-read_ba +* entity_name = 'ZDEMO_ABAP_RAP_RO_M' +* sub_name = '_CHILD' +* full = abap_true +* instances = REF #( rba_dyn ) +* results = REF #( rba_dyn_result ) +* links = REF #( rba_dyn_link ) ) ). +* +* READ ENTITIES OPERATIONS op_tab. +* +* output->display( `Read result (root)` ). +* output->display( input = read_dyn_result name = `read_dyn_result` ). +* output->display( `Read result (read-by-association)` ). +* output->display( input = rba_dyn_result name = `rba_dyn_result` ). +* output->display( `Links` ). +* output->display( input = rba_dyn_link name = `rba_dyn_link` ). + + ENDMETHOD. + + + METHOD initialize_dbtabs. + DELETE FROM zdemo_abap_rapt1. + DELETE FROM zdemo_abap_rapt2. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_rap_ext_num_m.clas.xml b/src/zcl_demo_abap_rap_ext_num_m.clas.xml new file mode 100644 index 0000000..a85fa73 --- /dev/null +++ b/src/zcl_demo_abap_rap_ext_num_m.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_RAP_EXT_NUM_M + E + ABAP cheat sheet: ABAP EML in a RAP scenario (managed BO) + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_rap_ext_num_u.clas.abap b/src/zcl_demo_abap_rap_ext_num_u.clas.abap new file mode 100644 index 0000000..841a481 --- /dev/null +++ b/src/zcl_demo_abap_rap_ext_num_u.clas.abap @@ -0,0 +1,1089 @@ +*********************************************************************** +* +* RAP BO consumer for a RAP demo scenario: +* ABAP EML in use: Unmanaged RAP BO with external numbering +* +* -------------------------- PURPOSE ---------------------------------- +* - This class is the RAP BO consumer for a RAP demo scenario that +* demonstrates various RAP BO standard operations and non-standard +* operations using ABAP EML in the context of an unmanaged RAP business +* object with RAP external numbering. +* See also the ABAP for RAP (EML) ABAP cheat sheet. +* - Topics covered: RAP BO operations like create, update, delete, +* executing actions, instance and global feature control, instance +* authorization, create-by-association (parent to child), read, +* read-by-association (parent to child), read (child entity), +* read-by-association (child to parent) +* - Underlying data model: Consists of a root entity and one child entity. +* The BDEF defines the behavior for these two entities which are connected +* via a CDS composition relation. The definitions in the BDEF determine +* which methods must be implemented in the ABAP behavior pool (ABP). +* - ABP for this scenario: zbp_demo_abap_rap_ro_u +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP Development Tools (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, check the notes +* included in the class as comments or refer to the respective topic +* in the ABAP Keyword Documentation. +* - Due to the amount of output in the console, the examples include +* numbers (e. g. 1) ..., 2) ..., 3) ...) for the individual example +* sections. Plus, the variable name is displayed in most cases. Hence, +* to easier and faster find the relevant output in the console, just +* search in the console for the number/variable name (STRG+F in the +* console) or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* This simplified example is not a real life scenario and rather +* focuses on the technical side by giving an idea how the communication +* and data exchange between a RAP BO consumer, which is a class +* in this case, and RAP BO provider can work. Additionally, it shows +* how the methods for non-standard RAP BO operations might be +* self-implemented in an ABP. The example is intentionally kept +* short and simple and focuses on specific RAP aspects. For this reason, +* the example might not fully meet the requirements of the RAP BO contract. +* +* For demonstration purposes, some of the operations are +* impacted by feature controls and instance authorization as specified +* in the BDEF. +* +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +*********************************************************************** +"!

ABAP cheat sheet: ABAP EML in a RAP scenario (unmanaged BO)

+"! Example to demonstrate ABAP EML in the context of a RAP demo scenario (unmanaged RAP BO with external numbering). +"! The class represents a RAP BO consumer.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_rap_ext_num_u DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + + CLASS-METHODS: + class_constructor. + +protected section. + PRIVATE SECTION. + CLASS-DATA: + failed TYPE RESPONSE FOR FAILED zdemo_abap_rap_ro_u, + reported TYPE RESPONSE FOR REPORTED zdemo_abap_rap_ro_u, + mapped TYPE RESPONSE FOR MAPPED zdemo_abap_rap_ro_u. + CLASS-METHODS: + initialize_dbtabs, + "If there are entries in the response parameters following EML + "requests, they should be processed for displaying purposes. + extract_from_reported RETURNING VALUE(messages) TYPE string_table, + extract_from_failed RETURNING VALUE(errors) TYPE string_table, + fill_db_tab. + +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_RAP_EXT_NUM_U IMPLEMENTATION. + + + METHOD class_constructor. + initialize_dbtabs( ). + ENDMETHOD. + + + METHOD extract_from_failed. + CLEAR errors. + + LOOP AT failed-root ASSIGNING FIELD-SYMBOL(). + + DATA op TYPE string. + + CASE if_abap_behv=>mk-on. + WHEN -%op-%create. + op = `create operation`. + WHEN -%op-%update. + op = `update operation`. + WHEN -%op-%delete. + op = `delete operation`. + WHEN -%op-%assoc-_child. + op = `operation involving the child entity`. + WHEN -%op-%action-multiply_by_2. + op = `executing action multiply_by_2`. + WHEN -%op-%action-multiply_by_3. + op = `executing action multiply_by_3`. + WHEN -%op-%action-set_z. + op = `executing action set_z`. + WHEN OTHERS. op = `operation`. + ENDCASE. + + APPEND `Error for instance with ` && + COND #( WHEN -%cid IS NOT INITIAL THEN `%cid = ` + && -%cid + ELSE `key = ` && -key_field ) && + `: Fail cause ` && -%fail-cause && ` for ` && op + && `.` TO errors. + + ENDLOOP. + + IF failed-child IS NOT INITIAL. + LOOP AT failed-child ASSIGNING FIELD-SYMBOL(). + + APPEND `Error for child instance with ` && + COND #( WHEN -%cid IS NOT INITIAL THEN `%cid = ` + && -%cid + ELSE `key_field = ` && -key_field && + ` and key_ch = ` && -key_ch ) && + `: Fail cause ` && -%fail-cause && ` for operation.` + TO errors. + + ENDLOOP. + ENDIF. + + ENDMETHOD. + + + METHOD extract_from_reported. + CLEAR messages. + + LOOP AT reported-root ASSIGNING FIELD-SYMBOL(). + IF -%global = if_abap_behv=>mk-on. + APPEND -%msg->m_severity && ` ` && + -%msg->if_t100_dyn_msg~msgv1 TO messages. + ELSE. + APPEND `Message for instance with ` && + COND #( WHEN -%cid IS NOT INITIAL + THEN `%cid = ` && -%cid + ELSE `key = ` && -key_field ) && + `: ` && -%msg->m_severity && ` ` && + -%msg->if_t100_dyn_msg~msgv1 TO messages. + + ENDIF. + ENDLOOP. + + IF reported-child IS NOT INITIAL. + LOOP AT reported-child ASSIGNING FIELD-SYMBOL(). + APPEND `Message for child instance with key_field = ` && + -key_field && ` and key_ch = ` + && -key_ch && `: ` && -%msg->m_severity && + ` ` && -%msg->if_t100_dyn_msg~msgv1 TO messages. + ENDLOOP. + ENDIF. + + ENDMETHOD. + + + METHOD fill_db_tab. + + MODIFY zdemo_abap_rapt1 FROM TABLE @( VALUE #( + ( key_field = 4 + field1 = 'fff' + field2 = 'ggg' + field3 = 40 + field4 = 41 ) ) ). + + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `RAP Demo: RAP BO Operations Using an ` && + `Unmanaged RAP BO (External Numbering)` ). + +********************************************************************** +* +* Create operation +* +********************************************************************** + + output->display( `1) Create Operation` ). + + "Adding an entry to the database table to provoke an error for the + "EML create request. + fill_db_tab( ). + +********************************************************************** +* Notes: +* - field4 is purposely not included in the FIELDS list +* - Effect: +* - %control value for field4 is set to if_abap_behv=>mk-off +* - Although the derived type (created inline here) includes a +* value assignment for field4 in an instance, the field value is +* not saved. The initial value is used. +* - The instance with key_field = 4 will not be saved since an entry +* already exists in the database table with the same key. +* - Response parameters are specified to receive information. +* - A COMMIT ENTITIES statement triggers the saving of the instances. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 ) + WITH VALUE #( ( %cid = 'cid1' + key_field = 1 + field1 = 'aaa' + field2 = 'bbb' + field3 = 10 + field4 = 11 ) "Value not respected + ( %cid = 'cid2' + key_field = 2 + field1 = 'ccc' + field2 = 'ddd' + field3 = 20 ) + ( %cid = 'cid3' + key_field = 3 + field1 = 'X' + field2 = 'eee' + field3 = 30 ) + ( %cid = 'cid4' "Instance to fail + key_field = 4 + field1 = 'error' + field2 = 'error' + field3 = 99 ) ) + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @DATA(tab_root). + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Update operations +* +********************************************************************** + + output->next_section( `2) Update Operation` ). + +********************************************************************** +* Notes: +* - The EML request includes a create and an update request. The +* create request is included to have a %cid to refer to for demo +* purposes. This instance has not yet been persisted. +* - The EML statement for the create operation includes the ABAP +* FROM ... (instead of FIELDS ( ... ) WITH ...) for demo purposes. +* Here, the %control values must be set explicitly. +* - The update request purposely excludes field2 so as not to update +* the value of this particular field. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + CREATE FROM VALUE #( + %control-key_field = if_abap_behv=>mk-on + %control-field1 = if_abap_behv=>mk-on + %control-field2 = if_abap_behv=>mk-on + %control-field3 = if_abap_behv=>mk-on + %control-field4 = if_abap_behv=>mk-on + ( %cid = 'cid5' + key_field = 5 + field1 = 'hhh' + field2 = 'iii' + field3 = 50 + field4 = 51 ) ) + UPDATE FIELDS ( field1 field3 field4 ) + WITH VALUE #( + "Update via cid_ref + ( %cid_ref = 'cid5' + field1 = 'up_jjj' + field2 = 'up_kkk' "Value not respected + field3 = 500 + field4 = 501 ) + "Updates via key + ( key_field = 1 + field1 = 'up_lll' + field3 = 100 + field4 = 101 ) + ( key_field = 2 + field1 = 'up_mmm' + field3 = 200 + field4 = 201 ) + ( key_field = 99 "Instance to fail + field1 = 'error' + field3 = 99 + field4 = 99 ) ) + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Delete operation +* +********************************************************************** + + output->next_section( `3) Delete Operation` ). + +********************************************************************** +* Notes: +* - The EML request includes a create and an delete request. The +* create request is included to have a %cid to refer to for demo +* purposes. This instance has not yet been persisted. +* - EML statements for delete operations can only be used with the +* ABAP word FROM .... +* - Note: Instance authorization is defined in the BDEF. In this +* example, the corresponding handler method is implemented in a way +* that disables the deletion of instances if a field has a certain +* value. If field1 has the value 'X', a deletion is disabled. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 field4 ) + WITH VALUE #( + ( %cid = 'cid_del' + key_field = 6 + field1 = 'a' + field2 = 'b' + field3 = 60 + field4 = 61 ) ) + DELETE FROM VALUE #( + "Deletion via %cid_ref + ( %cid_ref = 'cid_del' ) + "Deletions via key + ( key_field = 4 ) + ( key_field = 5 ) + "Instances to fail + ( key_field = 3 ) "Deletion disabled + ( key_field = 100 ) ) "Key not available + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Action multiply_by_2 +* +********************************************************************** + + output->next_section( `4) Executing Action mutliply_by_2` ). + +********************************************************************** +* Notes: +* - The EML request includes a create request and a request to execute +* an action. The create request is included to have a %cid to refer +* to for demo purposes. This instance has not yet been persisted. +* - EML statements for executing actions can only be used with the +* ABAP word FROM .... +* - As the name implies, the action multiplies field +* values (field3 and field4) by 2 for requested instances. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 field4 ) + WITH VALUE #( + ( %cid = 'cid_x2' + key_field = 7 + field1 = 'nnn' + field2 = 'ooo' + field3 = 70 + field4 = 71 ) ) + EXECUTE multiply_by_2 FROM VALUE #( + "Executing action via %cid_ref + ( %cid_ref = 'cid_x2' ) + "Executing action via key + ( key_field = 1 ) + ( key_field = 2 ) + ( key_field = 1234 ) ) "Instance to fail + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Action multiply_by_3 +* +********************************************************************** + + output->next_section( `5) Executing Action mutliply_by_3` ). + +********************************************************************** +* Notes: +* - The EML request includes a create request and a request to execute +* an action. The create request is included to have a %cid to refer +* to for demo purposes. This instance has not yet been persisted. +* - As the name implies, the action multiplies field +* values (field3 and field4) by 3 for requested instances. +* - Note: In the BDEF of this example, this action is defined with +* instance feature control. Here, the action execution is disabled +* if both integer values are 0. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 field4 ) + WITH VALUE #( + ( %cid = 'cid_x3' + key_field = 8 + field1 = 'ppp' + field2 = 'qqq' + field3 = 80 + field4 = 81 ) ) + EXECUTE multiply_by_3 FROM VALUE #( + "Executing action via %cid_ref + ( %cid_ref = 'cid_x3' ) + "Executing action via key + ( key_field = 1 ) + ( key_field = 2 ) + "Instances to fail + ( key_field = 3 ) "Action execution disabled + ( key_field = 1234 ) ) "Key not available + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + + ENDIF. + +********************************************************************** +* +* Action set_z +* +********************************************************************** + + output->next_section( `6) Executing ACTION set_z` ). + +********************************************************************** +* Notes: +* - The EML request includes a create request and a request to execute +* an action. The create request is included to have a %cid to refer +* to for demo purposes. This instance has not yet been persisted. +* - As the name implies, the action sets the value 'Z' for a +* particular field (field2) for requested instances. +* - Note: In the BDEF of this example, this action is defined with +* global feature control. Here, the action execution is disabled +* based on a certain time frame in which you run this example. You +* might want to change the time frame values in the ABP to check the +* effect. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 field4 ) + WITH VALUE #( + ( %cid = 'cid_setz' + key_field = 9 + field1 = 'rrr' + field2 = 'sss' + field3 = 90 + field4 = 91 ) ) + EXECUTE set_z FROM VALUE #( + "Executing action via %cid_ref + ( %cid_ref = 'cid_setz' ) + "Executing action via key + ( key_field = 2 ) ) + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + + ENDIF. + +********************************************************************** +* +* Create-by-Association Operation (from Root to Child Entity) +* +********************************************************************** + + output->next_section( `7) Create-by-Association Operation (from Root to Child Entity)` ). + +********************************************************************** +* Notes: +* - The EML request includes a create and create-by-association +* request, i. e. a "deep create". An instance is created for the +* root entity and, in the same request and based on this root +* instance, instances are created for the child entity, too. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 field4 ) + WITH VALUE #( + ( %cid = 'cid_cba' + key_field = 10 + field1 = 'ttt' + field2 = 'uuu' + field3 = 100 + field4 = 101 ) ) + CREATE BY \_child + FIELDS ( key_ch field_ch1 field_ch2 ) WITH VALUE #( + "CBA operation via %cid_ref + ( %cid_ref = 'cid_cba' + %target = VALUE #( ( %cid = 'cid_ch1' + key_ch = 11 + field_ch1 = 'aaa_ch' + field_ch2 = 110 ) + ( %cid = 'cid_ch2' + key_ch = 12 + field_ch1 = 'bbb_ch' + field_ch2 = 120 ) ) ) + "CBA operation via root key + ( key_field = 1 + %target = VALUE #( ( %cid = 'cid_ch3' + key_ch = 1 + field_ch1 = 'ccc_ch' + field_ch2 = 11 ) + ( %cid = 'cid_ch4' + key_ch = 2 + field_ch1 = 'ddd_ch' + field_ch2 = 22 ) ) ) + ( key_field = 2 + %target = VALUE #( ( %cid = 'cid_ch5' + key_ch = 3 + field_ch1 = 'ccc_ch' + field_ch2 = 33 ) + ( %cid = 'cid_ch6' + key_ch = 4 + field_ch1 = 'ddd_ch' + field_ch2 = 44 ) ) ) + "Instance to fail + ( key_field = 123 + %target = VALUE #( ( %cid = 'cid_ch7' + key_ch = 1 + field_ch1 = 'error' + field_ch2 = 2 ) + ( %cid = 'cid_ch8' + key_ch = 2 + field_ch1 = 'error' + field_ch2 = 3 ) ) ) ) + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + SELECT FROM zdemo_abap_rapt2 + FIELDS key_field, key_ch, field_ch1, field_ch2 + ORDER BY key_field, key_ch + INTO TABLE @DATA(tab_child). + + output->display( input = tab_root name = `tab_root` ). + output->display( input = tab_child name = `tab_child` ). + + "Displaying response information + IF mapped IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root and child entity)` ). + + output->display( input = mapped name = `mapped` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Read operation +* +********************************************************************** + + output->next_section( `8) Read Operation (Root Entity)` ). + +********************************************************************** +* Notes: +* - The EML request includes a read request. The EML statement uses +* the ABAP words ALL FIELDS WITH. In this case, as the name implies, +* all field values are retrieved. The %control values for all fields +* are set to if_abap_behv=>mk-on. +* - When using the ABAP words FIELDS ( ... ) WITH and specifying the +* concrete fields to be read, only for those fields %control is +* set accordingly. +* - Filling the parameter for RESULT is mandatory. +********************************************************************** + + READ ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + ALL FIELDS WITH VALUE #( + ( key_field = 1 ) + ( key_field = 2 ) + ( key_field = 5 ) ) "Instance to fail + RESULT DATA(result) + FAILED failed + REPORTED reported. + + "Displaying the read result and response information + output->display( input = result name = `result` ). + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************* +* +* Read operation (child entity) +* +********************************************************************** + + output->next_section( `9) Read Operation (Child Entity)` ). + +********************************************************************** +* Notes: +* - The EML request includes a read request. The read operation is +* executed on the child entity directly by specifying the alias, as +* it is defined in the BDEF, following the ABAP word ENTITY. +* - All field values are read using the addition ALL FIELDS WITH. +********************************************************************** + + READ ENTITIES OF zdemo_abap_rap_ro_u + ENTITY child + ALL FIELDS WITH VALUE #( + ( key_field = 1 key_ch = 1 ) + ( key_field = 2 key_ch = 4 ) + "Instances to fail + ( key_field = 9 ) + ( key_field = 9 key_ch = 11 ) ) + RESULT DATA(read_ch) + FAILED failed + REPORTED reported. + + "Displaying read result + output->display( input = read_ch name = `read_ch` ). + + "Displaying response information + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Read-by-association operation (from parent to child) +* +********************************************************************** + + output->next_section( `10) Read-by-Association Operation (from Parent to Child)` ). + +********************************************************************** +* Notes: +* - The EML request includes a read-by-association request from the +* parent to the child entity. +* - All field values are read using the addition ALL FIELDS WITH. +* - Specifying the parameter for RESULT is mandatory. +* - Additionally, the optional association links are retrieved. +********************************************************************** + + READ ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + BY \_child + ALL FIELDS WITH VALUE #( + ( key_field = 2 ) + ( key_field = 10 ) + ( key_field = 111 ) ) "Instance to fail + RESULT DATA(rba_result) + LINK DATA(association_links) + FAILED failed + REPORTED reported. + + "Displaying read result and association links + output->display( input = rba_result name = `rba_result` ). + output->display( input = association_links name = `association_links` ). + + "Displaying response information + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Read-by-association operation (from child to parent) +* +********************************************************************** + + output->next_section( `11) Read-by-Association Operation (from Child to Parent)` ). + +********************************************************************** +* Notes: +* - The EML request includes a read-by-association request from the +* child to the parent. +* - All field values are read using the addition ALL FIELDS WITH. +* - Specifying the parameter for RESULT is mandatory. +* - Additionally, the optional association links are retrieved. +********************************************************************** + + READ ENTITIES OF zdemo_abap_rap_ro_u + ENTITY child + BY \_parent ALL FIELDS WITH VALUE #( + ( key_field = 1 key_ch = 1 ) + ( key_field = 2 key_ch = 4 ) + "Instances to fail + ( key_field = 1 key_ch = 3 ) + ( key_field = 543 key_ch = 1 ) ) + RESULT DATA(rba_parent) + LINK DATA(association_links_parent) + FAILED failed + REPORTED reported. + + "Displaying read result and association links + output->display( input = rba_parent name = `rba_parent` ). + output->display( input = association_links_parent name = `association_links_parent` ). + + "Displaying response information + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Excursion: Read and read-by-association operation using dynamic +* EML statements +* +* Notes: +* - If the parameter for FULL is not flagged, only the association +* links are returned. The parameter for RESULT will be empty. +* - Remove the commented out section if you are at least on ABAP +* version 756 in your on-premise system. +********************************************************************** + +* output->next_section( `12) Excursion: Read and read-by-association ` && +* `operations using dynamic EML` ). +* +* DATA: +* op_tab TYPE abp_behv_retrievals_tab, +* read_dyn TYPE TABLE FOR READ IMPORT zdemo_abap_rap_ro_u, +* read_dyn_result TYPE TABLE FOR READ RESULT zdemo_abap_rap_ro_u, +* rba_dyn TYPE TABLE FOR READ IMPORT +* zdemo_abap_rap_ro_u\_child, +* rba_dyn_result TYPE TABLE FOR READ RESULT +* zdemo_abap_rap_ro_u\_child, +* rba_dyn_link TYPE TABLE FOR READ LINK zdemo_abap_rap_ro_u\_child. +* +* read_dyn = VALUE #( +* ( %key-key_field = 1 +* %control = VALUE #( +* field1 = if_abap_behv=>mk-on +* field2 = if_abap_behv=>mk-on +* field3 = if_abap_behv=>mk-on +* field4 = if_abap_behv=>mk-on ) ) +* ( %key-key_field = 2 +* %control = VALUE #( +* field1 = if_abap_behv=>mk-on +* field2 = if_abap_behv=>mk-on +* field3 = if_abap_behv=>mk-on +* field4 = if_abap_behv=>mk-on ) ) ). +* +* rba_dyn = VALUE #( +* ( %key-key_field = 1 +* %control = VALUE #( +* key_field = if_abap_behv=>mk-on +* key_ch = if_abap_behv=>mk-on +* field_ch1 = if_abap_behv=>mk-on +* field_ch2 = if_abap_behv=>mk-on ) ) +* ( %key-key_field = 2 +* %control = VALUE #( +* key_field = if_abap_behv=>mk-on +* key_ch = if_abap_behv=>mk-on +* field_ch1 = if_abap_behv=>mk-on +* field_ch2 = if_abap_behv=>mk-on ) ) ). +* +* output->display( `Result if FULL parameter is ` && +* `not flagged for RBA` ). +* +* op_tab = VALUE #( +* ( op = if_abap_behv=>op-r-read +* entity_name = 'ZDEMO_ABAP_RAP_RO_U' +* instances = REF #( read_dyn ) +* results = REF #( read_dyn_result ) ) +* ( op = if_abap_behv=>op-r-read_ba +* entity_name = 'ZDEMO_ABAP_RAP_RO_U' +* sub_name = '_CHILD' +* full = abap_false +* instances = REF #( rba_dyn ) +* results = REF #( rba_dyn_result ) +* links = REF #( rba_dyn_link ) ) ). +* +* READ ENTITIES OPERATIONS op_tab. +* +* output->display( input = read_dyn_result name = `read_dyn_result` ). +* output->display( input = rba_dyn_result name = `rba_dyn_result` ). +* output->display( input = rba_dyn_link name = `rba_dyn_link` ). +* +* output->display( `Result if FULL parameter is ` && +* `flagged for RBA` ). +* +* op_tab = VALUE #( +* ( op = if_abap_behv=>op-r-read +* entity_name = 'ZDEMO_ABAP_RAP_RO_U' +* instances = REF #( read_dyn ) +* results = REF #( read_dyn_result ) ) +* ( op = if_abap_behv=>op-r-read_ba +* entity_name = 'ZDEMO_ABAP_RAP_RO_U' +* sub_name = '_CHILD' +* full = abap_true +* instances = REF #( rba_dyn ) +* results = REF #( rba_dyn_result ) +* links = REF #( rba_dyn_link ) ) ). +* +* READ ENTITIES OPERATIONS op_tab. +* +* output->display( input = read_dyn_result name = `read_dyn_result` ). +* output->display( input = rba_dyn_result name = `rba_dyn_result` ). +* output->display( input = rba_dyn_link name = `rba_dyn_link` ). + + ENDMETHOD. + + + METHOD initialize_dbtabs. + DELETE FROM zdemo_abap_rapt1. + DELETE FROM zdemo_abap_rapt2. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_rap_ext_num_u.clas.xml b/src/zcl_demo_abap_rap_ext_num_u.clas.xml new file mode 100644 index 0000000..8a82b32 --- /dev/null +++ b/src/zcl_demo_abap_rap_ext_num_u.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_RAP_EXT_NUM_U + E + ABAP cheat sheet: ABAP EML in a RAP scenario (unmanaged BO) + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_sql.clas.abap b/src/zcl_demo_abap_sql.clas.abap new file mode 100644 index 0000000..eb1db8f --- /dev/null +++ b/src/zcl_demo_abap_sql.clas.abap @@ -0,0 +1,1477 @@ +*********************************************************************** +* +* ABAP cheat sheet: ABAP SQL - Working with +* persisted data in database tables +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntactical options for working with +* persisted data in database tables using ABAP SQL as outlined in the +* respective ABAP cheat sheet. +* - Topics covered: reading from database tables using SELECT, changing +* data in database tables using INSERT, UPDATE, MODIFY and DELETE +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP Development Tools (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, check the notes +* included in the class as comments or refer to the respective topic +* in the ABAP Keyword Documentation. +* - Due to the amount of output in the console, the examples include +* numbers (e. g. 1) ..., 2) ..., 3) ...) for the individual example +* sections. Plus, the variable name is displayed in most cases. Hence, +* to easier and faster find the relevant output in the console, just +* search in the console for the number/variable name (STRG+F in the +* console) or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** +"!

ABAP cheat sheet: ABAP SQL

+"! Example to demonstrate working with persisted data in database tables using ABAP SQL.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_sql DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + CLASS-METHODS: + class_constructor. + +protected section. + PRIVATE SECTION. + CLASS-METHODS: select_from_dbtab. + + CLASS-DATA: + struct TYPE zdemo_abap_flsch, + itab TYPE TABLE OF zdemo_abap_flsch, + itab_res TYPE TABLE OF zdemo_abap_carr. + +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_SQL 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: ABAP SQL - Working with persisted data in database tables` ). + output->display( `Using SELECT for Multiple Purposes` ). + output->display( `1) Reading a single row from database table ` && + `into a structure: ... ` ). + output->display( `1a) All fields` ). + + "Note that, although it is optional, a WHERE clause should always be + "specified for performance reasons and to restrict the read result. + "In the following SELECT statements, a simple WHERE condition is + "used to limit the number of found results. + + "Reading into existing structure having the same type. + SELECT SINGLE FROM zdemo_abap_flsch + FIELDS * + WHERE carrid = 'LH' AND connid = '400' + INTO @struct. + + "Alternative syntax (no FIELDS), target variable declared inline + SELECT SINGLE * + FROM zdemo_abap_flsch + WHERE carrid = 'AA' AND connid = '17' + INTO @DATA(struct1a). + + output->display( input = struct name = `struct` ). + output->display( input = struct1a name = `struct1a` ). + + output->next_section( `1b) Selected fields` ). + + SELECT SINGLE carrid, connid, cityfrom, cityto + FROM zdemo_abap_flsch + WHERE carrid = 'AZ' AND connid = '555' + INTO @DATA(struct1b1). + + "Alternative syntax (with FIELDS) + SELECT SINGLE + FROM zdemo_abap_flsch + FIELDS carrid, connid, cityfrom, cityto + WHERE carrid = 'DL' AND connid = '106' + INTO @DATA(struct1b2). + + "When reading a selected set of fields into an existing target + "variable, the CORRESPONDING FIELDS OF addition in the INTO clause + "should be used. Other, not selected fields remain initial. + DATA struct1b3 LIKE struct. + + SELECT SINGLE carrid, connid, cityfrom, cityto + FROM zdemo_abap_flsch + WHERE carrid = 'DL' AND connid = '106' + INTO CORRESPONDING FIELDS OF @struct1b3. + + output->display( input = struct1b1 name = `struct1b1` ). + output->display( input = struct1b2 name = `struct1b2` ). + output->display( input = struct1b3 name = `struct1b3` ). + + output->next_section( `2) Reading mutliple rows into an internal table` ). + + "Reading all fields into an existing internal table + SELECT FROM zdemo_abap_flsch + FIELDS * + WHERE carrid = 'DL' + INTO TABLE @itab. + + "Alternative syntax (no FIELDS), reading of a selected set of + "fields, internal table is declared inline + SELECT carrid, connid, cityfrom, cityto + FROM zdemo_abap_flsch + WHERE carrid = 'AZ' + INTO TABLE @DATA(itab2a). + + "When reading a selected set of fields into an existing target + "variable, the CORRESPONDING FIELDS OF addition in the INTO clause + "should be used. Other, not selected fields remain initial. + DATA itab2b LIKE itab. + + SELECT carrid, connid, cityfrom, cityto + FROM zdemo_abap_flsch + WHERE carrid = 'AZ' + INTO CORRESPONDING FIELDS OF TABLE @itab2b. + + output->display( input = itab name = `itab` ). + output->display( input = itab2a name = `itab2a` ). + output->display( input = itab2b name = `itab2b` ). + + output->next_section( `3) SELECT loop: Sequentially reading multiple ` && + `rows of a database table into a structure` ). + + "In the example below, the individual rows that are read are + "modified before they are appended to an internal table. + + DATA itab3 LIKE itab. + + SELECT FROM zdemo_abap_flsch + FIELDS * "All fields + WHERE carrid = 'DL' + INTO @DATA(struct3). + + "Further processing of the structure if the reading is successful + IF sy-subrc = 0. + "Modification: Converting miles to kilometers + IF struct3-distid = 'MI'. + struct3-distance = struct3-distance * '1.609344'. + struct3-distid = 'KM'. + ENDIF. + + "Appending structure to an internal table + APPEND struct3 TO itab3. + ENDIF. + ENDSELECT. + + output->display( input = itab3 name = `itab3` ). + + output->next_section( `4) Reading into an existing target variable that` && + ` has not a matching type` ). + + "Note: The addition CORRESPONDING FIELDS OF is needed when using + "an existing variable to read data into, otherwise a type + "compatibility issue might arise because the SELECT statement fills + "the variable from left to right beginning with the first + "component. In the example below, the identically named fields have + "a matching type. + + "Creating structure type, structure and internal table. + TYPES: BEGIN OF struc_type, + carrid TYPE zdemo_abap_flsch-carrid, + connid TYPE zdemo_abap_flsch-connid, + cityfrom TYPE zdemo_abap_flsch-cityfrom, + cityto TYPE zdemo_abap_flsch-cityto, + END OF struc_type. + + DATA struc4 TYPE struc_type. + DATA itab4 TYPE TABLE OF struc_type. + + "Reading into a structure that has not a matching type + SELECT SINGLE FROM zdemo_abap_flsch + FIELDS carrid, connid, cityfrom, cityto + WHERE carrid = 'AZ' AND connid = '555' + INTO CORRESPONDING FIELDS OF @struc4. + + "Reading into an internal table that has not a matching type + SELECT FROM zdemo_abap_flsch + FIELDS * + WHERE carrid = 'AZ' + INTO CORRESPONDING FIELDS OF TABLE @itab4. + + output->display( input = struc4 name = `struc4` ). + output->display( input = itab4 name = `itab4` ). + + output->next_section( `Clause Variations and Additions in SELECT Statements` ). + + "SELECT/FROM clause variants + output->display( 'SELECT/FROM clause variants' ). + + output->display( `5) Checking the existence of a row in ` && + `a database table` ). + + "Instead of @abap_true, you could also use 'X' in the example below. + + SELECT SINGLE @abap_true + FROM zdemo_abap_flsch + WHERE carrid = 'AZ' AND connid = '555' + INTO @DATA(exists). + + IF exists = abap_true. + output->display( |A line was found.| ). + ELSE. + output->display( `Nothing found.` ). + ENDIF. + + output->next_section( `6) DISTINCT addition: Removing rows that occur` && + ` more than once in a multiline result set` ). + + "The example shows the comparison of statements with and without + "the use of DISTINCT. When used without DISTINCT, the result + "shows multiple entries whereas the statement with DISTINCT + "filters the duplicates out. + + "DISTINCT addition + SELECT DISTINCT cityfrom + FROM zdemo_abap_flsch + WHERE carrid = 'LH' AND + cityto = 'NEW YORK' + INTO TABLE @DATA(itab6a). + + "Similar statement not using DISTINCT + SELECT cityfrom + FROM zdemo_abap_flsch + WHERE carrid = 'LH' AND + cityto = 'NEW YORK' + INTO TABLE @DATA(itab6b). + + output->display( input = itab6a name = `itab6a` ). + output->display( input = itab6b name = `itab6b` ). + + output->next_section( `7) Setting alias names` ). + + "Example 1: Some fields have an alias name + "Data is read into a target variable declared inline + SELECT FROM zdemo_abap_flsch + FIELDS carrid AS carr, + connid AS conn, + cityfrom AS ctyfr, + cityto + WHERE carrid = 'JL' + INTO TABLE @DATA(itab7a). + + "Example 2: Data is read from a database table into an existing + "table but the line type does not match. The fields also have + "different names (but the same type). Due to the use of alias + "names, the fields are read into the corresponding fields. + TYPES: BEGIN OF struc_type_diff, + carr_id TYPE zdemo_abap_flsch-carrid, + conn_id TYPE zdemo_abap_flsch-connid, + city_from TYPE zdemo_abap_flsch-cityfrom, + city_to TYPE zdemo_abap_flsch-cityto, + END OF struc_type_diff. + + DATA itab7b TYPE TABLE OF struc_type_diff. + + "In the case below, the addition CORRESPONDING FIELDS OF is not + "even necessary. + SELECT FROM zdemo_abap_flsch + FIELDS carrid AS carr_id, + connid AS conn_id, + cityfrom AS city_from, + cityto AS city_to + WHERE carrid = 'AZ' + INTO TABLE @itab7b. + "INTO CORRESPONDING FIELDS OF TABLE @itab7b. + + output->display( input = itab7a name = `itab7a` ). + output->display( input = itab7b name = `itab7b` ). + + "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. + +* output->next_section( `8) Getting data from a database table in ` && +* `another client` ). + + "In this example, the same client as the current one is used to + "guarantee data in the internal table. It is just to visualize + "the syntax. A literal with the 3 digit client number can be + "inserted after USING CLIENT. + "Other variants exist for client specification, e.g. ALL CLIENTS. + +* DATA(clnt) = sy-mandt. +* +* SELECT * +* FROM zdemo_abap_flsch USING CLIENT @clnt +* WHERE carrid = 'JL' +* INTO TABLE @DATA(itab8). +* +* output->display( input = itab8 name = `itab8` ). + + output->next_section( 'INTO clause variants' ). + output->display( `9) Restricting the absolute number of ` && + `table rows to be read` ). + + "Restricting the absolute number of returned table rows + "by specifying a number n in the addition UP TO n ROWS. + "In this case, the addition ORDER BY is also specified (but need not be specified). + "The rows of the hit list are sorted on the database server and only the number of + "sorted rows specified for UP TO n ROWS are passed to the result set. If the addition + "ORDER BY is not specified, n arbitrary rows that meet the WHERE condition are passed + "to the result set. If the ORDER BY clause does not sort the result set uniquely, + "it is not possible to define which rows are in the result set. + "Other examples here do not use the ORDER BY clause. + + SELECT * + FROM zdemo_abap_flsch + WHERE carrid = 'LH' + ORDER BY carrid + INTO TABLE @DATA(itab_up) + UP TO 2 ROWS. + + output->display( input = itab_up name = `itab_up` ). + + output->next_section( `Avoiding the deletion of existing internal` && + ` table lines by ...` ). + + output->display( `10) ... appending read database table rows to ` && + `the result set` ). + "In the example, the existing internal table has the same line type + "as the database table. The internal table from the previous + "example is used to have a table with entries. + + SELECT * + FROM zdemo_abap_flsch + WHERE carrid = 'JL' + APPENDING TABLE @itab_up. + + output->display( input = itab_up name = `itab_up` ). + + output->next_section( `11) ... appending the result set and filling` && + ` corresponding fields` ). + + "In the example, the existing internal table has not a matching + "line type as the database table. First, an internal table table + "is filled using the INTO CORRESPONDING ... addition. Then, a + "statement with an APPENDING CORRESPONDING ... addition ensures + "that the existing content is kept and the target variable + "receives the read data in the corresponding fields. + + DATA itab_corr TYPE TABLE OF struc_type. + + "INTO CORRESPONDING FIELDS OF: Filling internal table anew + SELECT * + FROM zdemo_abap_flsch + WHERE carrid = 'LH' + INTO CORRESPONDING FIELDS OF TABLE @itab_corr + UP TO 2 ROWS. + + "APPENDING CORRESPONDING FIELDS OF: Adding to existing table lines + SELECT * + FROM zdemo_abap_flsch + WHERE carrid = 'JL' + APPENDING CORRESPONDING FIELDS OF TABLE @itab_corr. + + output->display( input = itab_corr name = `itab_corr` ). + + output->next_section( `12) Reading into individual fields` ). + + "The field list and the INTO list must have the + "same number of elements. + "In the example, a structure and internal table are created + "to process the individually read fields within a SELECT loop. + + "Structure and internal table to include the read result + DATA struct_ind TYPE struc_type. + + DATA itab_ind TYPE TABLE OF struc_type. + + SELECT FROM zdemo_abap_flsch + FIELDS carrid, connid, cityfrom, cityto + WHERE carrid = 'JL' + INTO (@DATA(carr_id),@DATA(conn_id),@DATA(city_from), + @DATA(city_to)). + + IF sy-subrc = 0. + "Filling structure components with the individual values + struct_ind = VALUE #( carrid = carr_id + connid = conn_id + cityfrom = city_from + cityto = city_to ). + + "Appending structure to internal table + APPEND struct_ind TO itab_ind. + ENDIF. + ENDSELECT. + + output->display( input = itab_ind name = `itab_ind` ). + + output->next_section( `13) Reading into packages` ). + + "The package size defines how many rows should be selected in one + "iteration of the SELECT loop. The internal table that is displayed + "shows all entries, i.e. all packages. Furthermore, a string table + "is filled and displayed to visualize the package size of each + "internal table per iteration. + + DATA itab_pack TYPE TABLE OF zdemo_abap_flsch. + + DATA pack_table TYPE string_table. + + SELECT FROM zdemo_abap_flsch + FIELDS carrid, connid, cityfrom, cityto + WHERE carrid <> 'AZ' AND carrid <> 'DL' + INTO TABLE @DATA(itab_package) + PACKAGE SIZE 3. + + IF sy-subrc = 0. + APPEND |Internal table lines processed: | && + |{ lines( itab_package ) }| TO pack_table. + + "Adding internal table content to another internal table + itab_pack = CORRESPONDING #( BASE ( itab_pack ) + itab_package ). + ENDIF. + ENDSELECT. + + output->display( input = pack_table name = `pack_table` ). + output->display( input = itab_pack name = `itab_pack` ). + + output->next_section( `14) Excursion: ABAP SQL - Operands and Expressions` ). + output->display( `14a) SQL operands` ). + + "SQL operands are elementary operands in an ABAP SQL statement. + "Can be database table or view columns, a literal, host variables + "(i. e. global or local data objects escaped using @) or host + "expressions (@( ... )). + "The literals can be typed (using the type name and content within + "a pair of backquotes: char`abc`) with built-in ABAP Dictionary + "types or untyped. + "Regarding host expressions: Structures and internal tables are + "possible as host expressions for statements modifying the content + "of database tables as shown further down. + "The example below demonstrates possible operands. + + DATA upto TYPE i VALUE 3. + + SELECT FROM zdemo_abap_flsch + FIELDS + "Specifies a column of a data source directly using its name + cityfrom, + + "Column selector ~ can be used to prefix every specified column. + "Here, it is optional. It is non-optional, e. g., if multiple data + "sources in an ABAP SQL statement are edited and the column name + "is not unique. + zdemo_abap_flsch~cityto, + + 'Lufthansa' AS name, "Untyped literal + + char`X` AS flag, "Typed literal + + @upto as num, "Host variable + + @( cl_abap_context_info=>get_system_date( ) ) as date "Host expression + + WHERE carrid = 'LH' "Untyped literal + AND countryfr = char`DE` "Typed literal + + "Data object created inline and escaped with @ + INTO TABLE @DATA(sql_operands) + + "The following shows all options having the same effect + UP TO 3 ROWS. "Untyped numeric literal + "UP TO int4`3` ROWS. "Typed numerice literal + "UP TO @upto ROWS. "Host variable + "UP TO @( 10 - 7 ) ROWS. "Host expression + + output->display( input = sql_operands name = `sql_operands` ). + + output->next_section( `14b) Numeric functions ` ). + + "You can use built-in functions in ABAP SQL. + "Result: Value with the associated dictionary type. + "Arguments of the functions: Cover one or more SQL expressions. + + SELECT SINGLE + carrname, + + "Division, result rounded to an integer + div( 4, 2 ) AS div, + + "Division, 3rd argument: result is rounded to the specified + "number of decimals + division( 1, 3, 2 ) AS division, + + "Result is rounded to first greater integer + ceil( decfloat34`1.333` ) AS ceil, + + "Result is the remainder of division + mod( 3, 2 ) AS mod, + + "Result: Largest integer value not greater than the specified + "value + floor( decfloat34`1.333` ) AS floor, + + "Returns the absolute number + abs( int4`-2` ) AS abs, + + "Result is rounded to the specified position after the decimal + "separator + round( decfloat34`1.337`, 2 ) AS round + FROM zdemo_abap_carr + WHERE carrid = 'AA' + INTO @DATA(numeric_functions). + + output->display( input = numeric_functions name = `numeric_functions` ). + + output->next_section( `14c) String functions` ). + + SELECT SINGLE + carrid, "LH + carrname, "Lufthansa + url, "http://www.lufthansa.com + + "Concatenates strings, ignores trailing blanks + concat( carrid, carrname ) AS concat, + + "Concatenates strings, number denotes the blanks that are inserted + concat_with_space( carrid, carrname, 1 ) AS concat_with_space, + + "First letter of a word -> upper case, all other letters -> + "lower case; note that a space and other special characters means + "a new word. + initcap( url ) AS initcap, + + "Position of the first occurrence of the substring specified + instr( carrname,'a' ) AS instr, + + "String of length n starting from the left of an expression; + "trailing blanks are ignored + left( carrname, 4 ) AS left, + + "Number of characters in an expression, trailing blanks are + "ignored + length( url ) AS length, + + "Checks if expression contains a PCRE expression; + "case-sensitive by default (case_sensitive parameter can be + "specified) + "Notes on the result: 1 = found, 0 = not found + "PCRE below: Searches a period that is followed by any character + like_regexpr( pcre = '\..', + value = url ) AS like_regex, + + "Returns position of a substring in an expression, + "3rd parameter = specifies offset (optional) + "4th parameter = determines the number of occurrences (optional) + locate( carrname, 'a', 0, 2 ) AS locate, + + "Searches a PCRE pattern, returns offset of match; + "many optional parameters: occurrence, case_sensitive, start, + "group + locate_regexpr( pcre = '\..', "Period followed by any character + value = url, + occurrence = 2 ) "2nd occurrence in the string + AS locate_regexpr, + + "Searches a PCRE pattern, returns offset of match + 1; + "many optional parameters: occurrence, case_sensitive, start, + "group + locate_regexpr_after( pcre = '.', "Any character + value = url, + occurrence = 1 ) AS locate_regexpr_after, + + "Removes leading characters as specified in the 2nd argument, + "trailing blanks are removed + ltrim( carrname, 'L' ) AS ltrim, + + "Counts all occurrences of found PCRE patterns + occurrences_regexpr( pcre = '\..', + value = url ) AS occ_regex, + + "Replaces the 2nd argument with the 3rd in an expression + replace( carrname,'a','#' ) AS replace, + + "Replaces a found PCRE expression; + "more parameters possible: occurrence, case_sensitive, start + replace_regexpr( pcre = '\..', + value = url, + with = '#' ) AS replace_regex, + + "Extracts a string with the length specified starting from the + "right + right( carrname, 5 ) AS right, + + "Expands string to length n (2nd argument); trailing blanks + "produced are replaced by the characters from the (3rd) argument + "Note that if n is less than the string, the expression is + "truncated on the right. + rpad( carrname, 12, '#' ) AS rpad, + + "All trailing characters that match the character of the 2nd + "argument are removed; trailing blanks are removed, too + rtrim( carrname, 'a' ) AS rtrim, + + "Returns a substring; 2nd argument = position from where to start; + "3rd argument: length of the extracted substring + substring( carrname, 3, 3 ) AS substring, + + "Searches for a PCRE expression and returns the matched substring + "More parameters possible: occurrence, case_sensitive, start, group + substring_regexpr( pcre = '\...', + value = url ) AS substring_regexpr, + + "All lower case letters are transformed to upper case letters + upper( carrname ) AS upper + FROM zdemo_abap_carr + WHERE carrid = 'LH' + INTO @DATA(string_functions). + + output->display( input = string_functions name = `string_functions` ). + + output->next_section( `14d) Special functions` ). + + SELECT SINGLE + carrid, + + "Conversion functions + "When used: Special conversions that cannot be handled in a + "general CAST expression + + "Type conversion: string of fixed length (e.g. of type c) to + "variable length string of type string + to_clob( carrid ) AS clob, + + "Byte string -> character string + bintohex( raw`3599421128650F4EE00008000978B976` ) AS bintohex, + + "Character string -> byte string + hextobin( char`3599421128650F4EE00008000978B976` ) AS hextobin, + + "Byte field of type RAW to a byte string (BLOB) of type RAWSTRING + to_blob( raw`3599421128650F4EE00008000978B976` ) AS blob, + + "Unit conversion function + "More parameters are available. + + "Converts miles to kilometers + unit_conversion( quantity = d34n`1`, + source_unit = unit`MI`, + target_unit = unit`KM` ) AS miles_to_km, + + "Date and time functions + "There are plenty of functions; the below functions are a + "selection. + + add_days( @( cl_abap_context_info=>get_system_date( ) ), 4 + ) AS add_days, + add_months( @( cl_abap_context_info=>get_system_date( ) ), 2 + ) AS add_months, + is_valid( @( cl_abap_context_info=>get_system_date( ) ) ) AS date_is_valid, + is_valid( @( cl_abap_context_info=>get_system_time( ) ) ) AS time_is_valid + + FROM zdemo_abap_carr + WHERE carrid = 'AA' + INTO @DATA(special_functions). + + "Retrieving type information using RTTI to demonstrate the effect + "of type conversions like to_clob etc. + "type_kind: g (character string with variable length), + "C (character string of fixed length), X (binary), y (byte string) + DATA(components) = CAST cl_abap_structdescr( + cl_abap_typedescr=>describe_by_data( special_functions ) + )->components. + + output->display( input = components name = `components` ). + output->display( input = special_functions name = `special_functions` ). + + output->next_section( `15e) Aggregate Expressions` ). + + "Consist of aggregate functions and aggregate the values of + "multiple rows of the result set of a query into a single value. + "The example shows a selection of available functions. + + SELECT + carrid, + + "Average value of the content of a column in a row set + AVG( fltime ) AS fltime1, + + "AVG with data type specification for the result + AVG( fltime AS DEC( 14,4 ) ) AS fltime2, + + "Maximum value of the results in a row set + MAX( fltime ) AS max, + + "Minimum value + MIN( fltime ) AS min, + + "Sum of the results in a row set. + SUM( fltime ) AS sum, + + "Returns the number of rows in a row set. + "The following two have the same meaning. + COUNT( * ) AS count2, + COUNT(*) AS count3, + + "Chains the results in a row set. + "An optional separator can be specified + STRING_AGG( airpfrom, ', ' ) AS string_agg + + FROM zdemo_abap_flsch + WHERE carrid = 'LH' + GROUP BY carrid + INTO TABLE @DATA(agg_exp). + + output->display( input = agg_exp name = `agg_exp` ). + + output->next_section( `15f) More SQL Expressions` ). + + "Arithmetic expressions to perform arithmetic calculations + "Cast expressions to convert the value of operands to a dedicated + " dictionary type. Note that there are special conversion rules. + "String expressions using the operator && to concatenate character + " strings. + "Case distinctions to carry out either a simple (comparison of the + " values of a dedicated operand) or complex (searched case; + " evaluation of multiple logical expressions) case distinction. + + SELECT SINGLE + carrid, + + "Arithmethic expressions + "operators + - * + "Note that / is not allowed in integer expressions as the + "one below. + ( 1 + 2 ) * 3 AS calc, + + "/ used in an expression using type adjustment in ABAP SQL. + "A cast expression converts the value of the operands to the + "specified dictionary type. The result is a representation of + "the source value in the specified type. + CAST( 1 AS D34N ) / CAST( 2 AS D34N ) AS ratio, + + "String expression using && to concatenate two character strings; + "the result of the concatenation must not be longer than + "255 characters. + carrid && carrname AS concat, + + "Case distinction + "Simple case distinction + "The expression compares the values of an operand with other + "operands. Result: The first operand after THEN for which the + "comparison is true. If no matches are found, the result + "specified after ELSE is selected. + CASE currcode + WHEN 'EUR' THEN 'A' + WHEN 'USD' THEN 'B' + ELSE 'C' + END AS case_simple, + + "Complex case distinction + "The expression evaluates logical expressions. Result: The first + "operand after THEN for which the logical expression is true. If + "no logical expressions are true, the result specified after ELSE + "is selected. + CASE WHEN length( carrname ) <= 5 THEN 'small' + WHEN length( carrname ) BETWEEN 6 AND 10 THEN 'mid' + WHEN length( carrname ) BETWEEN 11 AND 15 THEN 'large' + ELSE 'huge' + END AS case_complex + FROM zdemo_abap_carr + WHERE carrid = 'AA' + INTO @DATA(more_sql_expr). + + output->display( input = more_sql_expr name = `more_sql_expr` ). + + output->next_section( `16a) Window expressions (1)` ). + + "A simple window is constructed in the OVER clause, + "window functions - here aggregate functions - are applied. + "To narrow the entries in the resulting table, duplicates + "are deleted. + + SELECT carrid, currency, + SUM( paymentsum ) OVER( PARTITION BY carrid ) AS sum, + AVG( price AS DEC( 14,2 ) ) OVER( PARTITION BY carrid ) AS avg, + MAX( price ) OVER( PARTITION BY carrid ) AS max + FROM zdemo_abap_fli + ORDER BY carrid + INTO TABLE @DATA(win). + + DELETE ADJACENT DUPLICATES FROM win COMPARING ALL FIELDS. + + output->display( input = win name = `win` ). + + output->next_section( `16b) Window expressions (2)` ). + + SELECT carrid, currency, fldate, + "Sorts the rows by some columns and counts the number of rows from + "the first row of the window to the current row. + COUNT( * ) OVER( ORDER BY currency, fldate + ROWS BETWEEN + "UNBOUNDED PRECEDING: frame starts at the + "first row of the window + UNBOUNDED PRECEDING + "CURRENT ROW: determines starting or ending + "at the current row; here, it ends + AND CURRENT ROW ) AS count1, + + "If no window frame is used, the default window frame is + "BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, + "i. e. the result of count1 equals count2 + COUNT( * ) OVER( ORDER BY currency, fldate ) AS count2, + + "Sorts the rows by some columns and counts the number of rows from + "the current row to the last row of the window. + "The result is reverse numbering. + COUNT( * ) OVER( ORDER BY currency, fldate + ROWS BETWEEN CURRENT ROW + "UNBOUND FOLLOWING: + "Determines the ending frame boundary, + "this addition specifies the last row of the + "window + AND UNBOUNDED FOLLOWING ) AS count_reverse, + + "Sorts the rows by some columns and calculates the rolling + "averages of a subset of rows from column price. The subset + "consists of the current row plus one preceding and one following + "row. A better use case as below example would be that, for + "example, you can calculate the 3-day-average temperature for + "every day from a list of temperature data. + AVG( price AS DEC( 14,2 ) ) OVER( ORDER BY currency, fldate + ROWS BETWEEN + "n PRECEDING: for both start and end of frame; + "frame to start/end n rows above the current row + 1 PRECEDING + "n FOLLOWING: for both start and end of frame; + "frame to start/end n rows beneath the current row + AND 1 FOLLOWING ) AS avg + + FROM zdemo_abap_fli + INTO TABLE @DATA(win_order). + + output->display( input = win_order name = `win_order` ). + + output->next_section( `17) SQL conditions` ). + output->display( `17a) SQL conditions (1)` ). + "The example demonstrates a WHERE clause with =, >, <, <=, >=, AND + + SELECT * FROM zdemo_abap_fli + WHERE carrid = 'LH' "or EQ + AND price > 700 "or GT + AND seatsocc < 320 "or LT + AND seatsmax <= 330 "or LE + AND seatsmax_b >= 30 "or GE + INTO TABLE @DATA(itab_comp_op). + + output->display( input = itab_comp_op name = `itab_comp_op` ). + + output->next_section( `17b) SQL conditions (2)` ). + + "The example demonstrates a WHERE clause with + "BETWEEN, NOT BETWEEN, OR + + SELECT * FROM zdemo_abap_fli + WHERE seatsmax BETWEEN 350 AND 400 "#EC CI_CMPLX_WHERE + OR price NOT BETWEEN 100 AND 1500 + INTO TABLE @DATA(it_sql_cond). + + output->display( input = it_sql_cond name = `it_sql_cond` ). + + output->next_section( `17c) SQL conditions (3)` ). + + "The example demonstrates a WHERE clause with character literals: + "- LIKE '%FRAN%': Condition is true if the column cityfrom contains + " a string containing the pattern 'FRAN' + "- NOT LIKE '_X%': Condition is true if the column airpto contains + " a value whose second character is not 'X'. + "- IN ( ... ): condition is true if the column cityto contains + " one of the values specified within the brackets + "- NOT IN ( ... ): condition is true if the column cityto does not + " contain one of the values specified within the brackets + SELECT * FROM zdemo_abap_flsch + WHERE cityfrom LIKE '%FRAN%' + AND airpto NOT LIKE '_X%' + AND cityto IN ( 'BERLIN', 'NEW YORK', 'LONDON' ) + AND cityto NOT IN ( 'SYDNEY' ) + INTO TABLE @DATA(itab_like_in). + + + output->display( input = itab_like_in name = `itab_like_in` ). + + output->next_section( `18) Further clauses in SELECT statements` ). + + output->display( `18a) GROUP BY: Combining groups of table rows ` && + `in the result set` ). + "In the example, the database table rows that have the same content + "in column CARRID are combined. The lowest and highest values in + "column PRICE are determined for each of these groups and placed + "into the combined row. Note that the GROUP BY clause requires all + "columns that are directly specified in the SELECT list or + "specified there as an argument of an SQL expression to be + "specified. Aggregate functions in aggregate expressions are an + "exception, as shown below. + + SELECT FROM zdemo_abap_fli + FIELDS carrid, + MIN( price ) AS min_price, + MAX( price ) AS max_price + GROUP BY carrid + INTO TABLE @DATA(itab_gr). + + output->display( input = itab_gr name = `itab_gr` ). + + output->next_section( `18b) HAVING: Limiting the number of rows` && + ` in groups in the result set` ). + + "The addition HAVING limits the number of rows in groups in the + "result set of a query by using a logical expression on these rows. + "The logical expression evaluates the content of row groups. Those + "rows are placed in the result set for which the logical expression + "is true. + + SELECT FROM zdemo_abap_flsch + FIELDS carrid, connid, cityfrom, cityto + WHERE carrid = 'LH' + GROUP BY carrid, connid, cityfrom, cityto + HAVING SUM( fltime ) > 100 + INTO TABLE @DATA(itab_hav). + + output->display( input = itab_hav name = `itab_hav` ). + + output->next_section( `18c) ORDER BY: Sorting the result set by ` && + `specified columns` ). + + "The following example shows the ordering of the result set based + "on the content of the primary key of the data source. You can also + "order by any columns and by explicitly specifying the sort order. + "There are more options to order, for example, by using SQL + "expressions. + + "Example 1: Sorting the result set by primary key + SELECT * + FROM zdemo_abap_flsch + WHERE carrid <> 'UA' + ORDER BY PRIMARY KEY + INTO TABLE @DATA(itab_ord1) + UP TO 3 ROWS. + + "Example 2: Sorting by arbitrary field and specifying the sort order + SELECT * + FROM zdemo_abap_flsch + WHERE carrid <> 'UA' + ORDER BY fltime DESCENDING + INTO TABLE @DATA(itab_ord2) + UP TO 3 ROWS. + + output->display( input = itab_ord1 name = `itab_ord1` ). + output->display( input = itab_ord2 name = `itab_ord2` ). + + output->next_section( `Using SELECT when Reading from Multiple Tables` ). + + output->display( `19) FOR ALL ENTRIES addition: Reading data from ` && + `a database table depending on the content of an internal table:` ). + + "In the example, only those entries should be read from the + "database table if entries exist in the internal table that meet + "the conditions specified in the WHERE clause. Note that you should + "ensure that the internal table is not initial. + "Check the ABAP Keyword Documentation for various restrictions that + "apply when using this addition. The following example is just for + "demonstrating the syntax - as are all examples. + + "Filling an internal table + SELECT * FROM zdemo_abap_fli + WHERE seatsmax < 300 + INTO TABLE @DATA(cond_tab). + + IF ( 0 < lines( cond_tab ) ). + SELECT carrid, connid, cityfrom, cityto "#EC CI_NO_TRANSFORM + FROM zdemo_abap_flsch + FOR ALL ENTRIES IN @cond_tab + WHERE carrid = @cond_tab-carrid + AND connid = @cond_tab-connid + INTO TABLE @DATA(itab_forall). + ENDIF. + + output->display( input = itab_forall name = `itab_forall` ). + + output->next_section( `20) Reading data from a database table depending` && + ` on data of another database table using a subquery` ). + + "In the example, all available flights leaving from a city with + "FRAN in the name (San Francisco, Frankfurt) existing in another + "database table and for which further comparisons are true + "(matching CARRID and CONNID) are read into an internal table. + + SELECT carrid, connid, fldate + FROM zdemo_abap_fli AS zdemo_abap_fli + WHERE EXISTS + ( SELECT carrid FROM zdemo_abap_flsch + WHERE carrid = zdemo_abap_fli~carrid + AND connid = zdemo_abap_fli~connid + AND cityfrom LIKE '%FRAN%' ) + INTO TABLE @DATA(itab_sub). + + output->display( input = itab_sub name = `itab_sub` ). + + output->next_section( `21) INNER JOIN: Combining data` ). + "Note: In this kind of joins, only those columns are joined if + "the ON conditions are met. + "As an alternative and if you frequently need joined data, you + "should create views. + + "Example 1 + SELECT p~carrid, p~connid, p~cityto, f~fldate + FROM zdemo_abap_flsch AS p + INNER JOIN zdemo_abap_fli AS f + ON p~carrid = f~carrid AND p~connid = f~connid + WHERE p~cityfrom = 'NEW YORK' + INTO TABLE @DATA(itab_in1). + + "Example 2: Multiple inner joins + SELECT p~carrid, s~carrname, p~connid, p~cityto, f~fldate + FROM zdemo_abap_flsch AS p + INNER JOIN zdemo_abap_fli AS f + ON p~carrid = f~carrid AND p~connid = f~connid + INNER JOIN zdemo_abap_carr AS s + ON p~carrid = s~carrid + WHERE p~cityfrom = 'FRANKFURT' + INTO TABLE @DATA(itab_in2). + + output->display( input = itab_in1 name = `itab_in1` ). + output->display( input = itab_in2 name = `itab_in2` ). + + output->next_section( `22) LEFT OUTER JOIN: Combining data` ). + + "Note: The columns of each line on the right-hand side that does not + "meet the ON condition is filled with initial values and linked + "with the columns of the left-hand side. If the conditions of the + "WHERE clause have been met, each line on the left-hand side of the + "left outer join produces at least one line in the selection, + "irrespective of the ON condition. + "In the example, all rows from the left-hand side (zdemo_abap_carr) + "are returned as well as the matching rows from the right-hand side + "(zdemo_abap_flsch). However, the ON condition + ""p~cityfrom = 'FRANKFURT'" is not met for several entries in + "zdemo_abap_flsch and a CONNID does not exist. + "Hence, initial values are returned for connid. + + SELECT s~carrid, s~carrname, p~connid + FROM zdemo_abap_carr AS s + LEFT OUTER JOIN zdemo_abap_flsch AS p + ON s~carrid = p~carrid AND p~cityfrom = 'FRANKFURT' + WHERE s~carrid <> 'UA' + INTO TABLE @DATA(itab_lo). + + output->display( input = itab_lo name = `itab_lo` ). + + output->next_section( `23) UNION: Combining data` ). + + "The addition UNION creates the union of the results sets + "of two SELECT statements. + "The columns of the result set keep the names defined in the + "SELECT statement on the left of UNION. + "The result set of ROWS of the SELECT statement on the right of + "UNION are inserted into the results set of the SELECT statement + "on the left of UNION. + "The example demonstrates the union of two tables and + "visualizes those columns that do + "not exist in the other table by setting the value '-'. + "In below example, a CAST is required for the column CONNID. + + SELECT FROM zdemo_abap_carr + FIELDS carrname, + CAST( '-' AS CHAR( 4 ) ) AS connid, + '-' AS cityfrom, + '-' AS cityto + WHERE carrid = 'LH' + UNION + SELECT FROM zdemo_abap_flsch + FIELDS '-' AS carrname, + + CAST( connid AS CHAR( 4 ) ) AS connid, + cityfrom, + cityto + WHERE carrid = 'LH' + ORDER BY carrname DESCENDING, connid, cityfrom, cityto + INTO TABLE @DATA(itab_union). + + output->display( input = itab_union name = `itab_union` ). + + output->next_section( `24) Common Table Expressions (CTE)` ). + + "The result sets of both common table expressions +connections + "and +sum_seats are merged in the subquery of the CTE +result in + "a join expression. An explicit name list assigns names to the + "resulting columns. These names are used in the main query to sort + "the results. For each flight connection of the selected airline, + "the total number of occupied seats is output from the database + "table. + + WITH + +connections AS ( + SELECT zdemo_abap_flsch~carrid, carrname, connid, cityfrom, cityto + FROM zdemo_abap_flsch + INNER JOIN zdemo_abap_carr + ON zdemo_abap_carr~carrid = zdemo_abap_flsch~carrid + WHERE zdemo_abap_flsch~carrid BETWEEN 'AA' AND 'JL' ), + +sum_seats AS ( + SELECT carrid, connid, SUM( seatsocc ) AS sum_seats + FROM zdemo_abap_fli + WHERE carrid BETWEEN 'AA' AND 'JL' + GROUP BY carrid, connid ), + +result( name, connection, departure, arrival, occupied ) AS ( + SELECT carrname, c~connid, cityfrom, cityto, sum_seats + FROM +connections AS c + INNER JOIN +sum_seats AS s + ON c~carrid = s~carrid AND + c~connid = s~connid ) + SELECT * + FROM +result + ORDER BY name, connection + INTO TABLE @DATA(itab_cte). + + output->display( input = itab_cte name = `itab_cte` ). + + output->next_section( `25) CTE and a SELECT Loop` ). + "The example shows a WITH statement, whose main query creates a + "tabular result set. Since the data is written into work area + "rather than to an internal table, a SELECT loop is opened, which + "must be closed with ENDWITH. + + WITH + +carriers AS ( SELECT FROM zdemo_abap_carr + FIELDS carrid, carrname ) + SELECT FROM zdemo_abap_flsch AS s + INNER JOIN +carriers AS c + ON s~carrid = c~carrid + FIELDS c~carrname, s~connid + WHERE s~carrid = 'LH' + INTO @DATA(wa_cte_loop) + UP TO 3 ROWS. + output->display( input = wa_cte_loop name = `wa_cte_loop` ). + ENDWITH. + + output->next_section( `26) Excursion: Reading from an internal ` && + `table using SELECT` ). + + "Note: The internal table from which to be read must be specified + "as host variable. The internal table should have an explicitly + "defined primary key. + + DATA itab_read1 TYPE TABLE OF zdemo_abap_flsch + WITH NON-UNIQUE KEY mandt carrid connid. + + "Reading from database table to fill an internal table. + SELECT FROM zdemo_abap_flsch + FIELDS mandt, carrid, connid, cityfrom, cityto + WHERE carrid = 'AA' + INTO TABLE @itab_read1. + + "Reading from internal table. + SELECT FROM @itab_read1 AS itab + FIELDS * + WHERE carrid = 'AA' + INTO TABLE @DATA(itab_read2). + + output->display( input = itab_read2 name = `itab_read2` ). + + output->next_section( `Changing data in database tables` ). + + "Deleting database table to work with + DELETE FROM zdemo_abap_carr. + + "Creating table rows to be inserted in the database table + DATA(row1) = VALUE zdemo_abap_carr( carrid = 'AF' + carrname = 'Air France' + currcode = 'EUR' + url = 'http://www.airfrance.fr' ). + + DATA(row2) = VALUE zdemo_abap_carr( carrid = 'UA' + carrname = 'United Airlines' + currcode = 'USD' + url = 'http://www.ual.com' ). + + output->display( `27) Inserting individual line into a database` && + ` table using INSERT` ). + + "Inserting from an existing structure + INSERT INTO zdemo_abap_carr VALUES @row1. + + "Alternative syntax having the same effect as the statement above + INSERT zdemo_abap_carr FROM @row2. + + "Inserting from a structure created inline using a + "constructor expression with VALUE within a host expression + INSERT zdemo_abap_carr FROM @( VALUE #( carrid = 'SR' + carrname = 'Swiss' + currcode = 'CHF' + url = 'http://www.swiss.com' ) ). + + + select_from_dbtab( ). + output->display( input = itab_res name = `itab_res` ). + + output->next_section( `28) Inserting multiple rows into a database` && + ` table using INSERT` ). + + "Creating and filling an internal table + DATA itab_insert TYPE TABLE OF zdemo_abap_carr. + + itab_insert = VALUE #( ( carrid = 'BA' + carrname = 'British Airways' + currcode = 'GBP' + url = 'http://www.british-airways.com' ) + ( carrid = 'FJ' + carrname = 'Air Pacific' + currcode = 'USD' + url = 'http://www.airpacific.com' ) ). + + + "Inserting from existing internal table + INSERT zdemo_abap_carr FROM TABLE @itab_insert. + + "Inserting from an internal table created inline using + "a constructor expression with VALUE within a host expression + INSERT zdemo_abap_carr FROM TABLE @( VALUE #( ( carrid = 'NW' + carrname = 'Northwest Airlines' + currcode = 'USD' + url = 'http://www.nwa.com' ) + ( carrid = 'QF' + carrname = 'Qantas Airways' + currcode = 'AUD' + url = 'http://www.qantas.com.au' ) ) ). + + select_from_dbtab( ). + output->display( input = itab_res name = `itab_res` ). + + output->next_section( `29) Inserting multiple rows into a database table` && + ` accepting duplicate keys` ). + + "ACCEPTING DUPLICATE KEYS addition: To avoid a runtime error when + "inserting entries from an internal table having duplicate keys, + "all lines that would produce duplicate entries in the database + "table regarding the keys are discarded and sy-subrc is set to 4. + + "Creating and filling an internal table + DATA itab28a TYPE TABLE OF zdemo_abap_carr. + + itab28a = VALUE #( ( carrid = 'SQ' + carrname = 'Singapore Airlines' + currcode = 'SGD' + url = 'http://www.singaporeair.com' ) + ( carrid = 'SQ' + carrname = 'Singapore Airlines' + currcode = 'SGD' + url = 'http://www.singaporeair.com' ) ). + + INSERT zdemo_abap_carr FROM TABLE @itab28a ACCEPTING DUPLICATE KEYS. + + DATA(subrc) = sy-subrc. + + select_from_dbtab( ). + output->display( input = itab_res name = `itab_res` ). + output->display( input = subrc name = `subrc` ). + + output->next_section( `30) INSERT statement using a subquery` ). + + "The purpose of this abstract example is just to visualize that + "subqueries are possible in INSERT statements. In the example, + "the goal is just to get one entry from table zdemo_abap_flsch. + "Since only MANDT and CARRID are shared fields in the two database + "tables other values in zdemo_abap_carr remain empty. The line is + "further processed in the following example. + + INSERT zdemo_abap_carr FROM ( SELECT carrid + FROM zdemo_abap_flsch + WHERE carrid = 'LH' AND + connid = '0400' ). + + select_from_dbtab( ). + output->display( input = itab_res name = `itab_res` ). + + output->next_section( `31) Changing content of existing rows ` && + `using UPDATE` ). + + "Creating and filling structure + "In the case below, all field values except the key field are updated. + DATA(row30) = VALUE zdemo_abap_carr( + carrid = 'LH' + carrname = 'Lufthansa' + currcode = 'EUR' + url = 'http://www.lufthansa.com' ). + + "Creating and filling internal table + DATA itab_update LIKE itab_insert. + + itab_update = VALUE #( ( carrid = 'BA' + carrname = 'British Airways' + currcode = 'GBP' + url = 'http://www.britishairways.com' ) "updated + ( carrid = 'FJ' + carrname = 'Fiji Airways' "updated + currcode = 'USD' + url = 'http://www.fijiairways.com' ) )."updated + + + UPDATE zdemo_abap_carr FROM @row30. + + UPDATE zdemo_abap_carr FROM TABLE @itab_update. + + select_from_dbtab( ). + output->display( input = itab_res name = `itab_res` ). + + output->next_section( `32) Changing values of specific fields in all` && + ` table rows using UPDATE` ). + + "Using the SET addition, you can change the values of specific + "fields in all table rows without overwriting existing values in + "other fields. + "In the example, the field CURRCODE is set as specified for all + "rows for which the WHERE condition is true. + + UPDATE zdemo_abap_carr + SET currcode = 'EUR' + WHERE carrid <> 'UA' AND carrid <> 'NW'. + + select_from_dbtab( ). + output->display( input = itab_res name = `itab_res` ). + + output->next_section( `33) INDICATORS addition to UPDATE statements: ` && + `Changing values of specific fields without overwriting ` && + `existing values of other fields ` ). + + "Example: + "- Structured type is created with WITH INDICATORS addition + "- Internal table from which to update a database table is created; it + " includes the indicator structure comp_ind + "- Internal table is filled; only one component is flagged as + " to be updated + "- Other fields remain unchanged; note that key fields must be + " included in ind_tab (indicator setting for key fields has + " no effect) + + "Structured type with WITH INDICATORS addition + TYPES ind_wa TYPE zdemo_abap_carr WITH INDICATORS comp_ind TYPE abap_bool. + + DATA ind_tab TYPE TABLE OF ind_wa. + + "Filling internal table; only CURRCODE should be updated + ind_tab = VALUE #( ( carrid = 'QF' + carrname = 'Qantas Airways' + currcode = 'AUD' + comp_ind-currcode = abap_true ) + ( carrid = 'SQ' + carrname = 'Singapore Airlines' + currcode = 'SGD' + comp_ind-currcode = abap_true ) ). + + UPDATE zdemo_abap_carr + FROM TABLE @ind_tab + INDICATORS SET STRUCTURE comp_ind. + + select_from_dbtab( ). + output->display( input = itab_res name = `itab_res` ). + + output->next_section( `34) Inserting and changing rows using MODIFY` ). + "The example only uses host expressions. + + "Modifying an entry based on a row. Here, a new entry is created in + "the database table since no row exists having the key. + "The example uses a structure created inline using a constructor + "expression with VALUE within a host expression. + MODIFY zdemo_abap_carr FROM @( + VALUE #( carrid = 'AZ' + carrname = 'Alitalia' + currcode = 'EUR' + url = 'http://www.alitalia.it' ) ). + + "Storing the value of system field sy-dbcnt to determine how many + "table rows were modified. + DATA(dbcnt) = sy-dbcnt. + + "Modifying entries based on an internal table. Here, no new entry + "is created. Existing entries are updated. + MODIFY zdemo_abap_carr FROM TABLE @( VALUE #( ( carrid = 'BA' + carrname = 'British Airways' + currcode = 'GBP' "modified + url = 'http://www.britishairways.co.uk' ) "mod + ( carrid = 'QF' + carrname = 'Qantas Airways' + currcode = 'AUD' + url = 'http://www.qantas.com' ) ) ). "modified + + "Adding the value of sy-dbcnt to the value from above to get + "the total number of modified rows in this example. + dbcnt = dbcnt + sy-dbcnt. + + select_from_dbtab( ). + output->display( input = itab_res name = `itab_res` ). + output->display( |{ dbcnt } table rows were modified.| ). + + output->next_section( `35) Deleting table rows` ). + "Note that you specify the key fields only. + + "Deleting an entry based on a row. Here, the example uses a + "structure created inline and by only specifying the key value + "using a constructor expression with VALUE within a host + "expression. + DELETE zdemo_abap_carr FROM @( VALUE #( carrid = 'QF' ) ). + + "Deleting multiple entries based on an internal table. + "Same as above, the internal table is created inline. + DELETE zdemo_abap_carr FROM TABLE @( VALUE #( ( carrid = 'AF' ) + ( carrid = 'AZ' ) + ( carrid = 'LH' ) ) ). + + select_from_dbtab( ). + output->display( input = itab_res name = `itab_res` ). + + output->next_section( `36) Deleting table rows based on a condition` ). + + DELETE FROM zdemo_abap_carr WHERE currcode <> 'USD'. + + select_from_dbtab( ). + output->display( input = itab_res name = `itab_res` ). + + output->next_section( `37) DELETE: Delete complete table` ). + + DELETE FROM zdemo_abap_carr. + + select_from_dbtab( ). + output->display( input = itab_res name = `itab_res` ). + + ENDMETHOD. + + + METHOD select_from_dbtab. + SELECT * + FROM zdemo_abap_carr + INTO TABLE @itab_res. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_sql.clas.xml b/src/zcl_demo_abap_sql.clas.xml new file mode 100644 index 0000000..dddcc88 --- /dev/null +++ b/src/zcl_demo_abap_sql.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_SQL + E + ABAP cheat sheet: ABAP SQL + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_sql_group_by.clas.abap b/src/zcl_demo_abap_sql_group_by.clas.abap new file mode 100644 index 0000000..4020004 --- /dev/null +++ b/src/zcl_demo_abap_sql_group_by.clas.abap @@ -0,0 +1,175 @@ +*********************************************************************** +* +* ABAP cheat sheet: ABAP SQL - Grouping Internal Tables +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate syntactical options when grouping internal +* tables. +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP Development Tools (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, check the notes +* included in the class as comments or refer to the respective topic +* in the ABAP Keyword Documentation. +* - Due to the amount of output in the console, the examples include +* numbers (e. g. 1) ..., 2) ..., 3) ...) for the individual example +* sections. Plus, the variable name is displayed in most cases. Hence, +* to easier and faster find the relevant output in the console, just +* search in the console for the number/variable name (STRG+F in the +* console) or use the debugger. +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** +"!

ABAP cheat sheet: ABAP SQL - Grouping Internal Tables

+"! Example to demonstrate working with structures.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_sql_group_by DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + + CLASS-METHODS: class_constructor. + +protected section. + PRIVATE SECTION. + CLASS-DATA: + wa TYPE zdemo_abap_flsch, + member TYPE zdemo_abap_flsch, + members TYPE STANDARD TABLE OF zdemo_abap_flsch WITH EMPTY KEY. + +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_SQL_GROUP_BY IMPLEMENTATION. + + + METHOD class_constructor. + "Fill 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: Grouping Internal Tables` ). + + SELECT * + FROM zdemo_abap_flsch + INTO TABLE @DATA(fli_tab). + + output->next_section( `1) Representative Binding` ). + output->display( `1a) Grouping by one column` ). + + LOOP AT fli_tab INTO wa + GROUP BY wa-carrid. + output->display( wa-carrid ). + ENDLOOP. + + output->next_section( `1b) Members of one column groups` ). + + LOOP AT fli_tab INTO wa + GROUP BY wa-carrid. + CLEAR members. + LOOP AT GROUP wa INTO member. + members = VALUE #( BASE members ( member ) ). + ENDLOOP. + + output->display( members ). + ENDLOOP. + + output->next_section( `1c) Grouping by two columns` ). + + LOOP AT fli_tab INTO wa + GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom ). + + output->display( |{ wa-carrid } { wa-airpfrom }| ). + ENDLOOP. + + output->next_section( `1d) Members of two column groups` ). + + LOOP AT fli_tab INTO wa + GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom ). + CLEAR members. + LOOP AT GROUP wa INTO member. + members = VALUE #( BASE members ( member ) ). + ENDLOOP. + + output->display( members ). + ENDLOOP. + + output->next_section( `2) Group Key Binding` ). + output->display( `2a) Grouping by one column` ). + + LOOP AT fli_tab INTO wa + GROUP BY wa-carrid + INTO DATA(key). + + output->display( key ). + ENDLOOP. + + output->next_section( `2b) Members of one column groups` ). + + LOOP AT fli_tab INTO wa + GROUP BY wa-carrid + INTO key. + CLEAR members. + LOOP AT GROUP key INTO member. + members = VALUE #( BASE members ( member ) ). + ENDLOOP. + + output->display( members ). + ENDLOOP. + + output->next_section( `2c) Grouping by two columns` ). + + LOOP AT fli_tab INTO wa + GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom ) + INTO DATA(keys). + + output->display( keys ). + ENDLOOP. + + output->next_section( `2d) Members of two column groups` ). + + LOOP AT fli_tab INTO wa + GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom ) + INTO keys. + CLEAR members. + LOOP AT GROUP keys INTO member. + members = VALUE #( BASE members ( member ) ). + ENDLOOP. + + output->display( members ). + ENDLOOP. + + output->next_section( `2e) Two column groups without members` ). + + LOOP AT fli_tab INTO wa + GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom + index = GROUP INDEX size = GROUP SIZE ) + WITHOUT MEMBERS + INTO DATA(keysplus). + + output->display( keysplus ). + ENDLOOP. + + ENDMETHOD. +ENDCLASS. \ No newline at end of file diff --git a/src/zcl_demo_abap_sql_group_by.clas.xml b/src/zcl_demo_abap_sql_group_by.clas.xml new file mode 100644 index 0000000..697073e --- /dev/null +++ b/src/zcl_demo_abap_sql_group_by.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_SQL_GROUP_BY + E + ABAP cheat sheet: ABAP SQL - Grouping Internal Tables + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_string_proc.clas.abap b/src/zcl_demo_abap_string_proc.clas.abap new file mode 100644 index 0000000..25353d3 --- /dev/null +++ b/src/zcl_demo_abap_string_proc.clas.abap @@ -0,0 +1,1270 @@ +*********************************************************************** +* +* ABAP cheat sheet: String processing +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntactical options for processing +* character strings as outlined in the respective ABAP cheat sheet. +* - Topics covered: creating strings and assigning values, chaining strings, +* string templates, concatenating/splitting/modifying strings, searching +* and replacing, regular expressions +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP Development Tools (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, check the notes +* included in the class as comments or refer to the respective topic +* in the ABAP Keyword Documentation. +* - Due to the amount of output in the console, the examples include +* numbers (e. g. 1) ..., 2) ..., 3) ...) for the individual example +* sections. Plus, the variable name is displayed in most cases. Hence, +* to easier and faster find the relevant output in the console, just +* search in the console for the number/variable name (STRG+F in the +* console) or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** +"!

ABAP cheat sheet: String processing

+"! Example to demonstrate string processing.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_string_proc DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + +protected section. +private section. +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_STRING_PROC IMPLEMENTATION. + + + METHOD if_oo_adt_classrun~main. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `Demo: String Processing` ). + output->display( `1) Creating Strings and Assigning Values` ). + + "Data object declarations providing default values + DATA: flag TYPE c LENGTH 1 VALUE 'X', "Single quotes + str_a1 TYPE string VALUE `Hallo, how are you?`. "Backquotes + + DATA: char_a1 TYPE c LENGTH 5, + str_a2 TYPE string, + str_a3 LIKE str_a2. + + "Examples for type n + DATA zip_code TYPE n LENGTH 5 VALUE '12345'. + + DATA isbn_number TYPE n LENGTH 13 VALUE '1234567890123'. + + "Value assignments to existing data objects + char_a1 = 'ab123'. + + str_a2 = `

Hallo!

`. + + "Escaping a backquote using another backquote + str_a3 = `This is a backquote: ``.`. + + "If possible, avoid unnecessary type conversion; in principle, + "every convertible type can be specified + "Assigning a fixed-length string to a variable-length string. + str_a2 = 'abc'. + + DATA str_a4 TYPE string VALUE 'X'. "Type c length 1 + + DATA str_a5 TYPE string VALUE -1. "Type i + + "Inline declaration: data object declaration and + "value assignment + "Data type is automatically derived + DATA(char_a2) = 'abcd'. "Type c length 4 + + DATA(str_a6) = `efgh`. "Type string + + "Note: Variable is of type c length 4. Characters are truncated. + char_a2 = 'ijklmnopq'. + + "Treating trailing blanks + DATA(char_a3) = 'ab '. + + DATA(str_a7) = `cdefgh`. + + str_a7 = char_a3. "Trailing blanks are not respected. + + "Excursion: Chaining strings + "Note the conversion result of str_a5 above (i to string) + DATA(str_a8) = str_a4 && ` ` && str_a5 && `!`. + + output->display( input = str_a3 name = `str_a3` ). + output->display( input = char_a2 name = `char_a2` ). + output->display( input = str_a7 name = `str_a7` ). + output->display( input = str_a8 name = `str_a8` ). + + output->next_section( `2) Chaining Strings` ). + + DATA(str_b1) = `Hallo`. + DATA(str_b2) = `how`. + DATA(str_b3) = `are`. + + "Chaining using && operator + DATA(str_b4) = str_b1 && ` ` && sy-uname && `, ` && str_b2 && ` ` && str_b3 && ` you?`. + + "Chaining only character literals of the same type using & operator + "Note: Such a combination of literals is possible up to 255 chars. + DATA(char_b1) = 'AB' & 'AP '. "Trailing blanks are ignored + + DATA(str_b5) = `AB` & `AP `. + + output->display( input = str_b4 name = `str_b4` ). + output->display( input = char_b1 name = `char_b1` ). + + output->next_section( `3) String Templates (1): Constructing Strings` ). + + "The expression must be convertible to a string. A blank (not + "within the curly brackets) means a blank in the resulting string. + DATA(str_c1) = `Hallo`. "String created with backquotes + DATA(str_c2) = `how`. "Strings created with pipes + DATA(str_c3) = `are`. + DATA(str_c4) = |{ str_c1 } { sy-uname }, | && + |{ str_c2 } { str_c3 } you?|. + + "Interpretation of character combinations as control characters + "\n interpreted as a line break + DATA(str_c5) = |{ str_c1 }\n{ sy-uname },| && + |\n{ str_c2 }\n{ str_c3 }\nyou?|. + + output->display( input = str_c4 name = `str_c4` ). + output->display( input = str_c5 name = `str_c5` ). + + output->next_section( `4) String Templates (2): Formatting Options` ). + "Time, date + DATA(str_d1) = + |Date: { cl_abap_context_info=>get_system_date( ) DATE = USER }\n| && + |Time: { cl_abap_context_info=>get_system_time( ) TIME = ISO }\n| && + |Timestamp: { utclong_current( ) TIMESTAMP = SPACE }|. + + "Upper, lower case + DATA(str_d2) = |AbCdEfG|. + DATA(str_d3) = |{ str_d2 CASE = LOWER }|. + DATA(str_d4) = |{ str_d2 CASE = UPPER }|. + + "Width and alignment + DATA(str_d5) = |{ 'Left' WIDTH = 20 ALIGN = LEFT }<---|. + DATA(str_d6) = |{ 'Center' WIDTH = 20 ALIGN = CENTER }<---|. + DATA(str_d7) = |{ 'Right' WIDTH = 20 ALIGN = RIGHT }<---|. + DATA(str_d8) = |{ 'Left' WIDTH = 20 ALIGN = LEFT PAD = '.' }<---|. + DATA(str_d9) = |{ 'Center' WIDTH = 20 ALIGN = CENTER PAD = '.' }<---|. + DATA(str_d10) = |{ 'Right' WIDTH = 20 ALIGN = RIGHT PAD = '.' }<---|. + + "Numbers + DATA(str_d11) = |{ - 2 / 3 DECIMALS = 3 }, { + CONV decfloat34( - 2 / 3 ) DECIMALS = 3 }, { + CONV f( - 2 / 3 ) DECIMALS = 3 }|. + + "Country-specific format + "The example shows USA and Germany. + "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. In that case, also + "comment in the display methods for these data objects further down. +* SET COUNTRY 'US' . +* DATA(str_d12) = |{ 1000000 NUMBER = ENVIRONMENT }|. +* +* SET COUNTRY 'DE' . +* DATA(str_d13) = |{ 1000000 NUMBER = ENVIRONMENT }|. + + "Escaping \|{} in string templates + DATA(str_d14) = |\\ \| \{ \}|. + + output->display( input = str_d1 name = `str_d1` ). + output->display( input = str_d3 name = `str_d3` ). + output->display( input = str_d4 name = `str_d4` ). + output->display( input = str_d5 name = `str_d5` ). + output->display( input = str_d6 name = `str_d6` ). + output->display( input = str_d7 name = `str_d7` ). + output->display( input = str_d8 name = `str_d8` ). + output->display( input = str_d9 name = `str_d9` ). + output->display( input = str_d10 name = `str_d10` ). + output->display( input = str_d11 name = `str_d11` ). +* output->display( input = str_d12 name = `str_d12` ). +* output->display( input = str_d13 name = `str_d13` ). + output->display( input = str_d14 name = `str_d14` ). + + output->next_section( `5) Determining the Length of Strings` ). + + DATA(str_e1) = `abc def ghi `. + DATA(char_e1) = 'abc def ghi '. + + "strlen + "Result depends on the type of the data object + "Fixed-length string ignores trailing blanks + DATA(length_e1) = strlen( str_e1 ). + DATA(length_e2) = strlen( char_e1 ). + + "numofchar + "To exclude trailing blanks in any case. + DATA(length_e3) = numofchar( str_e1 ). + DATA(length_e4) = numofchar( char_e1 ). + + "Excursion: + "To emphasizes modern, expression-enabled ABAP, the expression + "with the string function can be placed directly in the DO + "statement instead of having an extra variable. + DATA(str_e3) = `abcde`. + DATA(length_e5) = strlen( str_e3 ). + DATA(int_e1) = 0. + + DO length_e5 TIMES. + int_e1 += 1. + ENDDO. + + DATA(int_e2) = 0. + + DO strlen( str_e3 ) TIMES. + int_e2 += 1. + ENDDO. + + output->display( input = length_e1 name = `length_e1` ). + output->display( input = length_e2 name = `length_e2` ). + output->display( input = length_e3 name = `length_e3` ). + output->display( input = length_e4 name = `length_e4` ). + output->display( input = int_e1 name = `int_e1` ). + output->display( input = int_e2 name = `int_e2` ). + + output->next_section( `6) Concatenating Strings` ). + + DATA(str_f1) = `Hallo`. + DATA(str_f2) = `world`. + + "Concatenation using && operator and string templates + DATA(str_f3) = str_f1 && str_f2. + DATA(str_f4) = str_f1 && ` ` && str_f2. + DATA(str_f5) = |{ str_f1 } { str_f2 }|. + + "CONCATENATE statements + CONCATENATE str_f1 str_f2 INTO DATA(str_f6). + "Adding a separation sign using the addition SEPARATED BY + CONCATENATE str_f1 str_f2 INTO DATA(str_f7) SEPARATED BY ` `. + CONCATENATE str_f1 str_f2 INTO DATA(str_f8) SEPARATED BY `#`. + + DATA(char_f1) = '2 trailing blanks: '. + DATA(char_f2) = '3 trailing blanks: '. + DATA(char_f3) = '<-'. + "Keeping trailing blanks in the result when concatenating + "fixed-length strings. The ones of variable-length strings are + "respected by default + CONCATENATE char_f1 char_f2 char_f3 + INTO DATA(char_f4) RESPECTING BLANKS. + "Trailing blanks are ignored + CONCATENATE char_f1 char_f2 char_f3 INTO DATA(char_f5). + + "Example use case: Concatenating smaller text fragments + "sequentially into a longer character sequence. + DATA: itab_g TYPE TABLE OF string, + alphabet1 TYPE string. + + itab_g = VALUE #( ( `abc` ) ( `def` ) ( `ghi` ) ). + + LOOP AT itab_g ASSIGNING FIELD-SYMBOL(). + alphabet1 = alphabet1 && . + "Alternative: + "CONCATENATE alphabet INTO alphabet. + ENDLOOP. + + "Avoiding loops if your use case is to concatenate lines of an + "internal table into a string in one go + CONCATENATE LINES OF itab_g INTO DATA(alphabet2). + + ""Adding a separation sign using the addition SEPARATED BY + CONCATENATE LINES OF itab_g INTO DATA(alphabet3) + SEPARATED BY ` `. + + "String function concat_lines_of + DATA(alphabet4) = concat_lines_of( table = itab_g ). + "sep parameter specifying the separation sign + DATA(alphabet5) = concat_lines_of( table = itab_g sep = `,` ). + + output->display( input = str_f3 name = `str_f3` ). + output->display( input = str_f4 name = `str_f4` ). + output->display( input = str_f5 name = `str_f5` ). + output->display( input = str_f6 name = `str_f6` ). + output->display( input = str_f7 name = `str_f7` ). + output->display( input = str_f8 name = `str_f8` ). + output->display( input = char_f4 name = `char_f4` ). + output->display( input = char_f5 name = `char_f5` ). + output->display( input = alphabet1 name = `alphabet1` ). + output->display( input = alphabet2 name = `alphabet2` ). + output->display( input = alphabet3 name = `alphabet3` ). + output->display( input = alphabet4 name = `alphabet4` ). + output->display( input = alphabet5 name = `alphabet5` ). + + output->next_section( `7) Splitting Strings` ). + + DATA(str_g1) = `Hallo,world,12345`. + + SPLIT str_g1 AT `,` INTO DATA(str_g2) DATA(str_g3) DATA(str_g4). + + "Less data objects than possible splittings + SPLIT str_g1 AT `,` INTO DATA(str_g5) DATA(str_g6). + + "Splitting string into an internal table + DATA itab_g1 TYPE TABLE OF string. + + SPLIT str_g1 AT ',' INTO TABLE itab_g1. + + "Getting the value of a specific segment + DATA(str_g7) = segment( val = str_g1 index = 2 sep = `,` ). + + "Example with segment + "A string is split and the values of segments are retrieved. Here, + "all segments are retrieved and inserted into an internal table + "using a DO loop. If you specify an empty string, an exception of + "the class CX_SY_STRG_PAR_VAL is raised. This is true for this + "example since the DO loop inevitably runs into the error because + "of not specifying an appropriate number of loops. Note that + "if the index parameter of the segment function is positive, the + "occurrences are counted from the left. If index is negative, the + "occurrences are counted from the right. + DATA itab_g2 TYPE TABLE OF string. + DO. + TRY. + DATA(str_g8) = segment( val = str_g1 + index = sy-index + sep = `,` ). + + APPEND |Segment value: '{ str_g8 }' | && + |Segment index: '{ sy-index }'| TO itab_g2. + + CATCH cx_sy_strg_par_val. + DATA(seg_nom) = |There are { sy-index - 1 } | && + |segments in the string.|. + EXIT. + ENDTRY. + ENDDO. + + output->display( input = str_g2 name = `str_g2` ). + output->display( input = str_g3 name = `str_g3` ). + output->display( input = str_g4 name = `str_g4` ). + output->display( input = str_g5 name = `str_g5` ). + output->display( input = str_g6 name = `str_g6` ). + output->display( input = itab_g1 name = `itab_g1` ). + output->display( input = str_g7 name = `str_g7` ). + output->display( input = str_g8 name = `str_g8` ). + output->display( input = itab_g2 name = `itab_g2` ). + output->display( input = seg_nom name = `seg_nom` ). + + output->next_section( `Modifying Strings` ). + output->display( `8) Transforming to Lower and Upper Case` ). + + DATA(str_h1) = `It's a string`. + DATA(str_h2) = str_h1. + + "The string functions store the result in a target variable. + DATA(str_h3) = to_upper( str_h1 ). + DATA(str_h4) = to_lower( str_h1 ). + + "TRANSLATE does the transformation on the source variable. + TRANSLATE str_h1 TO UPPER CASE. + TRANSLATE str_h2 TO LOWER CASE. + + "to_mixed/from_mixed functions + "sep: Specifies the separator + "case: A character-like text field. A small character specifies + "that the first character of the string is in lower case. If the + "specification is, for example, case = 'X', the first character + "is capitalized. + "min: A positive number that specifies the minimum number of + "characters that must appear before the separator. The default + "value is 1. + DATA(str_h5) = `A_GREAT_STRING`. + DATA(str_h6) = to_mixed( val = str_h5 sep = `_` ). + DATA(str_h7) = to_mixed( val = str_h5 sep = `_` case = 'x' ). + DATA(str_h8) = to_mixed( val = str_h5 sep = `_` + case = 'a' min = 3 ). + + DATA(str_h9) = from_mixed( val = `someGreatString` sep = ` ` + case = 'a' min = 4 ). + + output->display( input = str_h3 name = `str_h3` ). + output->display( input = str_h4 name = `str_h4` ). + output->display( input = str_h1 name = `str_h1` ). + output->display( input = str_h2 name = `str_h2` ). + output->display( input = str_h6 name = `str_h6` ). + output->display( input = str_h7 name = `str_h7` ). + output->display( input = str_h8 name = `str_h8` ). + output->display( input = str_h9 name = `str_h9` ). + + output->next_section( `9) Shifting Content in Strings` ). + + DATA(str_i1) = `hallo`. + DATA(str_i2) = str_i1. + DATA(str_i3) = str_i1. + DATA(str_i4) = str_i1. + + "No addition; string is shifted one place to the left + SHIFT str_i2. + + "Shifting string by n places; without direction, + "left by default + SHIFT str_i3 BY 2 PLACES. + + "Direction explicitly specified + "Variable-length strings are extended + SHIFT str_i4 BY 3 PLACES RIGHT. + + DATA(char_i1) = 'world '. + DATA(char_i2) = char_i1. + DATA(char_i3) = char_i1. + DATA(str_i5) = `world `. + + "Comparison of behavior for fixed- and variable-length strings + SHIFT char_i1 BY 3 PLACES RIGHT. + SHIFT str_i5 BY 3 PLACES RIGHT. + + "CIRCULAR addition: characters that are moved out of the string are + "added at the other end again + SHIFT char_i2 BY 3 PLACES RIGHT CIRCULAR. + SHIFT char_i3 BY 2 PLACES LEFT CIRCULAR. + + DATA(str_i6) = ` hallo world `. + DATA(str_i7) = str_i6. + + "Moving characters up to a specific character set + SHIFT str_i6 UP TO 'or'. + + "Deleting leading and trailing characters with this sequence + "of statements + SHIFT str_i7 RIGHT DELETING TRAILING ` `. + SHIFT str_i7 LEFT DELETING LEADING ` `. + + "String functions storing the result in a target variable + DATA(str_i8) = `some string`. + + "shift_left + DATA(str_i9) = shift_left( val = str_i8 places = 3 ). + + DATA(str_i10) = shift_left( val = str_i8 circular = 7 ). + + "shift_right + "Note: When the parameter places is specified, the function + "shift_right has a different behavior than the SHIFT statement. + "Here, the length of the string is reduced. SHIFT extends the + "length or it remains the same. + DATA(str_i11) = shift_right( val = str_i8 places = 3 ). + + DATA(str_i12) = `shift_right and trailing blanks `. + + "sub: Specifying a substring; all substrings in the string that + "match the value are removed (sub also available for shift_left) + DATA(str_i13) = shift_right( val = str_i12 + sub = ` and trailing blanks ` ). + + DATA(str_i14) = shift_right( val = str_i12 sub = ` ` ). + + DATA(str_i15) = shift_right( val = str_i12 ). "Same effect as above + + output->display( `SHIFT statements:` ). + output->display( input = str_i2 name = `str_i2` ). + output->display( input = str_i3 name = `str_i3` ). + output->display( input = str_i4 name = `str_i4` ). + output->display( input = char_i1 name = `char_i1` ). + output->display( input = str_i5 name = `str_i5` ). + output->display( input = char_i2 name = `char_i2` ). + output->display( input = char_i3 name = `char_i3` ). + output->display( input = str_i6 name = `str_i6` ). + output->display( input = str_i7 name = `str_i7` ). + + output->display( `String functions:` ). + output->display( input = str_i9 name = `str_i9` ). + output->display( input = str_i10 name = `str_i10` ). + output->display( input = str_i11 name = `str_i11` ). + output->display( input = str_i13 name = `str_i13` ). + output->display( input = str_i14 name = `str_i14` ). + output->display( input = str_i15 name = `str_i15` ). + + output->next_section( `10) Condensing Strings` ). + + DATA(char_j1) = ' some text '. + DATA(char_j2) = ' some more text '. + DATA(char_j3) = ' a third text field literal '. + + "No addition: Removes leading and trailing blanks. This is also + "true for multiple blanks. It also replaces sequences of multiple + "blanks with a single blank. + CONDENSE char_j1. + CONDENSE char_j2. + + "NO-GAPS: Removes all blanks, also between words. When NO-GAPS + "is used with variable-length strings, trailing blanks remain + "removed. + CONDENSE char_j3 NO-GAPS. + + "RESPECTING BLANKS: Avoiding condensing + "A use case might be the assignment of strings with fixed- to + "variable-length strings. + DATA(char_j4) = ' abcef '. + DATA(char_j5) = ' ghij '. + DATA str_j TYPE string. + + "Result: ' abcef ghij ' + CONCATENATE char_j4 char_j5 INTO str_j RESPECTING BLANKS. + + "String function condense + "The advantage of using the string functions is + "that you can also specify random characters to be removed and + "not only blanks. + DATA(str_j1) = ` hi there `. + + "No parameters specified (i. e. their default values are provided); + "works like CONDENSE statements without the NO-GAPS addition + DATA(str_j2) = condense( str_j1 ). + + "Parameter 'from' specified with an initial string, 'del'/'to' not + "specified: Removes leading and trailing blanks. The 'from' + "parameter could also be specified with a text field literal: + "from = ' ' + DATA(str_j3) = condense( val = str_j1 from = `` ). + + "Parameter 'to' specified with an initial string, 'from'/'del' not + "specified: works like CONDENSE statements with the NO-GAPS + "addition + DATA(str_j4) = condense( val = str_j1 to = `` ). + + DATA(str_j5) = `ZZseeZZZyouZZ`. + DATA(str_j6) = condense( val = str_j5 del = `Z` ). + + "Parameters 'from', 'to' and 'del' are specified: Leading and + "trailing characters specified in 'del' are first removed. Then, + "in the remaining string, all substrings composed of characters + "specified in 'from' are replaced with the first character of the + "string specified in the 'to' parameter (in the example, it is a + "blank; the characters 'a', 'b', 'c' are not respected at all). + DATA(str_j7) = condense( val = str_j5 + del = `Z` + from = `Z` + to = ` abc` ). + + output->display( `CONDENSE statements:` ). + output->display( input = char_j1 name = `char_j1` ). + output->display( input = char_j2 name = `char_j2` ). + output->display( input = char_j3 name = `char_j3` ). + output->display( input = str_j name = `str_j` ). + output->next_section( `String function condense:` ). + output->display( input = str_j2 name = `str_j2` ). + output->display( input = str_j3 name = `str_j3` ). + output->display( input = str_j4 name = `str_j4` ). + output->display( input = str_j6 name = `str_j6` ). + output->display( input = str_j7 name = `str_j7` ). + + output->next_section( `11) Reversing Strings` ). + + DATA(str_k) = reverse( `ollah` ). + + output->display( input = str_k name = `str_k` ). + + output->next_section( `12) Inserting Substrings into Strings` ). + + DATA(str_l1) = `abcghi`. + + "Inserting into specific position + DATA(str_l2) = insert( val = str_l1 sub = `def` off = 3 ). + + "off is optional. If not specified (default value off = 0) + "the result is like concatenating a string with && + DATA(str_l3) = insert( val = str_l1 sub = `def` ). + + DATA(str_l4) = `def` && str_l1. + + output->display( input = str_l2 name = `str_l2` ). + output->display( input = str_l3 name = `str_l3` ). + output->display( input = str_l4 name = `str_l4` ). + + output->next_section( `13) Processing Substrings` ). + + DATA(str_m1) = `Lorem ipsum dolor sit amet`. + + "Extracting substring starting at a specific position + "'len' not specified means the rest of the remaining characters are + "respected + DATA(str_m2) = substring( val = str_m1 off = 6 ). + + "Extracting substring with a specific length + "'off' is not specified and has the default value 0. + DATA(str_m3) = substring( val = str_m1 len = 5 ). + + "Specifying both off and len parameters + DATA(str_m4) = substring( val = str_m1 off = 6 len = 5 ). + + "Excursion: Getting last character of a string + DATA(str_m5) = substring( val = str_m1 + off = strlen( str_m1 ) - 1 + len = 1 ). "t + + "Offset and length specification using the + sign after a variable + "After +, it is the offset, length is specified within parentheses. + DATA(str_m6) = str_m1+0(5). + + "* means respecting the rest of the remaining string + DATA(str_m7) = str_m1+12(*). + + "Excursion: Write access on substrings in fixed-length strings + DATA(char_m1) = 'Lorem ipsum dolor sit amet'. + DATA(char_m2) = char_m1. + DATA(char_m3) = char_m1. + + "Deleting content + CLEAR char_m2+11(*). + "Modifying string + char_m3+0(5) = 'abcde'. + + "More string functions to access substrings + "Note that lots of parameters are possible (not all covered here). + DATA(str_m8) = `aa1bb2aa3bb4`. + + "Extracting a substring ... + "... after a specified substring + DATA(str_m9) = substring_after( val = str_m8 sub = `aa` ). + + "... after a specified substring specifying the occurence in a + "string and restricting the length + DATA(str_m10) = substring_after( val = str_m8 sub = `aa` + occ = 2 len = 4 ). + + "... before a specified substring + DATA(str_m11) = substring_before( val = str_m8 sub = `b2` ). + + "... from a specified substring on. It includes the substring + "specified in sub. len/off and other parameters are possible. + DATA(str_m12) = substring_from( val = str_m8 sub = `a3` ). + + "... up to a specified substring. It includes the substring + "specified in sub. len/off and other parameters are possible. + "aa1bb2aa3b + DATA(str_m13) = substring_to( val = str_m8 sub = `3b` ). + + output->display( input = str_m2 name = `str_m2` ). + output->display( input = str_m3 name = `str_m3` ). + output->display( input = str_m4 name = `str_m4` ). + output->display( input = str_m5 name = `str_m5` ). + output->display( input = str_m6 name = `str_m6` ). + output->display( input = str_m7 name = `str_m7` ). + output->display( input = char_m2 name = `char_m2` ). + output->display( input = char_m3 name = `char_m3` ). + output->display( input = str_m9 name = `str_m9` ). + output->display( input = str_m10 name = `str_m10` ). + output->display( input = str_m11 name = `str_m11` ). + output->display( input = str_m12 name = `str_m12` ). + output->display( input = str_m13 name = `str_m13` ). + + output->next_section( `Searching and Replacing in Strings` ). + output->display( `14) Searching Specific Characters in Strings ` && + `Using Comparison Operators` ). + + DATA(str_n1) = `cheers`. + + "CA (contains any) + "sy-fdpos contains the offset of the first found character. + "If nothing is found, sy-fdpos contains the length of the string. + "Note that position 0 stands for the very first position. + IF str_n1 CA `aeiou`. + output->display( |CA: str_n1 contains any of the characters. | && + |The position of the first found character is { sy-fdpos }.| ). + ELSE. + output->display( |CA: str_n1 does not contain any of the characters. | && + |The length of str_n1 is { sy-fdpos }.| ). + ENDIF. + + "NA (contains not any) + IF str_n1 NA `xyz`. + output->display( |NA: str_n1 does not contain any of the characters.| && + |The length of str_n1 is { sy-fdpos }.| + ). + ELSE. + output->display( |NA: str_n1 contains any of the characters. | && + |The position of the first found character is { sy-fdpos }.| ). + ENDIF. + + "String functions to determine the offset of any character ... + "Note: If nothing is found, the value -1 is returned. + "There are more parameters possible. + "... contained in a substring + DATA(off_n1) = find_any_of( val = str_n1 sub = `aeiou` ). + output->display( input = off_n1 name = `off_n1` ). + + "... not contained in a substring + DATA(off_n2) = find_any_not_of( val = str_n1 sub = `xyz` ). + output->display( input = off_n2 name = `off_n2` ). + + "String functions to determine the total number of occurrences + DATA(occ_n1) = count_any_of( val = str_n1 sub = `e` ). + output->display( input = occ_n1 name = `occ_n1` ). + + DATA(occ_n2) = count_any_not_of( val = str_n1 sub = `s` ). + output->display( input = occ_n2 name = `occ_n2` ). + + "Determining if a string is exclusively composed of a certain + "character set + IF str_n1 CO `rs`. + output->display( |CO: str_n1 contains only the characters. | + && |The length of str_n1 is { sy-fdpos }.| ). + ELSE. + output->display( |CO: str_n1 does not contain only the characters. | + && |Offset of the first character in str_n1 that is not | + && |contained in the second operand: { sy-fdpos }.| ). + ENDIF. + + "Negation of CO + IF str_n1 CN `chers`. + output->display( |CN: str_n1 does not contain only the characters. | + && |Offset of the first character in str_n1 that is | + && |not contained in the second operand: { sy-fdpos }.| + ). + ELSE. + output->display( |CN: str_n1 contains only the characters. | + && |The length of str_n1 is { sy-fdpos }.| ). + ENDIF. + + output->next_section( `15) Replacing Specific Characters in Strings` ). + + DATA(str_o1) = `___abc_def_____ghi_`. + + "The replacement is done as follows: Each character specified in + "'from' is replaced by the character in 'to' that is on the same + "position, i. e. the second character in 'from' is replaced by the + "second character specified in 'to'. If there is no equivalent in + "'to', the character in 'from' is removed from the result. + + "abcdefgZZ + DATA(str_o2) = translate( val = str_o1 from = `hi_` to = `ZZ` ). + + "ZZZabcZdefZZZZZghiZ + DATA(str_o3) = translate( val = str_o1 from = `_` to = `ZZ` ). + + "TRANSLATE statement. The value after USING is interpreted as a + "string composed of character pairs. Starting with the first pair, + "a search is performed in text for the first character in every + "pair and each occurrence is replaced with the second character of + "the pair. + "...Zbc.def.....Yhi. + TRANSLATE str_o1 USING `_.aZgY`. + + output->display( input = str_o2 name = `str_o2` ). + output->display( input = str_o3 name = `str_o3` ). + output->display( input = str_o1 name = `str_o1` ). + + output->next_section( `16) Searching for Substrings in Strings` ). + DATA(str_p1) = `cheers`. + + "CS (contains string) + "sy-fdpos contains the offset of the found substring. + "If the substring is not found, sy-fdpos contains the length of the + "searched string. + IF str_p1 CS `rs`. + output->display( |CS: str_p contains the substring. | + && |The offset is { sy-fdpos }.| ). + ELSE. + output->display( |CS: str_p does not contain the substring. | + && |The length of str_p is { sy-fdpos }.| ). + ENDIF. + + "NS (contains no string) + IF str_p1 NS `abc`. + output->display( |NS: str_p does not contain the substring. | + && |The length of str_p is { sy-fdpos }.| ). + ELSE. + output->display( |NS: str_p contains the substring. | + && |The offset is { sy-fdpos }.| ). + ENDIF. + + DATA(str_p2) = `Pieces of cakes.`. + + "Specifying case-sensitivity + DATA(off_p1) = find( val = str_p2 sub = `OF` case = abap_false ). + + "No finding, result: -1 + DATA(off_p2) = find( val = str_p2 sub = `hallo` ). + + "Specifying the offset and length + DATA(off_p3) = find( val = str_p2 sub = `ce` off = 1 len = 7 ). + + "Parameter occ: A positive value means the nth position from the + "left, a negative value the nth position from the right. + DATA(off_p4) = find( val = str_p2 sub = `es` occ = -1 ). + + "Determining how often a substring occurs + DATA(cnt_p1) = count( val = str_p2 sub = `es` ). + + "FIND statements + DATA(str_p3) = `abc def ghi abc`. + + "sy-subrc is set on which you can react. + FIND `def` IN str_p3. + + IF sy-subrc = 0. + DATA(str_p4) = `"def" was found`. + ELSE. + str_p4 = `"def" was not found`. + ENDIF. + + "Addition SUBSTRING is optional + FIND SUBSTRING `abc` IN str_p3. + + IF sy-subrc = 0. + DATA(str_p5) = `"abc" was found`. + ELSE. + str_p5 = `"abc" was not found`. + ENDIF. + + "Case-insensitive search + FIND `aBC` IN str_p3 IGNORING CASE. + + IF sy-subrc = 0. + DATA(str_p6) = `"aBC" was found ignoring the case`. + ELSE. + str_p6 = `"aBC" was not found ignoring the case`. + ENDIF. + + "MATCH additions can be specified individually or combined + FIND ALL OCCURRENCES OF `abc` IN str_p3 + MATCH COUNT DATA(cnt_p2) "number of findings + MATCH OFFSET DATA(off_p5) "offset of last finding + MATCH LENGTH DATA(len_p1). "length of last finding + + "Finding the first occurrence and returning the offset + FIND FIRST OCCURRENCE OF `abc` IN str_p3 MATCH OFFSET DATA(off_p6). + + "Returning all of these pieces of information in a table for all + "findings + FIND ALL OCCURRENCES OF `abc` IN str_p3 RESULTS DATA(res_p1). + + "Restricting the search area (OFFSET/LENGTH can be specified + "individually) + FIND `abc` IN SECTION OFFSET 4 LENGTH 11 OF str_p3 + MATCH OFFSET DATA(off_p7). "12 + + "Searching in internal tables; search results are returned in an + "internal table + DATA(str_table) = VALUE string_table( ( `ZxZ` ) ( `yZ` ) ( `Zz` ) ). + + FIND ALL OCCURRENCES OF `Z` IN TABLE str_table RESULTS + DATA(res_p2) RESPECTING CASE. + + output->display( input = off_p1 name = `off_p1` ). + output->display( input = off_p2 name = `off_p2` ). + output->display( input = off_p3 name = `off_p3` ). + output->display( input = off_p4 name = `off_p4` ). + output->display( input = cnt_p1 name = `cnt_p1` ). + output->display( input = str_p4 name = `str_p4` ). + output->display( input = str_p5 name = `str_p5` ). + output->display( input = str_p6 name = `str_p6` ). + output->display( input = cnt_p2 name = `cnt_p2` ). + output->display( input = off_p5 name = `off_p5` ). + output->display( input = len_p1 name = `len_p1` ). + output->display( input = off_p6 name = `off_p6` ). + output->display( input = res_p1 name = `res_p1` ). + output->display( input = off_p7 name = `off_p7` ). + output->display( input = res_p2 name = `res_p2` ). + + output->next_section( `17) Replacing Substrings in Strings` ). + + DATA(str_q1) = `abc def ghi abc`. + + "replace function + "occ: Specifies the number of occurrences of the substring. The + "default value is 1, i. e. the first occurrence starting from the + "left. Setting occ to 0 means that all occurrences are respected. + DATA(str_q2) = replace( val = str_q1 + sub = `abc` + with = `###` ). + + "Specifying case (case sensitivity) and occ + DATA(str_q3) = replace( val = str_q1 + sub = `ABC` + with = `###` + case = abap_false + occ = 2 ). + + "All occurrences are respected + DATA(str_q4) = replace( val = str_q1 + sub = `abc` + with = `###` + occ = 0 ). + + DATA(str_q5) = str_q1. + DATA(str_q6) = str_q1. + DATA(str_q7) = str_q1. + DATA(str_q8) = str_q1. + DATA(str_q9) = str_q1. + DATA(str_q10) = str_q1. + DATA(str_q11) = str_q1. + DATA(str_q12) = str_q1. + DATA(str_q13) = str_q1. + + "REPLACE statements with selected additions + REPLACE `def` IN str_q5 WITH `###`. "abc ### ghi abc + + "### def ghi abc (explicitly using FIRST OCCURRENCE; first found + "is replaced) + REPLACE FIRST OCCURRENCE OF `abc` IN str_q6 WITH `###`. + + "### def ghi abc (first found is replaced) + REPLACE `abc` IN str_q7 WITH `###`. + + "### def ghi abc (SUBSTRING is optional) + REPLACE SUBSTRING `abc` IN str_q8 WITH `###`. + + "### def ghi ### (all occurrences are respected) + REPLACE ALL OCCURRENCES OF `abc` IN str_q9 WITH `###`. + + REPLACE `aBC` IN str_q10 WITH `###` IGNORING CASE. "### def ghi abc + + "REPLACEMENT additions; can be specified individually or combined + "### def ghi ### + REPLACE ALL OCCURRENCES OF `abc` IN str_q11 WITH `###` + REPLACEMENT COUNT DATA(cnt_q1) "number of replacements + REPLACEMENT OFFSET DATA(off_q1) "offset of last replacement + REPLACEMENT LENGTH DATA(len_q1). "length of last substr. inserted + + "Returning all of these pieces of information in a table for all + " replacements + REPLACE ALL OCCURRENCES OF `abc` IN str_q12 WITH `###` + RESULTS DATA(res_q1). "### def ghi ### + + "Position-based replacement (OFFSET/LENGTH can be specified + "individually) + "abc ### abc + REPLACE SECTION OFFSET 4 LENGTH 7 OF str_q13 WITH `###`. + + "Replacements in internal tables + DATA(str_tab) = VALUE string_table( ( `ZxZ` ) ( `yZ` ) ( `Zz` ) ). + + REPLACE ALL OCCURRENCES OF `Z` + IN TABLE str_tab WITH `` + RESPECTING CASE. "x / y / z + + output->display( input = str_q2 name = `str_q2` ). + output->display( input = str_q3 name = `str_q3` ). + output->display( input = str_q4 name = `str_q4` ). + output->display( input = str_q5 name = `str_q5` ). + output->display( input = str_q6 name = `str_q6` ). + output->display( input = str_q7 name = `str_q7` ). + output->display( input = str_q8 name = `str_q8` ). + output->display( input = str_q9 name = `str_q9` ). + output->display( input = str_q10 name = `str_q10` ). + output->display( input = str_q11 name = `str_q11` ). + output->display( input = cnt_q1 name = `cnt_q1` ). + output->display( input = off_q1 name = `off_q1` ). + output->display( input = len_q1 name = `len_q1` ). + output->display( input = str_q12 name = `str_q12` ). + output->display( input = res_q1 name = `res_q1` ). + output->display( input = str_q13 name = `str_q13` ). + output->display( input = str_tab name = `str_tab` ). + + output->next_section( `Pattern-Based Searching and Replacing in Strings` ). + output->display( `18) Simple Pattern-Based Searching ` && + `Using Logical Operators` ). + + DATA(str_r1) = `abc_def_ghi`. + + "CP (conforms to pattern) + "*: Any character sequence (including blanks). + "+: Any character (only one character, including blanks). + "#: Escaping symbol. The following character is marked for an exact + "comparison. + "Note: Patterns are not case sensitive except for characters marked + "by #. If a pattern is found, the system variable sy-fdpos returns + "the offset of the first finding. Otherwise, it contains the length + "of the searched string. + IF str_r1 CP `*f#_*`. + output->display( |CP: str_r1 covers the pattern. | + && |The offset is { sy-fdpos }.| ). + ELSE. + output->display( |CP: str_r1 does not cover the pattern. | + && |The length of str_r1 is { sy-fdpos }.| ). + ENDIF. + + "NP (does not conform to pattern) + IF str_r1 NP `i+`. + output->display( |NP: str_r1 does not cover the pattern. | + && |The length of str_r1 is { sy-fdpos }.| ). + ELSE. + output->display( |NP: str_r1 covers the pattern. | + && |The offset is { sy-fdpos }.| ). + ENDIF. + + output->next_section( `19) Complex Searching Using ` && + `Regular Expressions` ). + + DATA(str_s1) = `Cathy's black cat was fast asleep on the mat. ` && + `Later that day, the cat played with Matt.`. + + "Determining the position of the first finding + "here, parameter occ is 1 by default + DATA(off_s1) = find( val = str_s1 pcre = `at.` ). + + "Determining the number of findings + "Considers all 'a' characters not followed by 't', + "all 'at' plus 'att' + DATA(cnt_s1) = count( val = str_s1 pcre = `at*` ). + "Considers all 'at' plus 'att' and so on + DATA(cnt_s2) = count( val = str_s1 pcre = `at+` ). + + "String function match + "Extracting a substring matching a given pattern + DATA(str_w_email) = `The email address is jon.doe@email.com.`. + DATA(str_s2) = match( val = str_w_email + pcre = `\w+(\.\w+)*@(\w+\.)+(\w{2,4})` ). + + "Predicate function matches + "Checking a string if it matches a given pattern + DATA(email) = `jon.doe@email.com`. + + IF matches( val = email + pcre = `\w+(\.\w+)*@(\w+\.)+(\w{2,4})` ). + email = |{ email } is a valid email address.|. + ELSE. + email = |{ email } is not a valid email address.|. + ENDIF. + + output->display( input = off_s1 name = `off_s1` ). + output->display( input = cnt_s1 name = `cnt_s1` ). + output->display( input = cnt_s2 name = `cnt_s2` ). + output->display( input = str_s2 name = `str_s2` ). + output->display( input = email name = `email` ). + + output->next_section( `20) Replacing Using Regular Expressions` ). + + DATA(str_t) = `Cathy's black cat was fast asleep on the mat. ` && + `Later that day, the cat played with Matt.`. + + "Considers all 'a' characters not followed by 't', + "all 'at' plus 'att' + "occ = 0 -> replaces all occurrences + DATA(str_t1) = replace( val = str_t + pcre = `at*` + with = `#` + occ = 0 ). + + "Considers all 'at' plus 'att' + DATA(str_t2) = replace( val = str_t + pcre = `at+` + with = `#` + occ = 0 ). + + "Replaces 2 'e' characters in a row + DATA(str_t3) = replace( val = str_t + pcre = `e{2}` + with = `#` + occ = 0 ). + + "Replaces 'ay'. Preceding d is optional ('day' is replaced too) + DATA(str_t4) = replace( val = str_t + pcre = `d?ay` + with = `#` + occ = 0 ). + + "Subgroup specified, replacement happens if 'at' is followed + "by 'h' or 't' + DATA(str_t5) = replace( val = str_t + pcre = `at(h|t)` + with = `#` + occ = 0 ). + + "Replaces 'at' when followed by any whitespace character + DATA(str_t6) = replace( val = str_t + pcre = `at\s` + with = `#` + occ = 0 ). + + "Replacement starts at beginning of string that is followed by 'c' + "Marked as not case sensitive + "Instead of ^, you could also use \A + DATA(str_t7) = replace( val = str_t + pcre = `^c` + with = `#` + case = abap_false ). + + "Replacement starts at end of string + "Instead of $, you could also use \Z + DATA(str_t8) = replace( val = str_t + pcre = `$` + with = ` Awesome!` ). + + "Replaces words starting with 'ma', ending with another character + DATA(str_t9) = replace( val = str_t + pcre = `\bma.\b` + with = `#` + case = abap_false + occ = 0 ). + + "Replaces the beginning of words with 'ma' followed by another + "character. + "Marked as not case sensitive, hence 'Mat' is considered, too. + DATA(str_t10) = replace( val = str_t + pcre = `\bma.` + with = `#` + case = abap_false + occ = 0 ). + + "Replaces a specified set of literals; if 'case = abap_false' + "is not specified, case sensitivity is respected + DATA(str_t11) = replace( val = str_t + pcre = `[eC'.,]` + with = `#` + occ = 0 ). + + "Replaces a specified value range + DATA(str_t12) = replace( val = str_t + pcre = `[a-eA-C0-9]` + with = `#` + occ = 0 ). + + "Replaces a specified value range. The example is the negation + "of the previous example. + DATA(str_t13) = replace( val = str_t + pcre = `[^a-eA-C0-9]` + with = `#` + occ = 0 ). + + DATA(str_t14) = `

Date: 12/16/2022

` && + `

Time: 10:30

`. + + "Replacements considering subgroups + "Example switches the date format from US to European + "Sequences of digits are specified as subgroups followed by / + DATA(str_t15) = replace( val = str_t14 + pcre = `(\d+)/(\d+)/` + with = `$2.$1.` ). + + "Regex pitfall: Watch greediness when using PCRE expressions + "Example: Replacing all HTML tags in a string + DATA(str_t16) = replace( val = str_t14 + pcre = `<.*>` + with = `` + occ = 0 ). "Whole string replaced + + "The following pattern considers '<' not followed by '>' which is + "specified in a negated definition for a single character in the + "brackets. Then '*' greedily, matches anything until the next '>'. + DATA(str_t17) = replace( val = str_t14 + pcre = `<[^>]*>` + with = `` + occ = 0 ). + + "Positive lookahead: Replaces colons followed by digits + DATA(str_t18) = replace( val = str_t14 + pcre = `:(?=\d+)` + with = `.` + occ = 0 ). + + "Negative lookahead: Removes colons not followed by digits + ":(?!(\d+)) + DATA(str_t19) = replace( val = str_t14 + pcre = `:(?!\d+)` + with = `` + occ = 0 ). + + "Positive Lookbehind: Replaces a digit preceded by a blank + DATA(str_t20) = replace( val = str_t14 + pcre = `(?<=\s)\d` + with = `0` + occ = 0 ). + + "Negative lookbehind: Determines the position before closing p tags + "not preceded by 4 digits + DATA(str_t21) = replace( val = str_t14 + pcre = `(?)` + with = `:00$1` + occ = 0 ). + + DATA(str_t22) = `ab apppc app`. + + "Greedy search + "The pattern matches anything before 'p'. The matching is carried + "out as often as possible. Hence, in this example the match + "stretches until the end of the string since 'p' is the final + "character, i. e. this 'p' and anything before is replaced). + DATA(str_t23) = replace( val = str_t22 + pcre = `(.*)p` + with = `#` ). + + "Non-greedy search (denoted by '?' below) + "The pattern matches anything before 'p'. The matching proceeds + "until the first 'p' is found and does not go beyond (matching as + "few as possible). Hence, the first found 'p' including the content + "before is replaced. + DATA(str_t24) = replace( val = str_t22 + pcre = `(.*?)p` + with = `#` ). + + "Replacements with subgroups + "Replaces 'PP' (case-insensitive) with '#', the content before and + "after 'PP' is switched + DATA(str_t25) = replace( val = str_t22 + pcre = `(.*?)PP(.*)` + with = `$2#$1` + case = abap_false ). + + "REPLACE statement: Changing the source field directly + REPLACE PCRE `(.*?)PP(.*)` IN str_t22 WITH `$2#$1` IGNORING CASE. + + "Replacements in internal tables + DATA(itab_t) = VALUE + string_table( ( `Cathy's black cat was fast asleep on the mat.` ) + ( `Later that day, the cat played with Matt.` ) + ( `How about that?` ) ). + + "Replaces all 'th' occurrences in words beginning with 'th' + REPLACE ALL OCCURRENCES OF PCRE `\bth` IN TABLE itab_t WITH `#` + IGNORING CASE REPLACEMENT COUNT DATA(count_t). + + output->display( input = |Original str_t: { str_t }| ). + output->display( input = str_t1 name = `str_t1` ). + output->display( input = str_t2 name = `str_t2` ). + output->display( input = str_t3 name = `str_t3` ). + output->display( input = str_t4 name = `str_t4` ). + output->display( input = str_t5 name = `str_t5` ). + output->display( input = str_t6 name = `str_t6` ). + output->display( input = str_t7 name = `str_t7` ). + output->display( input = str_t8 name = `str_t8` ). + output->display( input = str_t9 name = `str_t9` ). + output->display( input = str_t10 name = `str_t10` ). + output->display( input = str_t11 name = `str_t11` ). + output->display( input = str_t12 name = `str_t12` ). + output->display( input = str_t13 name = `str_t13` ). + output->display( input = |Original str_t14: { str_t14 }| ). + output->display( input = str_t15 name = `str_t15` ). + output->display( input = str_t16 name = `str_t16` ). + output->display( input = str_t17 name = `str_t17` ). + output->display( input = str_t18 name = `str_t18` ). + output->display( input = str_t19 name = `str_t19` ). + output->display( input = str_t20 name = `str_t20` ). + output->display( input = str_t21 name = `str_t21` ). + output->display( input = str_t23 name = `str_t23` ). + output->display( input = str_t24 name = `str_t24` ). + output->display( input = str_t25 name = `str_t25` ). + output->display( input = str_t22 name = `str_t22` ). + output->display( input = itab_t name = `itab_t` ). + output->display( input = |Number of replacements in itab (count_t): { count_t }| ). + + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_string_proc.clas.xml b/src/zcl_demo_abap_string_proc.clas.xml new file mode 100644 index 0000000..03dfe51 --- /dev/null +++ b/src/zcl_demo_abap_string_proc.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_STRING_PROC + E + ABAP cheat sheet: String processing + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_structures.clas.abap b/src/zcl_demo_abap_structures.clas.abap new file mode 100644 index 0000000..0e8bfbf --- /dev/null +++ b/src/zcl_demo_abap_structures.clas.abap @@ -0,0 +1,1086 @@ +*********************************************************************** +* +* ABAP cheat sheet: Working with structures +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntactical options for working with +* structures as outlined in the respective ABAP cheat sheet. +* - Topics covered: creating structures and structured types, variants +* of structures, accessing components of structures, filling structures, +* clearing structures, structures in use in the context of tables +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP Development Tools (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, check the notes +* included in the class as comments or refer to the respective topic +* in the ABAP Keyword Documentation. +* - Due to the amount of output in the console, the examples include +* numbers (e. g. 1) ..., 2) ..., 3) ...) for the individual example +* sections. Plus, the variable name is displayed in most cases. Hence, +* to easier and faster find the relevant output in the console, just +* search in the console for the number/variable name (STRG+F in the +* console) or use the debugger. +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** +"!

ABAP cheat sheet: Structures

+"! Example to demonstrate working with structures.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_structures DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + + CLASS-METHODS: class_constructor. + +protected section. + PRIVATE SECTION. + "Creating structured data types + TYPES: "Flat structure + BEGIN OF gty_struc, + num1 TYPE i, + num2 TYPE i, + char1 TYPE c LENGTH 10, + char2 TYPE c LENGTH 5, + pnum TYPE p LENGTH 8 DECIMALS 2, + END OF gty_struc, + "Structures within deep structure + BEGIN OF line1, + col1 TYPE i, + col2 TYPE i, + END OF line1, + BEGIN OF line2, + col2 TYPE i, + col3 TYPE i, + col4 TYPE i, + END OF line2. + + CLASS-DATA: + "Flat structure + gs_struc TYPE gty_struc, + "Deep structure 1 + BEGIN OF gs_deep1, + comp1 TYPE c LENGTH 1 VALUE 'W', + BEGIN OF substruc, + comp1 TYPE c LENGTH 1 VALUE 'X', + BEGIN OF comp2, + col1 TYPE c LENGTH 1 VALUE 'Y', + col2 TYPE c LENGTH 1 VALUE 'Z', + END OF comp2, + END OF substruc, + itab TYPE TABLE OF line1 WITH EMPTY KEY, + END OF gs_deep1, + "Deep structure 2 + BEGIN OF gs_deep2, + BEGIN OF substruc, + comp1 TYPE string, + comp2 TYPE string, + comp3 TYPE string, + END OF substruc, + itab TYPE TABLE OF line2 WITH EMPTY KEY, + comp4 TYPE i, + END OF gs_deep2, + "Creating internal table for displaying purposes + gt_tab TYPE STANDARD TABLE OF zdemo_abap_tab1 + WITH NON-UNIQUE KEY key_field. + + CLASS-METHODS: + initialize_dbtabs, + fill_deep_structures, + select_from_dbtab. + +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_STRUCTURES IMPLEMENTATION. + + + METHOD class_constructor. + initialize_dbtabs( ). + fill_deep_structures( ). + "Filling demo database tables. + zcl_demo_abap_flight_tables=>fill_dbtabs( ). + ENDMETHOD. + + + METHOD fill_deep_structures. + "Clearing all content of gs_deep2 + CLEAR gs_deep2. + "Filling nested tables in deep structures + gs_deep2-substruc = VALUE #( comp1 = `aaa` + comp2 = `bbb` + comp3 = `ccc`). + + gs_deep1-itab = VALUE #( + ( col1 = 111 col2 = 222 ) + ( col1 = 333 col2 = 444 + ) ). + + gs_deep2-itab = VALUE #( + ( col2 = 1 col3 = 2 col4 = 3 ) + ( col2 = 4 col3 = 5 col4 = 6 ) + ( col2 = 7 col3 = 8 col4 = 9 ) + ). + + "Filling individual component that is not shared by both structures + gs_deep2-comp4 = 999. + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `Demo: Working with Structures` ). + + "The following declarations are just for demonstration purposes to + "visualize how declarations of local structures and structured + "types can look like. + + "Declaring structured type locally (flat structure) + TYPES: BEGIN OF lty_struc, + num1 TYPE i, + num2 TYPE i, + char1 TYPE c LENGTH 10, + char2 TYPE c LENGTH 5, + pnum TYPE p LENGTH 8 DECIMALS 2, + END OF lty_struc. + + "Alternatively, you could also use the syntax below. For better + "readability, use a chained statement with the colon as above. + TYPES BEGIN OF gs_struc_alt. + TYPES num1 TYPE i. + TYPES num2 TYPE i. + TYPES char1 TYPE c LENGTH 10. + TYPES char2 TYPE c LENGTH 5. + TYPES pnum TYPE p LENGTH 8 DECIMALS 2. + TYPES END OF gs_struc_alt. + + "Creating local structures + "a. Based on a local structured type. + DATA ls_struc TYPE lty_struc. + + "b. Based on global types in the DDIC + "Note: There might also be global structures or views + "which are not covered here. + DATA ls_glo_tab TYPE zdemo_abap_flsch. "database table + + "c. Directly declare data object plus defining the components + DATA: BEGIN OF ls_direct_decl, + num1 TYPE i, + num2 TYPE i, + char1 TYPE c LENGTH 10, + char2 TYPE c LENGTH 5, + pnum TYPE p LENGTH 8 DECIMALS 2, + END OF ls_direct_decl. + + "Alternatively, you could also use the syntax below. For better + "readability, use a chained statement with the colon as above. + DATA BEGIN OF ls_direct_decl_alt. + DATA num1 TYPE i. + DATA num2 TYPE i. + DATA char1 TYPE c LENGTH 10. + DATA char2 TYPE c LENGTH 5. + DATA pnum TYPE p LENGTH 8 DECIMALS 2. + DATA END OF ls_direct_decl_alt. + + "d. Based on another local structured data object + internal table + DATA ls_like_dobj LIKE ls_struc. + DATA ls_like_line_of_itab LIKE LINE OF gt_tab. + + "e. Using inline declaration. + DATA(struc_inl1) = ls_struc. + "Declaring structure inline and filling with VALUE operator + DATA(struc_inl2) = VALUE lty_struc( num1 = 1 num2 = 2 ). + + output->display( `Variants of structures` ). + output->display( `1) Flat structure with default values` ). + + "Flat structures only contain elementary data types + + "Flat structure with default values + DATA: BEGIN OF ls_flat, + num1 TYPE i VALUE 1, + num2 TYPE i VALUE 2, + char1 TYPE c LENGTH 10 VALUE 'abcdefghij', + char2 TYPE c LENGTH 5 VALUE 'klmno', + pnum TYPE p LENGTH 8 DECIMALS 2 VALUE '123.45', + END OF ls_flat. + + output->display( input = ls_flat name = `ls_flat` ). + + output->next_section( `2) Nested structure` ). + + "Nested structures contain at least one structure as component + + "Nested structure with default values + DATA: BEGIN OF ls_nested_address, + BEGIN OF name, + title TYPE string VALUE `Mr.`, + first_name TYPE string VALUE `Duncan`, + surname TYPE string VALUE `Pea`, + END OF name, + BEGIN OF street, + name TYPE string VALUE `Vegetable Lane`, + number TYPE string VALUE `11`, + END OF street, + BEGIN OF city, + zipcode TYPE string VALUE `349875`, + name TYPE string VALUE `Botanica`, + END OF city, + END OF ls_nested_address. + + output->display( input = ls_nested_address name = `ls_nested_address` ). + + output->next_section( `3) Deep structure with strings` ). + + "Deep structures contain at least one deep component, for + "example, internal tables, strings. + + "Deep structure with strings and with default values. + DATA: BEGIN OF ls_flat_address, + name TYPE string VALUE `Mr. Duncan Pea`, + street TYPE string VALUE `Vegetable Lane 11`, + city TYPE string VALUE `349875 Botanica`, + END OF ls_flat_address. + + output->display( input = ls_flat_address name = `ls_flat_address` ). + + output->next_section( `4) Deep structure with internal table as component` ). + + "Structured type for nested internal table + TYPES: BEGIN OF lty_flights, + connid TYPE zdemo_abap_flsch-connid, + countryfr TYPE zdemo_abap_flsch-countryfr, + cityfrom TYPE zdemo_abap_flsch-cityfrom, + airpfrom TYPE zdemo_abap_flsch-airpfrom, + countryto TYPE zdemo_abap_flsch-countryto, + cityto TYPE zdemo_abap_flsch-cityto, + airpto TYPE zdemo_abap_flsch-airpto, + END OF lty_flights. + + "Creating deep structure + DATA: BEGIN OF ls_flights, + carrier TYPE zdemo_abap_flsch-carrid VALUE 'LH', + carrier_name TYPE zdemo_abap_carr-carrname VALUE 'Lufthansa', + lt_flights TYPE TABLE OF lty_flights WITH EMPTY KEY, + END OF ls_flights. + + "Filling nested internal table for the output + SELECT * + FROM zdemo_abap_flsch + WHERE carrid = 'LH' + INTO CORRESPONDING FIELDS OF TABLE @ls_flights-lt_flights + UP TO 4 ROWS. + + output->display( input = ls_flights name = `ls_flights` ). + + output->next_section( `Accessing and filling structures` ). + output->display( `5) Filling structure components` && + ` using the component selector` ). + + gs_struc-num1 = 1. + gs_struc-num2 = 2. + gs_struc-char1 = 'aaa'. + gs_struc-char2 = 'bbb'. + gs_struc-pnum = '333.33'. + + output->display( input = gs_struc name = `gs_struc` ). + + output->next_section( `6) Filling structure components ` && + `using the VALUE operator` ). + + "Value assignments by addressing the structure components individually + "can be very bulky. Hence, the use of the VALUE operator is + "very handy for the value assignment, especially for filling structure + "components at operand position. In below examples the # sign is used + "before the parentheses which means that the type of the operand can be + "implicitly derived. + + "Flat structure + gs_struc = VALUE #( num1 = 3 + num2 = 4 + char1 = 'ccc' + char2 = 'ddd' + pnum = '555.55' ). + + "Nested structure + ls_nested_address = VALUE #( + name = VALUE #( title = `Mrs.` + first_name = `Jane` + surname = `Doe` ) + street = VALUE #( name = `Main Street` + number = 1 ) + city = VALUE #( zipcode = 12345 + name = `London` ) ). + + "Deep structure + ls_flights = VALUE #( + carrier = 'AA' + carrier_name = 'American Airlines' + lt_flights = VALUE #( ( connid = 17 countryfr = 'US' + cityfrom = 'New York' + airpfrom = 'JFK' + countryto = 'US' + cityto = 'San Francisco' + airpto = 'SFO' ) + ( connid = 64 + countryfr = 'US' + cityfrom = 'San Francisco' + airpfrom = 'SFO' + countryto = 'US' + cityto = 'New York' + airpto = 'JFK' ) ) ). + + output->display( input = gs_struc name = `gs_struc` ). + output->display( input = ls_nested_address name = `ls_nested_address` ). + output->display( input = ls_flights name = `ls_flights` ). + + output->next_section( `7) Creating and filling a new structure ` && + `using the VALUE operator` ). + + "In the example below in which a new structure is created by declaring + "a variable inline the '#' sign cannot be used before the parentheses + "because a type cannot be derived. Instead, the type must be + "specified before the parentheses explicitly. + + DATA(ls_copy) = VALUE gty_struc( num1 = 5 + num2 = 6 + char1 = 'ggg' + char2 = 'hhh' + pnum = '555.55' ). + + output->display( input = ls_copy name = `ls_copy` ). + + output->next_section( `8) Accessing individual components using the ` && + `component selector "-"` ). + + "Assigning value of individual component to a variable + DATA(lv_copy) = gs_struc-num1. + + "Assigning a value to a component in a nested structure. + ls_nested_address-name-first_name = 'Emma'. + + "Assigning a value to a component in a deep structure. + "The table line is determined using a table expression. + ls_flights-lt_flights[ 1 ]-cityto = 'San Fran'. + + output->display( input = lv_copy name = `lv_copy` ). + output->display( input = ls_nested_address-name-first_name name = `ls_nested_address-name-first_name` ). + output->display( input = ls_flights-lt_flights[ 1 ]-cityto name = `ls_flights-lt_flights[ 1 ]-cityto` ). + + output->next_section( `9) Excursion: Addressing components of a variable` && + ` referencing to a structure ` ). + + "Creating a data reference variable. + DATA(ref) = NEW gty_struc( ). + + "Assigning a structure to the data reference + ref->* = gs_struc. + + "Accessing a component using the object component selector + DATA(ref_comp1) = ref->char1. + + "The following syntax is also possible but less comfortable. + DATA(ref_comp2) = ref->*-char2. + + output->display( input = ref_comp1 name = `ref_comp1` ). + output->display( input = ref_comp2 name = `ref_comp2` ). + + output->next_section( `10) Using structure components for ` && + `data type and data object declarations` ). + + TYPES: lty_1 TYPE gty_struc-num1, + lty_2 LIKE gs_struc-num2. + + DATA: lv_num1 TYPE gty_struc-num1 VALUE 123, + lv_num2 LIKE gs_struc-num2 VALUE 456. + + output->display( input = lv_num1 name = `lv_num1` ). + output->display( input = lv_num2 name = `lv_num2` ). + + output->next_section( `11) Copying content of a structure to another ` && + `one that has the same type` ). + + "Note: In the case below, a MOVE-CORRESPONDING statement as shown + "further down would have the same effect: + "MOVE-CORRESPONDING gs_struc TO gs_struc_2. + + DATA gs_struc_2 TYPE gty_struc. + + gs_struc_2 = gs_struc. + + output->display( input = gs_struc_2 name = `gs_struc_2` ). + + output->next_section( `12) Copying content of a structure to another` && + ` one that has a different type using` && + ` MOVE-CORRESPONDING/CORRESPONDING operator` ). + + "Both statements with MOVE-CORRESPONDING and the CORRESPONDING + "operator are used to assign identically named components of + "structures to each other. + "Note: For value assignments, generally bear in mind that there are + "special conversion and comparison rules that apply to assignments. + "The following examples focus on flat structures. + + "Creating flat structure with different type and assigning + "default values. + DATA: BEGIN OF gs_struc_diff, + num1 TYPE i VALUE 111, + num2 TYPE i VALUE 222, + char1 TYPE c LENGTH 10 VALUE 'AAA', + c1 TYPE c LENGTH 1 VALUE 'B', + END OF gs_struc_diff. + + "Copying structure to have the same values for another syntax variant. + DATA(gs_struc_diff2) = gs_struc_diff. + DATA(gs_struc_diff3) = gs_struc_diff. + DATA(gs_struc_diff4) = gs_struc_diff. + DATA(gs_struc_diff5) = gs_struc_diff. + + output->display( `Original content of structures:` ). + output->display( input = gs_struc name = `gs_struc` ). + output->display( input = gs_struc_diff name = `gs_struc_diff` ). + + "Identically named components are moved... + "... and the content in nonidentical components of the target + "structure are kept. + MOVE-CORRESPONDING gs_struc TO gs_struc_diff. + + "... and the content in nonidentical components in the target + "structure are initialized. + gs_struc_diff2 = CORRESPONDING #( gs_struc ). + + "... and the content in nonidentical components of the target + "structure are kept. Same as MOVE-CORRESPONDING without additions. + gs_struc_diff3 = CORRESPONDING #( BASE ( gs_struc_diff3 ) + gs_struc ). + + "MAPPING addition: Specifying components of a source structure that + "are assigned to the components of a target structure in mapping + "relationships. Note the conversion and assignement rules. + gs_struc_diff4 = CORRESPONDING #( BASE ( gs_struc_diff4 ) + gs_struc MAPPING c1 = char2 ). + + "EXCEPT addition: Excluding components from the assignment. + gs_struc_diff5 = CORRESPONDING #( BASE ( gs_struc_diff5 ) + gs_struc EXCEPT num2 ). + + output->display( `Results of statements:` ). + output->display( input = gs_struc_diff name = `gs_struc_diff` ). + output->display( input = gs_struc_diff2 name = `gs_struc_diff2` ). + output->display( input = gs_struc_diff3 name = `gs_struc_diff3` ). + output->display( input = gs_struc_diff4 name = `gs_struc_diff4` ). + output->display( input = gs_struc_diff5 name = `gs_struc_diff5` ). + + output->next_section( `13) Excursion: Copying content of a deep ` && + `structure to another one by using various additions` ). + output->display( 'Original content of deep structures:' ). + + "Note: The example purposely uses non-fitting components + "to emphasize conversion and assignment rules. + + output->display( input = gs_deep1 name = `gs_deep1` ). + output->display( input = gs_deep2 name = `gs_deep2` ). + + output->display( `13a) MOVE-CORRESPONDING without additions` ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc is converted to string. Note that the two + " components in component substruc-comp2 of gs_deep1 are drawn + " together when being converted to string. + "- Content of gs_deep2-itab is replaced by table content of + " gs_deep1-itab. Value assignment, for example, + " for col2 in gs_deep2-itab: Despite the fact that there is no + " identically named component col1 in the target structure, + " values are assigned starting with the first column of the source + " structure. + + MOVE-CORRESPONDING gs_deep1 TO gs_deep2. + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + + output->next_section( `13b) MOVE-CORRESPONDING with the addition ` && + `EXPANDING NESTED TABLES` ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is replaced by table content of + " gs_deep1-itab. Due to the addition EXPANDING NESTED TABLES, the + " value assignment happens for identically named components. Hence, + " only col2 as the only shared and identically named component is + " filled. + + MOVE-CORRESPONDING gs_deep1 TO gs_deep2 EXPANDING NESTED TABLES. + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + + output->next_section( `13c) MOVE-CORRESPONDING with the addition` && + ` KEEPING TARGET LINES` ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is kept due to the addition KEEPING + " TARGET LINES and content of gs_deep1-itab is added. The value + " assignment concerning the added lines happens like the + " MOVE-CORRESPONDING statement without addition. That is, despite + " the fact that there is no identically named component col1 in + " the target structure, values are assigned starting with the + " first column of the source structure. + + MOVE-CORRESPONDING gs_deep1 TO gs_deep2 KEEPING TARGET LINES. + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + + output->next_section( `13d) MOVE-CORRESPONDING with the addition ` && + `EXPANDING NESTED TABLES KEEPING TARGET LINES` ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is kept due to the addition KEEPING + " TARGET LINES. Content of gs_deep1-itab is added. The value + " assignment concerning the added lines happens like the + " MOVE-CORRESPONDING statement with the addition EXPANDING NESTED + " TABLES. That is, the value assignment happens for identically + " named components. Hence, only col2 as the only shared and + " identically named component is filled. + + MOVE-CORRESPONDING gs_deep1 TO gs_deep2 + EXPANDING NESTED TABLES KEEPING TARGET LINES. + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + + output->next_section( `13e) CORRESPONDING operator without addition` ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " initialized. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is replaced by table content of + " gs_deep1-itab. Note the value assignment, for example, for col2 + " in gs_deep2-itab. Despite the fact that there is no identically + " named component comp1 in the target structure, values are + " assigned starting with the first column of the source structure. + + gs_deep2 = CORRESPONDING #( gs_deep1 ). + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + + output->next_section( `13f) CORRESPONDING operator with the addition` && + ` DEEP` ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " initialized. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is replaced by table content of + " gs_deep1-itab. Due to the addition DEEP, the value assignment + " happens for identically named components in the nested table. + " Hence, only col2 as the only shared and identically named + " component is filled. + + gs_deep2 = CORRESPONDING #( DEEP gs_deep1 ). + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + + output->next_section( `13g) CORRESPONDING operator with the addition` && + ` BASE` ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is replaced by table content of + " gs_deep1-itab. The value assignment in the nested table happens + " like using the CORRESPONDING operator without addition. Note the + " value assignment, for example, for col2 in gs_deep2-itab. + " Despite the fact that there is no identically named component + " col1 in the target structure, values are assigned starting with + " the first column of the source structure. + + gs_deep2 = CORRESPONDING #( BASE ( gs_deep2 ) gs_deep1 ). + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + + output->next_section( `13h) CORRESPONDING operator with the additions ` && + `DEEP BASE` ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is replaced by table content of + " gs_deep1-itab. The value assignment in the nested table happens + " like using the CORRESPONDING operator with the addition DEEP. + " That is, the value assignment happens for identically named + " components in the nested table. Hence, only col2 as the only + " shared and identically named component is filled. + + gs_deep2 = CORRESPONDING #( DEEP BASE ( gs_deep2 ) gs_deep1 ). + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + + output->next_section( `13i) CORRESPONDING operator with the additions ` && + `APPENDING` ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is kept and content of gs_deep1-itab is + " added. The value assignment concerning the added lines happens + " like using the CORRESPONDING operator without addition. Note the + " value assignment, for example, for col2 in gs_deep2- itab. + " Despite the fact that there is no identically named component + " col1 in the target structure, values are assigned starting with + " the first column of the source structure. + + gs_deep2 = CORRESPONDING #( APPENDING ( gs_deep2 ) gs_deep1 ). + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + + output->next_section( `13j) CORRESPONDING operator with the additions ` && + `DEEP APPENDING` ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is kept and content of gs_deep1-itab is + " added. The value assignment concerning the added lines happens + " like using the CORRESPONDING operator with the addition DEEP. + " That is, the value assignment happens for identically named + " components in the nested table. Hence, only col2 as the only + " shared and identically named component is filled. + "- It has the same effect as using DEEP APPENDING BASE. + + gs_deep2 = CORRESPONDING #( DEEP APPENDING ( gs_deep2 ) + gs_deep1 ). + + output->display( input = gs_deep2 name = `gs_deep2` ). + + output->next_section( `14) Clearing individual components of a ` && + `structure and the complete structure` ). + + "Clearing individual component + CLEAR gs_struc-char1. + + output->display( input = gs_struc name = `gs_struc` ). + + "Clearing the whole structure + CLEAR gs_struc. + + output->display( input = gs_struc name = `gs_struc` ). + + output->next_section( `Structures in use in the context of tables` ). + output->display( `15) Reading a line from a database table into a ` && + `structure ...` ). + output->display( `15a) ... that has a matching type` ). + + "The first entry that is found according to the WHERE condition is + "returned. Instead of creating a structure having a matching type, + "the structure can be declared inline. + + DATA ls_flsch1 TYPE zdemo_abap_flsch. + + SELECT SINGLE FROM zdemo_abap_flsch + FIELDS * + WHERE carrid = 'LH' AND connid = '0400' + INTO @ls_flsch1. + + SELECT SINGLE FROM zdemo_abap_flsch + FIELDS * + WHERE carrid = 'LH' AND connid = '0400' + INTO @DATA(ls_flsch2). + + output->display( input = ls_flsch1 name = `ls_flsch1` ). + output->display( input = ls_flsch2 name = `ls_flsch2` ). + + output->next_section( `15b) ... that has a different type` ). + + "Creating structure having a different type. + DATA: BEGIN OF ls_fli_diff, + carrid TYPE zdemo_abap_flsch-carrid, + connid TYPE zdemo_abap_flsch-connid, + countryfr TYPE zdemo_abap_flsch-countryfr, + cityfrom TYPE zdemo_abap_flsch-cityfrom, + countryto TYPE zdemo_abap_flsch-countryto, + cityto TYPE zdemo_abap_flsch-cityto, + fldate TYPE zdemo_abap_fli-fldate, + END OF ls_fli_diff. + + SELECT SINGLE FROM zdemo_abap_flsch + FIELDS * + WHERE carrid = 'JL' AND connid = '0408' + INTO CORRESPONDING FIELDS OF @ls_fli_diff. + + output->display( input = ls_fli_diff name = `ls_fli_diff` ). + + output->next_section( `16) Reading a line from an internal table into a structure ...` ). + output->display( `16a) ... using a SELECT statement` ). + + "Creating and filling an internal table to be read from + DATA itab TYPE TABLE OF zdemo_abap_flsch WITH EMPTY KEY. + SELECT FROM zdemo_abap_flsch + FIELDS * + WHERE carrid = 'LH' ORDER BY PRIMARY KEY + INTO TABLE @itab + UP TO 4 ROWS. + + "Reading from an internal table + SELECT SINGLE FROM @itab AS itab + FIELDS * + WHERE carrid = 'LH' + INTO @DATA(ls_select_itab). + + output->display( input = ls_select_itab name = `ls_select_itab` ). + + output->next_section( `16b) ... using a READ TABLE statement` ). + + "The example shows the reading of one line into a work area, field + "symbol and a data reference variable, all representing structured + "data objects and declared inline below. Here, the reading of a + "line is based on the line number by specifying INDEX. + + "Copying line into a work area + READ TABLE itab INTO DATA(ls_read_table) INDEX 1. + + "Assignment to a field symbol + READ TABLE itab ASSIGNING FIELD-SYMBOL() INDEX 2. + + "Reading into a data reference variable + READ TABLE itab REFERENCE INTO DATA(dref) INDEX 3. + + output->display( input = ls_read_table name = `ls_read_table` ). + output->display( input = name = `` ). + output->display( input = dref->* name = `dref->*` ). + + output->next_section( `16c) ... using a table expression` ). + "The line number, that is, the index, is specified in square + "brackets. + + DATA(ls_table_exp) = itab[ 3 ]. + + output->display( input = ls_table_exp name = `ls_table_exp` ). + + output->next_section( `17) Sequentially reading a line from ...` ). + output->display( `17a) ... a database table into a structure` ). + + "In the given simple example, the line that is found and returned + "in a structure, that is declared inline, is simply added to an + "internal table. + + SELECT FROM zdemo_abap_flsch + FIELDS * + WHERE carrid = 'AZ' + INTO @DATA(ls_sel_loop). + IF sy-subrc = 0. + APPEND ls_sel_loop TO itab. + ENDIF. + ENDSELECT. + + output->display( input = itab name = `itab` ). + + output->next_section( `17b) ... an internal table into a structure` ). + + "The given example covers the reading of a line into a field symbol. + "Within the loop, a modification is carried out on a component + "of the structures. + + LOOP AT itab ASSIGNING FIELD-SYMBOL() WHERE carrid <> 'LH'. + -carrid = 'XY'. + ENDLOOP. + + output->display( input = itab name = `itab` ). + + output->next_section( `18) Inserting an individual row from a structure` && + ` into a database table` ). + + "The statements in the given example can be considered as + "alternatives. The third statement demonstrates that the structure + "might also be created and filled in place instead of inserting a + "line from an existing structure. + + DATA ls_struc_db TYPE zdemo_abap_tab1. + + ls_struc_db = VALUE #( key_field = 1 + char1 = 'aaa' + char2 = 'bbb' + num1 = 2 + num2 = 3 ). + + INSERT INTO zdemo_abap_tab1 VALUES @ls_struc_db. + + "Structure filled anew with new primary key to + "avoid duplicate key error. + ls_struc_db = VALUE #( key_field = 2 + char1 = 'ccc' + char2 = 'ddd' + num1 = 4 + num2 = 5 ). + + INSERT zdemo_abap_tab1 FROM @ls_struc_db. + + INSERT zdemo_abap_tab1 FROM @( VALUE #( key_field = 3 + char1 = 'eee' + char2 = 'fff' + num1 = 6 + num2 = 7 ) ). + + select_from_dbtab( ). + output->display( input = gt_tab name = `gt_tab` ). + + output->next_section( `19) Updating an individual row from a structure` && + ` in a database table` ). + + ls_struc_db = VALUE #( key_field = 2 + char1 = 'GGG' + char2 = 'HHH' + num1 = 8 + num2 = 9 ). + + UPDATE zdemo_abap_tab1 FROM @ls_struc_db. + + UPDATE zdemo_abap_tab1 FROM @( VALUE #( key_field = 3 + char1 = 'III' + char2 = 'JJJ' + num1 = 10 + num2 = 11 ) ). + + select_from_dbtab( ). + output->display( input = gt_tab name = `gt_tab` ). + + output->next_section( `20) Excursion: Updating an individual row from a ` && + `structure in a database table without overwriting specific ` && + `components` ). + + "If you want to update a database table row from a structure by + "specifying components to be changed without overwriting other + "components, you might choose the following way. First, read the + "intended line from the database table into a structure. + "Then, use the VALUE operator with the addition BASE and specify + "the components to be changed. + + SELECT SINGLE * + FROM zdemo_abap_tab1 + WHERE key_field = 2 + INTO @DATA(wa). + + UPDATE zdemo_abap_tab1 FROM @( VALUE #( BASE wa char2 = '###' ) ). + + select_from_dbtab( ). + output->display( input = gt_tab name = `gt_tab` ). + + output->next_section( `21) Updating or creating an individual` && + ` row in a database table from a structure using MODIFY` ). + + "You can update or create an individual row in a database table + "from a structure using ABAP SQL statements with MODIFY. If a + "line in the database table already exists having the same keys as + "specified in the structure, the line gets updated. If a line does + "not exist with the keys specified in the structure, a new line is + "created in the database table. In the given example, the first + "statement demonstrates a modification of an existing line in the + "database table.The second and third statements create a new line + "in the database table. The third statement demonstrates that the + "structure might also be created and filled in place instead of + "inserting a line based on an existing structure. + + ls_struc_db = VALUE #( key_field = 1 + char1 = 'kkk' + char2 = 'lll' + num1 = 12 + num2 = 13 ). + + MODIFY zdemo_abap_tab1 FROM @ls_struc_db. + + ls_struc_db = VALUE #( key_field = 4 + char1 = 'mmm' + char2 = 'nnn' + num1 = 14 + num2 = 15 ). + + MODIFY zdemo_abap_tab1 FROM @ls_struc_db. + + MODIFY zdemo_abap_tab1 FROM @( VALUE #( key_field = 5 + char1 = 'ooo' + char2 = 'ppp' + num1 = 16 + num2 = 17 ) ). + + select_from_dbtab( ). + output->display( input = gt_tab name = `gt_tab` ). + + output->next_section( `22) Adding rows to and updating individual rows` && + ` in an internal table from a structure` ). + + "INSERT and MODIFY are ABAP statements in this context, not ABAP SQL + "statements. Both INSERT and APPEND add one line (or more) to an + "internal table. While APPEND adds at the bottom of the internal + "table, INSERT can be used to add lines at a specific position in + "tables. MODIFY changes the content of an internal table entry. + + ls_struc_db = VALUE #( key_field = 6 + char1 = 'ZZZ' + char2 = 'YYY' + num1 = 18 + num2 = 19 ). + + INSERT ls_struc_db INTO TABLE gt_tab. + + INSERT VALUE #( key_field = 7 + char1 = 'XXX' + char2 = 'WWW' + num1 = 20 + num2 = 21 ) INTO TABLE gt_tab. + + ls_struc_db = VALUE #( key_field = 8 + char1 = 'VVV' + char2 = 'UUU' + num1 = 22 + num2 = 23 ). + + APPEND ls_struc_db TO gt_tab. + + APPEND VALUE #( key_field = 9 + char1 = 'TTT' + char2 = 'SSS' + num1 = 24 + num2 = 25 ) TO gt_tab. + + ls_struc_db = VALUE #( key_field = 1 + char1 = 'RRR' + char2 = 'QQQ' + num1 = 26 + num2 = 27 ). + + MODIFY TABLE gt_tab FROM ls_struc_db. + + MODIFY TABLE gt_tab FROM VALUE #( key_field = 2 + char1 = 'PPP' + char2 = 'OOO' + num1 = 28 + num2 = 29 ). + + output->display( input = gt_tab name = `gt_tab` ). + + output->next_section( `23) Excursion: Including structures` ). + + "The example shows the inclusion of structured types and data + "objects in another structure. First, three structured types as + "well as a structured data object based on one of those types are + "created. Then, the types and the structure are included in the + "structured type address_type. With the optional addition AS and + "the specification of a name, the included components can be + "addressed by this common name as if the components are actually + "components of a substructure. With the optional addition + "RENAMING WITH SUFFIX and the specification of a name, the included + "components get a suffix name to avoid naming conflicts with other + "components. + + TYPES: BEGIN OF name_type, + title TYPE string, + prename TYPE string, + surname TYPE string, + END OF name_type, + BEGIN OF street_type, + name TYPE string, + number TYPE string, + END OF street_type, + BEGIN OF city_type, + zipcode TYPE string, + name TYPE string, + END OF city_type. + + DATA: city_struc TYPE city_type. + + TYPES BEGIN OF address_type. + INCLUDE TYPE name_type AS name. + INCLUDE TYPE street_type AS street RENAMING WITH SUFFIX _street. + INCLUDE STRUCTURE city_struc AS city RENAMING WITH SUFFIX _city. + TYPES END OF address_type. + + DATA: name TYPE name_type, + address TYPE address_type. + + name-title = `Mr.`. + name-prename = `Duncan`. + name-surname = `Pea`. + address-name = name. + address-street-name = `Vegetable Lane`. + address-street-number = `11`. + address-zipcode_city = `349875`. + address-name_city = `Botanica`. + + output->display( input = address name = `address` ). + + ENDMETHOD. + + + METHOD initialize_dbtabs. + DELETE FROM zdemo_abap_tab1. + ENDMETHOD. + + + METHOD select_from_dbtab. + + SELECT FROM zdemo_abap_tab1 + FIELDS * + WHERE key_field <> 0 + ORDER BY key_field + INTO TABLE @gt_tab. + + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_structures.clas.xml b/src/zcl_demo_abap_structures.clas.xml new file mode 100644 index 0000000..4e720ff --- /dev/null +++ b/src/zcl_demo_abap_structures.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_STRUCTURES + E + ABAP cheat sheet: Structures + 1 + X + X + X + + + + diff --git a/src/zdemo_abap_carr.tabl.xml b/src/zdemo_abap_carr.tabl.xml new file mode 100644 index 0000000..76b3728 --- /dev/null +++ b/src/zdemo_abap_carr.tabl.xml @@ -0,0 +1,94 @@ + + + + + + ZDEMO_ABAP_CARR + E + TRANSP + X + Demo table: Airline + E + A + 1 + 5 + + + ZDEMO_ABAP_CARR + A + 0 + APPL0 + N + + + + MANDT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + CARRID + X + 0 + C + 000006 + X + CHAR + 000003 + CHAR + + + CARRNAME + 0 + C + 000040 + X + CHAR + 000020 + CHAR + + + CURRCODE + 0 + C + 000010 + X + CUKY + 000005 + CUKY + + + URL + 0 + C + 000510 + X + CHAR + 000255 + CHAR + + + + + + + + + TABT ZDEMO_ABAP_CARR + + + + + ZDEMO_ABAP_CARR + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_draft.tabl.xml b/src/zdemo_abap_draft.tabl.xml new file mode 100644 index 0000000..ac51f8a --- /dev/null +++ b/src/zdemo_abap_draft.tabl.xml @@ -0,0 +1,127 @@ + + + + + + ZDEMO_ABAP_DRAFT + E + TRANSP + X + X + Draft table for RAP calculator + E + X + A + 1 + 5 + + + ZDEMO_ABAP_DRAFT + A + 0 + APPL0 + N + + + + CLIENT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + ID + X + SYSUUID_X16 + 0 + X + E + + + DRAFTUUID + X + SYSUUID_X16 + 0 + X + E + + + NUM1 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + ARITHM_OP + 0 + C + 000002 + CHAR + 000001 + CHAR + + + NUM2 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + CALC_RESULT + 0 + g + 000008 + STRG + STRG + + + CREA_DATE_TIME + TIMESTAMPL + 0 + E + + + LCHG_DATE_TIME + TIMESTAMPL + 0 + E + + + .INCLUDE + 0 + SYCH_BDL_DRAFT_ADMIN_INC + S + Standard Include for Draft Administration (BDL Syntax Check) + S + %ADMIN + + + + + + + + + TABT ZDEMO_ABAP_DRAFT + + + + + ZDEMO_ABAP_DRAFT + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_fli.tabl.xml b/src/zdemo_abap_fli.tabl.xml new file mode 100644 index 0000000..d26f555 --- /dev/null +++ b/src/zdemo_abap_fli.tabl.xml @@ -0,0 +1,192 @@ + + + + + + ZDEMO_ABAP_FLI + E + TRANSP + X + Demo table: Flight + E + A + 1 + 5 + + + ZDEMO_ABAP_FLI + A + 0 + APPL0 + N + + + + MANDT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + CARRID + X + 0 + C + 000006 + X + CHAR + 000003 + CHAR + + + CONNID + X + 0 + N + 000008 + X + NUMC + 000004 + NUMC + + + FLDATE + X + 0 + D + 000016 + X + DATS + 000008 + DATS + T + + + PRICE + 0 + P + 000008 + ZDEMO_ABAP_FLI + CURRENCY + CURR + 000015 + 000002 + CURR + + + CURRENCY + 0 + C + 000010 + X + CUKY + 000005 + CUKY + + + PLANETYPE + 0 + C + 000020 + X + CHAR + 000010 + CHAR + + + SEATSMAX + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + SEATSOCC + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + PAYMENTSUM + 0 + P + 000009 + ZDEMO_ABAP_FLI + CURRENCY + X + CURR + 000017 + 000002 + CURR + + + SEATSMAX_B + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + SEATSOCC_B + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + SEATSMAX_F + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + SEATSOCC_F + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + + + + + + + TABT ZDEMO_ABAP_FLI + + + + + ZDEMO_ABAP_FLI + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_flsch.tabl.xml b/src/zdemo_abap_flsch.tabl.xml new file mode 100644 index 0000000..6ea6343 --- /dev/null +++ b/src/zdemo_abap_flsch.tabl.xml @@ -0,0 +1,209 @@ + + + + + + ZDEMO_ABAP_FLSCH + E + TRANSP + X + Demo table: Flight schedule + E + X + A + 1 + 5 + + + ZDEMO_ABAP_FLSCH + A + 0 + APPL0 + N + + + + MANDT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + CARRID + X + 0 + C + 000006 + X + CHAR + 000003 + CHAR + + + CONNID + X + 0 + N + 000008 + X + NUMC + 000004 + NUMC + + + COUNTRYFR + 0 + C + 000006 + CHAR + 000003 + CHAR + + + CITYFROM + 0 + C + 000040 + X + CHAR + 000020 + CHAR + + + AIRPFROM + 0 + C + 000006 + X + CHAR + 000003 + CHAR + + + COUNTRYTO + 0 + C + 000006 + CHAR + 000003 + CHAR + + + CITYTO + 0 + C + 000040 + X + CHAR + 000020 + CHAR + + + AIRPTO + 0 + C + 000006 + X + CHAR + 000003 + CHAR + + + FLTIME + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + DEPTIME + 0 + T + 000012 + X + TIMS + 000006 + TIMS + T + + + ARRTIME + 0 + T + 000012 + X + TIMS + 000006 + TIMS + T + + + DISTANCE + 0 + P + 000005 + ZDEMO_ABAP_FLSCH + DISTID + X + QUAN + 000009 + 000004 + QUAN + + + DISTID + 0 + C + 000006 + X + UNIT + 000003 + UNIT + + + FLTYPE + 0 + C + 000002 + X + CHAR + 000001 + CHAR + + + PERIOD + 0 + X + 000001 + X + INT1 + 000003 + INT1 + + + + + + + + + TABT ZDEMO_ABAP_FLSCH + + + + + ZDEMO_ABAP_FLSCH + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_objects_interface.intf.abap b/src/zdemo_abap_objects_interface.intf.abap new file mode 100644 index 0000000..c436fa2 --- /dev/null +++ b/src/zdemo_abap_objects_interface.intf.abap @@ -0,0 +1,37 @@ +*********************************************************************** +* ---------------------------- PURPOSE -------------------------------- +* Interface to support the ABAP cheat sheet ABAP object orientation. +* +* ----------------------------- NOTE ---------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** +"!

Interface for ABAP cheat sheet example

+"! The interface supporta the ABAP cheat sheet on object orientation and demonstrates the use of interfaces. +INTERFACE zdemo_abap_objects_interface + PUBLIC . + + METHODS: double IMPORTING i_op TYPE i + RETURNING VALUE(r_double) TYPE i, + + triple DEFAULT IGNORE IMPORTING i_op TYPE i + RETURNING VALUE(r_triple) TYPE i . + + DATA: in_str TYPE string. + + CLASS-METHODS: halve IMPORTING i_op TYPE i + RETURNING VALUE(r_halve) TYPE i. + + CLASS-DATA: stat_str TYPE string. + +ENDINTERFACE. diff --git a/src/zdemo_abap_objects_interface.intf.xml b/src/zdemo_abap_objects_interface.intf.xml new file mode 100644 index 0000000..fc31bfe --- /dev/null +++ b/src/zdemo_abap_objects_interface.intf.xml @@ -0,0 +1,15 @@ + + + + + + ZDEMO_ABAP_OBJECTS_INTERFACE + E + Interface for ABAP cheat sheet example + 2 + 1 + X + + + + diff --git a/src/zdemo_abap_rap_calc_sd.srvd.srvdsrv b/src/zdemo_abap_rap_calc_sd.srvd.srvdsrv new file mode 100644 index 0000000..d94e533 --- /dev/null +++ b/src/zdemo_abap_rap_calc_sd.srvd.srvdsrv @@ -0,0 +1,4 @@ +@EndUserText.label: 'Service definition for RAP Calculator' +define service ZDEMO_ABAP_RAP_CALC_SD { + expose ZDEMO_ABAP_RAP_DRAFT_M; +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_calc_sd.srvd.xml b/src/zdemo_abap_rap_calc_sd.srvd.xml new file mode 100644 index 0000000..92217d0 --- /dev/null +++ b/src/zdemo_abap_rap_calc_sd.srvd.xml @@ -0,0 +1,19 @@ + + + + + + ZDEMO_ABAP_RAP_CALC_SD + SRVD/SRV + Service definition for RAP Calculator + EN + EN + ./zdemo_abap_rap_calc_sd/source/main + ABAP_SOURCE + ABAP Development Tools + S + Definition + + + + diff --git a/src/zdemo_abap_rap_ch_m.ddls.asddls b/src/zdemo_abap_rap_ch_m.ddls.asddls new file mode 100644 index 0000000..07c5a11 --- /dev/null +++ b/src/zdemo_abap_rap_ch_m.ddls.asddls @@ -0,0 +1,12 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +define view entity ZDEMO_ABAP_RAP_CH_M +as select from zdemo_abap_rapt2 +association to parent ZDEMO_ABAP_RAP_RO_M + as _parent on $projection.key_field = _parent.key_field +{ + _parent, + key key_field, + key key_ch, + field_ch1, + field_ch2 +} diff --git a/src/zdemo_abap_rap_ch_m.ddls.baseinfo b/src/zdemo_abap_rap_ch_m.ddls.baseinfo new file mode 100644 index 0000000..37140bc --- /dev/null +++ b/src/zdemo_abap_rap_ch_m.ddls.baseinfo @@ -0,0 +1,21 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_RAPT2" +], +"ASSOCIATED": +[ +"ZDEMO_ABAP_RAP_RO_M" +], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_ch_m.ddls.xml b/src/zdemo_abap_rap_ch_m.ddls.xml new file mode 100644 index 0000000..d9b7eca --- /dev/null +++ b/src/zdemo_abap_rap_ch_m.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_RAP_CH_M + E + CDS view entity (child) + W + + + + diff --git a/src/zdemo_abap_rap_ch_u.ddls.asddls b/src/zdemo_abap_rap_ch_u.ddls.asddls new file mode 100644 index 0000000..346ee49 --- /dev/null +++ b/src/zdemo_abap_rap_ch_u.ddls.asddls @@ -0,0 +1,12 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +define view entity ZDEMO_ABAP_RAP_CH_U +as select from zdemo_abap_rapt2 +association to parent ZDEMO_ABAP_RAP_RO_U + as _parent on $projection.key_field = _parent.key_field +{ + _parent, + key key_field, + key key_ch, + field_ch1, + field_ch2 +} diff --git a/src/zdemo_abap_rap_ch_u.ddls.baseinfo b/src/zdemo_abap_rap_ch_u.ddls.baseinfo new file mode 100644 index 0000000..e955a94 --- /dev/null +++ b/src/zdemo_abap_rap_ch_u.ddls.baseinfo @@ -0,0 +1,21 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_RAPT2" +], +"ASSOCIATED": +[ +"ZDEMO_ABAP_RAP_RO_U" +], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_ch_u.ddls.xml b/src/zdemo_abap_rap_ch_u.ddls.xml new file mode 100644 index 0000000..8d78b95 --- /dev/null +++ b/src/zdemo_abap_rap_ch_u.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_RAP_CH_U + E + CDS view entity (child) + W + + + + diff --git a/src/zdemo_abap_rap_draft_m.bdef.asbdef b/src/zdemo_abap_rap_draft_m.bdef.asbdef new file mode 100644 index 0000000..03a356b --- /dev/null +++ b/src/zdemo_abap_rap_draft_m.bdef.asbdef @@ -0,0 +1,31 @@ +managed implementation in class zbp_demo_abap_rap_draft_m unique; +strict; +with draft; + +define behavior for ZDEMO_ABAP_RAP_DRAFT_M alias calc +persistent table zdemo_abap_tabca +draft table zdemo_abap_draft +lock master +total etag crea_date_time +etag master lchg_date_time +authorization master ( global ) +late numbering +{ + create; + update; + delete; + field ( readonly ) id, calc_result, crea_date_time, lchg_date_time; + field ( mandatory ) num1, num2, arithm_op; + static action delete_all; + internal action calculation; + validation validate on save { create; field num1, arithm_op, num2; } + determination det_modify on modify { field num1, num2, arithm_op; } + draft action Resume; + draft action Edit; + draft action Activate; + draft action Discard; + draft determine action Prepare + { + validation validate; + } +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_draft_m.bdef.xml b/src/zdemo_abap_rap_draft_m.bdef.xml new file mode 100644 index 0000000..13c48b2 --- /dev/null +++ b/src/zdemo_abap_rap_draft_m.bdef.xml @@ -0,0 +1,50 @@ + + + + + + ZDEMO_ABAP_RAP_DRAFT_M + BDEF/BDO + BDEF, managed, draft, late numbering + 60 + EN + + + ./zdemo_abap_rap_draft_m/source/main/versions + http://www.sap.com/adt/relations/versions + Historic versions + + + ./zdemo_abap_rap_draft_m/source/main + http://www.sap.com/adt/relations/source + text/plain + Source Content + + + ./zdemo_abap_rap_draft_m/source/main + http://www.sap.com/adt/relations/source + text/html + Source Content (HTML) + + + ./zdemo_abap_rap_draft_m/objectstructure + http://www.sap.com/adt/relations/objectstructure + Object Structure + + + /sap/bc/adt/vit/wb/object_type/bdefbdo/object_name/ZDEMO_ABAP_RAP_DRAFT_M + self + application/vnd.sap.sapgui + Representation in SAP GUI + + + EN + 5 + ./zdemo_abap_rap_draft_m/source/main + ABAP_SOURCE + true + true + + + + diff --git a/src/zdemo_abap_rap_draft_m.ddls.asddls b/src/zdemo_abap_rap_draft_m.ddls.asddls new file mode 100644 index 0000000..af3a9a1 --- /dev/null +++ b/src/zdemo_abap_rap_draft_m.ddls.asddls @@ -0,0 +1,73 @@ + @AccessControl.authorizationCheck: #NOT_REQUIRED + +@ObjectModel.semanticKey: ['id'] +@UI: { headerInfo: { title: { value: 'id' }, + typeName: 'Calculation', typeNamePlural: 'Calculations' } } +define root view entity ZDEMO_ABAP_RAP_DRAFT_M + as select from zdemo_abap_tabca + { + //FACET SECTION + @UI.facet: [ + + // Header Facet (Object Page): + + { id: 'HeaderFacet', + purpose: #HEADER, + type: #FIELDGROUP_REFERENCE, + //label: 'Calculation ID', + targetQualifier: 'Fieldgroup:HeaderItems', + position: 10 }, + + // Body Facets (Object Page) + + { id: 'Calculation', + type: #IDENTIFICATION_REFERENCE, + label: 'Calculation', + position: 10 } + + ] + + // Element List + @EndUserText.label: 'Calculation ID' + @UI: { lineItem: [ { importance: #HIGH, position: 10, + label: 'Calculation ID' }, + { type: #FOR_ACTION, dataAction: 'delete_all', + label: 'Delete All Persisted Calculations' } ], + fieldGroup: [ { qualifier: 'Fieldgroup:HeaderItems', + position: 10 } ] } + key id, + @UI: { lineItem: [ { importance: #HIGH, position: 20, + label: '1st Operand' } ], + identification: [ { position: 20, + label: '1st Operand' } ], + fieldGroup: [ { qualifier: 'CaluclationItems', + position: 10 } ] } + num1, + @UI: { lineItem: [ { importance: #HIGH, position: 30, + label: 'Operator' } ], + identification: [ { position: 30, label: 'Operator' } ], + fieldGroup: [ { qualifier: 'CaluclationItems', + position: 20 } ] } + arithm_op, + @UI: { lineItem: [ { importance: #HIGH, position: 40, + label: '2nd Operand' } ], + identification: [ { position: 40, + label: '2nd Operand' } ], + fieldGroup: [ { qualifier: 'CaluclationItems', + position: 30 } ] } + num2, + @UI: { lineItem: [ { importance: #HIGH, position: 50, + label: 'Result' } ], + identification: [ { position: 50, label: 'Result' } ], + fieldGroup: [ { qualifier: 'CaluclationItems', + position: 40 } ] } + calc_result, + @UI: { hidden: true } + @Semantics.systemDateTime.lastChangedAt: true + crea_date_time, + @EndUserText.label: 'Last Changed At' + @UI: { fieldGroup: [ { qualifier: 'Fieldgroup:HeaderItems', + position: 20 } ] } + @Semantics.systemDateTime.localInstanceLastChangedAt: true + lchg_date_time + } diff --git a/src/zdemo_abap_rap_draft_m.ddls.baseinfo b/src/zdemo_abap_rap_draft_m.ddls.baseinfo new file mode 100644 index 0000000..5884140 --- /dev/null +++ b/src/zdemo_abap_rap_draft_m.ddls.baseinfo @@ -0,0 +1,19 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_TABCA" +], +"ASSOCIATED": +[], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_draft_m.ddls.xml b/src/zdemo_abap_rap_draft_m.ddls.xml new file mode 100644 index 0000000..09455dc --- /dev/null +++ b/src/zdemo_abap_rap_draft_m.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_RAP_DRAFT_M + E + CDS root view entity + W + + + + diff --git a/src/zdemo_abap_rap_ro_m.bdef.asbdef b/src/zdemo_abap_rap_ro_m.bdef.asbdef new file mode 100644 index 0000000..1766aa3 --- /dev/null +++ b/src/zdemo_abap_rap_ro_m.bdef.asbdef @@ -0,0 +1,30 @@ +managed implementation in class zbp_demo_abap_rap_ro_m unique; +strict; + +define behavior for ZDEMO_ABAP_RAP_RO_M alias root +persistent table zdemo_abap_rapt1 +lock master +authorization master ( global ) + +{ + create; + update; + delete; + association _child { create; } + action multiply_by_2; + validation val on save { field field3; } + determination det_add_text on save { create; } + field ( readonly:update ) key_field; +} + +define behavior for ZDEMO_ABAP_RAP_CH_M alias child +persistent table zdemo_abap_rapt2 +lock dependent by _parent +authorization dependent by _parent +{ + update; + delete; + field ( readonly ) key_field; + field ( readonly:update ) key_ch; + association _parent; +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_ro_m.bdef.xml b/src/zdemo_abap_rap_ro_m.bdef.xml new file mode 100644 index 0000000..7cab7ad --- /dev/null +++ b/src/zdemo_abap_rap_ro_m.bdef.xml @@ -0,0 +1,50 @@ + + + + + + ZDEMO_ABAP_RAP_RO_M + BDEF/BDO + BDEF, managed, external numbering + 60 + EN + + + ./zdemo_abap_rap_ro_m/source/main/versions + http://www.sap.com/adt/relations/versions + Historic versions + + + ./zdemo_abap_rap_ro_m/source/main + http://www.sap.com/adt/relations/source + text/plain + Source Content + + + ./zdemo_abap_rap_ro_m/source/main + http://www.sap.com/adt/relations/source + text/html + Source Content (HTML) + + + ./zdemo_abap_rap_ro_m/objectstructure + http://www.sap.com/adt/relations/objectstructure + Object Structure + + + /sap/bc/adt/vit/wb/object_type/bdefbdo/object_name/ZDEMO_ABAP_RAP_RO_M + self + application/vnd.sap.sapgui + Representation in SAP GUI + + + EN + 5 + ./zdemo_abap_rap_ro_m/source/main + ABAP_SOURCE + true + true + + + + diff --git a/src/zdemo_abap_rap_ro_m.ddls.asddls b/src/zdemo_abap_rap_ro_m.ddls.asddls new file mode 100644 index 0000000..1a747b5 --- /dev/null +++ b/src/zdemo_abap_rap_ro_m.ddls.asddls @@ -0,0 +1,12 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +define root view entity ZDEMO_ABAP_RAP_RO_M +as select from zdemo_abap_rapt1 +composition [0..*] of ZDEMO_ABAP_RAP_CH_M as _child +{ + key key_field, + field1, + field2, + field3, + field4, + _child +} diff --git a/src/zdemo_abap_rap_ro_m.ddls.baseinfo b/src/zdemo_abap_rap_ro_m.ddls.baseinfo new file mode 100644 index 0000000..c566316 --- /dev/null +++ b/src/zdemo_abap_rap_ro_m.ddls.baseinfo @@ -0,0 +1,21 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_RAPT1" +], +"ASSOCIATED": +[ +"ZDEMO_ABAP_RAP_CH_M" +], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_ro_m.ddls.xml b/src/zdemo_abap_rap_ro_m.ddls.xml new file mode 100644 index 0000000..21c2a2d --- /dev/null +++ b/src/zdemo_abap_rap_ro_m.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_RAP_RO_M + E + CDS root view entity + W + + + + diff --git a/src/zdemo_abap_rap_ro_u.bdef.asbdef b/src/zdemo_abap_rap_ro_u.bdef.asbdef new file mode 100644 index 0000000..514e174 --- /dev/null +++ b/src/zdemo_abap_rap_ro_u.bdef.asbdef @@ -0,0 +1,26 @@ +unmanaged implementation in class zbp_demo_abap_rap_ro_u unique; +strict; + +define behavior for ZDEMO_ABAP_RAP_RO_U alias root +lock master +authorization master ( global, instance ) + +{ + create; + update; + delete; + association _child { create; } + action multiply_by_2; + action ( features : instance ) multiply_by_3; + action ( features : global ) set_z; + field ( readonly:update ) key_field; +} + +define behavior for ZDEMO_ABAP_RAP_CH_U alias child +lock dependent by _parent +authorization dependent by _parent +{ + field ( readonly ) key_field; + field ( readonly : update ) key_ch; + association _parent; +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_ro_u.bdef.xml b/src/zdemo_abap_rap_ro_u.bdef.xml new file mode 100644 index 0000000..c7e29c2 --- /dev/null +++ b/src/zdemo_abap_rap_ro_u.bdef.xml @@ -0,0 +1,50 @@ + + + + + + ZDEMO_ABAP_RAP_RO_U + BDEF/BDO + BDEF, unmanaged, external numbering + 60 + EN + + + ./zdemo_abap_rap_ro_u/source/main/versions + http://www.sap.com/adt/relations/versions + Historic versions + + + ./zdemo_abap_rap_ro_u/source/main + http://www.sap.com/adt/relations/source + text/plain + Source Content + + + ./zdemo_abap_rap_ro_u/source/main + http://www.sap.com/adt/relations/source + text/html + Source Content (HTML) + + + ./zdemo_abap_rap_ro_u/objectstructure + http://www.sap.com/adt/relations/objectstructure + Object Structure + + + /sap/bc/adt/vit/wb/object_type/bdefbdo/object_name/ZDEMO_ABAP_RAP_RO_U + self + application/vnd.sap.sapgui + Representation in SAP GUI + + + EN + 5 + ./zdemo_abap_rap_ro_u/source/main + ABAP_SOURCE + true + true + + + + diff --git a/src/zdemo_abap_rap_ro_u.ddls.asddls b/src/zdemo_abap_rap_ro_u.ddls.asddls new file mode 100644 index 0000000..13df7b3 --- /dev/null +++ b/src/zdemo_abap_rap_ro_u.ddls.asddls @@ -0,0 +1,12 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +define root view entity ZDEMO_ABAP_RAP_RO_U +as select from zdemo_abap_rapt1 +composition [0..*] of ZDEMO_ABAP_RAP_CH_U as _child +{ + key key_field, + field1, + field2, + field3, + field4, + _child +} diff --git a/src/zdemo_abap_rap_ro_u.ddls.baseinfo b/src/zdemo_abap_rap_ro_u.ddls.baseinfo new file mode 100644 index 0000000..536a946 --- /dev/null +++ b/src/zdemo_abap_rap_ro_u.ddls.baseinfo @@ -0,0 +1,21 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_RAPT1" +], +"ASSOCIATED": +[ +"ZDEMO_ABAP_RAP_CH_U" +], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_ro_u.ddls.xml b/src/zdemo_abap_rap_ro_u.ddls.xml new file mode 100644 index 0000000..26e03d4 --- /dev/null +++ b/src/zdemo_abap_rap_ro_u.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_RAP_RO_U + E + CDS root view entity + W + + + + diff --git a/src/zdemo_abap_rapt1.tabl.xml b/src/zdemo_abap_rapt1.tabl.xml new file mode 100644 index 0000000..f08a832 --- /dev/null +++ b/src/zdemo_abap_rapt1.tabl.xml @@ -0,0 +1,101 @@ + + + + + + ZDEMO_ABAP_RAPT1 + E + TRANSP + X + Demo table for RAP + E + X + A + 1 + 5 + + + ZDEMO_ABAP_RAPT1 + A + 0 + APPL0 + N + + + + CLIENT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + KEY_FIELD + X + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + FIELD1 + 0 + C + 000020 + CHAR + 000010 + CHAR + + + FIELD2 + 0 + C + 000020 + CHAR + 000010 + CHAR + + + FIELD3 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + FIELD4 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + + + + + + + TABT ZDEMO_ABAP_RAPT1 + + + + + ZDEMO_ABAP_RAPT1 + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_rapt2.tabl.xml b/src/zdemo_abap_rapt2.tabl.xml new file mode 100644 index 0000000..7ace264 --- /dev/null +++ b/src/zdemo_abap_rapt2.tabl.xml @@ -0,0 +1,94 @@ + + + + + + ZDEMO_ABAP_RAPT2 + E + TRANSP + X + Demo table for RAP + E + X + A + 1 + 5 + + + ZDEMO_ABAP_RAPT2 + A + 0 + APPL0 + N + + + + CLIENT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + KEY_FIELD + X + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + KEY_CH + X + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + FIELD_CH1 + 0 + C + 000020 + CHAR + 000010 + CHAR + + + FIELD_CH2 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + + + + + + + TABT ZDEMO_ABAP_RAPT2 + + + + + ZDEMO_ABAP_RAPT2 + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_tab1.tabl.xml b/src/zdemo_abap_tab1.tabl.xml new file mode 100644 index 0000000..6798ad9 --- /dev/null +++ b/src/zdemo_abap_tab1.tabl.xml @@ -0,0 +1,101 @@ + + + + + + ZDEMO_ABAP_TAB1 + E + TRANSP + X + Demo table + E + X + A + 1 + 5 + + + ZDEMO_ABAP_TAB1 + A + 0 + APPL0 + N + + + + CLIENT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + KEY_FIELD + X + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + CHAR1 + 0 + C + 000020 + CHAR + 000010 + CHAR + + + CHAR2 + 0 + C + 000020 + CHAR + 000010 + CHAR + + + NUM1 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + NUM2 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + + + + + + + TABT ZDEMO_ABAP_TAB1 + + + + + ZDEMO_ABAP_TAB1 + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_tab2.tabl.xml b/src/zdemo_abap_tab2.tabl.xml new file mode 100644 index 0000000..66d3dca --- /dev/null +++ b/src/zdemo_abap_tab2.tabl.xml @@ -0,0 +1,92 @@ + + + + + + ZDEMO_ABAP_TAB2 + E + TRANSP + X + Demo table + E + X + A + 1 + 5 + + + ZDEMO_ABAP_TAB2 + A + 0 + APPL0 + N + + + + CLIENT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + KEY_FIELD + X + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + CHAR1 + 0 + C + 000020 + CHAR + 000010 + CHAR + + + NUM1 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + NUMLONG + 0 + 8 + 000008 + INT8 + 000019 + INT8 + + + + + + + + + TABT ZDEMO_ABAP_TAB2 + + + + + ZDEMO_ABAP_TAB2 + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_tabca.tabl.xml b/src/zdemo_abap_tabca.tabl.xml new file mode 100644 index 0000000..abd486c --- /dev/null +++ b/src/zdemo_abap_tabca.tabl.xml @@ -0,0 +1,109 @@ + + + + + + ZDEMO_ABAP_TABCA + E + TRANSP + X + Demo table for RAP calculator + E + X + A + 1 + 5 + + + ZDEMO_ABAP_TABCA + A + 0 + APPL0 + N + + + + CLIENT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + ID + X + SYSUUID_X16 + 0 + X + E + + + NUM1 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + ARITHM_OP + 0 + C + 000002 + CHAR + 000001 + CHAR + + + NUM2 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + CALC_RESULT + 0 + g + 000008 + STRG + STRG + + + CREA_DATE_TIME + TIMESTAMPL + 0 + E + + + LCHG_DATE_TIME + TIMESTAMPL + 0 + E + + + + + + + + + TABT ZDEMO_ABAP_TABCA + + + + + ZDEMO_ABAP_TABCA + CUS_DEV_SUP_DA + + + + +