Using Control Structures in ABAP L5

After completing this lesson, you will be able to:

Implement conditional branching.

Handle Exceptions.

Implement Iterations.

Conditional Branching

A conditional branching is a control structure that allows you to make the execution of code dependent on logical conditions.

The most common conditional branching consists of a pair of keywords IF and ENDIF. The code block between IF and ENDIF is only executed, if the condition after IF is fulfilled.

You can add more code blocks by extending the IF … ENDIF structure with up to one keyword ELSE and an arbitrary number of keywords ELSEIF. By adding keyword ELSE you ensure that always exactly one of the code blocks is executed. If ELSE is missing it can happen that none of the code blocks is executed.

The code block to be executed is determined as follows:

First, the IF condition is evaluated. If it is fulfilled, the related code block is executed and the program continues after ENDIF.

Only if the IF condition is not fulfilled, the condition after the first ELSEIF is evaluated. If it is fulfilled, the related code block is executed and the program continues after ENDIF.

This is done consecutively for all ELSEIF conditions. If none of the conditions is fulfilled and the structure contains ELSE, the code block after ELSE is executed. Otherwise the none of the code blocks is executed.

Hint

As opposed to many other programming languages, ABAP requires a delimiter (.) after each of the logical conditions and even after keyword ELSE.

Logical conditions are a combination of comparisons, logical operations, expressions and functions that the runtime system evaluates to decide whether the condition is true or false.

The most common use-case for logical conditions is after keywords IF or ELSEIF in an IF ... ENDIF. structure.

The first example is a simple comparison: The condition is true if the two data objects x and y have the same value.

The second example is a bit more sophisticated: Either the value of x is greater than or equal to y and less than twice the value of y or it is less than or equal to y and greater than twice the value of y.

The third example makes use of arithmetic function abs( ) and logical expression BETWEEN <expression1> AND <expression2> . The condition is true if the absolute value of x lies between the absolute value of y and the absolute value of two times y.

For simple value comparisons you can use operators =, <>, >, <, >=, and <=. You can not only compare the values of data objects, but the values of many other expressions, like the arithmetic expression 2 * y in the example.

Note

ABAP uses the same symbol (=) for value assignments and for value comparisons. The distinction is made based on the position.

You can use operators AND and OR to combine logical expressions and operator NOT to negate an expression. Without brackets, NOT binds stronger than AND and AND stronger than OR.

ABAP knows some special logical expressions:

<data object> IS INITIAL is true if <data object> contains its type-specific initial value

<data object> IS NOT INITIAL is true if <data object> contains a value that is different from the type-specific initial value

<data object> BETWEEN <expression1> AND <expression2>

Some special ABAP functions are predicate functions. This means that they are logical conditions themselves. Contains( ) is a function that compares character-like values and line_exists( ) performs an existence check for a row in an internal table.

A second technique for conditional branching is the CASE … WHEN .. ENDCASE control structure.

Conditional branching with CASE .. ENDCASE is a special case of the more general branching with IF … ENDIF. You can use CASE in situations where the branching depends on the value of a single data object, which you consecutively compare to a set of possible values, using an equals comparison each time.

In the example, the value of data object number is compared to values 1 and 2. If the value equals 1, <code_block_1> is executed and if the value euqals 2, <code_blocl_2> is executed instead. For any other value, the code block after WHEN OTHERS is executed.

Any conditional branching with CASE … ENDCASE could be implemented with an IF … ENDIF structure, as well. This is illustrated with the example on the right.

Hint

You should use CASE … ENDCASE when dealing with the special case to increase readability of your code.

Try It Out: Conditional Branching

Like in the first exercise of this course, create a new global class that implements interface IF_OO_ADT_CLASSRUN.

Copy the following code snippet to the implementation part of method if_oo_adt_classrun~main( ):

Code Snippet

Copy code

Switch to dark mode

* Declarations

**********************************************************************

CONSTANTS c_number TYPE i VALUE 0.

* CONSTANTS c_number TYPE i VALUE 1.

* CONSTANTS c_number TYPE i VALUE 2.

* CONSTANTS c_number TYPE i VALUE -1.

* CONSTANTS c_number TYPE i VALUE -2.

