Processing Internal Data

All working data within the internal session is referred to as internal data.

1. Assignments


An assignment passes the content of a source to a target data object. A source can be one of the following:

◈ Data objects
◈ Return values or results of functional methods, of predefined functions and of constructor expressions, or of table expressions
◈ Results of calculation expressions

1.1 Assignment and Conversion Rules

When assigning the value of a source object (source) to a destination object (destination), three cases can be distinguished with respect to the data type:

1. source and destination are compatible, which means that all technical type attributes match. The content is copied from source to destination without being converted. For flat data objects and related character-like or byte-like structural areas, a copy of the source object is created to the exact byte. For deep data objects, a reference is created in the destination object, the actual byte-like content of which cannot be seen externally. When entire structures are assigned, the response in terms of alignment gaps is undefined: The binary content of an alignment gap in the destination structure can either be copied from the source structure or retain the original value.

2. source and destination are not compatible, but can be converted. The content of source is converted in accordance with the conversion rules and then copied to destination. Two data types are convertible if a conversion rule exists for them. An exception is raised if the content of source cannot be handled in accordance with the conversion rules. After an exception, the content of destination is determined by the category of the data type. An assignment that requires a conversion is always slower than an assignment without conversion.

3. If the data objects are neither compatible nor convertible, no assignment can take place. If the syntax check detects this situation, a syntax error is displayed; otherwise an exception is raised when the program is executed.

The following sections describe the conversion rules. Conversion rules exist for:

◈ Elementary data objects
◈ Structures
◈ Internal tables
◈ Meshes

The special assignment rules for reference variables are described in a separate section.

The conversion rules explained here generally apply to all assignments and all statements in which the contents of data objects are changed. Exceptions to these rules are explained for each individual statement.

The conversion operator CONV can be used in many operand positions to perform a conversion of a source value to an explicitly specified target data type in accordance with the rules explained here. System classes are provided for special conversions of character sets and numeric formats.

Notes

◈ In some cases, for comparisons, different rules apply than the usual rules for assignments. More specifically, exceptions that are handleable in assignments are either handled implicitly or produce runtime errors and cannot be handled explicitly in comparisons.
◈ The lossless operator EXACT can be used to perform checks before the conversion is carried out. These ensure that only applicable values are assigned and that no values are lost during the assignment process.
◈ Checks generally are not made on assignments between compatible data objects. If a data object already contains an invalid value, for example an invalid date or time in a date or time field, this value is passed just like a valid value if the assignment is being made to a compatible data object.

1.1.1 Conversion Rules for Elementary Data Objects

The programming language ABAP includes a set of predefined elementary data types. It supports automatic type conversions and length adjustments for assignments between all these data types (with the exception of date fields and time fields, where many conversions are not applicable).

The conversion tables in the following sections define the rules for converting all possible elementary target fields for

◈ Numeric source fields
◈ Character-like source fields
◈ Byte-like source fields
◈ Date/time fields as source fields

In all conversions, it must be possible to create a value from the type-specific value range from the content of the source field; otherwise the system raises an exception that is defined in one of the subclasses of the class CX_SY_CONVERSION_ERROR. Conversion errors can usually be handled using CATCH, but there are some statements that perform assignments whose conversion errors are either handled implicitly or which produce a runtime error directly.

After an exception, the contents of the target field are undefined and should not be used. After an exception, the target field can contain either the previous value or an invalid value (the latter is the case after an overflow of a floating point number, for example).

◒ Numeric Source Fields

- i, int8, (b, s)
- p
- decfloat16, decfloat34
- f
- Note

To assign numeric values to text fields and text strings, string templates or the statement WRITE ... TO can be used with their formatting options.

◆ Source Field Type i, int8, (b, s)

Types b and s are internal and cannot be specified directly in ABAP statements. Self-defined data types and data objects in ABAP programs have the data types b or s if they have been defined with reference to data elements in ABAP Dictionary that have the external data types INT1 or INT2.

- Numeric Target Fields

Target Conversion
i, int8, (b, s) If an assignment is made to the same data type, the content is passed unconverted. Otherwise, the value of the integer is converted to internal format i, int8, (b, s). If the value range of the internal data types i, int8, b, or s is exceeded, the handleable exception CX_SY_CONVERSION_OVERFLOW is raised.
The value of the integer is converted into the internal format of a packed number. If the value range of the target field is too small, a handleable exception CX_SY_CONVERSION_OVERFLOW is raised. 
decfloat16, decfloat34  The value of the integer is converted to internal representation of a decimal floating point number with scaling 0. 
The value of the integer is converted into the internal format of a binary floating point number. 

- Character-Like Target Fields

Target Conversion
c The value of the integer is formatted in commercial notation and passed right-justified and without a decimal separator to the target field. The character "-" is set in the last place for a negative value and a blank is set for a positive value. If the target field is longer than the sequence of digits, including the sign, the field is padded with blanks on the left. If it is too short, the number representation is moved to the right by one place (in the case of positive values). If the target field is still too short (and in the case of negative values), characters are cut off on the left and the character "*" is set in the first place of the target field.
The absolute value of this integer number is passed as a right-justified string of digits to the target field. If the target field is longer than the string of digits, the field is padded with zeroes on the left. If it is too short, the values on the left are cut off.
string The value of the integer is formatted in commercial notation and passed, without gaps and without decimal separators, to the target field. The character "-" is set in the last place for a negative value and a blank is set for a positive value. The resulting length of the target field is determined by the number of digits plus the place for the sign.

- Byte-Like Target Fields

Target Conversion
x Data objects of the types b or s are converted in the internal representation from data type i. The 4 or 8 bytes of the data types i and int8 are positioned in the target field so that they are right-justified and in big endian sequence. If the target field is too long, it is padded on the left with hexadecimal 0s. If it is too short, it is truncated on the left.
xstrin Data objects of the types b or s are converted in the internal representation from data type i. The four bytes of the data type i are positioned in the target field so that they are in big endian order. In this representation, positive values have one, two, three, four, or eight bytes. Negative values always require four or eight bytes. For positive values, the leading zeroes before the fourth or eighth byte are not transported. Therefore, the resulting length of the target field is 1 byte for data type b, 1, 2, or 4 bytes for data type s, and 1, 2, 3, 4, or 8 bytes for data type int8.

Note

In conversions of the data types b and s to x and xstring, it is important to note that in the preceding conversion to i and also in the conversion of i to xstring, the leading zeroes are cut off. To ensure the result is correct, it is recommended that only the data types i, b, and s are converted to fields of type x and length 4 and then used. For example, negative values in a field of data type s always require four bytes during the conversion to x or xstring and not the potentially expected two bytes, which produces unexpected results in the target field of length 2. Target fields of the type x and length 8 are potential target fields for fields of the type int8.

- Date/Time Fields as Target Fields

Target Conversion
d If the value of the integer is between 1 and 3,652,060, it is interpreted as the number of days since 01.01.0001 and the resulting date is put in the target field in the format "yyyymmdd". If the value lies outside this range, the target field is padded with the character "0".
t The value of the integer is divided by the number of seconds in a day (86,400) and the integer remainder of the division is interpreted as the number of seconds since midnight. The resulting time is put in the target field in the format "hhmmss".

Notes

◈ The assignment of the value 0 to a target field of type d produces the value "0000000" for an invalid date and not "00010101" for 01.01.0001. The earliest valid date that can be created by assigning a number is 02.01.0001 by assigning the number 1.
◈ Converting 577,736 to a target field of type d produces 4.10.1582, and converting 577,737 produces 15.10.1582. The dates for the intermediate days that were lost during the switch from the Julian to the Gregorian calendar cannot be produced by assigning a numeric value to a target field of type d.

- - Conversion of Integer Numbers to Bytes

This example demonstrates the conversion of integers into byte fields and strings.

Source Code

REPORT demo_int_to_hex.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
  PRIVATE SECTION.
    TYPES:
      BEGIN OF line,
        int1 TYPE c LENGTH 60,
        int2 TYPE c LENGTH 60,
        int4 TYPE c LENGTH 60,
        int8 TYPE c LENGTH 60,
      END OF line.
    CLASS-DATA output TYPE TABLE OF line.
    CLASS-METHODS write_output IMPORTING VALUE(idx) TYPE i
                                         VALUE(col) TYPE i
                                         text       TYPE clike.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA: decf TYPE decfloat34,
          int1 TYPE int1,
          int2 TYPE int2,
          int4 TYPE int4,
          int8 TYPE int8,
          indx TYPE sy-index,
          xstr TYPE xstring,
          xfld TYPE x LENGTH 8.
    cl_demo_output=>begin_section(
      `Conversion of Integers to Byte Fields and Byte Strings` ).
    DO 9 TIMES.
      decf = 2 ** ( sy-index - 1 ) - 1 .
      int1 = decf.
      xstr = int1.
      xfld = int1.
      write_output(
        idx = sy-index
        col = 1
        text = |{ int1 WIDTH = 4 ALIGN = RIGHT } {
                  xfld ALIGN = LEFT } { xstr ALIGN = LEFT }| ).
    ENDDO.
    DO 15 TIMES.
      indx = 16 - sy-index.
      decf = - 2 ** ( indx ) + 1.
      int2 = decf.
      xstr = int2.
      xfld = int2.
      write_output(
        idx = sy-index
        col = 2
        text = |{ int2 WIDTH = 7 ALIGN = RIGHT } {
                  xfld ALIGN = LEFT } { xstr ALIGN = LEFT }| ).
    ENDDO.
    DO 16 TIMES.
      decf = 2 ** ( sy-index - 1 ) - 1 .
      int2 = decf.
      xstr = int2.
      xfld = int2.
      write_output(
        idx = sy-index + 15
        col = 2
        text = |{ int2 WIDTH = 7 ALIGN = RIGHT } {
                  xfld ALIGN = LEFT } { xstr ALIGN = LEFT }| ).
    ENDDO.
    DO 31 TIMES.
      indx = 32 - sy-index.
      decf = - 2 ** ( indx ) + 1.
      int4 = decf.
      xstr = int4.
      xfld = int4.
      write_output(
        idx = sy-index
        col = 3
        text = |{ int4 WIDTH = 12 ALIGN = RIGHT } {
                  xfld ALIGN = LEFT } { xstr ALIGN = LEFT }| ).
    ENDDO.
    DO 32 TIMES.
      decf = 2 ** ( sy-index - 1 ) - 1 .
      int4 = decf.
      xstr = int4.
      xfld = int4.
      write_output(
        idx = sy-index + 31
        col = 3
        text = |{ int4 WIDTH = 12 ALIGN = RIGHT } {
                  xfld ALIGN = LEFT } { xstr ALIGN = LEFT }| ).
    ENDDO.
    DO 63 TIMES.
      indx = 64 - sy-index.
      decf = - 2 ** ( indx ) + 1.
      int8 = decf.
      xstr = int8.
      xfld = int8.
      write_output(
        idx = sy-index
        col = 4
        text = |{ int8 WIDTH = 21 ALIGN = RIGHT } {
                  xfld ALIGN = LEFT } { xstr ALIGN = LEFT }| ).
    ENDDO.
    DO 64 TIMES.
      decf = 2 ** ( sy-index - 1 ) - 1 .
      int8 = decf.
      xstr = int8.
      xfld = int8.
      write_output(
        idx = sy-index + 63
        col = 4
        text = |{ int8 WIDTH = 21 ALIGN = RIGHT } {
                  xfld ALIGN = LEFT } { xstr ALIGN = LEFT }| ).
    ENDDO.
    cl_demo_output=>display( output ).
  ENDMETHOD.
  METHOD write_output.
    ASSIGN output[ idx ] TO FIELD-SYMBOL(<line>).
    IF sy-subrc <> 0.
      DO.
        APPEND INITIAL LINE TO output ASSIGNING <line>.
        IF sy-tabix = idx.
          EXIT.
        ENDIF.
      ENDDO.
    ENDIF.
    ASSIGN COMPONENT col OF STRUCTURE <line> TO FIELD-SYMBOL(<col>).
    <col> = text.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The program assigns numbers of the data types b, s, and i and int8, which cover the entire value range, a byte field with the lengths 1, 2, 4 and 8 as well as a byte string and displays them.

To calculate the numbers using powers of two, the calculation type decfloat34 is forced as a results field (using a decimal floating point number). If the integer types are used directly as results fields, the calculation type is f.

However, for exact calculations of the type int8, the calculation type f does not possess sufficient decimal places and may even cause an overflow, if rounding is performed on the limit of the int8 value range.

This example highlights the different lengths that can occur for these assignments to byte strings. It also shows that, in the case of assignments of the type s to x, a field length of 4 bytes is required for negative numbers.

◆ Source Field Type p

If the program attribute fixed point arithmetic is not set, the decimal separator in source fields with the type p is ignored, except in assignments to character-like target fields with the types c and string.

Numeric Target Fields


Target Conversion 
i, int8, (b, s) The value of the packed number is rounded commercially to an integer number. If this number is within the value range for the data type i, (b, s), it is converted to the internal representation of the corresponding integer number. If the number is not within this range, the handleable exception CX_SY_CONVERSION_OVERFLOW is raised.
The value of the packed number is rounded commercially to the number of decimal places of the target field. If this number is within the value range for the data type of the target field, it is converted to the internal representation of a packed number. If the number is not within this range, the handleable exception CX_SY_CONVERSION_OVERFLOW is raised. 
decfloat16, decfloat34  The value of the packed number is converted into the internal format of a decimal floating point number. If the number of places for a target field of type decfloat16 is greater than 16 when the assignment is performed, commercial rounding to 16 places is applied. If the mantissa of the target field is long enough, the scaling is set to the number of decimal places of the source field. An invalid value in the source field raises the handleable exception CX_SY_CONVERSION_NO_NUMBER. 
The value of the packed number is converted into the internal format of a binary floating point number. If the decimal number cannot be represented as a binary floating point number, the nearest value is used. An invalid value in the source field produces undefined behavior. 

Character-Like Target Fields

Target Conversion 
c The value of the packed number is rounded commercially to an integer number. If this number is within the value range for the data type i, (b, s), it is converted to the internal representation of the corresponding integer number. If the number is not within this range, the handleable exception CX_SY_CONVERSION_OVERFLOW is raised.
n The value of the packed number is rounded commercially to an integer number. The absolute value is passed as a right-justified string of digits to the target field. If the target field is longer than the string of digits, the field is padded with zeroes on the left. If it is too short, the values on the left are cut off.
string The value of the packed number is formatted in commercial notation and passed to the target field, flush left. The character "-" is set in the last place for a negative value and a blank is set for a positive value. The resulting length of the target field is determined by the number of digits, plus the places for the sign and the decimal separator.

Note

If the number of decimal places in the source field is greater than the number of digits calculated from 2 x the length of dobj - 1, the corresponding number of zeroes is inserted between decimal separators and digits in assignments to data objects of the types c and string and the longer sequence of digits is assigned.

Byte-Like Target Fields

Target Conversion 
x The content of the source field is converted first to data type i (see above) and then to type x (see conversion table for source field type i, int8, (b, s)).
xstring The content of the source field is converted first to data type i (see above) and then to type xstring (see conversion table for source field type i, int8, (b, s)).

Date/Time Fields as Target Fields

Target Conversion 
d The content of the source field is converted first to data type i (see above) and then to type d (see conversion table for source field type i, int8, (b, s)).
t The content of the source field is converted first to data type i (see above) and then to type t (see conversion table for source field type i, int8, (b, s)).

◆ Source Field Type decfloat16, decfloat34

If the source field does not contain a valid decimal floating point number, the handleable exception CX_SY_CONVERSION_NO_NUMBER is raised, except in the case of the assignment to another decimal floating point number. In ABAP, invalid decimal floating point numbers also include the special values "+Infinity", "-Infinity", "NaN", and "sNaN", which represent infinity or invalid numbers and are designated in the IEEE-754-2008 standard.

- Numeric Target Fields
- Character-Like Target Fields
- Byte-Like Target Fields
- Date/Time Fields as Target Fields

Numeric Target Fields

Target Conversion
i, int8, (b, s) The value of the decimal floating point number is rounded up to the nearest integer. If this number is within the value range for the data type i, int8, (b, s), it is converted to the internal representation of the corresponding integer number. If the number is not within this range, the handleable exception CX_SY_CONVERSION_OVERFLOW is raised.
The value of the decimal floating point number is rounded to the number of decimal places in the target field. If this number is within the value range for the data type of the target field, it is converted to the internal representation of a packed number. If the number is not within this range, the handleable exception CX_SY_CONVERSION_OVERFLOW is raised. 
decfloat16, decfloat34  If an assignment is made to the same data type, the content is passed unconverted. For an assignment from decfloat34 to decfloat16, the mantissa is shortened from 34 to 16 places and rounded commercially if necessary. If the mantissa of the target field is long enough, the scaling is preserved. If the value range of decfloat16 is exceeded, the handleable exception CX_SY_CONVERSION_OVERFLOW is raised. 
The value of the decimal floating point number is applied in the value of a binary floating point number. If the decimal number cannot be represented as a binary floating point number, the nearest value is used. If the value is outside the value range for floating point numbers, the handleable exception CX_SY_CONVERSION_OVERFLOW is raised. 

Character-Like Target Fields

Target Conversion
c The content of the source field is first converted into a mathematical or scientific notation, as described for the data type string. The result of this conversion is then copied, right-justified into the target field. If the target field is longer than the text string, it is padded on the left with blanks. If the target field is shorter than the text string, mathematical notation is converted into scientific notation and if necessary the mantissa is commercially rounded. If the length is not sufficient for at least one place of the mantissa, the handleable exception CX_SY_CONVERSION_OVERFLOW is raised.
n The value of the decimal floating point number is rounded up to the nearest integer. The absolute value is passed as a right-justified string of digits to the target field. If the target field is longer than the string of digits, the field is padded with zeroes on the left. If it is too short, the handleable exception CX_SY_CONVERSION_OVERFLOW is raised.
string If the exponent of the decimal floating point number is less than 0 and the absolute value of the number is bigger than 1E-6, the floating point number is formatted as follows in mathematical notation without gaps: The string of digits consists of the mantissa of the decimal floating point number without leading zeroes. If the exponent of the decimal floating point number is less than 0, a decimal point is inserted in the place in question. If the exponent of the decimal floating point number is greater than 0 or the absolute value of the number is less than 1E-6, the floating point number is formatted as follows into scientific notation: The mantissa is made up of the mantissa of the decimal floating point number without leading zeroes. If the mantissa contains more than one place, a decimal point is inserted after the first place. The exponent is always displayed with a sign and without leading zeroes. In both depictions the sign is displayed for negative values but not for positive. Both depictions are also used if the value of the decimal floating point number is 0. The maximum length of the target field is 24 for decfloat16 and 42 for decfloat34.

Notes

1. The conversion rule for floating point numbers to text strings is as follows: a text string filled by the assignment of a decimal floating point number produces the same internal representation as in the original decimal floating point number (preserves the scaling).
2. The formatting options STYLE for the statement WRITE ... TO and STYLE for embedded expressions in string templates of string expressions provide additional formats for the assignment of decimal floating point numbers to text strings.

Examples

The following table shows how decimal floating point numbers are converted to text strings:

Floating Point Number Text String 
123E+0 123
123E+1   1.23E+3 
2E+1   2E+1
1230E-2   12.30
5E-6   0.000005
50E-7   0.0000050
5E-7   5E-7
50E-8   5.0E-7
-0E+0   -0 
0E-100   0E-100 

Byte-Like Target Fields

Target Conversion 
x The content of the source field is converted first to data type i (see above) and then to type x (see conversion table for source field type i, int8, (b, s)).
xstring The content of the source field is converted first to data type i (see above) and then to type xstring (see conversion table for source field type i, int8, (b, s)).

Date/Time Fields as Target Fields

Target Conversion 
d The content of the source field is converted first to data type i (see above) and then to type d (see conversion table for source field type i, int8, (b, s)).
t The content of the source field is converted first to data type i (see above) and then to type t (see conversion table for source field type i, int8, (b, s)).

◆ Source Field Type f

Numeric Target Fields

Target Conversion 
i, int8, (b, s) The value of the floating point number is rounded up to the nearest integer. If this number is within the value range for the data type i, int8, (b, s), it is converted to the internal representation of the corresponding integer number. If the number is not within this range, the handleable exception CX_SY_CONVERSION_OVERFLOW is raised. 
p The value of the binary floating point number is rounded to the number of decimal places in the target field. If this number is within the value range for the data type of the target field, it is converted to the internal representation of a packed number. If the number is not within this range, the handleable exception CX_SY_CONVERSION_OVERFLOW is raised. 
decfloat16, decfloat34 The value of the binary floating point number is rounded to 17 places (depending on the platform) and converted to the value of a decimal floating point number. The format in the decimal floating point number is such that the mantissa has no trailing zeroes. 
f The content of the source field is passed unconverted. 

Character-Like Target Fields

Target Conversion 
c The value of the binary floating point number is formatted in the same way as when converting to the data type string. If the target field is shorter than the full notation, the mantissa is rounded up. If the target field is not long enough to include at least one digit of the mantissa apart from the exponent and the minus sign (for a negative value), the target field is padded with "*" characters. If the target field is longer than the full notation, it is padded with blanks on the left.
n The value of the floating point number is rounded up to the nearest integer. The absolute value is passed as a right-justified string of digits to the target field. If the target field is longer than the string of digits, the field is padded with zeroes on the left. If it is too short, the values on the left are cut off.
string The value of the binary floating point number is formatted in scientific notation and is copied into the target field. The exponent is always displayed with a sign and at least two places, and the mantissa with one integer digit and 16 decimal places. To do this, the internal representation of the floating point number is rounded to 17 places (depending on the platform) Depending on the sign and the length of the exponent, the resulting length of the target field is between 22 and 24.

Byte-Like Target Fields

Target Conversion 
x The content of the source field is converted first to data type i (see above) and then to type x (see conversion table for source field type i, int8, (b, s)).
xstring The content of the source field is first converted to the data type i (see above), and then to the type x (see the conversion table for source field type i, (b, s)).

Date/Time Fields as Target Fields

Target Conversion 
d The content of the source field is converted first to data type i (see above) and then to type d (see conversion table for source field type i, int8, (b, s)).
t The content of the source field is converted first to data type i (see above) and then to type t (see conversion table for source field type i, int8, (b, s)).

◒ Character-Like Source Fields

- c
- n
- string

Note: When converting character-like data objects to numeric data objects, the display of numeric values in character-like fields must be respected.

1. Source Field Type c

Numeric Target Fields

Target Conversion 
i, int8, (b, s) The source field must contain a number in mathematical or commercial notation. If not, the handleable exception CX_SY_CONVERSION_NO_NUMBER is raised. Exception: A source field that contains only blank characters is interpreted as the number zero. Scientific notation is not allowed, unless it can be interpreted as a mathematical notation. Decimal places are rounded commercially to integer values. If the number is in the value range of data types i, int8, (b, s), it is converted to the corresponding internal representation of an integral number, otherwise the handleable exception CX_SY_CONVERSION_OVERFLOW is raised. 
p The source field must contain a number in mathematical or commercial notation. If not, the handleable exception CX_SY_CONVERSION_NO_NUMBER is raised. Exception: A source field that contains only blank characters is interpreted as the number zero. Scientific notation is not allowed, unless it can be interpreted as a mathematical notation. Decimal places are rounded commercially to the number of decimal places in the target field. If the number is in the value range of the data type of the target field, it is converted to the internal representation of a packed number, otherwise the handleable exception CX_SY_CONVERSION_OVERFLOW is raised. 
decfloat16, decfloat34 The source field must contain a number in mathematical, scientific, or commercial notation. If not, the handleable exception CX_SY_CONVERSION_NO_NUMBER is raised. Exception: A source field that contains only blank characters is interpreted as the number zero. If the number of digits is greater than 16 or 34, it is rounded commercially to 16 or 34 places. If the absolute value of the number is less than 1E-398 or 1E-6176, it is rounded to 0. If the number is in the value range of the data types decfloat16 or decfloat34, it is converted to the internal representation of a decimal floating point number, otherwise the handleable exception CX_SY_CONVERSION_OVERFLOW is raised. If the mantissa of the target field is long enough, the scaling of the source field is preserved. The sign is always preserved, even if the number has the value 0. 
f The source field must contain a number in scientific notation, with the first blank after a number ending this number. If not, the handleable exception CX_SY_CONVERSION_NO_NUMBER is raised. Exception: A source field that contains only blanks or that starts with blanks not followed by a valid number is interpreted as zero. Mathematical or commercial notation is not permitted unless it can be interpreted as scientific notation. Exception: Commercial notation where the sign on the right is not separated by blanks is valid if the character string starts directly with the string of digits. If the mantissa contains more than 17 digits, surplus digits are rounded up or down. If the number is in the value range of data type f, it is converted to the internal representation of a binary floating point number, otherwise the handleable exception CX_SY_CONVERSION_OVERFLOW is raised. If the decimal number cannot be represented as a binary floating point number, the nearest value is used. 

Note

- The CL_ABAP_DECFLOAT class contains the methods READ_DECFLOAT34 and READ_DECFLOAT16, to convert character strings into floating point numbers. The exceptions of these methods are more significant than those of a normal assignment. Furthermore, the methods return a return value that reveals information about the roundings carried out.

Character-Like Target Fields

Target Conversion 
c The characters in the source field are passed left-justified to the target field. Trailing blanks in the source field are not passed. If the target field is longer than the number of characters passed, it is padded with blanks on the right. If the target field is shorter, it is truncated on the right. 
n The characters in the source field that represent digits are passed right-justified to the target field. Other characters are ignored. If the target field is longer than the number of digits in the source field, it is padded on the left with the character "0". If the target field is shorter, the characters are cut off on the left. 
string The characters in the source field are passed left-justified to the target field. Trailing blanks in the source field are not passed. The length of the target field is determined by the number of characters passed. 

Notes

◈ In ABAP, the trailing blanks in source fields of type c are ignored, by default. An assignment from source type c to target type t is an exception to this rule.
◈ To assign source fields of type c to target fields (particularly target fields of type string) and respect the trailing blanks, the addition RESPECTING BLANKS of the statement CONCATENATE can be used.

Byte-Like Target Fields.

Target Conversion 
x The characters in the source field are interpreted as the representation of the value of a half-byte in hexadecimal representation. If the valid characters "0" to "9" and "A" to "F" appear, the corresponding half-byte values are passed left-justified to the memory of the target field. If the target field is longer than the number of half-bytes passed, it is padded on the right with hexadecimal 0. If it is too short, the number is truncated on the right. The first invalid character terminates the conversion from the position of this character and the half-bytes not filled up to that point are padded with hexadecimal 0.
xstring The same conversion rules apply as to a field of type x. Half-bytes are passed to the target field, and the length of the target field is determined by the number of valid characters in the source field. If number of valid characters in the source field is odd, the last remaining half-byte in the target field is padded with hexadecimal 0.

Date/Time Fields as Target Fields

Target Conversion 
d The same conversion rules apply as to a field of type c of length 8. If the source field does not contain a valid date in the first eight places, in the format "yyyymmdd", an invalid date is created in the target field.
t The same conversion rules apply as to a field of type c with the length 6, with the exception that trailing blanks are passed and if the target field is longer than the number of characters passed, the field is padded on the right with the character "0".

2. Source Field Type n

The conversion rules are designed in such a way that when data objects of type n are assigned to character-like data objects they behave as character-like data objects. Valid data for data objects of type n is in the form of digit-only strings. When assigning valid data to number data objects, the numeric value of the string of digits is assigned to the target object. The conversion rules, however, also allow assignment of numeric text fields that contain invalid data. The latter is not recommended.

Numeric Target Fields

Target Conversion 
i, int8, (b, s) Content is handled in the same way as a source field of type c 
p Content is handled in the same way as a source field of type c 
decfloat16, decfloat34 Content is handled in the same way as a source field of type c
f Content is handled in the same way as a source field of type c 

Note

◈ The CL_ABAP_DECFLOAT class contains the methods READ_DECFLOAT34 and READ_DECFLOAT16, to convert character strings into floating point numbers. The exceptions of these methods are more significant than those of a normal assignment. Furthermore, the methods return a return value that reveals information about the roundings carried out.

Character-Like Target Fields

Target Conversion 
c Content is handled in the same way as a source field of type c
n The characters in the source field are passed right-justified to the target field. Trailing blanks are copied. If the target field is longer than the characters passed, the field is padded with "0" characters from the left. If the target field is shorter, the characters are cut off on the left.
String Content is handled in the same way as a source field of type c

Byte-Like Target Fields

Target Conversion 
x The content of the source field is converted first to data type i (see above) and then to type x (see conversion table for source field type i, int8, (b, s)).
xstring The content of the source field is converted first to data type i (see above) and then to type xstring (see conversion table for source field type i, int8, (b, s)).

Date/Time Fields as Target Fields

Target Conversion 
d Content is handled in the same way as a source field of type c
t Content is handled in the same way as a source field of type c

3. Source Field Type string

Numeric Target Fields

Target Conversion 
i, int8, (b, s) Content is handled in the same way as a source field of type c If the source field has a length of 0, the target field is assigned the value 0.
Content is handled in the same way as a source field of type c If the source field has a length of 0, the target field is assigned the value 0. 
decfloat16, decfloat34  Content is handled in the same way as a source field of type c If the source field has a length of 0, the target field is assigned the value 0.
Content is handled in the same way as a source field of type c If the source field has a length of 0, the target field is assigned the value 0. 

Note

The CL_ABAP_DECFLOAT class contains the methods READ_DECFLOAT34 and READ_DECFLOAT16, to convert character strings into floating point numbers. The exceptions of these methods are more significant than those of a normal assignment. Furthermore, the methods return a return value that reveals information about the roundings carried out.

Character-Like Target Fields

Target Conversion 
c The content is handled in the same way as a source field of type c, with the difference that trailing blanks are passed. If the length of the source field is 0, the target field is padded with blanks.
n Content is handled in the same way as a source field of type c If the length of the source field is 0, the target field is padded with the character "0".
string No conversion takes place. After the assignment, the internal reference of the target field points to the same string as the source field. A new string is only created in the memory if a change request for the content of the source field or target field is submitted.

Notes

◈ In ABAP, the trailing blanks are respected for source fields of type string but not for data objects of type c.
◈ If characters are cut off on the right when character strings containing non-Unicode double-byte characters are assigned, the character in question can be split down the middle, which generally produces an invalid character at the right margin. To prevent this, the method CL_SCP_LINEBREAK_UTIL=>STRING_SPLIT_AT_POSITION can be used.

Byte-Like Target Fields

Target Conversion 
x Content is handled in the same way as a source field of type c If the length of the source field is 0, the target field is padded with hexadecimal 0.
xstring Content is handled in the same way as a source field of type c If the length of the source field is 0, the length of the target field is also 0 after the assignment.

Date/Time Fields as Target Fields

Target Conversion 
d The content is handled in the same way as a source field of type c, with the difference that trailing blanks are passed. If the length of the source field is 0, the target field is padded with the character "0".
t Content is handled in the same way as a source field of type c If the length of the source field is 0, the target field is padded with the character "0" and the trailing blanks are passed.

4. Representation of Numeric Values in Character-Like Fields

The values of data objects of character-like data types can be assigned to data objects of the numeric types i, int8, (b, s), p, decfloat16, decfloat34, and f if they contain the valid representation of a number. The following character strings are valid numbers:

◈ Mathematical notation 

An uninterrupted string of digits with a maximum of one period (.) as a decimal separator and an optional sign "+" or "-" on the left. This sign can be separated from the digits by blanks. Any number of blanks may be in front of and behind the specified digits.

◈ Commercial notation 

An uninterrupted string of digits with a maximum of one period (.) as a decimal separator and an optional sign "+" or "-" on the right. This sign can be separated from the digits by blanks. Any number of blanks may be in front of and behind the specified digits.

◈ Scientific notation 

An uninterrupted string consisting of a mantissa and an exponent. The mantissa is an uninterrupted string of a sign "+" or "-" and a string of digits that can have a maximum of one period (.) as a decimal separator. The exponent is an uninterrupted string consisting of the character "E" or "e", a "+" or "-" sign and a string of digits. The number value is the value of the mantissa multiplied by ten to the power of the value that is after the character "E" or "e". There can be any number of blanks in front of the number. Parts of the notation that are not required, such as the sign or the exponent, can be omitted.

To summarize, these notations can be assigned to variables with numeric data types as follows:

◈ All three notations can be converted to decimal floating point numbers of the types decfloat16 and decfloat34.
◈ Only the scientific notation can be converted to binary floating point numbers of the type f. The first blank after the number closes the number and any other blanks are ignored. The mathematical and and commercial notations are only valid if they can be interpreted as scientific notation. Also, commercial notation where the sign on the right is not separated by blanks is valid if the character string starts directly with the string of digits.
◈ Only the mathematical and commercial notations can be converted to whole and packed numbers of the types i (b, s), and p.

Also, character strings containing no characters or only blanks can always be converted to the value 0, when being assigned to variables with numeric data types. When being assigned to the type f, a character string that does not contain any of the above notations is interpreted as a number with value 0, if it starts with a blank.

Notes

◈ Note that the different notations handle blanks in different ways. For example, if the character string " - 123.456" has the value -123.456 in mathematical notation, it represents the value 0, in accordance with the rules of scientific notation (above). The character "-" followed by a blank is interpreted as the number, and the subsequent characters "123.456" are ignored.

◈ When using numbers in character strings that are intended for assignments to a numeric data object, create them so that they are accepted by all possible target types with the same value. To do this, the sign must always be on the left, and there must be no blanks. Under no circumstances can there be blanks between the sign and the digits.

◒ Byte-Like Source Fields

- x
- xstring

1. Source Field Type x

- Numeric Target Fields

Target Conversion 
i, (b, s) Only the last 4 bytes of the source field are converted. If the source field is shorter than 4 bytes, it is made longer on the left with the hexadecimal 0 until it is 4 bytes long. The content of these bytes is interpreted as a number stored in big endian order, of type i. The hexadecimal values from "00000000" to "7FFFFFFF" are assigned to numbers from +0 to +2147483647 and the hexadecimal values from "80000000" to "FFFFFFFF" are assigned to the numbers -2147483648 to -1. The numbers obtained in this way are converted in the internal representation of the corresponding integer. If the value range of data types b or s is not sufficient, the handleable exception CX_SY_CONVERSION_OVERFLOW is raised.
int8 Only the last 8 bytes of the source field are converted. If the source field is shorter than 8 bytes, it is made longer on the left with the hexadecimal 0 until it is 8 bytes long. The content of these bytes is interpreted as a number stored in big endian order, of type int8. The hexadecimal values from "0000000000000000" to "7FFFFFFFFFFFFFFF" are assigned to numbers from +0 to +9223372036854775807 and the hexadecimal values from "8000000000000000" to "FFFFFFFFFFFFFFFF" are assigned to the numbers --9223372036854775808 to -1. The numbers obtained in this way are converted in the internal representation of the corresponding integer.
p The content of the source field is converted first to data type i (see above) and then to type p 
decfloat16, decfloat34 The content of the source field is first converted into data type i (see above) and then into type decfloat16 or decfloat34 
f The content of the source field is converted first to data type i (see above) and then to type f 

- Character-Like Target Fields

Target Conversion
c The values of each half-byte in the source field are converted to the hexadecimal characters "0" to "9" and "A" to "F" and passed left-justified to the target field. If the target field is longer than the number of characters passed, it is padded on the right with blank characters. If it is too short, the number is truncated on the right. 
The content of the source field is converted first to data type i (see above) and then to type n
string The values of each half-byte in the source field are converted to the hexadecimal characters "0" to "9" and "A" to "F" and passed left-justified to the target field. The resulting length of the target field is determined by the number of characters passed.

- Byte-Like Target Fields

Target Conversion
x The bytes in the source field are passed left-justified to the target field. The length of the target field is specified by the number of bytes passed. If the target field is shorter, it is truncated on the right.
xstring The bytes in the source field are passed left-justified to the target field. The resulting length of the target field is determined by the number of bytes passed.

- Date/Time Fields as Target Fields

Target Conversion
d The content of the source field is converted first to data type i (see above) and then to type d (see conversion table for source field type i, int8, (b, s)).
t The content of the source field is converted first to data type i (see above) and then to type t (see conversion table for source field type i, int8, (b, s)).

2. Source Field Type xstring

Numeric Target Fields

Target Conversion 
i, int8, (b, s) Content is handled in the same way as a source field of type x. If the source field has a length of 0, the target field is assigned the value 0.
Content is handled in the same way as a source field of type x. If the source field has a length of 0, the target field is assigned the value 0. 
decfloat16, decfloat34   Content is handled in the same way as a source field of type x. If the source field has a length of 0, the target field is assigned the value 0. 
Content is handled in the same way as a source field of type x. If the source field has a length of 0, the target field is assigned the value 0. 

Character-Like Target Fields

Target Conversion 
c Content is handled in the same way as a source field of type x. If the length of the source field is 0, the target field is padded with blanks. 
n Content is handled in the same way as a source field of type x. If the length of the source field is 0, the target field is padded with the character "0". 
string Content is handled in the same way as a source field of type x. If the length of the source field is 0, the length of the target field is also 0 after the assignment. 

Byte-Like Target Fields

Target Conversion 
x Content is handled in the same way as a source field of type x. If the length of the source field is 0, the target field is padded with hexadecimal 0.
xstring Content is handled in the same way as a source field of type x. If the length of the source field is 0, the length of the target field is also 0 after the assignment.

Date/Time Fields as Target Fields

Target Conversion 
d Content is handled in the same way as a source field of type x. If the length of the source field is 0, the target field is padded with the character "0".
t Content is handled in the same way as a source field of type x. If the length of the source field is 0, the target field is padded with the character "0".

◒ Date/Time Fields as Source Fields

- d
- t

Notes

◈ The conversion rules are designed so that data objects of the types d and t display character-like behavior in character-like operand positions and numeric behavior in numeric operand positions.
◈ In the case of a substring access to an operand of the type d or t, the substring is handled like an operand of the type n and the relevant conversion rules apply.

1. Source Field Type d

The conversion rules are designed in such a way that when data objects of the data type d are assigned to character-like data objects they behave as character-like data objects. The latter is the basis for date calculations in arithmetic expressions.

Only date entries in the format "yyyymmdd" are valid for data objects of type d. Here, "00010101" is the first valid value. The conversion rules, however, allow the assignment of date fields that contain invalid data. The latter is not recommended.

Numeric Target Fields

Target Conversion 
i, int8, (b, s)  If the source field contains a valid date in the format "yyyymmdd", it is used to calculate the number of days since 01.01.0001, and this value is then converted to the internal representation of the corresponding integer. If the source field contains an invalid date, the target field is assigned the value 0. If the value range for the internal data types b and s is insufficient, the non-handleable exception CX_SY_CONVERSION_OVERFLOW is raised. 
p If the source field contains a valid date in the format "yyyymmdd", it is used to calculate the number of days since 01.01.0001, and this value is then converted to the internal representation of a packed number. If the value range of the target field is too small, a handleable exception CX_SY_CONVERSION_OVERFLOW is raised. If the source field contains an invalid date, the target field is assigned the value 0. 
decfloat16, decfloat34 If the source field contains a valid date in the format "yyyymmdd", it is used to calculate the number of days since 01.01.0001, and this value is then converted to the internal representation of a decimal floating point number with a scaling of 0. If the source field contains an invalid date, the target field is assigned the value 0.
f If the source field contains a valid date in the format "yyyymmdd", it is used to calculate the number of days since 01.01.0001, and this value is then converted to the internal representation of a binary floating point number. If the source field contains an invalid date, the target field is assigned the value 0. 

Notes

- The conversion of a date into the number of days since 01.01.001 is designed in such a way that the transition from the Julian to Gregorian calendar is respected: 4.10.1582 produces the number 577,736 and 15.10.1582 produces 577,737. Converting the dates of the nonexistent days between 5.10.1582 and 14.10.1582 produces the same result as converting the existing days from 15.10.1582 to 24.10.1582.

- The assignment of the values "00000000" and "00010101" to a numeric type produces the value 0 in each case. The assignment of the value 0 to a date field produces "00000000". This means that the assignment of the value "00010101" to a numeric type cannot be reversed.

Character-Like Target Fields

Target Conversion 
c Content is handled in the same way as a source field of type c
n The characters in the source field are passed left-justified to the target field. Trailing blanks in the source field are passed. If the target field is longer than the source field, it is padded on the right with the character "0". If the target field is shorter, it is truncated on the right.
String Content is handled in the same way as a source field of type c

Byte-Like Target Fields

Target Conversion 
x The content of the source field is converted first to data type i (see above) and then to type x (see conversion table for source field type i, int8, (b, s)).
xstring The content of the source field is converted first to data type i (see above) and then to type xstring (see conversion table for source field type i, int8, (b, s)).

Date/Time Fields as Target Fields

Target Conversion 
d The content of the source field is passed unconverted.
t Not supported. Produces a syntax error or runtime error.

2. Source Field Type t

The conversion rules are designed in such a way that, when data objects of time type t are assigned to character-like data objects, they behave like character-like data objects. When assigned to numeric data objects, they behave as numeric data objects. The latter is used as the basis for calculating time in arithmetic expressions. If the content of data objects of type t are time entries in the format "hhmmss", and the values only correspond to valid times of day (hh is between 00 and 23, mm and ss are between 00 and 59), the value assigned to a numeric data object corresponds to the number of seconds since midnight.

Although the conversion rules actually permit the assignment of time fields that contain invalid data, this is not recommended.

Numeric Target Fields

Target Conversion 
i, int8, (b, s) If the source field contains only digits, the content is interpreted as a time entry in the format "hhmmss" from which the value hh*3600+mm*60+ss is calculated, which is then converted into the internal representation of the relevant integer number. If the source field does not only contain digits, the target field is given the value 0. If the value range of the internal data types b and s is not sufficient, a handleable exception CX_SY_CONVERSION_OVERFLOW is raised.
If the source field contains only digits, the content is interpreted as a time entry in the format "hhmmss" from which the value hh*3600+mm*60+ss is calculated, which is then converted into the internal representation of a packed number. If the value range of the target field is too small, a handleable exception CX_SY_CONVERSION_OVERFLOW is raised. If the source does not contain only digits, the target field is given the value 0. 
decfloat16, decfloat34  If the source field contains only digits, the content is interpreted as a time entry in the format "hhmmss" from which the value hh*3600+mm*60+ss is calculated, which is then converted into the internal representation of a decimal floating point number with scaling of 0. If the source does not contain only digits, the target field is given the value 0. 
If the source field contains only digits, the content is interpreted as a time entry in the format "hhmmss" from which the value hh*3600+mm*60+ss is calculated, which is then converted into the internal representation of a binary floating point number. If the source does not contain only digits, the target field is given the value 0. 

Character-Like Target Fields

Target Conversion
c Content is handled in the same way as a source field of type c
The characters in the source field are passed left-justified to the target field. Trailing blanks in the source field are passed. If the target field is longer than the source field, it is padded on the right with the character "0". If the target field is shorter, it is truncated on the right. 

Byte-Like Target Fields

TargetConversion
xThe content of the source field is converted first to data type i (see above) and then to type x (see conversion table for source field type i, int8, (b, s)).
xstringThe content of the source field is converted first to data type i (see above) and then to type xstring (see conversion table for source field type i, int8, (b, s)).

Date/Time Fields as Target Fields

Target Conversion 
string Content is handled in the same way as a source field of type c 
Not supported. Produces a syntax error or runtime error. 
The content of the source field is passed unconverted.

1.1.2 Conversion Rules for Structures

Assignments between structures distinguish between flat structures and deep structures:

◈ Deep structures can only be assigned to each other if they are compatible. Full compatibility is not required for the following deep components, to which the specified requirements apply:
     ◈ For components that have a reference type, up casts but not down casts are permitted.
     ◈ For table-like components, it is sufficient if the row type is compatible. This means that the full compatibility needed for table categories and table keys is not required here.
◈ For flat structures, there are conversion rules for the following assignments between incompatible data objects:
     ◈ Conversion between flat structures
     ◈ Conversion between flat structures and single fields

Notes

◈ If substring access is performed on a structure and the substring is shorter than the structure, the substring is handled like an operand of type c and the corresponding conversion rules apply. If the substring is exactly the same length as the structure, the substring is handled like the structure itself.
◈ A lossless assignment can be used to apply the same assignment rule to flat structures as to deep structures.
◈ The conversion rules for structures are designed to prevent handleable exceptions from being raised. If a conversion is allowed, it takes place.

1. Conversion Between Flat Structures

When flat structures are converted, the fragment view of the structures must be respected. The following rules apply when converting a flat structure to another flat structure.

◈ When assigning structures with the same fragment view, the structure is assigned without being converted.

◈ When assigning structures of different lengths where the length of the fragment view exactly matches the shorter structure, the assignment is made at the length of the shorter structure without conversion. If the target structure is longer than the source structure, the components of the target structure located after the shared fragments are filled with type-friendly initial values and any alignment gaps are set to hexadecimal 0. If the target structure is shorter than the source structure, the components of the source structure located after the shared fragments are cut off.

◈ When assigning structures of different lengths whose fragment views match until the second last fragment in the shorter structure, and in which the next fragment is character-like in one and byte-like in the other, the parts in which the fragments are the same are assigned without conversion. The characters in the next fragment in the source structure are assigned to the corresponding fragment in the target structure without conversion and left justified. If this fragment in the target structure is greater than that in the source structure, the right side is padded with blanks or with hexadecimal 0, depending on the data type. If it is shorter, the objects are cut off on the right. The remaining components after this fragment are either cut off or padded with type-friendly initial values.

No conversion rule is defined for any other cases, and assignments are not possible.

Notes

◈ If, in the affected structures, there are components of data type p, these components form individual fragments for which the length is significant but not the number of decimal places. When assigning such structures, the value of the source components of type p is cast to the number of decimal places of the target components, and the decimal point may shift. This means that the result for a component of this type can differ from the result of a direct assignment between the components.

◈ If a syntax error occurs due to an invalid assignment between flat structures, the fragment views can be displayed for the corresponding structures when displaying the syntax error in ABAP Editor by choosing the pushbutton with the information icon.

Examples

Assigning struc1 to struc2 and vice versa is not allowed because the fragment views are not the same (unlike struc2-b, struc1-x only fills one byte).

DATA:                        DATA:
  BEGIN OF struc1,             BEGIN OF struc2,
    a TYPE c LENGTH 1,           a TYPE c LENGTH 1,
    x TYPE x LENGTH 1,           b TYPE c LENGTH 1,
  END OF struc1.               END OF struc2.

Assignments of struc3 to struc4 and vice versa are allowed because the fragment view of the shorter structure struc3 is the same as the fragment view in the first part of the longer structure struc4.

DATA:                        DATA:
  BEGIN OF struc3,             BEGIN OF struc4,
    a TYPE c LENGTH 2,           a TYPE c LENGTH 8,
    n TYPE n LENGTH 6,           i TYPE i,
    i TYPE i,                    d TYPE decfloat16,
  END OF struc3.               END OF struc4.

Assignments of struc5 to struc6 and vice versa are also not allowed because the fragment views in the two structures do not match due to the alignment gaps before struc5-b and before struc6-struc0-b.

DATA:                        DATA:
  BEGIN OF struc5,             BEGIN OF struc6,
    a TYPE x LENGTH 1,           a TYPE x LENGTH 1,
    b TYPE x LENGTH 1,           BEGIN OF struc0,
    c TYPE c LENGTH 1,             b TYPE x LENGTH 1,
  END OF struc5.                   c TYPE c LENGTH 1,
                                 END OF struc0,
                               END OF struc6.

An assignment of struc7 to struc8 and vice versa is possible because the fragment view is the same until the second last fragment p in the shorter structure struc7:

DATA:                        DATA:
  BEGIN OF struc7,             BEGIN OF struc8,
    a TYPE i,                    a    TYPE i,
    p TYPE p LENGTH 8,           p TYPE p LENGTH 8,
    c TYPE c LENGTH 1,           c TYPE c LENGTH 5,
  END OF struc7.                 o TYPE p LENGTH 8,
                               END OF struc8.

A mapping from struc9 to struc10 (and in the reverse direction) is possible because the fragment view matches despite the differences in decimal places for the type p. If struc9-a has the value 999, struc10-a has the value 0.999 after a mapping from struc9 to struc10. A direct mapping from struc9-a to struc10-a, on the other hand, raises an exception of the class CX_SY_CONVERSION_OVERFLOW.

DATA:                        DATA:
  BEGIN OF struc9             BEGIN OF struc10,
    a TYPE p LENGTH 2            a TYPE p LENGTH 2
             DECIMALS 0,                  DECIMALS 3,
  END OF struc9.              END OF struc10.

1.2 Conversion Rules for Structures

This example demonstrates how structures can be converted from one type to another.

Source Code

REPORT demo_data_conversion_structure.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.

    DATA: BEGIN OF fs1,
             int   TYPE i            VALUE 5,
             pack  TYPE p DECIMALS 2 VALUE '2.26',
             text  TYPE c LENGTH 10  VALUE 'Fine Text',
             float TYPE decfloat16   VALUE '1.234e+05',
             date  TYPE d            VALUE '19950916',
          END OF fs1.

    DATA: BEGIN OF fs2,
             int  TYPE i            VALUE 3,
             pack TYPE p DECIMALS 2 VALUE '72.34',
             text TYPE c LENGTH 5   VALUE 'Hello',
          END OF fs2.

    DATA(out) = cl_demo_output=>new(
      )->begin_section( 'Source'
      )->write( |{ fs1-int   width = 10 } {
                   fs1-pack  width = 10 } {
                   fs1-text  width = 10 } {
                   fs1-float width = 10 } {
                   fs1-date  width = 10 }| ).

    out->next_section( 'Target'
      )->write( |{ fs2-int  width = 10 } {
                   fs2-pack width = 10 } {
                   fs2-text width = 10 }| ).

    fs2 = fs1.

    out->next_section( 'Result'
      )->display( |{ fs2-int  width = 10 } {
                     fs2-pack width = 10 } {
                     fs2-text width = 10 }| ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

In this example two different structures are defined, fs1 and fs2. The requirements of the third conversion rule for flat structures apply to both structures and the corresponding rule is also used. After the assignment of fs1 to fs2, only the result for the first two components is as if they had been transferred component by component. fs2-text is filled with the first five positions from fs1-text. All the remaining elements of fs1 are not transferred.

2. Conversion Between Flat Structures and Single Fields

The following rules apply when converting a flat structure to a single field and vice versa:

◈ If a structure is character-like only, it is handled in the conversion like a data object of the type c (casting). The single field can have any elementary data type.

◈ If the structure is not character-like only, the single field must have the type c and the structure must begin with a character-like fragment that is at least as long as the single field. The assignment takes place only between this fragment and the single field. The character-like fragment of the structure is handled like a data object of the type c (casting) in the assignment. If the structure is the target field, the remaining character-like fragments are filled with blanks and all other components with the type-friendly initial value.

No conversion rule is defined for any other cases, and assignments are not possible.

Note

If a syntax error occurs due to an invalid assignment between flat structures and single fields, the fragment view of the corresponding structure can be displayed when displaying the syntax error in ABAP Editor by choosing the pushbutton with the information icon.

3. Structural Fragment View

The structure fragment view splits a structure into fragments. A fragment is a grouping of structure components of the same or similar data types. In nested structures, the elementary components on the lowest nesting depth are respected when forming fragments in nested structures. The following parts of a structure are each grouped to form fragments:

◈ Consecutive flat character-like components of the types c, n, d, and t, between which there are no alignment gaps, form character-like fragments.
◈ Consecutive flat byte-like components of the type x, between which there are no alignment gaps, form byte-like fragments.
◈ Consecutive, similarly typed numeric components of type (b, s), i, int8, decfloat16, decfloat34, or f, between which there are no alignment gaps, each form a common fragment.
◈ Each individual numeric type p component forms a separate fragment. For this type of fragment it is the length that is important, not the number of decimal places.
◈ In deep structures, each deep component (reference) forms a separate fragment.
◈ Each alignment gap is regarded as a fragment.

Notes

◈ In nested structures, alignment gaps can arise before and after aligned substructures.
◈ When structure components are passed using INCLUDE, an additional alignment gap can appear in front of the passed components.
◈ The fragment view of structures with components of type p can match, even when the number of decimal places in the components concerned is different.

Example

DATA:
  BEGIN OF struc,
    a TYPE c LENGTH 3,
    b TYPE n LENGTH 4,
    c TYPE d,
    d TYPE t,
    e TYPE decfloat16,
    f TYPE x LENGTH 2,
    g TYPE x LENGTH 4,
    h TYPE i,
    i TYPE i,
    j TYPE i,
    k TYPE i,
  END OF struc.

The structure struc contains the following fragments for the character representation UCS-2 used by the ABAP programming language (in which character-like fields are represented by two bytes per character). Alignment gaps are marked with an A.

Fragment Components  Bytes 
1 a, b, c, d 6+8+16+12
8
f, g  2+4 
h, i, j, k  4+4+4+4 

1.1.3 Conversion Rules for Internal Tables

Internal tables can only be assigned to internal tables. Whether or not assignment is possible depends exclusively on row type, and is independent of table type, table key, and number of rows. Internal tables can be assigned to each other if their row types are compatible or convertible.

When assigning an internal table to another, the rows of the target table are deleted. A new row is created in the target table for each row in the source table. They are then filled with the row contents in the source table. The rows are stored according to the table category. For assignments to a sorted table, the content is automatically sorted and hashed tables are stored according to the hash algorithm.

The content of the individual rows in the source table is assigned to the rows in the target table according to the same rules as for assignments between individual data objects of corresponding row types. The same basic rule as for all conversions applies: The converted content of the single rows in the source table must lie within the value range of the row type in the target table.

Notes

◈ In internal tables with compatible or convertible row types, a non-handleable exception can be raised during assignment if, for example, in the target table a duplicate of a unique primary table key or secondary table key is created.
◈ Internal tables with elementary row types can raise the same handleable exceptions as when making assignments between the associated elementary data types. After an exception of this type, all rows assigned until this point are passed to the target table.
◈ In assignments of an initial internal table to a filled internal table, the target table is initialized in the same way as with the statement CLEAR. This frees up the memory space required for the table, except for the initial memory requirement.

1.1.4 Conversion Rules for Meshes

Meshes are not converted. Meshes can only be assigned to meshes and meshes can be assigned to each other only if they are fully compatible:

◈ Their node structure must be identical (including the names of the nodes).
◈ The associations of each node must match in full, which means that
◈ They must have the same name.
◈ The same ON conditions must be specified.
◈ The same table key must be used.

Note

MOVE-CORRESPONDING can also be used to assign incompatible meshes or structures and meshes to each other.

1.1.5 System Classes for Converting Character Sets and Number Formats

The system classes described below enable the conversion of text data between different code pages and of numeric data between different number representations.

Data that is not available in ABAP format (that is, text data that is not in the system code page format, or numeric data that is not in the byte order used on the current application server) can be saved in binary form in an x field or in an xstring.

◈ When converting to ABAP format from another format, data is read from a byte sequence and written to an ABAP data object.
◈ When converting from ABAP format to another format, data is read from an ABAP data object and written as a byte sequence.
This is done using the following classes:

◈ CL_ABAP_CONV_IN_CE
Extracts other formats to ABAP data objects (reads a binary input stream).
◈ CL_ABAP_CONV_OUT_CE
Exports ABAP data objects to another format (writes to a binary output stream).
◈ CL_ABAP_CONV_X2X_CE
Extracts data from any format and exports data to any other format (reads from a binary input stream and writes to a binary output stream).

Note

The class CL_ABAP_CODEPAGE wraps the classes above to make it easier to handle code pages when processing character and byte strings.

1.1.6 Conversions - Performance Notes

Conversions in assignments between data objects with different data types, or specifying a data object in an operand position where a different data type is expected, produce runtime costs. To avoid these costs, conversions should be avoided where possible and only data objects of the same type should be assigned to one another.

Conversion Costs

This example demonstrates costs for type conversions in assignments and operand positions.

Source Code

REPORT demo_conversion_costs.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA: num   TYPE n LENGTH 10,
          int   TYPE i,
          itab  TYPE STANDARD TABLE OF i,
          t1    TYPE i,
          t2    TYPE i,
          toff  TYPE i,
          tn    TYPE i,
          ti    TYPE i,
          msg   TYPE string.
    CONSTANTS n TYPE i VALUE 100000.

    GET RUN TIME FIELD t1.
    DO n TIMES.
    ENDDO.
    GET RUN TIME FIELD t2.
    toff = t2 - t1.

    GET RUN TIME FIELD t1.
    DO n TIMES.
      num = sy-index.
    ENDDO.
    GET RUN TIME FIELD t2.
    tn = t2 - t1 - toff.

    GET RUN TIME FIELD t1.
    DO n TIMES.
      int = sy-index.
    ENDDO.
    GET RUN TIME FIELD t2.
    ti = t2 - t1 - toff.

    cl_demo_output=>write(
      |Ratio of conversion to copy during assignment: | &&
      |{ tn / ti DECIMALS = 2 }| ).

    itab = VALUE #( ( 1 ) ).
    CLEAR: tn, ti.

    num = '1'.
    GET RUN TIME FIELD t1.
    DO n TIMES.
      READ TABLE itab TRANSPORTING NO FIELDS INDEX num.
    ENDDO.
    GET RUN TIME FIELD t2.
    tn = t2 - t1 - toff.

    int = 1.
    GET RUN TIME FIELD t1.
    DO n TIMES.
      READ TABLE itab TRANSPORTING NO FIELDS INDEX int.
    ENDDO.
    GET RUN TIME FIELD t2.
    ti = t2 - t1 - toff.

    cl_demo_output=>display(
      |Ratio of conversion to copy during assignment: | &&
      |{ tn / ti DECIMALS = 2 }| ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The program calculates the relationship between the costs of a conversion from type n to i and the costs of a direct memory copy from type i to i. The conversion is much slower than the unconverted copy, which can be seen directly in the assignment. The difference is not so clear in an operand position (here when specifying the index of a READ statement). This is because the conversion costs are not as high as for a table access.

1.2 = - Assignment Operator

Syntax

lhs = rhs.

Effect

If the character = is specified in a statement between a left side lhs (or "left hand side") and a right side rhs (or "right hand side"), it works like an assignment operator that assigns the value of the right side to the left side, possibly using a conversion.

The right side rhs, the source of the assignment, is a general expression position and can be specified as follows:

◈ As a single data object
◈ As a return value or result of functional methods, predefined functions, or constructor expressions or table expressions
◈ As a result of calculation expressions

The left side lhs, the target of the assignment, is a both a declaration position and a result position and can be specified as follows:

◈ As any data object that can be specified in a writing position. The data type of the data object must either be compatible with the assigned value or must be convertible to the data type of lhs in accordance with one of the conversion rules. If the assigned value does not match the data type of lhs, the exceptions described in the conversion rules may be raised.
◈ As an inline declaration DATA(var). The data type of the declared variable is determined by the right side and is described as one of the rhs options. Any variables used on the right side cannot be declared on the left side. An identically named data object from a more global context can be used on the right side and is not hidden by the local declaration until after the statement.
◈ As a writable expression representing an operand to which the right side can be assigned.

If an exception is raised on the right side, the statement is not executed and the value of the target field is undefined.

Notes

◈ If lhs is not a data object, it can be prefixed with the obsolete keyword COMPUTE. This keyword is ignored, however, and should no longer be used.
◈ Another obsolete variant of the assignment is the statement MOVE rhs TO lhs. Here, the left side is on the right side and does not cover all options of the assignment operator =.

1.2.1 = - Assign Data Objects

Syntax

destination = dobj.

Effect

In the simplest assignment case, a data object dobj is on the right side of the assignment operator =. This data object can be specified as described under Reading Positions. The content of the data object is assigned to the left side, destination. If necessary, type-specific conversions are made in accordance with the conversion rules. The variant shown here applies to all assignments between operands that are not reference variables. Special rules apply to reference variables.

The following can be specified for destination:

◈ Any data object that can be specified in a writing position. The data type of the data object must be either compatible with the data type of dobj or it must be possible to convert the content of dobj into the data type of destination in accordance with one of the conversion rules.

◈ An inline declaration DATA(var). If the data type of dobj is complete, it is used for the declaration. If dobj is a generically typed field symbol or a formal parameter of this kind, the following data types are used:

     ◈ string for csequence and clike
     ◈ xstring for xsequence
     ◈ decfloat34 for numeric and decfloat
     ◈ p with the length 8 and no decimal places if p is generic
     ◈ The standard key for a standard table type with generic primary table key

Other generic data types (except for table types) that are explicitly generic with respect to their secondary table keys cannot be made more concrete and produce a syntax error.

Notes

◈ A special assignment operator, ?=, which performs a down cast, is available for assignments of reference variables.

◈ If dobj and/or destination are field symbols, the content of the data objects to which the field symbols point is used (as in all ABAP statements). The actual pointer content of a field symbol can only be changed using the statement ASSIGN or the addition ASSIGNING when processing internal tables (value semantics).

◈ Inline declarations are not possible for every conceivable generic type of dobj. This is because, even in an inline declaration in the position of an actual parameter, the rules for deriving the data type for a generically typed output parameter of a method apply. Here, the typing check allows fewer combinations than the conversion rules of an assignment.

◈ Strings and internal tables are addressed internally using references. When assignments are made between strings and between internal tables of the same type (if the row type itself does not contain any table types), for performance reasons, only the internal administrative information is passed. After the assignment, the actual string or the actual table body of the source as well as the target object are addressed (sharing). Sharing is valid until the object is accessed to be changed. At this point, the sharing is canceled and a copy of the content is made. The sharing is displayed in the memory consumption display of ABAP Debugger and in the Memory Inspector tool. For internal tables where the row type itself contains internal table types, no sharing takes place. Sharing can, however, take place for the subtables with a certain row type.

◈ If an internal table with a header line is specified as an operand of an assignment, in nearly all operand positions, the header line is addressed and not the table body. To address the table body of a table with a header line, [] must be appended to the table name.

◈ The statement MOVE is an obsolete way of assigning data objects.

Example

Assigns a literal to a text string.

DATA text TYPE string.

text = `blah`.

Example

Assigns a generically typed field symbol, <fs>, to a data object, number, declared inline. In the assignment, the field symbol has the type i, however the field number is created with the type decfloat34 when the program is generated. A syntax check warning about this is hidden using the pragma ##type.

FIELD-SYMBOLS <fs> TYPE numeric.

ASSIGN 1 TO <fs>.

DATA(number) = <fs> ##type.

Exceptions

Handleable Exceptions

CX_SY_CONVERSION_NO_NUMBER

◈ Cause: Operand cannot be interpreted as number when assigned to a numeric data type
Runtime Error: CONVT_NO_NUMBER

CX_SY_CONVERSION_OVERFLOW

◈ Cause: Overflow in arithmetic operation (type p, with specified length)
Runtime Error: BCD_FIELD_OVERFLOW

◈ Cause: Operand too big or (interim) result too big
Runtime Error: CONVT_OVERFLOW

Non-Handleable Exceptions

◈ Cause: Source field (type p) contains an incorrect BCD format
Runtime Error: BCD_BADDATA

◈ Cause: Assignment for deep structures not allowed if they overlap
Runtime Error: MOVE_COMPLEX_OVERLAP

◈ Cause: Type conflict in assignment between object references
Runtime Error: MOVE_INTERFACE_NOT_SUPPORTED,
Runtime Error: MOVE_IREF_NOT_CONVERTIBLE,
Runtime Error: MOVE_IREF_TO_OREF,
Runtime Error: MOVE_OREF_NOT_CONVERTIBLE

◈ Cause: Type conflict in assignment between data references.
Runtime Error: MOVE_DREF_NOT_COMPATIBLE

◈ Cause: Assignment between the involved types is not supported
Runtime Error: MOVE_NOT_SUPPORTED

◈ Cause: Constants and literals cannot be overwritten
Runtime Error: MOVE_TO_LIT_NOTALLOWED

◈ Cause: Constants and literals cannot be overwritten
Runtime Error: MOVE_TO_LIT_NOTALLOWED_NODATA

◈ Cause: During a loop on an internal table, an attempt is made to overwrite a reference variable that is associated with the internal table using REFERENCE INTO.
Runtime Error: MOVE_TO_LOOP_REF

1.2.2 = - Assign Return Values and Results

Syntax

destination = meth( ) | func( ) | constr_expr | table_expr .

Effect

If one of the following calls or expressions is specified on the right side of the assignment operator =, its return value or result is determined and assigned to the left side, destination. If necessary, type-dependent conversions are performed in accordance with the conversion rules. The following items are possible on the right side:

◈ A functional method call meth( ) including method chainings. The return value of a function method or an attribute addressed using a method chaining always has a full type that must match the type of the left side.
◈ A predefined function func( ). The type of the return value (which in some numeric functions can be dependent on the data type of the argument) must match the type of the left side.
◈ A constructor expression constr_expr. The type of the result is determined by the type specified for the constructor expression and must match the type of the left side.
◈ A table expression table_expr. The type of the result must match the type on the left side.

The following can be specified for destination:

◈ A variable that is compatible with the return value or to whose type the return value or result can be converted.

◈ An inline declaration DATA(var). The data type of the declared variable var is the statically detectable type of the return value or result.

Note

If an arithmetic expression is specified as an argument for one of the overloaded numeric functions, the function itself works like an arithmetic expression and its assignment is one of the assignments of arithmetic expressions. This can cause the conversions to be performed in a different order.

Example

Assigns the return value of a method to an internal table declared inline.

METHOD m1.
  DATA(itab) = m2( ).
  ...
ENDMETHOD.

1.2.3 = - Assign Calculation Expressions

Calculation expressions are arithmetic expressions, string expressions, and bit expressions. Any calculation expression can be located on the right side of the assignment operator =.

◈ =, Arithmetic Expression

Syntax

result = arith_exp.

Effect

If an arithmetic expression arith_exp is specified on the right side of the assignment operator =, its calculation type is calculated and assigned to the left side result.

The following can be specified for result:

◈ A variable compatible with the numeric result of the arithmetic expression or to whose type the result can be converted.

◈ An inline declaration DATA(var). The data type of the declared variable var is the statically identifiable calculation type, where generically typed field symbols and formal parameters are respected using a standard type described there. In the case of the calculation type p, the data type of the declared variable is always p with the length 8 without decimal places.

If an existing variable is specified for result, its data type is respected when determining the calculation type. If the calculation type is not the data type of result, the result is converted to the data type of the result field before the assignment is made.

Notes

◈ The fact that the result field is respected when the calculation type is determined is a special property of ABAP that should always be kept in mind.

◈ The calculation type that is dependent on the data type of the result field is a major different from an assignment of data objects; if data objects are incompatible, the source field is always converted to the data type of the target field. When arithmetic expressions are assigned, their operands can also be converted to the data type of the result field before the calculation.

◈ A calculation type p on the right side of an inline declaration can produce the data type p with length 8 and no decimal places and this can produce unexpected results and raise exceptions. It is best to either avoid online declarations when using the calculation type p or to determine the data type by applying the conversion operator CONV to the arithmetic expression.

Example

The first assignment is an assignment of an arithmetic expression, because of its sign. The calculation type is determined as i and result is given the value "731036", the number of days since 01.01.0001. The second assignment, on the other hand, has the same meaning as an assignment of data objects and produces the value "20020704" in result.

DATA: result TYPE string,
      date   TYPE d VALUE '20020704'.

result = + date.
result =   date.

Example

This example demonstrates inline declarations with the calculation type p. Assignments of a data object use its data type, but assignments of an arithmetic expression with the calculation type p use the data type p with length 8 and no decimal places This means that decimal places are lost in the first assignment and that the second assignment produces and overflow and its corresponding exception. The conversion operator CONV can be used to bypass these problems.

TYPES pack8_3  TYPE p LENGTH 8 DECIMALS 3.
TYPES pack16   TYPE p LENGTH 16.
DATA  number1  TYPE pack8_3 VALUE '12345.6789'.
DATA  number2  TYPE pack16 VALUE  '12345678901234567890'.

DATA(result1) = number1.                    "p, length 8, decimals 3
DATA(result2) = number2.                    "p, length 16
TRY.
    DATA(result3) = 1 * number1.            "p, length 8, decimals 0
    DATA(result4) = 1 * number2.            "p, length 8 ->exception
  CATCH cx_sy_arithmetic_overflow.
ENDTRY.
DATA(result5) = CONV pack8_3( 1 * number1 ). "p, length 8, decimals 3
DATA(result6) = CONV pack16(  1 * number2 ). "p, length 16

◈ =, String Expression

Syntax

result = string_exp.

Effect

If a string expression string_exp is specified on the right side of the assignment operator =, its result of type string is calculated and assigned to the left side result.

The following can be specified for result:

- A variable that has type string or to whose data type the result can be converted.
- An inline declaration DATA(var). The data type of the declared variable var is string.

Note

In the following example, the result result is a variable with the type string, strings are appended to this variable on the right side, and the strings cannot be dependent on result (as detected by the compiler):

result = result && dobj1 && dobj2 && ...
result = |result...{ dobj1 ... }...{ dobj2 ... }...|.

Here, no subtotal is created and an append is made directly on result. In all other cases (namely data types other than string or when expressions or functions are appended on the right side), a subtotal is created first and then assigned. This optimized method improves performance, but it must not be canceled by using expression or functions on the right side (even if they are not dependent on result). This applies in particular to operations in loops (see the example).

Example

The first assignment declares a target field of the type string inline and assigns it the chained text "12". The second assignment converts the chained text "12" to the number 12.

DATA(text) = '1' && '2'.

DATA number TYPE i.
number = 1 && 2.

Chaining Strings

This example demonstrates how string chainings can be optimized.

Source Code

REPORT demo_string_concatenation.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    GET RUN TIME FIELD DATA(t1).
    DATA(result1) =
      REDUCE string( INIT s = ``
                     FOR i = 1 UNTIL i > 100000
                     NEXT s = s && CONV string( i ) ).
    GET RUN TIME FIELD DATA(t2).
    DATA(t21) = t2 - t1.

    GET RUN TIME FIELD DATA(t3).
    DATA(result2) =
      REDUCE string( INIT s = ``
                     FOR i = 1 UNTIL i > 100000
                     LET n = CONV string( i ) IN
                     NEXT s = s && n ).
    GET RUN TIME FIELD DATA(t4).
    DATA(t43) = t4 - t3.

    ASSERT result1 = result2.
    cl_demo_output=>display( |Optimization factor: { t21 / t43 }| ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

This example demonstrates how a string chaining is optimized when the right side of an assignment appends an expression to a string specified on the left side:

- The first REDUCE expression is not optimized, since an expression is appended to s that is potentially dependent on s.

- In the second REDUCE expression, the expression is assigned to an auxiliary variable using LET. This variable can be appended to s directly without a subtotal needing to be produced.

◈ =, Bit Expression

Syntax

result = bit_exp.

Effect

If a bit expression bit_exp is specified on the right side of the assignment operator =, a byte chain with the calculation length is calculated and assigned to the left side result like a source field with the type xstring.

The following can be specified for result:

◈ A variable with the type x or xstring.
◈ A variable with the type c or string .
◈ An inline declaration DATA(var). The data type of the declared variable var is xstring.

Example

Bit expressions as the right side of assignments. The expressions can also be used directly as input parameters of the output methods.

DATA hex1 TYPE xstring VALUE '0123456789ABCDEF'.
DATA hex2 TYPE xstring VALUE 'FEDCBA9876543210'.

DATA(result1)  = hex1 BIT-AND hex2.
DATA(result2)  = hex1 BIT-OR hex2.
cl_demo_output=>write(   result1 ).
cl_demo_output=>display( result2 ).

1.3 CONV - Conversion Operator

Syntax

... CONV type( [let_exp] dobj ) ...

Effect

A constructor expression with the conversion operator CONV converts the argument dobj to the data type specified using type and creates an appropriate result. The following can be specified for type:

◈ A non-generic data type dtype (with the exception of reference types).
◈ The # character as a symbol for the operand type.

     ◈ If the data type required in an operand position is unique and fully identifiable, this type is used.
     ◈ If the constructor expression is passed to an actual parameter with generically typed formal parameter, the operand type is derived in accordance with special rules.
     ◈ In other cases, the character # cannot be specified.

The parentheses must contain precisely one unnamed argument dobj that can be converted to the data type type, with the following restriction: If dobj is specified as a bit expression, type must be byte-like or character-like with the type c or string. dobj is a general expression position.

The content of the result is determined by an assignment of the argument in accordance with the associated conversion rule. If dobj is compatible with the data type type, CONV does not need to be used and a syntax check warning is usually produced.

Optionally, a LET expression let_exp can be specified before the data object to define local auxiliary fields. If a LET expression is specified, a warning is produced after conversions to compatible types, since LET can be used to construct new values using utility variables.

Notes

◈ The conversion operator CONV is suitable for avoiding the declaration of helper variables only needed to (for example)
◈ specify type-friendly actual parameters.
◈ influence the calculation type of an arithmetic expression or the comparison type of a relational expression.
◈ The argument of CONV can itself be a calculation expression, which means that CONV can be used within a calculation expression to transform results of subcalculations to a specific type.
◈ No empty parentheses can be specified after CONV to construct an initial value of the specified type. The expression VALUE #( ) can be used to do this.
◈ The conversion operator CONV closes the gap where the value operator VALUE cannot be used to construct values for elementary data objects except for the initial value.
◈ If a constructor expression with the conversion operator is used as a source field of an assignment where the same conversion takes place, it can be ignored and is removed when the program is compiled.
◈ For reference types, the conversion operator CONV is not necessary, since these involve only castings and no conversions. The operator CAST is used for castings.

Example

The method CONVERT_TO of the class CL_ABAP_CODEPAGE expects the data type string for the input parameter SOURCE. CONV is used to convert a text field to this data type, directly in the operand position.

DATA text TYPE c LENGTH 255.

DATA(xstr) = cl_abap_codepage=>convert_to(
  source      = CONV string( text )
  codepage    = `UTF-8` ).

Example

Even though the internal table itab in the method meth1 has the same row type as the table type of the parameter para of the method meth2, it cannot be passed directly due to its different table category and key. CONV is used to convert itab to the required table type.

CLASS class DEFINITION.
  PUBLIC SECTION.
    TYPES t_itab TYPE STANDARD TABLE OF i
                 WITH EMPTY KEY.
    METHODS meth1.
  PRIVATE SECTION.
    METHODS meth2 IMPORTING para TYPE t_itab.
ENDCLASS.

CLASS class IMPLEMENTATION.
  METHOD meth1.
    DATA itab TYPE SORTED TABLE OF i
              WITH NON-UNIQUE DEFAULT KEY.
    ...
    meth2( CONV #( itab ) ).
    ...
  ENDMETHOD.
  METHOD meth2.
    ...
  ENDMETHOD.
ENDCLASS.

Example

The two calculations produce different results. In the first case, the calculation type is f and the end result is converted to i. In the second case, CONV converts each intermediate result to the calculation type i.

DATA int TYPE i.

int = sqrt( 5 ) + sqrt( 6 ).
int = CONV i( sqrt( 5 ) ) + CONV i( sqrt( 6 ) ).

Example

The first logical expression is false, as specified in the comparison rules for character-like data types. CONV is used to alter the comparison type of the second comparison so that the comparison is true.

DATA txt TYPE abap_bool.
DATA str TYPE string.

txt = ' ' .
str = ` `.

IF txt = str.
  ...
ENDIF.

IF txt = CONV abap_bool( str ).
  ...
ENDIF.

1.3.1 CONV - Type Inference for Actual Parameters

If a constructor expression

CONV #( [let_exp] dobj )

is passed to generically typed formal parameters as an actual parameter using the character # as a symbol for the operand type, the following type inference is performed for the character #:

◈ If the data type of the argument dobj can be identified and matches the generic type of the formal parameter, nothing is converted and the type of the argument is used instead.

◈ If the data type of the argument dobj can be identified and is elementary, types are derived from it as follows for formal parameter types with generic lengths:

  ◈ Generic type c

c with the length of the argument in arguments of the types n, d, and t; c of the predefined output length of the argument in all other argument types except strings; no type is derived in arguments of the types string and xtring

  ◈ Generic type n

n with the length of the argument in arguments of the types n, d, and t and n with the length of a conversion of the argument to n in all other argument types except numeric types with decimal places and strings; no type is derived in arguments of the types decfloat16, decfloat34, f, and p with decimal places plus string and xtring

  ◈ Generic type x

x with half the length (rounded up) of the argument in arguments of the type c; x with the length 4 in all other argument types except strings; no type is derived in arguments of the types string and xtring

  ◈ Generic type p

p without decimal places with the length 16 in arguments of the types decfloat16, decfloat34, f, string, or c and n with lengths greater than 15; p without decimal places with the length 8 in all other argument types

◈ In other cases, the type is derived from the generic type of the formal parameter as follows:

◈ csequence and clike produce string
◈ xsequence produces xstring
◈ numeric and decfloat produce decfloat34
◈ p produces p with the length 8 without decimal places
◈ Standard table type with generic primary table key produces a standard table with a standard key

Other combinations of generic formal parameter types and arguments cannot be made more concrete in any meaningful way and produce a syntax error (with the exception of table types that are explicitly generic with respect to their secondary table keys).

Syntax warnings that can be hidden using pragmas also indicate any redundant conversions produced by the rules above.

Note

Rules apply when deriving the type in cases where # is specified for actual parameters that can be passed to generically typed formal parameters. These rules prevent syntax errors in programs that call a procedure and the procedure makes the full typing of a formal parameter type more general by switching to a generic type.

1.3.2 Conversion Operator, Type Inference

This example demonstrates a type inference for the conversion operator CONV.

Source Code

REPORT demo_conv_type_inference.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    TYPES c10 TYPE c LENGTH 10.
    CLASS-METHODS:
      main,
      meth1 IMPORTING p TYPE c10,
      meth2 IMPORTING p TYPE c,
      meth3 IMPORTING p TYPE csequence,
      descr IMPORTING p TYPE any.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    FIELD-SYMBOLS <fs> TYPE any.
    DATA txt TYPE c LENGTH 20.
    DATA num TYPE i.
    ASSIGN num TO <fs>.

    demo=>meth1( p = CONV #( txt  ) ) ##operator.
    demo=>meth1( p = CONV #( num  ) ).
    demo=>meth1( p = CONV #( <fs> ) ).
    cl_demo_output=>line( ).

    demo=>meth2( p = CONV #( txt  ) ) ##operator.
    demo=>meth2( p = CONV #( num  ) ).
   "demo=>meth2( p = CONV #( <fs> ) ). "not possible
    cl_demo_output=>line( ).

    demo=>meth3( p = CONV #( txt  ) ) ##operator.
    demo=>meth3( p = CONV #( num  ) ) ##type.
    demo=>meth3( p = CONV #( <fs> ) ) ##type.
    cl_demo_output=>display( ).
  ENDMETHOD.
  METHOD meth1.
    descr( p ).
  ENDMETHOD.
  METHOD meth2.
    descr( p ).
  ENDMETHOD.
  METHOD meth3.
    descr( p ).
  ENDMETHOD.
  METHOD descr.
    DATA type   TYPE string.
    DATA length TYPE i.
    DESCRIBE FIELD p TYPE type.
    IF type = 'g'.
      type = 'STRING'.
      length = strlen( p ).
    ELSE.
      DESCRIBE FIELD p LENGTH length IN CHARACTER MODE.
    ENDIF.
    cl_demo_output=>write( |{ type } { length }| ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

Passes constructor expressions with the conversion operator CONV to differently typed formal parameters of methods. In the case of generic formal parameters, special rules apply when identifying the operand type.

◈ Fully typed formal parameter

When the method meth1 is called with a fully typed formal parameter, the operand type for # is identified using this parameter and the result of the reduction is converted to c with length 10 in both calls. The first conversion is redundant here.

◈ Formal parameter typed generically with c
The operand type for # is determined from the argument.

- In the first call, the type c with length 20 of the argument matches the generic type and is used, which is why this conversion is redundant.

- In the second call, the type i does not match the generic type and the type c with the predefined output length 11 of i is used.

- Calls with the generically typed field symbol <fs> are not possible, since no type can be derived from the argument.

◈ Formal parameter typed generically with csequence

- In the first call, the type c with length 20 of the argument matches the generic type and is used, which is why this conversion is redundant.
- In the second call, the type i does not match the generic type and the type string is used. This is indicated by a syntax check warning.
- In the third call, no type can be determined from the generically typed field symbol <fs> and the type string is used. This is indicated by a syntax check warning.

1.4 Assigning Structure Components

The following special language constructs are used to assign components from structures or columns of internal tables:

◈ MOVE-CORRESPONDING for the assignment of components with the same name
◈ Component operator CORRESPONDING for the assignment of components using identical names or a static mapping rule.
◈ System class CL_ABAP_CORRESPONDING for dynamic mapping rules

1.4.1  MOVE-CORRESPONDING

Syntax Forms

Variant for Structures 

1. MOVE-CORRESPONDING [EXACT] struc1 TO struc2
    [EXPANDING NESTED TABLES].

Variant for Internal Tables 

2. MOVE-CORRESPONDING [EXACT] itab1 TO itab2
    [EXPANDING NESTED TABLES] [KEEPING TARGET LINES].

Effect

The statement MOVE-CORRESPONDING is used to assign components with the same name in structured data objects to each other. There are two variants of the statements, which can call each other internally:

◈ Both operands are structures.
◈ Both operands are internal tables.

No other combinations of operand types are possible. Field symbols typed with the generic type ANY or formal parameters can also be used as operands. An operand of this type must be either a structure or an internal table when the statement is executed and match the other operands; if not, an unhandleable exception is raised. struc1 and itab1 are functional operand positions.

Notes

◈ If mapping based on identical names is not sufficient for the statement MOVE-CORRESPONDING, the component operator CORRESPONDING can be used, which makes it possible to define separate mapping rules statically.
◈ If dynamic mapping rules are needed, the system class CL_ABAP_CORRESPONDING can be used.
◈ If an internal table with a header line is specified for one of the operands, the header line is used as an operand in accordance with its data type, not the table body.

Non-Handleable Exceptions

◈ Cause: The operands are not both structures or internal tables.
Runtime Error: TCHK_MOVE_CORRESPONDING:

◈ The same runtime errors can occur as for MOVE and MOVE EXACT.

◈ The same runtime errors can occur as for INSERT itab.

- MOVE-CORRESPONDING - structure

Syntax

MOVE-CORRESPONDING [EXACT] struc1 TO struc2
  [EXPANDING NESTED TABLES].

Additions

1. ... EXACT

2. ... EXPANDING NESTED TABLES

Effect

This variant of the statement MOVE-CORRESPONDING requires structures to be specified for struc1 and struc2. Meshes - as operands of statement MOVE-CORRESPONDING - are handled in the same way as normal structures and can also be specified.

The system searches for all components with the same name in struc1 and struc2 and the content of components in struc1 is assigned to the components with the same name in struc2. Other components are not affected. If field symbols are used as operands, the names of the components are evaluated in accordance with the data type of the field symbols. A casting may be used to make this type different from the names of the actual structures.

Nested structures are fully expanded. The names of the components are compared down to the lowest common level. If the addition EXPANDING NESTED TABLES is not specified, the statement

struc2-comp = struc1-comp.

is executed for each component pair comp with the same name. Any associated conversion are performed and the relevant exceptions may be raised. In particular, if the components are table-like, the entire table body is mapped in accordance with the conversion rules for internal tables.

If struc1 or struc2 are empty customizing includes when the statement is executed (that is they do not contain any components), the statement is ignored. If struc1 is a structure that contains empty customizing includes as components, these are also ignored when the structure is evaluated.

Notes

◈ If structures are specified for struc1 and struc2 and the structures are detectable statically, the names are compared once when the program is generated by ABAP Compiler. If untyped field symbols or formal parameters are used, the names must be compared each time the statement is executed.

◈ Field symbols that point to structures can have different names for the components than the structure itself, as specified by the CASTING addition of the statement ASSIGN. The statement MOVE-CORRESPONDING evaluates the names of the data type of the current operand. In this way, components in the same structure can also be assigned to each other. Note that the order of editing, and hence the result in a component that is both source and target, is usually undefined. No temporary intermediate result is created and it is not possible to exchange the content of two components from the same structure in a single statement. See the example Reflexive Component Assignments.

◈ The compiler optimizes the MOVE-CORRESPONDING statement for structures so that sequences of components that have the same names in both structures are grouped and copied together. It is therefore advisable to construct the structures in question in the same way whenever possible.

◈ MOVE-CORRESPONDING for structures ignores names that were only defined with the AS name addition of the INCLUDE statement or when structures were integrated into ABAP Dictionary. However, components that were renamed using the addition RENAMING WITH SUFFIX of the statement INCLUDE or similarly in ABAP Dictionary are not ignored.

Addition 1

... EXACT

Effect

If the addition EXACT is specified for MOVE-CORRESPONDING the following lossless assignment is made for each identically named component pair comp

struc2-comp = EXACT #( struc1-comp ).

and the corresponding checks are performed. If an exception is raised, all components are assigned up to the component that raised the exception. This component, and all following components, are not assigned.

Addition 2

... EXPANDING NESTED TABLES

Effect

This addition specifies that, for two components with the same name and which are both internal tables, no assignment is made and that the variant MOVE-CORRESPONDING [EXACT] for internal tables with the addition EXPANDING NESTED TABLES and without the addition KEEPING TARGET LINES is executed instead.

Tabular components are resolved at every hierarchy level and identically named components are mapped row by row. The target tables are deleted before a mapping.

Note

If one of two identically named components is an internal table and the other not, MOVE-CORRESPONDING is never possible, regardless of whether EXPANDING NESTED TABLES is used.

Example

In the following example, the structure struc1 contains the components:

◈ struc1-comp1

◈ struc1-struci-comp1

◈ struc1-struci-comp2-col1

◈ struc1-struci-comp2-col2

◈ struc1-itab

The structure struc2 contains the components:

◈ struc2-struci-comp1

◈ struc2-struci-comp2

◈ struc2-struci-comp3

◈ struc2-itab

Over the length of the shorter path, the components struci-comp1, struci-comp2, and itab have the same name. These are assigned from struc1 to struc2 in both MOVE-CORRESPONDING statements. In struc1, struci-comp2 is self-structured; in struc2, struci-comp2 is elementary. When struc1-struci-comp2 is assigned to struc2-struci-comp2, the source field is documented as an elementary field of type c in accordance with the conversion rules for structures.

The components itab are table-like and have compatible row types. The statement MOVE-CORRESPONDING without the addition EXPANDING NESTED TABLE maps the table body and the content of itab in struc2 then matches the content of itab in struc1. If the addition EXPANDING NESTED TABLE is used, only the component col2 is mapped and col3 remains initial.

The components struc1-comp1 and struc2-struci-comp3 do not have any equivalents with the same name and are ignored in the assignment.

TYPES: BEGIN OF line1,
         col1 TYPE i,
         col2 TYPE i,
       END OF line1,
       BEGIN OF line2,
         col2 TYPE i,
         col3 TYPE i,
       END OF line2.

DATA: BEGIN OF struc1,
        comp1 TYPE c LENGTH 1 VALUE 'U',
        BEGIN OF struci,
          comp1 TYPE c LENGTH 1 VALUE 'V',
          BEGIN OF comp2,
            col1 TYPE c LENGTH 1 VALUE 'X',
            col2 TYPE c LENGTH 1 VALUE 'Y',
          END OF comp2,
        END OF struci,
        itab TYPE TABLE OF line1 WITH EMPTY KEY,
     END OF struc1.

DATA: BEGIN OF struc2,
        BEGIN OF struci,
          comp1 TYPE string,
          comp2 TYPE string,
          comp3 TYPE string,
        END OF struci,
        itab TYPE TABLE OF line2 WITH EMPTY KEY,
     END OF struc2.

struc1-itab = VALUE #(
  ( col1 = 11 col2 = 12 )
  ( col1 = 21 col2 = 22 ) ).

MOVE-CORRESPONDING struc1 TO struc2.
MOVE-CORRESPONDING struc1 TO struc2 EXPANDING NESTED TABLES.

Example

This example shows how MOVE-CORRESPONDING is applied to two structures with the same type t_str and which are cast using field symbols with different types. The statement evaluates the names of the types of the field symbols, which assigns the content of components that actually have different names.

TYPES: BEGIN OF t_str,
         a1 TYPE i,
         a2 TYPE i,
       END OF t_str.

TYPES: BEGIN OF t_str1,
         b1 TYPE i,
         b2 TYPE i,
       END OF t_str1.

TYPES: BEGIN OF t_str2,
         b2 TYPE i,
         b1 TYPE i,
       END OF t_str2.

DATA(str1) = VALUE t_str( a1 = 1 a2 = 2 ).
DATA str2 LIKE str1.

FIELD-SYMBOLS <fs1> TYPE t_str1.
ASSIGN str1 TO <fs1> CASTING.

FIELD-SYMBOLS <fs2> TYPE t_str2.
ASSIGN str2 TO <fs2> CASTING.

MOVE-CORRESPONDING <fs1> TO <fs2>.

cl_demo_output=>write(   str1 ).
cl_demo_output=>display( str2 ).

MOVE-CORRESPONDING for Structures

This example demonstrates the statement MOVE-CORRESPONDING for structures.

Source Code

REPORT demo_move_corresponding_struct.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.

  PRIVATE SECTION.
    TYPES:
      c3 TYPE c LENGTH 3,

      BEGIN OF iline1,
        col1 TYPE c3,
        col2 TYPE c3,
      END OF iline1,

      BEGIN OF iline2,
        col2 TYPE c3,
        col3 TYPE c3,
      END OF iline2,

      BEGIN OF line1,
        col1 TYPE                   c3,
        col2 TYPE                   c3,
        col3 TYPE STANDARD TABLE OF iline1 WITH EMPTY KEY,
      END OF line1,

      BEGIN OF line2,
        col2 TYPE                   c3,
        col3 TYPE STANDARD TABLE OF iline2 WITH EMPTY KEY,
        col4 TYPE                   c3,
      END OF line2.

    CLASS-DATA:
      struct1 TYPE        line1,
      struct2 TYPE        line2,

      out     TYPE REF TO if_demo_output.

    CLASS-METHODS:
      fill_structures,
      display_structure1,
      display_structure2.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    out = cl_demo_output=>new( ).
    fill_structures( ).
    out->begin_section( `struct1` ).
    display_structure1( ).
    out->next_section( `struct2` ).
    display_structure2( ).

    out->begin_section(
      `MOVE-CORRESPONDING` ).

    MOVE-CORRESPONDING struct1 TO struct2.

    display_structure2( ).
    out->next_section(
      `MOVE-CORRESPONDING EXPANDING NESTED TABLES` ).

    MOVE-CORRESPONDING struct1 TO struct2 EXPANDING NESTED TABLES.

    display_structure2( ).
    out->display( ).
  ENDMETHOD.
  METHOD fill_structures.
    struct1 = VALUE #(
       col1 = 'a1'
       col2 = 'a2'
       col3 = VALUE #( ( col1 = 'a11'  col2 = 'a12' )
                       ( col1 = 'a21'  col2 = 'a22' ) ) ).

    struct2 = VALUE #(
       col2 = 'x11'
       col3 = VALUE #( ( col2 = 'x11'  col3 = 'x12' )
                       ( col2 = 'x21'  col3 = 'x22' )
                       ( col2 = 'x31'  col3 = 'x32' ) )
       col4 = 'x12'  ).
  ENDMETHOD.
  METHOD display_structure1.
    DATA:
      BEGIN OF outl,
        col1  TYPE c3,
        col2  TYPE c3,
        col31 TYPE c3,
        col32 TYPE c3,
      END OF outl.
    DATA output LIKE STANDARD TABLE OF outl WITH EMPTY KEY.
    outl-col1 = struct1-col1.
    outl-col2 = struct1-col2.
    LOOP AT struct1-col3 ASSIGNING FIELD-SYMBOL(<col3>).
      outl-col31 = <col3>-col1.
      outl-col32 = <col3>-col2.
      IF sy-tabix > 1.
        CLEAR outl-col1.
        CLEAR outl-col2.
      ENDIF.
      APPEND outl TO output.
    ENDLOOP.
    out->write( output ).
  ENDMETHOD.
  METHOD display_structure2.
    DATA:
      BEGIN OF outl,
        col2  TYPE c3,
        col32 TYPE c3,
        col33 TYPE c3,
        col4  TYPE c3,
      END OF outl.
    DATA output LIKE STANDARD TABLE OF outl WITH EMPTY KEY.
    outl-col2 = struct2-col2.
    outl-col4 = struct2-col4.
    LOOP AT struct2-col3 ASSIGNING FIELD-SYMBOL(<col3>).
      outl-col32 = <col3>-col2.
      outl-col33 = <col3>-col3.
      IF sy-tabix > 1.
        CLEAR outl-col2.
        CLEAR outl-col4.
      ENDIF.
      APPEND outl TO output.
    ENDLOOP.
    out->write( output ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

MOVE-CORRESPONDING and the available additions are used to declare two structures, struct1 and struct2, and assign them to each other.

◈ struct1 contains two elementary components, col1 and col2, and a tabular component col3 with the components col1 and col2.
◈ struct2 contains two elementary components, col2 and col4, and a tabular component col3 with the components col2 and col3.

The structures are filled with values. For the output, the structured components are resolved to elementary components of an output table, output.

The statement MOVE-CORRESPONDING finds the identically named components col2 and col3 in struct1 and struct2 and the assignments work as follows:

◈ MOVE-CORRESPONDING

After the assignment, the components col2 and col3 in struct2 have the same content as in struct1. Component col4 keeps its value.

◈ MOVE-CORRESPONDING EXPANDING NESTED TABLES

After the assignment, the component col2 in struct2 has the same content as in struc1. col4 preserves its value. The tabular component col3 is resolved and the identically named component col2 found there. The original content of struct2-col3 is deleted. After the assignment, the column col2 has the same content as in struct1-col3, whereas the column col3 remains initial.

- MOVE-CORRESPONDING - itab

Syntax

MOVE-CORRESPONDING [EXACT] itab1 TO itab2.
  [EXPANDING NESTED TABLES] [KEEPING TARGET LINES].

Additions

1. ... EXPANDING NESTED TABLES

2. ... KEEPING TARGET LINES

Effect

This variant of the statement MOVE-CORRESPONDING requires internal tables to be specified for itab1 and itab2. It searches for all similarly named components in the row types of itab1 and itab2 and assigns them from itab1 to itab2 in accordance with the rules below.

◈ If the row type is structured, the components are the structure components.

◈ A non-structured row type is handled like a structure with a single component. The type of the component is the row type of the internal table (elementary), is a table itself, or is a reference variable. The component has an internal name that is the same for all tables.

If there are components with the same name, the target table itab2 is deleted without the addition KEEPING TARGET LINES and the same number of initial rows are inserted as exist in the source table itab1. The rows of the source table are then extracted sequentially (in the same order as in the statement LOOP) and the content of each row is assigned to the corresponding row in the target table in accordance with the rules for MOVE-CORRESPONDING [EXACT] for structures. Finally, the table keys and associated table indexes are updated (if necessary) in the target table in accordance with the rules insertions in internal tables. The relevant exceptions are raised if uniqueness is violated.

If there are no components with the same name, no assignment is made and the target table is left unchanged.

Notes

◈ MOVE-CORRESPONDING never has an effect when an internal table with a non-structured row type is assigned to an internal table with a structured row type (or when the assignment is the other way round).

◈ Without additions, MOVE-CORRESPONDING has the same effect on the assignment of an internal table with non-structured row type to a similar table as a regular assignment.

◈ If an internal table, itab, is assigned to itself using MOVE-CORRESPONDING, the statement is ignored. This also means that the rows are not deleted first and then filled again.

◈ The system performs a static check (where possible) to see whether the components are convertible. If the convertibility cannot be identified statically, a runtime error is only raised if an assignment is really performed.

Addition 1

... EXPANDING NESTED TABLES

Effect

If this addition is specified, the individual rows are assigned in accordance with the rules for MOVE-CORRESPONDING [EXACT] with EXPANDING NESTED TABLES specified, and tabular components are resolved at every hierarchy level.

If the addition is not specified, the individual rows are assigned in accordance with the rules for MOVE-CORRESPONDING [EXACT] without EXPANDING NESTED TABLES specified, and tabular components are assigned in accordance with the rules for assignments or lossless assignments.

Addition 2

... KEEPING TARGET LINES

Effect

This addition stops the target table itab2 from being deleted. Instead, it appends the same number of initial rows as exist in the source table itab1. The rows of the source tables are then mapped to these rows. The table keys and indexes are then updated across all rows. If no identically named components are found, the target table remains unchanged.

Note

The addition KEEPING TARGET LINES is only effective on the rows of itab2. It cannot be effective on nested tables, even when specified with the addition EXPANDING NESTED TABLES. This is because nested tables are always resolved in new initial rows.

MOVE-CORRESPONDING for Internal Tables

This example demonstrates the statement MOVE-CORRESPONDING for internal tables.

Source Code

REPORT demo_move_corresponding_itab.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.

  PRIVATE SECTION.
    TYPES:
      c3 TYPE c LENGTH 3,

      BEGIN OF iline1,
        col1 TYPE c3,
        col2 TYPE c3,
      END OF iline1,

      BEGIN OF iline2,
        col2 TYPE c3,
        col3 TYPE c3,
      END OF iline2,

      BEGIN OF line1,
        col1 TYPE                   c3,
        col2 TYPE                   c3,
        col3 TYPE STANDARD TABLE OF iline1 WITH EMPTY KEY,
      END OF line1,

      BEGIN OF line2,
        col2 TYPE                   c3,
        col3 TYPE STANDARD TABLE OF iline2 WITH EMPTY KEY,
        col4 TYPE                   c3,
      END OF line2.

    CLASS-DATA:
      itab1 TYPE STANDARD TABLE OF line1 WITH EMPTY KEY,
      itab2 TYPE STANDARD TABLE OF line2 WITH EMPTY KEY,

      out   TYPE REF TO            if_demo_output.

    CLASS-METHODS:
      fill_tables,
      display_table1,
      display_table2.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA buffer LIKE itab2.
    out = cl_demo_output=>new( ).
    fill_tables( ).
    out->begin_section( `itab1` ).
    display_table1( ).
    out->next_section( `itab2` ).
    display_table2( ).
    buffer = itab2.

    out->begin_section(
      `MOVE-CORRESPONDING` ).

    MOVE-CORRESPONDING itab1 TO itab2.

    display_table2( ).
    itab2 = buffer.
    out->next_section(
      `MOVE-CORRESPONDING KEEPING TARGET LINES` ).

    MOVE-CORRESPONDING itab1 TO itab2 KEEPING TARGET LINES.

    display_table2( ).
    itab2 = buffer.
    out->next_section(
      `MOVE-CORRESPONDING EXPANDING NESTED TABLES` ).

    MOVE-CORRESPONDING itab1 TO itab2 EXPANDING NESTED TABLES.

    display_table2( ).
    itab2 = buffer.
    out->next_section(
      `MOVE-CORRESPONDING EXPANDING NESTED TABLES ` &&
      `KEEPING TARGET LINES` ).

    MOVE-CORRESPONDING itab1 TO itab2 EXPANDING NESTED TABLES
                                      KEEPING TARGET LINES.

    display_table2( ).
    out->display( ).
  ENDMETHOD.
  METHOD fill_tables.
    itab1 = VALUE #(
      ( col1 = 'a11'
        col2 = 'a12'
        col3 = VALUE #( ( col1 = 'a11'  col2 = 'a12' )
                        ( col1 = 'a21'  col2 = 'a22' ) ) )
      ( col1 = 'b21'
        col2 = 'b22'
        col3 = VALUE #( ( col1 = 'b11'  col2 = 'b12' )
                        ( col1 = 'b21'  col2 = 'b22' ) ) )
      ( col1 = 'c31'
        col2 = 'c32'
        col3 = VALUE #( ( col1 = 'c11'  col2 = 'c12' )
                        ( col1 = 'c21'  col2 = 'c22' ) ) ) ).

    itab2 = VALUE #(
      ( col2 = 'x11'
        col3 = VALUE #( ( col2 = 'x11'  col3 = 'x12' )
                        ( col2 = 'x21'  col3 = 'x22' )
                        ( col2 = 'x31'  col3 = 'x32' ) )
        col4 = 'x12' )
      ( col2 = 'y21'
        col3 = VALUE #( ( col2 = 'y11'  col3 = 'y12' )
                        ( col2 = 'y21'  col3 = 'y22' )
                        ( col2 = 'y31'  col3 = 'y32' ) )
        col4 = 'y22' ) ).
  ENDMETHOD.
  METHOD display_table1.
    DATA:
      BEGIN OF outl,
        col1  TYPE c3,
        col2  TYPE c3,
        col31 TYPE c3,
        col32 TYPE c3,
      END OF outl.
    DATA output LIKE STANDARD TABLE OF outl WITH EMPTY KEY.
    LOOP AT itab1 ASSIGNING FIELD-SYMBOL(<wa>).
      outl-col1 = <wa>-col1.
      outl-col2 = <wa>-col2.
      LOOP AT <wa>-col3 ASSIGNING FIELD-SYMBOL(<col3>).
        outl-col31 = <col3>-col1.
        outl-col32 = <col3>-col2.
        IF sy-tabix > 1.
          CLEAR outl-col1.
          CLEAR outl-col2.
        ENDIF.
        APPEND outl TO output.
      ENDLOOP.
    ENDLOOP.
    out->write( output ).
  ENDMETHOD.
  METHOD display_table2.
    DATA:
      BEGIN OF outl,
        col2  TYPE c3,
        col32 TYPE c3,
        col33 TYPE c3,
        col4  TYPE c3,
      END OF outl.
    DATA output LIKE STANDARD TABLE OF outl WITH EMPTY KEY.
    LOOP AT itab2 ASSIGNING FIELD-SYMBOL(<wa>).
      outl-col2 = <wa>-col2.
      outl-col4 = <wa>-col4.
      LOOP AT <wa>-col3 ASSIGNING FIELD-SYMBOL(<col3>).
        outl-col32 = <col3>-col2.
        outl-col33 = <col3>-col3.
        IF sy-tabix > 1.
          CLEAR outl-col2.
          CLEAR outl-col4.
        ENDIF.
        APPEND outl TO output.
      ENDLOOP.
    ENDLOOP.
    out->write( output ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

MOVE-CORRESPONDING and the available additions are used to declare two internal tables, itab1 and itab2, and assign them to each other.

◈ itab1 contains two elementary components, col1 and col2, and a tabular component col3 with the components col1 and col2.
◈ itab2 contains two elementary components, col2 and col4, and a tabular component col3 with the components col2 and col3.

The internal tables are filled with values. For the output, the structured components are resolved to elementary components of an output table, output.

The statement MOVE-CORRESPONDING finds the identically named components col2 and col3 in itab1 and itab2 and the assignments work as followst:

◈ MOVE-CORRESPONDING
The original content of itab2 is deleted. After the assignment, the columns col2 and col3 have the same content as in itab1, whereas col4 remains initial.
◈ MOVE-CORRESPONDING KEEPING TARGET LINES
The original content of itab2 is preserved. Three new rows are added in which the columns col2 and col3 have the same content as in itab1, whereas col4 remains initial.
◈ MOVE-CORRESPONDING EXPANDING NESTED TABLES
The original content of itab2 is deleted. After the assignment, the column col2 has the same content as in itab1 and col4 remains initial. The tabular component col3 is resolved and the identically named component col2 found there. The original content of itab2-col3 is deleted. After the assignment, the column col2 has the same content as in itab1-col3, whereas the column col3 remains initial.
◈ MOVE-CORRESPONDING EXPANDING NESTED TABLES KEEPING TARGET LINES
The original content of itab2 is preserved. Three new rows are added, to which the same applies as to the results of the previous assignment.

1.4.2 CORRESPONDING - Component Operator

Syntax Forms

Basic Form 

1. ... { CORRESPONDING type( [DEEP] [BASE ( base )] struct|itab ) }
    | { CORRESPONDING type( [BASE ( base )] struct|itab [ mapping] ) } ...

Lookup Table 

2. ... CORRESPONDING type( itab FROM lookup_tab
                               USING [KEY key_name] s1 = t1 s2 = t2 ...
                               [mapping] ) ...

Effect

A constructor expression with the component operator CORRESPONDING creates a result of a data type specified using type.

◈ In the basic component, the components of a structured or tabular results are constructed from the components of a structured or tabular parameter struc or itab.
◈ In the variant with lookup table, the components of an internal table itab and a lookup table lookup_tab are combined with a tabular result.

The following can be specified for type:

◈ A structured type or a table type.
◈ The # character as a symbol for the operand type. Can be specified only if the data type required in an operand position is unique and fully identifiable. The operand type must be a structure type or a table type. When a constructor expression is assigned to a field symbol or to a formal parameter with a generic table type, the operand type can only be determined at runtime.

The operator creates structures or internal tables with the specified data type. The components or columns of the result are filled using assignments of components of the parameters specified in the parentheses. The assignments are made using matching names or mapping relationships in a mapping rule.

➥ CORRESPONDING - Basic Form

Syntax

... { CORRESPONDING dtype|#( [DEEP] [BASE ( base )] struct|itab) }
  | { CORRESPONDING dtype|#( [BASE ( base )] struct|itab mapping ) } ...

Addition:

... BASE ( base ) ...

Effect

This expression constructs a result with the target type specified using dtype or # from the components of a parameter struct or itab struct and itab are general expression positions.

◈ If the target type is a structured type, a structure struct must be used as a parameter. The expression creates a structure of the target type. The target structure is either initial or is assigned the value of base after the optional addition BASE as a start value. The target structure is then by default assigned the identically named components of struct in accordance with the rules of MOVE-CORRESPONDING for structures.
◈ If the target type is a table type, an internal table itab must be used as a parameter. The expression creates an internal table of the target type. The target table is either initial or is assigned the value of base after the optional addition BASE as a start value. The target table is then by default assigned the identically named columns of itab in accordance with the rules of MOVE-CORRESPONDING for internal tables using the addition KEEPING TARGET LINES.

If the addition DEEP is specified, the assignment is made in the same way as with the addition EXPANDING NESTED TABLES of the statement MOVE-CORRESPONDING. A mapping rule mapping can be used to override the matching name assignment rule of MOVE-CORRESPONDING. If a mapping rule is specified, the addition DEEP is set implicitly. It is not allowed to be set explicitly.

Notes

◈ An assignment of structures
struct2 = CORRESPONDING #( struct1 ).
without the addition BASE is not the same as an assignment
MOVE-CORRESPONDING struct1 TO struct2.
In MOVE-CORRESPONDING, all not identically named components in struct2 keep their value. If the result of the constructor expression is assigned, however, they are assigned the value from there. This value is initial for ignored components. This behavior can be overridden using the addition BASE.

◈ In the case of an assignment of a parameter to the target type and its assignment to a data object
dobj = CORRESPONDING type( ... ).
the target object is used directly (for optimization reasons) and a temporary version of the expression is not created and assigned. This is not possible when the target object and parameter are identical and a mapping rule is used. This enables, for example, the content of two components to be switched. In cases like this, a temporary copy of the target object must be created and used and an appropriate syntax warning is produced. This warning can be hidden using a pragma. If this is not detected until runtime, the information needed to create the necessary temporary copy of the target object is missing and runtime error CORRESPONDING_SELF occurs.

Addition

... BASE ( base ) ...

Effect

The addition BASE can be used to specify a start value base for the new structure or internal table. base is a functional operand position in which a database convertible to the target type can be specified.

If the addition BASE is specified, the value of base is assigned to the target structure or target table in accordance with the general assignment rules before the remainder of the expression is evaluated.

Notes

◈ Unlike the operators NEW and VALUE, parentheses must be placed around base in CORRESPONDING .
◈ Unlike the operators NEW and VALUE, the data type of base is not used in CORRESPONDING to determine a result type specified using #.
◈ The addition BASE can be used with the component operator to replace the statement MOVE-CORRESPONDING as follows:

◈ An assignment of structures

struct2 = CORRESPONDING #( BASE ( struct2 ) struct1 ).

is the same as an assignment

MOVE-CORRESPONDING struct1 TO struct2.

◈ An assignment of internal tables

itab2 = CORRESPONDING #( BASE ( itab2 ) itab1 ).

is the same as an assignment

MOVE-CORRESPONDING itab1 TO itab2 KEEPING TARGET LINES.

➥ CORRESPONDING - Lookup table

Syntax

... CORRESPONDING dtype|#( itab FROM lookup_tab
                                USING [KEY key_name] s1 = t1 s2 = t2 ...
                                [mapping]  ) ...

Effect

This variant can only be used for internal tables. The expression constructs an internal table from the components of the internal table itab and a lookup table lookup_tab. The lines of the internal table come from a comparison of itab and lookup_tab. The target type specified using dtype or # must be a table type. The parameters itab and lookup_tab expect internal tables whose row type must be structured. Also, itab must be convertible to the target type. itab and lookup_tab are general expression positions.

The result of the expression is created in the following steps:

◈ First, an intermediate result of the type of the internal table itab is constructed as follows: For each row in itab, a row is searched for in lookup table lookup_tab that has the same content in the columns s1, s2, ... as the columns t1, t2, ... of the row in itab. The search must take place using a sorted table key or a hash key (see below). If the key is not unique, the first row found is used.

     ◈ If no row like this is found, a row with the unchanged content of the row in itab is inserted into the intermediate result.

     ◈ If a row like this is found, a row is inserted into the intermediate result that is the result of an assignment of the row found in itab to the current row in lookup_tab. By default, the assignment is made in accordance with the rules of MOVE-CORRESPONDING for structures with the addition EXPANDING NESTED TABLES, with the exception that the components used for searches are not assigned by default: The components s1, s2, ... from lookup_tab used for searches are by default not assigned to the identically named components of itab and the components t1, t2, ... from itab used for searches are not assigned the identically named components from lookup_tab.

◈ The intermediate result of the type of the table itab is assigned to the result of the expression in accordance with the regular assignment rules for internal tables and, if necessary, converted to the target type.

This means that the component by component assignment takes place between the lookup table lookup_tab as the source table and an intermediate result with the type of itab as the target table. A mapping rule mapping can be used to override the default assignment of identically named components and the default exclusion of the components s1, s2, ... and t1, t2, ... It is not possible to specify the same internal table for itab and lookup_tab, otherwise there can be a runtime error CORRESPONDING_SELF.

The search for the row in lookup_tab must take place using a sorted table key or a hash key:

◈ If the addition KEY is not specified, a sorted table or a hashed table must be specified for lookup_tab.
◈ If the addition KEY is specified, the key key_name specified after it is used. The following can be specified for key_name:
     ◈ a secondary key using its name
     ◈ a primary key using its predefined name primary_key or using an alias. If the primary key is specified, lookup_tab must be a sorted table or a hashed table.

The comparison fields s1, s2, ... must cover the full table key.

Notes

◈ Unlike the basic form, this variant does not have the addition DEEP. Instead, it always behaves as if DEEP were specified. The addition BASE cannot be specified either.

◈ Generally, the type of itab and the target type must be identical.

◈ In one common use case, an existing internal table itab is enriched with information from the lookup table lookup_tab. Here, the constructor expression is assigned to the same internal table as specified for itab. In this case, the expression is optimized internally to work directly with itab without creating an intermediate result.

◈ If the constructor expression is not assigned to the internal table specified for itab or if this table is not statically identifiable, the temporary intermediate result with the type itab must be created in full. This produces a syntax check warning that can be hidden by a pragma.

◈ If the same table is specified for itab and lookup_tab, a temporary copy of the table must be created as a target table of the assignment and a syntax check warning that can be hidden is produced here too. If this is not detected until runtime, the information needed to create the necessary temporary copy of the target object is missing and runtime error CORRESPONDING_SELF occurs.

➥ CORRESPONDING - mapping

Syntax

... [ MAPPING {t1 = s1}|( t1 = s1 [MAPPING ...] [EXCEPT ...] )
              {t2 = s2}|( t2 = s2 [MAPPING ...] [EXCEPT ...] )
              ...  ]
    [ EXCEPT {ti tj ...}|* ] ...

Additions

1. ... MAPPING  t1 = s1 t2 = s2 ...

2. ... EXCEPT  {t1 t2 ...}|*

Effect

Mapping rule for the component operator CORRESPONDING. The optional mapping rule overrides the default assignment of identically named components only. The additions MAPPING and EXCEPT can be used individually or together. EXCEPT must always be specified after MAPPING.

Notes

◈ It is best to use the component operator (see the example) for mapping tasks that can be solved using either the component operator or table comprehensions.
◈ When using the basic form with a mapping rule, assignments can currently only be made to the same structure or internal table specified as an argument if this is statically identifiable. The information need to create the necessary temporary copy of the target object is missing at runtime and a runtime error occurs.
◈ The system class CL_ABAP_CORRESPONDING is provided for assignments between structures or internal tables with a dynamic mapping rule.

Addition 1

... MAPPING  t1 = s1 t2 = s2 ...

Effect

After MAPPING, t1, t2, ... are used to assign the components s1, s2,... of a source structure or source table in mapping relationships to the components of a target structure or target table.

If the components specified on the left and right of an equals sign of a mapping relationship are themselves structured or tabular with a structured row type, a separate mapping rule can be nested for these components. Here, the mapping relationship is set in parentheses ( ... ) and a further mapping rule MAPPING ... and/or EXCEPT ... is specified after the mapping relationship in accordance with the same rules as on the top level. The parentheses are not allowed if a nested mapping rule is not used.

A component of a target object cannot appear more than once in a list after MAPPING and the structure component selector cannot be used to access subcomponents. Neither of these rules apply to components of the source object. If MAPPING is used, the table types involved must also have structured row types in the basic form and the addition DEEP is set implicitly.

◈ In the basic form, t1, t2, ... are components of the target type and s1, s2, ... are components of the parameter struct or itab.
◈ In the variant with lookup table, t1, t2, ... are columns of the parameter itab and s1, s2, ... are columns of the parameter lookup_tab.

The content of the component specified on the right side of an equals sign in a mapping relationship is assigned to each component specified on the left side. If there is an identically named component in the target structure for a component specified on the right side, it is also assigned content (unless it is specified on the left side of a mapping relationship itself). In elementary components the assignment is made in accordance with the associated assignment rules. In structured and tabular components, the assignment is made in accordance with the rules of MOVE-CORRESPONDING with the addition EXPANDING NESTED TABLES.

Identically named components can also be specified on the right and left side of the equals sign. This is a good idea in the following cases:

◈ If the variant with lookup table are used, identically named components in a mapping relationship override the rule that the components s1, s2, ... and t1, t2, ... used for searches are not assigned by default.
◈ Identically named components must be specified in a mapping relationship if a nested mapping rule is to be specified for these components.

Note: The pseudo component table_line cannot be specified as a component of an internal table in the mapping rule.

Addition 2

... EXCEPT  {t1 t2 ...}|*

Effect

After EXCEPT, components t1, t2, ... of the target structure or target table that are not specified in a preceding mapping relationship or an asterisk, *, can be specified:

◈ If explicit components t1, t2, ... are specified, these components of the result are not assigned content and remain initial.
◈ If an asterisk, *, is specified, all components of the result are initial that are not specified explicitly in a preceding mapping relationship.

Access to subcomponents of components of the target object using the structure component selector is not allowed in the list after EXCEPT either.

Notes

◈ If there are identically named components in the source and target object that are not compatible or convertible, they can be excluded from the assignment using EXCEPT. This avoids syntax errors or runtime errors.
◈ If EXCEPT * is specified without preceding mapping relationships, all components of the result remain initial.

Example

Assigns the components of the structure struct1 to the components of the structure struct2 using mapping rules for the components at the top level and the components of the substructure.

DATA: BEGIN OF struct1,
        mcomp1 TYPE i VALUE 1,
        mcomp2 TYPE i VALUE 2,
        BEGIN OF substruc,
          subcomp1 TYPE i VALUE 1,
          subcomp2 TYPE i VALUE 2,
          subcomp3 TYPE i VALUE 3,
        END OF substruc,
      END OF struct1.

DATA: BEGIN OF struct2,
        comp2 TYPE i,
        comp1 TYPE i,
        BEGIN OF substruc,
          comp3 TYPE i,
          comp2 TYPE i,
          comp1 TYPE i,
        END OF substruc,
      END OF struct2.

struct2 =
  CORRESPONDING #(
    struct1 MAPPING comp1    = mcomp1
                    comp2    = mcomp2
                  ( substruc = substruc MAPPING comp1 = subcomp1
                                                comp2 = subcomp2
                                                comp3 = subcomp3 ) )

1.4.3 CL_ABAP_CORRESPONDING - System Class

The system class CL_ABAP_CORRESPONDING enables assignments of components between structures or between internal tables with dynamically specified mapping rules. The factory method CREATE of a class must be used to create a mapping object before the class can be used:

DATA(mapper) = cl_abap_corresponding=>create( source      = struct|itab
                                              destination = struct|itab
                                              mapping     = mapping_tab ).

Structures struct or internal tables itab of the assigned data types must be passed to the parameters source and destination. An internal table of the type CL_ABAP_CORRESPONDING=>MAPPING_TABLE, containing the mapping rule, must be passed to the parameter mapping. If an initial mapping table is passed, only the identically named components are assigned. The mapping table has the following components:

◈ LEVEL

Level of the components in the structure or row structure. The value 0 stands for the top level.

◈ KIND

Mapping type. The possible values are:

   ◈ CL_ABAP_CORRESPONDING=>MAPPING_COMPONENT (1) (The components specified in this row are mapped to each other.)
   ◈ CL_ABAP_CORRESPONDING=>MAPPING_EXCEPT_COMPONENT (2) (The component of the source structure specified in this row is excluded from the mapping of identically named components.)
   ◈ CL_ABAP_CORRESPONDING=>MAPPING_EXCEPT_ALL (3) (All components of the current source structure are excluded from the mapping of identically name components.)

◈ SRCNAME

Component of the source structure.

◈ DSTNAME

Component of the target (destination) structure.

The rows of the internal table must be constructed so that they produce a mapping rule in the correct order. Components of the source structure for which no mapping is defined and that were not excluded are assigned to identically named components of the target structure.

The method EXECUTE of a mapping object can be used to perform any number of assignments between structures or internal tables src and dst whose data type matches the source type or target type specified when the object was created:

mapper->execute( EXPORTING source      = src
                 CHANGING  destination = dst ).

The assignment is performed component by component

◈ between the components specified in the mapping rule
◈ between the remaining identically named components at the same level (if not excluded in the mapping rule).

In assignments between structures, components of the target structure to which no components of the source structure are assigned keep their previous value, like the statement MOVE-CORRESPONDING but unlike the operator CORRESPONDING without the addition BASE. Any nested internal tables are always resolved, as when the addition EXPANDING NESTED TABLES is specified in MOVE-CORRESPONDING or the addition DEEP for the operator CORRESPONDING. In assignments between internal tables, the target table is always initialized first. There is no matching addition for the addition KEEPING TARGET LINES in MOVE-CORRESPONDING or BASE in CORRESPONDING.

Source and target must not be the same otherwise the runtime error CORRESPONDING_SELF can occur. Incorrect parameters passed to the methods of the class CL_ABAP_CORRESPONDING raised exceptions of the class CX_CORR_DYN_ERROR.

Notes

◈ The system class CL_ABAP_CORRESPONDING implements an assignment similar to the statement
dest = CORRESPONDING  #( struct|itab MAPPING ... EXCEPT ... ).
Here, the mapping rule is specified dynamically, however, as the content of a special internal table.

◈ The same restrictions apply as in the operator CORRESPONDING. Components can only be mapped to each other if they are on the same level. Components in a substructure cannot be assigned to the components at higher levels, nor higher level components to components in a substructure.

◈ The class CL_ABAP_CORRESPONDING always resolves tabular components, which is the same behavior as the operator CORRESPONDING when a mapping rule is specified. In this case, the addition DEEP is also set implicitly.

◈ To achieve the same results for standalone in assignments between structures as in the operator CORRESPONDING without BASE, an initial structure can be assigned to the parameter destination.

Example

Uses the class CL_ABAP_CORRESPONDING for assignments of components to a simple structure. The mapping rule dictates that the components a3 are assigned to b1 and a1 to b3. The component a2 is ignored since there are no identically named components in the target structure and b2 keeps its value. a4 and a5 in the target structure also keep their values, however, even though the source structure contains identically named components. This is because the value of CL_ABAP_CORRESPONDING=>MAPPING_EXCEPT_ALL is specified for the mapping type for all non-specified components. The executable example for simple structures enables interactive input of the component names that are mapped to each other.

DATA:
  BEGIN OF struct1,
    a1 TYPE string VALUE 'a1',
    a2 TYPE string VALUE 'a2',
    a3 TYPE string VALUE 'a3',
    a4 TYPE string VALUE 'a4',
    a5 TYPE string VALUE 'a5',
  END OF struct1,
  BEGIN OF struct2,
    b1 TYPE string VALUE 'b1',
    b2 TYPE string VALUE 'b2',
    b3 TYPE string VALUE 'b3',
    a4 TYPE string VALUE 'b4',
    a5 TYPE string VALUE 'b5',
  END OF struct2.

DATA(mapper) =
  cl_abap_corresponding=>create(
    source            = struct1
    destination       = struct2
    mapping           = VALUE cl_abap_corresponding=>mapping_table(
      ( level = 0 kind = 1 srcname = 'a1' dstname = 'b3' )
      ( level = 0 kind = 1 srcname = 'a3' dstname = 'b1' )
      ( level = 0 kind = 3 ) ) ).

mapper->execute( EXPORTING source      = struct1
                 CHANGING  destination = struct2 ).

cl_demo_output=>display( struct2 ).

1.4.4 Assigning Components: Examples

► MOVE-CORRESPONDING for Structures

This example demonstrates the statement MOVE-CORRESPONDING for structures.

Source Code

REPORT demo_move_corresponding_struct.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.

  PRIVATE SECTION.
    TYPES:
      c3 TYPE c LENGTH 3,

      BEGIN OF iline1,
        col1 TYPE c3,
        col2 TYPE c3,
      END OF iline1,

      BEGIN OF iline2,
        col2 TYPE c3,
        col3 TYPE c3,
      END OF iline2,

      BEGIN OF line1,
        col1 TYPE                   c3,
        col2 TYPE                   c3,
        col3 TYPE STANDARD TABLE OF iline1 WITH EMPTY KEY,
      END OF line1,

      BEGIN OF line2,
        col2 TYPE                   c3,
        col3 TYPE STANDARD TABLE OF iline2 WITH EMPTY KEY,
        col4 TYPE                   c3,
      END OF line2.

    CLASS-DATA:
      struct1 TYPE        line1,
      struct2 TYPE        line2,

      out     TYPE REF TO if_demo_output.

    CLASS-METHODS:
      fill_structures,
      display_structure1,
      display_structure2.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    out = cl_demo_output=>new( ).
    fill_structures( ).
    out->begin_section( `struct1` ).
    display_structure1( ).
    out->next_section( `struct2` ).
    display_structure2( ).

    out->begin_section(
      `MOVE-CORRESPONDING` ).

    MOVE-CORRESPONDING struct1 TO struct2.

    display_structure2( ).
    out->next_section(
      `MOVE-CORRESPONDING EXPANDING NESTED TABLES` ).

    MOVE-CORRESPONDING struct1 TO struct2 EXPANDING NESTED TABLES.

    display_structure2( ).
    out->display( ).
  ENDMETHOD.
  METHOD fill_structures.
    struct1 = VALUE #(
       col1 = 'a1'
       col2 = 'a2'
       col3 = VALUE #( ( col1 = 'a11'  col2 = 'a12' )
                       ( col1 = 'a21'  col2 = 'a22' ) ) ).

    struct2 = VALUE #(
       col2 = 'x11'
       col3 = VALUE #( ( col2 = 'x11'  col3 = 'x12' )
                       ( col2 = 'x21'  col3 = 'x22' )
                       ( col2 = 'x31'  col3 = 'x32' ) )
       col4 = 'x12'  ).
  ENDMETHOD.
  METHOD display_structure1.
    DATA:
      BEGIN OF outl,
        col1  TYPE c3,
        col2  TYPE c3,
        col31 TYPE c3,
        col32 TYPE c3,
      END OF outl.
    DATA output LIKE STANDARD TABLE OF outl WITH EMPTY KEY.
    outl-col1 = struct1-col1.
    outl-col2 = struct1-col2.
    LOOP AT struct1-col3 ASSIGNING FIELD-SYMBOL(<col3>).
      outl-col31 = <col3>-col1.
      outl-col32 = <col3>-col2.
      IF sy-tabix > 1.
        CLEAR outl-col1.
        CLEAR outl-col2.
      ENDIF.
      APPEND outl TO output.
    ENDLOOP.
    out->write( output ).
  ENDMETHOD.
  METHOD display_structure2.
    DATA:
      BEGIN OF outl,
        col2  TYPE c3,
        col32 TYPE c3,
        col33 TYPE c3,
        col4  TYPE c3,
      END OF outl.
    DATA output LIKE STANDARD TABLE OF outl WITH EMPTY KEY.
    outl-col2 = struct2-col2.
    outl-col4 = struct2-col4.
    LOOP AT struct2-col3 ASSIGNING FIELD-SYMBOL(<col3>).
      outl-col32 = <col3>-col2.
      outl-col33 = <col3>-col3.
      IF sy-tabix > 1.
        CLEAR outl-col2.
        CLEAR outl-col4.
      ENDIF.
      APPEND outl TO output.
    ENDLOOP.
    out->write( output ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

MOVE-CORRESPONDING and the available additions are used to declare two structures, struct1 and struct2, and assign them to each other.

◈ struct1 contains two elementary components, col1 and col2, and a tabular component col3 with the components col1 and col2.
◈ struct2 contains two elementary components, col2 and col4, and a tabular component col3 with the components col2 and col3.

The structures are filled with values. For the output, the structured components are resolved to elementary components of an output table, output.

The statement MOVE-CORRESPONDING finds the identically named components col2 and col3 in struct1 and struct2 and the assignments work as follows:

◈ MOVE-CORRESPONDING

After the assignment, the components col2 and col3 in struct2 have the same content as in struct1. Component col4 keeps its value.

◈ MOVE-CORRESPONDING EXPANDING NESTED TABLES

After the assignment, the component col2 in struct2 has the same content as in struc1. col4 preserves its value. The tabular component col3 is resolved and the identically named component col2 found there. The original content of struct2-col3 is deleted. After the assignment, the column col2 has the same content as in struct1-col3, whereas the column col3 remains initial.

► MOVE-CORRESPONDING for Internal Tables

This example demonstrates the statement MOVE-CORRESPONDING for internal tables.

Source Code

REPORT demo_move_corresponding_itab.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.

  PRIVATE SECTION.
    TYPES:
      c3 TYPE c LENGTH 3,

      BEGIN OF iline1,
        col1 TYPE c3,
        col2 TYPE c3,
      END OF iline1,

      BEGIN OF iline2,
        col2 TYPE c3,
        col3 TYPE c3,
      END OF iline2,

      BEGIN OF line1,
        col1 TYPE                   c3,
        col2 TYPE                   c3,
        col3 TYPE STANDARD TABLE OF iline1 WITH EMPTY KEY,
      END OF line1,

      BEGIN OF line2,
        col2 TYPE                   c3,
        col3 TYPE STANDARD TABLE OF iline2 WITH EMPTY KEY,
        col4 TYPE                   c3,
      END OF line2.

    CLASS-DATA:
      itab1 TYPE STANDARD TABLE OF line1 WITH EMPTY KEY,
      itab2 TYPE STANDARD TABLE OF line2 WITH EMPTY KEY,

      out   TYPE REF TO            if_demo_output.

    CLASS-METHODS:
      fill_tables,
      display_table1,
      display_table2.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA buffer LIKE itab2.
    out = cl_demo_output=>new( ).
    fill_tables( ).
    out->begin_section( `itab1` ).
    display_table1( ).
    out->next_section( `itab2` ).
    display_table2( ).
    buffer = itab2.

    out->begin_section(
      `MOVE-CORRESPONDING` ).

    MOVE-CORRESPONDING itab1 TO itab2.

    display_table2( ).
    itab2 = buffer.
    out->next_section(
      `MOVE-CORRESPONDING KEEPING TARGET LINES` ).

    MOVE-CORRESPONDING itab1 TO itab2 KEEPING TARGET LINES.

    display_table2( ).
    itab2 = buffer.
    out->next_section(
      `MOVE-CORRESPONDING EXPANDING NESTED TABLES` ).

    MOVE-CORRESPONDING itab1 TO itab2 EXPANDING NESTED TABLES.

    display_table2( ).
    itab2 = buffer.
    out->next_section(
      `MOVE-CORRESPONDING EXPANDING NESTED TABLES ` &&
      `KEEPING TARGET LINES` ).

    MOVE-CORRESPONDING itab1 TO itab2 EXPANDING NESTED TABLES
                                      KEEPING TARGET LINES.

    display_table2( ).
    out->display( ).
  ENDMETHOD.
  METHOD fill_tables.
    itab1 = VALUE #(
      ( col1 = 'a11'
        col2 = 'a12'
        col3 = VALUE #( ( col1 = 'a11'  col2 = 'a12' )
                        ( col1 = 'a21'  col2 = 'a22' ) ) )
      ( col1 = 'b21'
        col2 = 'b22'
        col3 = VALUE #( ( col1 = 'b11'  col2 = 'b12' )
                        ( col1 = 'b21'  col2 = 'b22' ) ) )
      ( col1 = 'c31'
        col2 = 'c32'
        col3 = VALUE #( ( col1 = 'c11'  col2 = 'c12' )
                        ( col1 = 'c21'  col2 = 'c22' ) ) ) ).

    itab2 = VALUE #(
      ( col2 = 'x11'
        col3 = VALUE #( ( col2 = 'x11'  col3 = 'x12' )
                        ( col2 = 'x21'  col3 = 'x22' )
                        ( col2 = 'x31'  col3 = 'x32' ) )
        col4 = 'x12' )
      ( col2 = 'y21'
        col3 = VALUE #( ( col2 = 'y11'  col3 = 'y12' )
                        ( col2 = 'y21'  col3 = 'y22' )
                        ( col2 = 'y31'  col3 = 'y32' ) )
        col4 = 'y22' ) ).
  ENDMETHOD.
  METHOD display_table1.
    DATA:
      BEGIN OF outl,
        col1  TYPE c3,
        col2  TYPE c3,
        col31 TYPE c3,
        col32 TYPE c3,
      END OF outl.
    DATA output LIKE STANDARD TABLE OF outl WITH EMPTY KEY.
    LOOP AT itab1 ASSIGNING FIELD-SYMBOL(<wa>).
      outl-col1 = <wa>-col1.
      outl-col2 = <wa>-col2.
      LOOP AT <wa>-col3 ASSIGNING FIELD-SYMBOL(<col3>).
        outl-col31 = <col3>-col1.
        outl-col32 = <col3>-col2.
        IF sy-tabix > 1.
          CLEAR outl-col1.
          CLEAR outl-col2.
        ENDIF.
        APPEND outl TO output.
      ENDLOOP.
    ENDLOOP.
    out->write( output ).
  ENDMETHOD.
  METHOD display_table2.
    DATA:
      BEGIN OF outl,
        col2  TYPE c3,
        col32 TYPE c3,
        col33 TYPE c3,
        col4  TYPE c3,
      END OF outl.
    DATA output LIKE STANDARD TABLE OF outl WITH EMPTY KEY.
    LOOP AT itab2 ASSIGNING FIELD-SYMBOL(<wa>).
      outl-col2 = <wa>-col2.
      outl-col4 = <wa>-col4.
      LOOP AT <wa>-col3 ASSIGNING FIELD-SYMBOL(<col3>).
        outl-col32 = <col3>-col2.
        outl-col33 = <col3>-col3.
        IF sy-tabix > 1.
          CLEAR outl-col2.
          CLEAR outl-col4.
        ENDIF.
        APPEND outl TO output.
      ENDLOOP.
    ENDLOOP.
    out->write( output ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

MOVE-CORRESPONDING and the available additions are used to declare two internal tables, itab1 and itab2, and assign them to each other.

◈ itab1 contains two elementary components, col1 and col2, and a tabular component col3 with the components col1 and col2.
◈ itab2 contains two elementary components, col2 and col4, and a tabular component col3 with the components col2 and col3.

The internal tables are filled with values. For the output, the structured components are resolved to elementary components of an output table, output.

The statement MOVE-CORRESPONDING finds the identically named components col2 and col3 in itab1 and itab2 and the assignments work as followst:

◈ MOVE-CORRESPONDING

The original content of itab2 is deleted. After the assignment, the columns col2 and col3 have the same content as in itab1, whereas col4 remains initial.

◈ MOVE-CORRESPONDING KEEPING TARGET LINES

The original content of itab2 is preserved. Three new rows are added in which the columns col2 and col3 have the same content as in itab1, whereas col4 remains initial.

◈ MOVE-CORRESPONDING EXPANDING NESTED TABLES

The original content of itab2 is deleted. After the assignment, the column col2 has the same content as in itab1 and col4 remains initial. The tabular component col3 is resolved and the identically named component col2 found there. The original content of itab2-col3 is deleted. After the assignment, the column col2 has the same content as in itab1-col3, whereas the column col3 remains initial.

◈ MOVE-CORRESPONDING EXPANDING NESTED TABLES KEEPING TARGET LINES

The original content of itab2 is preserved. Three new rows are added, to which the same applies as to the results of the previous assignment.

► Component Operator for Structures

This example demonstrates the component operator for structures.

Source Code

REPORT demo_corresponding_struct.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.

  PRIVATE SECTION.
    TYPES:
      c3 TYPE c LENGTH 3,

      BEGIN OF iline1,
        col1 TYPE c3,
        col2 TYPE c3,
      END OF iline1,

      BEGIN OF iline2,
        col2 TYPE c3,
        col3 TYPE c3,
      END OF iline2,

      BEGIN OF line1,
        col1 TYPE                   c3,
        col2 TYPE                   c3,
        col3 TYPE STANDARD TABLE OF iline1 WITH EMPTY KEY,
      END OF line1,

      BEGIN OF line2,
        col2 TYPE                   c3,
        col3 TYPE STANDARD TABLE OF iline2 WITH EMPTY KEY,
        col4 TYPE                   c3,
      END OF line2.

    CLASS-DATA:
      struct1 TYPE        line1,
      struct2 TYPE        line2,

      out     TYPE REF TO if_demo_output.

    CLASS-METHODS:
      fill_structures,
      display_structure1,
      display_structure2.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    out = cl_demo_output=>new( ).
    fill_structures( ).
    out->begin_section( `struct1` ).
    display_structure1( ).
    out->next_section( `struct2` ).
    display_structure2( ).

    DATA(base) = struct2.

    out->begin_section(
      `CORRESPONDING struct2( struct1 )` ).

    struct2 = CORRESPONDING #( struct1 ).

    display_structure2( ).
    out->next_section(
      `CORRESPONDING struct2( BASE ( struct2 ) struct1 )` ).

    struct2 = CORRESPONDING #( BASE ( base ) struct1 ).

    display_structure2( ).
    out->next_section(
      `CORRESPONDING struct2( DEEP struct1 )` ).

    struct2 = CORRESPONDING #( DEEP struct1 ).

    display_structure2( ).
    out->next_section(
      `CORRESPONDING struct2( DEEP BASE ( struct2 ) struct1 )` ).

    struct2 = CORRESPONDING #( DEEP BASE ( base ) struct1 ).

    display_structure2( ).
    out->display( ).
  ENDMETHOD.
  METHOD fill_structures.
    struct1 = VALUE #(
       col1 = 'a1'
       col2 = 'a2'
       col3 = VALUE #( ( col1 = 'a11'  col2 = 'a12' )
                       ( col1 = 'a21'  col2 = 'a22' ) ) ).

    struct2 = VALUE #(
       col2 = 'x11'
       col3 = VALUE #( ( col2 = 'x11'  col3 = 'x12' )
                       ( col2 = 'x21'  col3 = 'x22' )
                       ( col2 = 'x31'  col3 = 'x32' ) )
       col4 = 'x12'  ).
  ENDMETHOD.
  METHOD display_structure1.
    DATA:
      BEGIN OF outl,
        col1  TYPE c3,
        col2  TYPE c3,
        col31 TYPE c3,
        col32 TYPE c3,
      END OF outl.
    DATA output LIKE STANDARD TABLE OF outl WITH EMPTY KEY.
    outl-col1 = struct1-col1.
    outl-col2 = struct1-col2.
    LOOP AT struct1-col3 ASSIGNING FIELD-SYMBOL(<col3>).
      outl-col31 = <col3>-col1.
      outl-col32 = <col3>-col2.
      IF sy-tabix > 1.
        CLEAR outl-col1.
        CLEAR outl-col2.
      ENDIF.
      APPEND outl TO output.
    ENDLOOP.
    out->write( output ).
  ENDMETHOD.
  METHOD display_structure2.
    DATA:
      BEGIN OF outl,
        col2  TYPE c3,
        col32 TYPE c3,
        col33 TYPE c3,
        col4  TYPE c3,
      END OF outl.
    DATA output LIKE STANDARD TABLE OF outl WITH EMPTY KEY.
    outl-col2 = struct2-col2.
    outl-col4 = struct2-col4.
    LOOP AT struct2-col3 ASSIGNING FIELD-SYMBOL(<col3>).
      outl-col32 = <col3>-col2.
      outl-col33 = <col3>-col3.
      IF sy-tabix > 1.
        CLEAR outl-col2.
        CLEAR outl-col4.
      ENDIF.
      APPEND outl TO output.
    ENDLOOP.
    out->write( output ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

This example uses the same structures as the example for MOVE-CORRESPONDING. Here, the source structure struct1 is used as a parameter of a constructor expression with the component operator CORRESPONDING and the result is assigned to the target structure struct2. The source structure struct1 is assigned to the result with the type of struct2, both with and without the addition DEEP. If the addition BASE is used, the result is given the original value of struct2 as its start value and otherwise stays initial. The assignment is made exactly as described in the example for MOVE-CORRESPONDING with or without EXPANDING NESTED TABLES. If DEEP is used the name comparison is made for the components of the substructure col3. This is why fewer assignments are made than when omitting DEEP. The uninvolved component col4 keeps its initial value without BASE being used. If BASE is used, it keeps the set value. The result is assigned to the target structure struct2.

The result of the component operator matches the result of MOVE-CORRESPONDING only if the addition BASE is used.

► Component Operator for Internal Tables

This example demonstrates the component operator for internal tables.

Source Code

REPORT demo_corresponding_itab.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.

  PRIVATE SECTION.
    TYPES:
      c3 TYPE c LENGTH 3,

      BEGIN OF iline1,
        col1 TYPE c3,
        col2 TYPE c3,
      END OF iline1,

      BEGIN OF iline2,
        col2 TYPE c3,
        col3 TYPE c3,
      END OF iline2,

      BEGIN OF line1,
        col1 TYPE                   c3,
        col2 TYPE                   c3,
        col3 TYPE STANDARD TABLE OF iline1 WITH EMPTY KEY,
      END OF line1,

      BEGIN OF line2,
        col2 TYPE                   c3,
        col3 TYPE STANDARD TABLE OF iline2 WITH EMPTY KEY,
        col4 TYPE                   c3,
      END OF line2.

    CLASS-DATA:
      itab1 TYPE STANDARD TABLE OF line1 WITH EMPTY KEY,
      itab2 TYPE STANDARD TABLE OF line2 WITH EMPTY KEY,

      out   TYPE REF TO            if_demo_output.

    CLASS-METHODS:
      fill_tables,
      display_table1,
      display_table2.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    out = cl_demo_output=>new( ).
    fill_tables( ).
    out->begin_section( `itab1` ).
    display_table1( ).
    out->next_section( `itab2` ).
    display_table2( ).

    DATA(base) = itab2.

    out->begin_section(
      `CORRESPONDING itab2( itab1 )` ).

    itab2 = CORRESPONDING #( itab1 ).

    display_table2( ).
    out->next_section(
      `CORRESPONDING itab2( BASE ( itab2 ) itab1 )` ).

    itab2 = CORRESPONDING #( BASE ( base ) itab1 ).

    display_table2( ).
    out->next_section(
      `CORRESPONDING itab2( DEEP itab1 )` ).

    itab2 = CORRESPONDING #( DEEP itab1 ).

    display_table2( ).
    out->next_section(
      `CORRESPONDING itab2( DEEP BASE ( itab2 ) itab1 )` ).

    itab2 = CORRESPONDING #( DEEP BASE ( base ) itab1 ).

    display_table2( ).
    out->display( ).
  ENDMETHOD.
  METHOD fill_tables.
    itab1 = VALUE #(
      ( col1 = 'a11'
        col2 = 'a12'
        col3 = VALUE #( ( col1 = 'a11'  col2 = 'a12' )
                        ( col1 = 'a21'  col2 = 'a22' ) ) )
      ( col1 = 'b21'
        col2 = 'b22'
        col3 = VALUE #( ( col1 = 'b11'  col2 = 'b12' )
                        ( col1 = 'b21'  col2 = 'b22' ) ) )
      ( col1 = 'c31'
        col2 = 'c32'
        col3 = VALUE #( ( col1 = 'c11'  col2 = 'c12' )
                        ( col1 = 'c21'  col2 = 'c22' ) ) ) ).

    itab2 = VALUE #(
      ( col2 = 'x11'
        col3 = VALUE #( ( col2 = 'x11'  col3 = 'x12' )
                        ( col2 = 'x21'  col3 = 'x22' )
                        ( col2 = 'x31'  col3 = 'x32' ) )
        col4 = 'x12' )
      ( col2 = 'y21'
        col3 = VALUE #( ( col2 = 'y11'  col3 = 'y12' )
                        ( col2 = 'y21'  col3 = 'y22' )
                        ( col2 = 'y31'  col3 = 'y32' ) )
        col4 = 'y22' ) ).
  ENDMETHOD.
  METHOD display_table1.
    DATA:
      BEGIN OF outl,
        col1  TYPE c3,
        col2  TYPE c3,
        col31 TYPE c3,
        col32 TYPE c3,
      END OF outl.
    DATA output LIKE STANDARD TABLE OF outl WITH EMPTY KEY.
    LOOP AT itab1 ASSIGNING FIELD-SYMBOL(<wa>).
      outl-col1 = <wa>-col1.
      outl-col2 = <wa>-col2.
      LOOP AT <wa>-col3 ASSIGNING FIELD-SYMBOL(<col3>).
        outl-col31 = <col3>-col1.
        outl-col32 = <col3>-col2.
        IF sy-tabix > 1.
          CLEAR outl-col1.
          CLEAR outl-col2.
        ENDIF.
        APPEND outl TO output.
      ENDLOOP.
    ENDLOOP.
    out->write( output ).
  ENDMETHOD.
  METHOD display_table2.
    DATA:
      BEGIN OF outl,
        col2  TYPE c3,
        col32 TYPE c3,
        col33 TYPE c3,
        col4  TYPE c3,
      END OF outl.
    DATA output LIKE STANDARD TABLE OF outl WITH EMPTY KEY.
    LOOP AT itab2 ASSIGNING FIELD-SYMBOL(<wa>).
      outl-col2 = <wa>-col2.
      outl-col4 = <wa>-col4.
      LOOP AT <wa>-col3 ASSIGNING FIELD-SYMBOL(<col3>).
        outl-col32 = <col3>-col2.
        outl-col33 = <col3>-col3.
        IF sy-tabix > 1.
          CLEAR outl-col2.
          CLEAR outl-col4.
        ENDIF.
        APPEND outl TO output.
      ENDLOOP.
    ENDLOOP.
    out->write( output ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

This example uses the same internal tables as the example for MOVE-CORRESPONDING. Here, the source table itab1 is used as a parameter of a constructor expression with the component operator CORRESPONDING and the result is assigned to the target table itab2. The source table itab1 is assigned to the result with the type of itab2, both with and without the addition DEEP. If the addition BASE is used, the result is given the original value of itab2 as its start value and otherwise stays initial. The assignment is made exactly as described in the example for MOVE-CORRESPONDING with or without EXPANDING NESTED TABLES. The component selector with the addition BASE has the same effect as using MOVE-CORRESPONDING with the addition KEEPING TARGET LINES. If DEEP is used the name comparison is made for the components of the substructure col3. This is why fewer assignments are made than when omitting DEEP.

The uninvolved component col4 keeps its initial value in the new rows in both examples. The result is assigned to the target table itab2. Unlike in the example for structures, the final results for MOVE-CORRESPONDING and the component selector CORRESPONDING are the same, since assignments are made to initial new rows in both cases.

► Component Operator for Table Expression

This example demonstrates the component operator for a table expression.

Source Code

REPORT demo_corresponding_table_exp.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS:
      main,
      class_constructor.
  PRIVATE SECTION.
    CLASS-DATA:
      itab TYPE HASHED TABLE OF spfli WITH UNIQUE KEY carrid connid,
      BEGIN OF struct,
        carrid   TYPE spfli-carrid,
        connid   TYPE spfli-connid,
        cityfrom TYPE spfli-cityfrom,
        cityto   TYPE spfli-cityto,
      END OF struct,
      carrid TYPE spfli-carrid VALUE 'LH',
      connid TYPE spfli-connid VALUE '0400'.
    CLASS-METHODS
      input.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    input( ).
    TRY.

        struct =
          CORRESPONDING #( itab[ carrid = carrid connid = connid ] ).

        cl_demo_output=>display( struct ).
      CATCH cx_sy_itab_line_not_found.
        cl_demo_output=>display( 'Nothing found' ).
    ENDTRY.
  ENDMETHOD.
  METHOD class_constructor.
    SELECT *
           FROM spfli
           INTO TABLE @itab.
  ENDMETHOD.
  METHOD input.
    cl_demo_input=>add_field( CHANGING field = carrid ).
    cl_demo_input=>add_field( CHANGING field = connid ).
    cl_demo_input=>request( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

This example shows a table expression that reads a row of an internal table (as the parameter of a constructor expression) with the component operator CORRESPONDING. After the assignment is complete, the components of the target structure struct contain the values of the corresponding column of the row read by the table expression.

► Component operator, lookup table

This example demonstrates the component operator with FROM ... USING.

Source Code

REPORT demo_corresponding_using.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS:
      main,
      class_constructor.
  PRIVATE SECTION.
    TYPES:
      BEGIN OF struct1,
        a1 TYPE string,
        a2 TYPE string,
        b1 TYPE string,
        b2 TYPE string,
        c1 TYPE string,
        c2 TYPE string,
      END OF struct1,
      BEGIN OF struct2,
        a1 TYPE string,
        a2 TYPE string,
        b1 TYPE string,
        b2 TYPE string,
        c1 TYPE string,
        d2 TYPE string,
      END OF struct2,
      BEGIN OF struct3,
        u1 TYPE string,
        u2 TYPE string,
        v1 TYPE string,
        v2 TYPE string,
        w1 TYPE string,
        w2 TYPE string,
      END OF struct3.
    CLASS-DATA:
      itab TYPE STANDARD TABLE OF struct1,
      lookup_tab TYPE STANDARD TABLE OF struct2
                 WITH NON-UNIQUE SORTED KEY mkey COMPONENTS b1 b2,
      jtab TYPE STANDARD TABLE OF struct3.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA(out) = cl_demo_output=>new( ).

    out->begin_section( `itab`
      )->write( itab ).

    out->next_section(
      `lookup_tab`
      )->write( lookup_tab ).

    itab = CORRESPONDING #(
              itab FROM lookup_tab
                   USING KEY mkey b1 = a1 b2 = a2 ).
    out->next_section(
      `itab = CORRESPONDING #( itab FROM lookup_tab USING ... )`
      )->write( itab ).

    itab = CORRESPONDING #(
              itab FROM lookup_tab
                   USING KEY mkey b1 = a1 b2 = a2
                   MAPPING a1 = a1 a2 = a2 b1 = b1 b2 = b2 c2 = d2 ).
    out->next_section(
    `itab = CORRESPONDING #( itab FROM lookup_tab ` &&
                                 `USING ... MAPPING ... )`
      )->write( itab ).

    jtab = CORRESPONDING #(
             itab FROM lookup_tab
                  USING KEY mkey b1 = a1 b2 = a2 ) ##operator.
    out->next_section(
      `jtab = CORRESPONDING #( itab FROM lookup_tab USING ... )`
      )->write( jtab ).

    out->display( ).
  ENDMETHOD.
  METHOD class_constructor.
    itab = VALUE #(
      ( a1 = `id1_1` a2 = `id2_1`
        b1 = `000`   b2 = `000`
        c1 = `000`   c2 = `000` )
      ( a1 = `id1_2` a2 = `id2_2`
        b1 = `000`   b2 = `000`
        c1 = `000`   c2 = `000` )
      ( a1 = `id1_3` a2 = `id2_3`
        b1 = `000`   b2 = `000`
        c1 = `000`   c2 = `000` ) ).
    lookup_tab = VALUE #(
      ( a1 = `a_11`  a2 = `a_12`
        b1 = `id1_1` b2 = `id2_1`
        c1 = `c_11`  d2 = `d_12` )
      ( a1 = `a_21`  a2 = `a_22`
        b1 = `id1_3` b2 = `id2_3`
        c1 = `c_21`  d2 = `d_22` ) ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

This example joins two internal tables itab and itab2 in a constructor expression with the component operator CORRESPONDING, which uses the variant with lookup table.

◈ The first assignment specifies the conditionb1 = a1 b2 = a2 without a mapping rule after USING. In the lookup_tab, one row is found for the first and third row of itab and its identically named components are assigned to the corresponding row of itab, where b1 and b2 from lookup_tab and a1 and a2 from itab are ignored here. The only identically named component is c1. All other components keep their preceding value from itab. The second row is taken unchanged from itab1.

◈ In the second assignment, the default handling by specifying a mapping rule a1 = a1 a2 = a2 b1 = b1 b2 = b2 c2 = d2 is overridden by specifying a mapping relationship for all components ignored until now. Accordingly, the content of all components in the first and third row of itab are modified.

◈ Finally, the third assignment still demonstrates the assignment of the expression to an internal table jtab that does not have the type of itab but is compatible with it. The full result of the expression is assigned to jtab. The pragma ##operator hides the syntax check warning stating that a temporary copy of itab needs to be created.

► Component Operator, Reflexive Assignment

This example demonstrates the component operator with FROM USING for a single table.

Source Code

REPORT demo_corresponding_using_self.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS:
      main,
      class_constructor.
  PRIVATE SECTION.
    TYPES:
      BEGIN OF struct,
        node   TYPE string,
        parent TYPE string,
        text   TYPE string,
      END OF struct.
    CLASS-DATA
      itab TYPE HASHED TABLE OF struct
           WITH UNIQUE KEY node.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA(out) = cl_demo_output=>new( ).

    out->begin_section( `itab`
      )->write( itab ).


    itab = CORRESPONDING #(
              itab FROM itab
                   USING node = parent ) ##operator.
    out->next_section(
      `itab = CORRESPONDING #( itab FROM itab USING node = parent )`
      )->write( itab ).

    out->display( ).
  ENDMETHOD.
  METHOD class_constructor.
    itab = VALUE #(
      ( node = `0` parent = `-`  text = `node0`  )
      ( node = `1` parent = `0`  text = `node1` )
      ( node = `2` parent = `1`  text = `node2` )
      ( node = `3` parent = `1`  text = `node3` )
      ( node = `4` parent = `3`  text = `node4` )
      ( node = `5` parent = `3`  text = `node5` )
      ( node = `6` parent = `1`  text = `node6` ) ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The example associates an internal table itab in a component expression using the component operator CORRESPONDING in the variant with lookup table with itself and assigns the result to the same table. This requires a temporary copy of the table to be created, which raises a warning from the syntax check. This warning can be hidden using the pragma ##operator.

The table contains a hierarchical tree structure in which nodes of the column node are assigned to a top node using the column parent. This relationship is specified after USING, which finds the top node for every row. The assignment of the identically named component text ensures that every row in this column is given the content of its direct top node.

► Component Operator, Mapping Rule

This example demonstrates the component operator with explicit mapping rules.

Source Code

REPORT demo_corresponding_mapping.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
  PRIVATE SECTION.
    CLASS-DATA:
      BEGIN OF struct1,
        a1 TYPE string VALUE `a1_XX`,
        a2 TYPE string VALUE `a2_XX`,
        a3 TYPE string VALUE `a3_XX`,
        a4 TYPE string VALUE `a4_XX`,
      END OF struct1,
      BEGIN OF struct2,
        a1 TYPE string VALUE `a1_YY`,
        a2 TYPE string VALUE `a2_YY`,
        b3 TYPE string VALUE `b3_YY`,
        b4 TYPE string VALUE `b4_YY`,
      END OF struct2.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA(out) = cl_demo_output=>new( ).

    out->begin_section( `struct1`
      )->write( struct1
      )->next_section( `struct2`
      )->write( struct2 ).

    struct2 = CORRESPONDING #( struct1 ).
    out->begin_section(
      `struct2 = CORRESPONDING #( struct1 )`
     )->write( struct2 ).

    struct2 = CORRESPONDING #( struct1 MAPPING b4 = a3 ).
    out->begin_section(
      `struct2 = CORRESPONDING #( struct1 MAPPING b4 = a3 )`
     )->write( struct2 ).

    struct2 = CORRESPONDING #( struct1 MAPPING b4 = a1 ).
    out->begin_section(
      `struct2 = CORRESPONDING #( struct1 MAPPING b4 = a1 )`
     )->write( struct2 ).

    struct2 = CORRESPONDING #( struct1 EXCEPT a1 ).
    out->begin_section(
      `struct2 = CORRESPONDING #( struct1 EXCEPT a1 )`
     )->write( struct2 ).

    struct2 = CORRESPONDING #( struct1  MAPPING b4 = a3 EXCEPT a1 ).
    out->begin_section(
      `struct2 = CORRESPONDING #( struct1 MAPPING b4 = a3 EXCEPT a1 )`
     )->write( struct2 ).

    struct2 = CORRESPONDING #( struct1  MAPPING b4 = a3 EXCEPT * ).
    out->begin_section(
      `struct2 = CORRESPONDING #( struct1 MAPPING b4 = a3 EXCEPT * )`
     )->write( struct2 ).

    struct2 = CORRESPONDING #( struct1 EXCEPT * ).
    out->begin_section(
      `struct2 = CORRESPONDING #( struct1 EXCEPT * )`
     )->write( struct2 ).

    out->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The example assigns the basic form of a constructor expression with the component operator CORRESPONDING with the parameter struct1 to a compatible structure struct2. Various mapping rules are demonstrated.

◈ If no mapping rule is used, the identically named components a1 and a2 are assigned and the other components remain initial.
◈ The mapping rule MAPPING b4 = a3 assigns the component a3 to the component b4 too.
◈ The mapping rule MAPPING b4 = a1 assigns the component a1 twice, to b4 and to the identically named component a1.
◈ The mapping rule EXCEPT a1 assigns only the component a2 to the identically named component, since the component a1 is excluded.
◈ Alongside the identical name assignment, the mapping rule MAPPING b4 = a3 EXCEPT a1 assigns the component a3 to the component b4 and a1 is excluded from the identical name assignment.
◈ The mapping rule EXCEPT * does not assign any components and all components of the result remain initial.

► Component Operator, Nested Mapping Rule

This example demonstrates the component operator with a nested mapping rule.

Source Code

REPORT demo_corresponding_deep_mapp.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS:
      main,
      class_constructor.
  PRIVATE SECTION.
    CLASS-DATA:
      BEGIN OF struct1,
        a1 TYPE string VALUE `a1_XX`,
        a2 TYPE string VALUE `a2_XX`,
        BEGIN OF istruct,
          a1 TYPE string VALUE `a1_YY`,
          a2 TYPE string VALUE `a2_YY`,
        END OF istruct,
        itab LIKE STANDARD TABLE OF struct1-istruct WITH EMPTY KEY,
      END OF struct1,
      BEGIN OF struct2,
        b1 TYPE string,
        a2 TYPE string,
        BEGIN OF istruct,
          b1 TYPE string,
          a2 TYPE string,
        END OF istruct,
        jtab LIKE STANDARD TABLE OF struct2-istruct WITH EMPTY KEY,
      END OF struct2.
    TYPES:
      BEGIN OF out1,
        a1 LIKE struct1-a1,
        a2 LIKE struct1-a2,
      END OF out1,
      BEGIN OF out2,
        a1 LIKE struct2-b1,
        a2 LIKE struct2-a2,
      END OF out2.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA(out) = cl_demo_output=>new( ).

    out->begin_section( `struct1`
      )->write( CORRESPONDING out1( struct1 )
      )->write( struct1-istruct
      )->write( struct1-itab ).

    struct2 = CORRESPONDING #( struct1 ).
    out->next_section(
      `struct2 = CORRESPONDING #( struct1 )`
      )->write( CORRESPONDING out2( struct2 )
      )->write( struct2-istruct
      )->write( struct2-jtab ).

    struct2 = CORRESPONDING #( struct1 MAPPING jtab = itab ).
    out->next_section(
      `struct2 = CORRESPONDING #( struct1 MAPPING jtab = itab )`
      )->write( CORRESPONDING out2( struct2 )
      )->write( struct2-istruct
      )->write( struct2-jtab ).

    struct2 = CORRESPONDING #( struct1 MAPPING
               ( istruct = istruct MAPPING b1 = a1 EXCEPT a2 )
               ( jtab = itab MAPPING b1 = a1 ) ).
    out->next_section(
      `struct2 = CORRESPONDING #( struct1  MAPPING ( ... ) )`
      )->write( CORRESPONDING out2( struct2 )
      )->write( struct2-istruct
      )->write( struct2-jtab ).

    out->display( ).
  ENDMETHOD.
  METHOD class_constructor.
    struct1-itab = VALUE #(
      ( a1 = `a1_xx` a2 = `a2_xx` )
      ( a1 = `a1_yy` a2 = `a2_yy` ) ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The example assigns the basic form of a constructor expression with the component operator CORRESPONDING with the parameter struct1 to a compatible structure struct2. Various mapping rules are demonstrated. The structures contain a substructure and a tabular component.

◈ If there is not mapping rule, the identically named components a2 and istruct are assigned at the top level. The substructure istruct is resolved and the component a2 is assigned here. The tabular components are not identically named and are not assigned.

◈ The mapping rule MAPPING jtab = itab is used to assign the tabular component too. Here, only the identically named column a2 is respected.
◈ The nested mapping rule

MAPPING ( istruct = istruct MAPPING b1 = a1 EXCEPT a2 )
        ( jtab = itab MAPPING b1 = a1 )

is used to

◈ specify a mapping relationship for the components of the substructure istruct. Here, istruct = istruct must be specified at the top level.
◈ define a mapping for the tabular components. Here, a mapping is also nested for their columns.

In the result, all components are filled except the component a2 of the substructure, excluded using EXCEPT.

► Component Operator, Comparison with FOR Expression

This example compares the component operator with table comprehensions,

Source Code

REPORT demo_corresponding_vs_for.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS:
      main,
      class_constructor.
  PRIVATE SECTION.
    TYPES:
      BEGIN OF struct,
        carrier     TYPE spfli-carrid,
        connection  TYPE spfli-connid,
        departure   TYPE spfli-cityfrom,
        destination TYPE spfli-cityto,
      END OF struct.
    CLASS-DATA:
      itab    TYPE HASHED TABLE OF spfli
              WITH UNIQUE KEY carrid connid,
      result1 TYPE HASHED TABLE OF struct
              WITH UNIQUE KEY carrier connection,
      result2 TYPE HASHED TABLE OF struct
              WITH UNIQUE KEY carrier connection,
      result3 TYPE HASHED TABLE OF struct
              WITH UNIQUE KEY carrier connection,
      in      TYPE REF TO if_demo_input,
      out     TYPE REF TO if_demo_output.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA(iterations) = 10.
    in->request( CHANGING field = iterations ).

    DO iterations TIMES.

      DATA t1 TYPE i.
      GET RUN TIME FIELD DATA(t11).
      result1 = CORRESPONDING #(
                 itab MAPPING carrier     = carrid
                              connection  = connid
                              departure   = cityfrom
                              destination = cityto ).
      GET RUN TIME FIELD DATA(t12).
      t1 = t1 + t12 - t11.

      DATA t2 TYPE i.
      GET RUN TIME FIELD DATA(t21).
      result2 = VALUE #( FOR wa IN itab ( carrier     = wa-carrid
                                          connection  = wa-connid
                                          departure   = wa-cityfrom
                                          destination = wa-cityto ) ).
      GET RUN TIME FIELD DATA(t22).
      t2 = t2 + t22 - t21.

      DATA t3 TYPE i.
      GET RUN TIME FIELD DATA(t31).
      result3 = VALUE #( FOR wa IN itab (
                           CORRESPONDING #(
                             wa MAPPING carrier     = carrid
                                        connection  = connid
                                        departure   = cityfrom
                                        destination = cityto ) ) ).
      GET RUN TIME FIELD DATA(t32).
      t3 = t3 + t32 - t31.

    ENDDO.

    IF result1 = result2 AND result1 = result3.
      out->write(
       |CORRESPONDING:     {
         CONV decfloat16( t1 / iterations )
              WIDTH = 10 ALIGN = RIGHT } Microseconds\n| &&
       |FOR:               {
         CONV decfloat16( t2 / iterations )
              WIDTH = 10 ALIGN = RIGHT } Microseconds\n| &&
       |FOR CORRESPONDING: {
         CONV decfloat16( t3 / iterations )
              WIDTH = 10 ALIGN = RIGHT } Microseconds\n|
      )->line(
      )->display( result1 ).
    ELSE.
      out->display( `What?` ).
    ENDIF.

  ENDMETHOD.
  METHOD class_constructor.
    in  = cl_demo_input=>new( ).
    out = cl_demo_output=>new( ).
    SELECT *
           FROM spfli
           INTO TABLE @itab.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The columns of an internal table can be assigned to columns with different names in a different table in three different ways:

◈ Constructor expression with component operator CORRESPONDING and mapping rule

◈ Explicit assignment of the individual components behind a FOR expression of a table comprehension.

◈ FOR expression with evaluation of a constructor expression with component operator for each row.

Each method provides the same results. The component operator is quicker than the table comprehension when there are more than approximately 10 iterations. Using the component operator for each row is always the slowest solution.

► CL_ABAP_CORRESPONDING for Simple Structures

This example demonstrates the system class CL_ABAP_CORRESPONDING for simple structures.

Source Code

REPORT demo_corresponding_class_dyn.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    TYPES:
      BEGIN OF names,
        n1 TYPE c LENGTH 2,
        n2 TYPE c LENGTH 2,
        n3 TYPE c LENGTH 2,
      END OF names.

    DATA:
      BEGIN OF struct1,
        a1 TYPE string VALUE 'a1',
        a2 TYPE string VALUE 'a2',
        a3 TYPE string VALUE 'a3',
      END OF struct1,
      BEGIN OF struct2,
        b1 TYPE string VALUE 'b1',
        b2 TYPE string VALUE 'b2',
        b3 TYPE string VALUE 'b3',
      END OF struct2.

    DATA(src) = VALUE names( n1 = 'a1' n2 = 'a2' n3 = 'a3').
    DATA(dst) = VALUE names( n1 = 'b3' n2 = 'b2' n3 = 'b1').

    cl_demo_input=>new(
      )->add_field( CHANGING field = src-n1
      )->add_field( CHANGING field = dst-n1
      )->add_line(
      )->add_field( CHANGING field = src-n2
      )->add_field( CHANGING field = dst-n2
      )->add_line(
      )->add_field( CHANGING field = src-n3
      )->add_field( CHANGING field = dst-n3
      )->request( ).

    TRY.
        DATA(mapper) =
          cl_abap_corresponding=>create(
            source      = struct1
            destination = struct2
            mapping     = VALUE cl_abap_corresponding=>mapping_table(
              ( level = 0 kind = 1 srcname = src-n1 dstname = dst-n1 )
              ( level = 0 kind = 1 srcname = src-n2 dstname = dst-n2 )
              ( level = 0 kind = 1 srcname = src-n3 dstname = dst-n3 )
            ) ).

        mapper->execute( EXPORTING source      = struct1
                         CHANGING  destination = struct2 ).
      CATCH cx_corr_dyn_error INTO DATA(exc).
        cl_demo_output=>display( exc->get_text( ) ).
    ENDTRY.

    cl_demo_output=>display( struct2 ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The components of the structure struct1 are assigned to the structure struct2 using the system class CL_ABAP_CORRESPONDING. User input can specify which components are mapped to each other.

► CL_ABAP_CORRESPONDING for Nested Structures

This example demonstrates the system class CL_ABAP_CORRESPONDING for nested structures.

Source Code

REPORT demo_corresponding_class_str.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA:
      BEGIN OF struct1,
        a1 TYPE string VALUE 'a1',
        a2 TYPE string VALUE 'a2',
        a3 TYPE string VALUE 'a3',
        a4 TYPE string VALUE 'a4',
        a5 TYPE string VALUE 'a5',
        BEGIN OF asub1,
          s1_a1 TYPE string VALUE 's1_a1',
          s1_a2 TYPE string VALUE 's1_a2',
          s1_a3 TYPE string VALUE 's1_a3',
          BEGIN OF asub2,
            s2_a1 TYPE string VALUE 's2_a1',
            s2_a2 TYPE string VALUE 's2_a2',
            s2_a3 TYPE string VALUE 's2_a3',
          END OF asub2,
        END OF asub1,
      END OF struct1,
      BEGIN OF struct2,
        b1 TYPE string VALUE 'b1',
        b2 TYPE string VALUE 'b2',
        b3 TYPE string VALUE 'b3',
        a4 TYPE string VALUE 'b4',
        a5 TYPE string VALUE 'b5',
        BEGIN OF bsub1,
          s1_b1 TYPE string VALUE 's1_b1',
          s1_b2 TYPE string VALUE 's1_b2',
          s1_b3 TYPE string VALUE 's1_b3',
          BEGIN OF bsub2,
            s2_b1 TYPE string VALUE 's2_b1',
            s2_b2 TYPE string VALUE 's2_b2',
            s2_b3 TYPE string VALUE 's2_b3',
          END OF bsub2,
        END OF bsub1,
      END OF struct2.

    DATA(mapping_tab) = VALUE cl_abap_corresponding=>mapping_table(
    ( level = 0 kind = 1 srcname = 'a1' dstname = 'b3' )
    ( level = 0 kind = 1 srcname = 'a3' dstname = 'b1' )
    ( level = 0 kind = 2 srcname = 'a5' )
    ( level = 0 kind = 1 srcname = 'asub1' dstname = 'bsub1' )
    ( level = 1 kind = 1 srcname = 's1_a1' dstname = 's1_b3' )
    ( level = 1 kind = 1 srcname = 's1_a3' dstname = 's1_b1' )
    ( level = 1 kind = 1 srcname = 'asub2' dstname = 'bsub2' )
    ( level = 2 kind = 1 srcname = 's2_a1' dstname = 's2_b3' )
    ( level = 2 kind = 1 srcname = 's2_a3' dstname = 's2_b1' ) ).

    cl_abap_corresponding=>create(
      source            = struct1
      destination       = struct2
      mapping           = mapping_tab
      )->execute( EXPORTING source      = struct1
                  CHANGING  destination = struct2 ).

    cl_demo_output=>new(
      )->write( struct2-b1
      )->write( struct2-b2
      )->write( struct2-b3
      )->write( struct2-a4
      )->write( struct2-a5
      )->write( struct2-bsub1-s1_b1
      )->write( struct2-bsub1-s1_b2
      )->write( struct2-bsub1-s1_b3
      )->write( struct2-bsub1-bsub2-s2_b1
      )->write( struct2-bsub1-bsub2-s2_b2
      )->write( struct2-bsub1-bsub2-s2_b3
      )->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The components of the structure struct1 are assigned to the structure struct2 using the system class CL_ABAP_CORRESPONDING.
  • At the top level:
    • a1 is mapped to b3 and a3 is mapped to b1.
    • a2 to b2 are ignored, since their names do not match and b2 keeps their value.
    • The component a4 appears in both structures and is assigned using matching names.
    • a5 also appears in both structures but is excluded explicitly by the mapping type.
    • The substructure asub1 is mapped to the substructure bsub1.
  • At the level of the first substructure:
    • s1_a1 is mapped to s1_b3 and s1_a3 is mapped to s1_b1.
    • s1_a2 to s1_b2 are ignored, since their names do not match and s1_b2 keeps their value.
    • The substructure asub2 is mapped to the substructure bsub2.
  • At the level of the second substructure:
    • s2_a1 is mapped to s2_b3 and s2_a3 is mapped to s2_b1.
    • s2_a2 to s2_b2 are ignored, since their names do not match and s2_b2 keeps their value.
► CL_ABAP_CORRESPONDING for Internal Tables

This example demonstrates the system class CL_ABAP_CORRESPONDING for internal tables.

Source Code

REPORT demo_corresponding_class_tb.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    TYPES: BEGIN OF line1,
             col1 TYPE i,
             col2 TYPE i,
           END OF line1,
           BEGIN OF line2,
             col2 TYPE i,
             col3 TYPE i,
           END OF line2.

    DATA: itab1 TYPE TABLE OF line1 WITH EMPTY KEY,
          itab2 TYPE TABLE OF line2 WITH EMPTY KEY.

    DATA(out) = cl_demo_output=>new( ).

    itab1 = VALUE #(
      ( col1 = 11 col2 = 12 )
      ( col1 = 21 col2 = 22 ) ).

    itab2 = VALUE #(
      ( col2 = 212 col3 = 312 )
      ( col2 = 222 col3 = 322 ) ).

   cl_abap_corresponding=>create(
      source            = itab1
      destination       = itab2
      mapping           = VALUE cl_abap_corresponding=>mapping_table(  )
      )->execute( EXPORTING source      = itab1
                  CHANGING  destination = itab2 ).
    out->write( itab2 ).

    cl_abap_corresponding=>create(
      source            = itab1
      destination       = itab2
      mapping           = VALUE cl_abap_corresponding=>mapping_table(
       ( level = 0 kind = 1 srcname = 'col1' dstname = 'col2' )
       ( level = 0 kind = 1 srcname = 'col2' dstname = 'col3' ) )
      )->execute( EXPORTING source      = itab1
                  CHANGING  destination = itab2 ).
    out->write( itab2 ).

    out->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The components of the internal table itab1 are assigned to the internal table itab2 using the system class CL_ABAP_CORRESPONDING.

◈ The mapping table is empty in the first assignment. The assignment is made as in MOVE-CORRESPONDING using matching names and the column col3 of the target table is not filled.
◈ In the second assignment, a mapping table is passed that assigns all columns of the source table to a column in the target table regardless of their names.

► CL_ABAP_CORRESPONDING for Tabular Components

This example demonstrates the system class CL_ABAP_CORRESPONDING for tabular components of structures.

Source Code

REPORT demo_corresponding_class_strtb.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    TYPES: BEGIN OF line1,
             col1 TYPE i,
             col2 TYPE i,
           END OF line1,
           BEGIN OF line2,
             col2 TYPE i,
             col3 TYPE i,
           END OF line2.

    DATA: BEGIN OF struc1,
            itab TYPE TABLE OF line1 WITH EMPTY KEY,
          END OF struc1.

    DATA: BEGIN OF struc2,
            itab TYPE TABLE OF line2 WITH EMPTY KEY,
          END OF struc2.

    data(out) = cl_demo_output=>new( ).

    struc1-itab = VALUE #(
      ( col1 = 11 col2 = 12 )
      ( col1 = 21 col2 = 22 ) ).

    MOVE-CORRESPONDING struc1 TO struc2.
    out->write( struc2-itab ).

    CLEAR struc2-itab.
    MOVE-CORRESPONDING struc1 TO struc2 EXPANDING NESTED TABLES.
    out->write( struc2-itab ).

    CLEAR struc2-itab.
    struc2 = CORRESPONDING #( struc1 ).
    out->write( struc2-itab ).

    CLEAR struc2-itab.
    struc2 = CORRESPONDING #( DEEP struc1 ).
    out->write( struc2-itab ).

    CLEAR struc2-itab.
    cl_abap_corresponding=>create(
      source            = struc1
      destination       = struc2
      mapping           = VALUE cl_abap_corresponding=>mapping_table(  )
      )->execute( EXPORTING source      = struc1
                  CHANGING  destination = struc2 ).
    out->write( struc2-itab ).

    out->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The example compares the options for component-by-component assignments of structures with tabular components.

◈ The statement MOVE-CORRESPONDING can be executed both with and without the addition EXPANDING NESTED TABLES. Depending on the option chosen, the tabular component is assigned in full or by component (using matching names).

◈ The operator CORRESPONDING can be executed both with and without the addition DEEP. Depending on the option chosen, the tabular component is assigned in full or by component (using matching names).

◈ The class CL_ABAP_CORRESPONDING always resolves tabular components.

► Reflexive Component Assignments

This example demonstrates assignments between components of a structure.

Source Code

REPORT demo_corresponding_reflexive.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    TYPES: BEGIN OF t_str1,
             a1 TYPE i,
             a2 TYPE i,
             a3 TYPE i,
             a4 TYPE i,
           END OF t_str1.

    TYPES: BEGIN OF t_str2,
             b1 TYPE i,
             b2 TYPE i,
             a1 TYPE i,
             a2 TYPE i,
           END OF t_str2.

    TYPES: BEGIN OF t_str3,
             a4 TYPE i,
             a3 TYPE i,
             a1 TYPE i,
             a2 TYPE i,
           END OF t_str3.

    DATA(out) = cl_demo_output=>new( ).

    out->begin_section( 'Reflexive Component Assignments' ).

    DATA(str1) = VALUE t_str1( a1 = 1 a2 = 2 a3 = 3 a4 = 4 ).
    DATA(back) = str1.
    out->write( str1 ).

    out->begin_section( 'MOVE-CORRESPONDING' ).

    FIELD-SYMBOLS <fs2> TYPE t_str2.
    ASSIGN str1 TO <fs2> CASTING.
    MOVE-CORRESPONDING str1 TO <fs2>.
    out->write( str1 ).

    str1 = back.
    ASSIGN str1 TO <fs2> CASTING.
    MOVE-CORRESPONDING <fs2> TO str1.
    out->write( str1 ).

    str1 = back.
    FIELD-SYMBOLS <fs3> TYPE t_str3.
    ASSIGN str1 TO <fs3> CASTING.
    MOVE-CORRESPONDING str1 TO <fs3>.
    out->write( str1 ).

    str1 = back.
    ASSIGN str1 TO <fs3> CASTING.
    MOVE-CORRESPONDING <fs3> TO str1.
    out->write( str1 ).

    out->next_section( 'CORRESPONDING' ).

    str1 = back.
    str1 = CORRESPONDING #(
             str1 MAPPING a1 = a4 a2 = a3 a3 = a1 a4 = a2 ) ##operator.
    out->write( str1 ).

    str1 = back.
    str1 = CORRESPONDING #(
             str1 MAPPING a1 = a3 a2 = a4 a3 = a2 a4 = a1 ) ##operator.
    out->write( str1 ).

    out->next_section( 'CL_ABAP_CORRESPONDING' ).

     "Runtime error CORRESPONDING_SELF
*    str1 = back.
*    cl_abap_corresponding=>create(
*        source      = str1
*        destination = str1
*        mapping     = VALUE cl_abap_corresponding=>mapping_table(
*          ( level = 0 kind = 1 srcname = 'a4' dstname = 'a1' )
*          ( level = 0 kind = 1 srcname = 'a3' dstname = 'a2' )
*          ( level = 0 kind = 1 srcname = 'a1' dstname = 'a3' )
*          ( level = 0 kind = 1 srcname = 'a2' dstname = 'a4' ) )
*        )->execute( EXPORTING source      = str1
*                    CHANGING  destination = str1 ).
*    out->write( str1 ).
*
*    str1 = back.
*    cl_abap_corresponding=>create(
*        source      = str1
*        destination = str1
*        mapping     = VALUE cl_abap_corresponding=>mapping_table(
*          ( level = 0 kind = 1 srcname = 'a3' dstname = 'a1' )
*          ( level = 0 kind = 1 srcname = 'a4' dstname = 'a2' )
*          ( level = 0 kind = 1 srcname = 'a2' dstname = 'a3' )
*          ( level = 0 kind = 1 srcname = 'a1' dstname = 'a4' ) )
*        )->execute( EXPORTING source      = str1
*                    CHANGING  destination = str1 ).
*    out->write( str1 ).

    out->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The example summarizes various ways of making assignments of components in the same structure:
  • In the case of the statement, MOVE-CORRESPONDING this can be done using castings with differently typed field symbols:
    • There is no overlapping of source components and target components between the type t_str1 of the structure str1 and the type t_str2 of the field symbol fs2. In assignments of the structure to the field symbol that points to the structure, the content of the columns a1 and a2 is copied to the columns a3 and a4. This is because these columns have the names a1 and a2 in the field symbol. In assignments from the field symbol to the structure, however, the content of the columns a3 and a4 is copied to the columns a1 and a2.
    • Identically named components overlap between the type t_str1 of the structure str1 and the type t_str3 of the field symbol fs3. In assignments of the structure to the field symbol that points to the structure, the components are evaluated in an undefined order and the previously assigned current content of the components is used. Assignments from the field symbol to the structure produce yet another result.
  • If performed at all, reflexive assignments between the components of a structure using MOVE-CORRESPONDING should be free of overlaps.
  • In the case of the basic form of the constructor operator CORRESPONDING, a temporary copy of the structure is created as a target object and and assignment is made to this copy. The two mapping rules reflect the expectations of the two preceding MOVE-CORRESPONDING statements and produce the expected result.
  • The system class CL_ABAP_CORRESPONDING cannot be used to make assignments between components in the same structure.

1.5 Assigning References


This section discusses the assignments and special statements that set the references (or pointers) used in data references and field symbols.

◈ Setting Reference Variables
◈ Setting Field Symbols

When using field symbols and data references that contain references to data objects, the following should be noted:

In a regular assignment between field symbols, as occurs in an assignment of data objects, each field symbol is handled as a dereferenced pointer (value semantics).
In a regular assignment between data references, as occurs in an up cast or down cast, the references are copied (reference semantics). The corresponding rules can be found in the assignment rules for reference variables.

Note

For the dynamic data objects (strings, internal tables, and boxed components) which are also managed internally using references, value semantics apply. This means that the internal management is not visible for the developers.

1.5.1 Setting Reference Variables

Special rules apply when setting the reference or pointer of a reference variable:

◈ Assignment Rules for Reference Variables

A reference is always set as an up cast or as a down cast. When a reference variable is assigned to another reference variable, two different assignment operators are used plus a constructor operator.

◈ =, ?= - Up Cast and Down Cast
◈ CAST - Casting Operator

References in data reference variables can point to existing data objects. Here, a statement and a constructor operator are used.

◈ GET REFERENCE
◈ REF - Reference Operator

Like all data objects, reference variables are initialized using the statement CLEAR. The content of an initial reference variable is the null reference, which does not point to an object and can be assigned to every reference variable. 

Notes

◈ The same rules apply to references set when objects are created as to assignments.
◈ When a reference variable containing a heap reference is initialized, this affects Garbage Collector.

⬕ Assignment Rules for Reference Variables

The content of a reference variable can only be assigned to another reference variable. At the same time,

- data references can only be assigned to data reference variables and
- object references can only be assigned to object reference variables

. No conversion takes place when variables are assigned. For an assignment to take place, the static type of the target reference variable must be more general than or match the dynamic type of the source reference variable. If the assignment is successful, the target reference variable points to the same object as the source reference variable, meaning that the target reference variable inherits the dynamic type of the source reference variable.

- Static Type and Dynamic Type
- Up Cast and Down Cast
- Up Cast
    - Down Cast

Note 

Reference variables can point to objects in the internal session or in the shared objects memory. An area handle is used to bind an area instance version to a program, however references from the program can be made to shared objects and vice versa (if these are read or write references). In other cases, an area instance version is closed, which means that no references can point to the internal session from an area instance version. References that point from the internal session to shared objects can be saved, but not dereferenced. Object references and data references are possible within a completed area instance version, although data references are subject to restrictions with respect to the dynamic type.

Static Type and Dynamic Type

Each reference variable has a dynamic type and a static type.

◈ The dynamic type is defined at runtime of the program, and is the data type of the data object or the class of the object to which the reference variable points. It determines the components contained in the object.

◈ The static type is set with the declaration of the reference variable. In data references, the static type is either a non-generic data type or the predefined generic type data. In object references, the static type is either a class or an interface, so an object reference can also be referred to as a class reference or an interface reference.

The static type of a reference variable is always less specific or the same as the dynamic type.

Note

An initial reference variable contains the null reference, which does not point to any objects. It therefore does not have a data type or a class as a dynamic type. The same applies to a non-initial reference variable containing an invalid reference that no longer points to an object.

Example

The object reference variable oref has the static type super_class. After the object is generated with NEW, its dynamic type is the special class sub_class - as shown by RTTI.

CLASS super_class DEFINITION.
ENDCLASS.

CLASS sub_class DEFINITION INHERITING FROM super_class.
ENDCLASS.

DATA oref TYPE REF TO super_class.

START-OF-SELECTION.
  oref = NEW sub_class( ).

  cl_demo_output=>display( CAST cl_abap_classdescr(
    cl_abap_refdescr=>describe_by_object_ref( oref ) )->absolute_name ).

Up Cast and Down Cast

In an assignment between reference variables, the target variable applies the dynamic type of the source variable. An assignment is possible if the static type of the target variable is less specific or the same as the dynamic type of the source variables.

Up Cast

If the static type of the target variables is less specific or the same as the static type of the source variable, assignment is always possible. The name up cast arises from the fact that the movement within the inheritance space is upwards. Since the target variable can accept more dynamic types in comparison to the source variables, this assignment is also known as a widening cast. An up cast is possible in all ABAP statements in which the content of a data object is assigned to another data object. This includes, for example, assignments with the normal assignment operator (=), the insertion of rows in internal tables, or passes from actual to formal parameters.

Explicit castings using the casting operator ?= are also possible, but not usually necessary. Using the casting operators CAST for an up cast in combination with inline declarations, however, can be a good way of providing a reference variable with a more general type.

Example

See the previous example. The assignment oref = NEW sub_class( ) is an up cast, because it is followed by an reference variable of the static type of a superclass that references an object of a subclass.

Down Cast

If the static type of the target variable is more specific than the static type of the source variable, a check must be made at runtime (before the assignment is executed) to see whether it is less specific or the same as the dynamic type of the source variable. The name down cast arises from the fact that the movement in the inheritance space is downwards. Since the target variable can accept fewer dynamic types in comparison to the source variable, this assignment is also known as a narrowing cast. A down cast must always be performed explicitly. The following options are available:

◈ Special assignment with the casting operator ?=.
◈ Using a constructor expression with the casting operator CAST.
◈ Using the addition INTO of the statement WHEN TYPE in a case distinction with CASE TYPE OF for object reference variables.

If this prerequisite is not met, a handleable exception is raised and the reference variable keeps its original value after the assignment.

Notes

◈ For non-initial source variables, the predicate expression IS INSTANCE OF or the case distinction CASE TYPE OF can be used to check whether a down cast can be applied to specific classes or interfaces.
◈ The dynamic type of an initial or invalid source variable is undefined. In a down cast, the null reference or an invalid reference that does not point to an object can be assigned to every target variable that can be specified here.
◈ When a subclass is instantiated, an associated object reference variable cannot be cast to the subclass until the instance constructors of all superclasses have been executed. When an instance constructor is executed, the dynamic type of the object reference variable is the current superclass.

Example

The following example shows the three ways of performing a down cast for object reference variables.

◈ If the casting operator ?= is used, the exception CX_SY_MOVE_CAST_ERROR is caught if a down cast is not possible.
◈ If the casting operator CAST is used, the predicate expression IS INSTANCE OF is used in advance to check whether the down cast is possible.
◈ The use of the addition INTO of the statement WHEN TYPE in a case distinction with CASE TYPE OF combines the check and the down cast.

In each case, oref is assigned to the class reference variable that is reached first. Of course, a IS INSTANCE OF check would also be possible before the use of the casting operator ?=.

CLASS c1 DEFINITION.
  PUBLIC SECTION.
    DATA a1 TYPE string VALUE `C1`.
ENDCLASS.
CLASS c2 DEFINITION INHERITING FROM c1.
  PUBLIC SECTION.
    DATA a2 TYPE string VALUE `C2`.
ENDCLASS.
CLASS c3 DEFINITION INHERITING FROM c2.
  PUBLIC SECTION.
    DATA a3 TYPE string VALUE `C3`.
ENDCLASS.

DATA(o) = cl_demo_output=>new( ).

DATA oref TYPE REF TO object.

oref = NEW c1( ).
DATA:
  cref11 TYPE REF TO c1,
  cref12 TYPE REF TO c2,
  cref13 TYPE REF TO c3.
TRY.
    cref13 ?= oref.
    o->write( cref13->a3 ).
  CATCH cx_sy_move_cast_error.
    TRY.
        cref12 ?= oref.
        o->write( cref12->a2 ).
      CATCH cx_sy_move_cast_error.
        TRY.
            cref11 ?= oref.
            o->write( cref11->a1 ).
          CATCH cx_sy_move_cast_error.
        ENDTRY.
    ENDTRY.
ENDTRY.

oref = NEW c2( ).
IF oref IS INSTANCE OF c3.
  DATA(cref23) = CAST c3( oref ).
  o->write( cref23->a3 ).
ELSEIF oref IS INSTANCE OF c2.
  DATA(cref22) = CAST c2( oref ).
  o->write( cref22->a2 ).
ELSEIF oref IS INSTANCE OF c1.
  DATA(cref21) = CAST c1( oref ).
  o->write( cref21->a1 ).
ENDIF.

oref = NEW c3( ).
CASE TYPE OF oref.
  WHEN TYPE c3 INTO DATA(cref33).
    o->write( cref33->a3 ).
  WHEN TYPE c2 INTO DATA(cref32).
    o->write( cref32->a2 ).
  WHEN TYPE c1 INTO DATA(cref31).
    o->write( cref31->a1 ).
ENDCASE.

o->display( ).

- Assignments Between Data Reference Variables

Data reference variables are either completely typed or typed with the generic type data.

> Up Casts in Data References
> Down Casts in Data References

Up Casts in Data References

An up cast in data references is possible in the following cases:

◈ The static types of the source variable and the target variable match according to the following rules:
     ◈ Both are elementary data types with identical technical type attributes, that is predefined ABAP type, length and number of decimal places. The way in which the static types were defined is not important.
     ◈ Both have an identical structured type. In the case of structured types, identical technical type attributes are not sufficient and, in the definition of the static types, the same structured type must have been used.
     ◈ Both are table types with matching technical type attributes, that is row types, table category, and table key. In the case of non-structured row types, identical technical attributes of the row type are sufficient. In the case of structured row types, both definitions must contain the same structured type.
◈ The static type of the source variable is completely typed and that of the target variable is generic.

Note

With respect to checks on static types, structured types behave like classes. Two differently structured types do not match, even if they consist of identical components.

Down Casts in Data References

A down cast in data references is only possible if the static type of the source variable is generic and that of the target variable is completely typed. The syntax check makes it impossible that the static types of the source variable and the target variable are both completely typed, but not identical.

Example

The assignment from dref1 to dref2 is an up cast. The assignment from dref2 to dref1 is a down cast, which in the example below raises an exception. If the statement CREATE DATA had the addition TYPE i, the down cast would also have been successful.

DATA: dref1 TYPE REF TO i,
      dref2 TYPE REF TO data.

CREATE DATA dref1.
dref2 = dref1.
CREATE DATA dref2 TYPE string.

TRY.
  dref1 ?= dref2.
  CATCH cx_sy_move_cast_error.
  ...
ENDTRY.

- Assignments Between Object Reference Variables

Object reference variables are either class references or interface reference variables.

> Up Cast in Object References
> Down Cast in Object References

Up Cast in Object References

An up cast in object references is possible in the following cases:

◈ If both static types are classes, the class of the target variable must be of the same class or a superclass of the source variable.
◈ If both static types are interfaces, the interface of the target variable must be of the same interface or a component interface of the source variable.
◈ If the static type of the target variable is an interface and the static type of the source variable is a class, the class of the source variable must implement the interface of the target variable.
◈ If the static type of the target variable is a class and the static type of the source variable is an interface, the class of the target variable must be the generic type or the root class object.

Example

The statement iref = NEW class( ) is an up cast, because it is followed by an interface reference variable that points to a class.

INTERFACE intf.
ENDINTERFACE.

CLASS class DEFINITION.
  PUBLIC SECTION.
    INTERFACES intf.
ENDCLASS.

DATA iref TYPE REF TO intf.

START-OF-SELECTION.
  iref = NEW class( ).

Down Cast in Object References

For all cases not specified under the up cast, assignments can only be programmed using a down cast.

Example

Declaration of interfaces and classes, creation of an object in the subclass and access to the components of the object. In the statement CREATE OBJECT, an up cast occurs implicitly from c2 to iref. The interface reference iref can only be used to access the components declared in the interface i2. Method m1 of the object cannot be called using iref. Once the object reference has been assigned to the class reference cref using a down cast, m1 can be accessed dynamically but not statically.

INTERFACE i1.
  DATA a1 TYPE string.
ENDINTERFACE.

INTERFACE i2.
  INTERFACES i1.
  ALIASES a1 FOR i1~a1.
  DATA a1 TYPE string.
ENDINTERFACE.

CLASS c1 DEFINITION.
  PUBLIC SECTION.
    INTERFACES i2.
ENDCLASS.

CLASS c2 DEFINITION INHERITING FROM c1.
  PUBLIC SECTION.
    METHODS m1.
ENDCLASS.

...

DATA: iref TYPE REF TO i2,
      cref TYPE REF TO c1.

...

CREATE OBJECT iref TYPE c2.

... iref->a1 ...
... iref->a2 ...

...

IF iref IS INSTANCE OF c1.
  cref ?= iref.
  TRY.
      CALL METHOD cref->('M1').
    CATCH cx_sy_dyn_call_illegal_method.
      ...
  ENDTRY.
ENDIF.

⬕ =, ?= - Up Cast and Down Cast

Syntax

destination_ref =|?= source_ref.

Effect

Assignment between two reference variables. The reference in source_ref is assigned destination_ref. If the assignment is successful, destination_ref points to the same object as source_ref (reference semantics). The assignment of reference variables is a special form of assignments of data objects; two assignment operators are available for assignments between reference variables and these operators are used in accordance with the assignment rules for reference variables:

▶ In assignments between reference variables, the assignment operator = can only be used for up casts in which the static type of source_ref is more specific than or the same as the static type of destination_ref.

▶ The special casting operator ?= can only be used for assignments between reference variables. If the static type of source_ref is more general than the static type of destination_ref, ?= must be used to produce a down cast. If this is known statically, it is checked by the syntax check; if not, it is checked at runtime. The actual down cast (that is, the check to see whether assignments are possible in accordance with the assignment rules for reference variables) never takes place until runtime. If, then, the static type of destination_ref is not more general or is the same as the dynamic type of source_ref, a handleable exception is raised and the target variable keeps its original value.

The same applies to the right side and left side as when assigning data objects, with the following restrictions:

▶ The data types of the source and target are reference types, which means that functional method calls and constructor expressions and table expressions can be specified on the right side whose return value has a reference type. Built-in functions and calculation expressions do not return any reference variables and are not possible here.

▶ An inline declaration DATA(var) is possible only on the left side of =, and not on the left side of ?=. The static types of the reference variable source_ref is used, which must be known statically.

Notes

▶ The casting operator ?= can always be specified, even for up casts. This is, however, not usually necessary.

▶ If it is possible to know statically that assignments are not possible, neither = nor ?= can be used. This is the case, for example, when the static types of source variables and target variables come from different paths of the inheritance tree.

▶ The null reference of an initial reference variable can be assigned to every target variable in a down cast that can be specified here. The same applies to a non-initial invalid reference that no longer points to an object.

▶ For non-initial reference variables, the predicate expression IS INSTANCE OF or the case distinction CASE TYPE OF can be used to check whether a down cast can be applied to specific classes or interfaces.

▶ Alongside ?=, the casting operator CAST also enables down casts to operand positions, which saves on helper variables.

▶ Down casts are also possible using the INTO addition of the statement WHEN TYPE of a case distinction using CASE TYPE OF.

▶ The casting operator ?= cannot be used in multiple assignments.

▶ One obsolete form of down cast is the statement MOVE with the addition ?TO.

Example

▶ The first two assignments in the following source code section are up casts:

▶ The instance operator NEW creates a result with the static and dynamic type c2, which can be assigned to the more general reference variable oref1.

Any reference variable can be assigned to the reference variable oref with the most general static type object.

The next two assignments are down casts:

▶ It is not possible to test whether the general reference variable oref points to an object that can also point to oref2 until runtime. This is the case in the example.

▶ The down cast of oref2 to oref3, however, fails at runtime and raises the caught exception.

CLASS c1 DEFINITION INHERITING FROM object.
ENDCLASS.

CLASS c2 DEFINITION INHERITING FROM c1.
ENDCLASS.

CLASS c3 DEFINITION INHERITING FROM c2.
ENDCLASS.

DATA oref TYPE REF TO object.
DATA oref1 TYPE REF TO c1.
DATA oref2 TYPE REF TO c2.
DATA oref3 TYPE REF TO c3.

oref1 = NEW c2( ).

oref = oref1.

IF oref IS INSTANCE OF c2.
   oref2 ?= oref.
ENDIF.

TRY.
    oref3 ?= oref2.
  CATCH cx_sy_move_cast_error.
    ...
ENDTRY.

Handleable Exceptions

CX_SY_MOVE_CAST_ERROR

▶ Cause: Type conflict in down cast
Runtime error: MOVE_CAST_ERROR

▶ Cause: Source variable or target variable is not a reference variable
Runtime error: MOVE_CAST_REF_ONLY

▶ Cause: Dynamic type conflict in assignment of references
Runtime error: MOVE_CAST_ERROR_DYN

⬕ CAST - Casting Operator

Syntax

... CAST type( [let_exp] dobj ) ...

Effect

A constructor expression with the casting operator CAST performs a down cast or an up cast for the argument dobj and creates a reference variable of the static type type as a result. type can be specified as:

◈ any non-generic data type dtype or the fully generic data type data
◈ any object type (class or interface) including the fully generic object type object.
◈ The # character as a symbol for the operand type. Can be specified only if the data type required in an operand position is unique and fully identifiable.

The parentheses must contain precisely one unnamed argument dobj. If a data type is specified, dobj must be a data reference variable. If an object type is used, dobj must be an object reference variable. dobj is a general expression position.

Casts of dobj to the specified type are performed in accordance with the same rules as apply to the casting operator for assignments, ?=. If the specified type type is more specific than the static type of dobj, it is a down cast. If type is more general than the static type of dobj, it is an explicit up cast.

◈ Like a data reference variable of type dtype, a constructor expression CAST dtype( ... ) can be continued using a dereferencing operator ->*; if a structured type is used, it can be specified using an object component selector ->.
◈ Like an object reference variable of the type class, a constructor expression CAST class( ... ) can be specified using an object component selector -> and in general expression positions and functional positions.Furthermore, it is possible to introduce standalone method calls including chained method calls.

A single expression not followed by the object component selector -> can be specified as a standalone statement. This makes it possible to test the feasibility of a down cast by catching the associated exception CX_SY_MOVE_CAST_ERROR.

If a single expression points to one data object or component of a data object using precisely one following object component selector, ->,

◈ it can be specified as a writable expression in a result position. The type of the result must be convertible to the data type type with the following restriction: If the result is the result of a bit expression, type must be byte-like or character-like with the type c or string.
◈ a specified offset/length +off(len) can be appended (if the data type is suitable), except if the chaining uses the statement ASSIGN as a memory area.

An optional LET expression let_exp can be specified before the argument to define local helper fields.

Notes

◈ The casting operator CAST is suitable for avoiding the declaration of helper variables needed only for down casts.

◈ Explicit up casts with the casting operator CAST are suitable for declaring a more general type of a declared reference variable in inline declarations.

◈ No empty parentheses can be specified after CAST.

◈ Unlike NEW, CAST works with an existing reference variable, which means that the result does not need to be saved to persist the referenced object.

◈ The predicate expression IS INSTANCE OF or the case distinction CASE TYPE OF can be used to check whether a down cast can be applied to specific classes or interfaces.

◈ Down casts are also possible using the INTO addition of the statement WHEN TYPE of a case distinction using CASE TYPE OF.

◈ The constructor operator CONV is related to CAST, but performs a conversion instead of a casting.

Example

The source code section below shows two equally valid down casts with the two possible casting operators ?= and CAST. Before the actual cast, its feasibility is checked using the predicate expression IS INSTANCE OF.

CLASS c1 DEFINITION.
ENDCLASS.
CLASS c2 DEFINITION INHERITING FROM c1.
ENDCLASS.

DATA: oref1 TYPE REF TO c1,
      oref2 TYPE REF TO c2.

oref1 = new c2( ).

IF oref1 IS INSTANCE OF c2.
  oref2 ?= oref1.
  oref2 =  CAST #( oref1 ).
ENDIF.

Example

Uses dereferencing operator and component selector in CAST with data types. The constructor expressions can be used as writable expressions on the right and left of assignments with the assignment operator=.

TYPES: BEGIN OF t_struc,
        col1 TYPE i,
        col2 TYPE i,
       END OF t_struc.

DATA dref TYPE REF TO data.
DATA struc TYPE t_struc.

dref = NEW t_struc( ).

struc      = CAST t_struc( dref )->*.
struc-col1 = CAST t_struc( dref )->col1.
struc-col2 = CAST t_struc( dref )->col2.

CAST t_struc( dref )->*    = struc.
CAST t_struc( dref )->col1 = struc-col1.
CAST t_struc( dref )->col2 = struc-col2.

Example

RTTS often requires a helper variable to perform a down cast of a type description object to a specialized class. These examples show how helper variables can be omitted by using the operator CAST.

DATA(attributes) =
  CAST cl_abap_classdescr(
       cl_abap_classdescr=>describe_by_name( 'CL_ABAP_FORMAT' )
       )->attributes.

DATA(components) =
  CAST cl_abap_structdescr(
       cl_abap_typedescr=>describe_by_name( 'T100' )
       )->components.

DATA(no_of_components) =
  lines( CAST cl_abap_structdescr(
              cl_abap_typedescr=>describe_by_name( 'SYST' )
              )->get_components( ) ).

Example

The factory method of the following class returns a reference variable of the type of the class itself. To use interface variables to access the components declared in the interface if, as recommended, the variable iref, declared inline, is cast to the type of the interface.

INTERFACE if.
  ...
ENDINTERFACE.

CLASS cl DEFINITION CREATE PRIVATE.
  PUBLIC SECTION.
    INTERFACES if.
    CLASS-METHODS factory RETURNING value(ref) TYPE REF TO cl.
    ...
ENDCLASS.

CLASS cl IMPLEMENTATION.
  METHOD factory.
    ref = NEW #( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  DATA(iref) = CAST if( cl=>factory( ) ).

⬕ GET REFERENCE

Syntax

GET REFERENCE OF dobj INTO dref.

Effect

This statement sets the reference in the reference variable dref in a way that makes it point to the data object dobj. The following can be specified for dref:

◈ An existing data reference variable. The static type of the data reference variable must be more general than or the same as the data type dobj, according to the assignment rules for reference variables.

◈ An inline declaration DATA(var). This declares a data reference variable whose static type is the data type of dobj. The data type of dobj must be known statically as a full type or as the generic type data. Field symbols and formal parameters with other generic types, in particular any, are not possible.

The data object is specified directly and in accordance with the rules described in the section Reading Positions. If offsets/lengths (+off(len)) are specified, the data type dobj here cannot be string or xstring.

Notes

◈ Alongside the reference operator REF and the addition REFERENCE INTO, the statement GET REFERENCE is the only option available to statements for internal tables to create stack references. Stack referencescan become invalid if the referenced data object is deleted.

◈ When applied to data objects in the heap GET REFERENCE creates memory-retaining heap references.

◈ The content of two reference variables filled with GET REFERENCE is only the same if the remaining administration information is the same, apart from the referenced data objects. For example, a reference that is retrieved directly by specifying the data object is not the same as a reference that is retrieved by specifying a field symbol if this has a different data type due to a casting.

◈ When an internal table has a header line, a data reference variable can only point to this or the table body. In the statement GET REFERENCE, the name of an internal table with a header line addresses the header line. To address the table body, [] must be appended to the name in the usual way. A dereferenced data reference to which a table body is assigned behaves in the same way in operand positions as a table without a header line.

◈ If references are set using GET REFERENCE, permission to access the data object in question is only checked at the position of the statement. After that, the references can be passed on to any destination and the associated data objects can be accessed from any position using the references. To prevent access to private and read-only attributes using references outside classes, do not publish references to these attributes externally. A constant or read-only input parameter, however, can never be made modifiable by passing its reference.

◈ A data reference retrieved using GET REFERENCE that references a data object in the shared objects memory can also be stored in a closed area instance version. The restrictions described for the addition AREA HANDLE of the statement CREATE DATA must be respected.

◈ The reference operator REF works like the statement GET REFERENCE and can be used in general expression positions.

Example

Creates data references to the individual characters of a data object text and saves them in an internal table. Direct dereferencing at an operand position is possible because the data reference is fully typed. After the output, an alternative implementation with an iteration expression and the reference operator REF is shown that has the same result.

TYPES c1 TYPE c LENGTH 1.

DATA: dref         TYPE REF TO c1,
      dref_tab     LIKE TABLE OF dref WITH EMPTY KEY,
      dref_tab_new LIKE dref_tab.

DATA: text TYPE c LENGTH 10 VALUE '0123456789',
      off  TYPE i.

DO 10 TIMES.
  off = sy-index - 1.
  GET REFERENCE OF text+off(1) INTO dref.
  APPEND dref TO dref_tab.
ENDDO.

LOOP AT dref_tab INTO dref.
  cl_demo_output=>write( |{ dref->* }| ).
ENDLOOP.

cl_demo_output=>display( ).

dref_tab_new = VALUE #(
  FOR j = 0 UNTIL j > 9 ( REF #( text+j(1) ) ) ).
ASSERT dref_tab_new = dref_tab.

Non-Handleable Exceptions

◈ Cause: The data object specified after INTO is not a reference variable.
Runtime error: GET_REF_REFERENCE_EXPECTED

◈ Cause: GET REFERENCE is not permitted for a substring.
Runtime error: ILLEGAL_SUBSTRING_PARAMETER

⬕ REF - Reference Operator

Syntax

... REF type( dobj | table_exp ) ...

Alternatives:

1. ... REF type( dobj )

2. ... REF type( table_exp )

Effect

A constructor expression with the reference operator REF creates either a data reference variable that points to the argument dobj or controls a table expression table_exp.

Existing data objects and table expressions can be specified as an argument, but no further expressions or function calls.

Alternative 1

... REF type( dobj )

Effect

If a data object dobj is specified as an argument, the operator REF works like the statement GET REFERENCE and creates a data reference variable as a result. This variable points to the specified data object dobj. type determines the static type of the result. The following can be specified for type:

◈ A non-generic data type dtype that follows the rules of up casts in data references.
◈ The generic data type data.
◈ The # character for a data type, determined in accordance with the following hierarchy:
     ◈ If the data type required in an operand position is unique and known completely, the operand type is used.
     ◈ If the operand type cannot be derived from the context, the data type of dobj is used.
     ◈ If the data type of dobj is not known statically, the generic type data is used.

The parentheses must contain precisely one unnamed argument dobj. If offsets/lengths (+off(len)) are specified, the data type dobj here cannot be string or xstring.

Notes

◈ The conversion operator REF is suitable for avoiding declarations of helper variables only needed, for example, to specify data reference variables as actual parameters.
◈ No empty parentheses can be specified after REF.

Example

Fills an internal table with the value operator VALUE, where the component dref is given a value using REF. This is an excerpt from the executable example for the class CL_ABAP_BROWSER. Other uses can be found in the executable examples of ADBC, where parameter bindings are used.

TYPES pict_line(1022) TYPE x.

DATA  pict     TYPE STANDARD TABLE OF pict_line WITH EMPTY KEY.
DATA  ext_data TYPE cl_abap_browser=>load_tab.

ext_data = VALUE #( ( name = 'PICT.GIF'
                      type = 'image'
                      dref = REF #( pict ) ) ).

Alternative 2

... REF type( tab_exp )

Effect

If a table expression table_exp is specified as an argument, the operator REF controls the category of its result and enables a default value to be specified for unfound rows, as described in the related section.

Example

The result of the operator REF is a data reference variable, which references the fifth row of the internal table itab.

DATA itab TYPE STANDARD TABLE OF i WITH EMPTY KEY.

itab = VALUE #( FOR i = 1 UNTIL i >= 10 ( i * 10 ) ).

DATA(dref) = REF #( itab[ 5 ] ).

cl_demo_output=>display( dref->* ).

1.5.2 Setting Field Symbols

The reference in a field symbol is set using an assignment of a memory area to the field symbol by the statement

- ASSIGN

The reference in a field symbol is deleted by the statement

- UNASSIGN

1.5.2.1 ASSIGN

Syntax

ASSIGN mem_area TO <fs> casting_spec range_spec.

Effect

This statement assigns the memory area specified using mem_area to the field symbol <fs>. A data object or a memory area calculated from the address of a data object can be assigned. After the assignment, the field symbol refers to the assigned memory area and can be used in operand positions. When used in a statement, it behaves like a dereferenced data reference, meaning that the statement works with the content of the memory area.

The following can be specified for <fs>:

◈ An existing field symbol with appropriate typing.

◈ An inline declaration FIELD-SYMBOL(<fs>). The typing depends on the mem_area specified.

The data type with which the assigned memory area is handled depends on what is specified in casting_spec. Either an explicit casting can be performed or the field symbol uses the data type of the data object specified in the assignment. In both cases, the data type used must match the typing of the field symbol. A field symbol to which a memory area is assigned, has this data type after the assignment and behaves like a data object of this type.

The assigned memory area mem_area must be at least as long as the data type specified in casting_spec and must have at least the same alignment. If the data type determined in casting_spec is deep, the deep components with their type and position must appear in the assigned memory area exactly like this.

The information in range_spec is used to define the memory area that can be assigned to the field symbol.

System Fields

The return value is set only for the dynamic variants and the table expression variant of mem_area. If the constructor operator NEW is used as a writable expression, this operator sets the return value.

sy-subrc    Meaning
0           Assignment completed.
4                Assignment not completed.
8           A table expression was not assigned.

If a dynamic assignment or assignment of a table expression could not be performed, the field symbol keeps its previous state. If the static assignment could not be performed, a memory is not assigned to the field symbol after the statement ASSIGN and the assignment can be checked with the predicate expression <fs> IS ASSIGNED. If an assignment were to produce illegal memory accesses, an exception is raised in the case of both static and dynamic ASSIGN statements.

Notes

◈ If field symbols are set using ASSIGN, permission to access the assigned data object is only checked at the position of the statement. The field symbol can then be passed on as required and used to access the assigned data object in any position. To prevent access to private and read-only attributes using field symbols outside classes, field symbols for these attributes should not be published externally. A constant or read-only input parameter, however, can never be made modifiable by passing a field symbol.

◈ One obsolete form of the statement ASSIGN is ASSIGN LOCAL COPY.

Example

Three field symbols are assigned the subfields for year, month, and day of the system field sy-datlo. The subfields can then be addressed using field symbols.

FIELD-SYMBOLS <year>  TYPE n.
FIELD-SYMBOLS <month> TYPE n.
FIELD-SYMBOLS <day>   TYPE n.

ASSIGN sy-datlo+0(4) TO <year>.
ASSIGN sy-datlo+4(2) TO <month>.
ASSIGN sy-datlo+6(2) TO <day>.

cl_demo_output=>new(
  )->write(   <year>
  )->write(   <month>
  )->display( <day> ).

Exceptions

Handleable Exceptions

CX_SY_ASSIGN_CAST_ILLEGAL_CAST

◈ Cause: The type of the source field and the target type do not match exactly in offset and type in those components that are strings, tables, or references.
Runtime error: ASSIGN_CASTING_ILLEGAL_CAST

CX_SY_ASSIGN_CAST_UNKNOWN_TYPE

◈ Cause: A type specified dynamically after CASTING is unknown.
Runtime error: ASSIGN_CASTING_UNKNOWN_TYPE

CX_SY_ASSIGN_OUT_OF_RANGE

◈ Cause: The data object in addition RANGE does not contain the assigned data object.
Runtime error: ASSIGN_FIELD_NOT_IN_RANGE

Non-Handleable Exceptions

◈ Cause: The field symbol is structured and the assigned field is shorter than the structure.
Runtime error: ASSIGN_BASE_TOO_SHORT

◈ Cause: The alignment for field f is too short for the type of the field symbol.
Runtime error: ASSIGN_BASE_WRONG_ALIGNMENT

◈ Cause: Only simple types can be specified for TYPE.
Runtime error: ASSIGN_CAST_COMPLEX_TYPE

◈ Cause: The source field is longer than 16 bytes and cannot be interpreted as a type p field.
Runtime error: ASSIGN_CAST_P_TOO_LARGE

◈ Cause: The alignment of field f is too short for the type specified in TYPE.
Runtime error: ASSIGN_CAST_WRONG_ALIGNMENT

◈ Cause: The length of field f does not match the type specified in TYPE.
Runtime error: ASSIGN_CAST_WRONG_LENGTH

◈ Cause: The type specified in TYPE is unknown.
Runtime error: ASSIGN_CAST_WRONG_TYPE

◈ Cause: A maximum of 14 columns is permitted.
Runtime error: ASSIGN_DECIMALS_TOO_HIGH

◈ Cause: Decimal places are allowed only for type p.
Runtime error: ASSIGN_DECIMALS_WRONG_TYPE

◈ Cause: A length of 0 was specified for field f.
Runtime error: ASSIGN_LENGTH_0

◈ Cause: A length less than 0 was specified for field f.
Runtime error: ASSIGN_LENGTH_NEGATIVE

◈ Cause: An offset less than 0 was specified for field f.
Runtime error: ASSIGN_OFFSET_NEGATIVE

◈ Cause: An offset or length was specified for field f and the data type of the assigning field does not allow partial access. (This is the case for data types I, F, and P.)
Runtime error: ASSIGN_OFFSET_NOTALLOWED

◈ Cause: The offset specified for field f exceeds the range of the ABAP variable.
Runtime error: ASSIGN_OFFSET_TOOLARGE

◈ Cause: In the area addressed in the offset and length specifications for field f, deep components exist (data references, object references, strings, internal tables), which may not be overwritten.
Runtime error: ASSIGN_OFF+LENGTH_ILLEGAL_CAST

◈ Cause: Offset and length specified for field f exceed the range of the ABAP variable.
Runtime error: ASSIGN_OFFSET+LENGTH_TOOLARGE

◈ Cause: Field f is not a data reference. However, a data reference was expected.
Runtime error: ASSIGN_REFERENCE_EXPECTED

◈ Cause: The type of the source field and the target type do not match exactly in offset and type in those components that are strings, tables, or references.
Runtime error: ASSIGN_STRUCTURE_ILLEGAL_CAST

◈ Cause: Substrings cannot be assigned to a field symbol.
Runtime error: ASSIGN_SUBSTRING_NOT_ALLOWED

◈ Cause: The field symbol is typed and the type of the assigned field is incompatible with it.
Runtime error: ASSIGN_TYPE_CONFLICT

◈ Cause: The type of the source field contains strings, tables, or references.
Runtime error: ASSIGN_TYPE_ILLEGAL_CAST

◈ Cause: The type of the source field is a structure not compatible with the target type.
Runtime error: ASSIGN_UC_STRUCT_CONFLICT

◐ ASSIGN - mem_area

Syntax

... static_dobj
  | dynamic_dobj
  | dynamic_access
  | writable_exp ...

Alternatives:

Static Specification

1. ... static_dobj

Dynamic Specifications

2. ... dynamic_dobj

3. ... dynamic_access

Specifying an Expression

4. ... writable_exp

Effect

mem_area is used to specify the memory area that is assigned to the field symbol.

◈ The first variant static_dobj is a static variant that is assigned a statically defined data object or part of one of these objects.

◈ The second and third variants are dynamic. The variants dynamic_dobj are used for general dynamic access to data objects; the variants dynamic_access are used for dynamic access to the attributes of classes.

◈ The result of a writable expression is assigned in the fourth variant.

In an inline declaration of the field symbol with FIELD-SYMBOL(<fs>), the typing of the field symbol is performed for the static variant and for a specified expression with the data type mem_area. In the dynamic variants, the typing is performed with the generic type data.

The variants differ in the way that the system behaves after a successful assignment:

◈ The return code sy-subrc is not set for the static variant.

◈ In dynamic variants and when a table expression is specified, the statement ASSIGN sets the return code sy-subrc.

Notes

◈ In an internal table with a header line, either the header line or the table body can be assigned to a field symbol. In the statement ASSIGN, the name of an internal table with a header line addresses the header line. To address the table body, [] must be appended to the name in the usual way. A field symbol to which a table body is assigned behaves in the same way in operand positions as a table without a header line.

◈ Field symbols to which data objects or parts of data objects are assigned in the heap are memory-preserving, like heap references.

◈ In an inline declaration of the field symbol with FIELD-SYMBOL(<fs>), the data type of the assigned memory area determines the typing of the field symbol.

1. ASSIGN - static_dobj

... dobj[+off][(len)]  ...

Effect

In the static variant for the memory area, a data object dobj with an optional offset/length +off(len) can be specified in accordance with the rules in the section Data Objects in Operand Positions, with the following exception: A data reference dereferenced using the dereferencing operator ->* is specified dynamically. dobj specified using a field symbol, on the other hand, is a static variant.

The memory area is determined by the specified offset/length +off(len) as follows:

◈ If no offset/length specification is made, the assigned memory area is the same as the memory area of the data object. The entire data object dobj is assigned to the field symbol, and statements that contain the field symbol in operand positions work with the data object.

◈ If an offset/length is specified, the memory area is determined from the memory address of the data object and the offset/length specifications. The general rules for substring accesses apply. If these rules are met, memory outside the dobj field limits can also be addressed. The addressable memory is based on the specification range_spec. If an offset off is specified without a length len, the length of the data object dobj is used implicitly for len. If a field symbol is specified for dobj, to which a memory area is already assigned, the content of the offset length can be negative, as long as the area specified in range_spec is not exited. The following restrictions apply when offsets/lengths are specified:

◈ No inline declaration of the field symbol with FIELD-SYMBOL(<fs>) can be made.
◈ The data type of dobj cannot be string or xstring. This means that len can never be less than or equal to zero.
◈ If the name of a data object is specified for dobj and if no explicit RANGE addition is used, no offset off can be specified without the length len. If the name of a field symbol is specified for dobj, its data type must be flat and elementary whenever an offset off is specified without length len.

If the assignment is not successful, no memory area is assigned to the field symbol after the ASSIGN statement. The return code sy-subrc is not set for static variants. Instead, the predicate expression <fs> IS ASSIGNED can be evaluated.

In an inline declaration of the field symbol with FIELD-SYMBOL(<fs>), the field symbol is typed with the data type that can be determined statically for mem_area. If mem_area is a generically typed field symbol or a generically typed formal parameter, the generic type is used.

Notes

◈ Ensure that the system field sy-subrc is not evaluated inadvertently after the static variant. The value of the system field in this case is always the same as it was before the ASSIGN statement was executed and therefore does not indicate whether the statement was successful.

◈ Even the static variant is dynamic in the sense that the offset and length specifications may be dynamic. Dynamic offset/length specifications do not, however, result in the system field sy-subrc being set.

◈ If a generically typed field symbol or a generically typed formal parameter is specified for dobj, its current type at runtime determines the behavior, for example whether offsets/lengths can be specified.

Example

Assigns the memory area of the individual characters of a data object text to a field symbol <char>.

DATA text TYPE c LENGTH 10 VALUE '0123456789'.

FIELD-SYMBOLS <char> TYPE c.

DATA off TYPE i.

DO 10 TIMES.
  off = sy-index - 1.
  ASSIGN text+off(1) TO <char>.
  cl_demo_output=>write_text( |{ <char> }| ).
ENDDO.
cl_demo_output=>display( ).

Example

A field symbol <fs1> points to the component col1 of the structure struct. In the first assignment of <fs1> to a field symbol <fs2>, an offset without length is specified, which means that the length 10 of the component col1 is used implicitly. This assignment is not possible, since the assigned memory area is outside of the permitted range. In the second assignment, the permitted memory area is expanded to the full structure using the assignment RANGE and the assignment is successful.

DATA:
  BEGIN OF struct,
    col1 TYPE c LENGTH 10 VALUE 'aaaaaaaaaa',
    col2 TYPE c LENGTH 10 VALUE 'bbbbbbbbbb',
  END OF struct.

FIELD-SYMBOLS: <fs1> TYPE c,
               <fs2> TYPE c.

ASSIGN struct-col1 TO <fs1>.
ASSIGN <fs1>+5 TO <fs2>.
IF <fs2> IS NOT ASSIGNED.
  cl_demo_output=>write( `No assignment without sufficient RANGE` ).
ENDIF.
ASSIGN <fs1>+5 TO <fs2> RANGE struct.
IF <fs2> IS ASSIGNED.
  cl_demo_output=>display( `Assignment with sufficient RANGE` ).
ENDIF.

2. ASSIGN - dynamic_dobj

Syntax

... { (name) }
  | { dref->* }
  | {dobj INCREMENT inc}
  | { COMPONENT comp OF STRUCTURE struc } ...

Alternatives:

1. ... (name)

2. ... dref->*

3. ... dobj INCREMENT inc

4. ... COMPONENT comp OF STRUCTURE struc

Effect

These alternatives to specifying the memory area mem_area of the statement ASSIGN dynamically are used to dynamically access data objects.

In an inline declaration of the field symbol using FIELD-SYMBOL(<fs>), its typing is performed with the generic type data.

In these variants, the statement ASSIGN sets the return code sy-subrc. If the assignment is successful, sy-subrc is set to 0; if not, it is set to 4. If the assignment is not successful, the field symbol keeps its previous state. It is therefore not enough just to evaluate the predicate expression <fs> IS ASSIGNED in a dynamic ASSIGN; sy-subrc needs to be checked as well.

Alternative 1

... (name)

Effect

In this dynamic variant of mem_area, the memory area is not specified directly, but as content of a character-like data object (name) in parentheses. The following can be specified for name:

◈ Literal or constants
If the data object name is specified as a character literal or as a constant, it can be evaluated statically and the specified data object is identified as the used object.
◈ Variable
If the data object name is specified as a variable, it is specified only dynamically and the content is not evaluated statically.

When the statement is executed, name is not evaluated until runtime (in both cases). The name in name is structured in the same way as if specified directly: When executing the statement, the content of name must be the name of a data object which may contain offsets and lengths, structure component selectors, and component selectors for assigning structured data objects and attributes in classes or objects. The content of name does not have to be in uppercase letters.

name can contain a chain of names consisting of component selectors. For an individual name or if the first name is followed by an object component selector (->), the specified data object is searched for according to the following hierarchy:

1. If the statement is located in a procedure, the local data objects of the procedure are scanned.

2. If the statement is located in a method, the attributes visible in the method within the class are scanned. In instance methods, the static type of me (special case of cref->(attr_name) in dynamic_access) is scanned.

3. The global data of the current program is scanned.

4. The interface work areas of the main program of the current program group declared using TABLES are scanned.

5. If the statement is located in an instance method, the dynamic type of me (special case of cref->(attr_name) in dynamic_access) is scanned.

If the data object is found and the name is followed by an object component selector (->), the search for the following names is continued from left to right, as described under dynamic_access.

If the first name is followed by a class component selector (=>), the specified class is searched for, as described under dynamic_access, and the search is then continued accordingly from left to right.

Notes

◈ Dynamically specifying a structure component using a structure component selector produces worse performance than using the addition COMPONENT OF STRUCTURE (see this executable example).

◈ If an attribute of a class in a different program is specified in name using an absolute type name and followed by the class component selector (=>), it is loaded into a new additional program group or into the current program group (if not already loaded), depending on the program type. Any existing program constructors are not executed, however, unlike in a genuine dynamic_access.

◈ For internal use only, the name in name can also have the form "(PROG)DOBJ", where "PROG" is the name of an ABAP program and "DOBJ" the name of a global data object of this program (these names are not case-sensitive). If the program "PROG" is loaded into the same internal session as the current program when the statement ASSIGN is executed, the data object "DOBJ" is found in this program and the field symbol points to this data object if the assignment was successful.

◈ In an obsolete variant, the addition TABLE FIELD can be specified before name. This restricts the search to table work areas.

Example

Dynamic output of the content of any system field. The validity of the input is checked before it is dynamically assigned with (name) to field symbol syfield via an application of classes of RTTI on structure SYST

DATA(name) = `sy-uzeit`.
cl_demo_input=>request( EXPORTING text = `System field`
                        CHANGING field = name ).
name = to_upper( name ).

DATA(components) = CAST cl_abap_structdescr(
  cl_abap_typedescr=>describe_by_name( 'SYST' ) )->components.
IF NOT line_exists( components[ name = replace( val = name
                                sub  = `SY-`
                                with = `` ) ] ).
  cl_demo_output=>display( `Unknown system field` ).
  RETURN.
ENDIF.

ASSIGN (name) TO FIELD-SYMBOL(<syfield>).

IF sy-subrc = 0.
  cl_demo_output=>display( |{ name }: { <syfield> }| ).
ENDIF.

Alternative 2

... dref->*

Effect

When specifying a dereferenced data reference dref for mem_area using the dereferencing operator ->*, the memory area of the data object is assigned to the field symbol to which dref points. If the reference variable dref does not reference a data object, the assignment is not performed and sy-subrc is set to 4.

Unlike all other operand positions for which the data reference dref must be fully typed for dereferencing, dref can be typed generically in the statement ASSIGN using TYPE REF TO data. Dereferencing of a data reference that does not point to a data object also raises an non-handleable exception in all cases except in the statement ASSIGN.

Example

Creates a local copy of a global data object g_dat in a procedure using a data reference dref and a local field symbol <l_dat>.

DATA g_dat TYPE string VALUE '...'.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS meth.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD meth.
    DATA dref TYPE REF TO data.
    FIELD-SYMBOLS <l_dat> TYPE any.
    CREATE DATA dref LIKE g_dat.
    ASSIGN dref->* TO <l_dat>.
    <l_dat> = g_dat.
    cl_demo_output=>display_data( <l_dat> ).
  ENDMETHOD.
ENDCLASS.

Alternative 3

... dobj INCREMENT inc

Effect

This expression for mem_area assigns a memory area to the field symbol that has the same length as the memory area of dobj and is incremented inc times this length in reference to the memory area of dobj. inc expects a numeric data object. A data object or a field symbol must be specified directly for dobj. Offset or length specifications or the dereferencing of a data reference are not possible. The field symbol cannot be declared via an inline declaration FIELD-SYMBOL(<fs>).

Notes

The dynamic ASSIGN variant with INCREMENT is designed for sequential access to similar memory areas that are located at regular intervals after each other, such as consecutive structure components of the same data type. In all other cases, ASSIGN ... INCREMENT must be used with caution. Note the following in particular:

◈ Usually addition RANGE must be used to defined the area, within which it is possible to work with INCREMENT.

◈ The assigned memory area is handled using the data type dobj if the addition CASTING is not specified in casting_spec. This means that an implicit casting of the assigned memory areas to the data type of dobj is performed.

◈ The typing check also refers to dobj, but is performed only when the statement is executed.

◈ Runtime errors always occur if the following general rule is violated: If deep data objects that are in the assigned memory area do not match the typing as far as type and position are concerned.

Example

After the ASSIGN statement, the field symbol points to the fourth component col4. See the example for addition RANGE.
DATA:
  BEGIN OF struct,
    col1 TYPE string VALUE `COL1`,
    col2 TYPE string VALUE `COL2`,
    col3 TYPE string VALUE `COL3`,
    col4 TYPE string VALUE `COL4`,
    col5 TYPE string VALUE `COL5`,
  END OF struct.

FIELD-SYMBOLS <fs> TYPE string.
ASSIGN struct-col1 INCREMENT 3 TO <fs> RANGE struct.

cl_demo_output=>display( <fs> ).

Alternative 4

... COMPONENT comp OF STRUCTURE struc

Effect

This expression for mem_area assigns a memory area of a component comp of a structure struc to the field symbol.

struc is a result position. The structure can be specified as a data object or as a writable expression. If struc is specified as an expression, its result must be structured. If struc is specified as a data object, the result does not need to be structured.

comp is a character-like or numeric expression position. The evaluation depends on the data type of comp:

◈ If the field comp has a text-like type (c or string) or the type of a flat structure, which exclusively contains character-like components, the field content is interpreted as the name of the component. The name must be in uppercase letters. It may contain offsets and lengths, structure component selectors, and component selectors for assigning structured data objects and attributes in classes or objects.

◈ If the field comp has a non-text-like elementary type, the content is converted to the type i and interpreted as the position of the component in the structure. If the value of comp is 0, the memory area of the entire structure is assigned to the field symbol.

◈ If comp has a different type, a syntax error or runtime error occurs.

If an operand struc specified as a data object is not a structure or the specified component is not found, the assignment is not made and sy-subrc is set to 4.

Notes

◈ Identifying a component by its name is far more performance-intensive than using its position, since far more internal processes are involved. Using COMPONENTS OF, however, always produces better performance than specifying the name after the structure component selector within a fully dynamically specified component in a parenthesized data object name (see this executable example).

◈ If the structure struc is specified as a table expression and the corresponding row is not found, the exception CX_SY_ITAB_LINE_NOT_FOUND is raised.

◈ Writable expressions can be specified for struc but no other expressions, since these can have a non-temporary result. Assigning a component of a temporary structure to a field symbol would not make sense.

◈ If struc is specified, it is advisable to specify only structures as a data object and to check this in advance. Just evaluating sy-subrc is not enough to determine why an assignment was not successful.

Example

Assignment of all components of a structure to a field symbol in a loop. In every loop pass, the component is assigned whose position is determined by the loop index.

SELECT SINGLE *
       FROM scarr
       WHERE carrid = 'UA'
       INTO @DATA(wa).

DO.
  ASSIGN COMPONENT sy-index OF STRUCTURE wa TO FIELD-SYMBOL(<fs>).
  IF sy-subrc <> 0.
    EXIT.
  ENDIF.
  cl_demo_output=>write( <fs> ).
ENDDO.
cl_demo_output=>display( ).

Example

The following two methods show the dynamic assignment of the components of a structure (passed to the parameter para of the methods) to a field symbol <comp>.

◈ The first implementation does not use RTTI. The statement DESCRIBE FIELD is used to check whether the passed data object is a structure. The components are then assigned one after the other to the field symbol in a DO loop.

◈ The second implementation uses RTTI. A down cast of the type description object to the class CL_ABAP_STRUCTDESCR for the passed data object ensures that the object is a structure. A loop across the component table COMPONENTS assigns the components to the field symbol via their names.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    METHODS: meth1 IMPORTING para TYPE data,
             meth2 IMPORTING para TYPE data.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD meth1.
    DESCRIBE FIELD para TYPE DATA(dtype).
    IF dtype <> 'u' AND dtype <> 'v'.
      RETURN.
    ENDIF.
    DO.
      ASSIGN COMPONENT sy-index
        OF STRUCTURE para TO FIELD-SYMBOL(<comp>).
      IF sy-subrc <> 0.
        EXIT.
      ENDIF.
      ...
    ENDDO.
  ENDMETHOD.
  METHOD meth2.
    TRY.
        DATA(struct_descr) = CAST cl_abap_structdescr(
          cl_abap_typedescr=>describe_by_data( para ) ).
      CATCH cx_sy_move_cast_error.
        RETURN.
    ENDTRY.
    LOOP AT struct_descr->components
            ASSIGNING FIELD-SYMBOL(<comp_descr>).
      ASSIGN COMPONENT <comp_descr>-name
             OF STRUCTURE para TO FIELD-SYMBOL(<comp>).
      ...
    ENDLOOP.
  ENDMETHOD.
ENDCLASS.

Example

Assigns a component of a row of an internal table to a field symbol.

TYPES:
  BEGIN OF struc,
    col1 TYPE i,
    col2 TYPE i,
  END OF struc.

DATA itab TYPE STANDARD TABLE OF struc WITH EMPTY KEY.

itab = VALUE #( ( col1 = 1 col2 = 2 )
                ( col1 = 3 col2 = 4 ) ).

ASSIGN COMPONENT 2 OF STRUCTURE itab[ 2 ] TO FIELD-SYMBOL(<fs>).

cl_demo_output=>display( <fs> ).

3. ASSIGN - dynamic_access

Syntax

... { cref->(attr_name) }
  | { iref->(attr_name) }
  | { (class_name)=>(attr_name) }
  | { (class_name)=>attr }
  | { class=>(attr_name) } ...

Alternatives:

1. ... cref->(attr_name)

2. ... iref->(attr_name)

3. ... (class_name)=>(attr_name)

4. ... (class_name)=>attr

5. ... class=>(attr_name)

Effect

These alternatives to specifying the memory area mem_area of the statement ASSIGN dynamically are designed especially for dynamic access to attributes of classes (Dynamic Access).

In an inline declaration of the field symbol using FIELD-SYMBOL(<fs>), its typing is performed with the generic type data.

In these variants, the statement ASSIGN sets the return code sy-subrc. If the assignment is successful, sy-subrc is set to 0; if not, it is set to 4. If the assignment is not successful, the field symbol keeps its previous state. It is therefore not enough just to evaluate the predicate expression <fs> IS ASSIGNED; sy-subrc needs to be checked as well.

Note

Field symbols to which instance attributes (or parts of instance attributes) are assigned have a memory-preserving affect on the associated object.

Alternative 1

... cref->(attr_name)


Effect

This form may be used for all visible attributes of objects. cref can be any class reference variable pointing to an object which contains the attribute specified in a character-like field attr_name. The system searches for the attribute first in the static type of cref and then in the dynamic type.

The attribute name does not need to be specified in uppercase letters. It can contain offsets/lengths, structure component selectors, object component selectors, and class component selectors, to assign parts of the attribute (or referenced objects of the attribute).

Example

Dynamic assignment of an object attribute to a field symbol. The assignment is made via an object reference variable of the static type of the root class object, which can reference any object.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    METHODS meth IMPORTING oref TYPE REF TO object
                           attr TYPE string.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD meth.
    ASSIGN oref->(attr) TO FIELD-SYMBOL(<attr>).
    ...
  ENDMETHOD.
ENDCLASS.

Alternative 2

... iref->(attr_name)


Effect

This form may be used for all visible interface attributes of objects. iref can be any interface reference variable pointing to an object which contains the interface attributes specified in a character-like field attr_name. The search for this method takes place only in the static type of iref.

The attribute name does not need to be specified in uppercase letters. It can contain offsets/lengths, structure component selectors, object component selectors, and class component selectors, to assign parts of the attribute (or referenced objects of the attribute).

Example

Dynamic assignment of an object attribute to a field symbol. The assignment is made via an interface reference variable.

INTERFACE intf.
  CONSTANTS attr TYPE string VALUE `interface attribute`.
ENDINTERFACE.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    INTERFACES intf.
ENDCLASS.

DATA iref TYPE REF TO intf.
iref = NEW demo( ).

ASSIGN iref->('attr') TO FIELD-SYMBOL(<fs>).

cl_demo_output=>display( <fs> ).

Alternative 3

... (class_name)=>(attr_name)

Alternative 4

... (class_name)=>attr

Alternative 5

... class=>(attr_name)

Effect

These forms may be used for all visible static attributes. Both the class and the attribute can be specified dynamically. It is also possible to specify the attribute attr and the class class directly.

If the alternatives with dynamically specified class (class_name) are used, the system first searches for the class and then for the attribute. If the static specification class is used, the system searches for the attribute in the existing class.

The content of attr_name and class_name does not have to be in uppercase letters. attr_name can contain offsets/lengths, structure component selectors, object component selectors, and class component selectors, to assign parts of attributes or referenced objects of attributes. If the class name is specified dynamically and the attribute directly, no offsets/lengths or object component selectors can be specified behind the attribute.

Note

If, in class_name, a class of another program is specified using an absolute type name, this program is loaded into a new additional program group or into the current program group, depending on the program type (if not already loaded). If required, the program constructor is also executed.

Example

Various dynamic assignments of a static class attribute to field symbols.

DATA(class_name) = `cl_abap_browser`.
DATA(attr_name) =  `xlarge`.

ASSIGN (class_name)=>(attr_name) TO FIELD-SYMBOL(<fs1>).
ASSIGN (class_name)=>xlarge TO FIELD-SYMBOL(<fs2>).
ASSIGN cl_abap_browser=>(attr_name) TO FIELD-SYMBOL(<fs3>).

cl_demo_output=>display(
  |(class_name)=>(attr_name):    { <fs1> }\n| &&
  |(class_name)=>xlarge:         { <fs2> }\n| &&
  |cl_abap_browser=>(attr_name): { <fs3> }\n| ).

4. ASSIGN - writable_exp

Syntax

... NEW class( ... )->attr | CAST type( ... )->dobj
  | table_exp  ...

Alternatives:

1. ... NEW class( ... )->attr | CAST type( ... )->dobj

2. ... table_exp

Effect

The operand position after ASSIGN is a result position in which writable expressions can be specified.

Note

Writable expressions can be specified for the memory are but not any other expressions, because only writable expressions can have a non-temporary result. Assigning a temporary data object to a field symbol would not make sense.

Alternative 1

... NEW class( ... )->attr | CAST type( ... )->dobj

Effect

This alternative to specifying the memory area mem_area of the statement ASSIGN assigns the result of a constructor expression

NEW class( ... )->attr

CAST type( ... )->dobj

to the field symbol. The same rules apply as when statically specifying the memory area, but no offsets/lengths can be specified.

Notes

◈ Assigning an attribute attr of an object created using NEW to a field symbol persists this object as long as the field symbol points to the attribute.

◈ In this variant, the constructor expression in question sets the return code sy-subrc, not the statement ASSIGN.
     ◈ If the object is created successfully, the instance operator NEW sets the return code sy-subrc to 0.
     ◈ The casting operator CAST does not set the return code sy-subrc.

Example

Constructor expression with NEW in the specified memory area of the statement ASSIGN. The assignment of the attribute attr to a field symbol persists the object.

CLASS class DEFINITION.
  PUBLIC SECTION.
    DATA attr TYPE string VALUE 'foo'.
ENDCLASS.

START-OF-SELECTION.
  ASSIGN NEW class( )->attr TO FIELD-SYMBOL(<fs>).
  cl_demo_output=>display( <fs> ).

Example

Constructor expression with CAST in the specified memory area of ASSIGN statements.

TYPES: BEGIN OF t_struc,
        col1 TYPE i,
        col2 TYPE i,
       END OF t_struc.

DATA dref TYPE REF TO data.
DATA struc TYPE t_struc.

dref = NEW t_struc( ).

ASSIGN CAST t_struc( dref )->col1 TO FIELD-SYMBOL(<col1>).
ASSIGN CAST t_struc( dref )->col2 TO FIELD-SYMBOL(<col2>).

Alternative 2

... table_exp

Effect

This alternative way of specifying the memory area mem_area of the statement ASSIGN assigns the result of the table expression table_exp or table expression chaining to the field symbol. The result of a table expression in these positions is always a temporary field symbol.

◈ If a single table expression is specified, or a chaining whose last position is a table expression, the entire row that was found is assigned to the field symbol.
◈ If a chaining is specified whose last position is a structure component after a structure component selector, this component is assigned to the field symbol. No offsets/lengths, however, can be specified for the structure component here.

In this variant, the statement ASSIGN sets the return code sy-subrc.

◈ If the specified row is found, sy-subrc is set to 0.

◈ If the row is not found, sy-subrc is set to 4, except when the end of the table is reached in binary searches in sorted tables. In this case, sy-subrc is set to 8.

Unlike when table expressions are used in other ways, the system field sy-tabix is set here in the same way as in a corresponding READ TABLE statement.

If the assignment is not successful, the field symbol keeps its previous state. In this variant, it is therefore not enough just to evaluate the predicate expression <fs> IS ASSIGNED; sy-subrc needs to be checked as well.

In the case of this variant of the statement ASSIGN, the addition CASTING can only be specified in assignments to an existing field symbol and not in inline declarations, and only as a standalone addition. The addition RANGE cannot be specified.

Notes

◈ This variant of the statement ASSIGN can be viewed as a different form of READ TABLE ... ASSIGNING ....
     ◈ More specifically, the value of sy-subrc and sy-tabix is set as in the statement READ TABLE and
     ◈ the addition CASTING cannot be specified after an inline declaration for the field symbol.
Unlike READ TABLE, chainings can be used here to assign components of read rows or rows from nested internal tables.
◈ The constructor operators VALUE and REF used to control the result of the table expression cannot be used here.

◈ If the specified row is not found, an exception is not raised (unlike in other uses of table expressions).

Example

This example works in the same way as the example for READ TABLE ... ASSIGNING .... Here, the READ statement is replaced by an ASSIGN statements and the required component is assigned directly.

DATA: carrid TYPE sflight-carrid,
      connid TYPE sflight-connid,
      fldate TYPE sflight-fldate.

...

DATA sflight_tab TYPE SORTED TABLE OF sflight
                 WITH UNIQUE KEY carrid connid fldate.

SELECT *
       FROM sflight
       WHERE carrid = @carrid AND
             connid = @connid
       INTO TABLE @sflight_tab.

IF sy-subrc = 0.
  ASSIGN sflight_tab[ KEY primary_key COMPONENTS
                          carrid = carrid
                          connid = connid
                          fldate = fldate ]-price
         TO FIELD-SYMBOL(<price>).
  IF sy-subrc = 0.
    <price> = <price> * '0.9'.
  ENDIF.
ENDIF.

◐ ASSIGN - casting_spec

Syntax

... { }
  | { CASTING { { }
              | {TYPE type|(name)}
              | {LIKE dobj}
              | {[TYPE p] DECIMALS dec}
              | {TYPE HANDLE handle} } }
  | { obsolete_casting } ...

Alternatives:

1. ... { }

2. ... CASTING ...

Effect

If specified, casting_spec defines the data type used to handled the memory area mem_area assigned to the field symbol when a statement is given the field symbol in an operand position. Either the addition CASTING can be specified or nothing at all. Outside of classes, the obsolete variants obsolete_casting are also possible.

The following restrictions apply:

◈ In assignments of table expressions, the first alternative can be used without further additions.

◈ The CASTING addition cannot be used for enumerated types. This means that the assigned memory area must not be an enumerated object and the specified data type must not be an enumeration type.

Alternative 1

... { }

Effect

If nothing is specified for casting_spec, the field symbol is given the data type of the data object used in mem_area and the assigned memory area is handled accordingly. This data type must match the typing of the field symbol.

Example

After the first assignment the field symbol has type c of length 3 and after the second assignment the field symbol has type string. Statement DESCRIBE FIELD returns the corresponding values C 3 and g.

FIELD-SYMBOLS <fs> TYPE csequence.

ASSIGN 'xxx' TO <fs> .
DESCRIBE FIELD <fs> TYPE DATA(type)
                    LENGTH DATA(length) IN CHARACTER MODE.
cl_demo_output=>write( type ).

ASSIGN `xxx` TO <fs> .
DESCRIBE FIELD <fs> TYPE type.
cl_demo_output=>display( type ).

Alternative 2

... CASTING ...

Extras:

1. ... { }

2. ... TYPE type|(name)

3. ... LIKE dobj

4. ... [TYPE p] DECIMALS dec

5. ... TYPE HANDLE handle

Effect

If the addition CASTING is used in casting_spec, the memory area is handled as if it had the type specified by CASTING. If CASTING is specified, the field symbol must not be typed with the obsolete addition STRUCTURE of the statement FIELD SYMBOLS. .

Casting can either take place implicitly using the typing of the field symbol or explicitly using one of the additions TYPE, LIKE, or DECIMALS. In explicit castings, the field symbol cannot be specified in full; instead it must be specified generically.

Notes

◈ If the data type determined by CASTING is deep or if deep data objects are stored in the assigned memory area, the deep components must appear with exactly the same type and position in the assigned memory area. In particular, this means that individual reference variables can be assigned to only one field symbol that is typed as a reference variable by the same static type.

◈ The static check of the statement ASSIGN using the addition CASTING is done is such a way that all errors are identified, regardless of the system or platform on which the check is run. A runtime check only checks the data against the current system or the current platform. These checks likewise always take place if the field symbol <fs> is typed with the obsolete addition STRUCTURE of the statement FIELD SYMBOLS..

◈ If the addition CASTING is used, the result of the statement ASSIGN may be platform-dependent. The internal byte order of characters can, for example, be platform-dependent, which becomes obvious during a cast to a byte-like data type.

◈ The memory area mem_area must meet the alignment requirements of the data type specified by the casting. When flat elementary data types are specified, for example, the memory address mem_area must be divisible as follows:

◈ Divisible by 2 for the character-like data types c and n and the date/time types d and t.
◈ By 4 for the numeric data type i.
◈ By 8 for the numeric data types int8, f, and decfloat16.
◈ By 16 for the numeric data type decfloat34.

Addition 1

... { }

Effect

If the addition CASTING is specified without any further additions, the assigned memory area is cast to the type of the field symbol. The field symbol must be either fully typed, or typed with one of the generic built-in ABAP types c, n, p, or x.

Example

Casting of an integer 333 as a byte field. Depending on the byte order, the output is either 4D01 or 014D.

TYPES hex4 TYPE x LENGTH 2.
FIELD-SYMBOLS <fs> TYPE hex4.

ASSIGN 333 TO <fs> CASTING.
cl_demo_output=>display( <fs> ).

Addition 2

... TYPE type|(name)

Effect

Specifies a data types after TYPE explicitly. The name of the type can be specified as follows:

◈ type

Specified directly and statically as type.

◈ (name)

Specified as the content of a character-like data object name that contains the name of a data type (in uppercase) when the statement is executed. The following can be specified for name:

     ◈ Literal or constants

If the data object name is specified as a character literal or as a constant, it can be evaluated statically and the specified type is identified as the used object.

     ◈ Variable

If the data object name is specified as a variable, it is specified only dynamically and the content is not evaluated statically.

When the statement is executed, name is not evaluated until runtime (in both cases).

The assigned memory area is cast to the specified type. The data type specified after TYPE cannot be generic. The exceptions to this rule are the built-in ABAP types c, n, p, and x. However, table categories cannot be specified, nor REF TO.

The field symbol <fs> can only be typed generically and not in full. The specified data type has to match the generic typing of the field symbol, meaning that castings are allowed to specialize the generic typing but not to make more general.

If a generic character-like type c or n is specified after TYPE, the length of the assigned memory area must be a multiple of the length of a character in the memory when the statement is executed.

Example

Casting of a time field to a structured type with three components.

TYPES:
  BEGIN OF time,
    hours   TYPE c LENGTH 2,
    minute  TYPE c LENGTH 2,
    seconds TYPE c LENGTH 2,
  END OF time.

FIELD-SYMBOLS <fs> TYPE any.
ASSIGN sy-timlo TO <fs> CASTING TYPE time.
cl_demo_output=>display( <fs> ).

Example

In the following example, one of the two ASSIGN statements produces a runtime error, since the alignment requirement for the type c is not met. Which of the statements produces the runtime error is not generally defined and depends on the preceding declarations.

DATA hex        TYPE x LENGTH 10.

FIELD-SYMBOLS <fs> TYPE any.

ASSIGN hex+0(4) TO <fs> CASTING type c.
ASSIGN hex+1(4) TO <fs> CASTING type c.

Addition 3

... LIKE dobj

Effect

The following can be specified after LIKE:

◈ A data object dobj based on the rules for TYPES ... LIKE. The assigned memory area is cast to the data type of the data object.

     ◈ A generically typed field symbol.

     ◈ If a memory area is assigned to the field symbol, the data type used to handle the memory area is the object of the cast.
◈ If no memory area is assigned to the field symbol, a standard type produced by the following rules is used:

any, c, clike, csequence, data, and simple produce c with length 1.

decfloat produces decfloat34.

n produces n with length 1.

numeric and p produce p with length 8 and no decimal places.

x and xsequence produce x of the length 1.

Generic table types raise an exception of the class
CX_SY_ASSIGN_CAST_ILLEGAL_CAST.

◈ A generically typed formal parameter.

     ◈ If an actual parameter is assigned to the formal parameter, the data type of this parameter is the object of the cast.
     ◈ If no actual parameter is assigned to an optional formal parameter, its assigned standard type is used.

The field symbol <fs> can only be typed generically and not in full. The specified data type has to match the generic typing of the field symbol, meaning that castings are allowed to specialize the generic typing but not to make more general.

Notes

◈ LIKE can be used to refer to the data objects in its own program, and also to the public attributes of global classes.

◈ The standard type for generically typed field symbols specified after CASTING LIKE displays some differences to the standard type for generic field symbols and formal parameters (length 1 not 4 if any and data are used and no standard type for generic table types).

◈ If a generically typed field symbol is specified after CASTING LIKE, a memory area should be assigned to it when the statement is executed.

Example

Casting of a structure with three components to a time field.

DATA:
  BEGIN OF time,
    hours   TYPE c LENGTH 2 VALUE '11',
    minute  TYPE c LENGTH 2 VALUE '55',
    seconds TYPE c LENGTH 2 VALUE '00',
  END OF time.

FIELD-SYMBOLS <fs> TYPE any.
ASSIGN time TO <fs> CASTING LIKE sy-timlo.
cl_demo_output=>display( <fs> ).

Addition 4

... [TYPE p] DECIMALS dec

Effect

A numeric data object dec must be specified after DECIMALS. The assigned memory area is cast to the data type p, where the number of decimal places is determined by the content of dec. The number of decimal places must not exceed the total number of places. TYPE does not need to be specified for DECIMALS. If TYPE is used, only the data type p (which is used anyway) can be specified.

The field symbol <fs> can only be typed generically and not in full. The specified data type has to match the generic typing of the field symbol, meaning that castings are allowed to specialize the generic typing but not to make more general.

Example

Calculating the quotient from the packed number pack and the field symbol <pack> demonstrates the effect of casting with the addition DECIMALS. Factors between 10 and 100,000,000 are determined. When using <pack> in operand positions, a different value is used than when using pack.

DATA output TYPE TABLE OF string WITH EMPTY KEY.
DATA pack   TYPE p LENGTH 8 DECIMALS 0 VALUE '12345678'.
FIELD-SYMBOLS <pack> TYPE p.

DO 8 TIMES.
  ASSIGN pack TO <pack> CASTING DECIMALS sy-index.
  APPEND CONV string( pack / <pack> ) TO output.
ENDDO.
cl_demo_output=>display( output ).

Addition 5

... TYPE HANDLE handle

Effect

After TYPE HANDLE, a reference variable handle of the static type of the CL_ABAP_DATADESCR class or its subclasses is specified and it points to a type description object of the RTTS. The assigned memory area is cast to the type described by the type description object.

The field symbol <fs> can only be typed generically and not in full. The specified data type has to match the generic typing of the field symbol, meaning that castings are allowed to specialize the generic typing but not to make more general.

The addition CASTING TYPE HANDLE cannot be used with the addition RANGE.

Note

The type description object may have been created using the RTTS methods on existing data objects, or using the definition of a new data type.

Example

Create a type description object for a structure and use it to cast a date field.

DATA(struct) = cl_abap_structdescr=>create(
  VALUE abap_component_tab(
    ( name = 'YEAR'  type = cl_abap_elemdescr=>get_c( p_length = 4 ) )
    ( name = 'MONTH' type = cl_abap_elemdescr=>get_c( p_length = 2 ) )
    ( name = 'DAY'   type = cl_abap_elemdescr=>get_c( p_length = 2 ) )
  ) ).

FIELD-SYMBOLS <fs> TYPE any.
ASSIGN sy-datlo TO  <fs> CASTING TYPE HANDLE struct.
cl_demo_output=>display( <fs> ).

◐ ASSIGN - range_spec

Syntax

... { }
  | {RANGE range}.

Alternatives:

1. ... { }

2. ... RANGE range

Effect

When specified, range_spec defines the area limits within which a memory area can be assigned to the field symbol. Either nothing can be specified here or the addition RANGE .

At the same time, the statement ASSIGN assigns these area limits to the field symbol <fs>. If the field symbol <fs> is itself used in a subsequent ASSIGN statement to specify a memory area mem_area, the assigned memory areas are used for determining the area limits of the field symbol that is being assigned.

Note

The area limits assigned to a field symbol using range_spec only apply to the following ASSIGN statements. In other statements, with the exception of ADD UNTIL, the general rules apply.

Alternative 1

... { }

Effect

If no value is specified for range_spec, the area limits are defined as follows:

◈ If an elementary data object was specified for dobj in mem_area, the memory area of this data object determines the area limits.

◈ If a field symbol was specified for dobj in mem_area, and this field symbol has an elementary data object assigned to it, the field symbol <fs> of the current statement takes on the area limits assigned to this field symbol.

◈ If a structure or a field symbol was specified for dobj in mem_area, and one of these points to a structure, the system checks whether the structure has a character-like initial part (up to the first alignment gap). This then determines the area limits.

If these area limits are exceeded, no memory area is assigned for the static variant of mem_area after the ASSIGN statement. Also, the predicate expression <fs> IS ASSIGNED is incorrect, while sy-subrc is set to 4 in the dynamic variant.

Example

In the first ASSIGN statement, the area limits of the data object text are assigned to <fs1>. In the second ASSIGN statement, <fs2> takes on these limits. From the sixth pass, an attempt is made to assign a larger memory area to <fs2>, which makes the logical expression after IF false.

DATA text TYPE c LENGTH 8 VALUE '12345678'.

FIELD-SYMBOLS: <fs1> TYPE ANY,
               <fs2> TYPE ANY.

ASSIGN text+3(3) TO <fs1>.

DO 8 TIMES.
  ASSIGN <fs1>(sy-index) TO <fs2>.
  IF <fs2> IS ASSIGNED.
    cl_demo_output=>write_text( |{ <fs2> }| ).
  ENDIF.
ENDDO.
cl_demo_output=>display( ).

Alternative 2

... RANGE range

Effect

If the addition RANGE is specified in range_spec, the area limits are defined by the data area of a range data object. range expects a data object of any data type. This object must cover the area limits when the RANGE addition is not specified (see above). If it is established at runtime that range does not cover these area limits, a non-handleable exception is raised.

When the RANGE addition is used, only subareas of the range data object can be assigned to the field symbol. If these area limits are exceeded, no memory area is assigned for the static variant of mem_area after the ASSIGN statement. Also, the predicate expression <fs> IS ASSIGNED is incorrect, while sy-subrc is set to 4 in the dynamic variant.

The addition RANGE cannot be used with the addition CASTING TYPE HANDLE or for assigning table expressions.

Notes

◈ A RANGE addition does not remove the type-specific rules for substring accesses. To enable substring access beyond the field limits of a specified data object dobj, substring access must be possible in principle. In particular, substring access is never possible beyond the limits of a structure, since the character-like initial part is not accessed in this case.

◈ If a structure is specified for range that contains dynamic data objects, they only contribute the internal reference to the data area of the structure. The actual data area of the dynamic data objects is paged out and is ignored by RANGE. This also applies to substructures that are declared as boxed components.

Example

The struc structure is constructed from ten components col1_1, col2_1, ..., col1_5, col2_5. The ASSIGN statement assigns the memory area of two neighboring components to the structure-typed field symbol <sub>, one after the other. Here the memory area is determined by the common name of the first two components comp1 in the structure struc and the specification of INCREMENT. Without the RANGE addition, the WHILE loop would only execute once since it would only be possible to access the memory area of struc-comp1. The RANGE addition causes the loop to be passed five times. The components of the field symbol can be accessed after the assignment.

TYPES: BEGIN OF sub_struc,
          col1 TYPE c LENGTH 10,
          col2 TYPE c LENGTH 10,
       END OF sub_struc.

DATA BEGIN OF struc.
INCLUDE TYPE: sub_struc AS comp1 RENAMING WITH SUFFIX _1,
              sub_struc AS comp2 RENAMING WITH SUFFIX _2,
              sub_struc AS comp3 RENAMING WITH SUFFIX _3,
              sub_struc AS comp4 RENAMING WITH SUFFIX _4,
              sub_struc AS comp5 RENAMING WITH SUFFIX _5.
DATA END OF struc.

FIELD-SYMBOLS <sub> TYPE sub_struc.

struc = VALUE #( col1_1 = 'col1_1'  col2_1 = 'col2_1'
                 col1_2 = 'col1_2'  col2_2 = 'col2_2'
                 col1_3 = 'col1_3'  col2_3 = 'col2_3'
                 col1_4 = 'col1_4'  col2_4 = 'col2_4'
                 col1_5 = 'col1_5'  col2_5 = 'col2_5' ).

DATA inc TYPE i.

WHILE sy-subrc = 0.
  inc = sy-index  - 1.
  ASSIGN struc-comp1 INCREMENT inc TO <sub> CASTING
                                            RANGE struc.
  IF sy-subrc = 0.
    cl_demo_output=>write_data( <sub> ).
  ENDIF.
ENDWHILE.
cl_demo_output=>display( ).

1.5.2.2 UNASSIGN

Syntax

UNASSIGN <fs>.

Effect

This statement initializes the field symbol <fs>. After the statement, the field symbol does not reference a memory area and the predicate expression <fs> IS ASSIGNED is false.

Notes

◈ The statement CLEAR <fs> does not initialize the field symbol and initializes the memory area that is assigned to the field symbol instead.

◈ Unlike the initialization of reference variables, the statement UNASSIGN does not affect Garbage Collector.

Example

A field symbol to which a memory space is assigned is initialized with UNASSIGN.

FIELD-SYMBOLS <fs> TYPE d.

ASSIGN sy-datlo TO <fs>.
IF <fs> IS ASSIGNED.
  cl_demo_output=>write( 'assigned' ).
ENDIF.

UNASSIGN <fs>.
IF <fs> IS NOT ASSIGNED.
  cl_demo_output=>write( 'unassigned' ).
ENDIF.

cl_demo_output=>display( ).

1.6 Lossless Assignments

A lossless assignment is an assignment between incompatible data types in which the conversion is checked to see whether data is lost. Lossless assignments can be performed using the

◈ lossless operator EXACT, in which the argument is converted, according to the
◈ rules for lossless assignments,

into a result of the specified type. This makes it possible to use lossless assignments in all operand positions in which EXACT can be specified.

The real meaning of a lossless assignment is the following method of using a constructor expression with EXACT as the right side of an assignment:

Syntax

destination = EXACT #( source ).
In this case, source is converted to the data type of destination and checked accordingly.

Notes

◈ If the argument of the operator EXACT is an arithmetic expression, a lossless calculation is performed instead of a lossless assignment.

◈ When assigning structures component by component using MOVE-CORRESPONDING, there is an addition EXACT for lossless assignments.

◈ Using the addition EXACT in the obsolete statement MOVE is an obsolete form of lossless assignment.

1.6.1 EXACT - Lossless Operator

Syntax

... EXACT type( [let_exp] dobj ) ...

Effect

A constructor expression with the lossless operator EXACT performs either a lossless assignment or a lossless calculation (depending on the specified argument dobj) and creates a result with the data type type. The following can be specified for type:

◈ A non-generic data type dtype (with the exception of reference types).
◈ The # character for a data type, determined in accordance with the following hierarchy:
     ◈ If the data type required in an operand position is unique and known completely, the operand type is used. The operand type can also be generic and the current type is used at runtime.
     ◈ If the data type cannot be derived from the context, the calculation type decfloat34 is used in lossless calculations and the data type of the argument is used in lossless assignments.

The parentheses must contain precisely one unnamed argument dobj that can be converted to the data type type. dobj is a general expression position. The content of the result is defined as follows:

◈ If the argument dobj is specified as an arithmetic expression, the expression is calculated in accordance with the rules for a lossless assignment and the result (with calculation type decfloat34) is converted to the data type type.
◈ In all other cases, the content of the result is defined by an assignment of the argument in accordance with the associated conversion rules, during which a check is performed in accordance with the rules of lossless assignments.

If data is lost in either case, the corresponding exception is raised. If the argument is compatible with the data type type in a lossless assignment, EXACT does not perform any checks and a syntax check warning is produced. For enumerated types, additional special rules apply.

A LET expression let_exp can be specified (optional) before the argument to define local helper fields.

Note

The lossless operator EXACT replaces the identically named addition of the obsolete statements MOVE and COMPUTE.

Example

Lossless assignment. Here, the exception CX_SY_CONVERSION_ERROR is raised, because the argument contains an invalid value.

TYPES numtext TYPE n LENGTH 255.

TRY.
    DATA(number) = EXACT numtext( '4 Apples + 2 Oranges' ).
  CATCH cx_sy_conversion_error INTO DATA(exc).
    ...
ENDTRY.

Example

Lossless assignment with generic types. The first method call produces a successful assignment; the second raises the exception CX_SY_CONVERSION_EXACT_NOT_SUP. If the assignment is replaced by p2 = EXACT #( + p1 ), a lossless calculation is produced and no exception is raised.

CLASS c1 DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS m1 IMPORTING p1 TYPE data
                     EXPORTING p2 TYPE data.
ENDCLASS.

CLASS c1 IMPLEMENTATION.
  METHOD m1.
    DATA arg TYPE i.
    TRY.
        p2 = EXACT #( p1 ) ##operator.
        cl_demo_output=>display_data( p2 ).
      CATCH cx_sy_conversion_exact_not_sup
            cx_sy_conversion_error INTO DATA(err).
        cl_demo_output=>display_text( err->get_text( ) ).
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

DATA: date TYPE d,
      text TYPE string.

START-OF-SELECTION.

  c1=>m1( EXPORTING p1 = sy-timlo
          IMPORTING p2 = text ).

  c1=>m1( EXPORTING p1 = sy-timlo
          IMPORTING p2 = date ).

Example

Lossless calculation. Here, the exception CX_SY_CONVERSION_ROUNDING is raised, because the calculation is not lossless. The rounded result is assigned to the inline declared variable rounded_result.

TRY.
    DATA(exact_result) = EXACT #( 3 * ( 1 / 3 ) ).
  CATCH cx_sy_conversion_rounding INTO DATA(exc).
    DATA(rounded_result) = exc->value.
ENDTRY.

Exceptions

Handleable Exceptions

CX_SY_CONVERSION_EXACT_NOT_SUP

◈ Cause: Invalid combination of types or lengths.
Runtime error: CONVT_NOT_SUPPORTED

◉ EXACT - Lossless Conversion of Enumerated Types

If the constructor expression CONV is applied to enumerated types, the same rules apply as for the conversion operator CONV:

◈ An enumerated object can be converted to the base type if its enumerated type, as with CONV.
... EXACT base_type( enum_dobj ) ...

◈ A data object that can be converted to the base type of an enumerated type can be converted to the enumerated type, as with CONV.
... EXACT enum_type( dobj ) ...

The conditions of losslessness also apply:

◈ If a data object that can be converted to the base type is converted to the enumerated type, this happens in accordance with the rules of lossless assignment.
◈ If an arithmetic expression is used as the argument of a conversion to the enumerated type, the result must be obtained in accordance with the rules of lossless calculation.

Example

If the conversion operator is used, the argument is converted to the base type c with length 4, with surplus places cut off. If the Lossless operator is used, this results in an exception.

TYPES:
  char8 TYPE c LENGTH 4,
  BEGIN OF ENUM text BASE TYPE char8,
    first  VALUE IS INITIAL,
    second VALUE 'Seco',
    third  VALUE 'Thrd',
  END OF ENUM text.

DATA(result1) = CONV text( `Seco` && `nd` ).

TRY.
    DATA(result2) = EXACT text( `Seco` && `nd` ).
  CATCH cx_sy_conversion_data_loss.
    cl_demo_output=>display( `Oops!` ).
ENDTRY.

If an arithmetic expression is used in the conversion operator, the result is converted to the base type i, with the decimal places cut off. If the Lossless operator is used, this results in an exception.

TYPES:
  BEGIN OF ENUM number,
    n0, n1, n2,
  END OF ENUM number.

DATA(result1) = CONV number( + '1.2' ).

TRY.
    DATA(result2) = EXACT number( + '1.2' ).
  CATCH cx_sy_conversion_rounding.
    cl_demo_output=>display( `Oops!` ).
ENDTRY.

1.6.2 Lossless Assignments - Rules

A lossless assignment checks the assigned content, before its conversion to an incompatible target object, to see whether the assignment is possible without data loss. This means:

◈ the value is checked to see whether it is a valid value for the data type of the source

◈ whether a conversion to the target field can produce a loss of values

◈ whether the value is valid for the target field after conversion

If the source contains a valid value and no values are lost, the conversion is performed in accordance with the associated conversion rules. Otherwise no assignment is made. If it is known statically that one of the prerequisites is not met, a syntax error occurs. If this violation is not identified until the program is executed, a handleable exception is raised. The exception class of this exception is generally a subclass of CX_SY_CONVERSION_ERROR.

The following sections outline which rules are used by the operator EXACT when checking the content of its argument with respect to the data types involved:

◈ Checks elementary data objects

◈ Checks structures

◈ Checks internal tables

If an exception is raised as a result of an invalid or inappropriate value, the VALUE attribute of the exception object is assigned this value in character form; this value is generally also included in the exception text.

No checks are made for:

◈ compatible data types

The content of the argument is passed to the return value without being converted, even if it already contains an invalid value.

◈ Reference Variables

Checks on the content of a reference variable using EXACT are not useful. This is because nothing is converted and the content is either assignable or not assignable, as specified by the assignment rules for reference variables.

A syntax check warning is produced if it is known statically that no checks are performed for the combination of argument and target type, so making EXACT superfluous.

Note

Since lossless checks are only performed for conversions to incompatible data types, it is best to only use the operator EXACT to fill data objects that should only be given valid objects. Other types of assignments that require a start value to be specified in the declaration using VALUE can create invalid values that are then not recognized when a lossless assignment is made to a compatible target field.

1.6.2.1 Checking Elementary Data Objects

In conversions between incompatible elementary data objects, the operator EXACT ensures that no values are lost. Regardless of the value of the argument, the operator EXACT generally does not permit assignments between data objects with the following data types. The is because with these data types, values are always lost when the conversion rules are applied.

◈ From x, xstring to n, d, t, and reversed.
◈ From x, xstring to all numeric data types, and reversed.
◈ From d and t to b and s

Conversions from d to t (and reversed) are not permitted, regardless of whether the operator EXACT is used. Other conversions that are not permitted are conversions in which byte-like fields are converted directly or as an intermediate result to the data type i (or reversed).

If illegal conversions are made, a syntax error occurs, or if the cause cannot be known statically, an exception of the class CX_SY_CONVERSION_EXACT_NOT_SUP is raised. This also occurs with types for which conversions are permitted, but for which a loss of values can be identified regardless of the content, for example, if a conversion is made to fields that are too short.

When permitted conversions are made between incompatible elementary data objects, the operator EXACT checks the contents of the argument at runtime as follows:

◈ Check for valid values in the argument
◈ Check for appropriate values in the target type

If the elementary data types for which no conversion is required are compatible, no checks are performed and no exceptions are raised.

Notes

◈ When the operator EXACT is used, a handleable exception is always raised at runtime when an illegal conversions is made. This occurs in particular when conversions are attempted between d and t. In all other cases, an attempted conversion of this type raises a non-handleable exception.

◈ If an argument contains an invalid or inappropriate value, this is assigned to its return value, even if the operator EXACT is used.

1.6.2.2 Checking Structures

The operator EXACT can only be used to convert structures to compatible structures. Full compatibility is not required for the following deep components, to which the specified requirements apply:

◈ For components that have a reference type, up casts but not down casts are permitted.
◈ For table-like components, it is sufficient if the row type is compatible. This means that the full compatibility needed for table categories and table keys is not required here.

Notes

◈ When the operator EXACT is used, the conversion rules for flat structures are the same as those for deep structures. It is not possible to make conversions between structures and elementary data objects.

◈ If the structured types are compatible, no further checks are made on the content. If the source structure contains invalid data, this data is assigned to a compatible return value, even when the addition EXACT is used.

Example

Unlike simple conversion, lossless assignment leads to a syntax error.

DATA:
  BEGIN OF struc,
    col1 TYPE d,
    col2 TYPE t,
  END OF struc.

DATA(result1) = CONV string( struc ).  "Possible

DATA(result2) = EXACT string( struc ). "Not possible

1.6.2.3 Checking Internal Tables

The operator EXACT checks a table specified as an argument row-by-row against the table-like row type:

◈ If the row types are elementary, each row is checked for elementary data objects.
◈ If the row types are structured, each row is checked for structures.
◈ If the row types are table-like, the check described here is performed. If the inner table is a table with an elementary row type, the row types must be compatible.

The table category and the table key are ignored by the check.

If the check raises an exception due to an invalid value or loss of values, the target table is filled with all rows assigned up to that point.

Notes

◈ If an internal table with elementary row type is converted, this row type does not have to be compatible with the target row type. It only needs to be convertible according to the conditions of EXACT. If an internal table with elementary row type is converted that occurs as an inner table of another table, it is handled like a structure components and the row types must be compatible.

◈ If internal tables are converted using the operator EXACT, only internal tables with elementary row type can raise an exception.

Example

The first seven rows of the internal table itab are assigned to the internal table jtab. When the eighth row is assigned, the exception CX_SY_CONVERSION_NO_DATE occurs.

DATA itab TYPE STANDARD TABLE OF d
          WITH EMPTY KEY.

itab = VALUE #( ( CONV d( '20160101' ) )
                ( CONV d( '20160201' ) )
                ( CONV d( '20160301' ) )
                ( CONV d( '20160401' ) )
                ( CONV d( '20160501' ) )
                ( CONV d( '20160601' ) )
                ( CONV d( '20160701' ) )
                ( CONV d( '20160ß01' ) )
                ( CONV d( '20160901' ) )
                ( CONV d( '20161001' ) )
                ( CONV d( '20161101' ) )
                ( CONV d( '20161201' ) ) ).

DATA jtab TYPE SORTED TABLE OF string
          WITH UNIQUE KEY table_line.

TRY.
    jtab = EXACT #( itab ).
  CATCH cx_sy_conversion_no_date.
ENDTRY.

cl_demo_output=>display( jtab ).

1.7 Special Assignments

This section handles statements for special assignments:

- The assignment operator = for multiple assignments
- UNPACK for a special conversion of packed numbers

1.7.1 destination1 = destination2 = ...

Syntax

destination1 = destination2 = ... = destination = rhs.

Effect

The assignment operator = can be used to perform multiple assignments within a single statement. This statement is the same as:

destination  = rhs
         ... = destination
destination2 = ...
destination1 = destination2.

The same settings can be specified for rhs as for the simple assignment. Only existing variables can be specified for destination, destination1, destination2, but no inline declarations.

Note

Any conversions are performed in every single assignment, which means that a value assigned to a data object on the left side may be converted more than once if the operands have different data types. To assign the value of lhs to different data objects with one conversion each, multiple statements are needed.

Example

After the assignments, all data objects in question are given the name "Hugo".

DATA: name  TYPE string,
      name1 TYPE string,
      name2 TYPE string,
      name3 TYPE string.

name = `Hugo`.

name3 = name2 = name1 = name.

1.7.2 UNPACK

Syntax

UNPACK source TO destination.

Effect

This statement converts the content of the data object source in accordance with special rules and assigns the converted content to the data object destination. source expects the data type p of length 16 without decimal places. Operands of data type decfloat16 or decfloat34 cannot be used. The data type of destination must be character-like and flat.

The conversion is performed according to the following rules:

◈ If the data type of source is not of type p with length 16 and without decimal places, the content of source is converted to this data type. Contrary to the rules described in Conversion Rules for Elementary Data Types, any decimal separator in source is completely ignored.

◈ The digits of the intermediate result are assigned right-justified to the data object destination and without a sign. Any surplus places in destination are padded with zeroes on the left. If the length of destination is not sufficient, the assigned value is truncated on the left.

Notes

◈ The function of the statement UNPACK is based on the fact that the BCD representation of a place corresponds to the second half byte of code of a digit in most character representations. This transformation is generally known as "Unpacking".

◈ The statement PACK used for packing is obsolete and can be replaced by a regular assignment.

Example

After the assignments are made, char1 and char2 contain the values "123,456" and "0000123456".

DATA: pack  TYPE p LENGTH 8 DECIMALS 3 VALUE '123.456',
      char1 TYPE c LENGTH 10,
      char2 TYPE c LENGTH 10.

char1 = pack.
UNPACK pack TO char2.

Exceptions

Handleable Exceptions

CX_SY_CONVERSION_NO_NUMBER

◈ Cause: Source field cannot be interpreted as a number
Runtime error: CONVT_OVERFLOW

CX_SY_CONVERSION_OVERFLOW

◈ Cause: Overflow during conversion (type p)
Runtime error: BCD_OVERFLOW

Non-Handleable Exceptions

◈ Cause: Source field (type p) contains an incorrect BCD format
Runtime error: BCD_BADDATA

1.8 Initializations

The following statements initialize data objects, which means they set the content of a data object to an initial value:

◈ CLEAR
◈ FREE

Initialization does not delete the data objects in question. Data objects created by declarative statements are only deleted from the memory when leaving a program, together with the internal session. Objects created dynamically by the statement CREATE are deleted by Garbage Collector. The initialization of reference variables can, however, cause Garbage Collector to delete the referenced objects.

Note

REFRESH is an obsolete statement used to initialize internal tables.

1.8.1 CLEAR

Syntax

CLEAR dobj [ {WITH val [IN {CHARACTER|BYTE} MODE] }
           | {WITH NULL} ].

Addition:

... WITH val [IN {CHARACTER|BYTE} MODE]

Effect

Without the optional additions, dobj is assigned the type-dependent initial value. In the case of dobj, this is a result position, which means either a variable or a writable expression ´can be specified.

◈ Elementary data types are assigned initial values in accordance with the tables of built-in ABAP types.

◈ Enumerated variables are assigned to initial values in accordance with the elementary base type.

◈ Reference variables are assigned the null reference.

◈ Structures are set to their initial values component by component.

◈ All rows in an internal table are deleted. This frees up the memory space required for the table, except for the initial memory requirement (see INITIAL SIZE). The statement FREE is used to release the memory space occupied by the rows of internal tables.

The optional additions enable dobj to be filled with values other than the initial value.

Notes

◈ If dobj is an internal table with a header line, dobj[] must be specified to delete the rows, otherwise only the header line is deleted.

◈ In the case of CLEAR, the initial memory requirements of an internal table are not released, which can have a positive effect on performance when inserting new rows in the internal table. The statement FREE is required only if it is as much memory as possible really needs to be released.

Example

After initialization with CLEAR, the internal table itab no longer contains any rows.

DATA itab TYPE STANDARD TABLE OF i WITH EMPTY KEY.

itab = VALUE #( FOR i = 1 UNTIL i > 10 ( i ) ).
ASSERT lines( itab ) = 10.

CLEAR itab.
ASSERT lines( itab ) = 0.

Addition

... WITH val [IN {CHARACTER|BYTE} MODE]

Effect

If the addition WITH val is used and CHARACTER or BYTE MODE specified, all places in dobj are replaced either with the first character or the first byte in val. In val a functional operand position is involved. If dobj is of the type string or xstring, the string is processed in its current length.

If the MODE addition is not specified, the addition IN CHARACTER MODE applies. Depending on the addition, the data object dobj must be either character-like or byte-like. Depending on the addition, the operand val must be character-like or byte-like and have the length 1. If this is not the case, a syntax error occurs or a non-handleable exception is raised.

Note

If the obsolete addition WITH NULL is used, all bytes of a flat data object can be replaced by hexadecimal 0 outside classes.

Example

The byte string hexstring as assigned a specific byte value over the entire current length.

DATA: hexstring TYPE xstring,
      hex       TYPE x LENGTH 1 VALUE 'FF'.
...
hexstring = '00000000'.
...
CLEAR hexstring WITH hex IN BYTE MODE.

cl_demo_output=>display( hexstring ).

Exceptions

Non-Handleable Exceptions

Cause: Field val does not have length 1 for variant CLEAR fld WITH val IN BYTE MODE.
Runtime error: CLEAR_VALUE_BYTEMODE_WRONG_LEN

◈ Cause: Field val does not have length 1 for variant CLEAR fld WITH val [IN CHARACTER MODE].
◈ Runtime error: CLEAR_VALUE_WRONG_LENGTH

1.8.2 FREE

Syntax

FREE dobj.

Effect

The statement FREE deletes all rows from an internal table and releases the memory area that the rows occupied.

On other data objects, FREE works like the statement CLEAR.

If dobj is a structure with table-like components, the memory of all the table-like components is released.

Notes

◈ If dobj is an internal table with a header line, FREE is only applied to the table body and not the header line.

◈Unlike CLEAR, the initial memory area (see INITIAL SIZE) remains unoccupied when FREE is used. This can become necessary when there is a lack of memory.

◈In general, FREE should be used only if the entire memory is to be released in full and the internal table is no longer needed (or at the least not filled again right away).

Example

At the breakpoints in the memory analysis of the ABAP debugger you can see that FREE frees up more allocated bound memory than CLEAR.

DATA itab TYPE STANDARD TABLE OF i WITH EMPTY KEY
          INITIAL SIZE 10000.

itab = VALUE #( FOR i = 1 UNTIL i > 10000 ( i ) ).

CLEAR itab.
BREAK-POINT.

FREE itab.
BREAK-POINT.

2. Numeric Calculations


Usually, numeric calculations are performed using arithmetic expressions that can be used in operand positions and, in particular, on the right side of the assignment operator=. Numeric data objects (data objects that can be converted to numeric data types) and numeric functions that have a numeric return value can be used as operands of an arithmetic expression. As well as the numeric functions, some system classes are also available. Finally, four standalone statements can be used for the basic arithmetic operations.

◈ Numeric Data Types

ABAP supports the numeric data types i, int8, p, decfloat16, decfloat34, and f, plus the internal types b and s. The latter cannot be specified directly in programs but are created when the predefined types INT1 or INT2 from ABAP Dictionary are referenced. They are generally used in the same way as the type i and are often converted to i internally.

The numeric data objects are used to handle numeric values and are intended for calculations. Calculations involving fields of the types i, int8, and type f correspond more or less directly to the operating system's machine commands for the current application server. In contrast, calculations involving packed numbers of type p are programmed in the kernel of the ABAP runtime environment and are therefore somewhat slower. Until they are supported by the hardware of the application server, operations using the decimal floating point numbers decfloat16 and decfloat34 run using a library integrated into the ABAP kernel.

◈ Integer Numbers
◈ Packed Numbers
◈ Floating Point Numbers
     ◈ Decimal Floating Point Numbers
     ◈ Binary Floating Point Numbers

The common generic type of the numeric data types is numeric.

Notes

◈ To a great extent, decimal floating point numbers of the types decfloat16 and decfloat34 replace the binary floating point numbers with type f.
◈ The predefined type n (numeric text field) is not a numeric number type, even though its values are digit-only strings. Instead it is a character-like type, not advisable for use in calculations. Typical examples of numeric text fields are account numbers and article numbers, postal codes, and so on.

Integer Numbers

The data types for integer numbers i and int8 have a value range from -2147483648 to +2147483647 for i and -9,223,372,036,854,775,808 to +9,223,372,036,854,775,807 for int8 and only cover integers. Integer numbers with the type i can be specified directly in the program as numeric literals.

Intermediate results in arithmetic expressions of the calculation type i or int8 are stored in helper fields of the type i or int8. Otherwise, type i or int8 arithmetic is similar to performing calculations with type p without decimal places. In particular, division rounds numbers rather than truncating them and an overflow raises an exception. The data types i and int8 are typically used for counters, quantities, indexes, and offsets, as well as time periods.

Note

The data types b, s, i, and int8 provide a complete set of data types for single-byte, 2-byte, 4-byte, and 8-byte integer numbers. The types b and s for short integer numbers, however, cannot be specified directly in ABAP programs. Instead they have to be specified by using the predefined types INT1 and INT2 from ABAP Dictionary. These types do not have their own calculation type.

Example

Typical use of the data type i.

DATA counter TYPE i.
...
counter = counter + 1.
...

Packed Numbers

The data type p for packed numbers has a value range that depends on their length and the number of decimal places. Data objects of type p can be 1 to 16 bytes long, with two places packed into each byte, and one place and the sign packed into the last byte. A packed number consists of the length multiplied by 2 minus 1 digits and can have a maximum of 14 decimal places. Packed numbers are used to implement fixed point numbers. The decimal places in a packed number are an attribute of its data type and are fixed for this type.

Packed numbers with decimal places cannot be specified directly in the program. Instead, character literals must be used whose content can be interpreted as a packed number, meaning that it represents a number in mathematical or commercial notation. Scientific notation is not permitted unless it can be interpreted as a mathematical notation.

Helper fields for intermediate results in arithmetic expressions of calculation type p are always 16 bytes long and can thus hold up to 31 places. Before an overflow occurs, an arithmetic expression is calculated again with helper fields that are twice as large or 63 places. In the case of comparisons between packed numbers, the operand with fewer decimal places is also converted into a helper field of this type, though an overflow occurs if the sum of the integer digits and decimal places exceeds 31.

If packed numbers are used, the program attribute must always be set to fixed point arithmetic since only this setting ensures that the decimal point is calculated correctly. Otherwise, all numbers are specified as integers and all subtotals are rounded up to the next integer. If fixed point arithmetic is not configured, the decimal places defined for the number only appear in dynpro output or when formatting with WRITE [TO].

Calculations using calculation type p are performed using fixed point arithmetic. In other words, a calculation is performed "commercially", similarly to using a pocket calculator or paper and pencil. Type p is typically used for values such as lengths, weights, and sums of money.

Note

The number of decimal places in a packed number should be no greater than the number of digits; otherwise the decimal separator might be outside the sequence of digits. A packed number that has more decimal places than places can raise exceptions when converted to external formats such as data types of the database in Open SQL or in serializations to asXML.

Example

Typical use of the data type p.

DATA price TYPE p LENGTH 16 DECIMALS 2.

Floating Point Numbers

In floating point numbers, the number of decimal places is a part of the value and not a part of the data type.

Decimal Floating Point Numbers

The data types for decimal floating point numbers are decfloat16 and decfloat34. The value range is 1E385(1E-16 - 1) to -1E-383, 0, +1E-383 to 1E385(1 - 1E-16) for decfloat16 and 1E6145(1E-34 - 1) to -1E-6143, 0, +1E-6143 to 1E6145(1 - 1E-34) for decfloat34. The maximum precision is 16 places or 34 places, respectively. As well as its value, a decimal floating point number has a scale and a precision. These properties are not relevant for calculations and comparisons of values, but are used in conversion rules and for formatting output.

Decimal floating point numbers with decimal places or exponents cannot be specified directly in the program. Instead, character literals must be used whose content can be interpreted as a packed number, meaning that it represents a number in mathematical, scientific, or commercial notation.

Arithmetic expressions with decimal floating point numbers always have the calculation type decfloat34. Each calculation is made with decimal floating point arithmetic. Decimal floating point numbers are the best choice if precision and a large range of values are of importance. They do not have the disadvantages of binary floating point numbers described below. These binary floating point numbers cannot represent each decimal number in their value range exactly. Decimal floating point numbers have a much large value range and a higher level of precision than packed numbers.

By using the lossless operator EXACT, it is possible to force a lossless calculation for decimal floating point numbers under certain circumstances. No rounding is permitted in lossless calculations and raises an exception.

Internally, decimal floating point numbers are represented by a 16-digit or 34-digit decimal mantissa and a decimal exponent. The exponent is between -383 and +384 or -6143 and + 6144, respectively. Apart from potential roundings in assignments and calculations, the effects discussed below for binary floating point numbers are not observed. This is because every 16-digit or 34-digit decimal number can be represented exactly.

Binary Floating Point Numbers

The data type for binary floating point numbers, f, has a value range of 2.2250738585072014E-308 to 1.7976931348623157E+308, positive as well as negative, and the number 0, with an accuracy of at least 15 places. 17 places are represented in ABAP. Integers can be represented exactly up to an absolute value of 2**53, which is equivalent to 9,007,199,254,740,992. Any larger numbers are rounded.

Binary floating point numbers cannot be specified directly in the program. Instead, character literals must be used whose content can be interpreted as floating point numbers, meaning that it represents a number in scientific notation. Mathematical or commercial notation is not permitted unless it can be interpreted as scientific notation.

Arithmetic expressions with calculation type f are performed using binary floating point arithmetic. The following features of binary floating point arithmetic must be considered here.

Internally, binary floating point numbers are stored separately, each in two parts. This can produce unexpected results despite the high degree of intrinsic accuracy. These occur mainly when performing conversions from and to type f.

◈ For example, the number 1.5 can be represented exactly in this notation since 1.5 = 1*2**0 + 1*2**(-1), but the number 0.15 can only be represented approximately by the number 0.14999999999999999. If 0.15 is rounded up to 1 valid digit, the result is 0.1 rather than 0.2 as might be expected. On the other hand, the number 1.5E-12 is represented by the number 1.5000000000000001E-12, which would be rounded up to 2E-12.

◈ In a further real-life example, 7.27% of 73050 is to be calculated and rounded to 2 decimal places. The intermediate result is 5.3107349999999997E+03, since the correct result, 5310.735, cannot be represented exactly in two parts with 53 bits. (If the hardware cannot represent a real number exactly, it uses the next representable binary floating point number.) After rounding, the result is 5310.73, rather than 5310.74 as might be expected.

The ABAP runtime environment always calculates commercially and not numerically, like the underlying machine arithmetic. According to the rounding algorithm of the latter, the end digit 5 must always be rounded to the nearest even number (not the next largest number), namely from 2.5 to 2 and from 3.5 to 4.

Note also that multiplication using powers of 10 (positive or negative) is not an exact operation.

◈ Example: Although it can be represented exactly in two parts, a binary floating point number f of value 100.5, after the operation
f = f / 100 * 100.
has the value 100.49999999999999.

As well as rounding errors, the restricted number of places for the mantissa can cause trailing digits to be lost.

◈ Example: 1 - 1.0000000000000001 produces zero.

This means that the final places in binary floating point arithmetic are not reliable. In particular, it is not usually worth testing two binary floating point numbers a and b for equality. Instead, it is best to check whether the relative difference abs((a - b)/a) is less than a predefined limit, such as 10**(-7).

Ultimately, the display and therefore the value of a binary floating point number stored in a database can be platform-dependent.

Note

To assign numeric values to text fields and text strings, it is often better to use the statement WRITE ... TO or embedded expressions in string templates with the associated formatting options instead of using a conversion.

Example

The result of the calculation in result1 is 0.81499999999999995. The result of the calculation in result2, on the other hand, is the expected value 0.815.

DATA: result1 TYPE f,
      result2 TYPE decfloat34.

result1 = 815 / 1000.
result2 = 815 / 1000.

cl_demo_output=>display( |Binary  floating point: { result1 }\n| &&
                         |Decimal floating point: { result2 }\n| ).

Floating Point Numbers, Arithmetic Calculations


The example demonstrates arithmetic calculations with floating point numbers.

Source Code

REPORT demo_floating_point_numbers.

DATA ok_code       TYPE sy-ucomm.
DATA op1           TYPE c LENGTH 46.
DATA op2           TYPE c LENGTH 46.
DATA operator      TYPE c LENGTH 2.
DATA res_df34      TYPE c LENGTH 46.
DATA res_df16      TYPE c LENGTH 46.
DATA res_f         TYPE c LENGTH 46.
DATA exct_34       TYPE c LENGTH 4.
DATA exct_16       TYPE c LENGTH 4.
DATA continue_flag TYPE c LENGTH 1.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-DATA: df34_1   TYPE decfloat34,
                df34_2   TYPE decfloat34.
    CLASS-METHODS: start,
                   main,
                   init_operator,
                   check_operand IMPORTING operand TYPE  c
                                 CHANGING  df34    TYPE  decfloat34,
                   handle_user_command IMPORTING ucomm TYPE sy-ucomm.
  PRIVATE SECTION.
    CLASS-DATA: operator_list TYPE vrm_values,
                df34_r        TYPE decfloat34,
                df16_r        TYPE decfloat16,
                f1            TYPE f,
                f2            TYPE f,
                f_r           TYPE f.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
*   Calculate decfloat34
    TRY.
        CASE operator.
          WHEN ' '.
            df34_r = EXACT #( + df34_1 ).
          WHEN '+'.
            df34_r = EXACT #( df34_1 + df34_2 ).
          WHEN '-'.
            df34_r = EXACT #( df34_1 - df34_2 ).
          WHEN '*'.
            df34_r = EXACT #( df34_1 * df34_2 ).
          WHEN '/'.
            df34_r = EXACT #( df34_1 / df34_2 ).
          WHEN '**'.
            df34_r = df34_1 ** df34_2.
            CLEAR exct_34.
        ENDCASE.
      CATCH cx_sy_conversion_overflow.
        res_df34 = text-ove.
        CLEAR exct_34.
      CATCH cx_sy_arithmetic_overflow.
        res_df34 = text-ove.
        CLEAR exct_34.
      CATCH cx_sy_conversion_rounding INTO DATA(exrnd).
        df34_r = exrnd->value.
        exct_34 = text-noe.
    ENDTRY.
    IF res_df34 = ' '.
      res_df34 = |{ df34_r ALIGN = LEFT STYLE = SCALE_PRESERVING }|.
      continue_flag = 'X'.
    ENDIF.
*   Calculate decfloat16
    TRY.
        CASE operator.
          WHEN ' '.
            df16_r = EXACT #( df34_1 ).
          WHEN '+'.
            df16_r = EXACT #( df34_1 + df34_2 ).
          WHEN '-'.
            df16_r = EXACT #( df34_1 - df34_2 ).
          WHEN '*'.
            df16_r = EXACT #( df34_1 * df34_2 ).
          WHEN '/'.
            df16_r = EXACT #( df34_1 / df34_2 ).
          WHEN '**'.
            df16_r = df34_1 ** df34_2.
            CLEAR exct_16.
        ENDCASE.
      CATCH cx_sy_conversion_overflow.
        res_df16 = text-ove.
        CLEAR exct_16.
      CATCH cx_sy_arithmetic_overflow.
        res_df16 = text-ove.
        CLEAR exct_16.
      CATCH cx_sy_conversion_rounding INTO exrnd.
        df16_r = exrnd->value.
        exct_16 = text-noe.
    ENDTRY.
    IF res_df16 = ' '.
      res_df16 = |{ df16_r ALIGN = LEFT STYLE = SCALE_PRESERVING }|.
    ENDIF.
*   Calculate type f
    TRY.
        f1 = df34_1.
        f2 = df34_2.
        CASE operator.
          WHEN ' '.
            f_r = f1.
          WHEN '+'.
            f_r = f1 + f2.
          WHEN '-'.
            f_r = f1 - f2.
          WHEN '*'.
            f_r = f1 * f2.
          WHEN '/'.
            f_r = f1 / f2.
          WHEN '**'.
            f_r = f1 ** f2.
        ENDCASE.
      CATCH cx_sy_conversion_overflow.
        res_f = text-ove.
      CATCH cx_sy_arithmetic_overflow.
        res_f = text-ove.
    ENDTRY.
    IF res_f = ' '.
      res_f = |{ f_r ALIGN = LEFT }|.
    ENDIF.
  ENDMETHOD.
  METHOD start.
    CALL SCREEN 100.
  ENDMETHOD.
  METHOD check_operand.
    DATA rc    TYPE i.
    CLEAR exct_34 .
    CLEAR exct_16 .
    TRY.
        cl_abap_decfloat=>read_decfloat34(
        EXPORTING  string = operand
        IMPORTING  value  = df34
                   rc     = rc  ).
        IF rc = cl_abap_decfloat=>parse_inexact OR
           rc = cl_abap_decfloat=>parse_underflow.
          exct_34 = text-noe.
          exct_16 = text-noe.
        ENDIF.
      CATCH cx_sy_conversion_overflow.
        MESSAGE e002(sy) WITH |OVERFLOW ({ operand } TOO LARGE)|.
      CATCH cx_abap_decfloat_parse_err INTO DATA(expe).
        MESSAGE expe TYPE 'E'.
    ENDTRY.
  ENDMETHOD.
  METHOD init_operator.
    DATA name  TYPE vrm_id VALUE 'OPERATOR'.
    IF operator_list IS INITIAL.
      operator_list = VALUE #( ( )
                               ( key  = '+'  text = '+' )
                               ( key  = '-'  text = '-' )
                               ( key  = '*'  text = '*' )
                               ( key  = '/'  text = '/' )
                               ( key  = '**' text = '**' ) ).
    ENDIF.
    CALL FUNCTION 'VRM_SET_VALUES'
      EXPORTING
        id     = name
        values = operator_list.
  ENDMETHOD.
  METHOD handle_user_command.
    CASE ucomm.
      WHEN 'EXECUTE'.
        CLEAR: res_df34, res_df16, res_f,
               continue_flag.
        IF exct_34 = ' '.
          exct_34 = text-yes.
        ENDIF.
        IF exct_16 = ' '.
          exct_16 = text-yes.
        ENDIF.
        demo=>main( ).
      WHEN 'REFRESH'.
        CLEAR: op1, op2, operator,
               res_df34, res_df16, res_f,
               exct_34, exct_16,
               continue_flag.
      WHEN 'CONTINUE'.
        op1 = |{ df34_r COUNTRY = '   ' }|.
        CLEAR: op2, operator,
               res_df34, res_df16, res_f,
               exct_34, exct_16,
               continue_flag.
      WHEN OTHERS.
        "do nothing
    ENDCASE.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>start( ).

MODULE status_100 OUTPUT.
  IF continue_flag = 'X'.
    SET PF-STATUS 'STATUS_100'.
  ELSE.
    SET PF-STATUS 'STATUS_100' EXCLUDING 'CONTINUE'.
  ENDIF.
  SET TITLEBAR  'TITLE_100'.
  demo=>init_operator( ).
ENDMODULE.

MODULE cancel INPUT.
  LEAVE PROGRAM.
ENDMODULE.

MODULE user_command_100 INPUT.
  demo=>handle_user_command( ok_code ).
  CLEAR ok_code.
ENDMODULE.

MODULE check_op1 INPUT.
  demo=>check_operand(
    EXPORTING operand = op1
    CHANGING  df34    = demo=>df34_1 ).
ENDMODULE.

MODULE check_op2 INPUT.
  demo=>check_operand(
    EXPORTING operand = op2
    CHANGING  df34    = demo=>df34_2 ).
ENDMODULE.

Description

Two operands and various arithmetic operators can be entered. The calculation is performed for the data types decfloat34, decfloat16, and f. For decimal floating point numbers, the losslessness of the calculation is checked using the lossless operator EXACT.

◈ arith_exp - Arithmetic Expressions

Syntax

... [+|-] operand1
    [{+|-|*|/|DIV|MOD|**} [+|-] operand2
    [{+|-|*|/|DIV|MOD|**} [+|-] operand3
    ... ]] ...

Effect

An arithmetic expression expresses a calculation. The result of an arithmetic expression is a numeric value of the calculation type assigned to the arithmetic expression. In an arithmetic expression arith_exp, the arithmetic operators +, -, *, /, DIV, MOD, or ** can be used to join an operand operand1 with one or more operands operand2, operand3 ... Brackets are also possible. The calculation is made in accordance with calculation rules specified by the calculation type in question.

Arithmetic expressions can occur in the reading positions of certain statements, in particular on the right side of an assignment with the assignment operator =. When used as the operand of the lossless operator EXACT, an arithmetic expression can be checked for a lossless calculation.

The operand positions operand are general expression positions, which means that numeric data objects, built-in functions, functional methods, or compound arithmetic expressions can be specified. The arithmetic operators +, -, *, /, DIV, MOD, and ** join two adjacent operands. When the expression is evaluated, a numeric value is calculated and joined with the next adjacent operand. The priority of this join depends on the operators used.

Each operand can be preceded by the signs + or -, in any order and separated by one or more blanks. The effect of using a sign is the same as specifying the expression +1 * or -1 * in its place, which means that a sign has the same priority as a multiplication.

If functional methods or character-like expressions are specified as operands, they are specified from left to right and from inside to outside before the remainder of the expression is evaluated. The return values are buffered to be used in the corresponding operand positions. Here, character-like processing functions and string expressions are only possible as arguments of description functions.

Notes

◉ Note whether the value of a data object that is also used as an operand is changed in a specified functional method. Even if an operand of this type precedes the functional method, its value will always be changed by the method before it is evaluated.

◉ Arithmetic expressions, string expressions, and bit expressions cannot be combined. Built-in functions that are used as operands for arithmetic expressions can, however, contain string expressions or bit expressions as arguments.

◉ If an arithmetic expression is specified as an argument of one of the overloaded numeric functions, the entire function works like an arithmetic expression.

Example

The following program excerpt computes the hyperbolic sine with the Eulerian formula in the explicit calculation type decfloat34 and with the built-in function sinh in calculation type f and displays the difference.

REPORT kellerh_test1.

TYPES:
  BEGIN OF line,
    x       TYPE string,
    result1 TYPE string,
    result2 TYPE string,
    diff    TYPE string,
  END OF line.
DATA
  output TYPE TABLE OF line WITH EMPTY KEY.

DO 2001 TIMES.
  TRY.
      DATA(x) = sy-index - 1001.
      DATA(result1) = CONV decfloat34(
                             ( exp( x ) - exp( -1 * x ) ) / 2 ).
      DATA(result2) =  sinh( x ).
      DATA(diff)    =  abs( result1 - result2 ).
      IF diff <> 0.
        APPEND VALUE #(
          x       = |{ x }|
          result1 = |{ result1 STYLE = SCIENTIFIC }|
          result1 = |{ result1 STYLE = SCIENTIFIC }|
          diff    = |{ diff    STYLE = SCIENTIFIC }| ) TO output.
      ENDIF.
    CATCH cx_sy_conversion_overflow cx_sy_arithmetic_overflow.
  ENDTRY.
ENDDO.
cl_demo_output=>display( output ).

Exceptions

Handleable Exceptions

CX_SY_ARG_OUT_OF_DOMAIN

◉ Cause: Illegal argument in powers
Runtime error: COMPUTE_POW_DOMAIN

CX_SY_ARITHMETIC_OVERFLOW

◉ Cause: Overflow in arithmetic operation (type p)
Runtime error: BCD_OVERFLOW

◉ Cause: Overflow in arithmetic operation (all operands type p)
Runtime error: COMPUTE_BCD_OVERFLOW

◉ Cause: Overflow in cosh
Runtime error: COMPUTE_COSH_OVERFLOW

◉ Cause: Overflow or underflow in exp
Runtime error: COMPUTE_EXP_RANGE

◉ Cause: Overflow in addition (type decfloat)
Runtime error: COMPUTE_DECFLOAT_ADD_OVERFLOW

◉ Cause: Overflow in subtraction (type decfloat)
Runtime error: COMPUTE_DECFLOAT_SUB_OVERFLOW

◉ Cause: Overflow in multiplication (type decfloat)
Runtime error: COMPUTE_DECFLOAT_MUL_OVERFLOW

◉ Cause: Overflow in division (type decfloat)
Runtime error: COMPUTE_DECFLOAT_DIV_OVERFLOW

◉ Cause: Overflow in division (type decfloat)
Runtime error: COMPUTE_DECFLOAT_DIV_OVERFLOW

◉ Cause: Overflow in division (type f)
Runtime error: COMPUTE_FLOAT_DIV_OVERFLOW

◉ Cause: Overflow in subtraction (type f)
Runtime error: COMPUTE_FLOAT_MINUS_OVERFLOW

◉ Cause: Overflow in addition (type f)
Runtime error: COMPUTE_FLOAT_PLUS_OVERFLOW

◉ Cause: Overflow in multiplication (type f)
Runtime error: COMPUTE_FLOAT_TIMES_OVERFLOW

◉ Cause: Overflow in |-2147483648|
Runtime error: COMPUTE_INT_ABS_OVERFLOW

◉ Cause: Integer overflow in division
Runtime error: COMPUTE_INT_DIV_OVERFLOW

◉ Cause: Integer overflow after subtraction
Runtime error: COMPUTE_INT_MINUS_OVERFLOW

◉ Cause: Integer overflow while adding
Runtime error: COMPUTE_INT_PLUS_OVERFLOW

◉ Cause: Integer overflow in multiplication
Runtime error: COMPUTE_INT_TIMES_OVERFLOW

◉ Cause: Invalid call of log10
Runtime error: COMPUTE_LOG10_ERROR

◉ Cause: Invalid call of log
Runtime error: COMPUTE_LOG_ERROR

◉ Cause: Overflow or underflow in powers
Runtime error: COMPUTE_POW_RANGE

◉ Cause: Overflow in sinh
Runtime error: COMPUTE_SINH_OVERFLOW

◉ Cause: Overflow in function round or rescale
Runtime error: COMPUTE_ROUND_RESCALE_OVERFLOW

CX_SY_CONVERSION_NO_NUMBER

◉ Cause: Operand cannot be interpreted as a number
Runtime error: CONVT_NO_NUMBER

CX_SY_CONVERSION_OVERFLOW

Cause: Overflow in arithmetic operation (type p, with specified length)
Runtime error: BCD_FIELD_OVERFLOW

◉ Cause: Operand too big or (interim) result too big
Runtime error: CONVT_OVERFLOW

CX_SY_PRECISION_LOSS

◉ Cause: Result of cos not exact
Runtime error: COMPUTE_COS_LOSS

◉ Cause: Result of sin not exact
Runtime error: COMPUTE_SIN_LOSS

◉ Cause: Result of tan not exact
Runtime error: COMPUTE_TAN_LOSS

CX_SY_UNSUPPORTED_FUNCTION

◉ Cause: Invalid function for calculation type decfloat34
Runtime error: COMPUTE_UNSUPPORTED_DECF_FUNC

CX_SY_ZERODIVIDE

◉ Cause: Division by 0 (type p)
Runtime error: BCD_ZERODIVIDE

◉ Cause: Division by 0 (type f)
Runtime error: COMPUTE_FLOAT_ZERODIVIDE

◉ Cause: Division by 0 (types (b, s), i, int8)
Runtime error: COMPUTE_INT_ZERODIVIDE

◉ Cause: Division by 0 (type decfloat)
Runtime error: COMPUTE_DECFLOAT_ZERODIVIDE

Non-Handleable Exceptions

◉ Cause: p field does not contain the correct BCD format
Runtime error: BCD_BADDATA

◉ Cause: p field does not contain a correct sign
Runtime error: BCD_NO_SIGN

◈ Numerical Functions

Numerical functions belong to the predefined functions. The main argument of a numerical function must represent a numerical value. The data type of the return value is determined either by the argument of the function (overloaded functions), or by the function.

Outside of an arithmetic expression, the main argument of a numerical function must be a single, numeric data object. Within an arithmetic expression, the following are possible as main arguments of a numerical function:

◉ Numeric data object
◉ Arithmetic expression
◉ Predefined function
◉ Functional method

The handleable exceptions that can be raised during the computation of a numerical function are subclasses of the classes CX_SY_ARITHMETIC_ERROR and CX_SY_CONVERSION_ERROR.

The numerical functions are divided into:

◉ abs, sign, ceil, floor, trunc, frac

The following table shows the general numeric functions for a single unnamed argument with any numeric data type. These functions are overloaded with the effect that the return value can have different numeric types.

Syntax

... func( arg ) ...

Effect

The argument of a general numeric function must be an individual data object outside an arithmetic expression, and can itself be a numeric expression within an arithmetic expression.

Effect of the general numeric functions.

Function func Return Value 
abs Absolute value of argument arg 
sign  Sign of argument arg: -1, if the value of arg is negative; 0, if the value of arg is 0; 1, if the value of arg is positive 
ceil  Smallest integer that is not less than the value of the argument arg is 
floor  Largest integer that is not greater than the value of the argument arg is 
trunc  Value of the integer part of the argument arg; negative if arg is negative 
frac  Value of the decimal places of the argument arg; negative if arg is negative

The following applies to the data type of the return value:

◉ With the exception of an arithmetic expression, the data type of the argument determines the data type of the return value.

◉ Within an arithmetic expression, the argument of the function contributes to the calculation type of the entire expression and the function is calculated using the calculation type. If the argument itself is an arithmetic expression, its operands contribute to the entire calculation type and the argument is also calculated with this type.

◉ If the argument arg is a numeric expression, the function works like an arithmetic operator and it is handled in its operand position like an arithmetic expression.

If the argument of a numeric function outside of an arithmetic expression does not have a numeric data type i, int8, p, decfloat16, decfloat34, or f, its data type determines the type of return value as follows:

◉ d and t give i
◉ c, n and string give p
◉ x and xstring givei

Before the calculation of the function, the argument is converted into the respective type.

Note

The functions described here are some of the functions that can be used in the obsolete extended functional operand positions, even if their argument is a single data object.

Example

The following assertion should always be correct.

DATA pack TYPE p LENGTH 8 DECIMALS 4 VALUE '-1234.5678'.
cl_demo_input=>request( CHANGING field = pack ).

ASSERT pack  = trunc( pack ) + frac( pack ).

Executable Example

Numeric Functions


This example demonstrates the use of general numerical functions.

Source Code

REPORT demo_numerical_function.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA n TYPE decfloat16.
    DATA m TYPE decfloat16 VALUE '-5.55'.

    DATA(out) = cl_demo_output=>new( ).

    n = abs( m ).
    out->write( |ABS: { n }| ).
    n = sign( m ).
    out->write( |SIGN: { n }| ).
    n = ceil( m ).
    out->write( |CEIL: { n }| ).
    n = floor( m ).
    out->write( |FLOOR: { n }| ).
    n = trunc( m ).
    out->write( |TRUNC: { n }| ).
    n = frac( m ).
    out->write( |FRAC: { n }| ).

    out->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The output of the program shows how each of the general numerical functions affects the number -5.55.

◉ ipow - Integer Power Function

Syntax

... ipow( base = arg exp = n ) ...

Effect

This function raises the argument arg passed to base to the exponent n passed to exp. The arguments arg and n are numeric expression positions. Any numeric data object can be specified for arg. n expects the type i and exponents of other types are converted to i. If the argument arg has the value 0, the value of the exponent n must be greater than or equal to 0.

The function ipow is overloaded with the effect that the return value can have different numeric types.

◉ With the exception of an arithmetic expression, the data type of the argument arg determines the data type of the return value.
◉ Within an arithmetic expression, the argument arg contributes to the calculation type of the entire expression and the function is calculated using the calculation type.
◉ If the argument arg is a numeric expression, ipow works like an arithmetic operator and the function is handled in its operand position like an arithmetic expression.

This function can be specified in general and numeric expression positions. The calculation type is determined from the argument arg in the same way as with the other numeric functions. The argument n does not have any effect on the data type of the return value.

Notes

◉ The power function ipow can replace arg ** n calculations, if the calculation type f is to be avoided. This makes sense if the type f is not precise enough.

◉ In many cases, the power function ipow displays better performance than using the arithmetic operator **.

Example

The results are 1.4399999999999999 for ** and 1.4400 for ipow. The result of ipow is more precise.

cl_demo_output=>display( |**  : { '1.2' ** 2 } \n| &&
                         |ipow: { ipow( base = '1.2' exp = 2 ) }| ).

Executable Example


Power Function ipow

This example demonstrates the integer power function ipow.

Source Code

REPORT demo_ipow.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA n TYPE i.
    DATA arg1 TYPE p DECIMALS 1.
    DATA arg2 TYPE int8.

    n = 2.
    arg1 = `1.2`.
    DATA(out) = cl_demo_output=>new(
      )->write( |**  : { arg1 ** n }|
      )->write( |ipow: { ipow( base = arg1 exp = n ) }| ).

    cl_demo_output=>line( ).

    n = 62.
    arg2 = 2.
    out->write( |**  : { arg2 ** n }|
      )->write( |ipow: { ipow( base = arg2 exp = n ) }| ).

    out->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The program demonstrates that the integer power function ipow can be used to achieve more precise results than with the arithmetic operator **. In the cases shown here, the operator ** produces the calculation type f. If ipow is used, the calculation type is determined by the arguments arg1 and arg2.

◉ nmax, nmin - Numeric Extremum Functions

Syntax Forms

... nmax|nmin( val1 = arg1 val2 = arg2 [val3 = arg3] ... [val9 = arg9] ) ...

Effect

These functions return the value of the greatest or the least of the arguments passed. A minimum of two arguments, arg1 and arg2, and a maximum of nine arguments must be passed. Here, the optional input parameters val3 to val9 must be filled in ascending order without gaps. The arguments arg1 to arg9 are numeric expression positions.

The following applies to the data type of the return value:

◉ Outside of an arithmetic expression, a calculation type is determined from all the arguments, and it is used to perform the comparison. The calculation type is determined just like an arithmetic expression and also determines the data type of the return value.

◉ In an arithmetic expression, the arguments of the function contribute to the calculation type of the entire expression and the function is calculated using the calculation type. If an argument itself is an arithmetic expression, its operands contribute to the entire calculation type and the argument is also calculated using this type.

Notes

◉ The extremum functions cmax and cmin can be used to determine character-like extreme values.

◉ When using two input parameters:

result =  nmax|nmin( val1 = arg1 val2 = arg2 )

the evaluation of the functions is equivalent to:

IF num1 >= num2 | num1 <= num2.
  result = num1.
ELSE.
  result = num2.
ENDIF.

When using more than two input parameters, an equivalent control structure would be more complex. Example

Example

Determines the lesser of two time stamps. Here, the initial value of the conditional operator COND is not regarded as the least value.

CONSTANTS max_ts TYPE timestamp VALUE 999999999999999.

DATA: ts1 TYPE timestamp,
      ts2 TYPE timestamp.

GET TIME STAMP FIELD ts1.

DATA(min_ts) = nmin( val1 = COND timestamp( WHEN ts1 IS INITIAL
                                            THEN max_ts ELSE ts1 )
                     val2 = COND timestamp( WHEN ts2 IS INITIAL
                                            THEN max_ts ELSE ts2 ) ).

Extremum functions nmax, nmin

The example demonstrates the extremum functions nmax and nmin.

Source Code

REPORT demo_nmax_nmin.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA: a TYPE i VALUE 1,
          b TYPE i VALUE 0,
          c TYPE i VALUE 0.
    cl_demo_input=>new(
      )->add_text( `Parabola:`
      )->add_field( CHANGING field = a
      )->add_field( CHANGING field = b
      )->add_field( CHANGING field = c )->request( ).
    IF a = 0.
      cl_demo_output=>display(
        'You must enter a non-zero value for a' ).
      RETURN.
    ENDIF.

    CONSTANTS: xmin TYPE i VALUE -100,
               xmax TYPE i VALUE 100,
               n    TYPE i VALUE 20000.
    DATA: x  TYPE decfloat34,
          y  TYPE decfloat34,
          y0 TYPE decfloat34.
    DATA       txt  TYPE string.

    DO n + 1 TIMES.
      x = ( xmax - xmin ) / n * ( sy-index - 1 ) + xmin.
      y = a * x ** 2 + b * x + c.
      IF sy-index = 1.
        y0 = y.
      ELSE.
        IF a > 0.
          txt = 'Minimum'.
          y0 = nmin( val1 = y0 val2 = y ).
        ELSE.
          txt = 'Maximum'.
          y0 = nmax( val1 = y0 val2 = y ).
        ENDIF.
      ENDIF.
    ENDDO.
    cl_demo_output=>display( |{ txt } value of parabola is: { y0 }| ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The program determines the minimum or maximum value of a parabola which is opened upward or downward and whose parameters can be input on the selection screen.

◉ acos, sin, tanh, exp, log, sqrt

The following table shows the floating point functions that expect a floating point number as an unnamed argument. Floating point functions are overloaded so that the return code can have the type decfloat34 or f. Decimal floating point numbers are still only possible as arguments of exp, log, log10, and sqrt.

Syntax

... func( arg ) ...

Effect

The argument of a floating point function must be a single data object outside an arithmetic expression and can be an arithmetic expression itself within an arithmetic expression.

Effect of the floating point functions:

Function func Meaning  Definition Range
acos  arccosine [-1,1], no decfloat34
asin  arcsine  [-1,1], no decfloat34
atan  arctangent   -, no decfloat34
cos  cosine  -, no decfloat34 
sin  sine  -, no decfloat34 
tan  tangent  -, no decfloat34 
cosh  hyperbolic cosine   -, no decfloat34 
sinh  hyperbolic sine   -, no decfloat34 
tanh  hyperbolic tangent   -, no decfloat34 
exp  Exponential function for base e   [-709, 709] for type f and [-14144, 14149] for type decfloat34
log  Natural logarithm   > 0 
log10 Logarithm to base 10   > 0 
sqrt Square root >= 0 

Functions that specify "no decfloat34" cannot currently have the calculation type decfloat34. If one of these functions is specified in an expression with this calculation type, a syntax error occurs or the exception CX_SY_UNSUPPORTED_FUNCTION is raised.

The following applies to the floating point arithmetic in which a floating point function is calculated, and to the data type of the return code:

◉ If the argument has the type decfloat16 or decfloat34, a floating point function is calculated in decimal floating point arithmetic and the return code has the type decfloat34.

◉ If a floating point function is used in an arithmetic expression whose calculation type is decfloat34, or that contains a numeric expression of type decfloat34 as an argument, it also calculates a return code with the type decfloat34 and the argument is first converted to the data type decfloat34, if necessary.

◉ In all other cases, floating point functions use binary floating point arithmetic to calculate a return code with type f and the argument is first converted to the data type f, if necessary.

Functions with a definition range require the value of arg to be within the specified limits. Arguments within the definition ranges are guaranteed to be error-free for the exponential function exp, since the results are then within the value ranges for binary or decimal floating point numbers in accordance with IEEE-754. For arguments less than -709, the result for binary floating point numbers is (depending on the platform) a subnormal number, 0, or a handleable exception of the class CX_SY_ARITHMETIC_OVERFLOW is raised from a specific value.

The trigonometric functions sin, cos, and tan are defined for any arguments but the results become imprecise if the argument is greater than approximately 100,000,000.

Notes

◉ The atan function is undefined for odd number multiples of pi/2, but the definition range of atan is nevertheless restricted since an argument of this function can never contain the precise value of pi/2.

◉ The functions described here are some of the functions that can be used in the obsolete extended functional operand positions, even if their argument is a single data object.

Example

The results in the internal table should all have the value 1 or a value very close to this.

DATA itab TYPE TABLE OF f WITH EMPTY KEY.

itab = VALUE #( FOR i = 0 UNTIL i > 64
                LET n = CONV f( i / 10 ) IN
                ( sin( n ) ** 2 + cos( n ) ** 2 ) ).

cl_demo_output=>display( itab ).

Handleable Exceptions

CX_SY_ARG_OUT_OF_DOMAIN

◉ Cause: Invalid call of acos
Runtime error: COMPUTE_ACOS_DOMAIN

◉ Cause: Invalid call of asin
Runtime error: COMPUTE_ASIN_DOMAIN

◉ Cause: Invalid call of cos
Runtime error: COMPUTE_COS_DOMAIN

◉ Cause: Invalid call of log10
Runtime error: COMPUTE_LOG10_ERROR

◉ Cause: Invalid call of log
Runtime error: COMPUTE_LOG_ERROR

◉ Cause: Invalid call of sin
Runtime error: COMPUTE_SIN_DOMAIN

◉ Cause: Invalid call of sqrt
Runtime error: COMPUTE_SQRT_DOMAIN

◉ Cause: Invalid call of tan
Runtime error: COMPUTE_TAN_DOMAIN

◉ round, rescale - Rounding Functions

Syntax

... round|rescale( val = arg ...  ) ...

Effect

The rounding functions expect a decimal floating point number as a main argument val and additional arguments that describe how this floating point number is handled. The type of the return value of a rounding function is always decfloat34. Within an arithmetic expression, the argument for the decimal floating point number can either be an arithmetic expression or a function. The other arguments must always be specified as numeric data objects.

◉ Rounding Function
◉ Rescaling Function

Note

The class CL_ABAP_MATH includes the method NORMALIZE for normalizing a decimal floating point object. The mantissa does not have any trailing zeroes in a normalized floating point number.

Rounding Function

The rounding function round can be implemented in operand positions using the following syntax:

Syntax

... round( val = arg {dec = n}|{prec = n} [mode = m] ) ...

Effect

This function rounds a decimal floating point number declared as an argument for the parameter val. A data object specified for arg is converted to the data type decfloat34 before the function is executed, if necessary.

Either the parameter dec or the parameter prec must be given a value, and rounding must be to either a particular number of decimal places or precision:

◉ If the parameter dec is given a value, the value entered is rounded to the number of decimal places specified in n and returned. n expects data objects of the type i. The value of these data objects cannot be less than -6144. If a negative value is given, the relevant integer digit is rounded.
◉ If the parameter prec is given a value, the value entered is rounded to the precision specified in n and returned. n expects data objects of the type i. The value of these data objects must be greater than 0.

A rounding can reduce scaling and precision but cannot increase them. If dec is specified, the mantissa of the return code does not contain any zeroes after the place where the rounding applies. If prec is specified, the input value is returned without being changed, if the specified precision is greater than or equal to the input value.

The parameter mode (optional) can be used to set the rounding type. For m it is only possible to specify values that exist as ROUND_... constants in class CL_ABAP_MATH. The following table shows the possible rounding rules. If mode is not given a value, commercial rounding is used.

Constant Rounding Rule 
ROUND_HALF_UP The value is rounded up to the next round figure. If the value falls precisely halfway between two rounded values, it is rounded up away from zero (commercial rounding). 
ROUND_HALF_DOWN The value is rounded down to the next round figure. If the value falls precisely halfway between two round values, it is rounded down towards zero. 
ROUND_HALF_EVEN  The value is rounded to the next round figure. If the value falls precisely halfway between two rounded values, it is rounded to the value which has an even number in the last place.
ROUND_UP The value is always rounded away from zero/to the larger absolute value. 
ROUND_DOWN The value is always rounded to zero/to the smaller absolute value. 
ROUND_CEILING The value is always rounded in a positive direction/to the larger value. 
ROUND_FLOOR The value is always rounded in a positive direction/to the larger value. 

Example

The following tables show the results of commercial rounding of the decimal floating point number 1234.56789 (scaling 5, precision 9) with various values for dec and prec. The displayed results are generated by executing the program DEMO_ROUND_AND_RESCALE.

dec Result Scaling  Precision
-5 0E+5 -5 1
-4  0E+4  -4 
-3   1E+3   -3 
-2  1.2E+3 -2 
-1  1.23E+3   -1 
1235 
1234.6 
1234.57 
1234.568 
1234.5679  
1234.56789  
6 1234.56789

dec Result Scaling  Precision
1 1E+3 -3 1
1.2E+3   -2 
1.23E+3   -1 
1235 
1234.6  
1234.57 
1234.568 
1234.5679 
1234.56789  
10  1234.56789  

Rescaling Function

The rescaling function rescale can be implemented in operand positions using the following syntax:

Syntax

... rescale( val = arg {dec = n}|{prec = n} [mode = m] ) ...

Effect

This function changes the scaling of a decimal floating point number declared as an argument for the parameter val. A data object specified for arg is converted to the data type decfloat34 before the function is executed, if necessary.

Either the parameter dec or the parameter prec must be given a value, where either the scaling or the precision is set:

◉ If the parameter dec is given a value, the value entered is rounded using the scaling specified in n and returned. n expects data objects of the type i. The value of these data objects cannot be less than -6144. If the scaling produces more than 34 places in the mantissa of the return value, a handleable exception is raised.

◉ If the parameter prec is given a value, the value entered is returned with the precision specified in n and appropriate scaling and returned. n expects data objects of the type i. The value of these data objects must be greater than 0 and less than 34.

A rescaling can both reduce and increase scaling and precision. An increase adds zeroes on the right.

The input value is rounded if required. The optional parameter mod can be used to specify the rounding rule, as described under the function round. The default is commercial rounding.

Examples

The following tables show the results of rescaling of the decimal floating point number 1234.56789 (scaling 5, precision 9) with various values for dec and prec, if commercial rounding is used. The displayed results are generated by executing the program DEMO_ROUND_AND_RESCALE.

dec Result Scaling  Precision
-5 0E+5 -5 1
-4  0E+4  -4 
-3   1E+3   -3 
-2  1.2E+3 -2 
-1  1.23E+3   -1 
1235 
1234.6 
1234.57 
1234.568 
1234.5679  
1234.56789  
6 1234.567890 6 10
7 1234.5678900 7 11
8 1234.56789000 8 12

dec Result Scaling  Precision
1 1E+3 -3 1
1.2E+3   -2 
1.23E+3   -1 
1235 
1234.6  
1234.57 
1234.568 
1234.5679 
1234.56789  
10  1234.567890 6 10
11  1234.5678900 7 11
12 1234.56789000 8 12

◉ Examples of numerical functions

Example Numerical Functions

This example demonstrates the use of general numerical functions.

Source Code

REPORT demo_numerical_function.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA n TYPE decfloat16.
    DATA m TYPE decfloat16 VALUE '-5.55'.

    DATA(out) = cl_demo_output=>new( ).

    n = abs( m ).
    out->write( |ABS: { n }| ).
    n = sign( m ).
    out->write( |SIGN: { n }| ).
    n = ceil( m ).
    out->write( |CEIL: { n }| ).
    n = floor( m ).
    out->write( |FLOOR: { n }| ).
    n = trunc( m ).
    out->write( |TRUNC: { n }| ).
    n = frac( m ).
    out->write( |FRAC: { n }| ).

    out->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The output of the program shows how each of the general numerical functions affects the number -5.55.

Example Integer Power Function ipow

This example demonstrates the integer power function ipow.

Source Code

REPORT demo_ipow.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA n TYPE i.
    DATA arg1 TYPE p DECIMALS 1.
    DATA arg2 TYPE int8.

    n = 2.
    arg1 = `1.2`.
    DATA(out) = cl_demo_output=>new(
      )->write( |**  : { arg1 ** n }|
      )->write( |ipow: { ipow( base = arg1 exp = n ) }| ).

    cl_demo_output=>line( ).

    n = 62.
    arg2 = 2.
    out->write( |**  : { arg2 ** n }|
      )->write( |ipow: { ipow( base = arg2 exp = n ) }| ).

    out->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The program demonstrates that the integer power function ipow can be used to achieve more precise results than with the arithmetic operator **. In the cases shown here, the operator ** produces the calculation type f. If ipow is used, the calculation type is determined by the arguments arg1 and arg2.

Example Extremum functions nmax, nmin

The example demonstrates the extremum functions nmax and nmin.

Source Code

REPORT demo_nmax_nmin.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA: a TYPE i VALUE 1,
          b TYPE i VALUE 0,
          c TYPE i VALUE 0.
    cl_demo_input=>new(
      )->add_text( `Parabola:`
      )->add_field( CHANGING field = a
      )->add_field( CHANGING field = b
      )->add_field( CHANGING field = c )->request( ).
    IF a = 0.
      cl_demo_output=>display(
        'You must enter a non-zero value for a' ).
      RETURN.
    ENDIF.

    CONSTANTS: xmin TYPE i VALUE -100,
               xmax TYPE i VALUE 100,
               n    TYPE i VALUE 20000.
    DATA: x  TYPE decfloat34,
          y  TYPE decfloat34,
          y0 TYPE decfloat34.
    DATA       txt  TYPE string.

    DO n + 1 TIMES.
      x = ( xmax - xmin ) / n * ( sy-index - 1 ) + xmin.
      y = a * x ** 2 + b * x + c.
      IF sy-index = 1.
        y0 = y.
      ELSE.
        IF a > 0.
          txt = 'Minimum'.
          y0 = nmin( val1 = y0 val2 = y ).
        ELSE.
          txt = 'Maximum'.
          y0 = nmax( val1 = y0 val2 = y ).
        ENDIF.
      ENDIF.
    ENDDO.
    cl_demo_output=>display( |{ txt } value of parabola is: { y0 }| ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The program determines the minimum or maximum value of a parabola which is opened upward or downward and whose parameters can be input on the selection screen.

Example Rounding Function round

This example demonstrates the rounding function round.

Source Code

REPORT demo_round.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
  PRIVATE SECTION.
    CLASS-DATA: BEGIN OF mode,
                  value LIKE cl_abap_math=>round_half_up,
                  name  TYPE abap_attrdescr-name,
                END OF mode,
                modes LIKE SORTED TABLE OF mode
                      WITH UNIQUE KEY name.
    CLASS-METHODS get_modes.
    TYPES:
      BEGIN OF line,
        number          TYPE decfloat34,
        ceiling   TYPE decfloat34,
        down      TYPE decfloat34,
        floor     TYPE decfloat34,
        half_down TYPE decfloat34,
        half_even TYPE decfloat34,
        half_up   TYPE decfloat34,
        up        TYPE decfloat34,
      END OF line.
    CLASS-DATA output TYPE TABLE OF line.
    CLASS-METHODS write_output IMPORTING VALUE(idx) TYPE i
                                         VALUE(col) TYPE i
                                         text       TYPE clike.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA number TYPE decfloat34.
    cl_demo_output=>begin_section( `Rounding Function` ).
    get_modes( ).
    DO 21 TIMES.
      number = - ( sy-index - 11 ) / 10.
      write_output(
        idx = sy-index
        col = '1'
        text = |{ number }| ).
      LOOP AT modes INTO mode.
        write_output(
          idx = sy-index
          col = sy-tabix + 1
          text = |{ round( val  = number
                           dec  = 0
                           mode = mode-value ) }| ).
      ENDLOOP.
    ENDDO.
    cl_demo_output=>display( output ).
  ENDMETHOD.
  METHOD get_modes.
    DATA: modes   TYPE abap_attrdescr_tab,
          mode    LIKE LINE OF modes.
    FIELD-SYMBOLS <mode> LIKE cl_abap_math=>round_half_up.
    modes =
      CAST cl_abap_classdescr(
             cl_abap_classdescr=>describe_by_name( 'CL_ABAP_MATH' )
             )->attributes.
    DELETE modes WHERE name NP 'ROUND_*' OR is_constant <> 'X'.
    LOOP AT modes INTO mode.
      ASSIGN cl_abap_math=>(mode-name) TO <mode>.
      demo=>mode-value = <mode>.
      demo=>mode-name = mode-name.
      INSERT demo=>mode INTO TABLE demo=>modes.
    ENDLOOP.
  ENDMETHOD.
  METHOD write_output.
    ASSIGN output[ idx ] TO FIELD-SYMBOL(<line>).
    IF sy-subrc <> 0.
      DO.
        APPEND INITIAL LINE TO output ASSIGNING <line>.
        IF sy-tabix = idx.
          EXIT.
        ENDIF.
      ENDDO.
    ENDIF.
    ASSIGN COMPONENT col OF STRUCTURE <line> TO FIELD-SYMBOL(<col>).
    <col> = text.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

For fraction numbers between 1 and -1, the program depicts the effect of all rounding types from the class CL_ABAP_MATH in the rounding function round. The program reads the possible rounding methods using RTTI and sorts them by name.

◈ System Classes for Numbers

1. System class CL_ABAP_MATH

Constants for minimum and maximum values

Class CL_ABAP_MATH contains constants with the limits of the value ranges predefined numeric types.

Example

Display of minimum values and maximum values of numeric types.

cl_demo_output=>display(
  |i:          { cl_abap_math=>min_int4 WIDTH = 20
          } to { cl_abap_math=>max_int4 WIDTH = 20 } \n| &&
  |int8:       { cl_abap_math=>min_int8 WIDTH = 20
          } to { cl_abap_math=>max_int8 WIDTH = 20 } \n| &&
  |decfloat16: { cl_abap_math=>min_decfloat16 STYLE = SCIENTIFIC
                                              WIDTH = 20
          } to { cl_abap_math=>max_decfloat16 STYLE = SCIENTIFIC
                                              WIDTH = 20 } \n| &&
  |decfloat34: { cl_abap_math=>min_decfloat34 STYLE = SCIENTIFIC
                                              WIDTH = 20
          } to { cl_abap_math=>max_decfloat34 STYLE = SCIENTIFIC
                                              WIDTH = 20 } \n| ).

Operations with Floating Point Numbers

The class CL_ABAP_MATH contains methods for operations with floating point numbers.

Rounding Binary Floating Point Numbers

The method ROUND_F_TO_15_DECS rounds a binary floating point number to 15 places using commercial rounding.

Note

Commercial rounding to 15 digits can also be achieved with the built-in function round, the result of which then has the decfloat34 type.

Example

The example shows how rounding errors in the output formatting of binary floating point numbers can be prevented by two-step rounding using the method ROUND_F_TO_15_DECS. Instead of the method call, the function call round( val = float dec = 15 ) can be used here.

DATA float TYPE f VALUE '1.005'.

cl_demo_output=>display(
  |{ float DECIMALS = 2 }\n| &&
  |{ cl_abap_math=>round_f_to_15_decs( float ) DECIMALS = 2 }\n| ).

Operations with Decimal Floating Point Numbers

◉ The method GET_SCALE gets the scaling of a decimal floating point number.
◉ The method GET_NUMBER_OF_DIGITS gets the precision of a decimal floating point number.
◉ The method NORMALIZE gets a normalized floating point number. This means that the scaling and precision of an input value are changed so that the mantissa has no trailing zeroes.
◉ The method GET_SCALE_NORMALIZED gets the scaling of a normalized decimal floating point number. If the input value has decimal places, the return value is the number of decimal places without trailing zeroes.
◉ The method GET_MAX_DB_VALUE gets the maximum value of a number of the type DF16_DEC or DF34_DEC on the database.
◉ The method GET_DB_LENGTH_DECS gets the length and number of decimal places of a number of the type DF16_DEC or DF34_DEC on the database.

Note

The rounding functions round and rescale can be used to round and rescale decimal floating point numbers.

Example

Outputs the scaling and precision of a decimal floating point number.

DATA(decf) = CONV decfloat34( '1234.56789' ).

cl_demo_output=>display(
|value = { decf
}\nscale = { cl_abap_math=>get_scale( decf )
}\nprecision = { cl_abap_math=>get_number_of_digits( decf ) }| ).

2. Random Numbers

The class CL_ABAP_RANDOM calls the pseudo random number generator Mersenne Twister for different numeric types.

For the one-dimensional case, the following special classes generate random numbers for the different numeric types:

- CL_ABAP_RANDOM_INT for type i
- CL_ABAP_RANDOM_INT8 for type int8
- CL_ABAP_RANDOM_FLOAT for type f
- CL_ABAP_RANDOM_PACKED for type p
- CL_ABAP_RANDOM_PACKED_DEC1 to CL_ABAP_RANDOM_PACKED_DEC14 for type p with 1 to 14 decimal places.
- CL_ABAP_RANDOM_DECFLOAT16 for type decfloat16
- CL_ABAP_RANDOM_DECFLOAT34 for type decfloat34

Example

Creates a pseudo random number of the type i between 1 and 100 (initialized by the system time).

DATA(r) = cl_abap_random_int=>create( seed = CONV i( sy-uzeit )
                                      min  = 1
                                      max = 100 ).

◈ Statements for Numeric Calculations

Alongside arithmetic expressions, four special statements are provided for performing the basic arithmetic operations addition, subtraction, multiplication, and division:

◐ ADD

Syntax

ADD dobj1 TO dobj2.

Effect

This statement has the same effect as the statement

dobj2 = dobj2 + dobj1.

The content of dobj1 is added to the content of dobj2 and the result is assigned to dobj2. The data objects dobj1 and dobj2 have to be numeric. Only data objects can be specified, no calls or other expressions. The calculation type is determined like an arithmetic expression.

Note

Obsolete forms of the statement ADD are ADD...THEN...UNTIL and ADD-CORRESPONDING.

Example

After adding, the result contains the character string "_________1". The calculation type is decfloat34. The content of the character-like field result is converted to decfloat34 before the operation and back to c after the operation.

DATA: operand TYPE decfloat16 VALUE 1,
      result  TYPE c LENGTH 10.

ADD operand TO result.

Exceptions

Handleable Exceptions

CX_SY_ARITHMETIC_OVERFLOW

- Cause: Overflow in arithmetic operation (type p)
Runtime error: BCD_OVERFLOW

- Cause: Integer overflow while adding
Runtime error: COMPUTE_INT_PLUS_OVERFLOW

CX_SY_CONVERSION_OVERFLOW

- Cause: Overflow in arithmetic operation (type p, with specified length)
Runtime error: BCD_FIELD_OVERFLOW

Non-Handleable Exceptions

Runtime error: ADD_FIELDS_ILLEGAL_ACCESS

- Cause: p field does not contain the correct BCD format
Runtime error: BCD_BADDATA

◐ SUBTRACT

Syntax

SUBTRACT dobj1 FROM dobj2.

Effect

This statement has the same effect as the statement

dobj2 = dobj2 - dobj1.

The content of dobj1 is subtracted from the content of dobj2 and the result is assigned to dobj2. The data objects dobj1 and dobj2 have to be numeric. Only data objects can be specified, no calls or other expressions. The calculation type is determined like an arithmetic expression.

Note

One obsolete form of the statement SUBTRACT is SUBTRACT-CORRESPONDING.

Example

Subtraction of the offset of a runtime measurement.

GET RUN TIME FIELD DATA(t1).

...

GET RUN TIME FIELD DATA(t2).

SUBTRACT t1 FROM t2.

Exceptions

Handleable Exceptions

CX_SY_ARITHMETIC_OVERFLOW

◉ Cause: Overflow in conversion/arithmetic operation (type p)
Runtime error: BCD_OVERFLOW

◉ Cause: Integer overflow after subtraction
Runtime error: COMPUTE_INT_MINUS_OVERFLOW

CX_SY_CONVERSION_OVERFLOW

◉ Cause: Overflow in conversion/arithmetic operation (type p, with specified length)
Runtime error: BCD_FIELD_OVERFLOW

Non-Handleable Exceptions

◉ Cause: p field does not contain the correct BCD format
Runtime error: BCD_BADDATA

◐ MULTIPLY

Syntax

MULTIPLY dobj1 BY dobj2.

Effect

This statement has the same effect as the statement

dobj1 = dobj1 * dobj2.

The content of dobj1 is multiplied by the content of dobj2 and the result is assigned to dobj1. The data objects dobj1 and dobj2 have to be numeric. Only data objects can be specified, no calls or other expressions. The calculation type is determined like an arithmetic expression.

Note

One obsolete form of the statement MULTIPLY is MULTIPLY-CORRESPONDING.

Example

Multiplication of a percentage by 100.

DATA percentage TYPE p DECIMALS 2.

percentage = '0.85'.

MULTIPLY percentage BY 100.

Exceptions

Handleable Exceptions

CX_SY_ARITHMETIC_OVERFLOW

- Cause: Overflow in conversion/arithmetic operation (type p)
Runtime error: BCD_OVERFLOW

- Cause: Integer overflow in multiplication
Runtime error: COMPUTE_INT_TIMES_OVERFLOW

CX_SY_CONVERSION_OVERFLOW

- Cause: Overflow in conversion/arithmetic operation (type p with specified length)
Runtime error: BCD_FIELD_OVERFLOW

Non-Handleable Exceptions

- Cause: p field does not contain the correct BCD format
Runtime error: BCD_BADDATA

◐ DIVIDE

Syntax

DIVIDE dobj1 BY dobj2.

Effect

This statement has the same effect as the statement

dobj1 = dobj1 / dobj2.

The content of dobj1 is divided by the content of dobj2 and the result is assigned to dobj1. The data objects dobj1 and dobj2 have to be numeric. Only data objects can be specified, no calls or other expressions. The calculation type is determined like an arithmetic expression.

Notes

- Division by the value 0 is undefined and raises a handleable exception. The only situation where division by 0 does not raise an exception is if the dividend is also 0. Here, the result is set to 0.

- One obsolete form of the statement DIVIDE is DIVIDE-CORRESPONDING.

Example

Division of a percentage by 100.

DATA percentage TYPE p DECIMALS 2.

percentage = '85'.

DIVIDE percentage BY 100.

Exceptions

Handleable Exceptions

CX_SY_ARITHMETIC_OVERFLOW

- Cause: Overflow in arithmetic operation (type p)
Runtime error: BCD_OVERFLOW

- Cause: Integer overflow in division
Runtime error: COMPUTE_INT_DIV_OVERFLOW

CX_SY_CONVERSION_OVERFLOW

- Cause: Overflow in arithmetic operation (type p, with specified length)
Runtime error: BCD_FIELD_OVERFLOW

CX_SY_ZERODIVIDE

- Cause: Division by 0 (type p)
Runtime error: BCD_ZERODIVIDE

- Cause: Division by 0 (type f)
Runtime error: COMPUTE_FLOAT_ZERODIVIDE

- Cause: Division by 0 (type i)
Runtime error: COMPUTE_INT_ZERODIVIDE

Non-Handleable Exceptions

- Cause: p field does not contain the correct BCD format
Runtime error: BCD_BADDATA

3. Character String and Byte String Processing


This section describes the language elements, expressions, and functions used for the processing of character strings and byte strings in character-like and byte-like data objects.

3.1 Character String and Byte String Processing - Overview

- Character Strings and Byte Strings

Character Strings

Character-like data objects contain character strings. A character-like data object either has a character-like data type (c, n, or string) or it is a date/time type (d or t), or it is a flat structure with exclusively character-like components.

ABAP supports the character format UCS-2 and a character always occupies two bytes. This ensures that all characters from the system code page UTF-16 are handled correctly (except for those in the surrogate area. These characters occupy four bytes and hence are handled as two characters by ABAP. This can produce unexpected results when cutting character strings or comparing individual characters in character sets.

Byte Chains

Byte-like data objects contain byte chains (or byte strings). A byte-like data object always has the byte-like data type x or xstring.

Example

A byte string of type xstring contains a byte chain, which is decoded in accordance with codepage UTF-8 into a text in a text string of type string.

DATA hex  TYPE xstring VALUE '48656C6C6F20576F726C64'.
DATA text TYPE string.

text = cl_abap_codepage=>convert_from( hex ).

cl_demo_output=>display( text ).

- Operands in Character String and Byte String Processing

Operands in Character String Processing

In character string processing (defined by the addition IN CHARACTER MODE in overloaded statements) and in statements that only support character string processing, the relevant operands must be character-like. This is because the operands are processed by character and the repository of the characters in the memory is dependent on the code page used. This condition is vital to ensure that character string processing functions correctly.

In Unicode programs, only data objects, return values, or results with the character-like data types c, n, and string, plus the date/time types d and t, or structures with exclusively flat character-like components are permitted as character-like operands. Data objects with the types n, d, and t and structures with exclusively character-like components are handled like data objects with the type c.

Notes

- If the character-like data objects n, d, and t are used in character string processing, it must be noted that the type-friendly conversion rules do not apply to the assignment of intermediate results to target fields; instead the conversion rules for data type c apply.
- No structures can be used as character-like operands in string expressions and string functions with named arguments.

Operands in Byte String Processing

In byte string processing (defined by the addition IN BYTE MODE in overloaded statements) and in the statements GET BIT and SET BIT, the relevant operands must be byte-like. This is because the operands are processed by byte.

- Trailing Blanks in Character String Processing

Statements for character string processing generally preserve leading blanks for operands of data types with fixed lengths (c, n, d, and t or character-like structures) and cut off trailing blanks. Exceptions to this rule are explained in the affected statements. All blanks are generally preserved for operands of the data type string.

If the result of a statement for character string processing is assigned to an operand, the operand is generally padded on the right with blanks if the result is shorter than the length of the operand. String assignments generally adapt the string length to the length of the result. Exceptions to this rule are explained in the affected statements.

Notes

- The cutting off of trailing blanks especially affects the text field literal ' ' and the constant space. These should therefore never be specified in operand positions where trailing blanks are cut off.
- In long results fields, padding with blanks can produce invalid values, if the fields have the type n, d, or t.
- The preservation of trailing blanks can be forced for assignments by using the addition RESPECTING BLANKS of the statement CONCATENATE.

Example

The result of the following concatenation is a string with exactly one blank. The blank characters in space and ' ' are ignored. At operand positions after SEPARATED BY however, the blank character which is contained explicitly in '' is not ignored. If the addition RESPECTING BLANKS is used, the result is a string with three blanks.

DATA text TYPE string.

CONCATENATE space ' ' INTO text SEPARATED BY ''.

3.2 Statements for Character String and Byte String Processing

The following table shows the key words for character string and byte string processing and the processes supported by these statements.

Keyword Character String Processing Byte String Processing 
CONCATENATE x x
FIND  
REPLACE  
SHIFT 
SPLIT 
CONDENSE  -
CONVERT TEXT  -
OVERLAY 
TRANSLATE 
WRITE TO  
SET BIT  - x
GET BIT

There is a strict split between character string processing and byte string processing. Each of the key words in the table that supports both character string and byte string processing has the optional addition

... IN {CHARACTER|BYTE} MODE ...

This addition defines which processing is used. If this addition is not specified, character string processing is used in these statements.

Notes

- In most cases, expressions and functions for string processing can be used instead of the above statements for string processing. These have the advantage that expressions can be specified for all operands, which is only possible to a very limited extent in the above statements.
- As well as the statements shown here, a range of obsolete statements for character and byte string processing exists.

◉ CONCATENATE

Syntax

CONCATENATE {dobj1 dobj2 ...}|{LINES OF itab}
            INTO result
            [IN {CHARACTER|BYTE} MODE]
            [SEPARATED BY sep]
            [RESPECTING BLANKS].

Extras:

1. ... IN {CHARACTER|BYTE} MODE

2. ... SEPARATED BY sep

3. ... RESPECTING BLANKS

Effect

Concatenates either the content of the data objects dobj1, dobj2, ... of the rows of the internal table itab in accordance with their order and assigns them to the target field result. itab is a functional operand position. The following can be specified for the target field result:

- An existing character-like or byte-like variable to which the result of the chaining can be converted.

- An inline declaration with DATA(var). If IN CHARACTER MODE is used, the declared variable is of the type string; if IN BYTE MODE is used, it is of the type xstring.

If the target field result has a fixed length and this is greater than the length required, the field is filled on the right with blanks or hexadecimal 0. If the target field is too short, the concatenation is truncated on the right. If the target field is a string, its length is adjusted accordingly.

When character strings are processed, trailing blanks are usually ignored for data objects dobj1, dobj2 ... or rows in the internal table itab of fixed length.

System Fields

sy-subrc Meaning
The content of all data objects dobj1, dobj2 ... or itab rows was passed to the target field result.
4 The content of the data objects dobj1, dobj2 ... or itab rows could not be passed completely, since result is too short.

Notes

◉ Instead of CONCATENATE, it is usually also possible to use string expressions for elementary fields. These expressions enable concatenations using either concatenation operators && or embedded expressions in string templates. The built-in function concat_lines_of can be used to concatenate rows in an internal table.

◉ The ABAP runtime environment executes an internal optimization to reduce reallocations if new fragments are attached to an existing string within a loop. If the string or parts of the string themselves are appended, no optimization takes place. In loops this causes a squared increase in runtime. This can be prevented by using helper variables. See also the Performance Note for string expressions.

Example

The example shows that a chaining with CONCATENATE has the same result as a chaining with the chaining operator && or via embedded expressions.

CONCATENATE 'a' 'b' 'c' INTO DATA(str).
ASSERT str = 'a' && 'b' && 'c'.
ASSERT str = |{ 'a' }{ 'b' }{ 'c' }|.

Addition 1

... IN {CHARACTER|BYTE} MODE

Effect

The optional addition IN {CHARACTER|BYTE} MODE determines whether character string or byte string processing is performed. If the addition is not specified, character string processing is performed. Depending on the processing type, the data objects dobj1, dobj2 ..., the rows of the internal table itab, and the separator sep must be character-like or byte-like.

Example

Chaining of individual bytes from an internal table in a byte string.

TYPES hex TYPE x LENGTH 1.
DATA itab TYPE TABLE OF hex WITH EMPTY KEY.

itab = VALUE #(
  ( CONV hex( '48' ) )
  ( CONV hex( '65' ) )
  ( CONV hex( '6C' ) )
  ( CONV hex( '6C' ) )
  ( CONV hex( '6F' ) ) ).

CONCATENATE LINES OF itab INTO DATA(xstr) IN BYTE MODE.

cl_demo_output=>display( cl_abap_codepage=>convert_from( xstr ) ).

Addition 2

... SEPARATED BY sep

Effect

The addition SEPARATED BY is used to insert the content of data object sep between the content of the consecutive data objects dobj1, dobj2 .... When strings are processed, trailing blanks are respected in separators sep of fixed length.

Example

After the first CONCATENATE statement, the result contains "Wehaveallthetimeintheworld", while after the second it contains "We have all the time in the world".

DATA: t1 TYPE c LENGTH 10 VALUE 'We',
      t2 TYPE c LENGTH 10 VALUE 'have',
      t3 TYPE c LENGTH 10 VALUE 'all',
      t4 TYPE c LENGTH 10 VALUE 'the',
      t5 TYPE c LENGTH 10 VALUE 'time',
      t6 TYPE c LENGTH 10 VALUE 'in',
      t7 TYPE c LENGTH 10 VALUE 'the',
      t8 TYPE c LENGTH 10 VALUE 'world'.

CONCATENATE t1 t2 t3 t4 t5 t6 t7 t8
            INTO DATA(result).
...
CONCATENATE t1 t2 t3 t4 t5 t6 t7 t8
            INTO result SEPARATED BY space.

Addition 3

... RESPECTING BLANKS

Effect

The addition RESPECTING BLANKS is only allowed when strings are processed and respects the trailing blanks for data objects dobj1, dobj2 ... or rows in the internal table itab. If this addition is not used, the blanks are respected for data type string only.

Note

If the addition RESPECTING BLANKS is specified, the statement CONCATENATE can be used to assign any character strings text to target fields str of type string while respecting trailing blanks: CLEAR str. CONCATENATE str text INTO str RESPECTING BLANKS.

Example

After the first CONCATENATE statement, result contains "When_the_music_is_over", and after the second it contains "When______the_______music_____is________ over______". The underscores here represent blanks.

TYPES text   TYPE c LENGTH 10.
DATA  itab   TYPE TABLE OF text.

APPEND 'When'  TO itab.
APPEND 'the'   TO itab.
APPEND 'music' TO itab.
APPEND 'is'    TO itab.
APPEND 'over'  TO itab.

CONCATENATE LINES OF itab INTO DATA(result) SEPARATED BY space.
...
CONCATENATE LINES OF itab INTO result RESPECTING BLANKS.

◉ FIND

Syntax

FIND [{FIRST OCCURRENCE}|{ALL OCCURRENCES} OF] pattern
  IN [section_of] dobj
  [IN {CHARACTER|BYTE} MODE]
  [find_options].

Extras:

1. ... {FIRST OCCURRENCE}|{ALL OCCURRENCES} OF

2. ... [IN {CHARACTER|BYTE} MODE]

Effect

The operand dobj is searched for the character or byte sequence specified by the search string pattern.

The additions FIRST OCCURRENCE and ALL OCCURRENCES determine whether all occurrences or only the first one is searched. The addition section_of can be used to restrict the search range. The search is terminated if the search pattern was found for the first time, or if all search patterns were found in the entire search area, or if the end of the search area was reached. The search result is communicated by setting sy-subrc. The addition MODE determines a character or byte string is processed, and the addition find_options provides additional options for controlling and analyzing the statement.

When a character string is processed, dobj is a character-like expression position and the blanks are respected for dobj operands of a fixed length.

System Fields

sy-subrc Meaning 
0 The search pattern was found at least once in the search range. 
4 The search pattern was not found in the search range.

Notes

◉ The statement FIND IN TABLE is available for searching in internal tables.

◉ Search functions can be used to search in a string in an operand position. They mask some of the functions of the statement FIND.

◉ The statement FIND and the search functions can be quicker than the relational operator CS by some magnitude.

Example

The simplest form of the statement FIND.

FIND 'bcd' in 'abcde'.
ASSERT sy-subrc = 0

Addition 1

... {FIRST OCCURRENCE}|{ALL OCCURRENCES} OF

Effect

The optional addition {FIRST OCCURRENCE}|{ALL OCCURRENCES} OF defines whether all or only the first occurrence of the search pattern is searched. If the addition FIRST OCCURRENCE or none of the additions is specified, only the first occurrence is found. Otherwise, all occurrences are found.

If substring is an empty string in the pattern or is of type c, n, d, or t and only contains blanks, the place in front of the first character or byte of the search range is found when searching for the first occurrence. If searching for all occurrences, in this case the exception CX_SY_FIND_INFINITE_LOOP is triggered.

If regex contains a regular expression in the pattern that matches the empty character string, the search for the first occurrence also finds the place before the first character. When searching for all occurrences, in this case, the search finds the space before the first character, all intermediate spaces that are not within a match, and the space after the last character.

Example

All three occurrences of the letter "a" are searched for and found.

FIND ALL OCCURRENCES OF 'a' in 'ababa' MATCH COUNT DATA(mcnt).
ASSERT mcnt = 3.

Addition 2

... IN {CHARACTER|BYTE} MODE

Effect

The optional addition IN {CHARACTER|BYTE} MODE determines whether character string or byte string processing is performed. If the addition is not specified, character string processing is carried out. Depending on the processing type, dobj and substring in pattern must be character-like or byte-like. If regular expressions are used in pattern, only character string processing is permitted.

Example

Finds the first byte that represents a blank space in the code page UTF-8.

DATA(xstr) = cl_abap_codepage=>convert_to( `a b c` ).
DATA(xspc) = cl_abap_codepage=>convert_to( ` ` ).
FIND xspc IN xstr IN BYTE MODE MATCH OFFSET DATA(moff).
ASSERT moff = 1.

Exceptions

Handleable Exceptions

CX_SY_FIND_INFINITE_LOOP

◉ Cause: Substring of length 0 creates an endless loop when searching for all occurrences.
Runtime error: FIND_INFINITE_LOOP

CX_SY_RANGE_OUT_OF_BOUNDS

◉ Cause: Illegal offset or length specified in the addition SECTION OF.
Runtime error: REFI_WRONG_SECTION

CX_SY_INVALID_REGEX

◉ Cause: Invalid expression after the addition REGEX.
Runtime error: INVALID_REGEX

CX_SY_REGEX_TOO_COMPLEX

◉ Cause: More information: Exceptions in Regular Expressions.
Runtime error: REGEX_TOO_COMPLEX

◉ REPLACE

Syntax Forms

Pattern-Based Replacement

1. REPLACE [{FIRST OCCURRENCE}|{ALL OCCURRENCES} OF] pattern
          IN [section_of] dobj WITH new
          [IN {CHARACTER|BYTE} MODE]
          [replace_options].

Position-Based Replacement

2. REPLACE SECTION [OFFSET off] [LENGTH len] OF dobj WITH new
                  [IN {CHARACTER|BYTE} MODE].

Effect

This statement replaces characters or bytes of the variable dobj with characters or bytes of the operand new. This operand is a character-like expression position. Here, there is a difference between pattern-based and position-based replacements.

When the replacement is performed, an intermediate result without a length limit is created implicitly and passed to the data object dobj. If the length of the intermediate result is longer than the length of dobj, the object is truncated on the right in the case of data objects of fixed length. If the length of the intermediate result is shorter than the length of dobj, data objects of fixed length are padded on the right with blanks or hexadecimal zeros. Data objects of variable length are adjusted accordingly. If data is truncated on the right when the intermediate result is assigned, sy-subrc is set to 2.

When the character string is processed, the trailing blanks are respected for data objects dobj of fixed length. For new the closing blanks are ignored.

Example

The simplest form of the statement REPLACE.

DATA(str) = `Hallo`.
REPLACE `a` IN str WITH `e`.
cl_demo_output=>display( str ).

System Fields

sy-subrc Meaning 
0 The search pattern or specified section was replaced by the content of new and the result is available in full in dobj.
The search pattern or specified section was replaced by the content of new in dobj and the result of the replacement was truncated on the right. 
The search pattern in pattern was not found in dobj in the pattern-based search. 

Notes

◉ The statement REPLACE IN TABLE can be used to make replacements in internal tables.

◉ These forms of the statement REPLACE replace the following obsolete form:

REPLACE substring WITH new INTO dobj ...

◉ To replace parts of a character string in an operand position, a replace function can be used that includes some of the functions of the statement REPLACE.

◉ SHIFT

Syntax

SHIFT dobj [ {[ places][ direction]} | deleting ]
           [IN {CHARACTER|BYTE} MODE].

Addition:

... IN {CHARACTER|BYTE} MODE

Effect

This statement shifts the content of a variable dobj. In places, the number of places to be shifted can be specified and in direction, the direction of the shift. In deleting, the characters to be deleted from the data object by the shift can be specified. If no additions are specified, the content is shifted to the left by one place.

By default, free places created by the shift are filled with blanks or hexadecimal 0 for data objects of fixed length, depending on the processing method. Data objects of type string or xstring are shortened by the number of shifted places when shifted to the left, and lengthened by the number of shifted places when shifted to the right.

When the character string is processed, the trailing blanks are respected for data objects dobj of fixed length.

Note

To shift a string in an operand position, shift functions that cover some of the functions of the statement SHIFT can be used.

Example

The shortest form of statement SHIFT. The content of str is moved one place to the left.

DATA(str) = `0123456789`.
SHIFT str.
cl_demo_output=>display( str ).

Addition

... IN {CHARACTER|BYTE} MODE

Effect

The optional addition IN {CHARACTER|BYTE} MODE determines whether character string or byte string processing is performed. If the addition is not specified, character string processing is carried out. Depending on the processing method, dobj, substring and mask must be character-like or byte-like.

Example

Moving a byte string one byte to the left.

DATA(xstr) = CONV xstring( `AABBCCDDEEFF` ).
SHIFT xstr IN BYTE MODE.
cl_demo_output=>display( xstr ).

↬ SHIFT - places

Syntax

... {BY num PLACES} | {UP TO substring} ...

Variants:

1. ... BY num PLACES ...

2. ... UP TO substring ...

Variant 1

... BY num PLACES ...

Effect

The content of dobj is shifted to the left or right (as specified by direction) by the places specified in num. num is a numeric expression position of operand type i. If the content of num is less than or equal to 0, the content of the data object dobj remains unchanged.

Example

Using the statement FIND, the offset of the word "you" in text is determined and its content is shifted by this length to the left or right. After the shift, text contains "you know" and is eight characters long.

DATA: text TYPE string VALUE `I know you know`,
      off  TYPE i.

FIND 'you' IN text MATCH OFFSET off.

SHIFT text BY off PLACES.

Variant 2

... UP TO substring ...

Effect

In the data object dobj, the first substring is searched for whose contents match those of substring. This is case-sensitive. The content of the data object dobj is shifted as far possible to the left or right (as specified in direction) until the byte or character string contained in substring is at the position that is at the beginning or end of the data object dobj before the shift.

substring expects a character-like or byte-like data object. If substring is an empty string, the place in front of the first character or byte is found. On the left, no shift takes place and a shift by the entire length of dobj takes place on the right.

In character string processing, substring is a character-like expression position; in data objects, substring with a fixed length respects the trailing blanks.

System Fields

sy-subrc Meaning 
0 The substring in substring was found in the data object dobj and its contents were moved accordingly.
4 The substring in substring was not found in the data object dobj and its contents remain unchanged.

Note

If the case of data objects of fixed length, the substring searched for after the shift is either (depending on the direction) flush left at the beginning or flush right at the end of the data object. If the case of strings, the shift makes the data object longer on the right. This means that the substring is not at the right margin after the shift.

Example

This example has the same result as the previous example. However, here the search for "you" is not performed in the statement SHIFT itself.

DATA text TYPE string VALUE `I know you know `.

SHIFT text UP TO 'you'.


↬ SHIFT - direction

Syntax

... [LEFT|RIGHT] [CIRCULAR] ...

Extras:

1. ... LEFT|RIGHT

2. ... CIRCULAR

Addition 1

... LEFT|RIGHT

Effect

The shift direction is defined using LEFT or RIGHT. If neither of the additions is specified, LEFT is used implicitly. If the data object dobj is a string and the addition CIRCULAR is not specified, it is truncated when shifted to the left by the places shifted and lengthened accordingly when shifted to the right.

Example

The content of text field text is moved one place to the right, by which the digit "9" is lost. The data type string would lengthen the string by one space.

DATA(text) = '0123456789'.
SHIFT text RIGHT.
cl_demo_output=>display( text ).

Addition 2

... CIRCULAR

Effect

Using the addition CIRCULAR, the content shifted from the data object to the left or to the right, is inserted again in the places that become available on the opposite side. If the addition CIRCULAR is specified, data objects of the type string or xstring are not shortened or lengthened; instead, they are handled as data objects of fixed length.

Example

This example is a variant of the second example under places. Using the addition CIRCULAR, the result becomes "you know I know".

DATA text TYPE string VALUE `I know you know `.

SHIFT text UP TO 'you' LEFT CIRCULAR.

↬ SHIFT - deleting

Syntax

... { {LEFT DELETING LEADING}
    | {RIGHT DELETING TRAILING} } mask ...

Effect

If these additions are used, the statement SHIFT shifts the content of dobj by one place to the left or the right until the content of the first or last place in dobj is displayed in mask.

If the data object dobj is a string, it is shortened when it is shifted to the left but not lengthened when it is shifted to the right. This means the content of mask can also be moved out of a string to the right.

In character string processing, mask is a character-like expression position. It is case-sensitive and any trailing blanks in mask are respected. If the content of the first or last place in dobj is not in mask, the content of text remains unchanged. More specifically, nothing is shifted if mask is an empty string.

Example

After it has been shifted to the right, text contains the value "_______I know you" and has a length of 15 characters.

DATA text TYPE string VALUE `I know you know `.

SHIFT text RIGHT DELETING TRAILING 'no kw'.

Example

The following example removes first the trailing blank and then the leading blanks from a string. For strings without leading blanks, this sequence of statements can be used to remove trailing blanks.

DATA txt TYPE string VALUE `XXXXX     `.

SHIFT txt RIGHT DELETING TRAILING ` `.
SHIFT txt LEFT  DELETING LEADING  ` `.

◉ SPLIT

Syntax

SPLIT dobj AT sep INTO
      { {result1 result2 [...]} | {TABLE result_tab} }
      [IN {CHARACTER|BYTE} MODE].

Addition:

... IN {CHARACTER|BYTE} MODE

Effect

The content of the operand dobj is separated into segments in accordance with the sequence of separators in sep. The results are either stored in individual target fields result1 result2 ... or in the rows of an internal table result_tab.

◉ At least two target fields result1 result2 ... must be specified. The following can be specified for the target fields:
     ◉ Existing character-like or byte-like variables.
     ◉ Inline declarations with DATA(var). If IN CHARACTER MODE is used, the declared variables are of the type string; if IN BYTE MODE is used, they are of the type xstring.
◉ The following can be specified for the internal table result_tab:
    ◉ An existing internal table with character-like or byte-like row type. It must be a standard table without secondary table keys. The table is initialized before being split.
    ◉ An inline declaration with DATA(var). A standard table is declared with the table row as the primary table key (and without secondary table keys). If IN CHARACTER MODE is used, the row type is of the type string; if IN BYTE MODE is used, it is of the type xstring.

dobj and sep are character-like expression positions.

The system searches the operand dobj from left to right for all occurrences of the content of the operand sep. The search is case-sensitive. All segments from the start of the operand to the first occurrence, between the occurrences, and from the last occurrence to the end of the operand are assigned one by one to the individual target fields result1 result2 ..., or appended to the internal table result_tab.

◉ If the target fields result1 result2 ... or the rows of the internal table result_tab have a fixed length and this length is not enough for the segment, the segment is truncated on the right and sy-subrc is set to 4. If the length is greater than the length of the segment, it is padded with blanks or hexadecimal 0 on the right. If the target fields result1 result2 ... or the rows of the internal table result_tab are strings, their length is adjusted to match the length of the associated segment.

◉ If there are not enough target fields result1 result2 ... to include all the segments, dobj is only split until all the target fields result1 result2 ... have been assigned values, except for the last target field. The remaining content of dobj is assigned to the final operand, without being split.

◉ The segments are assigned directly while ignoring the conversion rules.

◉ If more target fields result1 result2 ... are specified than required, the surplus target fields with fixed lengths contain blanks or hexadecimal 0 after the assignment. Any surplus strings are initial.

If the content of the operand sep is found immediately at the start of dobj, or occurs in direct succession in dobj, the result of the separation is an empty string. If the content of sep is at the end of dobj, the search is terminated and no further separation takes place to the right of this point.

If the content of the operand sep is not found or is an empty string, the result of the split is a single segment that contains the whole content of dobj, and which is assigned to the first individual target field or the first row of the internal table.

When strings are processed, trailing blanks are respected in separators sep of fixed length. If the operand dobj has a fixed length, any trailing blanks are ignored both in the operand dobj and in the new segments. If the operand dobj has the type string, any trailing blanks are respected both in the operand dobj and in the new segments.

System Fields

sy-subrc - Meaning

0  - The segments were passed to the target fields or the internal table without being truncated.
4 - At least one of the segments was truncated on the right when being passed to the target fields or internal table.

Notes

◉ If enough target fields are specified or the segments have been saved to an internal table, the number of segments created is defined by the number of separators found, as follows:
     ◉ If the last occurrence is not at the end of the operand, the number of segments matches the number of occurrences plus 1.
     ◉ If the last occurrence is at the end of the operand, the number of segments matches the number of occurrences.
◉ In the case of target fields or table rows with the type n, d, or t, the type-dependent assignment rules and initial values are not relevant. The segments are assigned without being converted and any surplus target fields are filled with blanks.

◉ All single fields result1 result2 ... specified are given values.

◉ To access the segments of a character string directly in an operand position, a segment function can be used that includes some of the functions of the statement SPLIT.

Example

The text field text is separated at its blanks, firstly into the three strings str1, str2, and str3, and then into an internal table with the row type string. Since the three strings are not enough for all seven parts, str3 contains "drag it is getting old" after the separation, while the internal table contains seven rows; one for each word in text.

DATA text TYPE string.

text = `What a drag it is getting old`.

SPLIT text AT space INTO: DATA(str1) DATA(str2) DATA(str3),
                         TABLE DATA(itab).

Example

A text string text1 and a text field text2 have the same content and are segmented into a table in the same way. When the text string is segmented, the segments contain trailing blanks. This is not the case when the text field is segmented.

DATA:
  text1 TYPE string      VALUE ` 1 : 2 : 3 `,
  text2 TYPE c LENGTH 11 VALUE ' 1 : 2 : 3 '.

SPLIT text1 AT ':' INTO TABLE DATA(segments).
LOOP AT segments INTO DATA(segment).
  cl_demo_output=>write( segment && `<--` ).
ENDLOOP.

cl_demo_output=>line( ).

SPLIT text2 AT ':' INTO TABLE segments.
LOOP AT segments INTO segment.
  cl_demo_output=>write( segment && `<--` ).
ENDLOOP.

cl_demo_output=>display( ).

Example

The following SPLIT statement extracts all strings of digits in a text string to an internal table.

DATA(text) = `aaa123bbb456ccc789`.
cl_demo_input=>request( CHANGING field =  text ).

SPLIT condense( replace( val   = text
                         regex = `\D`
                         with  = ` `
                         occ = 0 ) ) AT ` ` INTO TABLE DATA(itab).

cl_demo_output=>display( itab ).

Addition

... IN {CHARACTER|BYTE} MODE

Effect

The optional addition IN {CHARACTER|BYTE} MODE determines whether character string or byte string processing is performed. If the addition is not specified, character string processing is carried out. Depending on the type of processing, the operands dobj, sep, and the target fields result1 result2 ... or the rows of the internal table result_tab must be byte-like or character-like.

Example

The byte string xstr is split at bytes with the value hexadecimal 20, which stands for a blank in code page UTF-8, into an internal table with row type xstring.

DATA(xstr) = cl_abap_codepage=>convert_to( `Like a Hurricane` ).

SPLIT xstr AT CONV xstring( '20' )
              INTO TABLE DATA(xtab) IN BYTE MODE.

LOOP AT xtab INTO xstr.
  cl_demo_output=>write( cl_abap_codepage=>convert_from( xstr ) ).
ENDLOOP.
cl_demo_output=>display( ).

◉ CONDENSE

Syntax

CONDENSE text [NO-GAPS].

Effect

In the variable text, any leading and trailing blanks are removed and any other blanks that follow each other directly are replaced by exactly one blank or, if NO-GAPS is specified, are also removed completely.

The data object text must be character-like. If the data object has a fixed length, any space created by the condense operation is padded with blanks on the right. If the data object is of the type string, its length is adapted to the result of the condense operation.

Note

A character string can also be condensed in an operand position using a condense function which includes the functions of the statement CONDENSE.

Example

The flat structure sentence contains only character-like components and can therefore be assigned to the string text. After the statement CONDENSE is executed, text contains "She feeds you tea and oranges". Before the condense operation, the words in text are 30 places apart from one another.

DATA: BEGIN OF sentence,
        word1 TYPE c LENGTH 30 VALUE 'She',
        word2 TYPE c LENGTH 30 VALUE 'feeds',
        word3 TYPE c LENGTH 30 VALUE 'you',
        word4 TYPE c LENGTH 30 VALUE 'tea',
        word5 TYPE c LENGTH 30 VALUE 'and',
        word6 TYPE c LENGTH 30 VALUE 'oranges',
      END OF sentence,
      text TYPE string.

text = sentence.
CONDENSE text.

◉ CONVERT TEXT

Syntax

CONVERT TEXT text INTO SORTABLE CODE hex.

Effect

The content of the operand text is converted to a sortable byte string and the result is assigned to the target field hex. The data object text itself remains unchanged.

The operand text must be of the type c or string. text is a character-like expression position. The following can be specified for hex:

◉ An existing byte-like variable.

◉ An inline declaration DATA(var), where a variable of type xstring is declared.

The content of text must contain valid characters. The sortable byte string is defined by platform such that a size comparison or standard sort of multiple of these fields produces an order in which the source fields text are sorted by the locale defined in the current text environment. The text environment is set when an internal session is opened or by using the statement SET LOCALE.

If the target field hex is of type x and its length is not sufficient for the byte string, a non-handleable exception is raised. If the length is greater than that of the byte string, it is filled with hexadecimal 0 on the right. The minimum length for all platforms is calculated as 24 times the length of text plus 24, but can be significantly shorter for some platforms. If hex is of type xstring, its length is adapted automatically.

Note

The CONVERT TEXT statement is designed to fill an index column (in internal tables) by which the rows of the table can be sorted by locale. Since internal tables can also be sorted by locale directly by using the AS TEXT addition of the SORT statement, CONVERT TEXT is especially useful in the following cases:

◉ If an internal table is sorted by locale and then subjected to a binary search using the statement READ TABLE or using a table expression.
◉ An internal table is to be sorted more than once by locale. Using a sortable byte string ensures better performance than when using the AS TEXT addition.
◉ Indices for database tables are to be structured in accordance with a locale.
◉ Textual comparisons are to be made between character-like data objects.

Example

The characters "a" and "Z" are compared directly after they have been converted by CONVERT TEXT. While "Z" is in front of "a" for the majority of code pages in direct comparisons, the comparison of the converted values returns the expected textual order. See also the executable example for SORT for internal tables.

IF 'a' > 'Z'.
  cl_demo_output=>write_text( `'a' > 'Z'` ).
ELSE.
  cl_demo_output=>write_text( `'a' < 'Z'` ).
ENDIF.

CONVERT TEXT: 'a' INTO SORTABLE CODE DATA(ax),
              'Z' INTO SORTABLE CODE DATA(zx).

IF ax > zx.
  cl_demo_output=>write_text( `'a' > 'Z'` ).
ELSE.
  cl_demo_output=>write_text( `'a' < 'Z'` ).
ENDIF.

cl_demo_output=>display( ).

Non-Handleable Exceptions

◉ Cause: One of the operands has an invalid data type.
Runtime error: CONVERT_TEXT_BAD_OP

◉ Cause: The source field has invalid content.
Runtime error: CONVERT_TEXT_BAD_SRC

◉ Cause: The target field is too small.
Runtime error: CONVERT_TEXT_DEST_TOO_SHORT

◉ OVERLAY

Syntax

OVERLAY text1 WITH text2 [ONLY mask].

Effect

Characters in the variable text1 are replaced by the characters in the operand text2 that are in the same place there. text2 is a character-like expression position.

If the addition ONLY is not specified, all blanks in text1 are replaced. If the addition ONLY is specified, all characters are replaced that occur in the operand mask. This is case-sensitive. mask is also a character-like expression position.

If the lengths of text1 and text2 are different, text1 is processed using the shorter length only.

The operands text1, text2, and mask have to be character-like operands. In operands of fixed length, trailing blanks are respected. If mask is an empty string, no replacements are made.

System Fields

sy-subrc - Meaning

0 - At least one character in text1 is replaced.
4 - No characters in text1 are replaced.

Example

After the text field has been assigned to the time field, it contains the invalid time "12__00" (according to the conversion rules). As a result of the overlay with the initial_time constants, the two blanks are replaced by "00" and the result is the valid time "120000".

CONSTANTS initial_time TYPE t VALUE IS INITIAL.
DATA: time        TYPE t,
      text TYPE c LENGTH 4.

text = '12'.
time = text.
OVERLAY time WITH initial_time.

◉ TRANSLATE

Syntax

TRANSLATE text {TO {UPPER|LOWER} CASE}
             | {USING mask}.

Extras:

1. ... TO {UPPER|LOWER} CASE


2. ... USING mask

Effect

This statement translates the individual characters of the character-like data object text. Either CASE can be used to translate the case or USING can be used for a translation by pattern. The variable text must be a character-like variable.

Addition 1

... TO {UPPER|LOWER} CASE

Effect

If UPPER is specified, all lowercase letters of the data object text are translated to uppercase. If LOWER is specified, all uppercase letters are translated to lowercase.

Note

To modify the case of the letters in a character string in an operand position, a case function can be used that includes the functions of the statement TRANSLATE.

Example

After the translation, the variable text contains "CAREFUL WITH THAT AXE, EUGENE".

DATA text TYPE string.
text = `Careful with that Axe, Eugene`.
TRANSLATE text TO UPPER CASE.

Addition 2

... USING mask

Effect

If USING is specified, the characters in text are translated in accordance with the rule specified in the data object mask. mask is a character-like expression position whose value is interpreted as a string of character pairs. A search is performed in text, beginning with the first pair, for the first character in every pair. Every occurrence is replaced with the second character of the pair. The search is case-sensitive. If mask contains a character multiple times as the first character of a pair, only the first pair is respected. A character in text that has already been replaced cannot be replaced again in the same TRANSLATE statement. Therefore, if the second character of a pair in mask appears as the first character of a subsequent pair, the second pair affects only the original characters in text.

Trailing blanks in data objects text and mask are respected for data objects. If mask contains an odd number of characters, the last character is ignored. If mask is an empty string, no replacements are made.

Note

To translate a character string in an operand position, a translate function that includes the functions of the statement TRANSLATE can also be used.

Example

Translates the characters "A" to "B", "a" to "b" and back. After implementation, text contains "Abracadabra".

DATA text TYPE string.
text = `Barbcbdbarb`.
TRANSLATE text USING 'ABBAabba'.

◉ WRITE - TO

Syntax

WRITE {source|(source_name)} TO destination
                             [format_options].

Effect

The statement WRITE TO prepares the content of a source either using

◐ predefined formats or

◐ explicit formatting options format_options

as a character string and assigns the formatted content to the character-like data object destination.

The source field can be specified either statically or dynamically:

◐ source

If static, the field is specified directly as source. source is a general expression position with the restriction that no numeric literals, arithmetic expressions, or bit expressions can be specified directly.

◐ source_name

If dynamic, the field is specified as the content of a parenthesized flat character-like data object source_name, which must contain the name of the data object in question (non-case-sensitive). If the data object specified in source_name does not exist, no assignment takes place and sy-subrc is set to 4. When evaluating source_name, the same applies as to a dynamically specified (name) in the statement ASSIGN. The following can be specified for source_name:

     ◐ Literal or constants

If the data object source_name is specified as a text field literal or as a constant, it can be evaluated statically and the specified data object is identified as the used object.

    ◐ Variable

If the data object source_name is specified as a variable, it is specified only dynamically and the content is not evaluated statically.

When the statement is executed, name is not evaluated until runtime (in both cases).

Only the data types grouped together under the simple generic type can be used as source fields.

◐ All flat data types; flat structures are handled like a data object of type c and can only contain any character-like components.
◐ The data types STRING and XSTRING
◐ enumeration types; the name (maximum three characters) of the enumerated constant is used in uppercase letters, which defines the the current enumerated value.

The target field destination must be character-like and flat‎.

◐ If the length of the target field destination is less than the length of the data object format either predefined or specified using format_options, the output (when formatted and assigned) is shortened in accordance with a type-specific cutoff behavior

◐ If the available length exceeds the length required for formatting, the system places the result in the target field based on the predefined or self-defined alignment and fills the non-required space with blanks.

System Fields

sy-subrc - Meaning

0 - The data object specified in source_name was found and the assignment was performed.
4 - The data object specified in source_name could not be found and the assignment was not performed.

If the static source is specified, sy-subrc is not set.

Notes

◐ Although the statement WRITE TO does not send any data to an output medium, the system may execute a conversion routine.

◐ The statement WRITE TO is primarily designed for formatting data for output purposes but not for further internal processing. For example, a field can no longer be handled as a numeric data object if the decimal separator is displayed as a comma.

◐ No numeric literals can be specified between WRITE and TO. This is because, in this case, the statement cannot be distinguished from a WRITE statement with a specified position.

◐ Use of the WRITE TO statement can be replaced in most cases by embedded expressions in string templates of string expressions.

◐ It is possible to use string templates as source fields for the statement WRITE TO, but this is not usually recommended since both WRITE TO and string templates are used to format data and only a single method should be used. It is best to use string templates only.

Example

After the assignment, the variables date_short and date_long contain the current local date in the order specified in the user master record. The variable date_long contains the separators defined there. The variable date_short does not contain any separators since their length is not sufficient. The content of the variable date_mask is formatted according to the formatting addition DD/MM/YY, for which their length is sufficient.

DATA: date_long  TYPE c LENGTH 10,
      date_short TYPE c LENGTH 8,
      date_mask  TYPE c LENGTH 8.

WRITE sy-datlo TO: date_short,
                   date_long,
                   date_mask DD/MM/YY.

Exceptions

Handleable Exceptions

CX_SY_WRITE_INVALID_STYLE

- Cause: Incorrect output format for decimal floating point numbers with the STYLE addition.
Runtime error: WRITE_INVALID_STYLE

CX_SY_CONVERSION_NO_NUMBER

- Cause: Invalid format of the source field in the output of a decimal floating point number.
Runtime error: CONVT_NO_NUMBER

CX_SY_CONVERSION_OVERFLOW

- Cause: Target field is too short to display a decimal floating point number.
Runtime error: CONVT_OVERFLOW

Non-Handleable Exceptions

- Cause: Negative length specified when offset/length is specified.
Runtime error: WRITE_TO_LENGTH_NEGATIVE

- Cause: Negative offset specified when offset/length is specified.
Runtime error: WRITE_TO_OFFSET_NEGATIVE

- Cause: Offset specified when offset/length is specified is greater than the field length.
Runtime error: WRITE_TO_OFFSET_TOOLARGE

- Cause: CURRENCY parameter in WRITE does not have type c
Runtime error: WRITE_CURRENCY_ILLEGAL_TYPE

- Cause: Rounding parameter less than -14
Runtime error: WRITE_ROUND_TOO_SMALL

- Cause: UNIT parameter in WRITE does not have type c
Runtime error: WRITE_UNIT_ILLEGAL_TYPE

- Cause: Incomplete rules for a time zone when using the addition TIME ZONE
Runtime error: CONVERT_TSTMP_INCONSISTENT_TAB

◉ SET BIT

Syntax

SET BIT bitpos OF byte_string [TO val].

Effect

This statement sets the bit in bit position bitpos of the variable byte_string to 1 or, if specified, to the value of data object val. The other bits in byte_string remain unchanged.

The data object byte_string must be byte-like, whereas bitpos and val are numeric expression positions of operand type i. The value of bitpos must be greater than 0 and val must be either 0 or 1. Otherwise a non-handleable exception is raised. The bit positions in byte_string are counted from the beginning of the data object. If the value of bitpos is greater than the number of bits in byte_string, no bit is replaced and sy-subrc is set to 4.

System Fields

sy-subrc - Meaning

0 - A bit in data object byte_string was set at position bitpos.
4 - The value of bitpos is greater than the number of bits in byte_string, therefore no bit was set.

Notes

- If byte_string has the deep type xstring, a bit is set in the referenced byte string, not in the reference.

- Setting single bits is especially suited to preparing operators in bit expressions.

- The predefined function bit-set can also be used to set an individual bit.

Example

In the data object hex with eight bits, the bit is set to the value 1 at the position determined by the loop counter sy-index, with all other bits set to 0. The output is "80 40 20 10 08 04 02 01".

DATA hex        TYPE x LENGTH 1.
DATA output TYPE string.
DO 8 TIMES.
  CLEAR hex.
  SET BIT sy-index OF hex.
  output = output && |{ hex } |.
ENDDO.
cl_demo_output=>display( output ).

Exceptions

Non-Handleable Exceptions

- Cause: The bit position is greater than or equal to 0.
Runtime error: BIT_OFFSET_NOT_POSITIVE

- Cause: The value of val is not equal to 0 or 1.
Runtime error: BIT_NO_ZERO_OR_ONE

◉ GET BIT

Syntax

GET BIT bitpos OF byte_string INTO val.

Effect

This statement reads the bit at the bit position bitpos of the data object byte_string and assigns its value to the target field val. val can be specified as follows:

- An existing variable that expects the data type i.

- An inline declaration DATA(var), where a variable of type i is declared.
The data object byte_string must be byte-like. bitpos is a numeric expression position of operand type i. The value of bitpos must be greater than 0, otherwise non-handleable exceptions are raised. The bit positions in byte_string are counted from the beginning of the data object. If the value of bitpos is greater than the number of bits in byte_string, no bit is read and sy-subrc is set to 4. System Fields

System Fields

sy-subrc - Meaning

0 - The value of the bit at position bitpos of byte_string was placed in the result field val.
4 - The value of bitpos is greater than the number of bits in byte_string, therefore no bit was read.

Notes

If byte_string has the deep type xstring, a bit is read from the referenced byte string, not from the reference.

The reading of single bits is especially intended for evaluating the results of bit expressions.

Example

The hexadecimal value "1B" is assigned to the data object hex and its bits are read from front to back. The output is "00011011", which corresponds to the binary display of decimal 27 or hexadecimal "1B".

DATA hex TYPE xstring VALUE `1B`.
DATA(len) = xstrlen( hex ) * 8.
DATA output TYPE string.

WHILE sy-index <= len.
  GET BIT sy-index OF hex INTO DATA(res).
  output = output && res.
ENDWHILE.
cl_demo_output=>display_text( output ).

Exceptions

Non-Handleable Exceptions

- Cause: n is smaller than or equal to 0.
Runtime error: BIT_OFFSET_NOT_POSITIVE

3.3 Expressions and Functions for String Processing

Expressions and functions for character string processing can be processed at many operand positions.

3.3.1 string_exp - String Expressions

Syntax

... { |string_template| }
  | { operand1 && operand2 [&&  operand3 ... ] } ...

Effect

A string expression formulates an operation (calculation) with character-like operands. The result of a string expression is a character string. A string expression consists of

- precisely one string template |string_template| or

- two or more operands operand1, operand2 ..., concatenated as a character string using the string operator &&, where one of the operands can itself be a string template.

String expressions can occur in the reading positions of certain statements, in particular on the right side of an assignment with the assignment operator =.

The operands of string expressions must have character-like data types. Structures with character-like-only flat components cannot be specified.

Notes

- The program DEMO_EXPRESSIONS shows examples of how to use string expressions.

- String expressions and arithmetic expressions can not be mixed. Arithmetic expressions can, however, also be specified as embedded expressions of string templates. In addition, the formatting options of embedded expressions or string functions in operand positions can contain arithmetic expressions as arguments.

- The conversion operator CONV can be applied to a structure with character-like-only components to use it as an operand of a string expression.

- If the right side of an assignment appends strings to a variable specified with the type string on the left side of the assignment, the variable is used directly in specific cases without producing a subtotal. Be careful to preserve this optimization, especially in loops.

Example

Outputting a character string expression. It is a chaining of a string literal with a string template that contains an embedded expression.

cl_demo_output=>display(

   `Hello ` && |{ sy-uname }!|

   ).

◉ string_exp - String Templates

Syntax

|[literal_text][ embedded_expressions][ control_characters]|

Effect

A string template is enclosed by two characters "|" and creates a character string that is used by the string expression instead of the string templates. The characters of this character string consist of any sequence of the following syntax elements of the string template:

- Literal text literal_text
- Embedded expressions embedded_expressions
- Control characters control_characters

A string template that starts with "|" must be closed with "|" within the same line of source code. The only exceptions to this rule are line breaks in embedded expressions. There are, however, no length restrictions on a string template. The literal operator & or the chaining operator && can be used to join multiple string templates in a single string template. A string template can be defined across multiple lines of source code and be given comments.

Notes

- To represent the delimiter "|" as a string template with in literal text, it must be prefixed with the escape character \.
- The delimiter characters "|" can be formatted in the ABAP Editor by choosing Fonts and Colors → Token operator to highlight them in the source code.
- Using the character string function escape, all special characters for character string templates can be put in front of their escape character.
- When string templates are joined, the literal operator & behaves differently than with literals. This operator is executed at runtime (like the chaining operator &&) and not during compilation as a one-off process. This means the restriction of 255 characters for literals no longer applies. The two joins
|...| &  |...|
|...| && |...|

are identical for string templates. For literal-only content, they are the same as

`...` && `...`

However they are different to

`...` &  `...`
'...' &  '...'
'...' && '...'

In the first two cases, a length restriction of 255 characters applies. In the third case, trailing blanks are ignored.

- If the right side of an assignment appends strings using string templates to a variable specified with the type string on the left side of the assignment, the variable is used directly in some cases without producing a subtotal. Be careful to preserve this optimization, especially in loops.

Example

String template with literal text and an embedded expression. The result is made up of the content of the literal text and the result of the embedded expression.

DATA(result) = |Hello { sy-uname }!|.

Example

The following piece of source code shows three similar string templates that all display the character string "Hello World!". The first string template contains the entire character string as literal text. The next two string expressions distribute the literal text across multiple parts of the string template. The literal operator & is used to join them as the first string template.

DATA(result1) = |Hello World!|.

DATA(result2) = |Hello| & | | & |World| & |!|.

DATA(result3) = |Hello| & "sub template 1
                | |     &
                |World| & "sub template 3
* sub template 4:
                |!|.

⬥ string_exp - String Operator

One string operator is currently available for string expressions.

Syntax

... operand1 && operand2 ...

Effect

The chaining operator && concatenates two operands in a string expression as a character string.

The operand positions operand are enhanced character-like expression positions, which means that character-like data objects, string expressions, and predefined functions, or functional methods and method chainings whose return code has a character-like data type, can be specified. Furthermore, operands of non-character-like data types can be specified, which are convertible to the type string.

In the case of character-like operands with a fixed length, trailing blanks are ignored. All operands that do not have a character-like data type are converted to the type string before the chaining is performed.

Notes

- In particular, string templates can be specified as operands.
- String functions with character-like return codes are the best choice when specifying predefined functions as operands.
- Do not confuse the chaining operator && with the literal operator &, which joins two character literals as a literal. The literal operator is generally used if a literal string template is to be defined across multiple program lines. The operator is executed only once, when the program is compiled and trailing blanks of character literals are always respected. A string expression with a chaining operator, on the other hand, is recalculated each time (like all expressions) and can be used to concatenate any number of character-like operands.
- If the right side of an assignment appends strings using && to a variable specified with the type string on the left side of the assignment, the variable is used directly in some cases without producing a subtotal. Be careful to preserve this optimization, especially in loops.

Example

Concatenation of four operands as the string "Hello world!". The last operand is a string template that has only literal content.

DATA text TYPE string VALUE `Hello`.

text  = text && ` ` &&  'world' && |!|.

◉ string_exp - Performance Note

If the right side of an assignment appends strings in a string expression to a variable str with the type string specified on the left side of the assignment, the variable is used directly in the following cases without producing a subtotal.

- str = str && dobj1 && dobj2 && ... .
- str = |{ str }...{ dobj1 [format_options] }...{ dobj2 [format_options] }...|.

The appended strings cannot be dependent on str (as known by the compiler):

- The target field str can occur only once in the string expression, and only at the very beginning.
- Formatting options cannot be used on str in a string template.
- Only directly specified data objects dobj1, dobj2, and so on can be used, even if they are not dependent on str. No other expressions or functions can be used.

Optimization is therefore deactivated in the following cases:

- str = str && ... && meth( ... ) && ... .
- str = str && ... && str && ... .
- str = |{ str }...{ expr( ... ) }...|.
- str = |{ str format_options }...|.
- str = |{ str }...{ str }...|.

Although it is not usually a problem to deactivate optimization for assignments that are not used very often, we strongly recommend only using optimizable forms within loops, because otherwise the runtime would increase quadratically with the number of iterations. In loops, the results of expressions and functions that can only be determined there should be assigned to helper variables dobj1, dobj2, and so on, and these should be used.

Note

If optimization is deactivated, the runtime is related quadratically to the number of iterations, since the length of the string in the subtotal increases in proportion with the number of iterations and has to be copied to the result in every loop pass.

Example

In the following example, where an HTML file is generated, there is no optimization. The runtime is quadratically related to the number of iterations n.

DATA(n) = 10000.

DATA(html) = `<html><body><table border=1>`.
DO n TIMES.
  html = |{ html }<tr><td>{
            CONV string( ipow( base = sy-index exp = 2 ) )
            }</td></tr>|.
ENDDO.
html = html && `</table></body></html>`.

The following example shows how the runtime can be optimized by using a helper variable. The bigger n is, the greater the difference is from the runtime in the previous example.

DATA(n) = 10000.

DATA(html) = `<html><body><table border=1>`.
DATA square type string.
DO n TIMES.
  square = ipow( base = sy-index exp = 2 ).
  html = |{ html }<tr><td>{ square }</td></tr>|.
ENDDO.
html = html && `</table></body></html>`.

3.3.2 String Functions

String functions are part of the predefined functions. They are divided into:

◈ Description functions
◈ Processing functions

Description functions have a numerical result; processing functions have a character-like result.

Parameters that are used by more than one function in the same way are described under Shared Parameters of String Functions.

Note: The program DEMO_EXPRESSIONS shows you examples of how to use string expressions.

3.3.2.1 Description Functions for Character-Like Arguments

◈ charlen, dbmaxlen, numofchar, strlen -

These length functions have an unnamed character-like argument.

Syntax

... func( arg ) ...

Effect

The following table shows the length functions with one unnamed argument. The arguments arg of all length functions except dbmaxlen are character-like expression positions. The argument of dbmaxlen is a character-like functional operand position. The return code has the type i for all length functions.

Function func Return Value 
charlen Length of first character of arg in the code page used: 1 for a single Unicode character; 2 for surrogate pairs.
dbmaxlen   Maximum length of a string defined in ABAP Dictionary (RAWSTRING, SSTRING, STRING). If the string is unrestricted, the constant abap_max_db_string_ln or abap_max_db_rawstring_ln from the type group ABAP is returned. The latter is also returned for the predefined ABAP types string and xstring. 
numofchar   Number of characters in arg, where trailing blanks are not counted in data objects with fixed lengths or in data objects with the type string. 
strlen  Number of characters in arg, where trailing blanks in data objects with fixed lengths are not counted. They are counted though in data objects with the type string. 

Note

The functions described here are some of the functions that can be used in the obsolete extended functional operand positions, even if their argument is a single data object.

Example

The results of the following length determinations are 10 and 5.

DATA:
  str TYPE string      VALUE `12345     `,
  txt TYPE c LENGTH 10 VALUE '12345     '.

cl_demo_output=>display( |{ strlen( str )
                       }, { strlen( txt ) }| ).

◈ char_off - Length Function

This length function has a named character-like argument.

Syntax

... char_off( val = text add = pos [off = off] ) ...

Effect

The function returns the offset of the character in text that is pos places away from the character in the offset specified in off. The default value for off is 0. text is a character-like expression position. pos and off are numeric expression positions with the type i.

This function can be specified in general and numeric expression positions. The return value has the type i.

The value of pos can be positive and negative and has the appropriate number of places on the right or on the left. If pos identifies a positions outside of text, the function returns the value -1. If off is greater than the length of text, an exception of the class CX_SY_RANGE_OUT_OF_BOUNDS is raised.

Note

The function char_off was suitable for finding the correct offsets of characters in non-Unicode double-byte systems.

Example

The result of the following function call is 7.

DATA(result) = char_off( val = `12345678` add = 4 off = 3 ).

◈ find, find_... - Search Functions

Syntax Forms

1. ... find( val = text  { sub = substring}|{ regex = regex} [case = case]
           [ off = off] [ len = len] [occ = occ] ) ...

2. ... find_end( val = text regex = regex [case = case]
               [ off = off] [ len = len] [occ = occ] ) ...

3. ... find_any_of( val = text  sub = substring
                  [ off = off] [ len = len] [occ = occ] ) ...

4. ... find_any_not_of( val = text  sub = substring
                      [off = off] [ len = len] [occ = occ] ) ...

Effect

The search functions find and find_... scan text for the characters specified in substring or for a match with a regular expression specified in regex, with the scanned subarea specified in the optional parameters off and len and the occurrence of the match specified in the optional parameter occ.

The return code has the type i and is filled as follows:

◉ The function find searches for the exact substring specified in substring or for a match with the regular expression regex and returns the offset of the occurrence with respect to the full length of text. The search is case-sensitive by default, but this can be overridden using the parameter case. If substring is empty, an exception from the class CX_SY_STRG_PAR_VAL is raised.

◉ The function find_end searches like find, however it returns the sum of the offset of the location and the length of the correspondence to the regular expression.

◉ The function find_any_of returns the offset of the occurrence of any character in substring, and is always case-sensitive. If substring is empty, the value -1 is returned.

◉ The function find_any_not_of returns the offset of the found occurrence of any character not in substring, and is always case-sensitive. If substring is empty, the value -1 is returned.

If a search is not successful, all functions return the value -1.

The optional parameters off, len, and occ have the following meaning when combined:

◉ If occ is positive, the subarea defined by off and len is searched from left to right.
◉ If occ is negative, the subarea defined by off and len is searched from right to left.

The occurrence of the match specified by occ refers to the search area defined by off and len.

Notes

◉ Using the related search functions count and count_..., it is possible to determine the total number of occurrences instead of an offset.
◉ Like the statement FIND, the search functions can be faster than the relational operator CS cy some magnitude.

Example

The result of the following function calls is 3, 6, 3, and 3.

DATA(result1) = find(            val = `xxx123yyy` regex = `\d+` ).
DATA(result2) = find_end(        val = `xxx123yyy` regex = `\d+` ).
DATA(result3) = find_any_of(     val = `xxx123yyy` sub   = `123` ).
DATA(result4) = find_any_not_of( val = `xxx123yyy` sub   = `x`   ).

Exceptions

Handleable Exceptions

CX_SY_RANGE_OUT_OF_BOUNDS

◉ Cause: Illegal offset or length specified in off and len.
Runtime error: STRING_OFFSET_TOO_LARGE

CX_SY_REGEX_TOO_COMPLEX

◉ Cause: More information: Exceptions in Regular Expressions.
Runtime error: REGEX_TOO_COMPLEX

CX_SY_STRG_PAR_VAL

◉ Cause: Substring in sub or regular expression in regex is empty or occurrence in occ is 0.
Runtime error: STRG_ILLEGAL_PAR

◈ count, count_... - Search Functions

Syntax Forms

1. ... count( val = text  {sub = substring}|{ regex = regex} [case = case]
             [ off = off] [len = len] ) ...

2. ... count_any_of( val = text  sub = substring
                    [off = off] [ len = len] ) ...

3. ... count_any_not_of( val = text  sub = substring
                        [ off = off] [len = len] ) ...

Effect

Just like the search functions find and find_..., the search functions count and count_... search text in the entire string text or in a subarea defined using off and len for characters specified in substring or for a match with a regular expression specified in regex. Instead of an offset, they return the number of occurrences.

The return value has the type i.

Example

The result of the following function calls is 1, 3, and 6.

DATA(result1) = count(            val = `xxx123yyy` regex = `\d+` ).
DATA(result2) = count_any_of(     val = `xxx123yyy` sub   = `123` ).
DATA(result3) = count_any_not_of( val = `xxx123yyy` sub   = `x`   ).

Exceptions

Handleable Exceptions

CX_SY_RANGE_OUT_OF_BOUNDS

- Cause: Illegal offset or length specified in off and len.
Runtime error: STRING_OFFSET_TOO_LARGE

CX_SY_REGEX_TOO_COMPLEX

- Cause: More information: Exceptions in Regular Expressions.
Runtime error: REGEX_TOO_COMPLEX

CX_SY_STRG_PAR_VAL

- Cause: Substring in sub or regular expression in regex is empty or occurrence in occ is 0.
Runtime error: STRG_ILLEGAL_PAR

◈ distance - Distance Function

Syntax

... distance( val1 = text1 val2 = text2 [max = max] ) ...

Effect

This function returns the Levenshtein distance between two strings text1 and text2. This distance is the minimum number of insert, delete, and replace operations required to change one string to another and hence reflects the similarity of the two strings. The return value has the type i.

max can be used to declare a positive value other than 0; the calculation of the Levenshtein distance is canceled if the distance is greater than max. The value of max is returned instead. max is a numeric expression position of the type i. If the value of max is less than or equal to 0, an exception of the class CX_SY_STRG_PAR_VAL is raised. If max is not specified, the calculation is not terminated

Notes

◉ The complexity of the function increases in line with the product of the lengths of the two strings. The calculation time can be limited by declaring max.

◉ One typical application of this function is to find a word in a word set that is most similar to a word entered in a search.

Example

The result of the following function calls is 0, 1, and 3.

DATA(result1) = distance( val1 = `abcdefg` val2 = `abcdefg` ).
DATA(result2) = distance( val1 = `abcdefg` val2 = `abcXefg` ).
DATA(result3) = distance( val1 = `abcdefg` val2 = `aXcdXXg` ).

Exceptions

Handleable Exceptions

CX_SY_STRG_PAR_VAL

◉ Cause: Value in max is less than or equal to 0.
Runtime error: STRG_ILLEGAL_PAR

3.3.2.2 Processing Functions for Character-Like Arguments

The following processing functions have a character-like result. They can be declared at general expression positions and character-like expression positions. The return values have the type string.

◉ cmax, cmin - character-like extreme value functions
◉ condense - condense function
◉ concat_lines_of - concatenation function
◉ escape - escape function
◉ insert - insert function
◉ match - match function
◉ repeat - repeat function
◉ replace - replace function
◉ reverse - reverse function
◉ segment - segment function
◉ shift_left, shift_right - shift functions
◉ substring, substring_... - substring functions
◉ to_upper, to_lower, to_mixed, from_mixed - case functions
◉ translate - translate function

The function boolc also has a return value of the type string and is (in principle) also one of these functions. It is handled, however, in accordance with its role as a logical function.

Notes

◉ The processing functions for character-like arguments ignore the trailing blanks for input parameters with fixed lengths.

◉ The processing functions for character-like arguments cannot be used directly as operands of arithmetic expressions, but can be used as operands of descriptive functions.

◉ The type string of the return values must be respected mainly when using functions in comparisons.

🔸 cmax, cmin - Character-Like Extreme Value Functions

Syntax Forms

... cmax|cmin( val1 = text1  val2 = text2
              [ val3 = text3] ... [val9 = text9] ) ...

Effect

These functions return the value of the biggest or the smallest of the character-like arguments text1, text2, ... passed. The content of the arguments is compared from left to right in the evaluation. The first different character from the left decides which operand is bigger or smaller on the basis of the sequence in the code page used.

At least two arguments, text1 and text2, and a maximum of nine arguments must be passed. Here, the optional input parameters val3 through val9 must be filled in ascending order without gaps.

The return code has the type string.

Notes

- The extreme functions nmax and nmin can be used to determine numeric extreme values.

- Character-like extreme functions, like numeric extreme functions, can be traced back to the equivalent control structures using relational operators. Remember that, unlike with the usual ABAP comparison rules, shorter arguments of fixed lengths are not padded to the length of longer arguments by entering blanks.

- As with comparisons with relational operators, the current locale is not significant when determining the extreme values.

Example

The example demonstrates how the smallest and largest letters of a random set are found, represented here by the rows of an internal table. Of course, in this case the result can also be achieved by sorting the internal table, which is done here for verification.

TYPES itab TYPE TABLE OF char1 WITH EMPTY KEY.

DATA(rnd) = cl_abap_random_int=>create(
  seed = CONV i( sy-uzeit ) min = 0 max = strlen( sy-abcde ) - 1 ).

DATA(itab) = VALUE itab( FOR i = 1 UNTIL i > 9
                         LET off = rnd->get_next( ) IN
                         ( sy-abcde+off(1) ) ).

DATA(min) =  cmin( val1 = itab[ 1 ]
                   val2 = itab[ 2 ]
                   val3 = itab[ 3 ]
                   val4 = itab[ 4 ]
                   val5 = itab[ 5 ]
                   val6 = itab[ 6 ]
                   val7 = itab[ 7 ]
                   val8 = itab[ 8 ]
                   val9 = itab[ 9 ] ).

DATA(max) =  cmax( val1 = itab[ 1 ]
                   val2 = itab[ 2 ]
                   val3 = itab[ 3 ]
                   val4 = itab[ 4 ]
                   val5 = itab[ 5 ]
                   val6 = itab[ 6 ]
                   val7 = itab[ 7 ]
                   val8 = itab[ 8 ]
                   val9 = itab[ 9 ] ).

SORT itab BY table_line.

ASSERT min = itab[ 1 ].
ASSERT max = itab[ lines( itab ) ].

🔸 condense - Condense Function

Syntax

... condense( [val =] text [del = del] [from = from] [to = to] ) ...

Effect

This function returns the content of text, condensed as follows:

◉ All leading and trailing characters specified in del are removed from the string in text. If del is an empty string, no characters are removed.
◉ In the character string from text, all subsequences that are compiled from the characters in from are replaced by the first character in the character string specified in to. If from is an empty string, no characters are replaced. If to an empty string, the characters specified in from are removed from the character string.

The default values for del, from, and to are blank in each case. If del, from, and to are not specified, val = can also be omitted.

del, from, and to are character-like expression positions. If they have a fixed length, trailing blanks are ignored.

The return code has the type string.

Notes

◉ When the standard values for del, from, and to are specified, the condense function works in the same way as the CONDENSE statement without the NO-GAPS addition.

◉ By entering an empty string for from, the leading and trailing blanks can be removed without affecting the character string.

Example

The return codes of the following functions are "abc_def", "abc___def" and "abcXdef" (where "_" stands for a blank).

DATA  result TYPE string.

result = condense( val = `  abc   def  ` ).

result = condense( val = `  abc   def  ` from = `` ).

result = condense( val = `XXabcXXXdefXX`
                   del = 'X' from = 'X' to = 'X' ).

Example

The return code of the following function call is "Rock'n'Roll".

DATA(result) = condense( val  = `  Rock'xxx'Roller`
                         del = `re `
                         from = `x` to = `n`  ).

🔸 concat_lines_of - Concatenation Function

Syntax

... concat_lines_of( [table =] itab [sep = sep] ) ...

Effect

This function concatenates all row contents of an internal table itab and returns the result as a character string. itab expects an index table with character-like row type. itab is a functional operand position.

sep can be used to specify a string as a separator, which is then inserted between the rows. sep is a character-like expression position. If sep is not specified, the row contents of the internal table are appended to each other directly. If the row types or the argument sep have a fixed length, trailing blanks are ignored.

The formal parameter table must be specified explicitly only if sep is also specified.

The return code has the type string.

Example

This function returns "ABAP Objects".

TYPES: c80  TYPE c LENGTH 80,
       itab TYPE TABLE OF c80 WITH EMPTY KEY.

DATA(itab) = VALUE itab( ( 'ABAP' ) ( 'Objects' ) ).

cl_demo_output=>display(
  concat_lines_of( table = itab sep = ` ` ) ).

🔸 escape - Escape Function

Syntax

... escape( val = text format = format ) ...

Effect

This function gets the content of the character string in text, and hides certain special characters with escape characters according to a rule specified in format.

The possible values of format are defined as constants with the prefix "E_" in the class CL_ABAP_FORMAT. Each value defines which special characters are replaced, and how. There are rules for special characters in markup languages (XML and HTML), in URIs and URLs, in JSON, as well as in regular expressions and character string templates. An important part is also played by attack protection using Cross Site Scripting (XSS) on Web applications.

format expects data objects of the type i. An invalid value for format raises an exception of the class CX_SY_STRG_PAR_VAL. For all characters whose codes are between x00 and xFF, the program DEMO_ESCAPE demonstrates the effect of all associated formats from the class CL_ABAP_FORMAT. The top row contains the names of the constants from the class CL_ABAP_FORMAT without the prefix "E_". The other rows show the effect on the characters specified in the first two columns.

🔸 insert - Insert Function

Syntax

... insert( val = text sub = substring [off = off] ) ...

Effect

This function inserts the character string specified in substring in the string specified in text, either before the first character or at the optional offset specified off and returns the result. If substring is empty, the unchanged content of text is returned.

The return code has the type string.

Example

After the following iteration is executed, the result is in result "X X X X X".

DATA(result) = `XXXXX`.

result =
  REDUCE #( INIT r = result
            FOR j = 1 UNTIL j > strlen( result ) - 1
            NEXT r = insert( val = r sub = ` ` off = j * 2 - 1 ) ).

Handleable Exceptions

CX_SY_RANGE_OUT_OF_BOUNDS

- Cause: Invalid offset specified in places.
Runtime error: STRING_OFFSET_TOO_LARGE

🔸 match - Match Function

The match function returns a substring of a character-like argument that matches a regular expression.

Syntax

... match( val = text regex = regex [ case = case] [occ = occ] ) ...

Effect

The match function scans text for the matches (specified in occ) with the regular expression (specified in regex) and returns the substring found. The search is case-sensitive by default, but this can be overridden using the parameter case.

The return code has the type string.

Example

The following match function returns x2.

cl_demo_output=>display(
  match( val = 'x1 x2 x3' regex = 'x.' occ = 2 ) ).

Exceptions

Handleable Exceptions

CX_SY_REGEX_TOO_COMPLEX

- Cause: More information: Exceptions in Regular Expressions.
Runtime error: REGEX_TOO_COMPLEX
CX_SY_STRG_PAR_VAL

- Cause: Regular expression in regex is empty or there are 0 occurrences in occ.
Runtime error: STRG_ILLEGAL_PAR

🔸 repeat - Repeat Function

Syntax

... repeat( val = text  occ = occ ) ...

Effect

This function returns a character string that contains the content of text as many times as specified in occ. If text is an empty string or if occ contains the value 0, an empty string is returned.

occ is a numeric expression position of type i. If the value of occ is negative, an exception of the class CX_SY_STRG_PAR_VAL is raised.

The return code has the type string.

Note

The parameter occ has a different meaning here than in functions used for searching.

Example

The function repeat is used to create a string with ten blanks.

DATA(result) = repeat( val = ` ` occ = 10 ).

Exceptions

Handleable Exceptions

CX_SY_STRG_PAR_VAL

- Cause: Occurrences in occ are less than 0.
Runtime error: STRG_ILLEGAL_PAR

🔸 replace - Replace Function

Syntax Forms

1. ... replace( val = text [ off = off] [len = len]
               with = new ) ...

2. ... replace( val = text { sub = substring}|{ regex = regex}
               with = new [ case = case] [occ = occ] ) ...

Effect

This function replaces a substring of text with the character string specified in new and returns the changed text.

The substring is determined as follows:

◉ The variant with the arguments off and len replaces the substring defined by the offset off and the length len. At least one of these additions must be defined.

◉ The variant with the arguments sub or regex scans the text for the occurrence specified in occ for a match with the substring specified in substring or with a regulären Ausdruck specified in regex and replaces the occurrence. If occ contains the value 0, all occurrences are replaced. If substring is empty, an exception from the class CX_SY_STRG_PAR_VAL is raised. The search is case-sensitive by default, but this can be overridden using the parameter case. If no substring is found, the unchanged content of text is returned.

new is a character-like expression position. If they have a fixed length, trailing blanks are ignored.

The return code has the type string accordingly.

Notes

◉ Borderline cases for the variants with the arguments off and len:
    ◉ If only off is specified or if the value 0 is specified for len, replace works like insert.
    ◉ If only len is specified or if off has the value 0, the first segment of the length len is replaced.
    ◉ If the value of off is equal to the length of text, the value of len must be equal to 0 or len is not specified. The character string new is then attached to the end of text.
◉ If a regular expression is used with regex, special replacement models that allow references to particular occurrences can be specified in new. Note that, in regular replacement texts, the associated special characters $, &, `, and ´ plus the escape character \ must be transformed to literal characters using the prefix \.

Example

The result of the following replacement is "<title>Th<b>i</b>s <b>i</b>s the <i> T<b>i</b>tle</i> </title>". In an HTML line, a particular letter is placed in format tags if it is not itself in a tag.

DATA(html) = `<title>This is the <i>Title</i></title>`.

DATA(repl)   = `i`.

html = replace( val   = html
                regex = repl && `(?![^<>]*>)`
                with  = `<b>$0</b>`
                occ   =   0 ).

Exceptions

Handleable Exceptions

CX_SY_RANGE_OUT_OF_BOUNDS

- Cause: Illegal offset or length specified in off and len.
Runtime error: STRING_OFFSET_TOO_LARGE
CX_SY_REGEX_TOO_COMPLEX

- Cause: More information: Exceptions in Regular Expressions.
Runtime error: REGEX_TOO_COMPLEX
CX_SY_STRG_PAR_VAL

- Cause: Substring in sub or regular expression in regex is empty.
Runtime error: STRG_ILLEGAL_PAR

🔸 reverse - Reverse Function

Syntax

... reverse( [val =] text  ) ...

Effect

This function returns a character string that reverses the content of text.

The return code has the type string.

Example

Gets the Latin alphabet in reverse.

DATA(result) = reverse( sy-abcde ).

🔸 segment - Segment Function

Syntax

... segment( val = text index = idx [sep|space = delim] ) ...

Effect

This function returns the occurrence of a segment of the argument text specified by index. A segment is defined by delimiters. The beginning and ending of the string in text are the outer delimiters. Inner delimiters delim can be passed to sep or space as follows:

◉ If the argument sep is provided, the substring specified in delim is searched for (case-sensitive as in text) and used as a delimiter. If a substring specified in delim occurs directly one after another in text, an empty segment is created and an empty string is returned for this segment.

◉ If the argument space is provided, each individual character is searched for in delim (case-sensitive) and this is used as a delimiter. If the individual characters specified in delim occur directly one after another in text, no empty segment is created and no result is returned.

The delimiters are not part of the segments. If sep or space are not specified, the argument sep is filled implicitly a single blank. In both cases, specifying an empty string raises an exception of the class CX_SY_STRG_PAR_VAL.

If index is positive, the occurrences are counted from the left; if index is negative, the instance are counted from the right. The values 1, 2, .... indicate the first, second, ... occurrences. The values -1, -2, .... indicate the last, last but one, ... occurrences. If the value of index is 0, or the specified segment does not exist, an exception of the class CX_SY_STRG_PAR_VAL is raised.

delim is a character-like expression position and index is a numeric expression position with type i. If delim has a fixed length, trailing blanks are ignored.

The return code has the type string.

Note

If the substring specified in delim is not found, the entire character string forms a single segment. This segment can only be addressed by using the values 1 or -1 for index.

Example

The following function calls produce "AB", "CD", "EF", "GH", respectively, and raise an exception at the end.

DATA  result TYPE string.

DO.
  TRY.
      result = segment( val   = 'AB\brCD\brEF\brGH'
                        index = sy-index
                        sep = `\br` ).
      ...
    CATCH cx_sy_strg_par_val.
      EXIT.
  ENDTRY.
ENDDO.

DO.
  TRY.
      result = segment( val   = 'AB  CD - EF_GH'
                        index = sy-index
                        space = ` -_` ).
      ...
    CATCH cx_sy_strg_par_val.
      EXIT.
  ENDTRY.
ENDDO.

Handleable Exceptions

CX_SY_STRG_PAR_VAL

- Cause: Delimiter in sep is empty or the occurrence in index is 0 or was not found.
Runtime error: STRG_ILLEGAL_PAR

🔸 shift_left, shift_right - Shift Functions

The shift functions shift the content of a character-like argument.

Syntax Forms

1. ... shift_left( [val =] text
                  [places = places]|[circular = places]|[ sub = substring] ) ...

2. ... shift_right( [val =] text
                   [places = places]|[circular = places]|[ sub = substring] ) ...

Effect

The functions shift_left and shift_right shift the character string text to the left or to the right and return the result. The shift depends on the parameter pass as follows:

◉ If the argument places is given a number value, this many characters are removed on the left or right of the character string and the length of the character string is reduced accordingly. If the value of places is negative or longer than the character string, an exception of the class CX_SY_RANGE_OUT_OF_BOUNDS is raised.

◉ If the argument circular is given a number value, the functions work like places, but insert the substring again at the end or start of the character string.

◉ If the argument sub is given a character string in substring, all substrings in the character string from text are removed on the left or right that match the content of substring. If no substrings are found or if substring is given an empty string, the character string remains unchanged.

◉ If none of the arguments places, circular, or sub are specified, the functions work as if the sub argument has been passed a blank character. All blank characters on the left or right are removed. In this case, an explicit val= can also be omitted.

places and circular are numeric expression positions of type i.

The return code has the type string accordingly.

Note

When places is specified, the function shift_right behaves differently from the statement SHIFT with the additions RIGHT and PLACES. The function shift_right reduces the length of the string, but the statement SHIFT makes the string longer or keeps it the same length.

Example

The following program line

txt = shift_right( txt ).

has the same meaning as

txt = shift_right( val = txt sub = ` ` ).

and removes the trailing blanks from a string txt.

Executable Example

String Functions, shift and substring

Handleable Exceptions

CX_SY_RANGE_OUT_OF_BOUNDS

- Cause: Invalid value in places.
Runtime error: STRING_LENGTH_TOO_LARGE or STRING_LENGTH_NEGATIVE

🔸 substring, substring_... - Substring Functions

Syntax Forms

1. ... substring( val = text [ off = off] [len = len] ) ...

2. ... substring_from( val = text { sub = substring}|{ regex = regex}
                     [ case = case] [ occ = occ] [ len = len]  ) ...

3. ... substring_after( val = text { sub = substring}|{ regex = regex}
                      [case = case] [ occ = occ] [ len = len]  ) ...

4. ... substring_before( val = text { sub = substring}|{ regex = regex}
                       [case = case] [ occ = occ] [ len = len]  ) ...

5. ... substring_to( val = text { sub = substring}|{ regex = regex}
                   [ case = case] [ occ = occ] [ len = len]  ) ...

Effect

In the argument text, the substring functions determine a substring and return it.

The substring is determined as follows:

- The function substring uses the offset off and the length len to return a certain subrange. At least one of the two arguments off or len must be specified.

- The function substring_from scans text for the match (specified in occ) with the character string specified in substring or with the regular expression specified in regex and returns the subrange of the length len from the offset of the occurrence. If len is not specified, the substring is returned to the end of the character string. If substring is empty, an exception from the class CX_SY_STRG_PAR_VAL is raised. The search is case-sensitive by default, but this can be overridden using the parameter case.
If no substring is found, the return code is empty.

- The function substring_after works in the same way as substring_from, but the subrange plus the length of the substring found is returned, from the offset of the occurrence.

- The function substring_before works in the same way as substring_from, but the subrange of the length len is returned, before the offset of the occurrence. If len is not specified, the subrange is formed from the start of the character string.

- The function substring_to works in the same way as substring_before, but the subrange before the offset of the occurrence plus the length of the substring found is returned.

The return code has the type string accordingly.

Note

The performance of the substring functions is not as good as a direct substring access. They do, however, make it possible to use expressions in all operand positions.

Example

The return codes of the following function calls are: "CD", "CDEFGH", "EFGH", "AB", and "ABCD".

DATA  result TYPE string.
...
result = substring( val = 'ABCDEFGH' off = 2 len = 2 ).
...
result = substring_from( val = 'ABCDEFGH' sub = 'CD' ).
...
result = substring_after( val = 'ABCDEFGH' sub = 'CD' ).
...
result = substring_before( val = 'ABCDEFGH' sub = 'CD' ).
...
result = substring_to( val = 'ABCDEFGH' sub = 'CD' ).
...

Exceptions

Handleable Exceptions

CX_SY_RANGE_OUT_OF_BOUNDS

- Cause: Illegal offset or length specified in off and len.
Runtime error: STRING_OFFSET_TOO_LARGE
CX_SY_REGEX_TOO_COMPLEX

- Cause: More information: Exceptions in Regular Expressions.
Runtime error: REGEX_TOO_COMPLEX

CX_SY_STRG_PAR_VAL

- Cause: Substring in sub or regular expression in regex is empty or occurrence in occ is 0.
Runtime error: STRG_ILLEGAL_PAR

🔸 to_upper, to_lower, to_mixed, from_mixed -

Syntax Forms

1. ... to_upper( [val =] text ) ...

2. ... to_lower( [val =] text ) ...

3. ... to_mixed( [val =] text [sep = sep] [case = case] [min = min] ) ...

4. ... from_mixed( [val =] text [sep = sep] [case = case] [min = min] ) ...

Effect

These functions return the character string from text after it has been converted in accordance with the following case rules:

◉ The function to_upper transforms all letters in the character string to uppercase letters.

◉ The function to_lower transforms all letters in the character string to lowercase letters.

◉ The function to_mixed transforms all letters in the character string to lowercase letters from the second position. It then removes occurrences of the first character specified in sep from the character string (from left to right from the second position) and transforms the next letter to an uppercase letter. The default value for separator sep is an underscore (_). If case is not specified, the first character of the string remains unchanged. If case is specified and the first character of case is an uppercase letter, the first character in the string is also uppercase and then lowercase in all other occurrences. A positive number can be passed to min to specify a minimum number of characters that must appear before a separator (from the start of the string or since the last replacement) before the separator becomes effective. The default value for min is 1.

◉ The function from_mixed inserts the first character specified in sep before each uppercase letter (from left to right and from the second position). The default value for separator sep is an underscore (_). If case is not specified or if the first character in case is an uppercase letter, the entire string is displayed as uppercase; otherwise it is displayed as lowercase. A positive number can be passed to min to specify a minimum number of characters that must appear before an uppercase letter (from the start of the string or since the last insert) so that a separator is inserted. The default value for min is 1.

sep is a character-like expression position. If they have a fixed length, trailing blanks are ignored. min is a numeric expression position of type i. case expects a character-like data object with a fixed length. val = can be omitted only if none of the optional arguments are specified. If sep is an empty string or if min is negative, an exception of class CX_SY_STRG_PAR_VAL is raised.

The return code has the type string accordingly.

Note

The functions to_mixed and from_mixed are used to transform standard ABAP naming conventions for names with underscores to standard Java naming conventions with uppercase and lowercase letters and (namely Mixed Case Style and Camel Case Style) and the other way around.

Example

Displays the Latin alphabet in lowercase letters.

cl_demo_output=>display( to_lower( sy-abcde ) ).

Handleable Exceptions

CX_SY_STRG_PAR_VAL

◉ Cause: The separator in sep is empty or the minimum number in min is negative.
Runtime error: STRG_ILLEGAL_PAR

🔸 translate - Translate Function

Syntax

... translate( val = text from = from to = to ) ...

Effect

This function returns the character string from text with each character that occurs in from replaced by the character that occurs in the same place in to as in from. If to is shorter than from, the surplus characters from from are removed from the character string. If from is an empty string, the content of text is returned unchanged.

from and to are character-like expression positions. If they have a fixed length, trailing blanks are ignored.

The return code has the type string.

Example

The return value of the following function call is "Horray!".

DATA(result) = translate( val = `---Hur-rah!---` from = `uh-` to = `oy` ).

3.3.2.3 Common Parameters of Character String Functions

The following parameters are used to achieve the same effect with multiple character string functions and predicate functions for character-type arguments:

◇ case - Uppercase and Lowercase

Syntax

... ( ... case = case ...  ) ...

Effect

Searches and comparisons in string functions are case-sensitive by default, but this can be overridden if necessary using the parameter case. The argument case requires a constant or a literal of the type ABAP_BOOL from the type group ABAP with the value of the constants ABAP_TRUE or ABAP_FALSE. If case contains the value of ABAP_TRUE, the search is case-sensitive; if it contains the value of ABAP_FALSE, the search is not case-sensitive.

Example

The result of the following function calls is 2 and 3.

DATA(result1) = find( val = `aAbBcC` sub  = `B` case = abap_false ).
DATA(result2) = find( val = `aAbBcC` sub  = `B` case = abap_true  ).

◇ occ - Occurrence

Syntax

... ( ... occ = occ ...  ) ...

Effect

In string functions that are being searched, the parameter occ specifies the occurrence of a match. occ is a numeric expression position of type i.

If occ is positive, the occurrences are counted from the left; if occ is negative, the they are counted from the right. The values 1, 2, .... indicate the first, second, ... occurrences. The values -1, -2, .... indicate the last, last but one, ... occurrences.

The default value of occ is 1. Except in the case of the replacement function replace, the value 0 raises an exception from the class CX_SY_STRG_PAR_VAL.

Note

The sign of occ also modifies the default values of off and len.

Example

The result of the following function calls is UX and XU.

DATA(result1) = replace( val = `XX` sub  = `X` with = `U` occ =  1 ).
DATA(result2) = replace( val = `XX` sub  = `X` with = `U` occ = -1 ).

◇ off, len - Offset and Length

Syntax

... ( ... off = off len = len ...  ) ...

Effect

off is used to pass an offset and len is used to pass a length. In functions where both off and len can be passed, they determine the substring in which a string is to be edited.

off and len are numeric expression positions with the type i.

The default value of off is generally 0 and the default value of len is generally the length of the string minus a specified offset or an offset passed using off. Only in functions where a negative argument is passed for the occurrence occ (where possible) is the default value of off the length of the string and the default value of len the value of the associated offset.

If the value of off or len is negative, an offset defined using off is outside the string, or a substring defined using off and len is not fully contained in the string, an exception of the class CX_SY_RANGE_OUT_OF_BOUNDS is raised.

Example

The result of the following function calls is 17 and 12.

DATA(result1) = count( val = `Intro: blahblahblah` regex = `\w` ).
DATA(result2) = count( val = `Intro: blahblahblah` regex = `\w` off = 6 ).

◇ regex - Regular Expression

Syntax

... ( ... regex = regex ...  ) ...

Effect

regex is used to pass a regular expression to be found or matched. regex is a character-like expression position that must contain a correct regular expression. Only arguments with elementary types can be specified.

If a character-like data object with a fixed length is specifed, any trailing blanks are ignored. If regex is empty, an exception from the class CX_SY_STRG_PAR_VAL is raised.

Note

The regular expression in regex may be have correct syntax but be too complex for the execution of the function, which can raise a handleable exception of the class CX_SY_REGEX_TOO_COMPLEX. See Exceptions in Regular Expressions.

Example

The result of the following function call is <tag>def</tag>.

DATA(result) = replace( val   = `<tag>abc</tag>`
                        regex = `(<tag>)[^<]+(</tag>)`
                        with  = `$1def$2` ).

◇ sub - Substring

Syntax

... ( ... sub = substring ...  ) ...

Effect

substring is used to pass a character string whose characters are to be found or inserted. substring is a character like expression position. Only arguments with elementary types can be specified.

If a character-like data object with a fixed length is specified, any trailing blanks are ignored.

Note

The behavior displayed when an empty string is specified in substring depends on the function in question.

Example

The result of the following function call is 2.

DATA(result) = count( val = 'axaxa' sub = 'x' ).

◇ val - Input Value

Syntax

... ( ... val = text ...  ) ...

Effect

text passes the main argument to be processed by the function. text is an enhanced character-like expression position. Functional method calls whose return value is convertible to the type string can also be specified. Only elementary data types can be edited.

If a character-like data object with a fixed length is specified, any trailing blanks are ignored. Non-character-like return values are converted to the data type string.

Notes

- The conversion operator CONV can be applied to edit those non-elementary data types that can be converted to an elementary character-like data type (such as structures with character-like-only flat components).

- Explicit specification of val = can also be optional.

Example

The following two function calls are the same, as specification of val = in to_upper is optional.

DATA(result1) = to_upper( val = 'aAbBcC' ).
DATA(result2) = to_upper(       'aAbBcC' ).

3.3.2.4 Examples of String Functions

- Example Character String Functions, cmax, cmin and segment

The example demonstrates the extremum functions cmax and cmin as well as the segment function segment.

Source Code

REPORT demo_cmax_cmin.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA:  txt TYPE string,
           max TYPE string,
           min TYPE string,
           msg TYPE string.
    txt = `one two three four five six seven eight nine ten`.
    max = | |.
    min = |�|.
    DO.
      TRY.
          max = cmax( val1 = max
                      val2 = segment( val   = txt
                                      index = sy-index sep = ` ` ) ).
          min = cmin( val1 = min
                      val2 = segment( val   = txt
                                      index = sy-index sep = ` ` ) ).
        CATCH cx_sy_strg_par_val.
          EXIT.
      ENDTRY.
    ENDDO.
    cl_demo_output=>display(
      |Maximum is { max } and minimum is { min }| ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The program determines the minimum and the maximum segment of a character string with reference to the current code page.

- Example String Functions, distance

The example demonstrates the string function distance.

Source Code

REPORT demo_string_distance.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA: word    TYPE c LENGTH 30 VALUE 'CALL METHOD',
          percent TYPE i VALUE 50.
    cl_demo_input=>add_field( CHANGING field = word ).
    cl_demo_input=>request(   CHANGING field = percent ).
    IF word IS INITIAL.
      cl_demo_output=>display(
        'Enter a word' ).
      RETURN.
    ENDIF.
    IF percent NOT BETWEEN 0 AND 100.
      cl_demo_output=>display(
         'Enter a percentage between 0 and 100' ).
      RETURN.
    ENDIF.

    TYPES: BEGIN OF distance,
             text TYPE string,
             dist TYPE i,
           END OF distance.
    DATA: index_tab TYPE REF TO cl_abap_docu=>abap_index_tab,
          distances TYPE SORTED TABLE OF distance
                    WITH NON-UNIQUE KEY dist.

    index_tab = cl_abap_docu=>get_abap_index(
      COND #( WHEN sy-langu <> 'D'
                      THEN 'E'
                      ELSE 'D' ) ).
    LOOP AT index_tab->* ASSIGNING FIELD-SYMBOL(<index>).
      DATA(str1) = to_upper( val = <index>-key1 ).
      DATA(str2) = to_upper( val = word ).
      DATA(max) = COND i( WHEN strlen( str1 ) > strlen( str2 )
                            THEN strlen( str1 )
                            ELSE strlen( str2 ) ).
      max = ( 100 - percent  ) * max / 100 + 1.
      DATA(dist) = distance( val1 = str1 val2 = str2 max = max ).
      IF dist < max.
        distances = VALUE #( BASE distances
                            ( text = str1 dist = dist ) ).
      ENDIF.
    ENDLOOP.

    cl_demo_output=>display( distances ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The program compares a text that has been input with the index entry in the ABAP keyword documentation using distance. The index entries that correspond to the longer texts to an arbitrary precentage are output in order of increasing edit distance.

- Example String Functions, escape for HTML

This example demonstrates the string function escape for HTML.

Source Code

REPORT  demo_escape_html.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA: body     TYPE string,
          esc_body TYPE string.

    body = `<table border> `
        && `<tr><td>11</td><td>12</td></tr> `
        && `<tr><td>21</td><td>22</td></tr> `
        && `</table>`.

    esc_body = escape( val    = body
                       format = cl_abap_format=>e_html_text ).

    cl_demo_output=>new(
      )->begin_section( 'Original text'
      )->write_text( body

      )->next_section( 'Original text formatted as HTML'
      )->write_html( body

      )->next_section( 'Escaped text'
      )->write_text( esc_body

      )->next_section( 'Escaped text formatted as HTML'
      )->write_html( esc_body

      )->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The program applies the function escape to a string used as the body of a HTML file. The string is displayed as a text and in the HTML file both before and after the function is executed.

- Example String Functions, escape for XSS

This example demonstrates the string function escape for preventing XSS.

Source Code

REPORT demo_xss.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS:
       main,
       class_constructor.
  PRIVATE SECTION.
    CLASS-DATA:
      in  TYPE REF TO if_demo_input,
      icf_node TYPE string.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    CONSTANTS xss_demo TYPE string
                       VALUE `foo" onmouseover="alert('Gotcha!')`.

    DATA: query TYPE string VALUE `ABAP Objects`,
          esc_flag  TYPE abap_bool VALUE abap_true,
          xss_flag  TYPE abap_bool VALUE abap_false.

    DO.
      in->add_field( EXPORTING text = 'Input'
                     CHANGING field = query
       )->add_field( EXPORTING text = 'Escape'
                               as_checkbox = abap_true
                     CHANGING field =  esc_flag
       )->request(   EXPORTING text = 'XSS-Demo'
                               as_checkbox = abap_true
                     CHANGING field =  xss_flag ).
      IF query IS INITIAL AND xss_flag = abap_false.
        EXIT.
      ENDIF.

      IF xss_flag = abap_true.
        query = escape( val    = xss_demo
                        format = cl_abap_format=>e_xss_ml ).
        xss_flag = abap_false.
        CONTINUE.
      ENDIF.

      IF esc_flag = abap_true.
        query = escape( val    = query
                        format = cl_abap_format=>e_xss_ml ).
      ELSEIF query <> xss_demo.
        MESSAGE
          `Without escaping only the prepared XSS-Demo is allowed.`
          TYPE 'I'.
        CONTINUE.
      ENDIF.

      DATA(html) =
        `<html>`  &&
        `<body>`  &&
        `<p><a href="` && icf_node &&
        `?query=` && query &&
        `">Search in ABAP Documentation</a></p>` &&
        `<p><a href="http://www.google.com/search?q=` &&
        query && `">Search with Google</a></p>` &&
        `</body>` &&
        `</html>` ##no_text.
      cl_abap_browser=>show_html( html_string  = html
                                  buttons      = abap_true
                                  check_html   = abap_false
                                  context_menu = abap_true ).
    ENDDO.
  ENDMETHOD.
  METHOD class_constructor.
    CONSTANTS path TYPE string VALUE `/sap/public/bc/abap/docu`.
    DATA(location) =
      cl_http_server=>get_location( application = path ).
    IF location IS NOT INITIAL.
      icf_node = location && path.
    ENDIF.
    in  = cl_demo_input=>new( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

A search term can be entered in a dialog box. An output window provides a search function in the ABAP keyword documentation and with an external search engine. By default, the input is escaped using the function escape and the format cl_abap_format=>e_xss_ml. This prevents cross site scripting (XSS).

The function can be disabled for specific input, which demonstrates the effects of an XSS attack. The input makes the links on the output window and the following input field unusable. More harmful functions could be used instead of the JavaScript function alert, but are not permitted in this example.

- Example String Functions, count, find, and match

The example demonstrates the string functions count, find, and match.

Source Code

REPORT demo_find_and_match.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA: text TYPE c LENGTH 120
               VALUE `Cathy's cat with the hat sat on Matt's mat.`,
          regx TYPE c LENGTH 120
               VALUE `\<.at\>`.
    DATA: result TYPE i,
          substr TYPE string.
    data out TYPE c LENGTH 120.
    cl_demo_input=>add_field( CHANGING field = text ).
    cl_demo_input=>request(   CHANGING field = regx ).
    cl_demo_output=>write( text ).
    result = count( val   = text
                    regex = regx ).
    DO result TIMES.
      result = find( val   = text
                     regex = regx
                     occ   = sy-index ).
      substr = match( val   = text
                      regex = regx
                      occ   = sy-index ).
      data(len) = strlen( substr ).
      out+result(len) = substr.
    ENDDO.
    cl_demo_output=>display( out ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

In the text field text all the occurrences are searched for (using count and find) that correspond to a regular expression. When a search is successful, the subfield found is read out and displayed with the help of the match function.

Instead of using the count function, you could also use an unlimited DO loop that is left using EXIT if the result of find has the value -1.

- Example String Functions, shift and substring

This example demonstrates the string functions shift and substring.

Source Code

REPORT demo_shift_substring.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA output TYPE TABLE OF string.
    output = VALUE #(
      ( `            oo            ` ) ).
    output = VALUE #(
      LET n = strlen( output[ 1 ] ) IN
      BASE output
      FOR j = 2 UNTIL j > n / 2
      LET r = output[ j - 1 ] l = strlen( r ) / 2 IN
        ( shift_left(  val = substring( val = r
                                        len = l )
                                        circular = 1 ) &&
          shift_right( val = substring( val = r
                                        off = l )
                                        circular = 1 ) ) ).
    output = VALUE #(
      LET n = strlen( output[ 1 ] ) IN
      BASE output
      FOR j = n / 2 + 1 UNTIL j > n - 1
      LET r = output[ j - 1 ] l = strlen( r ) / 2 IN
        ( shift_right( val = substring( val = r
                                        len = l )
                                        circular = 1 ) &&
          shift_left(  val = substring( val = r
                                        off = l )
                                        circular = 1 ) ) ).
    cl_demo_output=>display( output ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

This program creates a diamond shape in the display.

- Example String Functions, to_mixed and from_mixed

This example demonstrates the string functions to_mixed and from_mixed.

Source Code

REPORT demo_to_from_mixed.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA:
      original  TYPE c LENGTH 30 VALUE 'ABAP_DOCU_START',
      to_sep    TYPE c LENGTH 1  VALUE '_',
      to_case   TYPE c LENGTH 1 VALUE 'a',
      to_min    TYPE i VALUE 1,
      from_sep  TYPE c LENGTH 1  VALUE '.',
      from_case TYPE c LENGTH 1  VALUE 'A',
      from_min  TYPE i VALUE 5.

    cl_demo_input=>new(
     )->add_field( CHANGING field = original
     )->add_line(
     )->add_field( CHANGING field = to_sep
     )->add_field( CHANGING field = to_case
     )->add_field( CHANGING field = to_min
     )->add_line(
     )->add_field( CHANGING field = from_sep
     )->add_field( CHANGING field = from_case
     )->add_field( CHANGING field = from_min
     )->request( ).

    DATA(out) = cl_demo_output=>new( ).
    TRY.
        out->write( |original:   { original }| ).
        DATA(to_mixed) = to_mixed( val  = original
                                   sep  = to_sep
                                   case = to_case
                                   min  = to_min ).
        out->write( |to_mixed:   { to_mixed }| ).
        DATA(from_mixed) = from_mixed( val  = to_mixed
                                       sep  = from_sep
                                       case = from_case
                                       min  = from_min ).
        out->write( |from_mixed: { from_mixed }| ).
      CATCH cx_sy_strg_par_val.
        out->write( 'Invalid parameters' ).
    ENDTRY.
    out->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The program queries the parameters for the functions to_mixed and from_mixed. The result of to_mixed is edited by from_mixed.

3.3.3 Regular Expressions

ABAP supports extended regular expressions in accordance with POSIX standard 1003.2. Regular expressions can be used after the addition REGEX of the statements

◉ FIND
◉ REPLACE

and also as an argument of the following functions

◉ count, count_...
◉ contains_...
◉ find, find_...
◉ match
◉ matches
◉ replace
◉ substring, substring_...

where they are used for searching in and testing character strings. The classes

◉ CL_ABAP_REGEX
◉ CL_ABAP_MATCHER

permit object-oriented use of regular expressions.

A regular expression r is made up of literal characters and special characters in accordance with the syntax of regular expressions and represents a set of character strings. If text is a character string represented by r, this indicates that r matches text or that r fits text. Two (different) regular expressions match if they fit the same set of character strings.

If a regular expression is applied to a character string text as a search string, this indicates that a search for matches of the regular expression with substrings of text is intended. In this case, special characters in the regular expression do not match characters, but instead match positions, thus influencing the type and number of occurrences. When character strings are tested, this involves checking whether the full content matches a pattern.

Notes

◉ A regular expression can have correct syntax, but be too complex for to be executed. This raises a handleable exception of the class CX_SY_REGEX_TOO_COMPLEX.

◉ The example program DEMO_REGEX and its enhancement DEMO_REGEX_TOY makes it possible to test the search and replace functions by applying regular expressions to texts.

3.4 Expressions and Functions for Byte String Processing

Expressions and functions for byte string processing can be processed at many operand positions.

- Bit expressions

Syntax

... [BIT-NOT] operand1
    [{BIT-AND|BIT-OR|BIT-XOR} [ BIT-NOT] operand2
    [{BIT-AND|BIT-OR|BIT-XOR} [ BIT-NOT] operand3
    ...  ]] ...

Effect

A bit expression formulates a binary calculation. The operands must be byte-like (type x or xstring). The result of a bit expression is a byte chain in the calculation length assigned to the bit expression. During assignment to an inline declaration DATA(var), the byte chain is handled as a byte string of data type xstring, and when passing a generic typed formal parameter it is handled as a byte field of data type x.

In a bit expression bit_exp, an operand operand1 can be joined with one or more operands operand2, operand3, and so on using bit operators BIT-AND, BIT-OR, or BIT-XOR; Brackets are possible. Bit expressions can occur in the reading positions of certain statements, in particular on the right side of an assignment with the assignment operator =.

The operand positions operand are general expression positions, which means byte-like data objects, functional methods with byte-like return codes, or bracketed bit expressions can be specified. The bit operators BIT-AND, BIT-OR, and BIT-XOR join two adjacent operands. When the expression is evaluated, a byte-like value is calculated and joined with the next adjacent operand. The priority of the join depends on the operators that are used; if use functional methods are used, the same applies as described for arithmetic expressions.

The bit operator BIT-NOT can be specified one or more times before an operand to negate the value of the operand. If BIT-NOT is specified an even number of times, the operand remains unchanged. An odd number of uses negates the operand.

If functional methods are specified as operands, they are specified from left to right and from inside to outside before the remainder of the expression is evaluated. The return values are buffered to be used in the corresponding operand positions.

Example

Bit sequences are an efficient way of mapping set operations. If a set contains n elements, the existence of an element i in n can be represented by a 1 at the position of i in a byte-like field. The statement SET BIT. among others, can be used to add an element to the set. The operators BIT-AND, BIT-OR, and BIT-XOR can then be used to calculate the intersection, the union, and the symmetric difference of different sets.

In the following example, the attributes of person groups are mapped in the fields p1, p2, p3 of the type x. The bit operator BIT-AND is then used to determine which attributes are shared by all people. The result shows that only the eighth attribute is shared.

DATA:
  p1  TYPE x LENGTH 1 VALUE '5B',    "01011011
  p2  TYPE x LENGTH 1 VALUE '13',    "00010011
  p3  TYPE x LENGTH 1 VALUE 'A5'.     "10100101

DATA(res) = p1 BIT-AND p2 BIT-AND p3. "00000001

Non-Handleable Exceptions

- Cause: An operand or the result field is not of type x.
Runtime error: BITS_WRONG_TYPE

1. bit_exp - Bit Operators

Bit operators work with the individual bits of the operands. The calculation length is specified by the operands involved. Linking two operands with BIT-AND, BIT-OR, and BIT-XOR has a result of this length, as each individual bit is set according to the table from the bits of the corresponding positions in the operands. BIT-NOT changes the bits of the operands to its right as shown in the table.

x BIT-NOT x   x BIT-AND y   x BIT-XOR y   x BIT-OR y 
0 0 1 0 0 0

2. bit_exp - ( )

Syntax

... ( bit_exp ) ...

Effect

A full bit expression bit_exp can be placed in parentheses. A bit expression in parentheses can be used as the operand of another bit expression and is not calculated until used in this way.

Example

Parentheses in bit expressions

TYPES output TYPE TABLE OF xstring WITH EMPTY KEY.
DATA hex1 TYPE xstring VALUE '0011'.
DATA hex2 TYPE xstring VALUE '0101'.
DATA hex3 TYPE xstring VALUE '1100'.
cl_demo_output=>display( VALUE output(
  (   hex1 BIT-AND hex2   BIT-OR hex3  )
  ( ( hex1 BIT-AND hex2 ) BIT-OR hex3  )
  (   hex1 BIT-AND ( hex2 BIT-OR hex3 ) ) ) ).

The displayed result is:

1101
1101
0001

3. bit_exp - Calculation Length

A bit expression is assigned a calculation length in which the operation is executed.

The calculation length is the length of the longest bit expression operand involved. Unlike in the calculation type of an arithmetic expression, the type of the result is not included.

Before the entire expression is evaluated, all operands in question are converted to the length of the longest operand based on the rules for the source field type x and xstring. This means that shorter operands are padded with hexadecimal 0 on the right.

The result in the calculation length is handled as follows:

◉ If used in the statement COMPUTE, the final result of the entire expression is converted to the length of the data object result based on the rules for source field type x and xstring. In assignments to an inline declaration DATA(var) the result of type xstring is located in the calculation length.

◉ If used in relational expressions, the result is used as an operand in the length resulting from the calculation.

◉ If used as an argument of built-in functions or of constructor expressions, the result is used as an operand in the length resulting from the calculation.

◉ If used as an actual parameter for input parameters of methods, the final result of the entire expression is converted to the length of the formal parameter based on the rules for source field type x and xstring. If the formal parameter is typed generically, it is set to the type x in the length determined by the operands.

Example

The result of the following bit expression is hexadecimal 11111010, with a length of 4 bytes.

DATA hex1 TYPE xstring VALUE '10101010'.
DATA hex2 TYPE xstring VALUE '0101'.
cl_demo_output=>display(
  |{ hex1 bit-or hex2 }\n{
     xstrlen( conv xstring( hex1 bit-or hex2 ) ) }| ).

4. Example Set Operations with Bit Sequences

This example demonstrates how bit operators are used.

Source Code

REPORT demo_data_bit.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.

    TYPES: BEGIN OF t_spfli,
             carrid    TYPE spfli-carrid,
             cityfrom  TYPE spfli-cityfrom,
          END OF t_spfli.

    DATA: frankfurt TYPE x LENGTH 4,
          frisco    TYPE x LENGTH 4,
          intersect TYPE x LENGTH 4,
          union     TYPE x LENGTH 4,
          bit       TYPE i,
          spflitab  TYPE TABLE OF t_spfli,
          wa        TYPE t_spfli,
          carrid    TYPE t_spfli-carrid,
          carrier   LIKE SORTED TABLE OF carrid
                                WITH UNIQUE KEY table_line.


    SELECT carrid FROM scarr INTO TABLE @carrier.

    SELECT carrid, cityfrom
           FROM spfli
           INTO CORRESPONDING FIELDS OF TABLE @spflitab.

    DATA(out) = cl_demo_output=>new(
      )->begin_section(
      'Airlines with departure cities'
      )->write_data( spflitab
      )->end_section( ).

    LOOP AT spflitab INTO wa.

      READ TABLE carrier FROM wa-carrid TRANSPORTING NO FIELDS.

      CASE wa-cityfrom.
        WHEN 'FRANKFURT'.
          SET BIT sy-tabix OF frankfurt.
        WHEN 'SAN FRANCISCO'.
          SET BIT sy-tabix OF frisco.
      ENDCASE.

    ENDLOOP.

    intersect = frankfurt BIT-AND frisco.
    union     = frankfurt BIT-OR  frisco.

    out->begin_section(
      'Airlines flying from Frankfurt and San Francisco' ).
    DO 32 TIMES.
      GET BIT sy-index OF intersect INTO bit.
      IF bit = 1.
        carrid = carrier[ sy-index ].
        out->write( |{ carrid }| ).
      ENDIF.
    ENDDO.

    out->next_section(
      'Airlines flying from Frankfurt or San Francisco' ).
    DO 32 TIMES.
      GET BIT sy-index OF union INTO bit.
      IF bit = 1.
        carrid = carrier[ sy-index ].
        out->write( |{ carrid }| ).
      ENDIF.
    ENDDO.

    out->display( ).

  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The method main works with four hexadecimal fields of length 4, namely frankfurt, frisco, intersect, and union. These fields can represent sets with a maximum of 32 elements. Here, the basic set is supposed to be the set of all possible airlines from the database table SCARR. Each bit of the relevant bit sequences represents an airline. To index the airlines, an internal table carrier is created and filled with the abbreviations for the airlines from SCARR. An airline is identified using the internal index of the table carrier.

Depending on the departure city, the SELECT loop across the database table SPFLI sets the bit at the position that matches the row number of the airline in the table carrier, in either the frankfurt field or the frisco field. For this purpose, the row number sy-tabix is declared using a READ statement, in which no other fields are transported.

Using the bit operations BIT-AND and BIT-OR, the intersection and union of frankfurt and frisco are created.

In the two DO loops, the bits from intersect and union are read and evaluated individually. For every set position, the airline abbreviations are read from the table carr using a READ statement.

- Binary functions

Byte string functions belong to thepredefined functions. They are divided into:

Description function for byte-like data types
Bit functions

◔ xstrlen - Length Function

One length function is currently available for unnamed byte-like arguments.

Syntax

... xstrlen( arg ) ...

The function xstrlen returns the number of bytes in arg. The argument arg is a byte-like functional operand position. The return value has the type i.

Note

The function described here is one of the functions that can be used in the obsolete extended functional operand positions, even if its argument is a single data object.

Example

A six-character text in the code page UTF-16 is represented by 12 bytes.

DATA(text) = `Hello!`.
DATA(hex) = cl_abap_codepage=>convert_to( source   = text
                                          codepage = `UTF-16` ).
cl_demo_output=>display(
  |Text length {   strlen( text )
  }\nByte length { xstrlen( hex )  }| ).

◔ bit-set - Bit Function

Bit functions process individual bits and return byte-like results. At present, the one bit function is available:

Syntax

... bit-set( arg ) ...

This function expects an integer numeric value as an argument. The argument is a numeric expression position of the type i.

◉ If the argument is positive, the function creates a byte chain in which the bit has the value 1 in the place specified by the argument. All other bits have the value 0.

◉ If the argument has the value 0, the result is an empty byte chain.

◉ If the argument is negative, the function creates a byte chain in which all bits up to and including the place specified by the argument have the value 1, and all bits behind this place have the value 0.

The length of the result is the minimum number of bytes needed to include the set bits.

The function can be used in all places in which a bit expression can be used, particularly in bit expressions themselves. As regards handling the result, the same applies as for bit expressions. More specifically, during assignment to an inline declaration DATA(var), the resulting byte chain is handled as a byte string of data type xstring, and when passing a generic typed formal parameter it is handled as a byte field of data type x. An empty byte chain stamps a generically typed formal parameter as a byte field with a length of 1 and a hexadecimal value of "00".

Notes

◈ The function boolx is a bit function in principle, but due to its properties is handled as a logical function.
◈ Unlike the statement SET BIT, the result field does not need to be filled first.

◔ Example Bit function

This example demonstrates how bit-set works.

Source Code

REPORT demo_bit_set.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA(number) = 1.
    cl_demo_input=>request( CHANGING field = number ).
    IF abs( number ) <= 200.
      cl_demo_output=>display( CONV xstring( bit-set( number ) ) ).
    ELSE.
      cl_demo_output=>display(
       'Number in Example must not exceed 200' ).
    ENDIF.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The output of the program shows how bits are set in a specified place in hexadecimal form.

3.5 System Classes for Character String and Byte String Processing

3.5.1 Class for Trailing Blanks

The class CL_ABAP_STRING_UTILITIES contains methods for processing text strings.

C2STR_PRESERVING_BLANKS assigns text fields while preserving the trailing blanks.
DEL_TRAILING_BLANKS deletes the trailing blanks from strings.

Note

Tasks like this can now also be performed using statements and predefined functions.

Example

The program compares the method C2STR_PRESERVING_BLANKS with the built-in function shift_right. The implementation of the method now consists of nothing more than this function.

DATA(str) = `abc   `.

DATA(rslt) = str.
cl_abap_string_utilities=>del_trailing_blanks( CHANGING str = rslt ).

ASSERT rslt =  shift_right( str ).

3.5.2 Class for Compressing Character Strings and Byte Strings

These classes offer a range of methods that enable text in text fields or text strings, or binary data in byte fields or byte strings to be compressed or decompressed using GZIP.

◉ CL_ABAP_GZIP
◉ CL_ABAP_GZIP_BINARY_STREAM
◉ CL_ABAP_GZIP_TEXT_STREAM
◉ CL_ABAP_UNGZIP_BINARY_STREAM
◉ CL_ABAP_UNGZIP_TEXT_STREAM

Example

Compression and decompression of text.

DATA zip TYPE xstring.
cl_abap_gzip=>compress_text(
  EXPORTING text_in  = `Hello!`
  IMPORTING gzip_out = zip ).

DATA txt TYPE string.
cl_abap_gzip=>decompress_text(
  EXPORTING gzip_in  = zip
  IMPORTING text_out = txt ).

ASSERT txt = `Hello!`.

3.5.3 Class with Utilities for String Processing

The class CL_ABAP_CHAR_UTILITIES provides attributes and methods as utilities for string processing.

The components of this class are all static and public. The attributes are read-only and are initialized in the class constructor. Details about the attributes and methods of this class are described in Class Builder.

Example

The following rows demonstrate that attributes of the class CL_ABAP_CHAR_UTILITIES containing control characters can be replaced by a representation of the control characters in a string template .

ASSERT cl_abap_char_utilities=>newline        = |\n|.
ASSERT cl_abap_char_utilities=>horizontal_tab = |\t|.
ASSERT cl_abap_char_utilities=>cr_lf          = |\r\n|.

3.5.4 Class for Handling Code Pages

The class CL_ABAP_CODEPAGE contains methods for converting the representation of character strings between different code pages. The interface IF_ABAP_CODEPAGE enables objects in the class to be accessed.

Example

Converts the content of a string into its UTF-8 representation.

DATA(str) = `...`.

DATA(xstr) = cl_abap_codepage=>convert_to(
               source      = str
               codepage    = `UTF-8` ).

4. Date and Time Processing


This section describes the options for accessing, creating, and editing the values for the date and time in ABAP programs.

4.1 Date, Time, Time Stamp, and Time Zones

Dates, times, and time stamps specify times with varying levels of precision.

◉ A date (or calendar date) is a time specified to a precise day, week, or month with respect to a calendar. AS ABAP always references implicitly the Gregorian calendar valid across most of the world. Output can be converted to country-specific calendars. Dates are generally interpreted as local dates that are valid in the current time zone.

◉ A time is specified to a precise second or minute with respect to a day. AS ABAP always saves times in 24-hour format. The output can have a country-specific format. Times are generally interpreted as local times that are valid in the current time zone.

◉ A time stamp specifies a combined date and time to a precise number of fractions of seconds, seconds, or minutes, and with respect to a reference time. AS ABAP always references implicitly the UTC reference time. The time stamp can be transformed to a local date and local time, using statement and functions where the required time zone is specified.

The relationship between the local date and local time on the one hand, and global UTC time stamps on the other, is governed by a set of time zone rules. These rules are stored in database tables. These rules also respect summer time.

◉ Rules for Time Zones

The conversion of the UTC reference time to the local date and the local time zone is based on a set of rules stored in AS ABAP system tables. The names of all associated tables start with TTZ. The following database tables, whose content you can maintain using transaction STZBD, are relevant for time zones:

- The database table TTZZ contains a list of possible time zones in the column TZONE. The entries in the columns ZONERULE and DSTRULE refer to the rules for the time difference of the time zone to the UTC reference time in table TTZR and to the rules for summer time in the tables TTZD, TTZDF, and TTZDV.

- The column ZONERULE of the database table TTZR contains a list of possible rules for the time difference between time zones and the UTC reference time. The columns UTCDIFF and UTCSIGN contain the time differences to UTC and their signs, while ignoring summer time.

- The database table TTZD contains a list of all possible summer time rules in column DSTRULE. The column DSTDIFF contains the time difference between summer time and winter time.

- The database table TTZDF contains a list of fixed summer time rules in column DSTRULE. The column YEARACT contains year numbers for every year for which the rule applies. The columns DATEFROM, TIMEFROM, DATETO, and TIMETO contain the date and time for the beginning and end of summer time.

- The database table TTZDV contains a list of variable summer time rules in column DSTRULE. The column YEARFROM contains a year number for every rule, saying in which year the rule applies.

- The columns MONTHFROM, WEEKDFROM, WEEKDCFROM, TIMEFROM, MONTHTO, WEEKDTO, WEEKDCTO, and TIMETO contain the month, week, day, and time for the beginning and end of summer time.

A correct set of rules must contain all rules specified in TTZZ for the time difference between time zones and the UTC reference time in TTZR and all rules specified for summer time in TTZD. If the time difference between summer time and winter time in TTZD is not equal to 0, the respective summer time rule must be included in at least one of the tables TTZDF or TTZDV. While TTZDF contains fixed dates for the switch, the date in TTZDV is variable, because a day in a particular week of a month is specified. The system always checks TTZDF first for the summer time rule and then TTZDV.

Notes for Summer Time

If a time zone specifies a summer time rule with a summer time difference not equal to 0, note the following:

- If during summer time a new year starts (in the southern hemisphere), then the year specified in the database tables TTZDF or TTZDV identifies the beginning of the summer time, while the end is in the following year.

- The time specified for the beginning of summer time in the tables TTZDF and TTZDV identifies the time when the clock is put forward by the summer time difference when the winter time ends. The first second of summer time is the time you get when you add the summer time difference to the specified time.

- When summer time begins, a time interval of the length of the summer time difference is created, for which you can formulate a date and time, but which does not exist as a local time and which cannot be assigned to an UTC reference time. A local time of this type is handled in the CONVERT INTO TIME STAMP statement as an invalid time.

- The time specified in the database tables TTZDF or TTZDV for the end of summer time identifies the time when the clock is put back by the summer time difference when summer time ends. The first second of the winter time is the time you get when you subtract the summer time difference from the specified time.

- When summer time ends, a time interval of the length of the summer time difference is created, which is passed twice as a local time (the double hour). If you formulate a date and time for this interval, the statement CONVERT INTO TIME STAMP handles it as a time from summer time, by default.

◉ System Time Zone and User Time Zone

Two time zones are preconfigured in AS ABAP, the system time zone and the user time zone.

- The system time zone is the client-specific time zone of the system time and the system date and cannot be changed while the system is running. It is contained in the column TZONESYS in the client-specific database table TTZCU.

- The user time zone is a client-specific time zone that can be defined for the user time and user date of each individual user in the user master record. It is contained in the system field sy-zonlo.

Both time zones are displayed in transaction SU01 on the Fixed Values tab, however only the user time zone can be changed here. This requires a new logon to take effect. If no user time zone is defined in the user master record, or the specified time zone is invalid or inactive, the default value from the column TZONEDEF in the database table TTZCU is used. If it is invalid or inactive, the system field sy-zonlo is filled with blanks.

Note

The current client's system time zone can be read using function module GET_SYSTEM_TIMEZONE.

Example

Determines the date and time in the system time zone from a time stamp.

GET TIME STAMP FIELD DATA(ts).

SELECT SINGLE tzonesys
       FROM ttzcu
       INTO @DATA(tzone).

CONVERT TIME STAMP ts TIME ZONE tzone
        INTO DATE DATA(date) TIME DATA(time).

System Time and System Date

The system time and the system date are the local time and local date in AS ABAP in the system time zone. They are produced by regular synchronizations between the clock of the ABAP runtime server and the clock of the database server. During the synchronization process, the ABAP runtime environment clock is set to the database server clock. Since this happens on all application servers in AS ABAP, the ABAP runtime environment clock is synchronized with the clocks on all other application servers and with the database system clock, and therefore shows the system time and system date of the entire AS ABAP.

The character-like system fields sy-uzeit and sy-datum are provided with the system time and system date at certain times.

Note

From a technical perspective, the ABAP runtime environment clock is synchronized with the database server clock on request. If a time is required (for example, to populate system fields for date and time), the system checks whether the last synchronization was performed during the specified interval. If this is not the case, synchronization is performed.

Example

Creates a time stamp from the system date and system time.

DATA tz TYPE timezone.
CALL FUNCTION 'GET_SYSTEM_TIMEZONE'
  IMPORTING
    timezone = tz.

CONVERT DATE sy-datum TIME sy-uzeit
        INTO TIME STAMP DATA(ts) TIME ZONE tz.

User Time and User Date

The user time and the user date are the local time and local date of the current user, calculated from the system time and the user time zone. The character-like system fields sy-timlo and sy-datlo are provided with the user time and user date at certain times. If sy-zonlo is initial, sy-timlo and sy-datlo are given the same values as sy-uzeit and sy-datum, the system time and system date.

Note

To get the local time and local date from a UTC time stamp created using GET TIME STAMP, the content of the system field sy-zonlo can be passed to the statement CONVERT TIME STAMP. An initial value for the time zone is set in CONVERT TIME STAMP, but set implicitly to "UTC" and not to the system time zone.

Example

Determines a local date and a local time from a time stamp.

GET TIME STAMP FIELD DATA(ts).

CONVERT TIME STAMP ts TIME ZONE sy-zonlo
        INTO DATE DATA(date) TIME DATA(time).

4.2 Date Fields and Time Fields

The ABAP types d and t are predefined types for data objects that specify dates and times. The date type d describes a character-like local date in the format "yyyymmdd". The date type t describes a character-like local date in the format "hhmmss".

4.2.1 Access to date fields and time fields

Only a few operand positions are affected by the fact that date fields and time fields contain a local date in the format "yyyymmdd" or a local time in the format "hhmmss":

◉ When using date fields and time fields as source fields or target fields of a lossless assignment.
◉ When converting a local date and a local time to a time stamp using CONVERT INTO TIME STAMP and back again using CONVERT TIME STAMP.
◉ In output formatting using string templates or the statement WRITE [TO]
◉ When mapping to asXML or JSON.

In all other operand positions, the handling of date fields and time fields is specified in the conversion rules and comparison rules for these data types.

◉ The conversion rules are designed so that data objects of the types d and t display character-like behavior in character-like operand positions and numeric behavior in numeric operand positions. In the latter case, the content of a date field is converted to the number of days since 01.01.0001 and the content of a time field is converted to the number of seconds since midnight.

◉ The comparison rules are designed so that a later date or later time is greater than an earlier date or earlier time.

Direct assignments or comparisons between the data types d and t are meaningless and therefore forbidden. The following sections show examples of how to access date fields and time fields effectively.

◉ Character-like access to date/time fields
◉ Numeric access to date/time fields

Notes

◉ The function modules of the function group SCAL provide some additional information about character-like dates, for example the day of the week or the calendar week for a given date.
◉ Special date functions and time functions can be used in the CDS DDL of the ABAP CDS to edit dates and times saved in database tables.

Example

Conversion of a time stamp to date and time fields and their type-friendly formatting as string templates.

GET TIME STAMP FIELD DATA(ts).

CONVERT TIME STAMP ts TIME ZONE sy-zonlo
        INTO DATE DATA(date) TIME DATA(time).

cl_demo_output=>display( |{ date DATE = ISO
                       }\n{ time TIME = ISO }| ).

- Character-Like Access to Date Fields and Time Fields

Character-like access to character-like content of date fields and time fields is evaluated in a character-like manner. The character-like nature of date fields and time fields can be exploited, for example, to access detailed information. To avoid unexpected results from this type of access, the validity of the content of the date or time fields must be verified. Most statements and functions used in string processing are not suitable for editing date fields and time fields because they generally produce invalid content.

Example

The following example demonstrates how substring functions can be used to extract the components year, month, day, hour, minute, and second from date fields and time fields.

DATA: date TYPE d,
      time TYPE t.

DATA: year   TYPE i,
      month  TYPE i,
      day    TYPE i,
      hour   TYPE i,
      minute TYPE i,
      second TYPE i.

date = sy-datlo.
time = sy-timlo.

year   = substring( val = date off = 0 len = 4 ).
month  = substring( val = date off = 4 len = 2 ).
day    = substring( val = date off = 6 len = 2 ).
hour   = substring( val = time off = 0 len = 2 ).
minute = substring( val = time off = 2 len = 2 ).
second = substring( val = time off = 4 len = 2 ).

- Numeric Access to Date Fields and Time Fields

Numeric access to date fields and time fields exploits the fact that the conversion of the types d and t to numeric values produces an integer number of days or seconds. This applies particularly when using date fields and time fields in numeric calculations, where these fields are converted to the corresponding calculation type. This enables differences to be calculated or values to be added or subtracted from date fields or time fields. None of the other arithmetic operations are generally advisable here. To avoid unexpected results from these calculations, the validity of the content of the date or time fields must be verified.

Example

The following calculations provide the current day of the year, plus the hour, minutes, and seconds of the current time. The date and time are provided by a time stamp.

DATA: date TYPE d,
      time        TYPE t,
      time_stamp     TYPE timestamp,
      day    TYPE i,
      hour   TYPE i,
      minute TYPE i,
      second      TYPE i,
      first_day   TYPE d.

GET TIME STAMP FIELD time_stamp.
CONVERT TIME STAMP time_stamp TIME ZONE sy-zonlo
        INTO DATE date TIME time.

first_day = date(4) && '0101'.
day       = date - first_day + 1.
second    = time.
hour      = second DIV 3600.
second    = second - hour * 3600.
minute    = second DIV 60.
second    = second - minute * 60.

Example

The following calculation produces the day of the week for a date field date containing any valid date. 1 means Monday, 2 means Tuesday, and so on.

day = ( 5 + date  MOD 7 ) MOD 7  + 1.
Example

The following calculation produces the last day of the previous month.

DATA date TYPE d.

date = sy-datlo.
date+6(2) = '01'.
date     = date - 1.

- Example Evaluating Date Fields and Time Fields

This example demonstrates calculations with date and time fields.

Source Code

REPORT demo_data_date_time.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.

    DATA: ultimo TYPE d,
          t1 TYPE t VALUE '000000',
          t2 TYPE t,
          diff TYPE i,
          seconds TYPE i,
          hours TYPE i.

    "date calculation

    ultimo      = sy-datlo.
    ultimo+6(2) = '01'.
    ultimo      = ultimo - 1.
    cl_demo_output=>write(
      |Last day of last month: { ultimo }| ).

    "time calculation

    t2 = sy-timlo.
    diff = t2 - t1.
    seconds = diff MOD 86400.
    hours = seconds / 3600.
    cl_demo_output=>display(
      |Number of hours since midnight: { hours }| ).

  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

In the first part of the main method, the last day of the previous month is assigned to ultimo. First, ultimo is filled with the current date. Then the date is changed to the first day of the current month using an offset. Finally, 1 is subtracted from ultimo, so that the content of the field is changed to the last day of the previous month. Before the subtraction takes place, the system converts ultimo into the number of days since 01.01.0001 and converts the result back into a date.

In the second part, the number of hours between midnight and the current time are calculated. First, the difference between the time fields is calculated. The difference is then converted into the total number of seconds using the MOD operation. A positive difference remains unchanged. This step is only required for negative differences. Finally, the number of hours is calculated by dividing the number of seconds by 3600.

4.2.2 Validity of date fields and time fields

Date fields and time fields of the types d and t can contain any alphanumeric characters.

◉ Valid values for the type d, however, are only those digits that are valid dates according to the calendar rules in the format "yyyymmdd": "yyyy" (year): 0001 to 9999, "mm" (month): 01 to 12, "dd" (day): 01 to 31.
◉ Valid values for the type t, however, are only those digits that are interpreted as times in the 24-hour clock format "hhmmss". "hh" (hours): 00 to 23, "mm" (minutes): 00 to 59, "ss" (seconds): 00 to 59.

The ABAP runtime environment checks the validity at the following points:

◉ Date fields and time fields with invalid values raise an exception when used as a source field of a lossless assignment. Conversely, source fields of other types may not produce any invalid values in date fields and time fields as target fields in a lossless assignment.
◉ Invalid values cannot be converted to time stamps by using the statement CONVERT INTO TIME STAMP.
◉ In a regular assignment to numeric fields, date fields and target fields with invalid values produce the value 0, with the following exceptions:
     ◉ For a source field of the type d, the values from 5.10.1582 to 14.10.1582, which are actually invalid due to the conversion from the Julian to Gregorian calendar, are handled just like the valid values from 15.10.1582 to 24.10.1582.
     ◉ For a source field of the type t that contains only digits, the numeric value is calculated using the formula hh*3600+mm*60+ss, even if the times are invalid.
◉ For regular assignments of numeric fields to date fields and time fields, the numeric fields must be within the value range of the data type i and comply with the following special rules:
     ◉ When number values are assigned to date fields, and these fields cannot be produced by the reverse conversion, the date field is initialized
     ◉ Any number values can be assigned to time fields. When converted, the number value is divided by 86400 and only the integer remainder of the division is respected. This always produces a valid time in hours.

No checks are generally made on other assignments to date fields and time fields not mapped to assignments of numeric values, nor to other operand positions. As a consequence, invalid values are produced easily by date fields and time fields, and then processed further.

Initial Value of Date Fields

The initial value "00000000" of the data type d and the date 01.01.0001 have a special part to play here.

◉ The initial value "00000000" of data type d is not a valid date. A conversion of the initial value "00000000" to a numeric data type produces, like all invalid values, the value 0. Conversely, the conversion of the number 0 to a date field of the type d always produces the invalid date "00000000". Negative and oversized numbers also produce the invalid date "00000000".

◉ A conversion of a valid date to a field of the type d produces the number of days since 01.01.0001, which makes this date the zero point for date calculations. A conversion of a date field of the type d with the value "00010101" to a numeric value produces the value 0, like the conversion of the initial value or any other invalid value, and the conversion cannot be reversed.

1 is the smallest number that is converted to a valid date, 02.01.0001, when assigning to a date field of type d. Assignments between valid date fields and numeric fields can be reversed only from this date.

In lossless assignments, invalid values in source fields raise exceptions instead of producing the value 0 or "00000000". Here, the value "00010101", which is really part of the value range, is regarded as invalid, while "00000000", not actually part of the value range, is regarded as valid. This means that the initial value "00000000" can be used by lossless assignments and all valid assignments between date fields and numeric fields can be reversed.

Note

The validity of the content of date fields and time fields must be verified before they are accessed.

Example

Identify an invalid date by a comparison with the value 0. For the comparison, the date field is converted to an integer field of type i, where an invalid date produces the value 0. The valid date "00010101", which would also produce the value 0, is handled separately beforehand.

DATA(date) = CONV d( '20160231' ).

date = COND #( WHEN date = '00010101' OR date <> 0 THEN date
               ELSE THROW cx_sy_conversion_no_date( ) ).

Example

Identify an invalid time by a comparison with the value 0. For the comparison, the time field is converted to an integer field of type i, where an invalid time produces the value 0. The valid time "000000", which would also produce the value 0, is handled separately beforehand.

DATA(time) = CONV t( 'XXXXXX' ).

time = COND #( WHEN time = '000000' OR time <> 0 THEN time
               ELSE THROW cx_sy_conversion_no_time( ) ).

4.3 Time Stamp

This section describes how non-time-zone-specific time stamps are represented and the statements used to access them.

4.3.1 Time Stamps - Overview

Representation of Time Stamps

Time stamps are represented using packed numbers of the type p. There is a short form and a long form.

◉ In the short form, a time stamp is represented precisely to the second using a packed number with length 8, and the ABAP Dictionary type TIMESTAMP. The digits of the packed number show the time stamp in the format "yyyymmddhhmmss", where "yyyy" is the year, "mm" is the month, "dd" is the day, "hh" is the hour, "mm" are the minutes, and "ss" are the seconds.

◉ In the long form, a time stamp is represented precisely to 100 ns using a packed number with length 11 and seven decimal places, and the ABAP Dictionary type TIMESTAMPL The digits of the packed number show the time stamp in the format "yyyymmddhhmmss.sssssss", where, in addition to the short form, the seven decimal places "sssssss" are the fractions of a second. The maximum time resolution depends on the operating system of the application server and can be less.

In its integer part, a valid time stamp must contain valid dates and times:

◉ When specifying the date, only the values 01 to 9999 for the year, 01 to 12 for the month, and 01 to 31 for the day are valid.
◉ When specifying the time, only the values 00 to 23 for the hours, and 00 to 59 for the minutes and seconds are valid.

A time valid in the Gregorian calendar must be represented. Leap seconds are not supported.

Note

The method NORMALIZE of class CL_ABAP_TSTMP can be used to convert invalid values in time stamps to valid values.

Access to Time Stamps

Only a few statements recognize that packed numbers of the types TIMESTAMP and TIMESTAMPL are time stamps. All other statements interpret the content of these data types numerically and, with the exception of direct comparisons, are not suitable for handling time stamps. The following statements can be used to handle time stamps:

◉ GET TIME STAMP creates a current time stamp.
◉ CONVERT TIME STAMP converts a time stamp to a local date and a local time.
◉ CONVERT INTO TIME STAMP converts a local date and a local time to a time stamp.

The following settings for formatting output also handle time stamps in a specific way:

◉ Use of the options TIMESTAMP and TIMEZONE for embedded expressions in string templates
◉ Use of the addition TIME ZONE of the statement WRITE [TO].
◉ The domains XSDDATETIME_Z and XSDDATETIME_LONG_Z support the serialization and deserialization of ABAP time stamps in asXML and asJSON.

The system class CL_ABAP_TSTMP provides methods for adding, subtracting, converting, and comparing time stamps.

Note

Special time stamp functions can be used in the CDS DDL of the ABAP CDS for editing time stamps saved in database tables.

Notes on Handling Time Stamps

Time stamps are only interpreted as such when they are accessed by the statements, methods, and functions listed above. When being assigned or converted, they behave like packed numbers of the type p, which means they are not suitable for direct calculations. Comparisons produce a meaningful result only when two time stamps are compared with each other. In programs for which the program attribute Fixed Point Arithmetic is not set, note the rules applying to the data type p. In particular, direct comparisons of time stamps in the long form with the short form produce a meaningful result only when the program attribute fixed point arithmetic is set. Otherwise, you must use system class CL_ABAP_TSTMP for comparisons as well. When assigning time stamps in the long form to time stamps in the short form, unwanted rounding effects occur. For this reason, always use the method MOVE of the system class CL_ABAP_TSTMP.

When time stamps are used in operand positions where they are not supposed to be used, no warnings are given by the syntax check or by the extended program check (not even in lossless assignments).

Examples

Example

Direct comparison of time stamps of the same data type.

GET TIME STAMP FIELD DATA(ts2).
WAIT UP TO 1 SECONDS.
GET TIME STAMP FIELD DATA(ts1).

ASSERT ts2 < ts1.

Example

Converts a time stamp to a date and a time field, and determines the summer time marker.

GET TIME STAMP FIELD DATA(ts).

CONVERT TIME STAMP ts TIME ZONE sy-zonlo
        INTO DATE DATA(date) TIME DATA(time)
        DAYLIGHT SAVING TIME DATA(dst).

cl_demo_output=>display( |{ date }\n{
                            time }\n{
                            dst } | ).

Example

Type-friendly formatting of a time stamp in a string template.

GET TIME STAMP FIELD DATA(ts).

cl_demo_output=>display( |{ ts TIMESTAMP = ISO } | ).

Example

Serializes a time stamp using a special domain.

DATA ts TYPE xsddatetime_z.
GET TIME STAMP FIELD ts.

CALL TRANSFORMATION id SOURCE ts = ts
                       RESULT XML DATA(xml).
cl_demo_output=>display_xml( xml ).

Example

Calculates the difference between two time stamps with the class CL_ABAP_TSTMP.

DATA: ts1 TYPE timestampl,
      ts2 TYPE timestampl.

GET TIME STAMP FIELD ts2.
WAIT UP TO 1 SECONDS.
GET TIME STAMP FIELD ts1.

DATA(seconds) = cl_abap_tstmp=>subtract(
    EXPORTING
      tstmp1 = ts1
      tstmp2 = ts2 ).

cl_demo_output=>display( seconds ).

Example

Direct calculation with time stamps. If, for example, ts1 has the value 20161004130733, adding 3600 s in ts2 produces the value 20161004140733. Since the time stamps are interpreted as numbers of type p in the calculation, the result is 10000, which would generally be unexpected.

GET TIME STAMP FIELD DATA(ts1).

DATA(ts2) = cl_abap_tstmp=>add( tstmp = ts1
                                secs  = 3600 ).

cl_demo_output=>display( ts2 - ts1 ).

Example

Incorrect calculation with time stamps. The assumption here is that time stamps are interpreted as a number of seconds in calculations. This is not the case here. The result does not meet expectations and is generally not a valid time stamp. For example, if 20161004131906 is calculated, this produces the invalid value 20161004315506.

GET TIME STAMP FIELD DATA(ts).

ts = ts + 86400 * 2 + 3600 * 3.

cl_demo_output=>display( ts ).

4.3.2 GET TIME STAMP

Syntax

GET TIME STAMP FIELD time_stamp.

Effect

This statement creates a POSIX UTC time stamp from the system time and system date in AS ABAP and assigns it to the variable time_stamp.

The following can be specified for time_stamp:

- An existing variable of the data type TIMESTAMP or TIMESTAMPL from ABAP Dictionary, in accordance with ABAP type p with length 8 or p with length 11, with seven decimal places. Depending on the data type, the time stamp is created either in the short form or in the long form.

- An inline declaration DATA(var), where a variable of type TIMESTAMP is declared.

Note

The precision of the decimal places of the long form depends on the hardware (processor) of the application server. The maximum resolution of 100 ns is not always reached. On some platforms, only a resolution of milliseconds can be reached.

Example

Creates two time stamps. An existing variable of the type TIMESTAMPL must be used for the long form. An inline declaration can be used for the short form. The time stamps must be given a special format for the output, since otherwise only numbers of type p would be displayed.

DATA tsl TYPE timestampl.

GET TIME STAMP FIELD DATA(ts).
GET TIME STAMP FIELD tsl.

cl_demo_output=>new(
  )->write( |{ ts  TIMESTAMP = ISO
                   TIMEZONE = 'UTC' }|
  )->write( |{ tsl TIMESTAMP = ISO
                   TIMEZONE = 'UTC' }|
  )->display( ).

Exceptions

Non-Handleable Exceptions

- Cause: The target field differs from TIMESTAMP or TIMESTAMPL with respect to type, length, and decimal places.
Runtime error: GET_TIMESTAMP_FORMAT

4.3.3 CONVERT TIME STAMP

Syntax

CONVERT TIME STAMP time_stamp TIME ZONE tz
        INTO [DATE dat]
             [TIME tim] [DAYLIGHT SAVING TIME dst].

Effect

This statement interprets the content of time_stamp as a time stamp, converts it to the local data and the local time in the time zone specified in tz, and assigns the result to the variables dat, tim, and dst.

time_stamp and tz are functional operand positions.

◉ The operand time_stamp must have the type TIMESTAMP or TIMESTAMPL from ABAP Dictionary, in accordance with the ABAP type p with length 8 or p with length 11 with seven decimal places. Depending on the data type, the content is interpreted either as a time stamp in the short form or as a time stamp in the long form. No other data types can be specified. If time_stamp does not contain a valid time stamp, the content of dat and tim is not changed, and sy-subrc is set to 12.

◉ The operand tz must be character-like and contain a time zone from the database table TTZZ.

     ◉ If tz is initial, the time zone is set implicitly to "UTC", dat and tim are given the appropriate values, and sy-subrc is set to 4.
     ◉ If the specified time zone is not found in the database table TTZZ, the content of dat and tim is not changed and sy-subrc is set to 8.
     ◉ If the rule set for the specified time zone is incomplete, a non-handleable exception is raised.
◉ The local date in the format of the data type d is assigned to dat and a variable must be specified to which the return value can be converted.

◉ The local time in the format of the data type t is assigned to tim and a variable must be specified to which the return value can be converted. If the time stamp in time_stamp is in the long form, the seconds fractions in the decimal places are ignored.

◉ If the time stamp in time_stamp for the time zone specified in tz is in summer time, dst is given the value "X". Otherwise it is given the value " ".

The following applies to the return values dat, tim, and dst:

◉ The return value for dat has the data type d. The following can be specified for dat:
- An existing variable to which the return value can be converted.
- An inline declaration DATA(var), where a variable of data type d is declared.

◉ The return value for tim has the data type t. The following can be specified for tim:
- An existing variable to which the return value can be converted.
- An inline declaration DATA(var), where a variable of data type t is declared.

◉ The return value for dst has the data type c with the length 1. The following can be specified for dst:
- An existing variable of the type c with length 1
- An inline declaration DATA(var), where a variable of data type c with length 1 is declared.

When time stamps are converted to reflect the conversion from the Julian calendar to the Gregorian calendar and the non-existence of the days between 19/5/1582 and 10/14/1582, this returns the same results as the conversion for the days from 10/15/1582 to 10/24/1582 (which do exist).

If time_stamp contains a valid value but produces an invalid date when combined with a valid time zone in tz, dat and tim are not modified and sy-subrc is set to 12.

System Fields

sy-subrc Meaning
Time stamp was converted into the local time of the specified time zone and assigned to the target fields. 
Time stamp was assigned to the target fields without conversion into the local time. 
Time stamp could not be converted, because the specified time zone is not in the database table TTZZ. 
12  Time stamp could not be converted since time_stamp contains an invalid value or produces an invalid date when combined with the time zone. 

Notes

◉ A current UTC time stamp can be created using the statement GET TIME STAMP.

◉ The current user time zone can be found in the system field sy-zonlo.

◉ It is now possible to use the return value for the summer time in dst to distinguish duplicate local time specifications that occur when UTC time stamps are converted into local time during the double hour in the changeover between summer and winter time.

◉ Usually, an invalid date can be created from a valid time stamp only by combining the first valid day with time zones west of UTC or the last valid day with time zones east of UTC.

Example

For the time zone "EST" in the database table TTZZ, a shift of -5 hours from the UTC reference time is entered in the database table TTZR. In the database table TTZDV, the end of summer time is defined as the last Sunday in October at 02:00 using the key "USA". In 2016 the last Sunday is October 30. If these settings are used in the rules, the two conversions below both produce the same local time of "01:30:00". The first conversion shows that the time is still in the summer time.

DATA: time_stamp     TYPE timestamp,
      tz  TYPE ttzz-tzone.

tz = 'EST'.
time_stamp = 20161030053000.
CONVERT TIME STAMP time_stamp TIME ZONE tz
        INTO DATE DATA(dat) TIME DATA(tim)
        DAYLIGHT SAVING TIME DATA(dst).
cl_demo_output=>write( |{ dat DATE = ISO } {
                          tim TIME = ISO } { dst }| ).

time_stamp = 20161030063000.
CONVERT TIME STAMP time_stamp TIME ZONE tz
        INTO DATE dat TIME tim
        DAYLIGHT SAVING TIME dst.
cl_demo_output=>write( |{ dat DATE = ISO } {
                          tim TIME = ISO } { dst }| ).

cl_demo_output=>display( ).

Exceptions

Non-Handleable Exceptions

- Cause: Inconsistent control tables for the conversion.
Runtime error: CONVERT_TSTMP_INCONSISTENT_TAB

4.3.4 CONVERT INTO TIME STAMP

Syntax

CONVERT DATE dat
        [TIME tim [DAYLIGHT SAVING TIME dst]]
        INTO TIME STAMP time_stamp TIME ZONE tz.

Effect

This statements converts a date specified in dat, a time specified in tim, and a summer time marker specified in dst from the time zone specified in tz into a time stamp and maps the result to the variable time_stamp.

dat, tim, dst, and tz are functional operand positions.

◉ dat expects a data object of the type d containing a valid date. Operands of other types are converted to d. If dat contains an invalid value, time_stamp is not changed and sy-subrc is set to 12.

◉ tim expects a data object of the type t containing a valid time. Operands of other types are converted to t. If the TIME addition is not specified, the system implicitly uses the initial time "000000" for tim. In tim, only the values 00 to 23 for the hours, and 00 to 59 for the minutes are valid. If tim contains an invalid value, time_stamp is not changed and sy-subrc is set to 12.

dst can be specified as a data object of the type c with length 1 containing the value "X" or " ". This controls the behavior of the statement with respect to summer time.

     ◉ If dst has the value "X", the value of tim is applied as the specified time in daylight saving time.
     ◉ If dst has the value "X", the value of tim is applied as the specified time in winter time.

If the time zone specified in tz does not have a summer time rule (for example when "UTC" is specified), the addition DAYLIGHT SAVING TIME is ignored. If the addition DAYLIGHT SAVING TIME is not specified, the value of dst is set to "X" implicitly if the data in tim and dat is in summer time and is set to " " for data in winter time. In the extra hour that is caused by switching from summer time to winter time, tim and dat are interpreted as a time in summer time and dst is set to the value "X". If the value in dst does not match the data in tim and dat (that is, if the value "X" is specified in winter time and the value " " in summer time, time_stamp is not changed and sy-subrc is set to 12.

◉ tz expects a character-like data object containing a time zone from the database table TTZZ.

     ◉ If tz is initial, the time zone is set implicitly to "UTC", time_stamp is given the appropriate data, and sy-subrc is set to 4.
     ◉ If the specified time zone is not found in the database table TTZZ, time_stamp remains unchanged and sy-subrc is set to 8.
     ◉ If the rule set for the specified time zone is incomplete, a non-handleable exception is raised.

The following can be specified for time_stamp:

◉ An existing variable of the data type TIMESTAMP or TIMESTAMPL from ABAP Dictionary, in accordance with ABAP type p with length 8 or p with length 11, with seven decimal places. If time_stamp has the data type TIMESTAMPL for the long form, the seconds fractions in the decimal places are initialized when the assignment is made.

◉ An inline declaration DATA(var), where a variable of type TIMESTAMP is declared.

When dates are converted to reflect the conversion from the Julian calendar to the Gregorian calendar and the non-existence of the days between 19/5/1582 and 10/14/1582, this returns the same results as the conversion for the days from 10/15/1582 to 10/24/1582 (which do exist).

If dat and tim contain valid values but produce an invalid time stamp when combined with a valid time zone in tz, time_stamp is not modified and sy-subrc is set to 12.

System Fields

sy-subrc Meaning 
0 Local time of specified time zone was converted to time stamp and assigned to the target field
The specified time was converted to a time stamp without time shift and assigned to the target field.
The specified time could not be converted, because the specified time zone is not in the database table TTZZ 
12  The specified time could not be converted, because dat, tim, or dst contain invalid or inconsistent values. 

Notes

◉ Current user-specific local times and the corresponding local time zones are stored in the system fields sy-datlo, sy-timlo, and sy-zonlo.

◉ Specifying daylight saving time and winter time after DAYLIGHT SAVING TIME enables different UTC time stamps to be created from matching local time stamps within the extra hour when switching from daylight saving time to winter time.

◉ When the switch is made from winter to daylight saving time, an hour is lost. For example, in the "CET" time zone in the year 2009, on March 29, the hour between 02:00 and 03:00 does not exist. If an attempt is made to convert a time during this missing hour, the statement is always terminated with the value 12 for sy-subrc because this time or this local time stamp does not exist.

◉ Usually, an invalid time stamp can be created from a valid date and time only by combining the first valid date 00010101 with time zones east of UTC or the last valid date 99991231 with time zones west of UTC.

Example

For the time zone "EST", the settings apply that are described in the example for CONVERT TIME STAMP in the rule set for time stamps. By specifying the daylight saving and winter time, two different UTC time stamps "20161030053000" and "20161030063000" are created from one specified local time. Without the addition DAYLIGHT SAVING TIME, the UTC time stamp "20161030053000" is created.

DATA: dat TYPE d,
      tim TYPE t,
      tz  TYPE ttzz-tzone.

tz = 'EST'.
dat = '20161030'.
tim = '013000'.

CONVERT DATE dat TIME tim DAYLIGHT SAVING TIME 'X'
        INTO TIME STAMP DATA(time_stamp) TIME ZONE tz.
cl_demo_output=>write_data( time_stamp ).

CONVERT DATE dat TIME tim DAYLIGHT SAVING TIME ' '
        INTO TIME STAMP time_stamp TIME ZONE tz.
cl_demo_output=>write_data( time_stamp ).

cl_demo_output=>display( ).

Non-Handleable Exceptions

- Cause: Inconsistent control tables for the conversion.
Runtime error: CONVERT_TSTMP_INCONSISTENT_TAB

4.3.5 System Class for Time Stamps

The class CL_ABAP_TSTMP is used to calculate and convert time stamps. Important methods include:

◉ The method ADD or SUBTRACTSECS adds or subtracts seconds from time stamps.
◉ The method TD_SUBTRACT calculates the difference between two time stamps.
◉ The method MOVE converts the long form of time stamps to the short form, and back, avoiding unwanted rounding effects.
◉ The method NORMALIZE normalizes time stamps. Invalid time stamps are converted into valid time stamps.

Example

Creation of a one hour earlier time stamp by subtracting 3600 seconds.

GET TIME STAMP FIELD DATA(ts1).

DATA(ts2) = cl_abap_tstmp=>subtractsecs( tstmp = ts1
                                         secs  = 3600 ).

cl_demo_output=>display( |{ ts1 TIMESTAMP = ISO
                       }\n{ ts2 TIMESTAMP = ISO }| ).

4.3.6 Example Converting Time Stamps

This example demonstrates the statements CONVERT TIME STAMP and CONVERT INTO TIME STAMP.

Source Code

REPORT demo_convert_time_stamp.

SELECTION-SCREEN: BEGIN OF SCREEN 100 AS WINDOW,
                  BEGIN OF BLOCK bl1 WITH FRAME.
PARAMETERS: date1 TYPE d,
            time1 TYPE t,
            tz1   TYPE ttzz-tzone.
SELECTION-SCREEN  BEGIN OF LINE.
PARAMETERS  dst_flag AS CHECKBOX.
SELECTION-SCREEN: COMMENT 3(29) text-dst,
                  POSITION POS_LOW.
PARAMETERS  dst1  TYPE abap_bool.
SELECTION-SCREEN: END OF LINE,
                  END OF BLOCK bl1,
                  BEGIN OF BLOCK bl2 WITH FRAME.
PARAMETERS  tsout TYPE c LENGTH 19 MODIF ID out.
SELECTION-SCREEN: END OF BLOCK bl2,
                  BEGIN OF BLOCK bl3 WITH FRAME.
PARAMETERS  tz2   TYPE ttzz-tzone.
PARAMETERS: date2 TYPE d         MODIF ID out,
            time2 TYPE t         MODIF ID out,
            dst2  TYPE abap_bool MODIF ID out.
SELECTION-SCREEN: END OF BLOCK bl3,
                  END OF SCREEN 100.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA: date TYPE d,
          time TYPE t,
          ts   TYPE timestamp.
    date1 = sy-datlo.
    time1 = sy-timlo.
    tz1 = tz2 = 'UTC'.
    DO.
      IF sy-index > 1.
        CALL SELECTION-SCREEN 100 STARTING AT 10 10.
        IF sy-subrc <> 0.
          EXIT.
        ENDIF.
      ENDIF.
      date = date1.
      time = time1.
      IF dst_flag = abap_false.
        CONVERT DATE date TIME time
                INTO TIME STAMP ts TIME ZONE tz1.
      ELSE.
        CONVERT DATE date TIME time DAYLIGHT SAVING TIME dst1
                INTO TIME STAMP ts TIME ZONE tz1.
      ENDIF.
      CASE sy-subrc.
        WHEN 4.
          MESSAGE 'Time zone set to UTC'
                  TYPE 'I' DISPLAY LIKE 'W'.
        WHEN 8.
          MESSAGE 'Invalid time zone'
                  TYPE 'I' DISPLAY LIKE 'E'.
          CONTINUE.
        WHEN 12.
          MESSAGE 'Invalid input values for date,'
                & ' time or daylight saving time'
                   TYPE 'I' DISPLAY LIKE 'E'.
          CONTINUE.
      ENDCASE.
      CONVERT TIME STAMP ts TIME ZONE tz2
              INTO DATE date TIME time DAYLIGHT SAVING TIME dst2.
      CASE sy-subrc.
        WHEN 4.
          MESSAGE 'Time zone set to UTC'
                  TYPE 'I' DISPLAY LIKE 'W'.
        WHEN 8.
          MESSAGE 'Invalid time zone'
                  TYPE 'I' DISPLAY LIKE 'E'.
          CONTINUE.
        WHEN 12.
          MESSAGE 'Invalid time stamp'
                  TYPE 'I' DISPLAY LIKE 'E'.
          CONTINUE.
      ENDCASE.
      tsout = |{ ts TIMESTAMP = ISO TIMEZONE = 'UTC   ' }|.
      date2 = date.
      time2 = time.
    ENDDO.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

AT SELECTION-SCREEN OUTPUT.
  LOOP AT SCREEN INTO DATA(screen_wa).
    IF screen_wa-group1 = 'OUT'.
      screen_wa-input   = '0'.
      screen_wa-output  = '1'.
    ENDIF.
    MODIFY SCREEN FROM screen_wa.
  ENDLOOP.

Description

The program is given a date, a time, and a time zone and converts this information into a time stamp. The program can also include summer time in its calculations. The resulting time stamp is then converted to the local date and local time of another time zone. The following table shows several possible combinations of input and output, where spc stands for a space and - for no input or output.

date1 time1   tz1 dst1 ts 
16.07.2010 09:10:00 CET - 2010-07-16T07:10:00
16.07.2010   09:10:00   CET  2010-07-16T07:10:00  
16.07.2010   09:10:00   CET  spc 
16.12.2010   09:10:00   CET  spc  2010-12-16T08:10:00  
16.12.2010   09:10:00   CET 
16.12.2010  09:10:00   AUSTAS 2010-12-15T22:10:00  

tz2 date2  time2   dst2   sy-subrc 
CET 16.07.2010 09:10:00 X -
CET  16.07.2010   09:10:00  
CET  12 
CET  16.12.2010   09:10:00  
CET  12 
AUSTAS  16.12.2010   09:10:00  

The third and fifth rows demonstrate that invalid summer time data produces the value 12 in sy-subrc.

4.4 System Fields for Date and Time

The following table shows the system fields that contain information about date and time.

System Field Type  Length  Content  GET TIME 
sy-datlo d - User date
sy-datum   System date
sy-dayst   Flag for summer time in the system time zone. During summer time, "X", otherwise " ". 
sy-fdayw   Factory calendar weekday in the system time zone. "1" for Monday, ..., "5" for Friday. 
sy-timlo   User time  
sy-tzone   Time difference between the system time and UTC reference time in seconds, ignoring summer time 
sy-uzeit   System time 
sy-zonlo   User time zone 

The values of all system fields in this table are set implicitly when the program is started, every time a screen of a dynpro is sent, and when the internal session is set. The last column of the table shows which of the system fields can be updated explicitly using the statement GET TIME.

The content of sy-zonlo is the user time zone described under System Time Zone and User Time Zone. The local values of sy-datlo and sy-timlo that make reference to the user time zone are determined from the system time and the system date. If sy-zonlo is initial, sy-timlo and sy-datlo have the same values as sy-uzeit and sy-datum.

Example

If the system time zone is the same as the user time zone, the corresponding system fields for system date and user date and/or system time and user time must be the same.

DATA tz TYPE timezone.
CALL FUNCTION 'GET_SYSTEM_TIMEZONE'
  IMPORTING
    timezone = tz.

IF tz = sy-zonlo.
  ASSERT sy-datum = sy-datlo.
  ASSERT sy-uzeit = sy-timlo.
ENDIF.

GET TIME

Syntax

GET TIME [FIELD tim].

Addition:

... FIELD tim

Effect

Without the addition FIELD, the system fields for date and time, sy-datlo, sy-datum, sy-timlo, and sy-uzeit, are set to the current value. The content of the system fields sy-dayst, sy-fdayw, sy-tzone, and sy-zonlo is not updated.

Note

Except for GET TIME, the system fields are updated after starting a program, after sending a screen, and after changing the internal session.

Example

Approximate runtime measurement. You should use the statement GET RUN TIME instead.

GET TIME.
DATA(t1) = sy-timlo.

DO 5000000 TIMES.
ENDDO.

GET TIME.
DATA(t2) = sy-timlo.

cl_demo_output=>display( t2 - t1 ).

Addition

... FIELD tim

Effect

The addition FIELD is used to pass the current system time in the format "hhmmss" to the variable tim instead of sy-uzeit, and none of the system fields are updated. The return value of the statement has data type t. The following can be specified for tim:

◉ An existing variable of the data type t or a variable to which the type t can be converted.

◉ An inline declaration DATA(var), where a variable of type t is declared.

Example

The example has the same function as the previous one.

GET TIME.

DO 5000000 TIMES.
ENDDO.

GET TIME FIELD DATA(time).

cl_demo_output=>display( time - sy-uzeit ).

5. Structures


Structures are data objects (comprised of components of any data type) that are saved in sequence in the memory. The data type of a structure is a structured type or a structure defined in the ABAP Dictionary.

In a program, a structured type or structure is created using the additions BEGIN OF ... END OF additions of the statements TYPES, DATA etc. They can also be created dynamically using RTTC methods. Im ABAP Dictionary, structures appear as standalone data types, as types of database tables, of classic views or of CDS entities.

Types of Structures

Structures are named as follows (depending on the type of the component):

◉ Flat structures do not contain any deep components. They only contain components with flat data types, such as elementary types c, n, d, t, decfloat16, decfloat34, f, i, int8, p, x, plus b, s (or structures with these types).
◉ Flat character-like structures are flat structures that contain only character-like components.
◉ Nested structures contain at least one substructure. A nested structure is flat or character-like, depending on the attributes of all of the components.
◉ Deep structures contain at least one deep component, at any nesting level. Possible deep components include strings, internal tables, boxed components, data references, or object references.

A structure that contains static or dynamic components is (formally) a static or dynamic data object, respectively.

The following figure illustrates a deep structure.

Note

The term "nested structure" must not be confused with the term "deep structure". A nested structure is flat if it only contains flat components and subcomponents. A nested structure is deep when it has at least one deep component or subcomponent.

Example

Nested structure. The substructure is created using a reference to the dictionary structure SCARR for the second component. Access to the components using the structure component selector (-).

DATA:
  BEGIN OF struct,
    name  TYPE c LENGTH 10 VALUE 'SCARR',
    scarr TYPE scarr,
  END OF struct.

SELECT SINGLE *
       FROM scarr
       WHERE  carrid = 'LH'
       INTO CORRESPONDING FIELDS OF @struct-scarr.

cl_demo_output=>new(
)->write_data( struct-name
)->write_data( struct-scarr-carrid
)->write_data( struct-scarr-carrname
)->display( ).

Example

Deep structure The following structure contains deep components only.

DATA:
  BEGIN OF struct,
    text TYPE string,
    hex  TYPE xstring,
    tab  TYPE STANDARD TABLE OF i WITH EMPTY KEY,
    dref TYPE REF TO i,
    iref TYPE REF TO if_demo_output,
  END OF struct.

Using Structures

Structures are addressed either fully or by component, at appropriate operand positions. The structure component selector (-) is used for the latter case. In the case of data reference variables that are typed as a structure, the components of the structure to which the data reference variable points are addressed using the object component selector (->).

Special conversion and comparison rules apply to assignments that involve structures. Flat character-like structures differ in that they can be used like character-like data objects in many operand positions and included in the generic ABAP type clike. They can be specified in almost all operand positions where elementary character-like data objects are possible. The ABAP runtime environment then interprets the structure as an elementary field of the type c in the length of the structure. An exception to this are operand positions of string expressions and string functions.

For the assignement of structure components, there is a special statement MOVE-CORRESPONDING, a constructor operator CORRESPONDING, and a system class CL_ABAP_CORRESPONDING. The INTO clause of Open SQL also has an addition CORRESPONDING. The ASSIGN statement has a special variant COMPONENT OF STRUCTURE for dynamic access to stucture components.

Example

Inline-Deklaration einer Struktur wa in einer SELECT-Anweisung mit Bezug auf die Datenbanktabelle SCARR im ABAP Dictionary. Zugriff auf die Komponenten der Struktur über den Strukturkomponenten-Selektor (-). .

SELECT carrid, carrname
       FROM scarr
       INTO @DATA(wa).
  cl_demo_output=>write( |{ wa-carrid WIDTH = 5
                         }{ wa-carrname }| ).
ENDSELECT.
cl_demo_output=>display( ).

Example

Erzeugung einer Struktur als anonymes Datenobjekt mit dem Typ der Datenbanktabelle SCARR im ABAP Dictionary, auf das die Datenreferenzvariable dref zeigt. Verwendung über den Dereferenzierungsoperator (->*) als Zielbereich einer SELECT-Anweisung. Zugriff auf die Komponenten der Struktur über den Objektkomponenten-Selektor (->).

DATA(dref) = NEW scarr( ).

SELECT *
       FROM scarr
       INTO @dref->*.
  cl_demo_output=>write( |{ dref->carrid WIDTH = 5
                         }{ dref->carrname }| ).
ENDSELECT.
cl_demo_output=>display( ).

Example

Das folgende Beispiel zeigt, dass sich eine flache Struktur mit rein zeichenartigen Komponenten sowohl wie eine Struktur als auch wie ein zeichenartiges Datenobjekt verhalten kann.

DATA:
  BEGIN OF struct,
    col1 TYPE c LENGTH 5 VALUE '12345',
    col2 TYPE c LENGTH 5 VALUE 'abcde',
  END OF struct.

cl_demo_output=>new(
  )->write_data( struct
  )->write_data( struct-col1
  )->write_data( struct-col2
  )->write_data( CONV string( struct )
  )->display( ).

Using Deep Structures

The data content of deep structures is not saved in full within the memory bounds of the structure and the deep components are instead just references to the actual data. This means that some general restrictions apply when using deep structures (unlike flat structures):

◉ Deep structures must be compatible in assignments and comparisons.
◉ Substring access using offset/lengths is not possible for deep structures.
◉ Deep structures cannot be used as character-like data objects in operand positions and are not included in the generic ABAP type clike. This applies in particular to structures that contain strings.
◉ When data objects are cast with ASSIGN, the type, position in the type of the data object, and the position in the type of the field symbol must match for deep components in deep structures.
◉ The work area of Open SQL statements cannot contain any deep components other than strings or LOB handles
◉ The target or source field of the statements OPEN DATASET and TRANSFER cannot be a deep structure.
◉ Internal tables with a deep row type cannot be passed to a TABLES parameter in a remote function call. Deep structures can be passed to the other parameters, as long as they do not contain any reference variables.

Note

These restrictions are particularly important if single components in existing flat character-like structures are converted to strings, which makes the entire structure deep.

Example

The following two deep structures are not compatible and cannot be converted to each other.

DATA:
  BEGIN OF struct1,
    col1 TYPE c LENGTH 4,
    col2 TYPE REF TO data,
  END OF struct1,
  BEGIN OF struct2,
    col1a TYPE c LENGTH 2,
    col1b TYPE c LENGTH 2,
    col2 TYPE REF TO data,
  END OF struct2.

struct1 = struct2. "syntax error

Using Deep ABAP Dictionary Structures

The same restrictions apply to deep structures defined in ABAP Dictionary as to deep structures defined in ABAP programs. Further restrictions apply only when the following language elements forbidden in classes are used (only flat ABAP Dictionary structures can be specified in classes):

◉ Statement TABLES for table work areas
◉ Obsolete use of LIKE instead of TYPE as a reference to data types in ABAP Dictionary with type declarations and typings
◉ Obsolete use of INCLUDE STRUCTURE
◉ Obsolete forcing of a structure STRUCTURE to field symbols or interface parameters of function modules or subroutines

Boxed Components

The substructures of nested structures and structured components of classes or interfaces can be declared as boxed components.

5.1 Boxed Components

Boxed components are structures that are not saved in the higher-level context itself. Instead, an internal reference that points to the actual structure is stored in place of the structure. A boxed component is always a deep component of its context.

It is currently to possible to declare substructures of data types and structured attributes of classes as

◉ static boxes

that support initial value sharing.

Notes

◉ Boxed components are a halfway house between static and dynamic data objects. Their memory requirement has not already been defined when the program is started in the internal session, but they can be handled like static data objects with a fixed length.

◉ A nested structure that contains a boxed component as a component is always a deep structure.

◉ Structures in ABAP Dictionary can also contain boxed components. ABAP Dictionary database tables cannot contain any boxed components as their structures have to be flat.

◉ In the classes and objects of the RTTS, boxed components are handled as follows:

     ◉ The class CL_ABAP_TYPEDESCR contains the constant TYPEKIND_BREF for static boxes. The value of these constants is specified as the type of a static box in the component table COMPONENTS of the class CL_ABAP_STRUCTDESCR or the attribute table ATTRIBUTES of the classes CL_ABAP_CLASSDESCR or CL_ABAP_INTFDESCR.

     ◉ In the return code of the method GET_COMPONENTS of the class CL_ABAP_STRUCTDESCR or GET_ATTRIBUTE_TYPE of the classes CL_ABAP_CLASSDESCR or CL_ABAP_INTFDESCR, boxed components are specified as type description objects of the class CL_ABAP_REFDESCR, like reference variables. The method GET_REFERENCED_TYPE of this class gets a type description object for the substructure. A type description object of the class CL_ABAP_REFDESCR, which describes a boxed component, cannot be used in the statements CREATE DATA or ASSIGN CASTING.

5.1.1 Static Boxes

Static boxes are boxed components whose components are known statically and which are subject to initial value sharing. Declarations of static boxes generally require less memory for structures that occur multiple times but are rarely used.

The following can currently be declared as static boxes:

◉ Substructures of structured data with the addition BOXED of the statement TYPES

◉ Structured attributes of classes or interfaces with the addition BOXED of the statement [CLASS-]DATA.

A static box can have one of two states:

◉ Initial value sharing

As long as none of the actions named in the following point were executed, initial value sharing applies to a static box. The internal reference points to a type-dependent initial value for the structure, which is saved exactly once in each application server in the PXA. The memory requirement in the internal session is determined only by the internal reference and its administration.

◉ Revoked initial value sharing

The following actions revoke initial value sharing for a static box:

- Writes to the static box or one of its components
- Assigning the static box or one of its components to a field symbol using ASSIGN
- Addressing the static box or one of its components using a data reference
- Using a static box or one of its components as an actual parameter for procedure calls

The internal reference then references an instance of the structure in the current internal session. The memory requirement is the same as for a regular structure plus the administration costs for the internal reference.

In initial value sharing, the memory required for the internal session of a static box is not known initially when the program is executed. However, unlike in real dynamic data objects, the length of a static box is always known statically. In static boxes, the same functions are available as for a regular component of the same type. When a structure that contains a static box is accessed, however, it must not be forgotten that it is a deep component.

Notes

◉ Static boxes can be used to optimize the memory requirement of structures that are used more than once. If, for example, the row structure of an internal table contains substructures, the memory requirement of the substructure accumulates without the use of static boxes for each row, even if the substructure is initial. If static boxes are used, initial substructures do not require multiple memories as long as only reads are performed.

◉ In addition to the optimized memory requirements, static boxes generally have a positive impact on runtime performance, because assignments between components for which initial value sharing is active only require the internal reference (and no additional data) to be copied.

◉ The statements CLEAR and FREE do not operate as write statements on a static box that has the initial value sharing state, and the state is persisted. On the other hand, once the initial value sharing state is revoked, these statements do not currently free up any memory and provide the local instance of the static box with type-dependent initial values instead.

Example

Declares a substructure scarr as a static box.

TYPES:
  BEGIN OF struct,
    comp      TYPE c LENGTH 10,
    scarr TYPE scarr BOXED,
  END OF struct.

5.2 Examples of structures

Filling a Structure

The example demonstrates the filling of a nested structure.

Source Code

REPORT demo_structure_filling.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    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,
      BEGIN OF address_type,
        name   TYPE name_type,
        street TYPE street_type,
        city   TYPE city_type,
      END OF address_type.
    CLASS-METHODS main.
ENDCLASS.
CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA: name TYPE name_type,
          addr TYPE address_type.
    name-title   = `Mr.`.
    name-prename = `Duncan`.
    name-surname = `Pea`.
    addr-name = name.
    addr-street-name   = `Vegetable Lane`.
    addr-street-number = `11`.
    addr-city-zipcode = `349875`.
    addr-city-name    = `Botanica`.

    DATA(address) =
      VALUE address_type(
        name-title   = `Mr.`
        name-prename = `Duncan`
        name-surname = `Pea`
        street = VALUE #( name   = `Vegetable Lane`
                          number = `11` )
        city   = VALUE #( zipcode = `349875`
                          name    = `Botanica` ) ).

    ASSERT address = addr.

    cl_demo_output=>new(
      )->write( address-name
      )->write( address-street
      )->write( address-city
      )->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

In this example, the structure from the executable example Declaring a Nested Structure is defined with TYPES as the data type address_type and used for the data objects addr and address. A structure type is also defined for each separate substructure.

If a structure is declared by reference to a structure type, as shown here, the addition VALUE cannot be used. Instead, the structure must be filled by accessing the components. In particular, this also applies to the frequently occurring reference to structures of the ABAP Dictionary.

◉ In the first part of the method main, the structure addr is filled by using the structure component selector. The component name is assigned a prefilled structure. In the components street and city, the components that are nested there are accessed.

◉ In the second part of the method main, the structure address is filled with the value operator VALUE, but the structure itself is created using an inline declaration. The parentheses after VALUE show different options for accessing the components of substructures. Either the structure component selector is used again, as with the substructure name, or additional VALUE operators are nested, as with street and city.

The content of the two structures is the same. The value operator VALUE can be used to replace many fully spelled names simply by using parentheses.

Structure from ABAP Dictionary

This example demonstrates a structure from the ABAP Dictionary and its use.

Source Code

REPORT demo_dictionary_structure.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.
CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA carrier TYPE scarr.
    carrier-carrid = 'UA'.
    cl_demo_input=>request( CHANGING field = carrier-carrid ).
    SELECT SINGLE *
           FROM scarr
           INTO carrier
           WHERE carrid = carrier-carrid.
    cl_demo_output=>display( carrier ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

Structure types of the ABAP Dictionary are typically used in ABAP programs to define structures or internal tables with a structured row type to process data that is imported to them from the database. The example shows how a structure carrier local to the program is declared by reference to a database table SCARR defined in the ABAP Dictionary, and used as the target area of a SELECTstatement.

6. Enumerated Objects


This section summarizes the use of enumerated objects (data objects with enumerated types.

◉ Declaration

An enumerated object is a data object with an enumerated type defined by one of the following TYPES statements:

TYPES BEGIN OF ENUM enum_type ...
  TYPES val1 ...
  TYPES val2 ...
  ...
TYPES END OF ENUM enum_type ...

The technical data type of the content of an enumerated object is the base type of the enumerated type. The potential content is defined by the enumerated values defined using TYPES val1, TYPES val2, ..., of which at least one value must have the type-dependent initial value. The base type is i by default but it can be a different elementary data type. The following enumerated objects exist:

◉ Enumerated variables

An enumerated variable is a variable defined using

DATA enum_var TYPE enum_type ...

that can contain only enumerated values of the enumerated type. This is ensured by the ABAP runtime environment and the rules for using enumerated types.

◉ Enumeration constants

An enumeration constant is used to define a value in the value set of an enumerated type. In the
definition of an enumerated type, it is defined using

TYPES val ...

under the name val. This constant is a constant of the context of its definition and contains the enumerated value assigned in the definition. It can be specified in all reading positions in which enumerated objects are possible. Its enumerated value is either determined automatically as a whole number or can be specified explicitly in the definition.

◉ Components of enumeration structures

A component of an enumeration structure is a special form of an enumeration constant that exists as a component of a constant structure and not as a single data object. The enumeration structure struc is defined using

TYPES BEGIN OF ENUM enum_type STRUCTURE struc ...

. This makes the enumeration constants defined using

TYPES val ...

into their structure components. Otherwise, the same applies as to regular enumeration constants.

Notes

◉ Enumerated objects are mainly used to check permitted values. This usually restricts the actual parameters passed to methods to the enumerated values defined in the class.

◉ The base type and the actual enumerated value are almost always ignored when enumerated objects are used. Assignments and comparisons are usually only made between enumerated objects with the same enumerated type.

◉ Base types other than i and the actual enumerated values may be significant in transformations between enumerated type or in migrations of predecessor concepts.

◉ Enumeration structures can be used to avoid naming conflicts if there are multiple enumerated types in a single namespace. Enumeration structures can be used to enable the use of the same enumeration constant name in multiple enumerated types.

Example

Declares an enumerated type size in a class. The method parameter size has the enumerated type and only enumerated objects of this type can be passed to this parameter. This example show how the enumeration constant demo=>l is passed. This guarantees that only enumerated values of the enumerated type can be passed to the parameter. These values can be evaluated in comparisons with the enumeration constants. In the CASE control structure shown here, the statement block after WHEN OTHERS can be reached only when demo=>xl and demo=>xxl are passed.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    TYPES:
      BEGIN OF ENUM size,
        s, m, l, xl, xxl,
      END OF ENUM size.
    CLASS-METHODS main
      IMPORTING size TYPE size.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    CASE size.
      WHEN s.
        ...
      WHEN m.
        ...
      WHEN l.
        ...
      WHEN OTHERS.
        ...
    ENDCASE.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( demo=>l ).

◉ Processing of Enumerated Objects

Enumerated objects are almost always processed independently of the base type of the enumerated type. Only the enumerated type itself is of relevance for all rules specified here. Assignments of enumerated objects with a numeric base type, for example, cannot be assigned to numeric target fields and cannot be compared with numeric fields. The enumerated vale in the base type can be accessed using the constructor operators CONV and EXACT only.

     ◉ Operand Positions for Enumerated Objects

- Reading positions

Enumerated objects can be used in all reading positions in which the operand type is their enumerated type or in which the operand is converted to one of the character-like types c or string. Substring access is not possible.

- Writing positions

Enumerated variables can only be used in writing positions in which the operand type is the enumerated type and only the associated enumerated values can be written. If known statically, an attempt to assign a value other than a valid enumerated value to an enumerated variable produces a syntax error. If not known statically, an exception is raised.

Example

In the first assignment, the enumeration constant xl in a reading position is assigned to the enumerated variable size in a writing position. The string expression in the second half exploits the fact that the enumeration constants are converted implicitly to the type string before the chaining. The result is SMLXLXXL.

TYPES:
  BEGIN OF ENUM size,
    s, m, l, xl, xxl,
  END OF ENUM size.

DATA size TYPE size.

size = xl.

DATA(str) = s && m && l && xl && xxl.

     ◉ Value Assignments

Only enumerated objects with the same enumerated type can be assigned to an enumerated variable. In the assignment, the target field is given the enumerated value of the source field.

The initial value of the base type is always a valid enumerated value of an enumerated type. Accordingly, an enumerated variable can be set to the initial value of its base type using CLEAR. Assignments of VALUE enum_type( ) are also possible.

In the reverse case, enumerated objects can only be assigned to compatible enumerated variables, with the following exception: There is a conversion rule for assignments of enumerated objects to character-like variables of the types c and string. In this case, the target field is assigned the name of the enumeration constant or of the component of the enumeration structure under which the enumerated value of the source field is defined in the enumerated type-

In structures, each component comprises a separate fragment of the Unicode fragment view using an enumerated type. In assignments between structures of this type, the fragment views must match. This makes sure that only components with the same enumerated type can be assigned to each other.

Example

In the first assignment, the enumeration constant sz-xl is assigned to the enumerated variable size of its enumerated type. This variable then contains the associated enumerated value 3. In the second assignment, the enumeration constant is assigned to the text string size_string. This string is given the value XL in accordance with the conversion rule.

TYPES:
  BEGIN OF ENUM size STRUCTURE sz,
    s, m, l, xl, xxl,
  END OF ENUM size STRUCTURE sz.

DATA size TYPE size.
size = sz-xl.

DATA size_string TYPE string.
size_string = sz-xl.

     ◉ Comparisons

In comparisons between enumerated objects, the comparison rule applies that an enumerated object can only be compared with an enumerated object with the same enumerated type. Here, the values of the operands are compared in accordance with their base type.

Each enumerated type has an initial enumerated value, which makes checks with the predicate expression IS INITIAL possible.

Example

The first comparison shows a typical case where an enumerated variable is compared with an enumeration constant. The syntax of the second comparison (in a comment) is not possible. In the third comparison, the enumerated variable is converted explicitly to the type string before the comparison with a text string.

TYPES:
  BEGIN OF ENUM size,
    s, m, l, xl, xxl,
  END OF ENUM size.

DATA size TYPE size.

...

IF size = xl.
ENDIF.

"IF size = `XL`. "<--- Syntax error
"ENDIF.

IF CONV string( size ) = `XL`.
ENDIF.

     ◉ Typing of Formal Parameters and Field Symbols

If formal parameters of procedures or field symbols are typed with an enumerated type, only enumerated objects with the same enumerated type can be assigned to them. As usual, an exception to this are return values of functional methods that can also be assigned to character-like objects of the types c and string.

Enumerated types are covered by the generic types any, data, and simple. When an enumerated object is passed to generically typed formal parameters or in assignments to generically typed field symbols, these are given the enumerated type. In assignments to field symbols, castings with the CASTING addition are not possible and an enumerated type cannot be specified after this addition.

When generically typed formal parameters or field symbols are used for enumerated objects, the restriction applies that only statically known operands with the same enumerated type are permitted in reading positions in which an enumerated object is expected and this is known statically. This affects, for example, the source field of an assignment to an enumerated variable or an operand compared with an enumerated object. In writing positions for enumerated objects, however, generic formal parameters or field symbols are allowed for enumerated types. If the operand type is not known statically, the check is only made at runtime in reading positions too.

Example

The field symbol fs1 typed generically with simple cannot be assigned to an enumerated variable size known statically or compared with it. An assignment of size to the field symbol and fully generic handling are, however, possible.

TYPES:
  BEGIN OF ENUM size,
    s, m, l, xl, xxl,
  END OF ENUM size.

FIELD-SYMBOLS <fs1> TYPE simple.
FIELD-SYMBOLS <fs2> TYPE simple.

DATA(size) = xl.

ASSIGN size TO <fs1>.
ASSIGN size TO <fs2>.

<fs1> = size.
"size = <fs1>.        "<--- Syntax error
"ASSERT size = <fs1>. "<--- Syntax error

<fs2> = <fs1>.
ASSERT <fs1> = <fs2>.

     ◉ Access to the Enumerated Value

A special rule for the conversion operator CONV applies when accessing the enumerated value of an enumerated object:

... CONV base_type( enum_dobj ) ...

When the base type base_type of an enumerated object enum_dobj specified as an argument is specified directly or indirectly, CONV returns its enumerated value.

In the reverse case, a valid enumerated value can be converted to an enumerated object:

... CONV enum_type( dobj ) ...

The argument dobj is converted to the base type of the enumerated type enum_type and CONV returns an enumerated object with this value. Any invalid values raise an exception.

In combinations of these two variants (in which CONV base_type( enum_dobj ) is used as an argument dobj of CONV enum_type( dobj )), there is a short form:

... CONV enum_type( enum_dobj ) ...

If different enumerated types can have the same base type, an enumerated object of an enumerated type can be converted to the corresponding enumerated object of a different enumerated type.

Note

The corresponding rules applies to the lossless operator EXACT. Here, additional losslessness checks are made.

Example

The inner conversion operator CONV accesses the current enumerated value of the enumerated object size and returns it in the type i. The outer conversion operator CONV converts the result of the addition back to an enumerated type and assigns this enumerated value to the enumerated variable size. The enumerated value in size is raised by one in each iteration. The final result is the value of the enumeration constant xxl.

TYPES:
  BEGIN OF ENUM size,
    s, m, l, xl, xxl,
  END OF ENUM size.

DATA size TYPE size.

DO 4 TIMES.
  size = CONV #( CONV i( size ) + 1 ).
ENDDO.

     ◉ Type Descriptions

The type returned for an enumerated object by the statement DESCRIBE FIELD is k. The length is the length of the enumerated value in the base type in bytes.

In RTTS, enumerated objects are described by objects of the class CL_ABAP_ENUMDESCR. This class can be used for RTTI and RTTC purposes. Like any enumerated type, an enumerated type created using RTTC is only compatible with itself.

The following attributes exist in a type description of the class CL_ABAP_ENUMDESCR:

- KIND always has the value E for the elementary base type
- TYPE_KIND always has the value k (as in the statement DESCRIBE FIELD)
- BASE_TYPE_KIND describes the base type
- MEMBERS is a table of the enumeration constants and the associated enumerated values

     ◉ Data interfaces

The following data interfaces support enumerated types:

The character-like representation of enumerated objects (namely the result of a c or string) is used for their output and serialization. The output consists of the name (with a maximum of thirty characters) of the enumeration constant of the current enumerated value in uppercase. Deserializations are performed in the reverse direction. The following are supported:

- Data clusters with the statements EXPORT and IMPORT. When an enumerated object is exported, the enumerated value is saved in the base type and flagged as an enumerated value. Both enumerated objects and exported data objects of the base type can be imported to a suitable enumerated object (the value is checked here). No exported enumerated objects, however, can be exported to data objects of the base type.
- ABAP file interface with the statements TRANSFER and READ DATASET. In writes to a file and reads from a file, enumerated objects are handled like data objects of its base type. In reads from a file to an enumerated object, the value is checked to see whether it is a valid enumerated value.

- Serializations and deserializations from and to XML and JSON. The formats asXML and asJSON represent the content of enumerated objects in their character-like representation (the name of the enumeration constant of the current enumerated value). Only valid names are allowed in deserializations to an enumerated object.

- List output with the statement WRITE. Like WRITE TO, this statement converts an enumerated object to its character-like representation (the name of the enumeration constant of the current enumerated value). The output length is the same as the maximum length of the name (30 characters).

Note

A deserialization of a name of an enumeration constant is one of the few ways of creating an enumerated value from the name in full and dynamically.

Example

WRITE output of the enumeration constants of an enumeration structure.

TYPES:
  BEGIN OF ENUM size STRUCTURE sz,
    s, m, l, xl, xxl,
  END OF ENUM size STRUCTURE sz.

WHILE sy-subrc = 0.
  ASSIGN COMPONENT sy-index OF STRUCTURE sz TO FIELD-SYMBOL(<fs>).
  IF sy-subrc = 0.
    WRITE / <fs>.
  ENDIF.
ENDWHILE.

     ◉ Forbidden Uses

The following uses are forbidden to ensure that an enumerated object only ever contains a single valid enumerated value:

◉ Within ABAP, enumerated objects are never interpreted in accordance with their base type. This means that they cannot be used in operand positions that expect numeric, character-like, or byte-like data types. The only exception to this are the operand positions in which an implicit conversion to a character-like type takes place.

◉ Enumerated types are not currently supported by ABAP Dictionary. Accordingly, no database tables whose columns have an enumerated type can be defined in ABAP Dictionary.

◉ In both Open SQL and Native SQL (EXEC SQL, ADBC), no host variables or references to ABAP variables with enumerated type can be used.

◉ No enumerated types are supported for the input fields of selection screens.

◉ It is not possible to use enumerated values from lists in the list buffer, since there are no conversion rules between character-like types and enumerated types.

If an enumerated object is provided with an invalid value due to a gap in the rules, this results in the following behavior:

◉ The result of a conversion to c or string is the string <illegal enum value>, which is displayed accordingly in the ABAP Debugger.

◉ An enumerated object with an invalid value can be assigned to other enumerated objects with the same enumerated type without being checked.

◉ The expressions CONV base_type( enum_dobj ) and EXACT base_type( enum_dobj ) return the invalid value.

An invalid value must be viewed as an error and should never occur.

Note

Classic Dynpros represent a known gap. Enumerated types are not supported by dynpros. When dynpro input fields are taken from a program by using enumerated objects, they are handled like an object of the base type. Any invalid values are passed to the associated enumerated object in the event PAI without being checked. For this reason, enumerated objects should never be associated with classic dynpros.

Example

The program DEMO_ENUM_DYNPRO uses an enumerated object on a dynpro. This can produce invalid values in the program.

6.1 Enumerated Objects, Use

This example demonstrates the use of enumerated types.

Source Code

REPORT demo_enumerated_types.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-DATA html TYPE string.
    CLASS-METHODS:
      class_constructor,
      main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA: size   TYPE  cl_demo_wrap_browser=>size
                 VALUE cl_demo_wrap_browser=>sz-l,
          format TYPE  cl_demo_wrap_browser=>format
                 VALUE cl_demo_wrap_browser=>fmt-l.

    cl_demo_input=>add_field( EXPORTING text = `Size (S, M, L, XL)`
                              CHANGING  field = size ).
    cl_demo_input=>request(   EXPORTING text  = `Format (L, P)`
                              CHANGING  field = format ).

    cl_demo_wrap_browser=>show( html   = html
                                size   = size
                                format = format ).
  ENDMETHOD.
  METHOD class_constructor.
    cl_abap_docu_external=>get_abap_docu_for_adt(
       EXPORTING
        language = COND #( WHEN sy-langu = 'D' THEN 'DE' ELSE 'EN' )
      IMPORTING
        html     = html  ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The program calls the method SHOW of class CL_DEMO_WRAP_BROWSER . Two enumerated types are defined as follows in this class:

TYPES:
  BEGIN OF ENUM size STRUCTURE sz,
    s, m, l, xl,
  END OF ENUM size STRUCTURE sz.
TYPES:
  BEGIN OF ENUM format STRUCTURE fmt,
    l, p,
  END OF ENUM format STRUCTURE fmt.

The use of enumeration structures means that the same name l can occur twice. Method SHOW wraps method SHOW_HTML of class CL_ABAP_BROWSER:

METHOD show.
  cl_abap_browser=>show_html(
    html_string = html
    size    = SWITCH #(  size
                         WHEN sz-s  THEN cl_abap_browser=>small
                         WHEN sz-m  THEN cl_abap_browser=>medium
                         WHEN sz-l  THEN cl_abap_browser=>large
                         WHEN sz-xl THEN cl_abap_browser=>xlarge )
    format  = SWITCH #(  format
                         WHEN fmt-l THEN cl_abap_browser=>landscape
                         WHEN fmt-p THEN cl_abap_browser=>portrait )
    buttons = abap_true ).
ENDMETHOD.

The input parameters size and format format of the method have the same enumerated types and can only contain their enumerated values. These are mapped to the corresponding constants of class CL_ABAP_BROWSER. These constants can be regarded as a workaround for real enumerated types, which were not available when CL_ABAP_BROWSER was developed.

Enumerated values can be specified for the size and format when the program is executed. The values are passed internally to the program by deserializing the character-like values into the local enumerated variables size and format. The exception for invalid values is caught internally; here the enumerated variables are initialized, which corresponds to the values of the enumeration constants sz-s and fmt-l.

6.2 Example Enumerated Objects, Type Description

This example demonstrates type description in enumerated objects.

Source Code

REPORT demo_describe_enums.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA(out) = cl_demo_output=>new( ).

    TYPES:
      BEGIN OF ENUM size,
        s, m, l, xl, xxl,
      END OF ENUM size.

    DATA(size) = VALUE size( ).

    out->begin_section( 'DESCRIBE FIELD' ).

    DESCRIBE FIELD size TYPE DATA(type)
                        LENGTH DATA(length) IN BYTE MODE
                        OUTPUT-LENGTH DATA(output_length).

    out->write_data( type
      )->write_data( length
      )->write_data( output_length ).

    out->next_section( 'CL_ABAP_ENUMDESCR' ).

    DATA(enum_descr) = CAST cl_abap_enumdescr(
      cl_abap_typedescr=>describe_by_data( size ) ).

    out->write_data( enum_descr->kind
      )->write_data( enum_descr->type_kind
      )->write_data( enum_descr->base_type_kind
      )->write_data( enum_descr->members ).

    out->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The statement DESCRIBE FIELD and the type description class CL_ABAP_ENUMDESCR are applied to an enumerated variable size of the enumerated type with the same name.

6.3 Example Enumerated Objects, Deserialization

This example demonstrates how a dynamically created XML file is deserialized to an enumerated object.

Source Code

REPORT.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA input TYPE c LENGTH 3 VALUE 'XL'.
    cl_demo_input=>request( CHANGING field = input ).
    input = to_upper( input ).

    TYPES:
      BEGIN OF ENUM size,
        s, m, l, xl, xxl,
      END OF ENUM size.

    DATA(xml) =
      cl_abap_codepage=>convert_to(
        `<asx:abap version="1.0"` &&
        ` xmlns:asx="http://www.sap.com/abapxml">` &&
        `  <asx:values>` &&
        `  <ENUM>` && input && `</ENUM>` &&
        `  </asx:values>` &&
        `</asx:abap>` ) ##no_text.

    DATA size TYPE size.
    TRY.
        CALL TRANSFORMATION id
                            SOURCE XML xml
                            RESULT enum = size.
      CATCH cx_transformation_error INTO DATA(exc).
        cl_demo_output=>display( exc->previous->get_text( ) ).
        RETURN.
    ENDTRY.

    cl_demo_output=>display( |Name:  { size
                           }\nValue: { CONV i( size ) }| ).

    FIELD-SYMBOLS <fs> TYPE size.
    ASSIGN (input) TO <fs>.
    IF sy-subrc <> 0.
      cl_demo_output=>display( `Wrong name` ).
      RETURN.
    ENDIF.
    ASSERT size = <fs>.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The composition of a a byte string means that it can represent an XML file in asXML format for an enumerated object of the enumerated type size and is deserialized as such. Any invalid entries raise an exception.

Next, the dynamic assignment of a name to a field symbol is displayed. This is successful but the prerequisite here is that the enumerated type size is known in the current context. However, the XML file can be serialized in an enumerated object without static knowledge of the enumerated type.

7. Internal Tables


7.1 Internal Tables - Overview

Internal tables provide a means of taking variable data from a fixed structure and storing it in working memory in ABAP. An internal table is a data object that contain any rows with any data type whose total is not defined statically. The data type of an internal table is a table type. As well as the row type, the table type also defines the table category and the table key.

Internal tables offer dynamic arrays and removes the task of program-driven dynamic memory management from the programmer (see Memory Consumption of Deep Data Objects). A particularly important use for internal tables is for storing and formatting data from a database table within a program. In conjunction with structures, they are also the best way of defining complicated data structures in an ABAP program.

- Internal Tables - Data Type
- Internal Tables - Table Category
- Internal Tables - Table Key
- Internal Tables - Access
- Row-Based Administration Costs of Internal Tables

7.1.1 Data Type of an Internal Table

A table type defined in ABAP Dictionary or using TYPES or DATA is specified fully using:

- Row Type

The row type of an internal table can be any data type. If the row type is structured, the individual components of a row are also known as the columns of the internal table.

- Table Category

The table category defines how an internal table is administered and how its individual rows can be accessed. There are three table categories:
     - Standard tables are administered internally using a primary table index. If required, this primary table index can be implemented using a logical index. The table can be accessed using either a table index or a table key. The primary key of a standard table is always non-unique. The response time for accessing the table using the primary key is proportional to the number of entries in the table. Secondary table keys can be defined to make key access to standard tables more efficient.
     - Sorted tables are also administered internally using a primary table index. They are always sorted according to the primary table key. Sorting is in ascending order by size according to the comparison rules for the data types of the key fields and returns the same result as the SORT statement without additions. The table can be accessed using either a table index or a table key. The primary key of a sorted table can be either unique or non-unique. The response time for accessing the table using the primary key is logarithmically proportional to the number of table entries, since a binary search is used.
     - Hashed tables are administered internally using a hash algorithm. Hashed tables can be accessed using a table key or a secondary table index. The primary key of hashed tables is always unique. The response time for primary key access is constant and independent of the number of entries in the table.

- Table Key

A table key is used to identify rows in a table (see below). There are two possible key types for internal tables, primary keys and optional secondary keys. Every internal table has a primary key, which is either the standard key or a self-defined key. Depending on the table category, the primary key is unique or non-unique. A secondary key is either a sorted key, which can be unique or non-unique, or a unique hash key. If keys are unique, a row with a specific content in the key fields can exist only once in the internal table. A table key can consist of components of the row type or of the entire row ( pseudo component table_line), if they are not internal tables or do not contain internal tables. When a table key is defined, the order of the key fields is significant.

Unlike all other self-defined data types, a table type defined in ABAP Dictionary or using TYPES does not have to be specified in full. Either only the keys or the row type and the keys can be omitted from the definition. This makes the type generic and it can be used only for typings of field symbols and formal parameters. The predefined generic ABAP types ANY TABLE and INDEX TABLE can be used for these purposes. The first type includes all table categories, the second type includes standard tables and sorted tables, known as index tables.

Like strings, internal tables are dynamic data objects. Their row type, table category, and table key are always specified in full, but the number of rows is variable and restricted only by the capacity of the system installation.

Note

Table keys are evaluated when typings are checked. Only actual parameters that have the same type can be assigned to a formal parameter or a field symbol whose table type has a non-generic primary key or one or more secondary keys.

Example

Definition of a table type spfli_tab with the structured row type SPFLI from the ABAP Dictionary, the table category hash table, a unique primary key, and exactly one non-unique sorted secondary key cities.

TYPES spfli_tab
  TYPE HASHED TABLE OF spfli
       WITH UNIQUE KEY carrid connid
       WITH NON-UNIQUE SORTED KEY cities COMPONENTS cityfrom cityto
       WITHOUT FURTHER SECONDARY KEYS.

7.1.2 Selection of Table Category

The category of table used in each individual case depends on the type of individual row access that is used most often on the table. These rules are made suitably relative to tables with secondary keys.

◉ Standard Tables

This category of table is appropriate when the individual entries can be addressed using the index. Access by the index is the fastest possible access to table entries. You should fill standard tables by appending lines using APPEND and implement the other accesses using an index specification (INDEX addition of the respective statements). Since the cost of accesses to standard tables using the primary key increases linearly with the number of table entries, this type of access should only be used on standard tables if the filling of the table can be separated from the rest of the processing. If a standard table is sorted after filling, the cost of a key access with a binary search (BINARY SEARCH) has a logarithmic relationship to the number of table entries.

◉ Sorted Tables

This category of table is appropriate if the table must be sorted from the time of creation. The filling of the table takes place by insertion using the INSERT statement and in accordance with the sort order defined by the primary table key. The cost of key accesses is related logarithmically to the number of table entries because a binary search is automatically carried out. Sorted tables are also particularly suited for partially sequential access in a LOOP loop, if the first part of the table key is specified in the WHERE condition.

◉ Hashed Tables

This category of table is suitable when key accesses are the central operation to be carried out on the table. Hashed tables cannot be accessed using a primary table index. However, the cost per key access is always constant and independent of the number of table entries. As with database tables, the key of hashed tables is always unique. Therefore hashed tables are suitable for creating internal tables that are similar to databases and can be used in a corresponding fashion.

Example

A table scarr_tab_down intended for index access should be sorted by key field in descending order. This can only be a standard table. If you want a table in ascending order, you can and should use a sorted table, in this case scarr_tab_up. If only key access is required, then a hash table like scarr_tab_key can be used.

DATA scarr_tab_down
  TYPE STANDARD TABLE OF scarr
       WITH NON-UNIQUE KEY carrid.

DATA scarr_tab_up
  TYPE SORTED TABLE OF scarr
       WITH UNIQUE KEY carrid.

DATA scarr_tab_key
  TYPE HASHED TABLE OF scarr
       WITH UNIQUE KEY carrid.

SELECT *
       FROM scarr
       ORDER BY carrid DESCENDING
       INTO TABLE @scarr_tab_down.

scarr_tab_up = scarr_tab_down.

scarr_tab_key = scarr_tab_up.

cl_demo_output=>new(
)->write( scarr_tab_down[ 1 ]
)->write( scarr_tab_up[ 1 ]
)->write( scarr_tab_key[ carrid = 'UA' ]
)->display( ).

7.1.3 Table Keys

Internal tables always have a primary key and can have up to 15 optional secondary table keys. Standard tables are automatically assigned a standard key if an explicit primary key is not defined.

◈ primary table keys

Each internal table has a primary table key that enables access to individual rows in the table by means of a key specification.

- The components of the primary table key are declared using the UNIQUE|NON-UNIQUE KEY additions of the statements TYPES, DATA, and so on.
- The standard key, which can be declared both explicitly and implicitly, has a special part to play here.
- The primary table key of a standard table can also be empty, meaning that it does not contain any key fields.
- In the case of key accesses for internal tables, the primary key is always used implicitly processing statements as long as no secondary key is specified. In table expressions, the primary key must also always be specified explicitly.
- The primary key has the predefined name primary_key, with which it can also be addressed explicitly in processing statements. In table expressions, primary_key or an alias name must be specified, if the primary key is to be used explicitly.
- Access to an internal table using the primary table key is determined by the table category and not the table key. For sorted tables and hashed tables, key access is always optimized using the primary table key. Primary key access to standard tables, however, uses a linear search.
- The key fields of the primary table key of sorted tables and hashed tables are always read-only.
- In the case of sorted tables and hashed tables, separate key administration exists for the primary table key, which enables optimized access but also affects the memory requirement of the internal table. There is no separate key administration for the primary table key of standard tables.

Notes

- Since sorted tables and hashed tables have real key administration for the primary key, unlike standard tables, these tables are also grouped under the term key tables.
- To achieve optimized key access to standard tables, secondary keys can be used.

Example

Declaration of a hash table with a unique primary key. The table is filled with data from a database table, read using a table expression with values specified for the primary key.

DATA carrid TYPE spfli-carrid VALUE 'LH'.
cl_demo_input=>add_field( CHANGING field = carrid ).
DATA connid TYPE spfli-connid VALUE '0400'.
cl_demo_input=>request( CHANGING field = connid ).

DATA spfli_tab
  TYPE HASHED TABLE OF spfli
       WITH UNIQUE KEY primary_key COMPONENTS carrid connid.

SELECT *
       FROM spfli
       INTO TABLE @spfli_tab.

cl_demo_output=>display(
  VALUE #( spfli_tab[ KEY primary_key
                      carrid = carrid
                      connid = connid ] OPTIONAL ) ).

1. Standard Key

The standard key is a special primary table key in an internal table.

Key Fields of the Standard Key

The key fields of the standard key are defined as follows:

◉ In tables with a structured row type, the standard key is formed from all components with character-like and byte-like data types, with any substructures being expanded by elementary components. If the row type does not contain components like these, the standard key is empty for standard tables, meaning it does not contain any key fields.

◉ The standard key for tables with non-structured row types is the entire table row, if the row type itself is not table-like. If the row type is table-like, the standard key is empty for standard tables.

Empty standard keys are not possible for sorted tables and hashed tables, and an error occurs if an attempt is made to create a key like this.

Notes

◉ In tables with non-structured row types, the standard key can also have a numeric type or reference type; this does not apply to the key fields if the row types are structured.

◉ The static boxes of a structured row type are handled like regular components, with respect to the standard key.

Declaration of the Standard Key

The standard key can be declared as follows:

◉ Explicitly using the additions UNIQUE|NON-UNIQUE KEY of the statements TYPES, DATA and so on, with the addition DEFAULT KEY being specified rather than a list of components.

◉ Implicitly if no explicit primary key specification is made in the declaration of a standard table with the statement DATA.

◉ Implicitly, if a standard table type with a generic primary table key is specified behind TYPE in the statement DATA.

Notes on Use

Using standard keys is critical for various reasons:

◉ It is not usually enough to identify the key fields just by their data type and not their semantic attributes. This often leads to unexpected behavior in sorts and other access types.

◉ The potential for empty standard keys in standard tables often causes unexpected behavior too. For example, using keys like this for sorting is useless, whereas a corresponding read finds the first row.

◉ The standard key often contains too many key fields, leading to performance problems in key accesses.

◉ If using the standard key and a structured row type, all character-like and byte-like fields of sorted tables and hashed tables are read-only, which can cause unexpected runtime errors.

For this reason, declare the primary key by listing the components explicitly, if possible. In particular, the primary key must not be set as the standard key by mistake, the key must not be forgotten in declarations of standard tables using DATA, and that the generic table type must not be used unknowingly.

Example

Internal table with standard key. In the structure SCARR, all five components meet the requirements for the standard key and are used as key fields. In a key access using a table expression, the syntax check requires that each key field of the primary table key is specified.

DATA scarr_tab
  TYPE HASHED TABLE OF scarr
       WITH UNIQUE DEFAULT KEY.

...

DATA(scarr_line) =
  VALUE #( scarr_tab[ KEY primary_key
                      mandt    = sy-mandt
                      carrid = '...'
                      carrname = '...'
                      currcode = '...'
                      url      = '...' ] OPTIONAL ).

2. Empty Table Key

The primary table key of a standard table can be empty. An empty table key does not contain any key fields.

Declaration

An empty primary table key can be created as follows:

◉ Explicitly with the addition EMPTY KEY of the statements TYPES, DATA, and so on.
◉ Explicitly using an inline declaration INTO TABLE @DATA(itab) in the statement SELECT
◉ Implicitly when using the standard key if a structured row type does not contain any non-numeric elementary components or if an unstructured row type is table-like.

Notes on Use

Uncritical use

An empty primary table key can be used to handle a table like an array. This means that filling the table and other access do not rely on an order determined by key values. In this case, an empty internal table key can be used in all statements that determine (implicitly or explicitly) the order in which the internal table is accessed.

Notes

◉ Developers should always be aware of the fact that they are handling an empty primary table key. This is really achieved only when the empty key is declared explicitly. This implicit declaration using the standard key (in which the row type determines whether the primary table key is empty or not) is not usually suitable.
◉ Even an empty primary key in a standard table has the order specified by the primary index, which can be exploited in related index accesses or loops.

Example

A particularly prominent example is the statement LOOP AT itab, which when used implicitly or explicitly (using USING primary_key) defines the processing order with respect to the primary table index, but is otherwise ignored.

Critical use

In the following statements, which work with the primary table key without specifying the key fields explicitly, specifying an empty primary table key is critical and generally produces unexpected behavior. An empty table key that is known statically produces a syntax check warning.

◉ Primary table key specified by a work area:
     -If FROM wa is used to specify an empty table key for the statement READ TABLE, the first row of the internal table is read.
     -If FROM wa is used to specify an empty table key for the statement MODIFY, the first row of the internal table is modified.
     -If FROM wa is used to specify an empty table key for the statement DELETE, the first row of the internal table is deleted.

◉ If the statement SORT itab is executed without the addition BY and the primary table key is empty, the statement is ignored and the table is not sorted.
◉ If the statement DELETE ADJACENT DUPLICATES is executed and the primary table key is empty, no rows are deleted.
◉ If the primary table key is empty and the statement COLLECT is executed, the first row of the internal table is compressed. In this case, all components of the row type must be numeric.

Note

The statements described above can be particularly unpredictable when using the standard key (which itself can be declared implicitly) to declare an empty internal table key.

Example

Typical use of a table with an empty table key, in which the order of the rows should not be changed by sorting. A SORT source statement would have no effect.

DATA source
  TYPE STANDARD TABLE OF string
       WITH EMPTY KEY.

READ REPORT 'DEMO_TAB_EXP_LINE' INTO source.

cl_demo_output=>display( source ).

◈ Secondary Table Key

Hash keys and sorted keys can be declared as secondary table keys for each internal table. For each sorted key, an additional secondary table index is created.

Access to an internal table using a secondary key is always optimized. This allows additional optimized keys to be introduced for sorted and hashed tables as well as optimized key accesses for standard tables.

◉ Declaration of secondary table keys

For data types declared in ABAP programs, a secondary table key is declared using additions UNIQUE|NON-UNIQUE KEY key_name COMPONENTS of statements TYPES, DATA and so on. For table types created in ABAP Dictionary, the tool provides the corresponding functions.

◉ Access using secondary keys

In key accesses to internal tables, the addition WITH [TABLE] KEY key_name can be used in processing statements to specify which secondary table key to use. In index accesses, USING KEY keyname can be used to specify the table index of which secondary key to use. In table expressions, this is specified using the addition KEY. Secondary keys are not selected automatically. If no secondary key is specified in a processing statement, the primary key or primary table index is always used. If no explicit key is specified for a table expression, a free search key is used to perform reads.

Statements where secondary keys can be specified are:

◉ READ TABLE itab
The rows to be read can be specified using a secondary key.

◉ LOOP AT itab
The processing order and conditions can be controlled using a secondary table key.

◉ INSERT itab
Only a secondary key for the source table can be specified here, from which multiple rows are copied. The position they are inserted at is determined solely using the primary key and the primary index.

◉ APPEND
Only a secondary key for the source table can be specified here, onto which multiple rows are appended.

◉ MODIFY itab
The rows to be modified can be specified using a secondary key.

◉ DELETE itab
The rows to be deleted can be specified using a secondary key.

◉ TYPES ... ASSOCIATION, \_assoc[ ... ]
The secondary key used in the evaluation of a mesh path can be specified using USING KEY.

If the system field sy-tabix is set by this type of access, and a sorted secondary key is used, the row number refers to the associated secondary table index. In statements that these additions has not been introduced for, like SORT, COLLECT, or PROVIDE, secondary keys are not explicitly supported.

The key fields of a secondary table key are only write-protected if the secondary table key is in use in a LOOP loop or in a MODIFY statement. Otherwise, the secondary key fields are not write-protected.

Notes

- Optimized access times for the individual rows using secondary keys are bought in exchange for the fact that the ABAP runtime environment then needs to administer the additional keys. For hash keys, this means additional hash administration. For each sorted key, it means an additional secondary table index.
- When working with internal tables for which a secondary key is declared, it must be ensured that the required key or table index is used in the processing statements.

Example

Declaration of a hash table with a unique primary key and a non-unique sorted secondary key cities. The table is filled with data from a database table, read using a table expression with values specified for the secondary key. The first row found is read.

DATA cityfrom TYPE spfli-cityfrom VALUE 'FRANKFURT'.
cl_demo_input=>add_field( CHANGING field = cityfrom ).
DATA cityto TYPE spfli-cityto VALUE 'NEW YORK'.
cl_demo_input=>request( CHANGING field = cityto ).

DATA spfli_tab
  TYPE HASHED TABLE OF spfli
       WITH UNIQUE KEY primary_key        COMPONENTS carrid connid
       WITH NON-UNIQUE SORTED KEY cities  COMPONENTS cityfrom cityto.

SELECT *
       FROM spfli
       INTO TABLE @spfli_tab.

cl_demo_output=>display(
  VALUE #( spfli_tab[ KEY cities
                      cityfrom = cityfrom
                      cityto   = cityto ] OPTIONAL ) ).

- Updating Secondary Keys

For all statements that change the content and structure of an internal table, the internal administration of the secondary keys is updated automatically as follows:

◉ For all operations that insert rows into tables or delete rows from tables, the secondary key administration for unique keys is updated immediately. This means that a unique secondary key is up-to-date immediately after the operation (direct update). Non-unique secondary table keys are not updated immediately. Instead, they are updated when the secondary key is next used explicitly using USING KEY or WITH KEY ... COMPONENTS (lazy update). If updating the administration causes a secondary key uniqueness violation, a handled exception or runtime error occurs.

Inserting operations include table statements such as INSERT and APPEND and block operations for which the entire body of the table is filled at once, like for statements between internal tables, passing parameters to procedures, filling internal tables with SELECT, importing with IMPORT, and so on. The operation for deleting table rows is the DELETE table statement.

◉ For operations that change the components of secondary table keys in existing rows, the secondary key administration is either updated directly or when a specified synchronization point is reached.
     - If MODIFY is used to modify individual rows, any unique secondary keys are updated directly (direct update). If the modification produces duplicate entries, a non-handleable exception is raised.
     - If field symbols or data references are used to modify individual rows, and these symbols or references point to table rows, any unique secondary keys are updated the next time the internal table is accessed (delayed update). The uniqueness check also waits until the synchronization point is reached. An internal table can therefore be in an inconsistent state with respect to the secondary key following a modification to existing rows using field symbols or data references. The corresponding exception is only raised when the table is next used.

In both cases, a non-unique key not updated until the secondary table key is next used explicitly (lazy update).

The methods FLUSH_ITAB_KEY and FLUSH_ITAB_KEYS of the class CL_ABAP_ITAB_UTILITIES can be used to update individual secondary keys or all secondary keys of an internal table explicitly in exceptional circumstances. These methods can be used for analysis and test purposes. It might also make sense to use them after making changes if the next access does not take place immediately afterwards to handle possible exceptions there and then.

Executable Example

The example of Deletion Using Table Keys demonstrates the runtimes that are required to create the secondary key and when they occur.

- Using Secondary Keys

Notes on Using Secondary Table Keys

◉ The standard scenario for sensibly using secondary table keys relates to a very large internal table that is created in the memory and whose content is changed very infrequently. Costs for administrating the secondary keys are then only incurred when setting up the internal table.

◉ As a matter of principle, secondary hash keys should not have too many components, since otherwise the system has to deal with a high system load for the additional hash management. Sorted keys are preferable for secondary keys with a large number of components.

◉ For an index table with secondary keys, the primary index of the table is updated immediately when rows are inserted or deleted using a secondary key. Note deleting a row from a standard table using a secondary key cannot be optimized because the index entry to be deleted must be searched linearly.

◉ Secondary keys might be useful for small internal tables in some circumstances because they ensure unique table entries in relation to particular components. Primary keys do not allow this, especially in the case of standard tables.

◉ In the case of read-only reads (where ensuring unique table entries is not relevant), non-unique secondary keys are usually sufficient. Performing reads here is almost as quick as for unique keys, while the update following a table modification is quicker and is performed using a lazy update.

◉ Secondary table keys should not be used in the following situations:
     ◉ For small internal tables (less than 50 rows), the performance benefits in the case of reads are far outweighed by the increased memory and administration costs.
     ◉ In tables where a large number of changes are made, the load incurred by updating the secondary keys outweighs the performance benefits in the case of reads. In delayed updates and lazy updates in particular, the update load can actually be incurred by the read for which the optimization was actually required.
     ◉ Unless the uniqueness of table entries is of absolute importance, it is better to avoid using secondary hash keys.

Example

The program DEMO_SECONDARY_KEYS demonstrates how a secondary table key is specified and the resulting performance gain.

- Restrictions for Secondary Keys

Internal tables with secondary keys can be used in all processing statements for internal tables and other statements that work with internal tables. However, there are also operand positions for internal tables for which it does not make sense to use secondary keys. Secondary keys are therefore not supported for those operand positions.

The use of tables with secondary keys causes syntax or runtime errors for the following operand positions:
  • An internal table as an actual parameter for a TABLES parameter for RFC can have any table type but no secondary key.
  • Only standard tables without secondary keys are possible for the following:
    • rspar in SUBMIT WITH rspar and rtab in SUBMIT WITH sel IN rtab
    • itab in FIND IN TABLE itab and REPLACE IN TABLE itab
    • result_tab in SPLIT INTO TABLE result_tab
    • column_syntax in the SELECT list, in GROUP BY, and in ORDER BY
    • cond_syntax in WHERE
    • source_syntax in SELECT
    • target_syntax in INSERT, UPDATE, MODIFY, and DELETE
    • expr_syntax in UPDATE
    • IN, OUT, INOUT parameters in EXEC SQL - EXECUTE
    • itab in EXPORT TO INTERNAL TABLE itab and IMPORT FROM INTERNAL TABLE itab
    • itab in GENERATE SUBROUTINE POOL itab, READ REPORT INTO itab, INSERT REPORT FROM itab, and SYNTAX-CHECK FOR itab.
    • itab in EDITOR-CALL FOR itab
    • itab in WRITE TO itab
◈ Duplicate Unique Keys

When rows are inserted into internal tables with a unique primary key or unique secondary key, duplicates can occur with respect to one or more of these keys. Depending on whether the insert takes place as a single record operation or a mass operation, the ABAP runtime environment responds as follows to an attempt to insert an entry with duplicate key values:

◉ First, it checks whether duplicate key values would occur with respect to the primary key. The system behavior is as follows depending on the operation:

     ◈ When single rows are inserted using the variant

INSERT wa INTO TABLE itab

duplicate with respect to the primary key are ignored and sy-subrc is set to 4. This is often used to filter out duplicates when the table is constructed.

     ◈ In all other cases, such as

INSERT ... INTO itab INDEX idx
INSERT LINES OF (mass operation)
APPEND
COLLECT
=, IMPORT (mass operations)

the runtime error ITAB_DUPLICATE_KEY occurs.

◈ A check is then made to see whether duplicate key values would occur with respect to any existing unique secondary keys. If this is the case,
 ◈ an exception of the class CX_SY_ITAB_DUPLICATE_KEY is raised for the statements INSERT and APPEND, if the operation is a single record operation.
◈ For all other insert and assignment operations, particularly for all mass operations, the runtime error ITAB_DUPLICATE_KEY occurs.

Example

The first INSERT statement does not insert a line and sets sy-subrc to the value 4. The second INSERT statement produces a runtime error.

DATA itab TYPE SORTED TABLE OF i WITH UNIQUE KEY table_line.

itab = VALUE #( ( 1 ) ( 2 ) ( 3 ) ).

INSERT 2 INTO TABLE itab.

INSERT 2 INTO itab INDEX 2.

◈ Duplicate Non-Unique Keys

Non-unique table keys can produce duplicate rows with respect to these keys. This section describes the order of these duplicates when data is inserted into table with non-unique sorted table keys. This order is ignored with respect to non-unique primary keys in standard tables.

Single Record Operations

When individual rows are inserted where the insert position is determined using a table key, that is, in the case of

◉ the operations INSERT ... INTO TABLE ...
◉ lazy updates of sorted secondary keys

the order of the duplicates with respect to the table key of the target table is determined in accordance with the insert order of the individual rows. The duplicate row last inserted into the table is sorted before all other duplicates.

Block Operations

For block operations such as an assignment of an internal table to another table or when inserting multiple rows using INSERT LINES OF, the order of duplicates with respect to a sorted key of the target table in the block is taken from the source table. If there are already one or more duplicates in the target table, the source block duplicates are inserted in their original order in front of the first duplicate in the target table.

Special Features

Some operations have the characteristics of block operations but are executed internally as sequences of single record operations. In this case, the original order of duplicates with respect to a sorted key in the target table is not retained. This is the case for the following operations:

◉ Filling an internal table using IMPORT from a table previously created with EXPORT and all operations that are based internally on such an import, such as posting operations.

◉ Passing and applying internal tables for Remote Function Calls.

◉ The deserialization of an internal table from a table previously serialized to XML using CALL TRANSFORMATION.

Example

Example

The result of the following insertion is

a a
b z
b y
b x
b b
TYPES:
  BEGIN OF line,
    col1 TYPE string,
    col2 TYPE string,
  END OF line,
  itab TYPE SORTED TABLE OF line
            WITH NON-UNIQUE KEY col1.

DATA(itab) = VALUE itab(
( col1 = 'b' col2 = 'b' )
( col1 = 'a' col2 = 'a' ) ).

DATA(jtab) = VALUE itab(
( col1 = 'b' col2 = 'x' )
( col1 = 'b' col2 = 'y' )
( col1 = 'b' col2 = 'z' ) ).

INSERT LINES OF jtab INTO TABLE itab.

cl_demo_output=>display( itab ).

7.1.4 Access to Internal Tables

When internal tables are read, either the entire table or table body can be accessed, or individual rows.

◉ The table body is read using special statements such as SORT, but can also be accessed using general statements where internal tables can be specified at operand positions. Examples are assignments, parameter passing, target or source areas in Open SQL and many other statements that return or expect tabular data.
◉ Individual rows are accessed using
     ◉ special statements such as READ TABLE, LOOP AT, MODIFY.
     ◉ table expressions itab[ ... ].
     ◉ Mesh path expressions mesh_path

When individual rows are read, either a work area is used into which the row content is read or from which it is modified, or a row is associated with a field symbol or a data reference variable and these are used to access the row directly.

The table category and the table keys are significant when internal tables are edited:

◉ The primary table index (which always exists) can be used to access index tables (standard tables and sorted tables).
◉ Primary table keys can be used for optimized access to sorted tables and hashed tables.
◉ A secondary table index can be used to access any tables with a sorted secondary table index.
◉ The secondary table key can be used for optimized access to any tables with a secondary sorted key or hash key. Notes

Notes

◉ Internal tables must be specified at operand positions for internal tables, both when the statement is executed and if known statically. Generic formal parameters and field symbols can only be specified if they are typed with at least the generic type any table. Only index tables can be specified at operand positions that include index access, and generic formal parameters and field symbols must be typed with at least the generic type index table.

◉ If the row type of internal tables contains object reference variables as the components comp, the attributes attr of the object to which the reference points can be used as key values for reading, sorting, and modifying table rows. This is always possible for statements that address individual components of the table.

◉ The content of the primary table key cannot be changed for any writes to individual rows of sorted tables and hashed tables. If writes are performed in writing positions across the entire table row in these tables (for example, as a target field of assignments or as actual parameters for output parameters), an exception is always raised. It is not possible to access entire table rows using field symbols, data references or table expressions.

Example

In the following example, data is written to an internal table with Open SQL, sorting takes place, and reads are demonstrated with the statement READ TABLE and a table expression.

DATA scarr_tab
  TYPE STANDARD TABLE OF scarr
       WITH NON-UNIQUE KEY carrid.

SELECT *
       FROM scarr
       INTO TABLE @scarr_tab.

SORT scarr_tab BY carrid ASCENDING.

READ TABLE scarr_tab WITH TABLE KEY carrid = 'LH'
                     TRANSPORTING NO FIELDS.
IF sy-subrc = 0.
  DATA(idx) = sy-tabix.
  TRY.
      cl_demo_output=>display( scarr_tab[ idx + 1 ]-carrid ).
    CATCH cx_sy_itab_line_not_found.
      ...
  ENDTRY.
ENDIF.

7.1.5 Row-Based Administration Costs of Internal Tables

In addition to the basic memory requirement described in Memory requirement for deep data objects required to administrate internal tables in table headers, additional memory is required to administrate each individual row, so permitting optimized access to the individual rows. This internal administration of individual rows has two variants:

◉ A table index administrates the logical order of table rows. The additional memory requirement is 6 bytes per table row on average. Exception: If the logical order matches the physical order in the table body, no additional memory is required for the index.

◉ Hash administration allows table rows to be accessed by hashing the corresponding key components. The additional memory requirement is 18 bytes per table row on average as long as the table is not accessed using the DELETE or SORT statements. Following access of this type, an average of 30 bytes is required per table row.

How a table row is administrated depends on the table category and any existing secondary table keys. The table category determines the basic administration of the rows of an internal table (table index for index tables, hash administration for hashed tables). Each additional secondary table key introduces additional row administration (table index for sorted keys, hash administration for hash keys).

Indexes are therefore created in the following cases:

◉ As the primary table index of a standard table for managing the order arising from index operations. However, the order in the index has no relation to the content of the table rows. The index is only for the optimization of index accesses. Content searches cannot be optimized and always produce a linear search of all table rows.

◉ As the primary table index of a sorted table or as the secondary table index of a sorted secondary table of any other table for managing the order of table rows in accordance with the sorted primary or secondary table key. The order of table rows is determined by the view of the table rows defined by the sorted table keys. This defines an order for the rows of the table that can be used for optimized access by the binary search.

Hash administrations are created in the following cases:

◉ For the primary table key of a hashed table or for a secondary hash key of any other table. They do not permit index access. They can only be used for optimized access with a fully specified table key.

If secondary table keys are used, additional memory costs may be incurred.

◉ An internal table with at least one ambiguous sorted secondary key needs an additional 8 bytes per table row for the basic costs of administrating possible duplicates. These additional basic costs are incurred only once and not for each table key.

◉ Additional memory costs are incurred if a secondary key needs to be updated following a change to the content of the internal table. Again, this costs a few bytes per table row but varies greatly depending on the number of changes made.

Examples

◉ A standard table with a secondary hash key that is exclusively filled with APPEND requires:
◉ No additional memory for the primary index since the logical and physical orders are the same
◉ 18 bytes per row for the hash administration of the secondary key

Following an access using DELETE or SORT, the memory requirement per row jumps to 6 bytes for the primary index and 30 bytes for the hash administration.

◉ A sorted table with a secondary hash key and two non-unique sorted secondary keys requires:
◉ 6 bytes per row for the primary index
◉ 18 or 30 bytes per row for the hash administration of the secondary hash key
◉ 6 bytes per row and key for the sorted secondary key from the moment from which it is used
◉ 8 bytes per row because there is at least one non-unique sorted secondary key

7.2 Processing Statements for Internal Tables

7.2.1 READ TABLE itab

Syntax

READ TABLE itab { table_key
                | free_key
                | index } result.

Effect

This statement reads a row from the internal table itab. itab is a functional operand position.

The row must be specified by naming values for either table_key for a table key, a free condition free_key, or an index index. The latter is possible only for index tables and when using a sorted secondary key. The output response result determines how and to where the row contents are read.

If the row to be read is not specified uniquely, the first suitable row is read. In the case of index tables, this row has the lowest row number of all suitable rows in the table index used.

If the internal table is specified as the return value or result of a functional method, a constructor expression, or a table expression, the value is only available when the statement is executed. Afterwards, it is no longer possible to access the internal table.

System Fields

The statement READ TABLE sets the values for the system fields sy-subrc and sy-tabix.

sy-subrc Meaning 
0 Row is found. sy-tabix is set to the row number of the entry in the primary or secondary table index used. If a hash key is used, it is set to 0. 
2 Like sy-subrc equals 0. Distinguishes cases that use the addition COMPARING in result.
4 The row was not found. If the entry was determined using a binary search, sy-tabix is set to the table index of the entry before which it would be inserted using INSERT ... INDEX ..., to preserve the sort order. This is the case if, when sorted keys are used, the addition table_key or free_key was specified alongside the full table key or if the addition BINARY SEARCH was specified explicitly. Otherwise, sy-tabix is undefined (-1). 
The row was not found. The entry was found using a binary search and the end of the table was reached. sy-tabix is set to the number of rows + 1. 


The system fields sy-tfill and sy-tleng are also filled.

Notes

◉ There is no implicit selection of a suitable key or index. The used table key or table index is always specified uniquely. The syntax check issues a warning if there is a suitable secondary table key but this table key is not used. This warning should be removed through using the key. However, in exceptional cases, it can be bypassed using a pragma.

◉ As an alternative to the statement READ TABLE, table expressions can be used to allow reads to be performed on individual table rows at operand positions.

◉ Internal tables can also be specified as a data source of a query in Open SQL.

◉ If multiple rows of an internal table are to be read, the statement LOOP generally displays better performance than using the statement READ TABLE in a loop.

Example

Reads individual table rows from a standard table in a WHILE loop. The table rows are read in reverse order with respect to the sorted secondary key sort_key. A CHECK statement exits the loop if the specified condition is not met. This construct provides the missing option of performing LOOPs in reverse order. See also the corresponding executable example with a FOR expression.

DATA itab TYPE STANDARD TABLE OF i
          WITH EMPTY KEY
          WITH NON-UNIQUE SORTED KEY sort_key COMPONENTS table_line.

itab = VALUE #( ( 2 ) ( 5 ) ( 1 ) ( 3 ) ( 4 ) ).

DATA(output) = ``.
DATA(idx) = lines( itab ).
WHILE idx > 0.
  READ TABLE itab INDEX idx USING KEY sort_key
             ASSIGNING FIELD-SYMBOL(<fs>).
  idx = idx  - 1.
  CHECK <fs> > 2.
  output = output && <fs> && ` `.
ENDWHILE.

cl_demo_output=>display( output ).

Exceptions

Non-Handleable Exceptions

◉ Cause: When reading a table using READ ... WITH [TABLE] KEY, overlapping or duplicate keys were specified.
Runtime error: DYN_KEY_DUPLICATE

◉ Cause: When reading a table of the type SORTED, the specified key fields have to be an initial part of the table key when BINARY SEARCH is specified.
Runtime error: ITAB_ILLEGAL_BINARY_SEARCH

◉ Cause: No key specified.
Runtime error: ITAB_KEY_COMPONENT_MISSING

◉ Cause: Invalid key specified when accessing a key table.
Runtime error: ITAB_KEY_ILLEGAL_COMPONENT

◉ Cause: Key specified implicitly (invalid)
Runtime error: READ_ITAB_UC_KEY_ERROR

◉ Cause: Invalid incompatible work area specified.
Runtime error: OBJECTS_WA_NOT_COMPATIBLE

◉ Cause: Memory area violated when TABLES parameter accessed
Runtime error: ITAB_STRUC_ACCESS_VIOLATION

7.2.2 LOOP AT itab

Syntax Forms

1. LOOP AT itab result [cond].
    ...
  ENDLOOP.

2. LOOP AT itab result [ cond] GROUP BY group_key
                             [ASCENDING|DESCENDING [AS TEXT]]
                             [WITHOUT MEMBERS]
                             [group_result].
    ...
  ENDLOOP.

Effect

Executes a table iteration as a loop across an internal table itab. itab is a functional operand position.

The statements LOOP and ENDLOOP define the statement block of the loop. The statement LOOP reads rows from the internal table itab sequentially that meet an optional condition cond.

- If the addition GROUP BY is not specified, the statement block is executed for each read row and the row can be processed here. The way in which the read row is addressed in the statement block is specified in the output behavior result.

- The addition GROUP BY is used to group the read rows by a group key group_key and the statement block is then executed for every group. The way in which the current group is addressed in the statement block is specified in the output behavior group_result.

To exit processing of the statement block, the statements described in the leave loops section can be used.

Note

Internal tables can also be specified as a data source of a query in Open SQL.

Example

Simple LOOP over an internal table.

DATA itab TYPE TABLE OF i WITH EMPTY KEY.
itab = VALUE #( FOR i = 1 UNTIL i > 10 ( i ) ).

DATA(str) = ``.
LOOP AT itab ASSIGNING FIELD-SYMBOL(<fs>).
  str = str && CONV string( <fs> ) && ` `.
ENDLOOP.
cl_demo_output=>display( str ).

7.2.3 INSERT itab

Syntax

INSERT line_spec INTO itab_position [result].

Effect

This statement adds one or more rows line_spec to a position itab_position in an internal table. The position can be specified using the primary table key or a table index. Use result when appending a single row to set a reference to the appended row in the form of a field symbol or a data reference.

When the row is in inserted, all existing unique table keys are checked. These can be a primary table key and multiple unique secondary table keys. The system handles any duplicates of the various key according to the following hierarchy:

1. If attempting to insert a single row using a primary key would produce duplicates with respect to the unique primary key, no row is inserted and sy-subrc is set to 4.

2. If attempting to insert a single row using the key or the index would produce duplicates with respect to a unique secondary key, a handleable exception of the class CX_SY_ITAB_DUPLICATE_KEY is raised.

3. If the attempt to insert a single row (using an index) or multiple rows (as a block) would produce duplicates (in terms of a unique primary or secondary key), a runtime error occurs.

System Fields

sy-subrc Meaning 
One or more rows were inserted.
No row was inserted because either a row of the same unique key already existed when inserting single rows using the primary key or the specified index was greater than the current number of rows plus one when inserting the rows using the table index. 

The system field sy-tabix is not set.

Notes

◉ The administration of an unique secondary table key is updated immediately (direct update) and the administration of a non-unique secondary table key is updated at the next explicit use of the secondary table key (lazy update). Runtime costs for creating or updating a non-unique secondary table key are not incurred therefore until it is used for the first time (see the executable example).

◉ The value operator VALUE can also be used to construct the content of internal tables.

◉ A special variant INSERT mesh_path can be used to insert rows into the last node of a mesh path.

Example

Inserts single rows in a standard table int_tab using the table index and inserts references to these rows in a hashed table ref_tab using the table key. The output in the LOOP loops produces the numbers 10 to 1 for int_tab and the numbers 1 to 10 for ref_tab.

TYPES intref type REF TO i.

DATA: int_tab TYPE STANDARD TABLE OF i,
      ref_tab TYPE HASHED TABLE OF intref
              WITH UNIQUE KEY table_line.

DO 10 TIMES.
  INSERT sy-index
         INTO int_tab INDEX 1
         REFERENCE INTO DATA(dref).
  INSERT dref
         INTO TABLE ref_tab.
ENDDO.

cl_demo_output=>begin_section( `Integer Table` ).
LOOP AT int_tab INTO DATA(int).
  cl_demo_output=>write( |{ int }| ).
ENDLOOP.
cl_demo_output=>next_section( `Reference Table` ).
LOOP AT ref_tab INTO dref.
  cl_demo_output=>write( |{ dref->* }| ).
ENDLOOP.
cl_demo_output=>display( ).

Handleable Exceptions

CX_SY_ITAB_DUPLICATE_KEY

◉ Cause: Duplicate key values in unique secondary key
Runtime error: ITAB_DUPLICATE_KEY

Non-Handleable Exceptions

◉ Cause: When inserting a set of rows, entries with an identical key were produced (the target table is defined by UNIQUE).
Runtime error: ITAB_DUPLICATE_KEY

◉ Cause: Sort order violated when using an INSERT with index in a sorted table.
Runtime error: ITAB_ILLEGAL_SORT_ORDER

◉ Cause: Invalid index value (<= 0) when FROM, TO, or INDEX specified
Runtime error: TABLE_INVALID_INDEX

7.2.4 COLLECT

Syntax

COLLECT wa INTO itab [result].

Effect

This statement inserts the content of a work area wa either as a single row in an internal table itab or adds the values of its numeric components to the corresponding values of existing rows with the same primary table key. wa is a functional operand position.

result can be used to set a reference to the inserted or changed row in the form of a field symbol or data reference.

Prerequisite for the use of this statement is that wa is compatible with the row type of itab. All components that are not part of the primary table key must have a numeric data type.

The table is scanned for a row with the same primary key as follows:

◉ In standard tables that are filled using COLLECT only, the entry is determined by a temporary hash administrator. The workload is independent of the number of entries in the table. The hash administrator is temporary and is generally invalidated when the table is accessed to be changed. If COLLECT statements are specified after an invalidation, a linear search of all table rows is performed. The workload for this search increases in a linear fashion in relation to the number of entries.

◉ In sorted tables, the entry is determined using a binary search. The workload has a logarithmic relationship to the number of entries in the table.

◉ In hashed tables, the entry is determined using the hash administrator of the table and is always independent of the number of table entries.

If no row is found with an identical primary key, a row is inserted as described below and filled with the content of wa:

◉ In standard tables, the row is appended as the last row of the primary table index.

◉ In sorted tables, the new row is inserted in the sort order of the internal table in accordance with its key values, and the primary table index of the subsequent rows is increased by 1.

◉ In hashed tables, the new row is inserted into the internal table by the hash administrator, in accordance with its key values.

If the internal table already contains one or more rows with an identical primary key, those values of the components of work area wa that are not part of the key are added to the corresponding components of the uppermost existing row (in the case of index tables, this is the row with the lowest primary table index).

A non-handleable exception is raised if a duplicate entry in a unique secondary table key is produced when the statement COLLECT is executed.

If the primary table key of a standard table is empty, all components of the row type must be numeric and the first row of the internal table is always compressed. If the system can statically detect this, the syntax check displays a warning that can be hidden using a pragma.

System Fields

The statement COLLECT sets sy-tabix for standard tables and sorted tables to the row number of the inserted or existing row in the primary table index, and for hashed tables to the value 0.

Notes

◉ COLLECT should only be used if internal tables are to be created that are genuinely unique or compressed. In this case, COLLECT can greatly benefit performance. If uniqueness or compression are not required, or the uniqueness is guaranteed for other reasons, the statement INSERT should be used instead.

◉ The statement COLLECT is not suitable for standard tables and should no longer be used for them. COLLECT can be used for sorted tables and hashed tables without any problems since these, unlike standard tables, always have a separate, stable key administration that can be utilized by COLLECT. When used for sorted tables, these should have a unique primary key or the table should be filled with COLLECT only. For hashed tables, all prerequisites are met automatically.

◉ If a standard table is still filled using COLLECT, it should not be edited using any other statement, with the exception of MODIFY. If the latter is used with the addition TRANSPORTING, no primary key fields can be changed. This is the only way to ensure that the table entries are always unique and compressed, and that the statement COLLECT runs efficiently. The function module ABL_TABLE_HASH_STATE can be used to check whether a standard table is suitable for editing using COLLECT.

◉ The method HAS_COLLECT_HASH_ADMIN of the class CL_ABAP_ITAB_UTILITIES can be used in standard tables to check whether temporary hash management insists in the statement COLLECT.

◉ With the exception, an obsolete short form is possible where wa INTO can be omitted if the internal table has a header line itab with the same name. The statement then uses the header line as the work area implicitly.

Example

Compressed insertion of data from the database table sflight into the internal table seats_tab. The rows in which the primary key components carrid and connid are identical are compressed by adding the number of occupied seats to the numeric component seatsocc.

DATA: BEGIN OF seats,
        carrid   TYPE sflight-carrid,
        connid   TYPE sflight-connid,
        seatsocc TYPE sflight-seatsocc,
      END OF seats.

DATA seats_tab LIKE HASHED TABLE OF seats
               WITH UNIQUE KEY carrid connid.

SELECT carrid, connid, seatsocc
       FROM sflight
       INTO @seats.
  COLLECT seats INTO seats_tab.
ENDSELECT.

Handleable Exceptions

CX_SY_ARITHMETIC_OVERFLOW

◉ Cause: Overflow in integer field when creating totals
Runtime error: COLLECT_OVERFLOW

◉ Cause: Overflow in type p field when creating totals
Runtime error: COLLECT_OVERFLOW_TYPE_P

Non-Handleable Exceptions

◉ Cause: COLLECT used for non-numeric fields
Runtime error: TABLE_COLLECT_CHAR_IN_FUNCTION

◉ Cause: Memory area violated when TABLES parameter accessed
Runtime error: ITAB_STRUC_ACCESS_VIOLATION

7.2.5 APPEND

Syntax

APPEND line_spec TO itab [SORTED BY comp] [ result].

Addition:

... SORTED BY comp

Effect

This statement appends one or more rows line_spec to an internal index table itab. It is appended so that a new last row is created with respect to the primary table index.

If itab is a standard table, SORTED BY can be used to sort the table in a specified way. Use result when appending a single row to set a reference to the appended row in the form of a field symbol or a data reference.

For the individual table types, appending is done as follows:

◉ To standard tables, rows are appended directly and without checking the content of the internal table.

◉ Rows are appended to sorted tables only if they match the sort order and do not create duplicate entries (if the primary table key is unique).

◉ No rows can be appended to hashed tables.

Exceptions are raised in the following cases:

◉ If a row to be appended would produce a duplicate entry in a unique primary table key, a non-handleable exception is raised.

◉ If a row to be appended would produce a duplicate entry in a unique secondary table key, a handleable exception of the class CX_SY_ITAB_DUPLICATE_KEY is raised.

◉ If a block of rows to be appended would produce a duplicate entry in a unique secondary table key, a non-handleable exception is raised.

◉ If the row being appended would destroy the sort order of sorted tables, a non-handleable exception is raised (the secondary index of a sorted secondary key, however, is updated before it is used again).

System Fields

The APPEND statement sets sy-tabix to the row number of the last appended row in the primary table index.

Notes

◉ The administration of a unique secondary table key is updated immediately; the administration of a non-unique key is not updated until the secondary table key is next used explicitly (lazy update).
◉ Runtime costs for creating or updating a non-unique secondary table key are not incurred therefore until it is used for the first time.

The value operator VALUE can also be used to construct the content of internal tables.

Example

Appends 100 random numbers to the internal table itab with row type i.

DATA itab TYPE TABLE OF i WITH EMPTY KEY.

DATA(rnd) = cl_abap_random_int=>create( seed = + sy-uzeit
                                        min  = 1
                                        max  = 100 ).

DO 100 TIMES.
  APPEND rnd->get_next( ) TO itab.
ENDDO.

Addition

... SORTED BY comp

Effect

Used correctly, this addition can produce ranking lists in descending order. This only works if a value greater than 0 is specified in the declaration of the internal table in the addition INITIAL SIZE. If the value 0 is specified for INITIAL SIZE, the statement APPEND is ignored when used with the addition SORTED BY.

The addition SORTED BY can be used only when a work area wa is specified and for a standard table. Also, wa must be compatible with the row type of the table. The component comp can be specified as shown in the section Specifying Components, however only a single component can be addressed using the object component selector, and no attributes of classes.

As long as the declaration of the internal table for INITIAL SIZE has a value greater than zero, the statement is executed in two steps:

◉ Starting from the final row, the table is scanned for a row in which the value of the component comp is greater than or equal to the value of the component comp of wa. If a row like this exists, the work area wa is inserted after this row with respect to the primary index. In no such row is found, the work area wa is inserted before the first row with respect to the primary index. The row numbers of all rows after the inserted row are increased by 1 in the primary table index.

◉ If the number of rows before the statement is executed is greater than or equal to the number specified in the declaration of the internal table in the addition INITIAL SIZE, the new final row is deleted with respect to the primary table index.

When using only the statement APPEND with the addition SORTED BY to fill an internal table with a value no greater than 0 for INITIAL SIZE, this rule produces an internal table that contains no more than the number of rows specified in its definition after INITIAL SIZE and that is sorted in descending order with respect to the primary table index by component comp (ranking).

Example

Creates a ranking of the three flights of a connection showing the most free seats.

DATA: carrid TYPE sflight-carrid VALUE 'LH',
      connid TYPE sflight-connid VALUE '0400'.
cl_demo_input=>new(
  )->add_field( CHANGING field = carrid
  )->add_field( CHANGING field = connid )->request( ).

DATA: BEGIN OF seats,
        fldate TYPE sflight-fldate,
        seatsocc TYPE sflight-seatsocc,
        seatsmax  TYPE sflight-seatsmax,
        seatsfree TYPE sflight-seatsocc,
      END OF seats.

DATA seats_tab LIKE STANDARD TABLE OF seats
               INITIAL SIZE 3.

SELECT fldate, seatsocc, seatsmax, seatsmax - seatsocc AS seatsfree
       FROM sflight
       WHERE carrid = @carrid AND
             connid = @connid
       INTO @seats.
  APPEND seats TO seats_tab SORTED BY seatsfree.
ENDSELECT.

cl_demo_output=>display( seats_tab ).

Exceptions

Handleable Exceptions

CX_SY_ITAB_DUPLICATE_KEY

◉ Cause: Duplicate key values in unique secondary key
Runtime error: ITAB_DUPLICATE_KEY

Non-Handleable Exceptions

◉ Cause: Row with identical key inserted (target table defined using UNIQUE)
Runtime error: ITAB_DUPLICATE_KEY_IDX_OP

◉ Cause: Sort order violated after an APPEND on a sorted table
Runtime error: ITAB_ILLEGAL_SORT_ORDER:

◉ Cause: Invalid index value (<= 0) when FROM, TO, or INDEX specified
Runtime error: TABLE_INVALID_INDEX

◉ Cause: Memory area violated when TABLES parameter accessed
Runtime error: ITAB_STRUC_ACCESS_VIOLATION

7.2.6 MODIFY itab

Syntax

MODIFY { itab_line | itab_lines }.

Effect

This statement changes the content either of a single row itab_line or multiple rows itab_lines, which can be specified using a table key or a table index.

The following limitations apply when modifying key fields of the primary and secondary table keys:

◉ The key fields of the primary table key of sorted tables and hashed tables are read-only and must not be modified. This would invalidate internal table administration. The processing statements for internal tables check whether writes are performed on individual key fields and a corresponding non-handleable exception raised. If writes are performed in write positions across the entire table row (for example, as a target field of assignments or as actual parameters for output parameters), an exception is always raised.

◉ The key fields of a secondary table key, however, are only read-only while the secondary table is being used. This is the case in LOOP loops and during the use of the MODIFY statement, in which the secondary key is specified after USING KEY. Otherwise the key fields are not read-only.

The administration of unique secondary keys is updated directly after a modification using MODIFY, and raises a non-handleable exception if duplicate entries would otherwise be produced. The non-unique secondary keys are updated when the secondary table key is next used explicitly (lazy update).

System Fields

sy-subrc Meaning 
0 At least one row was changed. 
4 No rows were changed, since no suitable row was found during the search using a table key or in the logical expression, or the specified index was greater than the current number of rows for the search using a table index.

The system field sy-tabix is not set.

Notes

◉ Apart from using the MODIFY statement, the content of an individual table row can be changed using assignments to field symbols and dereferenced data references that point to the table row.

◉ There is no implicit selection of a suitable key or index. The used table key or table index is always specified uniquely. The syntax check issues a warning if there is a suitable secondary table key but this table key is not used. This warning should be removed through using the key. However, in exceptional cases, it can be bypassed using a pragma.

◉ Using a special variant MODIFY mesh_path, rows from the last path node of a mesh path can be changed.

Example

Modifies a row in an internal table using a key access. A row with a specific key value is read to a work area wa. From this value, a structure with a different value is then constructed in a non-key component after the addition FROM of the statement MODIFY. The table row with the appropriate key value is then modified.

DATA itab TYPE HASHED TABLE OF scarr WITH UNIQUE KEY carrid.

SELECT *
       FROM scarr
       INTO TABLE @itab.

DATA(wa) = VALUE #( itab[ carrid = 'LH' ] OPTIONAL ).

IF wa IS NOT INITIAL.
  MODIFY TABLE itab
         FROM VALUE #( BASE wa carrname = '...' ).
ENDIF.

Exceptions

Handleable Exceptions

CX_SY_ITAB_DYN_LOOP

◉ Cause: Error in a dynamic WHERE condition
Runtime error: DYN_WHERE_PARSE_ERROR

Non-Handleable Exceptions

◉ Cause: Invalid dynamic specification of a row component
Runtime error: ITAB_ILLEGAL_COMPONENT

◉ A read-only secondary table key would be overwritten
Runtime error: ITAB_ACTIVE_KEY_VIOLATION

◉ Cause: Memory area violated when TABLES parameter accessed
Runtime error: ITAB_STRUC_ACCESS_VIOLATION

7.2.7 DELETE itab

Syntax

DELETE { itab_line | itab_lines | duplicates }.

Effect

This statement either deletes a row (itab_line) or several rows (itab_lines), which can be specified with a table key or a table index, or it deletes adjacent duplicate rows duplicates.

System Fields

sy-subrc Meaning 
At least one row was deleted.
No rows were deleted, since no appropriate row was found when deleting using a table key or when specifying a logical expression, the index specified when deleting using a table index was greater than the current number of rows, or no duplicate adjacent rows were found.

The system field sy-tabix is not set.

Notes

◉ Deleting rows in internal tables using DELETE does not usually free any memory in the internal table. Statements such as CLEAR or FREE must be used to free this memory.

◉ An internal table - in which all rows have been deleted with DELETE - is usually not an initial internal table.

◉ When deleting rows in an internal table, costs are incurred for updating all existing table keys and table indexes. The primary key and all unique secondary keys are updated directly, whereas non-unique secondary keys are only updated if the rows to be deleted are contained within an updated part of a relevant index (lazy update). When deleting a row from a standard table and the row is found using a secondary key, the primary table index in particular must be updated, and this usually requires a linear search.

◉ There is no implicit selection of a suitable key or index. The used table key or table index is always specified uniquely. The syntax check issues a warning if there is a suitable secondary table key but this table key is not used. This warning should be removed through using the key. However, in exceptional cases, it can be bypassed using a pragma.

◉ Using a special variant DELETE mesh_path, rows from the last path node of a mesh path can be deleted.

Example

Deletes all initial rows of an internal table.

DATA itab TYPE HASHED TABLE OF scarr WITH UNIQUE KEY carrid.

...

DELETE itab WHERE table_line IS INITIAL.

Exceptions

Handleable Exceptions

CX_SY_ITAB_DYN_LOOP

- Cause: Error in a dynamic WHERE condition
Runtime error: DYN_WHERE_PARSE_ERROR

7.2.8 SORT itab

Syntax

SORT itab [STABLE]
          { { [ASCENDING|DESCENDING]
              [AS TEXT]
              [BY { comp1 [ASCENDING|DESCENDING] [AS TEXT]}
                  { comp2 [ASCENDING|DESCENDING] [AS TEXT]}
                  ... ] }
          | { [BY (otab)] }
          | { [BY expr] } }.

Extras:

1. ... STABLE

2. ... ASCENDING|DESCENDING

3. ... AS TEXT

4. ... BY compi [ASCENDING|DESCENDING] [AS TEXT]

5. ... BY (otab)

6. ... BY expr

Effect

This statement sorts an internal table itab by the size of its components. Here, default sizes are compared using the general comparison rules, that is:

◉ Numeric and byte-like components are sorted by their values.

◉ Character-like components are sorted by default by their binary representation (code page). Textual sorting of character-like components can be performed using the addition AS TEXT.

◉ The sizes of other component types are compared using the corresponding rules for reference variables, structures, and internal tables.

If no explicit sort key is entered using the addition BY, the internal table itab is sorted by the primary table key. The priority of the sort is based on the order in which the key fields are specified in the table definition. In standard keys, the sort is prioritized according to the order of the key fields in the row type of the table. If the primary table key of a standard table is empty, no sort takes place. If this is known statically, the syntax check produces a warning.

Sorting is unstable by default, which means that the relative order of rows that do not have different sort keys is not preserved when they are sorted. The order can be different depending on the platform or when sorted multiple times. The addition STABLE can be used for stable sorting.

itab expects a standard table or a hashed table.

◉ In standard tables, the primary table index is applied in accordance with the sort order

◉ In hashed tables, the internal order is modified. This internal order was defined either by inserting rows in the internal table or by a previous sort using the statement SORT.

In both table categories, SORT specifies the order in which a subsequent LOOP runs without the addition USING KEY.

Sorted tables cannot be sorted using SORT and applying the statement SORT to sorted tables is prohibited by the syntax. If the system only detects that a sorted table is to be sorted at runtime, a non-handleable exception is raised if this action could modify the existing sorting. The latter occurs in the following cases:

◉ if the addition BY is used to specify a different sort key as the initial part of the table key.

◉ if the addition DESCENDING is used.

◉ if the addition AS TEXT is used.

◉ if an attribute of an object is specified as a component in the addition BY.

Otherwise, the statement SORT is ignored for sorted tables.

Notes

◉ It is best to specify an explicit sort key behind BY, if possible. An implicit sort behind the primary table key (which can itself, in standard tables, be defined implicitly as a standard key) makes a program difficult to understand and possibly unpredictable.

◉ When using the primary table key, note that this key can be the standard key, which can also have unexpected consequences:

     ◉ If the row type is structured, the table is sorted by all character-like and byte-like components.
     ◉ The standard key of a standard table can be empty.

◉ Secondary table keys cannot be specified as sort keys.

◉ SORTs are ignored by the assignment of rows to a secondary table index.

◉ The addition GROUP BY of the statement LOOP AT itab or of a FOR expression also has the additions ASCENDING and DESCENDING for sorting groups. These can be used to expand the statement SORT if its sort criteria are not sufficient (see the executable example).

◉ It is possible to sort columns with reference types but doing this is questionable. Here it is important to note that no comparison rule is defined for non-initial invalid references. An internal table can only be sorted by valid or initial references. A non-initial, invalid reference leads to a runtime error if it is involved in sorting.

◉ System class CL_ABAP_ITAB_UTILITIES contains method VIRTUAL_SORT, which can be used to virtually sort a set of internal tables. See also the executable examples listed below.

Example

Simplest form of statement SORT for internal tables. The hash table carriers is sorted by its primary key (in other words, sorted by column carrid).

DATA carriers TYPE HASHED TABLE OF scarr
              WITH UNIQUE KEY carrid.

SELECT *
       FROM scarr
       INTO TABLE @carriers.

SORT carriers.


Executable Examples

- Sorting Internal Tables
- Sorting Internal Tables with Secondary Keys

Addition 1

... STABLE

Effect

STABLE is used to perform stable sorts, which means that the relative order of rows (an order that does not change in the sort key) remains unchanged in the sort. If the STABLE addition is not specified, the order is not stable:

The order can depend on the platform.

Multiple sorting of a table using the same sort key can produce a different order each time the table is sorted.

Example

Stabile sorting of internal table flights by columns cityfrom cityto, whereby the order within this sorting with regards to carrid and connid remains the same.

SELECT carrid, connid, cityfrom, cityto
       FROM spfli
       ORDER BY carrid, connid
       INTO TABLE @DATA(flights).

SORT flights STABLE BY cityfrom cityto.

Addition 2

... ASCENDING|DESCENDING

Effect

The addition ASCENDING or DESCENDING can be used to specify the sort direction explicitly as ascending or descending. If neither of the additions is specified, the table is sorted in ascending order. This sort direction can be overwritten after the addition BY for components specified individually here.

Example

The internal table itab is sorted by its primary key (in other words, by its rows). Next, LOOP AT GROUP BY can be used for grouping and determine the number of rows per group.

DATA itab TYPE TABLE OF i WITH NON-UNIQUE KEY table_line.

DATA(rnd) = cl_abap_random_int=>create( seed = + sy-uzeit
                                        min  = 1
                                        max  = 10 ).

itab = VALUE #( FOR i = 1 UNTIL i > 100 ( rnd->get_next( ) ) ).

SORT itab DESCENDING.

LOOP AT itab ASSIGNING FIELD-SYMBOL(<fs>)
             GROUP BY ( key = <fs> size = GROUP SIZE )
             ASSIGNING FIELD-SYMBOL(<key>).
  cl_demo_output=>write( |{ <key>-key WIDTH = 4
                         }{ <key>-size }| ).
ENDLOOP.
cl_demo_output=>display( ).

Addition 3

... AS TEXT

Effect

The addition AS TEXT specifies that text-like components are sorted in accordance with the locale of the current text environment. If AS TEXT is not specified, text-like components are sorted according to the encoding in the code page of the current text environment. This can be overwritten after the addition BY for the components specified individually here. The text environment is set when an internal session is opened or by using the statement SET LOCALE.

Notes

- The result of sorting without the addition AS TEXT depends on the operating system of the application server. Although the sequence of individual letters that belong to the activated language remains the same across different operating systems, there are differences in terms of the characters that do not belong to the alphabet of the activated language. Even if only the letters from the alphabet of the activated language are used, some slight differences occur when sorting complete words. Furthermore, the order of uppercase and lowercase letters is specific to the operating system.

- The use of the addition AS TEXT usually renders the statement CONVERT TEXT superfluous in the context of internal tables.

- A sort without the addition AS TEXT is considerably faster than a sort that does use this addition. If it is certain that both sorts produce the same order, the addition AS TEXT is not necessary. This can be the case if, for example, text-like components contain characters from the ASCII character set only and only lowercase or uppercase letters.

Example

Sorting a hashed table text_tab by the order in the code page and in accordance with the locale of the current text environment. If a western European text environment is configured, the sorts produce the orders Miller, Moller, Muller, Möller and Miller, Moller, Möller, Muller respectively (also see the executable example for SET LOCALE).

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
  PRIVATE SECTION.
    CLASS-DATA text_tab TYPE HASHED TABLE OF string
               WITH UNIQUE KEY table_line.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    text_tab = VALUE #(
      ( `Muller` )
      ( `Möller` )
      ( `Moller` )
      ( `Miller` ) ).
    SORT text_tab.
    cl_demo_output=>write_data( text_tab ).
    SORT text_tab AS TEXT.
    cl_demo_output=>display_data( text_tab ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Addition 4

... BY compi [ASCENDING|DESCENDING] [AS TEXT]

Effect

The addition BY compi does not sort the table by the primary table key, but by the components comp1 comp2... specified after it instead. The components are specified as described under Specifying Components. If all components are specified using name variables and these variables contain only blanks, no sort takes place. The priority of the sort depends on the order in which the components comp1 comp2 ... are specified from left to right. The specified components can also be duplicated or can overlap. The specified components can have any data type. The relevant comparison rules apply to the evaluation.

If neither of the additions ASCENDING or DESCENDING are specified after compi, the sort direction specified by addition 2 is used. If one of the additions ASCENDING or DESCENDING is specified, it overwrites the default for this component.

If the addition AS TEXT is not specified after a text-like component compi, the instructions defined by addition 3 are used. If the addition AS TEXT is specified after a text-like component, it overwrites the default for this component. In non-text-like components, AS TEXT cannot be specified, unless a structured component is specified. In structured components, AS TEXT only affects text-like components.

Notes

- If the row type of the internal table is not known statically, the components can only be specified dynamically and not directly.

- Instead of individual dynamic components, an internal table can be specified directly as otab or as the result of an expression expr as a dynamic sort key (see additions 5 and 6). Using a table like this has the advantage that any exceptions are handleable. When specifying the table, the number of components of the sort key is also dynamic. In contrast, when individual dynamic components are used, a character-like data object must be specified for any required component, which is ignored if it only contains blank characters.

- An obsolete variant allows field symbols to also be specified for the components outside of classes, for standard tables.

Example

Sorting in ascending order of internal table itab by column col1 and sorting in descending order by column col2.

TYPES:
  BEGIN OF line,
    col1 TYPE c LENGTH 1,
    col2 TYPE i,
  END OF line.

DATA itab TYPE STANDARD TABLE OF line WITH EMPTY KEY.

itab = VALUE #( ( col1 = 'A' col2 = 6 )
                ( col1 = 'B' col2 = 4 )
                ( col1 = 'B' col2 = 7 )
                ( col1 = 'C' col2 = 1 )
                ( col1 = 'C' col2 = 3 )
                ( col1 = 'B' col2 = 9 )
                ( col1 = 'A' col2 = 2 )
                ( col1 = 'A' col2 = 5 )
                ( col1 = 'C' col2 = 8 ) ).

SORT itab BY col1 ASCENDING col2 DESCENDING.

cl_demo_output=>display( itab ).

Addition 5

... BY (otab)

Effect

The addition BY (otab) does not sort the table by the primary table key, but by the component specified dynamically in the internal table otab instead. Each row of the table otab defines a component of the sort key. The priority of the sort is based on the order of the rows in otab. If the table otab is initial, the table is not sorted.

For otab, a standard table of the table type ABAP_SORTORDER_TAB from ABAP Dictionary must be specified. The row type of this table is the dictionary structure ABAP_SORTORDER with the following components:

- NAME of type SSTRING

for specifying a component of the sort key. The component is specified in the form "comp_name[+off(len)]", where "comp_name" must be the name of a component in itab (uppercase characters). The component name may contain offsets and lengths, structure component selectors, and component selectors for assigning structured data objects and attributes in classes or objects.

- DESCENDING of the type CHAR of a length 1

for specifying the sort direction for the current component. If DESCENDING is initial, the sort is performed in ascending order. If DESCENDING has the value "X", the table is sorted in descending order.

- ASTEXT of type CHAR with length 1

for the text sorting of the current component. If ASTEXT has the value "X", the sort is performed as with the addition AS TEXT. This is only possible for character-like components. If ASTEXT is initial, character-like components are sorted in accordance with their binary representation.

If a column of otab has invalid content (that is, if NAME contains the name of a component that does not exist or an incorrect offset/length or if DESCENDING and ASTEXT do not contain "X" or the initial value), this raises a handleable exception of the class CX_SY_DYN_TABLE_ILL_COMP_VAL.

Notes

- The addition BY (otab) cannot be combined with BY compi.

- When using the addition BY (otab), it is not possible to use DESCENDING or AS TEXT to specify a descending sort direction or textual sorting for all components.

- If a single parenthesized data object (dobj) is specified after the BY addition, its data type decides whether its content is used to specify a single dynamic component or multiple components. In either case, no sort takes place if dobj is initial.

Example

Dynamic import of a database table into a dynamic internal table and dynamic sorting of its content. The name of the database table and the names of the columns (which the table is to be sorted by) can be entered.

DATA dbtab TYPE c LENGTH 30 VALUE 'spfli'.
cl_demo_input=>add_field( CHANGING field = dbtab ).
DATA columns TYPE string VALUE `cityfrom, cityto`.
cl_demo_input=>request( CHANGING field = columns ).
dbtab = condense( to_upper( dbtab ) ).

DATA dref TYPE REF TO data.
FIELD-SYMBOLS <itab> TYPE STANDARD TABLE.

TRY.
    CREATE DATA dref TYPE STANDARD TABLE OF (dbtab).
    ASSIGN dref->* TO <itab>.
  CATCH cx_sy_create_data_error.
    cl_demo_output=>display( 'Wrong data type!' ).
    LEAVE PROGRAM.
ENDTRY.

TRY.
    SELECT *
           FROM (dbtab)
           INTO TABLE @<itab>.
  CATCH cx_sy_dynamic_osql_semantics.
    cl_demo_output=>display( 'Wrong database table!' ).
    LEAVE PROGRAM.
ENDTRY.

SPLIT columns AT `,` INTO TABLE DATA(column_tab).
DATA(order) = VALUE abap_sortorder_tab(
                FOR wa IN column_tab
               ( name = condense( to_upper( wa ) ) ) ).

TRY.
    SORT <itab> BY (order).
  CATCH cx_sy_dyn_table_ill_comp_val.
    cl_demo_output=>display(  'Wrong column name!' ).
    LEAVE PROGRAM.
ENDTRY.

cl_demo_output=>display( <itab> ).

Addition 6

... BY expr

Effect

The addition BY expr can be used to specify an expression or a functional method call expr whose result is an internal table with the same type and content as in the preceding addition BY (otab). expr is a general expression position. The behavior is the same as when specifying a parenthesized internal table directly.

Note

Parentheses cannot be placed around expr.

Example

The above example for specifying BY (otab) can be written in a shorter form as shown below. Instead of specifying the unnecessary internal table order, you can specify the tabular value (constructed using value operator VALUE) of the required content.

...

SPLIT columns AT `,` INTO TABLE DATA(column_tab).
TRY.
    SORT <itab> BY VALUE #( FOR wa IN column_tab
                ( name = condense( to_upper( wa ) ) ) ).
  CATCH cx_sy_dyn_table_ill_comp_val.
    cl_demo_output=>display(  'Wrong column name!' ).
    LEAVE PROGRAM.
ENDTRY.

...

Exceptions

Non-Handleable Exceptions

CX_SY_DYN_TABLE_ILL_LINE_TYPE

- Cause: The table otab has a prohibited line type.
Runtime error: DYN_TABLE_ILL_LINE_TYPE

CX_SY_DYN_TABLE_ILL_COMP_VAL

- Cause: A column of the table otab contains a prohibited value.
Runtime error: DYN_TABLE_ILL_COMP_VAL

Non-Handleable Exceptions

- Cause: A sort criterion dynamically specified in the form (name) with the explicit addition AS TEXT is not text-like.
Runtime error: SORT_AS_TEXT_BAD_DYN_TYPE

- Cause: A field symbol used as a dynamic sort criterion with an explicit addition AS TEXT is not text-like.
Runtime error: SORT_AS_TEXT_BAD_FS_TYPE

- Cause: A field symbol used as a dynamic sort criterion does not point to the header row of the internal table to be sorted.
Runtime error: SORT_ITAB_FIELD_INVALID

- Cause: For a table of the type SORTED TABLE, the sort key does not match a beginning piece of the table key.
Runtime error: SORT_SORT_ILL_KEY_ORDER

- Cause: The additions DESCENDING and AS TEXT are not allowed for tables of the type SORTED TABLE.
Runtime error: SORT_SORT_ILLEGAL

- Cause: More than 250 sort criteria.
Runtime error: SORT_TOO_MANY_FIELDS

- Cause: Sort mode neither 'E'(xternal) nor 'I'(nternal)
Runtime error: SORT_ILLEGAL_MODE

7.2.9 FIND IN TABLE itab

Syntax

FIND [{FIRST OCCURRENCE}|{ALL OCCURRENCES} OF] pattern
  IN TABLE itab [table_range]
  [IN {CHARACTER|BYTE} MODE]
  [find_options].

Effect

The internal table itab is searched row-by-row for the character strings or byte strings specified in pattern. itab is a functional operand position.

itab must be a standard table with no secondary table keys. The rows in the table must be character-like or byte-like, depending on the addition CHARACTER or BYTE MODE. Character strings or byte strings that cover multiple table rows are not found.

The addition table_range can be used to restrict the search range in the table. When making replacements in the individual table rows, the other additions generally have the same meaning as the statement FIND for elementary character or byte strings. Here, a further addition MATCH LINE also returns the row number of any occurrence.

The search is terminated if the search pattern was found for the first time, or if all search patterns were found in the entire search area, or if the end of the search area was reached. The search result is communicated by setting sy-subrc.

In string processing with row types of fixed length, trailing blanks are respected.

Notes

- Using FIND IN TABLE you can search tables with structured row types for character strings, if the structure only contains flat character-like components. Every row is then handled in the same way as a field of type c.

- Searching using FIND IN TABLE produces better performance than running a LOOP and using FIND to search the individual rows.

- If searching multiple rows, the rows can be transferred in a string by using the concatenation function concat_lines_of or the statement CONCATENATE LINES OF. Ensure that trailing blanks are handled correctly.

System Fields

sy-subrc Meaning

0      The search pattern was found at least once in the search range.
4      The search pattern was not found in the search range.

The values of sy-tabix and sy-fdpos are not changed.

Example

Reads a text into an internal table in ITF format and searches for the first string "ABAP" or "XML". The positions the occurrence are written in variables. The row type of the internal table is interpreted as a single field of the type c despite being a structured type.

DATA itf_tab TYPE tline_tab.
IF cl_abap_docu_itf=>get_docu( EXPORTING id = 'SD'
                                         langu = 'E'
                                         object = 'ABENABAP_XML'
                               IMPORTING itf = itf_tab ) = 0.

  FIND FIRST OCCURRENCE OF REGEX 'ABAP|XML'
       IN TABLE itf_tab
       RESPECTING CASE
       MATCH LINE DATA(mline)
       MATCH OFFSET DATA(moff)
       MATCH LENGTH DATA(mlen).

  IF sy-subrc = 0.
    cl_demo_output=>display( |{ mline }, { moff }, { mlen }| ).
  ENDIF.
ENDIF.

Exceptions

Handleable Exceptions

CX_SY_RANGE_OUT_OF_BOUNDS

- Cause: Illegal offset or length specified in the addition SECTION OF.
Runtime error: REFI_WRONG_SECTION

CX_SY_TAB_RANGE_OUT_OF_BOUNDS

- Cause: Illegal offset or length specification in the addition of FROM ... OFFSET ... TO OFFSET.
Runtime error: INVALID_TABLE_RANGE

CX_SY_INVALID_REGEX

- Cause: Illegal expression after the addition REGEX.
Runtime error: INVALID_REGEX

7.2.10 REPLACE IN TABLE itab

Syntax

REPLACE [{FIRST OCCURRENCE}|{ALL OCCURRENCES} OF] pattern
        IN TABLE itab [table_range] WITH new
        [IN {CHARACTER|BYTE} MODE]
        [replace_options].

Effect

The internal table itab is scanned row-by-row for the character or byte strings specified by pattern and replaces any occurrences with the content of the operand new. new is a character-like expression position.

itab expects a standard table without secondary table keys. The rows in the table must be character-like or byte-like, depending on the addition CHARACTER or BYTE MODE. Byte or character strings that cover multiple table rows are not replaced.

The table_range addition can be used to restrict the search range in the table. When searching in individual table rows, the other additions operate in the same way as in the statement REPLACE pattern IN for elementary character or byte strings, with an extra addition, REPLACEMENT LINE, returning the row number of an occurrence.

In string processing for row types of fixed length, trailing blanks are respected, whereas with new they are ignored.

Notes

◉ REPLACE IN TABLE can be used in tables with structured row types to replace character strings if the structure only contains flat character-like components. Every row is then handled in the same way as a field of type c.

◉ Replacements using REPLACE IN TABLE give better performance than running a LOOP and using REPLACE to make replacements in individual rows.

System Fields

sy-subrc Meaning 
0 The search string was replaced by the content of new and the full result is available in the table row(s).
The search pattern was replaced by the content of new and the result of the replacement was truncated on the right in at least one table row. 
The search pattern in pattern was not found in the internal table. 
The operands pattern or new do not contain interpretable double-byte characters. 

The values of sy-tabix and sy-fdpos are not changed.

Example

A simple "DM-Euro Conversion".

DATA menu TYPE TABLE OF string WITH EMPTY KEY.

menu = value #( ( `Beer  -  3 DM` )
                ( `Pizza - 10 DM` ) ).

REPLACE ALL OCCURRENCES OF REGEX `\b(DM)\b`
        IN TABLE menu WITH 'EUR'
        RESPECTING CASE.

Handleable Exceptions

CX_SY_REPLACE_INFINITE_LOOP

◉ Cause: Substring of length 0 creates an endless loop when searching for all occurrences.
Runtime error: REPLACE_INFINITE_LOOP

CX_SY_TAB_RANGE_OUT_OF_BOUNDS

◉ Cause: Illegal offset or row specification in the addition of FROM ... OFFSET ... TO OFFSET.
Runtime error: INVALID_TABLE_RANGE

CX_SY_INVALID_REGEX

◉ Cause: Illegal expression after the addition REGEX.
Runtime error: INVALID_REGEX

7.2.11 Interval Join in Internal Tables

These language elements should be used only by specialists with in-depth knowledge of the corresponding environment.

PROVIDE

Syntax

PROVIDE FIELDS {*|{comp1 comp2 ...}}
               FROM itab1 INTO wa1 VALID flag1
               BOUNDS intliml1 AND intlimu1
               [WHERE log_exp1]
        FIELDS {*|{comp1 comp2 ...}}
               FROM itab2 INTO wa2 VALID flag2
               BOUNDS intliml2 AND intlimu2
               [WHERE log_exp2]
               ...
        BETWEEN extliml AND extlimu
        [INCLUDING GAPS].
  ...
ENDPROVIDE.

Extras:

1. ... WHERE log_exp

2. ... INCLUDING GAPS

Effect

Table iteration across multiple tables as specified by interval boundaries. The statements PROVIDE and ENDPROVIDE define a loop around a statement block. Any number of internal tables itab1 itab2 ... are edited together in this loop. A single table can appear more than once. A FIELDS addition must be specified for each table itab. After FIELDS, either the character * must be specified for all components or a list comp1 comp2 ... must be specified for certain components of the table in question. The names of the components comp1 comp2 ... can only be specified directly.

To be able to process internal tables using PROVIDE, all tables itab1 itab2 ... must be fully typed index tables and contain two special columns that have the same data type (d, i, n, or t) for all relevant tables. For every table, the names intliml1 intliml2 ... and intlimu1 intlimu2 ... of these columns must be specified using the addition BOUNDS.

The columns intliml1 intliml2 ... and intlimu1 intlimu2 ... in every row of the relevant internal tables must contain values that can be interpreted as limits of closed intervals. Within a table, the intervals specified in these columns must not overlap and must be sorted in ascending order by the primary table index. The intervals therefore make up a unique key for every row.

For every table, a work area wa1 wa2 ... compatible with the row type and a variable flag1 flag2 ... (which expects a character-like data type with length 1) must be specified. In the PROVIDE loop, the components specified after FIELDS are filled with values in the relevant work areas wa1 wa2 ... for every specified internal table. The variables flag1 flag2 ... are also filled. A work area wa1 wa2 ... or a variable flag1 flag2 ... cannot be specified more than once.

The BETWEEN addition must be used to specify an interval extliml, extlimu. It must be possible to convert the data objects extliml and extlimu into the data types of the respective columns intliml1 intliml2 ... and intlimu1 intlimu2 ... of the individual tables.

The interval limits intliml1 intliml2 ... and intlimu1 intlim2 for every row of all relevant internal tables itab1 itab2 ... that are within the closed interval made up by extliml and extlimu divide the latter into new intervals and every interval limit closes one interval in the original direction. If, within a relevant table, a lower interval limit follows an upper interval limit with no space or gap between them and the components of the corresponding rows specified after FIELDS have the same content, the two intervals are combined and the corresponding interval limits intliml1 intliml2 ... and intlimu1 intlimu2 ... are ignored for the new intervals.

For every interval that is created in such a way and overlaps with at least one of the intervals of a table involved, the PROVIDE loop is passed once. The components of every work area wa1 wa2 ... specified after FIELDS and the variables flag1 flag2 ... are filled with values as follows:

◉ The components intliml1 intliml2 ... and intlimu1 intlimu2 ... of every work area wa1 wa2 ... are filled with the interval limits of the current interval.

◉ If the current interval overlaps with one of the intervals of an involved table, the remaining components of the corresponding work area are assigned the contents of the relevant components of this table row and the variable flag1 flag2 ... is set to the value "X". Otherwise, the work area components and the variables flag1 flag2 ... are set to their Initial value. For performance reasons, the components of the work areas are provided with values again only if the corresponding content of the table rows has been modified since the preceding loop.

Except for intliml1 intliml2 ... and intlimu1 intlimu2 ..., the components not specified after FIELDS are always set to their initial value. The components intliml1 intliml2 ... and intlimu1 intlimu2 ... are always assigned.

The ABAP runtime environment checks for every table involved, whether the condition of sorted and non-overlapping intervals is met within the interval made up by extliml and extlimu and, if necessary, raises a handleable exception.

Notes

◉ The statement PROVIDE is intended mainly for the editing of internal tables for HR- info types declared using the special statement INFOTYPES or that have a corresponding layout.

◉ The relevant internal tables should not be modified in the PROVIDE loop.

◉ Only reads should be performed on the work areas wa1 wa2 ... as well, since they are not necessarily filled again at the start of each loop.

◉ The WHERE condition can be used to remove overlaps between the tables involved, or to ensure the sorting of the intervals.

◉ Any secondary table keys of the internal tables involved are not supported in the PROVIDE statement. The primary table index is always (implicitly) used for processing.

◉ Besides this variant of the PROVIDE statement, anobsolete short form can also be used outside of classes.

Addition 1

... WHERE log_exp

Effect

The addition WHERE can be used to specify a condition for every table itab1 itab2 ... involved. After WHERE, any logical expression log_exp1, log_exp2 ... can be specified. Here, a component of the internal table must be specified as the first operand of each relational expression (and this component must be the only operand and not part of an expression). Only components that are also specified after FIELDS can be specified. Any comparison expression and the predicate expression IS INITIAL can be specified as relational expressions. Other predicates cannot be specified. The other operands of a comparison can be any suitable individual operands or arithmetic expressions but not components of the internal table. The table entries for which the condition is not met are ignored by the PROVIDE loop. The PROVIDE loop can be exited using the instructions in the section Exiting Loops.

Addition 2

... INCLUDING GAPS

Effect

If the INCLUDING GAPS addition is specified, the system passes the PROVIDE loop for every interval, which is also the case when the current interval does not overlap with at least one of the intervals of an involved table. In the latter case, the variable flag is of initial value for every relevant table.

System Fields

sy-subrc and sy-tabix are set to the value 0 before every loop pass and in ENDPROVIDE. Only if the loop is not passed at all, is sy-subrc set to 4 in ENDPROVIDE.

Example

In two tables, itab1 and itab2, the respective columns col1 and col2 are interval limits of type i. The filling of the internal tables produces the following intervals (rows two and three):

-------------------------------------------
|01|02|03|04|05|06|07|08|09|10|11|12|13|14|
-------------------------------------------
|   Itab1 Int1    |     |Itab1 Int2 |     |
-------------------------------------------
|        |      Itab2 Int1       |        |
-------------------------------------------
|  |          ... BETWEEN ...             |
-------------------------------------------
|  | i1  |   i2   | i3  |   i4   |i5|     |
-------------------------------------------

The interval specified in the BETWEEN addition of the PROVIDE statement is shown in the fourth row. It produces the five intervals in the fifth row represented by i1 to i5. These can be processed in the PROVIDE loop.

Because each of the five intervals overlaps with one of the intervals from rows two and three, the PROVIDE loop is passed five times.

Only the component col3 of wa1 is filled in the first pass, only the component col3 of wa2 in the third pass, and the components col3 of both work areas in the second and fourth passes. The fields valid1 and valid2 are set accordingly.

DATA: BEGIN OF wa1,
        col1 TYPE i,
        col2 TYPE i,
        col3 TYPE string,
      END OF wa1.

DATA: BEGIN OF wa2,
        col1 TYPE i,
        col2 TYPE i,
        col3 TYPE string,
      END OF wa2.

DATA: itab1 LIKE STANDARD TABLE OF wa1,
      itab2 LIKE STANDARD TABLE OF wa2.

DATA: flag1(1) TYPE c,
      flag2(1) TYPE c.

itab1 = VALUE #( ( col1 = 1 col2 = 6  col3 = 'Itab1 Int1' )
                 ( col1 = 9 col2 = 12 col3 = 'Itab1 Int2' ) ).
itab2 = VALUE #( ( col1 = 4 col2 = 11 col3 = 'Itab2 Int1' ) ).

DATA output TYPE TABLE OF string WITH EMPTY KEY.
PROVIDE FIELDS col3 FROM itab1 INTO wa1
                               VALID flag1
                               BOUNDS col1 AND col2
        FIELDS col3 FROM itab2 INTO wa2
                               VALID flag2
                               BOUNDS col1 AND col2
        BETWEEN 2 AND 14.
  APPEND | { wa1-col1 WIDTH = 2 } { wa1-col2 WIDTH = 2 } {
             wa1-col3 } { flag1 } | TO output.
  APPEND | { wa2-col1 WIDTH = 2 } { wa2-col2 WIDTH = 2 } {
             wa2-col3 } { flag2 } | TO output.
  APPEND INITIAL LINE TO output.
ENDPROVIDE.
cl_demo_output=>display( output ).

The output is as follows:

   2           3  Itab1 Int1 X
   2           3

   4           6  Itab1 Int1 X
   4           6  Itab2 Int1 X

   7           8
   7           8  Itab2 Int1 X

   9          11  Itab1 Int2 X
   9           11  Itab2 Int1 X

  12          12  Itab1 Int2 X
  12          12

Handleable Exceptions

CX_SY_PROVIDE_INTERVAL_OVERLAP

◉ Cause: In one of the involved tables there are overlapping intervals within extlim1 and extlim2.
Runtime error: UNCAUGHT_EXCEPTION

CX_SY_PROVIDE_TABLE_NOT_SORTED

◉ Cause: One of the involved tables is not sorted in ascending order by the intervals within extlim1 and extlim2.

Runtime error: UNCAUGHT_EXCEPTION

7.2.12 Internal Tables - comp1 comp2 ...

Addresses individual components of internal table rows in a range of statements for editing internal tables and in table expressions. If not stated otherwise, the following syntax applies to the names comp1 comp2...:

Syntax

... { comp_name[-sub_comp][{+off(len)}|{->attr}] } | { (name) } ...

Effect

The following alternatives are available for specifying components:

◉ Directly specifying the name comp_name of a component.
     ◉ If the data type of the components is character-like and flat, an offset/length +off(len) can be appended to the name of the component (as in substring access) to access subareas of the component. Only directly specified numbers or constants can be specified for off and len.
     ◉ If the component is structured, the structure component selector - can be used to access the sub_comp components of the substructure.
     ◉ If the component has a reference type, the object component selector -> can be used to access all visible attributes attr of the referenced object. In this case, a table component can be specified more than once.

◉ Specifying a parenthesized character-like data object name, which contains the name of the component when the statement is executed.
     ◉ The name of the component in name can contain an offset/length.
     ◉ The object component selector -> can be specified in name to read attributes, but only those attributes can be addressed that can be identified statically.
     ◉ name is not case-sensitive. If name only contains blanks, this specified component is ignored when the statement is executed. If name contains a nonexistent component, a non-handleable exception is raised.
◉  Specifying the pseudo component table_line to address the whole table row as a component.

Notes

◉ A component can only be specified more than once if it has a reference type. Further components, however, can be specified alongside table_line. This is necessary, for example, if the row type of the internal table is an object reference and the value of the reference and the value of an attribute of the object are to be specified at the same time.

◉ If the table has a non-structured row type, the pseudo comment table_line can be addressed as the only component.

◉ The pseudo component table_line is a reserved name. In ABAP and in ABAP Dictionary, no structure components called table_line can be declared.

◉ If the row type of the internal table cannot be statically identified, the components can usually only be specified dynamically and not directly.

◉ The components specified must not be elementary.

◉ If the data type allows it (character-like and flat), an offset/length can also include adjacent components

◉ A customizing include must not be specified as a component if it is empty.

Example

Sorting by a dynamically specified component. An incorrect name produces a runtime error.

DATA(column) = `carrid`.
cl_demo_input=>request( CHANGING field = column ).

DATA itab TYPE TABLE OF spfli WITH EMPTY KEY.

SELECT *
       FROM spfli
       INTO TABLE @itab.

SORT itab BY (column).

cl_demo_output=>display( itab ).

7.2 13 Internal Tables - keyname

The name of the table key can be specified in a range of statements and in table expressions for the editing of internal tables. The table key name can used to access a table row or to control processing. The following syntax applies to the name keyname:

Syntax

... key_name | (name)  ...

Effect

The name of a table key can either be specified directly, as key_name, or dynamically, as the contents of a parenthesized character-like data object name (not case-sensitive). If the name is specified directly, this name must clearly indicate that the internal table has this key. With generic data types, the name can only be specified dynamically. If the name is specified dynamically and is incorrect, this raises a non-handleable exception.

The following can be specified:

◉ a secondary table key using its name
◉ the primary table key using its predefined name primary_key
◉ the primary table key using an alias
◉ the table key used in a LOOP-loop using its predefined name loop_key. In this case, the statement must be executed within the loop.

Notes

◉ Normally secondary table keys are specified. Only if searches are to be performed explicitly in a table expression using the primary table key does the key need be specified using its predefined name primary_key or an alias name.
◉ When specifying the primary table key using primary_key, it is important to note that it may be empty for standard tables. This can produce unexpected behavior in statements where the key is used to specify the rows to be processed.

Example

Dynamic specification of the key according to which the LOOP loop is executed. The loop can be executed with the entries skey and primary_key (not case sensitive). Any other entries produce a runtime error.

DATA(key) = `skey`.
cl_demo_input=>request( CHANGING field = key ).

DATA itab TYPE TABLE OF i
          WITH NON-UNIQUE KEY primary_key COMPONENTS table_line
          WITH NON-UNIQUE SORTED KEY skey COMPONENTS table_line.

itab = VALUE #( ( 3 ) ( 2 ) ( 1 ) ).

LOOP AT itab INTO DATA(wa) USING KEY (key).
  cl_demo_output=>write( wa ).
ENDLOOP.
cl_demo_output=>display( ).

7.3 Expressions and Functions for Internal Tables

Expressions and functions for reading internal tables can be processed in many operand positions.

- Table Expressions
- Table Functions
- FOR - Table Iterations
- FILTER - Filter Operator

Mesh path expressions are a special form of table expressions.

7.3.1 table_exp - Table Expressions

Syntax

... itab[ itab_line ][-comp|[ ... ]|->comp] ...

Effect

A table expression consists of an internal table itab, followed directly by a row (itab_line) specified in square brackets [ ]. A chaining -comp|[ ... ]|->comp can be appended to this row.  The expression searches for the specified row in the internal table.

◉ If no chaining is specified, the whole row is returned as the result of the corresponding row type.
◉ If a chaining, the following is possible:
     ◉ The structure component selector - can be used to access the component comp in the read row.
     ◉ Square brackets [ ... ] can be used to chain multiple table expressions.
     ◉ The object component selector -> can be used to access a component comp of the object referenced by the preceding expression.

The result of a table expression can be used as follows:

◉ Reading positions
     ◉ A table expression can be specified in general expression positions and functional operand positions with an appropriate operand type. The result is used here as an operand. The type of the result cannot be controlled in these operand positions using constructor operators. A default value can be specified for rows not found.
     ◉ A table expression can be specified as a special expression variant for the memory area in the statement ASSIGN.
     ◉ A table expression can be specified as an argument of the table function line_index and the predicate function line_exists.
◉ Writing positions
     ◉ A table expression can be specified as a writable expression in result positions. Once found, the row in question can be modified directly here.

The internal table itab must be specified directly using its name, a field symbol, or a dereferenced data reference as described under Reading Positions. In a table with header line, the table body is addressed and not the header line.

If the specified row is not found, a handleable expression of the class CX_SY_ITAB_LINE_NOT_FOUND is raised in all operand positions, except when

◉ a default value is specified in the definition of the type of the result,
◉ a table expression is used in the statement ASSIGN, where sy-subrc is set to the value 4,
◉ when used in the predicate function line_exists, where the truth value "false" is returned,
◉ when used in the table function line_index, where the value 0 is returned.

Notes

◉ In table expressions, the empty square brackets [] cannot be specified behind itab. In other operand positions, these empty brackets distinguish the table body from header lines.

◉ Functions and constructor expressions cannot currently be specified for itab, but the table expressions shown under Chainings are possible.

◉ A table expression cannot be followed directly by a specified offset/length +off(len), but this is possible after a chaining whose final place is a suitable structure component after a structure component selector.

◉ Duplicate selections (multiple reads performed on the same row of an internal table in different expressions) must be avoided. In these cases, a selection should be made before the statement and the result referenced by a field symbol or reference variable.

◉ The exception class CX_SY_ITAB_LINE_NOT_FOUND contains attributes for displaying the row number in the index or key used when a row cannot be accessed. If only index accesses are used in statements with multiple table expressions, it is not possible to distinguish which expression was unsuccessful.

◉ Each table expression can be view as a short form for a variant of the statement READ TABLE that enables reads to be performed on rows of internal tables in operand positions.

◉ In writing positions, the same restrictions with respect to modifying key fields apply to table expressions whose result is a field symbol or temporary reference variable as to other field symbols or data reference variables that point to rows of internal tables. More specifically, table expressions that return a row of a sorted table or hashed table to writing positions (like the left side of an assignment) or as actual parameters for output parameters always raise an exception.

◉ Unlike READ TABLE, a table expression does not modify the value of the system field sy-tabix (except when used in the statement ASSIGN).

◉ Like the statement READ TABLE, a table expression is a single row read. If multiple rows of an internal table are to be read, the statement LOOP or a FOR expression generally displays better performance than using table expressions in a loop.

◉ Mesh path expressions are a special form of table expression that can be used in exactly the same way as table expressions.

Example

The content of the component carrid of the row of the internal table carrier_tab is passed to the method get_spfli. In this table, the component carrname of the secondary key name has a specific value.

DATA carrier_tab TYPE HASHED TABLE OF scarr
                 WITH UNIQUE KEY carrid
                 WITH NON-UNIQUE SORTED KEY name COMPONENTS carrname.

SELECT * FROM scarr INTO TABLE @carrier_tab.

TRY.
    DATA(flight_tab) = cl_demo_spfli=>get_spfli(
      carrier_tab[ KEY name
                   COMPONENTS carrname = 'United Airlines' ]-carrid ).
    cl_demo_output=>display( flight_tab ).
  CATCH cx_sy_itab_line_not_found.
    cl_demo_output=>display( `Nothing found` ).
ENDTRY.

Example

Here, the first calculation with table rows is a bad example of how to use table expressions. The same selection is made three times in the same statement. The second calculation shows how this can be avoided by using an assignment to a field symbol.

DATA itab TYPE TABLE OF i.
itab = VALUE #( ( 3 ) ( 5 ) ).

"Bad example
itab[ table_line = 3 ] =
  itab[ table_line = 3 ] * itab[ table_line = 3 ].

"Good example
ASSIGN itab[ table_line = 5 ] TO FIELD-SYMBOL(<fs>).
<fs> = <fs> * <fs>.

Examples

The program DEMO_TABLE_EXPRESSIONS shows further examples of how to use table expressions.

Exceptions

Handleable Exceptions

CX_SY_ITAB_LINE_NOT_FOUND

◉ Cause: The specified table row was not found.
Runtime error: ITAB_LINE_NOT_FOUND

7.3.2 Table Functions

Table functions are part of the predefined functions. The following table functions are possible:

- lines - Row function
- line_index - Index function

Furthermore, the string function concat_lines_of expects an internal table as an argument; the predicate function line_exists expects a table expression or a mesh path expression.

Note

Table functions can be used in suitable operand positions, for example directly as an operand of arithmetic or relational expressions.

7.3.3 FOR - Table Iterations

Syntax Forms

1. ... FOR wa|<fs> IN itab [INDEX INTO idx] [ cond] [let_exp]  ...

2. ... FOR GROUPS [group|<group>] OF wa|<fs> IN itab
          [INDEX INTO idx] [cond]
          GROUP BY group_key
          [ASCENDING|DESCENDING [AS TEXT]]
          [WITHOUT MEMBERS]
          [let_exp] ...

3. ... FOR { wa|<fs> IN GROUP group [INDEX INTO idx] [ WHERE ( log_exp )] }
        | { GROUPS OF
            wa|<fs> IN GROUP group [INDEX INTO idx] [ WHERE ( log_exp )]
            GROUP BY group_key
            [ASCENDING|DESCENDING [AS TEXT]]
            [WITHOUT MEMBERS] } [ let_exp] ...

Effect

These syntax forms of an iteration expression using FOR perform table iterations.

◉ When used in a constructor expression with the reduction operator REDUCE, these forms are known as table reductions.
◉ When used in a constructor expression with the instance operator NEW or with the value operator VALUE for internal tables, these forms are known as table comprehensions.

A FOR expression like this evaluates the content of an internal table and its result can be used to construct the result of the wrapper constructor expression. The three variants of a FOR expression for internal tables work in the same way as the following variants of the statement LOOP AT itab:

◉ The first variant FOR ... IN itab works in the same way as the regular row variant LOOP AT itab without the addition GROUP BY.

◉ The second variant FOR GROUPS ... OF works in the same way as the variant for grouping rows with the addition GROUP BY.

◉ The third variant FOR ... IN GROUP works in the same way as the variant LOOP AT GROUP for member loops.

The variables or field symbols declared in the FOR expressions are local in these expressions. The local data from all outer FOR expressions can be used when their values are defined. As an option, LET expressions let_exp can be used to define local helper fields at the end of each FOR expression.

The system field sy-tabix is not set by a FOR expression. The addition INDEX INTO can be used instead.

Note

Multiple sequential FOR expressions with different variants (including the conditional iteration) can be specified in a constructor expression. These expressions then work in the same way as nested loops.

Example

Generates an internal table jtab from an internal table itab using a table comprehension. Generates a text string str from the internal table jtab using a table reduction. The result in str is the character string 1, 9, 25.

TYPES itab TYPE TABLE OF i WITH EMPTY KEY.

DATA(itab) = VALUE itab( ( 1 ) ( 3 ) ( 5 ) ).

DATA(jtab) = VALUE itab( FOR wa IN itab ( ipow( base = wa exp = 2 ) ) ).

DATA(str) = REDUCE string( INIT s = ``
                           FOR wa IN jtab
                           NEXT s = COND #( WHEN s = `` THEN |{ wa }|
                                            ELSE |{ s }, { wa }| ) ).

7.3.4 FILTER - Filter Operator

Syntax Forms

Basic Form

1. ... FILTER type( itab [EXCEPT] [USING KEY keyname]
                        WHERE c1 op f1 [AND c2 op f2 [...]] ) ...

Filter table

2. ... FILTER type( itab [EXCEPT] IN ftab [USING KEY keyname]
                        WHERE c1 op f1 [AND c2 op f2 [...]] ) ...

Addition:

... EXCEPT

Effect

A constructor expression with the component operator FILTER creates a result of a table type specified using type. ‎The rows are taken from an existing internal table itab in accordance with the condition after WHERE, converted to the row type of type, and inserted into the target table in accordance with the rules of INSERT ... INTO TABLE.

- In the basic form the condition is created with single values.
- When using a filter table, the condition is created with values from the filter table ftab.

The following can be specified for type:

- A non-generic table type.
- The # character as a symbol for the operand type. If the data type required in an operand position is not unique and not known completely, the type of itab is used if known.

itab is a functional operand position. The row type of itab must be convertible to the row type of the target type type. USING KEY can (or must) be used to specify a table key for evaluating itab or ftab (depending on the variant).

Notes

- Table filtering can also be performed using a table comprehension or a table reduction with an iteration expression for table iterations with FOR. The operator FILTER provides a shortened format for this special case and is more efficient to execute.
- A table filter constructs the result row by row. If the result contains almost all rows in the source table, this method can be slower than copying the source table and deleting the surplus rows from the target table.

Addition

... EXCEPT

Effect

If EXCEPT is not specified, those rows from itab are used that meet the WHERE condition. If EXCEPT is specified, those rows from itab are used that do not meet the WHERE condition.

Note

The addition EXCEPT is not the same as a negation of the WHERE condition, particularly in the variant with a filter table.

Example

Using the addition EXCEPT in the basic form of the FILTER operator.

DATA messages TYPE SORTED TABLE OF t100 WITH NON-UNIQUE KEY sprsl.
SELECT *
       FROM t100
       WHERE arbgb = 'SABAPDEMOS'
       ORDER BY msgnr
       INTO TABLE @messages.

cl_demo_output=>display(
  FILTER #( messages EXCEPT WHERE sprsl = 'D' ) ).

7.4 System Class for Internal Tables

The class CL_ABAP_ITAB_UTILITIES contains the following methods:

- FLUSH_ITAB_KEY and FLUSH_ITAB_KEYS can be used to update individual secondary table keys or all secondary table keys of an internal table explicitly. Otherwise the update takes place as a delayed or lazy update.
- READ_BINARY_SEARCH_CHECK can be used with the addition BINARY SEARCH to activate checks on the required sort for test purposes. If the required sort does not exist, the runtime error ITAB_ILLEGAL_ORDER is raised when the check is activated.
- HAS_COLLECT_HASH_ADMIN can be used to check whether the temporary hash management of the statement COLLECT exists for a standard table.
- INDEX_HEALTH_CHECK can be used to check the consistency of primary and secondary table indexes.
- VIRTUAL_SORT can be used to apply virtual sorts to a set of internal tables with the same number of rows. The internal tables are treated internally like a single combined table containing all the columns of the involved internal tables. The result is an array of row numbers of the virtually sorted combined table

4.5 Internal Tables - Performance Notes

Table sharing

When assignments are made between internal tables of the same type whose row type does not contain any table types (for performance reasons), only the internal administration functions are passed to the table body. Table sharing is revoked only when change access to one of the tables involved is initiated. At this point, the actual copy process takes place.

Initial Allocated Memory Area

Internal tables are dynamic data objects whose area in the memory is increased block by block. The size of the first block in the memory can be controlled in the declaration of an internal table using the additions INITIAL SIZE and the obsolete OCCURS. If the first block is no longer enough, further blocks are created using an internal duplication strategy until a maximum size is reached. All additional blocks are then created with a constant size of between 8 and 16 KB.

In general, the system can be left to determine the size of the first block (by not specifying INITIAL SIZE, or by entering the value 0). In this case, when the first rows are entered in an internal table, a suitable block size is chosen.

Entering a concrete value greater than 0 after INITIAL SIZE is only practical if it is known in advance how many entries are to be made in the table, and the first block is therefore to be created with the most suitable dimensions. This can be particularly important for internal tables that are components of other internal tables, and which only contain a few rows (no more than around 5).

To avoid excessive memory demands, the system ignores values that produce memory demands greater than the constant block size.

Index Administration

In an index table, the logical order of the table entries is not generally consistent with the physical order of the entries in the main memory. In this case, the logical order is no longer administrated by a physical index, but by a logical index instead. The same is basically true for the secondary table indexes used to manage secondary sorted keys.

Use of the logical index produces additional memory requirements, and index maintenance places a high demand on resources (time and memory) when inserting or deleting table rows. The resource requirements rise in proportion to the number of remaining rows after the insertion or deletion position. For very large internal tables, this can result in considerable demands on performance at runtime.

The logical index is not created until the moment at which it is required, in other words, when a row is inserted before another row, if the order of the table rows is changed, or a row other than the last row is deleted. A logical index is not required if an internal table is filled using only APPEND, and if only its last row(s) is/are deleted using DELETE.

Note

In contrast to filling a table with INSERT, appending rows with APPEND does not require any resources for index maintenance. It is therefore preferable to use APPEND instead of INSERT to create a standard table. This is possible if the order of the entries is not important, or if entries can be appended in the correct order.

Block Processing of Table Rows

If whole row areas of a table can be processed at once, this should not be done in rows, but using block operations instead. Block operations are possible using the FROM and TO additions of the statements INSERT, APPEND and DELETE. Block operations are also more efficient than single record operations when reading from or modifying database tables with Open SQL statements with the additions FROM|APPENDING|TO TABLE.

Selective Data Transport

If, when reading table rows using READ TABLE or LOOP AT, a work area is used or table rows can be changed using MODIFY instead of by direct access, the TRANSPORTING addition can be used to prevent unnecessary assignments of table components to the work area. This can lead to a noticeable acceleration in performance, particularly if table-like components are excluded from processing.

Using Secondary Keys

The use of secondary table keys should be planned and executed with care. The time gained when accessing individual rows should not be lost again by the increased memory and time requirements for managing the secondary keys. Secondary keys are generally recommended for internal tables that are filled once and rarely changed during program execution.

Example

The program DEMO_SECONDARY_KEYS demonstrates how a secondary table key is specified and the resulting performance gain.

8. Meshes

Meshes are instances of the following defined mesh types:

TYPES BEGIN OF MESH
  ...
  TYPES snode ... ASSOCIATION _assoc
                  TO tnode ON tcomp1 = scomp1 [AND ...].
  ...
TYPES END OF MESH

These are special structures. Their components (referred to as nodes) are either structured internal tables or reference variables that point to structured internal tables.

The addition ASSOCIATION makes it possible to define Mesh associations (using ON conditions) between start nodes and target nodes of the mesh. These semantic relationships between the mesh nodes can be evaluated in mesh paths. The mesh paths can be used in expressions and processing statements for meshes.

◉ Mesh paths
◉ Results of mesh paths

Notes

◉ A mesh node is addressed in the same way as a normal structure component; by using the structure component selector (-) or the object component selector (->). The associations are evaluated in mesh paths. If no path is specified, the mesh node is handled in the same way as a normal structure component. The same applies to field symbols or reference variables that point to mesh nodes. The statement MOVE-CORRESPONDING also handles a mesh like a normal structure.

◉ In a simple case, the relationships (implemented using associations) between mesh nodes can be compared to foreign key dependencies. Here the start node of an association is the check table and the target node is the foreign key table. The use of mesh paths makes it easier to evaluate these relationships, without the need to program the relevant selections.

Example

The program DEMO_MESH_PACK demonstrates an application based on meshes, which analyses the repository objects in packages. All the possible uses of meshes are demonstrated and can be precisely analyzed in the debugger.

◉ Meshes - Mesh Paths

Syntax

... {mesh-|<mesh>-|mesh_ref->}rnode \_associ[ ... ] \_assoc1[ ... ]\_assoc2[ ... ] ...

Extras:

1. ... {mesh-|<mesh>-|mesh_ref->}rnode

2. ... \_associ[ source [cond] ]

3. ... \_assoc1[ [cond] ]\_assoc2[ [cond] ] ...

Effect

Specifies a mesh path in statements and expressions used to process meshes. A mesh path is always constructed as follows:

- A root node rnode
- An initial association \_associ between a root node and a follow-on node
- Optional further associations \_assoc1, \_assoc2, ... whose source nodes can be the follow-on nodes of preceding associations.

When a mesh path is specified, it is not significant whether the address nodes of the mesh are internal tables or references to internal tables. When a mesh path is evaluated, references to internal tables are dereferenced automatically. A mesh path describes a set of rows in its final path node as its result.

Note

A mesh path cannot exit a mesh. Each follow-on node is part of the same mesh as the root node. Meshes can, however, be nested by specified a mesh path expression as the source of the initial association.

Addition 1

... {mesh-|<mesh>-|mesh_ref->}rnode

Effect

Root node rnode of a mesh path. rnode is a nodes of a mesh type that must be the start or target node of an association of the mesh type. The same options are available when specifying the mesh as when addressing regular structure components:

- mesh-rnode

mesh is a mesh, that is a data object of the mesh type in question. The root node is addressed using the structure component selector (-).

- <mesh>-rnode

<mesh> is a field symbol typed with the mesh type and to which a mesh is assigned. The root node is addressed using the structure component selector (-).

- mesh_ref->rnode

mesh_ref is a reference variable whose static type is the mesh type and which points to a mesh. The root node is addressed using the object component selector (->).

Example

Defines a mesh type mesh with internal tables and a mesh type refmesh with references to internal tables as components. A data object mesh has the type mesh and a data object meshref points to a data object of type refmesh. The root node of the first mesh path is mesh-node1. The structure component selector is used here. The root node of the second mesh path is node1 in the anonymous data object of type refmesh. The object component selector is used here. The references within the mesh are dereferenced implicitly.

TYPES:
  BEGIN OF MESH mesh,
    node1 TYPE itab1 ASSOCIATION _assoc2 TO node2 ON ...
    node2 TYPE itab2,
  END OF MESH mesh,
  BEGIN OF MESH refmesh,
    node1 TYPE REF TO itab1 ASSOCIATION _assoc2 TO node2 ON ...
    node2 TYPE REF TO itab2,
  END OF MESH refmesh.

DATA(mesh) =    VALUE mesh( ).
DATA(meshref) = NEW refmesh( VALUE refmesh( ) ).

... mesh-node1\_assoc2[ ... ] ...
... meshref->node1\_assoc2[ ... ] ...

◉ Meshes - Mesh Path Result

9. Attributes of Data Objects
10. Streaming 

No comments:

Post a Comment