* Example 1: Simple IF ... ENDIF.

**********************************************************************

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

out->write( `Example 1: Simple IF ... ENDIF.` ).

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

IF c_number = 0.

out->write( `The value of C_NUMBER equals zero` ).

ELSE.

out->write( `The value of C_NUMBER is NOT zero` ).

ENDIF.

* Example 2: Optional Branches ELSEIF and ELSE

**********************************************************************

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

out->write( `Example 2: Optional Branches ELSEIF and ELSE` ).

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

IF c_number = 0.

out->write( `The value of C_NUMBER equals zero` ).

ELSEIF c_number > 0.

out->write( `The value of C_NUMBER is greater than zero` ).

ELSE.

out->write( `The value of C_NUMBER is less than zero` ).

ENDIF.

* Example 3: CASE ... ENDCASE

**********************************************************************

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

out->write( `Example 3: CASE ... ENDCASE` ).

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

CASE c_number.

WHEN 0.

out->write( `The value of C_NUMBER equals zero` ).

WHEN 1.

out->write( `The value of C_NUMBER equals one` ).

WHEN 2.

out->write( `The value of C_NUMBER equals two` ).

WHEN OTHERS.

out->write( `The value of C_NUMBER equals non of the above` ).

ENDCASE.

Press CTRL + F3 to activate the class and F9 to execute it as a console app.

Analyze the console output. Play around with the source code to get familiar with the concepts; Uncomment different declarations of constant c_number with different values to see, which branches of the code get executed.

Exception Handling

Exceptions

In ABAP, an exception is an error situation during execution of an ABAP program. An exception is raised by the code that detects the error situation.

Depending on who raises the exception, we distinguish between system exceptions and application exceptions.

Without further measures exceptions result in runtime errors. A runtime error terminates the program and is documented by default in a short dump.

You can avoid the runtime error if the exception in question is catchable. A catchable exception can be treated in the program using statements TRY … CATCH … ENDTRY.

All application exceptions and many system exceptions are catchable. Later in this course you will learn how to raise application exceptions. At this point we will focus on the handling of catchable system exceptions.

The following video shows some examples of runtime errors.

Exception Handling

To prevent a program from terminating because of a catchable exception, you have to surround the code from where the exception originates with statements TRY and ENDTRY. By doing so, the code becomes part of the TRY block of the TRY … ENDTRY structure.

Before the ENDTRY statement you have to add a CATCH statement followed by the ID of the exception you want to handle. Optionally, you can add more than one CATCH statement to handle several different exceptions. Each CATCH statement should be followed by code to handle this exception. This code is called the CATCH block.

When program execution reaches the TRY statement, it continues with the code in the TRY block. Three things can happen then:

If no exception is raised during the TRY block, the CATCH blocks are ignored. Execution continues after the ENDTRY statement.

If an exception is raised during the TRY block, for which a matching CATCH exists, execution of the TRY block is terminated and the CATCH block for this exception is executed. Afterward, execution continues after the ENDTRY statement.

If an exception is raised during the TRY block for which no matching CATCH exists, the program terminates with a runtime error.

Now that you have learned about exceptions, let's see how you can handle them.

Try It Out: Exception Handling

Like in the first exercise of this course, create a new global class that implements interface IF_OO_ADT_CLASSRUN.

Copy the following code snippet to the implementation part of method if_oo_adt_classrun~main( ):

Code Snippet

Copy code

Switch to dark mode

* Declarations

**********************************************************************

DATA result TYPE i.

DATA numbers TYPE TABLE OF i.

* Preparation

**********************************************************************

APPEND 123 TO numbers.

* Example 1: Conversion Error (no Number)

**********************************************************************

CONSTANTS c_text TYPE string VALUE 'ABC'.

* CONSTANTS c_text TYPE string VALUE '123'.

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

out->write( `Example 1: Conversion Error` ).

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

TRY.

result = c_text.

out->write( |Converted content is { result }| ).

CATCH cx_sy_conversion_no_number.

out->write( |Error: { c_text } is not a number!| ).

ENDTRY.

* Example 2: Division by Zero

**********************************************************************

CONSTANTS c_number TYPE i VALUE 0.

* CONSTANTS c_number TYPE i VALUE 7.

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

out->write( `Example 2: Division by Zero` ).

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

TRY.

result = 100 / c_number.

out->write( |100 divided by { c_number } equals { result }| ).

CATCH cx_sy_zerodivide.

out->write( `Error: Division by zero is not defined!` ).

ENDTRY.

* Example 3: Itab Error (Line Not Found)

**********************************************************************

CONSTANTS c_index TYPE i VALUE 2.

* CONSTANTS c_index TYPE i VALUE 1.

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

out->write( `Example 3: Line Not Found` ).

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

TRY.

result = numbers[ c_index ].

out->write( |Content of row { c_index } equals { result }| ).

CATCH cx_sy_itab_line_not_found.

out->write( `Error: Itab has less than { c_index } rows!` ).

ENDTRY.

* Example 4: Combination of Different Exceptions

**********************************************************************

* CONSTANTS c_char TYPE c LENGTH 1 VALUE 'X'.

* CONSTANTS c_char TYPE c length 1 value '0'.

CONSTANTS c_char TYPE c LENGTH 1 VALUE '1'.

* CONSTANTS c_char TYPE c length 1 value '2'.

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

out->write( `Example 4: Combination` ).

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

TRY.

result = numbers[ 2 / c_char ].

out->write( |Result: { result } | ).

CATCH cx_sy_zerodivide.

out->write( `Error: Division by zero is not defined` ).

CATCH cx_sy_conversion_no_number.

out->write( |Error: { c_char } is not a number! | ).

CATCH cx_sy_itab_line_not_found.

out->write( |Error: Itab contains less than { 2 / c_char } rows| ).

ENDTRY.

Press CTRL + F3 to activate the class and F9 to execute it as a console app.

Analyze the console output. Play around with the source code to get familiar with the concept:

Comment the exception handling to make the system raise a runtime error.

Change the values of constants c_number, c_text, c_index, and c_charin a way that no exceptions are raised.

Iterations

Iterations are control structures that define a block of code which is executed several times.

The simplest form of iteration consists of a code block surrounded by pair of statements DO and ENDDO. Without further measures this establishes an endless loop which must be avoided by one of the following possibilities:

Specified number of iterations

By extending the DO statement with an integer expression followed by keyword TIMES, you can specify explicitly how often the code block is to be iterated. The integer expression can be as simple as number literal, but also arithmetic calculations are an option. If the value of the expression equals 0, the code block between DO and ENDDO is not executed at all and the program immediately continues with the code after ENDDO.

Abort based on a logical condition

You can abort an iteration any time using the EXIT statement. The program then continues with the code after ENDDO. Be aware that outside of iterations EXIT has a different effect. There it terminates the processing of the current processing block, for example the current method.

Usually, EXIT is surrounded by IF and ENDIF to terminate the iteration depending on an abort condition. Be aware that such iterations can turn into endless loops, if the abort condition never gets true.

Of course it is possible to combine the two techniques, that is, explicitly specify the number of iterations and then leave the iteration with EXIT. Thus the number of iterations becomes a maximum number that might not be reached at runtime.

Based on an internal table

A third type of iteration is the LOOP … ENDLOOP structure that is used to consecutively read the rows of an internal table. Here, the number of iterations is determined by the number of rows in the internal table.

In the code block between DO and ENDDO, you can implement read-accesses to ABAP built-in data object sy-index. This integer variable serves as an iteration counter, that is, the ABAP runtime increases it by one at the beginning of each new iteration.

In contradiction to what you might be used to from other programming languages, sy-index starts with value 1 during the first iteration.

ABAP built-in variable sy-tabix can fulfill a similar purpose for iterations with LOOP. But be aware that strictly speaking sy-tabix is not really a counter but it identifies the position of the table row that is processed in the current iteration. Later we will see the difference when not all rows of an internal table are processed in a LOOP … ENDLOOP structure.

Try It Out: Iterations

Like in the first exercise of this course, create a new global class that implements interface IF_OO_ADT_CLASSRUN.

Copy the following code snippet to the implementation part of method if_oo_adt_classrun~main( ):

Code Snippet

Copy code

Switch to dark mode

* Declarations

**********************************************************************

CONSTANTS c_number TYPE i VALUE 3.

* CONSTANTS c_number TYPE i VALUE 5.

* CONSTANTS c_number TYPE i VALUE 10.

DATA number TYPE i.

* Example 1: DO ... ENDDO with TIMES

**********************************************************************

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

out->write( `Example 1: DO ... ENDDO with TIMES` ).

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

DO c_number TIMES.

out->write( `Hello World` ).

ENDDO.

* Example 2: DO ... ENDDO with Abort Condition

**********************************************************************

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

out->write( `Example 2: With Abort Condition` ).

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

number = c_number * c_number.

" count backwards from number to c_number.

DO.

out->write( |{ sy-index }: Value of number: { number }| ).

number = number - 1.

"abort condition

IF number <= c_number.

EXIT.

ENDIF.

ENDDO.

Press CTRL + F3 to activate the class and F9 to execute it as a console app.

Analyze the console output. Play around with the source code to get familiar with the concepts; Uncomment different declarations of constant c_number to see how the different values affect the result.

After completing this lesson, you will be able to: Implement conditional branching. Handle Exceptions. Implement Iterations.

Conditional Branching

A conditional branching is a control structure that allows you to make the execution of code dependent on logical conditions. The most common conditional branching consists of a pair of keywords IF and ENDIF.

The code block between IF and ENDIF is only executed if the condition after IF is fulfilled. You can add more code blocks by extending the IF … ENDIF structure with up to one keyword ELSE and an arbitrary number of keywords ELSEIF. By adding keyword ELSE you ensure that always exactly one of the code blocks is executed. If ELSE is missing, it can happen that none of the code blocks is executed.

The code block to be executed is determined as follows:

  1. First, the IF condition is evaluated. If it is fulfilled, the related code block is executed, and the program continues after ENDIF.

  2. Only if the IF condition is not fulfilled, the condition after the first ELSEIF is evaluated. If it is fulfilled, the related code block is executed, and the program continues after ENDIF.

  3. This is done consecutively for all ELSEIF conditions.

  4. If none of the conditions is fulfilled and the structure contains ELSE, the code block after ELSE is executed. Otherwise, none of the code blocks is executed.

Hint: As opposed to many other programming languages, ABAP requires a delimiter . after each of the logical conditions and even after keyword ELSE.

Logical Conditions

Logical conditions are a combination of comparisons, logical operations, expressions, and functions that the runtime system evaluates to decide whether the condition is true or false. The most common use-case for logical conditions is after keywords IF or ELSEIF in an IF ... ENDIF structure.

Common Comparison Operators:

Operator

Description

=

Equal to

&lt;

Less than

&gt;

Greater than

&lt;=

Less than or equal to

&gt;=

Greater than or equal to

&lt;&gt;

Not equal to

Special Conditions:

  • <data object> IS INITIAL is true if <data object> contains its type-specific initial value.

  • <data object> IS NOT INITIAL is true if <data object> contains a value that is different from the type-specific initial value.

  • <data object> BETWEEN <expression1> AND <expression2> is true if the data object's value is inclusively between expression1 and expression2.

Predicate Functions:
Some special ABAP functions are predicate functions, meaning they are logical conditions themselves:

  • contains( ) is a function that compares character-like values.

  • line_exists( ) performs an existence check for a row in an internal table.

Example 1: Complex Logical Condition

The second example ( x >= y AND x < 2 * y ) OR ( x <= y AND x > 2 * y ) is a bit more sophisticated: Either the value of xx is greater than or equal to yy and less than twice the value of yy, or it is less than or equal to yy and greater than twice the value of yy.

Example 2: Using abs() and BETWEEN

The third example makes use of arithmetic function abs( ) and logical expression BETWEEN <expression1> AND <expression2>. The condition abs( x ) BETWEEN abs( y ) AND abs( 2 * y ) is true if the absolute value of xx lies between the absolute value of yy and the absolute value of two times yy.

CASE … WHEN .. ENDCASE

A second technique for conditional branching is the CASE … WHEN .. ENDCASE control structure. Conditional branching with CASE .. ENDCASE is a special case of the more general branching with IF … ENDIF. You can use CASE in situations where the branching depends on the value of a single data object, which you consecutively compare to a set of possible values, using an equals comparison each time.

In the example, the value of data object number is compared to values 11 and 22. If the value equals 11, <code_block_1> is executed, and if the value equals 22, <code_block_2> is executed instead. For any other value, the code block after WHEN OTHERS is executed.

Any conditional branching with CASE … ENDCASE could be implemented with an IF … ENDIF structure, as well. This is illustrated with the example on the right.

Hint: You should use CASE … ENDCASE when dealing with such special cases to increase readability of your code.

Use Case: Status Classification

DATA current_status TYPE c LENGTH 1.
current_status = 'B'.

CASE current_status.
  WHEN 'A'.
    out->write( 'Status is Active' ).
  WHEN 'B'.
    out->write( 'Status is Blocked' ).
  WHEN 'E'.
    out->write( 'Status is Error' ).
  WHEN OTHERS.
    out->write( 'Unknown Status' ).
ENDCASE.
Try It Out: Conditional Branching

Like in the first exercise of this course, create a new global class that implements interface IF_OO_ADT_CLASSRUN. Copy the following code snippet to the implementation part of method if_oo_adt_classrun~main( ):

* Declarations ***************************************************************
CONSTANTS c_number TYPE i VALUE 0.
* CONSTANTS c_number TYPE i VALUE 1.
* CONSTANTS c_number TYPE i VALUE 2.
* CONSTANTS c_number TYPE i VALUE -1.
* CONSTANTS c_number TYPE i VALUE -2.

* Example 1: Simple IF ... ENDIF.
******************************************************************************
out->write( `--------------------------------` ).
out->write( `Example 1: Simple IF ... ENDIF.` ).
out->write( `-------------------------------` ).
IF c_number = 0.
  out->write( `The value of C_NUMBER equals zero` ).
ELSE.
  out->write( `The value of C_NUMBER is NOT zero` ).
ENDIF.

* Example 2: Optional Branches ELSEIF and ELSE
******************************************************************************
out->write( `--------------------------------------------` ).
out->write( `Example 2: Optional Branches ELSEIF and ELSE` ).
out->write( `--------------------------------------------` ).
IF c_number = 0.
  out->write( `The value of C_NUMBER equals zero` ).
ELSEIF c_number > 0.
  out->write( `The value of C_NUMBER is greater than zero` ).
ELSE.
  out->write( `The value of C_NUMBER is less than zero` ).
ENDIF.

* Example 3: CASE ... ENDCASE
******************************************************************************
out->write( `---------------------------` ).
out->write( `Example 3: CASE ... ENDCASE` ).
out->write( `---------------------------` ).
CASE c_number.
  WHEN 0.
    out->write( `The value of C_NUMBER equals zero` ).
  WHEN 1.
    out->write( `The value of C_NUMBER equals one` ).
  WHEN 2.
    out->write( `The value of C_NUMBER equals two` ).
  WHEN OTHERS.
    out->write( `The value of C_NUMBER equals non of the above` ).
ENDCASE.

Press CTRL + F3 to activate the class and F9 to execute it as a console app. Analyze the console output. Play around with the source code to get familiar with the concepts; Uncomment different declarations of constant c_number with different values to see which branches of the code get executed.

Exception Handling

In ABAP, an exception is an error situation during the execution of an ABAP program. An exception is raised by the code that detects the error situation. Depending on who raises the exception, we distinguish between system exceptions and application exceptions. Without further measures, exceptions result in runtime errors. A runtime error terminates the program and is documented by default in a short dump.

You can avoid the runtime error if the exception in question is catchable. A catchable exception can be treated in the program using statements TRY … CATCH … ENDTRY. All application exceptions and many system exceptions are catchable. Later in this course, you will learn how to raise application exceptions. At this point, we will focus on the handling of catchable system exceptions.

Exception Handling Mechanism

To prevent a program from terminating because of a catchable exception, you have to surround the code from where the exception originates with statements TRY and ENDTRY. By doing so, the code becomes part of the TRY block of the TRY … ENDTRY structure.

Before the ENDTRY statement, you have to add a CATCH statement followed by the ID of the exception you want to handle. Optionally, you can add more than one CATCH statement to handle several different exceptions. Each CATCH statement should be followed by code to handle this exception. This code is called the CATCH block.

Execution Flow of TRY...CATCH...ENDTRY:

  1. When program execution reaches the TRY statement, it continues with the code in the TRY block.

  2. If no exception is raised during the TRY block, the CATCH blocks are ignored. Execution continues after the ENDTRY statement.

  3. If an exception is raised during the TRY block for which a matching CATCH exists, execution of the TRY block is terminated, and the CATCH block for this exception is executed. Afterward, execution continues after the ENDTRY statement.

  4. If an exception is raised during the TRY block for which no matching CATCH exists, the program terminates with a runtime error.

Common Catchable System Exceptions:

Exception Class

Description

Use Case Example

cx_sy_conversion_no_number

Occurs when converting non-numeric text to a number.

DATA(num) = 'ABC'.

cx_sy_zerodivide

Occurs during division by zero.

DATA(result) = 10 / 0.

cx_sy_itab_line_not_found

Occurs when accessing a non-existent line in an internal table.

my_itab[index_not_found].

cx_sy_range_out_of_bounds

Occurs when trying to access an array/string out of its defined bounds.

my_string+10(5) when string length is less than 15.

Use Case: Input Validation and Calculation
Consider a scenario where a user inputs a value from the UI, and your program needs to perform a division. Invalid input (non-numeric) or a value of zero for the divisor could lead to runtime errors. Exception handling ensures the program gracefully handles these situations and provides user-friendly feedback.

DATA lv_input TYPE string VALUE 'abc'.
DATA lv_divisor TYPE i VALUE 0.
DATA lv_result TYPE i.

TRY.
    lv_divisor = lv_input. " Potential cx_sy_conversion_no_number
    lv_result = 100 / lv_divisor. " Potential cx_sy_zerodivide

    out->write( |Division result: { lv_result }| ).

  CATCH cx_sy_conversion_no_number.
    out->write( |Error: Input '{ lv_input }' is not a valid number.| ).
  CATCH cx_sy_zerodivide.
    out->write( |Error: Division by zero is not allowed.| ).
  CATCH cx_root. " General catch for any other exception
    out->write( |An unexpected error occurred.| ).
ENDTRY.
Try It Out: Exception Handling

Like in the first exercise of this course, create a new global class that implements interface IF_OO_ADT_CLASSRUN. Copy the following code snippet to the implementation part of method if_oo_adt_classrun~main( ):

* Declarations ***************************************************************
DATA result TYPE i.
DATA numbers TYPE TABLE OF i.

* Preparation ****************************************************************
APPEND 123 TO numbers.

* Example 1: Conversion Error (no Number)
******************************************************************************
CONSTANTS c_text TYPE string VALUE 'ABC'.
* CONSTANTS c_text TYPE string VALUE '123'.
out->write( `---------------------------` ).
out->write( `Example 1: Conversion Error` ).
out->write( `---------------------------` ).
TRY.
  result = c_text.
  out->write( |Converted content is { result }| ).
CATCH cx_sy_conversion_no_number.
  out->write( |Error: { c_text } is not a number!| ).
ENDTRY.

* Example 2: Division by Zero
******************************************************************************
CONSTANTS c_number TYPE i VALUE 0.
* CONSTANTS c_number TYPE i VALUE 7.
out->write( `---------------------------` ).
out->write( `Example 2: Division by Zero` ).
out->write( `---------------------------` ).
TRY.
  result = 100 / c_number.
  out->write( |100 divided by { c_number } equals { result }| ).
CATCH cx_sy_zerodivide.
  out->write( `Error: Division by zero is not defined!` ).
ENDTRY.

* Example 3: Itab Error (Line Not Found)
******************************************************************************
CONSTANTS c_index TYPE i VALUE 2.
* CONSTANTS c_index TYPE i VALUE 1.
out->write( `-------------------------` ).
out->write( `Example 3: Line Not Found` ).
out->write( `-------------------------` ).
TRY.
  result = numbers[ c_index ].
  out->write( |Content of row { c_index } equals { result }| ).
CATCH cx_sy_itab_line_not_found.
  out->write( `Error: Itab has less than { c_index } rows!` ).
ENDTRY.

* Example 4: Combination of Different Exceptions
******************************************************************************
* CONSTANTS c_char TYPE c LENGTH 1 VALUE 'X'.
* CONSTANTS c_char TYPE c length 1 value '0'.
CONSTANTS c_char TYPE c LENGTH 1 VALUE '1'.
* CONSTANTS c_char TYPE c length 1 value '2'.
out->write( `----------------------` ).
out->write( `Example 4: Combination` ).
out->write( `----------------------` ).
TRY.
  result = numbers[ 2 / c_char ].
  out->write( |Result: { result } | ).
CATCH cx_sy_zerodivide.
  out->write( `Error: Division by zero is not defined` ).
CATCH cx_sy_conversion_no_number.
  out->write( |Error: { c_char } is not a number! | ).
CATCH cx_sy_itab_line_not_found.
  out->write( |Error: Itab contains less than { 2 / c_char } rows| ).
ENDTRY.

Press CTRL + F3 to activate the class and F9 to execute it as a console app. Analyze the console output. Play around with the source code to get familiar with the concept: Comment the exception handling to make the system raise a runtime error. Change the values of constants c_number, c_text, c_index, and c_char in a way that no exceptions are raised.

Iterations

Iterations are control structures that define a block of code which is executed several times. The simplest form of iteration consists of a code block surrounded by pair of statements DO and ENDDO.

Without further measures, this establishes an endless loop which must be avoided by one of the following possibilities:

1. Specified Number of Iterations (DO ... TIMES)

By extending the DO statement with an integer expression followed by keyword TIMES, you can specify explicitly how often the code block is to be iterated. The integer expression can be as simple as a number literal, but also arithmetic calculations are an option. If the value of the expression equals 00, the code block between DO and ENDDO is not executed at all, and the program immediately continues with the code after ENDDO.

Example:

DATA lv_counter TYPE i VALUE 1.
DO 5 TIMES.
  out->write( |Iteration { lv_counter }: Hello World!| ).
  lv_counter = lv_counter + 1.
ENDDO.

DO ( 2 + 1 ) * 2 TIMES. " Loop 6 times
  out->write( 'Calculated iterations.' ).
ENDDO.
2. Abort based on a Logical Condition (DO ... ENDDO with EXIT)

You can abort an iteration any time using the EXIT statement. The program then continues with the code after ENDDO. Be aware that outside of iterations, EXIT has a different effect; there it terminates the processing of the current processing block, for example, the current method. Usually, EXIT is surrounded by IF and ENDIF to terminate the iteration depending on an abort condition. Be aware that such iterations can turn into endless loops if the abort condition never gets TRUE.

Of course, it is possible to combine the two techniques, that is, explicitly specify the number of iterations and then leave the iteration with EXIT. Thus, the number of iterations becomes a maximum number that might not be reached at runtime.

Use Case: Searching for a record or limited processing

DATA lv_target_value TYPE i VALUE 5.
DATA lv_found TYPE abap_bool VALUE abap_false.
DATA lv_current_num TYPE i VALUE 1.

DO.
  out->write( |Checking number: { lv_current_num }| ).
  IF lv_current_num = lv_target_value.
    lv_found = abap_true.
    EXIT. " Exit the loop if target found
  ENDIF.
  IF lv_current_num > 10. " Prevent endless loop, set maximum iterations
    EXIT.
  ENDIF.
  lv_current_num = lv_current_num + 1.
ENDDO.

IF lv_found = abap_true.
  out->write( 'Target value found.' ).
ELSE.
  out->write( 'Target value not found within limits.' ).
ENDIF.
3. Based on an Internal Table (LOOP ... ENDLOOP)

A third type of iteration is the LOOP … ENDLOOP structure that is used to consecutively read the rows of an internal table. Here, the number of iterations is determined by the number of rows in the internal table.

In the code block between LOOP and ENDLOOP, you can implement read-accesses. ABAP built-in data object sy-index is an integer variable that serves as an iteration counter; the ABAP runtime increases it by one at the beginning of each new iteration. In contradiction to what you might be used to from other programming languages, sy-index starts with value 11 during the first iteration.

ABAP built-in variable sy-tabix can fulfill a similar purpose for iterations with LOOP. But be aware that, strictly speaking, sy-tabix is not really a counter but it identifies the position of the table row that is processed in the current iteration. Later we will see the difference when not all rows of an internal table are processed in a LOOP … ENDLOOP structure.

Syntax Variations for LOOP AT:

LOOP AT <internal_table> INTO <work_area>.
" Code block for each row
ENDLOOP.

LOOP AT <internal_table> ASSIGNING <field_symbol>.
" Code block for each row, <field_symbol> points to the current row
ENDLOOP.

LOOP AT <internal_table> REFERENCE INTO <data_reference>.
" Code block for each row, <data_reference> points to the current row
ENDLOOP.

LOOP AT <internal_table> WHERE <condition>.
" Process only rows matching the condition
ENDLOOP.

Distinguishing sy-index and sy-tabix:

  • sy-index: Represents the current pass number of any loop (e.g., DO, LOOP AT). It always starts from 11. If you have nested loops, sy-index will reflect the iteration count of the innermost active loop.

  • sy-tabix: Specifically represents the index of the current row being processed in an internal table during a LOOP AT statement. If the LOOP AT includes a WHERE clause, sy-tabix will still hold the original index of the row in the internal table, not just the count of items processed in the filtered loop.

Example: sy-index vs. sy-tabix with LOOP AT WHERE

DATA lt_numbers TYPE TABLE OF i VALUE '1,2,3,4,5'.
DATA lv_number TYPE i.

out->write( 'Full table loop:' ).
LOOP AT lt_numbers INTO lv_number.
  out->write( |sy-index: { sy-index }, sy-tabix: { sy-tabix }, Value: { lv_number }| ).
ENDLOOP.

out->write( 'Filtered table loop (WHERE value > 2):' ).
LOOP AT lt_numbers INTO lv_number WHERE table_line > 2.
  out->write( |sy-index: { sy-index }, sy-tabix: { sy-tabix }, Value: { lv_number }| ).
ENDLOOP.

Expected Output for the example above:

Full table loop:
sy-index: 1, sy-tabix: 1, Value: 1
sy-index: 2, sy-tabix: 2, Value: 2
sy-index: 3, sy-tabix: 3, Value: 3
sy-index: 4, sy-tabix: 4, Value: 4
sy-index: 5, sy-tabix: 5, Value: 5
Filtered table loop (WHERE value > 2):
sy-index: 1, sy-tabix: 3, Value: 3
sy-index: 2, sy-tabix: 4, Value: 4
sy-index: 3, sy-tabix: 5, Value: 5

Notice how sy-index resets for the second loop and counts from 11 again, while sy-tabix indicates the original position of the row in lt_numbers (e.g., value 33 is at index 33).

Try It Out: Iterations

Like in the first exercise of this course, create a new global class that implements interface IF_OO_ADT_CLASSRUN. Copy the following code snippet to the implementation part of method if_oo_adt_classrun~main( ):

* Declarations ***************************************************************
CONSTANTS c_number TYPE i VALUE 3.
* CONSTANTS c_number TYPE i VALUE 5.
* CONSTANTS c_number TYPE i VALUE 10.
DATA number TYPE i.

* Example 1: DO ... ENDDO with TIMES
******************************************************************************
out->write( `----------------------------------` ).
out->write( `Example 1: DO ... ENDDO with TIMES` ).
out->write( `----------------------------------` ).
DO c_number TIMES.
  out->write( `Hello World` ).
ENDDO.

* Example 2: DO ... ENDDO with Abort Condition
******************************************************************************
out->write( `-------------------------------` ).
out->write( `Example 2: With Abort Condition` ).
out->write( `-------------------------------` ).
number = c_number * c_number. " count backwards from number to c_number.
DO.
  out->write( |{ sy-index }: Value of number: { number }| ).
  number = number - 1. "abort condition
  IF number <= c_number.
    EXIT.
  ENDIF.
ENDDO.

Press `CTRL