From 58af51f4a0d32e7bc6750e55a6cb1ea3f620dc5e Mon Sep 17 00:00:00 2001 From: Daniel Reger <16720986+danrega@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:49:27 +0100 Subject: [PATCH] Add cheat sheet for program flow logic --- 13_Program_Flow_Logic.md | 661 ++++++++ README.md | 14 +- src/zcl_demo_abap_prog_flow_logic.clas.abap | 1456 +++++++++++++++++ ..._abap_prog_flow_logic.clas.locals_def.abap | 11 + src/zcl_demo_abap_prog_flow_logic.clas.xml | 16 + 5 files changed, 2152 insertions(+), 6 deletions(-) create mode 100644 13_Program_Flow_Logic.md create mode 100644 src/zcl_demo_abap_prog_flow_logic.clas.abap create mode 100644 src/zcl_demo_abap_prog_flow_logic.clas.locals_def.abap create mode 100644 src/zcl_demo_abap_prog_flow_logic.clas.xml diff --git a/13_Program_Flow_Logic.md b/13_Program_Flow_Logic.md new file mode 100644 index 0000000..6bf37b2 --- /dev/null +++ b/13_Program_Flow_Logic.md @@ -0,0 +1,661 @@ + + +# Program Flow Logic + +- [Program Flow Logic](#program-flow-logic) + - [Intro](#intro) + - [Expressions and Functions for Conditions](#expressions-and-functions-for-conditions) + - [Control Structures](#control-structures) + - [`IF` Statements](#if-statements) + - [Excursion: `COND` Operator](#excursion-cond-operator) + - [`CASE`: Case Distinctions](#case-case-distinctions) + - [Excursion: `SWITCH` Operator](#excursion-switch-operator) + - [Loops](#loops) + - [`DO`: Unconditional Loops](#do-unconditional-loops) + - [Interrupting and Exiting Loops](#interrupting-and-exiting-loops) + - [`WHILE`: Conditional Loops](#while-conditional-loops) + - [Loops Across Tables](#loops-across-tables) + - [Calling Procedures](#calling-procedures) + - [Handling Exceptions](#handling-exceptions) + - [Notes on Exception Classes](#notes-on-exception-classes) + - [Raising Exceptions](#raising-exceptions) + - [Excursion: Runtime Errors and Terminating Programs](#excursion-runtime-errors-and-terminating-programs) + - [Executable Example](#executable-example) + + +This cheat sheet gathers information on program flow logic. Find more details +[here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_flow_logic.htm) +in the ABAP Keyword Documentation. + +## Intro + +In ABAP, the flow of a program is controlled by [control structures](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencontrol_structure_glosry.htm), [procedure](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenprocedure_glosry.htm) calls and the raising or handling of [exceptions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenexception_glosry.htm). + +Using control structures as an example, you can determine the conditions for further processing of code, for example, if at all or how often a statement block should be executed. Control structures - as, for example, realized by an `IF ... ELSEIF ... ELSE ... ENDIF.` statement - can include multiple [statement blocks](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenstatement_block_glosry.htm) that are executed depending on conditions. + +In a very simple form, such an [`IF`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapif.htm) statement might look as follows: + +```abap +DATA(num) = 1 + 1. + +"A simple condition: Checking if the value of num is 2 +IF num = 2. + ... "Statement block + "Here goes some code that should be executed if the condition is true. +ELSE. + ... "Statement block + "Here goes some code that should be executed if the condition is false. + "For example, if num is 1, 8, 235, 0 etc., then do something else. +ENDIF. +``` + +

(back to top)

+ +## Expressions and Functions for Conditions +- So, such control structures are executed depending on conditions as specified above: `... num = 2 ...` - a [logical expression](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogical_expression_glosry.htm). +- Control structures are generally controlled by logical expressions that define conditions for [operands](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenoperand_glosry.htm). +- The result of such an expression is either true or false. +- Logical expressions are either single [relational expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrelational_expression_glosry.htm) or expressions combined from one or more logical expressions with Boolean operators like `NOT`, `AND` and `OR`. + +```abap +"Single relational expression +IF num = 1. + ... +ENDIF. + +"Multiple expressions +IF num = 1 AND flag = 'X'. + ... +ENDIF. + +IF num = 1 OR flag = 'X'. + ... +ENDIF. + +"Multiple expressions can be parenthesized explicitly +IF ( num = 1 AND flag = 'X' ) OR ( num = 2 AND flag = 'X' ). + ... +ENDIF. +``` + +- The components of such relational expressions can be [comparisons](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomparison_glosry.htm) or [predicates](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenpredicate_glosry.htm). Note that for [comparison expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencomparison_expression_glosry.htm), +the comparisons are carried out according to [comparison rules](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlogexp_rules.htm). + +The following code snippet shows a selection of possible expressions and operands of such expressions using a big `IF` statement. Certainly, such a huge statement is far from ideal. Here, it is just meant to touch on many syntax options in one go for demonstration purposes. + +```abap +"Some declarations to be used in the IF statement below +DATA(num) = 2. "integer +DATA(empty_string) = ``. "empty string +DATA(flag) = 'x'. +DATA(dref) = NEW string( `ref` ). "data reference variable + +"Object reference variable +DATA oref TYPE REF TO object. +"Creating an object and assigning it to the reference variable +oref = NEW zcl_demo_abap_prog_flow_logic( ). + +"Declaration of and assignment to a field symbol +FIELD-SYMBOLS TYPE string. +ASSIGN `hallo` TO . + +"Creating an internal table of type string inline +DATA(str_table) = VALUE string_table( ( `a` ) ( `b` ) ( `c` ) ). + +"The following IF statement includes multiple expressions combined by AND to demonstrate different options + +"Comparisons +IF 2 = num "equal, alternative EQ +AND 1 <> num "not equal, alternative NE +AND 1 < num "less than, alternative LT +AND 3 > num "greater than, alternative GT +AND 2 >= num "greater equal, alternative GE +AND 2 <= num "less equal, alternative LE + +"Checks whether the content of an operand is within a closed interval +AND num BETWEEN 1 AND 3 +AND NOT num BETWEEN 5 AND 7 "NOT negates a logical expression +AND ( num >= 1 AND num <= 3 ) "Equivalent to 'num BETWEEN 1 AND 3'; + "here, demonstrating the use of parentheses + +"Comparison operators CO, CN ,CA, NA, CS, NS, CP, NP for character-like data types; +"see the cheat sheet on string processing + +"Predicate Expressions +AND empty_string IS INITIAL "Checks whether the operand is initial. The expression + "is true, if the operand contains its type-dependent initial value +AND num IS NOT INITIAL "NOT negates + +AND dref IS BOUND "Checks whether a data reference variable contains a valid reference and + "can be dereferenced; + "Negation (IS NOT BOUND) is possible which is also valid for the following examples +AND oref IS BOUND "Checks whether an object reference variable contains a valid reference + +"IS INSTANCE OF checks whether for a +"a) non-initial object reference variable the dynamic type +"b) for an initial object reference variable the static type +"is more specific or equal to a comparison type. +AND oref IS INSTANCE OF zcl_demo_abap_prog_flow_logic +AND oref IS INSTANCE OF if_oo_adt_classrun + +AND IS ASSIGNED "Checks whether a memory area is assigned to a field symbol + +"See the predicate expression IS SUPPLIED in the executable example. +"It is available in method implementations and checks whether a formal parameter +"of a procedure is filled or requested. + +"Predicate function: Some examples +AND contains( val = pcre = `\D` ) "Checks whether a certain value is contained; + "the example uses the pcre parameter for regular expressions; + "it checks whether there is any non-digit character contained +AND matches( val = pcre = `ha.+` ) "Compares a search range of the argument for the val parameter; + "the example uses the pcre parameter for regular expressions; + "it checks whether the value matches the pattern 'ha' + "and a sequence of any characters + +"Predicate functions for table-like arguments +"Checks whether a line of an internal table specified in the table expression +"exists and returns the corresponding truth value. +AND line_exists( str_table[ 2 ] ) + +"Predicative method call +"The result of the relational expression is true if the result of the functional method call +"is not initial and false if it is initial. The data type of the result of the functional method call, +"i. e. the return value of the called function method, is arbitrary. +"A check is made for the type-dependent initial value. +AND check_is_supplied( ) +"It is basically the short form of such a predicate expression: +AND check_is_supplied( ) IS NOT INITIAL + +"Boolean Functions +"Determine the truth value of a logical expression specified as an argument; +"the return value has a data type dependent on the function and expresses +"the truth value of the logical expression with a value of this type. + +"Function boolc: Returns a single-character character string of the type string. +"If the logical expression is true, X is returned. False: A blank is returned. +"Not to be compared with the constants abap_true and abap_false in relational expressions, +"since the latter convert from c to string and ignore any blanks. Note: If the logical +"expression is false, the result of boolc does not meet the condition IS INITIAL since +"a blank and no empty string is returned. If this is desired, the function xsdbool +"can be used instead of boolc. +AND boolc( check_is_supplied( ) ) = abap_true + +"Result has the same ABAP type as abap_bool. +AND xsdbool( check_is_supplied( ) ) = abap_true + +"Examples for possible operands + +"Data objects as shown in the examples above +AND 2 = 2 +AND num = 2 + +"Built-in functions +AND to_upper( flag ) = 'X' +AND NOT to_lower( flag ) = 'X' + +"Numeric functions +AND ipow( base = num exp = 2 ) = 4 + +"Functional methods +"Method with exactly one return value +AND addition( num1 = 1 num2 = 1 ) = 2 + +"Calculation expressions +AND 4 - 3 + 1 = num + +"String expressions +AND `ha` && `llo` = + +"Constructor expression +AND CONV i( '2.03' ) = num +AND VALUE string_table( ( `a` ) ( `b` ) ( `c` ) ) = str_table + +"Table expression +AND str_table[ 2 ] = `b`. + + "All of the logical expressions are true. + +ELSE. + + "At least one of the logical expressions is false. + +ENDIF. +``` + +> **💡 Note**
+> Logical expressions and functions can also be used in other ABAP statements. + +

(back to top)

+ +## Control Structures + +### `IF` Statements + +- As already shown above, `IF` statements define statement blocks that can be included in [branches](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenbranch_glosry.htm). +- The statement blocks are executed depending on conditions. +- A maximum of one statement block is executed. +- The check is carried out from top to bottom. The statement block after the first logical expression that is true is executed. +- If none of the logical expressions are true, the statement block after the `ELSE` statement is executed. +- `ELSE` and `ELSEIF` statements are optional. However, it is recommended that you specify an `ELSE` so that at least one statement block is executed. +- If the end of the executed statement block is reached or if no statement block has been executed, the processing is continued after `ENDIF.`. + + +```abap +"IF statement with multiple included ELSEIF statements +IF log_exp1. + + ... "statement_block1 + +ELSEIF log_exp2. + + ... "statement_block2 + +ELSEIF log_exp3. + + ... "statement_block3 + +... "further ELSEIF statements + +ELSE. + + ... "statement_blockn + +ENDIF. +``` + + +Control structures can be nested. +```abap +DATA(num) = 1. +DATA(flag) = 'X'. + +IF num = 1. + + IF flag = 'X'. + ... + ELSE. + ... + ENDIF. + +ELSE. + + ... "statement block, e. g. + "ASSERT 1 = 0. + "Not to be executed in this example. + +ENDIF. +``` + +> **💡 Note**
+> - Control structures can be nested. It is recommended that you do not include more than 5 nested control structures since the code will +> get really hard to understand. Better go for outsourcing functionality into methods to reduce nested control control structures. +> - Keep the number of consecutive control structures low. +> - If you are convinced that a specified logical expression must always be true, you might include a statement like `ASSERT 1 = 0` to go +> sure - as implied in the example's `ELSE` statement above. However, an `ELSE` statement that is never executed might be a hint that +> logical expressions might partly be redundant. + +

(back to top)

+ +#### Excursion: `COND` Operator + +- The conditional operator [`COND`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconditional_expression_cond.htm) can also be used to implement branches in operand positions that are based on logical expressions. +- Such conditional expressions have a result that is dependent on the logical expressions. +- The result's data type is specified after `COND` right before the first parenthesis. It can also be the `#` character as a symbol for the operand type if the type can be derived from the context. See the ABAP Keyword Documentation and the cheat sheet on constructor expressions for more information. +- All operands specified after `THEN` must be convertible to the result's data type. + +```abap +... COND type( WHEN log_exp1 THEN result1 + WHEN log_exp2 THEN result2 + ... + ELSE resultn ) ... +``` + +

(back to top)

+ +### `CASE`: Case Distinctions + +- [`CASE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcase.htm) statements are used for case distinctions. +- Such statements can also contain multiple statement blocks of which a maximum of one is executed depending on the value of the operand specified after `CASE`. +- The check is carried out from top to bottom. If the content of an operand specified after `WHEN` matches the content specified after `CASE`, the statement block is executed. Constant values should be specified as operands. +- The `WHEN` statement can include more than one operand using the syntax `WHEN op1 OR op2 OR op3 ...`. +- If no matches are found, the statement block is executed after the statement `WHEN OTHERS.` which is optional. +- If the end of the executed statement block is reached or no statement block is executed, the processing continues after `ENDCASE.`. + + +```abap +CASE operand. + WHEN op1. + ... "statement_block + WHEN op2. + ... "statement_block + WHEN op3 OR op4. + ... "statement_block + WHEN OTHERS. + ... "statement_block +ENDCASE. +``` + +Special control structure introduced by [`CASE TYPE OF`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcase_type.htm): Checks the type of object reference variables. + +```abap +"oref must be an object reference variable with the static type of a class or an interface. +CASE TYPE OF oref. + WHEN TYPE some_class. + ... "statement_block + WHEN TYPE some_intf. + ... "statement_block + WHEN OTHERS. + ... "statement_block +ENDCASE. +``` + +

(back to top)

+ +#### Excursion: `SWITCH` Operator + +The conditional operator [`SWITCH`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenconditional_expression_switch.htm) can also be used to make case distinctions in operand positions. As mentioned above for `COND`, a result is constructed. The same criteria apply for `SWITCH` as for `COND` regarding the type. See the ABAP Keyword Documentation and the cheat sheet on constructor expressions for more information. + + +```abap +... SWITCH type( operand + WHEN const1 THEN result1 + WHEN const2 THEN result2 + ... + ELSE resultn ) ... +``` + +

(back to top)

+ +### Loops + +#### `DO`: Unconditional Loops + +- A statement block specified between `DO` and `ENDDO` is carried out multiple times. +- The loop is exited when a statement to terminate the loop is reached (`EXIT`, see further down). Otherwise, it is executed endlessly. + + ```abap + DO. + ... "statement_block + "To be terminated with an EXIT statement. + ENDDO. + ``` +- To restrict the loop passes, you can use the `TIMES` addition and specify the maximum number of loop passes. + + ```abap + DO 5 TIMES. + ... "statement_block + ENDDO. + ``` +- The value of the system field `sy-index` within the statement block contains the number of previous loop passes including the current pass. + +

(back to top)

+ +#### Interrupting and Exiting Loops + +The following ABAP keywords are available for interrupting and exiting loops: + +| Keyword | Syntax | Details | +|---|---|---| +| [`CONTINUE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcontinue.htm) | `CONTINUE.` | The current loop pass is terminated immediately and the program flow is continued with the next loop pass. | +| [`CHECK`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcheck_loop.htm) | `CHECK log_exp.` | Conditional termination. If the logical expression `log_exp` is false, the current loop pass is terminated immediately and the program flow is continued with the next loop pass. | +| [`EXIT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapexit_loop.htm) | `EXIT.` | The loop is terminated completely. The program flow resumes after the closing statement of the loop. | + +> **💡 Note**
+> - [`RETURN`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapreturn.htm) statements immediately terminate the current processing block. However, according to the [guidelines (F1 docu for standard ABAP)](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenexit_procedure_guidl.htm), `RETURN` should only be used to exit procedures like methods. +> - `EXIT` and `CHECK` might also be used for exiting procedures. However, their use inside loops is recommended. + +

(back to top)

+ +#### `WHILE`: Conditional Loops + +- Conditional loops introduced by `WHILE` and ended by `ENDWHILE` are repeated as long as a logical expression is true. +- These loops can also be exited using the statements mentioned above. +- Like in `DO` loops, the system field `sy-index` contains the number of previous loop passes including the current pass. + +```abap +WHILE log_exp. + ... "statement_block +ENDWHILE. +``` + +

(back to top)

+ +#### Loops Across Tables +Further keywords for defining loops are as follows. They are not dealt with here since they are touched on in other ABAP cheat sheets. + +- [`LOOP ... ENDLOOP`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaploop_at_itab.htm) statements are meant for loops across internal tables. See also the cheat sheet on internal tables. + - In contrast to the loops above, the system field `sy-index` is not set. Instead, the system field `sy-tabix` is set and which contains the table index of the current table line in the loop pass. + - You can also realize loops using iteration expressions with `VALUE` and `REDUCE`. See the cheat sheet example on internal tables, too. +- [`SELECT ... ENDSELECT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect.htm) statements loop across the result set of a database access. See also the cheat sheet on ABAP SQL. + +

(back to top)

+ +## Calling Procedures + +Calling [procedures](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenprocedure_glosry.htm) would actually fit here in the context of dealing with program flow logic since they can be called explicitly in an [ABAP program](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_program_glosry.htm). +However, ... +1. in modern ABAP programs, only methods should be implemented (instead of [function modules](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfunction_module_glosry.htm) and [subroutines](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensubroutine_glosry.htm) in most cases) and +2. methods are described in the context of the ABAP cheat sheet on ABAP object orientation. Hence, see more details there. + +Regarding the exiting of procedures, note the hint mentioned above. The use of `RETURN` is recommended. + +

(back to top)

+ +## Handling Exceptions +- [Exceptions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenexception_glosry.htm) ... + - are events during the execution of an ABAP program that interrupt the program flow because it is not possible for the program to continue in a meaningful way. For such situations, you can implement an exception handling in which you can react on the situations appropriately. Consider, for example, the implementation of a simple calculation. If there is a division by zero, the program will be terminated with a [runtime error](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenruntime_error_glosry.htm) unless you handle the exception appropriately. + - can be raised either by the program or by the [ABAP runtime framework](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_runtime_frmwk_glosry.htm). Exceptions raised by the latter are generally caused by error situations that cannot be detected by the static program check. The division by zero is such an example. + - should, in modern ABAP, only be designed as [class-based exceptions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenclass_based_exception_glosry.htm), i. e. exceptions are represented by [objects](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenobject_glosry.htm) of classes. Global exception classes usually use the naming convention `CX_...`. + - are either [catchable](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencatchable_exception_glosry.htm) (they are based on predefined or self-defined exception classes) or [uncatchable](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenuncatchable_exception_glosry.htm) (they directly produce runtime errors, i. e. error situations cannot be handled appropriately). + +`TRY` control structures are meant for handling catchable exceptions locally: +- To be prepared for potential exceptions that are raised when executing statements, the statements can be included and executed within a *protected area*, a [`TRY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaptry.htm) control structure. +- In doing so, it is possible for the ABAP runtime framework to catch exceptions and react on error situations. + + ```abap + TRY. + "statement block + ENDTRY. + ``` + +- The `TRY` control structure in the snippet above produces a syntax warning. A [`CATCH`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcatch_try.htm) block is expected, too. +- One or more class-based exceptions can be handled in one or more subsequent `CATCH` blocks. The `CATCH` statement must include an "appropriate" class-based exception. "Appropriate" means that, certainly, an exception class should be specified that is suitable for the error handling. In the following example, the predefined exception class `CX_SY_ZERODIVIDE` is specified that is, as the name implies, specific for the (potential) exception in case of a division by zero. + + ```abap + TRY. + "TRY block + DATA(div1) = 1 / 0. + + "Predefined exception class cx_sy_zerodivide as suitable exception class to be used here. + "If the exception is not handled, the program is terminated and the runtime error + "COMPUTE_INT_ZERODIVIDE occurs. + CATCH cx_sy_zerodivide. + ... "CATCH block + ENDTRY. + + "Example for catching an exception in the context of a table expression + TRY. + "Copying a line of an internal table + DATA(line) = some_itab[ 12345 ]. + + "Predefined exception class cx_sy_itab_line_not_found as suitable exception class to be used here. + "If the exception is not handled, the program is terminated and the runtime error + "ITAB_LINE_NOT_FOUND occurs. + CATCH cx_sy_itab_line_not_found. + ... "CATCH block + ENDTRY. + + "Note on inheritance relationships in exception classes + TRY. + "TRY block + DATA(div2) = 1 / 0. + + "A CATCH block is in this example not only valid for cx_sy_zerodivide as specified above + "but also for all derived exceptions classes. + "In the following CATCH block, the predefined exception class cx_sy_arithmetic_error + "is specified. cx_sy_zerodivide is derived from cx_sy_arithmetic_error. + "Hence, cx_sy_arithmetic_error can be specified and handle the exception, too. + "Basically, using the exception root class cx_root would be possible, too, however, + "that would not be ideal(see the notes further down). + + CATCH cx_sy_arithmetic_error. + ... "CATCH block + ENDTRY. + + + "Multiple classes in a list and CATCH blocks can be specified + "Note: If there are multiple CATCH blocks for exceptions that are in an inheritance + "relationship, you must pay attention that the more special exceptions are specified + "before the more general ones. Otherwise, a handler for a more general exception + "is called before the more special one. + TRY. + ... "TRY block + CATCH cx_abc cx_bla cx_blabla. + ... "CATCH block + CATCH cx_la cx_lala. + ... "CATCH block + CATCH cx_lalala. + ... "CATCH block + ENDTRY. + ``` + +- If the addition `INTO` is specified in the `CATCH` statement, a reference to the exception object is stored. +- This is for, example relevant, to determine the exact exception. In the code snippet above, the exception class `CX_SY_ZERODIVIDE` is mentioned. Consider a calculator. It should not only be able to deal with error situations like zero division but also, for example, overflows in arithmetic operations. The predefined exception class `CX_SY_ARITHMETIC_OVERFLOW` is available. It is also derived from `CX_SY_ARITHMETIC_ERROR`. If you then specify the exception class `CX_SY_ARITHMETIC_ERROR` which is higher up in the inheritance hierarchy and can handle both error situations (`CX_SY_ARITHMETIC_OVERFLOW` and `CX_SY_ZERODIVIDE`), the concrete exception that was raised is unclear. Using the `INTO` clause and the stored exception object, it is possible to carry out certain tasks, for example, retrieving and displaying the exception text. + + ```abap + DATA: exception TYPE REF TO cx_root. "Note the root class + "Note: For a self-defined exception class, the object reference must be typed appropriately. + + TRY. + ... "TRY block + + "Storing a reference to the exception object. + "Note: The type is cx_root since attributes and methods of the root class that are defined there can be accessed. + CATCH INTO exception. + ... "CATCH block + ENDTRY. + + "Inline creation of exception object reference and getting exception texts + TRY. + ... "TRY block + + "The object reference variable can be created inline, for example, using DATA(...). + CATCH cx_sy_arithmetic_error INTO DATA(error_oref). + ... "catch block + "To get exception texts, you can call, for example, the method get_text + DATA(error_text) = error_oref->get_text( ). + + ENDTRY. + ``` + +- Regarding the program flow: + - The statement block following `TRY.` is always processed. If an exception is raised within this `TRY` block, the system searches for an exception handler, i. e. a `CATCH` block that is able to handle the exception. + - If there is no `CATCH` statement that is able to handle the catchable exception or if erroneous code is not within a `TRY` control structure at all, the exception is propagated to the caller. + - Exceptions can be handled either in the context locally (using such a `TRY` control structure) or be propagated to the caller so that the caller is responsible for reacting appropriately (for example, in another `TRY` control structure) to the error situation. In doing so, you can better structure your code by reacting on error situations centrally instead of locally checking, for example, each procedure call individually. + - If, at the end, the exception can nowhere be caught and handled, the program is terminated with a runtime error. + - If the exception can be handled or no exception is raised in the `TRY` block and it reaches its end, the processing continues after `ENDTRY`. + + +> **💡 Note**
+> - Non-class-based exceptions are considered obsolete and should not be defined any more in new developments according to the [guidelines (F1 docu for standard ABAP)](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenclass_exception_guidl.htm) and are not dealt with here. +>- For all exceptions that are raised by the ABAP runtime environment and that are not handled, there is a corresponding runtime error. For example, in the case of exception class `CX_SY_ZERODIVIDE`, it is the runtime error `COMPUTE_INT_ZERODIVIDE`. For self-defined exception classes, an exception that is not handled generally triggers the runtime error `UNCAUGHT_EXCEPTION`. +> - For `TRY` control structures, there are further additions available dealing with more advanced error handling, e. g. [resumable exceptions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapresume.htm). + +

(back to top)

+ +#### Notes on Exception Classes +- To distinguish exception classes from "normal" classes, use the naming convention `CX` as prefix and not `CL`. +- All exception classes (also the self-defined ones) are directly or indirectly derived from three abstract subclasses: `CX_STATIC_CHECK`, `CX_DYNAMIC_CHECK` and `CX_NO_CHECK`. These three "exception class categories" have different properties. +- The class `CX_ROOT` is the root class. Directly deriving from `CX_ROOT` is not possible. +- Apart from global classes, exception classes can also be defined as local classes within an ABAP program. +- As mentioned, there are predefined exception classes like `CX_SY_ZERODIVIDE` for divisions by zero. However, you can create your own exception classes so that you can react on issues that are specific to your ABAP program. The exception class must, as stated above, be derived from one of the three abstract classes: + - `CX_STATIC_CHECK`: For forcing users to handle exceptions. + Generally speaking, exceptions that can occur in procedures should be handled locally there in the implementation or be declared explicitly in the [procedure interface](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenparameter_interface_glosry.htm) so that a caller knows which error situations can be expected. Exception classes of type `CX_STATIC_CHECK` enforce this. A check is carried out statically at compile time. Users of a procedure are then forced to either handle the exception locally in a `TRY` control structure or the users declare the exception themselves in their procedure interface to propagate the exception to their users. If this is not the case, a warning is produced. + - As an example, a method signature might look as follows. The addition `RAISING` in method signatures are used to declare one or more class-based exceptions that can be propagated from the method to the caller. + - When users of this method implement the method, they are made aware of the fact that an error situation can occur and a particular exception can be raised. The assumption is that `CX_SOME_ERROR` is derived from `CX_STATIC_CHECK`. Users of the method should then prepare the code accordingly. + ```abap + "Method definition using the RAISING parameter + CLASS-METHODS: some_meth IMPORTING str TYPE string + RETURNING VALUE(ret_value) TYPE string + RAISING cx_some_error. + + ... + + "Method call: Somewhere in the code of a user that calls the method + "Exception handled locally in a TRY control structure. + TRY. + DATA(val) = some_meth( str = `hallo` ). + + CATCH cx_some_error. + ... + ENDTRY. + + "If it was just like this without the TRY control structure, a warning would be produced. + DATA(val2) = some_meth( str = `hi` ). + ``` + + - `CX_DYNAMIC_CHECK`: For exceptions that can be checked and avoided by preconditions. As a consequence and in contrast to an exception class derived from `CX_STATIC_CHECK`, exception classes of type `CX_DYNAMIC_CHECK` do not enforce the local handling and the declaration in procedure interfaces. However, an appropriate exception handling should be implemented in cases in which you cannot rule out the raising of the exceptions in your program logic. The checking if a local handling or an explicit declaration in procedure interfaces is available is carried out at runtime only ("dynamic check") and only in case the exception is indeed raised. + - If it is determined at runtime that such an exception is neither locally handled nor an interface is declared appropriately - and the exception is raised - a new exception of type `CX_SY_NO_HANDLER` is raised. In this case, the attribute `PREVIOUS` contains a reference to the original exception. + - Example: The predefined class `CX_SY_ZERODIVIDE` is derived from `CX_DYNAMIC_CHECK`. The operands of a calculation can be checked appropriately (e. g. in case of a division, the implementation should guarantee that the second operand is not 0) before carrying out the arithmetic operation. In doing so, the exception can be avoided. + + - `CX_NO_CHECK`: For error situations that can basically occur any time, cannot be locally handled in a meaningful way or cannot be avoided even following a check. An example for such an error situation might be a lack of memory. If the handling of such exceptions was checked statically or dynamically, it would basically mean to specify it in each procedure interface - not ideal for a clear program structuring. + - Note that exceptions derived from `CX_NO_CHECK` are always declared implicitly in all procedure interfaces. + +**Basic rule**: [Use a suitable exception category (F1 docu for standard ABAP)](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenexception_category_guidl.htm). + +> **💡 Note**
+> - Each exception has a an [exception text](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenexception_text_glosry.htm) that describes the error situation and that you can retrieve as outlined above. It helps you analyze the error. Plus, imagine using exceptions in the context of user interfaces. If a user faces an error situation, such exception texts may be displayed on the UI. +> - Find more information on exception texts [here](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenexception_texts.htm) in the ABAP Keyword Documentation. + +

(back to top)

+ +### Raising Exceptions + +- Either the ABAP runtime framework raises predefined exceptions or you raise exceptions programmatically using dedicated statements. You can raise both predefined and self-defined exceptions. +- As the name implies, [`RAISE EXCEPTION`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapraise_exception_class.htm) statements raise class-based exceptions and thus interrupt the execution of the current statement block. +- The `COND` operator includes the optional addition `THROW` to raise class-based exceptions. + +```abap +... +"RAISE EXCEPTION statement +"The TYPE addition specifies the type of the exception, i. e. the exception class. +"The statement is also possible without TYPE. In that case, you can use an existing exception object. +"Note that there are plenty of additions. Check the ABAP Keyword Documentation. +RAISE EXCEPTION TYPE cx_sy_zerodivide. +... + +"THROW addition for the COND operator +... = COND #( WHEN ... THEN ... + WHEN ... THEN ... + ELSE THROW cx_some_error( ) ). +``` + +

(back to top)

+ +## Excursion: Runtime Errors and Terminating Programs +- Runtime errors are caused by uncatchable exceptions when a program is executed, when a catchable exception is not caught, or they can be forced by, for example, using [`ASSERT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapassert.htm) statements. +- Every runtime error terminates the program, which in turn raises a [database rollback](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendatabase_rollback_glosry.htm) and is documented by default in a [short dump](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenshort_dump_glosry.htm). + +- Regarding `ASSERT` statements: `ASSERT` is followed by a logical expression. If the expression is false, the program is terminated and an uncatchable exception is raised resulting in the runtime error `ASSERTION_FAILED`. Note that each runtime error is identified by a name and assigned to a specific error situation. It leads to a . + +```abap +"The ASSERT keyword is followed by a logical expression. +"If the expression is false, the program is terminated and an uncatchable exception is raised +"resulting in the runtime error ASSERTION_FAILED. + +DATA(flag) = abap_false. + +ASSERT flag = abap_true. +``` + +> **💡 Note**
+> - Each runtime error is identified by a name and assigned to a specific error situation. +> - In ADT, you will see a message popping up and informing you about the runtime error. You can check the details by choosing the "Show" button in the pop-up. Furthermore, you can check the content of the "Feed Reader" tab in ADT. There, just expand your project and find the runtime errors caused by you. + +

(back to top)

+ +## Executable Example +[zcl_demo_abap_prog_flow_logic](./src/zcl_demo_abap_prog_flow_logic.clas.abap) + +Note the steps outlined [here](README.md#-getting-started-with-the-examples) about how to import and run the code. diff --git a/README.md b/README.md index d92767d..64242fe 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,8 @@ ABAP cheat sheets[^1] ... |[Excursion Down to Bits and Bytes](09_Bits_and_Bytes.md)|Touches on the technical background of data types and data objects|-| |[ABAP SQL: Working with Hierarchies](10_ABAP_SQL_Hierarchies.md)|Summarizes the functions ABAP SQL offers together with ABAP CDS for working with hierarchical data that is stored in database tables|-| |[ABAP SQL: Grouping Internal Tables](11_ABAP_SQL_Grouping_Internal_Tables.md)|Touches on the `GROUP BY` clause in ABAP SQL|[zcl_demo_abap_sql_group_by](./src/zcl_demo_abap_sql_group_by.clas.abap)| -|[A Glimpse on AMDP](12_AMDP.md)|Touches ABAP Managed Database Procedures (AMDP): AMDP Procedures and AMDP Functions (including CDS Table Functions)|[zcl_demo_abap_amdp](./src/zcl_demo_abap_amdp.clas.abap) +|[A Glimpse on AMDP](12_AMDP.md)|Touches on ABAP Managed Database Procedures (AMDP): AMDP Procedures and AMDP Functions (including CDS Table Functions)|[zcl_demo_abap_amdp](./src/zcl_demo_abap_amdp.clas.abap)| +|[Program Flow Logic](13_Program_Flow_Logic.md)|Deals with control structures (`IF`, `CASE`), loops (`DO`, `WHILE`) and exception handling|[zcl_demo_abap_prog_flow_logic](./src/zcl_demo_abap_prog_flow_logic.clas.abap) ## 🎬 Getting Started with the Examples @@ -87,12 +88,14 @@ The executable examples are especially targeted at being imported into the SAP B
**Prerequisites** -- [x] Before importing the code, you have made a system-wide search for, for example, classes named `ZCL_DEMO_ABAP*` so as not to run into issues when you try to import the code. If someone has already imported the content in the system, you can simply check out that imported version and proceed with the step *3) Run the code*. - [x] You have access to an SAP BTP ABAP Environment instance (see [here](https://blogs.sap.com/2018/09/04/sap-cloud-platform-abap-environment) for additional information). - [x] You have downloaded and installed ABAP Development Tools (ADT). Make sure that you use the most recent version as indicated on the [installation page](https://tools.hana.ondemand.com/#abap). +- [x] Before importing the code, you have made a system-wide search for, for example, classes named `ZCL_DEMO_ABAP*` so as not to run into issues when you try to import the code. If someone has already imported the content in the system, you can simply check out that imported version and proceed with the step *3) Run the code*. - [x] You have created an ABAP cloud project in ADT that allows you to access your SAP BTP ABAP Environment instance (see [here](https://help.sap.com/viewer/5371047f1273405bb46725a417f95433/Cloud/en-US/99cc54393e4c4e77a5b7f05567d4d14c.html) for additional information). Your logon language is English. - [x] You have installed the [abapGit](https://github.com/abapGit/eclipse.abapgit.org) plug-in for ADT from the [update site](http://eclipse.abapgit.org/updatesite/). + + **Import Code** Use the abapGit plug-in to install the ABAP Cheat Sheets by carrying out the following steps: @@ -134,7 +137,6 @@ https://github.com/SAP-samples/abap-cheat-sheets.git - [x] You have downloaded and installed ABAP Development Tools (ADT). Make sure that you use the most recent version as indicated on the [installation page](https://tools.hana.ondemand.com/#abap). - [x] You have created an ABAP Project in ADT that allows you to access your Application Server as mentioned above. Your logon language is English. - [x] You have downloaded and installed the standalone version of the abapGit report. Make sure that you use the most recent version as indicated on the [installation page](https://docs.abapgit.org/). You can create a report, for example, `zabapgit_standalone` and copy and paste [this code](https://raw.githubusercontent.com/abapGit/build/main/zabapgit_standalone.prog.abap) into the program. -- [x] You have installed the certificate files for github.com, see [abapGit Documentation](https://docs.abapgit.org/guide-ssl-setup.html). **Import Code** @@ -171,7 +173,7 @@ Use the standalone version of the abapGit report to import the demo examples of > **💡 Note**
>- Check notes on the context and the ABAP syntax used included in the class as comments. ->- Due to the amount of output in the console, the examples include numbers (e. g. 1) ..., 2) ..., 3) ...) representing the header of the individual example code sections. Plus, the variable name is displayed in the console in most cases. Hence, to easier and faster find the relevant output in the console, just search in the console for the number (e. g. search for `3)` for the particular output) or variable name (`STRG+F` in the console) or use break points in the code to check variables in the debugger. +>- Due to the amount of output in the console, the examples include numbers (e. g. 1) ..., 2) ..., 3) ...) representing the header of the individual example code sections. Plus, the variable name is displayed in the console in most cases. Hence, to easier and faster find the relevant output in the console, just search in the console for the number (e. g. search for `3)` for the particular output) or variable name (`CTRL+F` in the console) or use break points in the code to check variables in the debugger. >- You might want to clear the console by making a right-click within the console and choosing `Clear` before running another demo class so as not to confuse the output of multiple classes. @@ -188,7 +190,7 @@ Use the standalone version of the abapGit report to import the demo examples of - ... **on-premise system**: Access the documentation in SAP GUI via the transactions `ABAPDOCU` (opens the documentation directly) and `ABAPHELP` (opens an input field with which you can search the documentation content, for example, you can search for a keyword like `SELECT`). Or, certainly, in your code, choose `F1` for a keyword. If you are in SAP GUI (e. g. in `SE80`), the system-internal version is opened. If you are in ADT, the documentation is opened in the *ABAP Language Help* view. - ... **SAP BTP ABAP environment**: In ADT, you find the documentation in the *ABAP Language Help* view where you can also search. When choosing `F1` for a keyword in your code, the documentation is opened there accordingly. - Links to the online version of the ABAP Keyword Documentation for: - - **Standard ABAP**: Unrestricted ABAP language scope, for example, for developments in an on-premise ABAP system → [Online version of the documentation (latest version)](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenabap.htm) + - **Standard ABAP**: Unrestricted ABAP language scope, for example, for developments in an on-premise ABAP system → [Online version of the documentation (latest version)](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenabap.htm). To access the online documentation for a particular ABAP version, e. g. 7.54, you can select the version from the dropdown [here](https://help.sap.com/docs/ABAP?locale=en-US) (*latest* is preselected). The *ABAP* link under *Development* takes you to the desired documentation. - **ABAP for Cloud Development**: Restricted ABAP language scope for developments in the SAP BTP ABAP environment → [Online version of the documentation](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm) - Regarding demonstration examples of the ABAP Keyword Documentation in your on-premise system: Have you ever checked out the package `SABAPDEMOS`? This package contains all the examples used in the ABAP Keyword Documentation. To get the context, program names etc., check out the [example page](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenabap_examples.htm) (which is also available in the system-internal SAP GUI version as node in the topic tree) summarizing executable examples. Certainly, you also find the example topics in the context of the individual ABAP Keyword Documentation topic. The example topics have a ⚙️ sign: @@ -206,4 +208,4 @@ This project is provided "as-is": there is no guarantee that raised issues will ## 📜 License Copyright (c) 2022 SAP SE or an SAP affiliate company. All rights reserved. This project is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](LICENSE) file. -[^1]: "A written [...] aid (such as a sheet of notes) that can be referred to for help in understanding or remembering something complex" (Definition for "cheat sheet" in Merriam-Webster Dictionary). +[^1]: "A written [...] aid (such as a sheet of notes) that can be referred to for help in understanding or remembering something complex" (Definition for "cheat sheet" in Merriam-Webster Dictionary). \ No newline at end of file diff --git a/src/zcl_demo_abap_prog_flow_logic.clas.abap b/src/zcl_demo_abap_prog_flow_logic.clas.abap new file mode 100644 index 0000000..257027d --- /dev/null +++ b/src/zcl_demo_abap_prog_flow_logic.clas.abap @@ -0,0 +1,1456 @@ +*********************************************************************** +* +* ABAP cheat sheet: Program Flow Logic +* +* -------------------------- PURPOSE ---------------------------------- +* - Demonstration example for the topic program flow logic covering +* the following: +* - Expressions and functions for conditions +* - Control structures with IF and CASE, including the COND and SWITCH +* operators +* - Unconditional loops with DO +* - Conditional loops with WHILE +* - Handling exceptions +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP Development Tools (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, check the notes +* included in the class as comments or refer to the respective topic +* in the ABAP Keyword Documentation. +* - Due to the amount of output in the console, the examples include +* numbers (e. g. 1) ..., 2) ..., 3) ...) for the individual example +* sections. Plus, the variable name is displayed in most cases. Hence, +* to easier and faster find the relevant output in the console, just +* search in the console for the number/variable name (CTRL+F in the +* console) or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** +"!

ABAP cheat sheet: Program Flow Logic

+"! Demonstration example for the topic program flow logic.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_prog_flow_logic DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + + PRIVATE SECTION. + + "Structured type for calculation example + TYPES: BEGIN OF calc_results_struc, + sy_tabix LIKE sy-tabix, + calculation TYPE string, + res_if TYPE string, + res_case TYPE string, + res_cond TYPE string, + res_switch TYPE string, + END OF calc_results_struc. + + CLASS-METHODS: + check_is_supplied IMPORTING num1 TYPE i DEFAULT 0 + num2 TYPE i DEFAULT 0 + RETURNING VALUE(res) TYPE string_table, + + addition IMPORTING num1 TYPE i DEFAULT 0 + num2 TYPE i DEFAULT 0 + RETURNING VALUE(res) TYPE i, + + calc IMPORTING num1 TYPE i DEFAULT 0 + operator TYPE c + num2 TYPE i DEFAULT 0 + RETURNING VALUE(res) TYPE calc_results_struc, + + validate_email IMPORTING email TYPE string + RETURNING VALUE(is_valid_email) TYPE abap_bool + RAISING lcx_invalid_email, + + meth_with_return IMPORTING num TYPE i + RETURNING VALUE(res) TYPE string, + + whats_my_user IMPORTING get_name TYPE abap_bool + RETURNING VALUE(name) TYPE string + RAISING lcx_static_exc_class, + + prep_calc_result CHANGING VALUE(res) TYPE string, + + power2_and_sqrt IMPORTING num TYPE i + RETURNING VALUE(result) TYPE string + RAISING cx_sy_arithmetic_overflow. + + CLASS-DATA: exception_text TYPE string, + exception TYPE REF TO cx_root. + +ENDCLASS. + +CLASS zcl_demo_abap_prog_flow_logic IMPLEMENTATION. + + METHOD if_oo_adt_classrun~main. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Demonstration Example: Program Flow Logic` ). + +********************************************************************** + + output->display( `1) Control Structure with IF` ). + + "Simple control structure realized by an IF ... ELSEIF ... ELSE ... ENDIF. + "statement. Multiple statement blocks can be included, of which only 1 is + "executed at most and depending on conditions. + + DATA(operator) = `+`. + DATA(num1) = 5. + DATA(num2) = 7. + + IF operator = `+`. + + output->display( |The result of { num1 } { operator } { num2 } is { num1 + num2 }. | ). + + ELSEIF operator = `-`. + + output->display( |The result of { num1 } { operator } { num2 } is { num1 - num2 }. | ). + + ELSE. + + output->display( |The operator { operator } is not possible.| ). + + ENDIF. + +********************************************************************** + + output->next_section( `2) IF: Checking sy-subrc` ). + + "A prominent use case for IF statements: Checking sy-subrc. + "In the case below, a FIND statement is used. If there is a finding, + "sy-subrc has the value 0. + + DATA(to_be_found) = `AB`. + DATA(string_to_search) = `ABAP is great!`. + + FIND to_be_found IN string_to_search. + + IF sy-subrc = 0. + + output->display( |'{ to_be_found }' was found in the string '{ string_to_search }'.| ). + + ELSE. + + output->display( |'{ to_be_found }' was not found in the string '{ string_to_search }'.| ). + + ENDIF. + +********************************************************************** + + output->next_section( `3) Excursion: COND Operator` ). + + "The conditional operator COND can also be used to implement branches in operand positions + "that are based on logical expressions. Such conditional expressions have a result that + "is dependent on the logical expressions. + "The example provides an output based on the time stamp. + + DATA(syst_time) = cl_abap_context_info=>get_system_time( ). + + DATA(greetings) = COND #( WHEN syst_time BETWEEN '050001' AND '120000' + THEN |It's { syst_time TIME = ISO }. Good morning, { sy-uname }.| + WHEN syst_time BETWEEN '170001' AND '210000' + THEN |It's { syst_time TIME = ISO }. Good evening, { sy-uname }.| + WHEN syst_time BETWEEN '210001' AND '050000' + THEN |It's { syst_time TIME = ISO }. Good night, { sy-uname }.| + ELSE |It's { syst_time TIME = ISO }. Hallo, { sy-uname }.| ). + + output->display( input = greetings name = `greetings`). + +********************************************************************** + + output->next_section( `4) Expressions and Functions for Conditions` ). + + "Control structures are generally controlled by logical expressions that + "define conditions for operands. The result of such an expression is either true or false. + "The example demonstrates a selection of possible expressions and operands of such expressions + "using a big IF statement. It includes multiple expressions combined by AND to demonstrate + "different options. Here, it is just meant to touch on many syntax options in one go + "for demonstration purposes. + + "Data declarations to be used in the IF statement below + DATA(num) = 2. "integer + DATA(empty_string) = ``. "empty string + DATA(flag) = 'x'. + DATA(dref) = NEW string( `ref` ). "data reference variable + + "Object reference variable + DATA oref TYPE REF TO object. + "Creating an object and assigning it to the reference variable + oref = NEW zcl_demo_abap_prog_flow_logic( ). + + "Declaration of and assignment to a field symbol + FIELD-SYMBOLS TYPE string. + ASSIGN `hallo` TO . + + "Creating an internal table of type string inline + DATA(str_table) = VALUE string_table( ( `a` ) ( `b` ) ( `c` ) ). + + "Comparisons + IF 2 = num "equal, alternative EQ + AND 1 <> num "not equal, alternative NE + AND 1 < num "less than, alternative LT + AND 3 > num "greater than, alternative GT + AND 2 >= num "greater equal, alternative GE + AND 2 <= num "less equal, alternative LE + + "Checks whether the content of an operand operand is within a closed interval + AND num BETWEEN 1 AND 3 + AND NOT num BETWEEN 5 AND 7 "NOT negates a logical expression + AND ( num >= 1 AND num <= 3 ) "Equivalent to 'num BETWEEN 1 AND 3'; + "here, demonstrating the use of parentheses + + "CO, CN ,CA, NA, CS, NS, CP, NP "Comparison operators for character-like data types; + "see the cheat sheet on string processing + + "Predicate Expressions + AND empty_string IS INITIAL "Checks whether the operand operand is initial. The expression + "is true, if the operand contains its type-dependent initial value + AND num IS NOT INITIAL "NOT negates + + AND dref IS BOUND "Checks whether a data reference variable contains a valid reference and + "can be dereferenced; + "IS NOT BOUND is possible which is valid for the following examples, too + AND oref IS BOUND "Checks whether an object reference variable contains a valid reference + + "IS INSTANCE OF checks whether for a + "a) non-initial object reference variable the dynamic type + "b) for an initial object reference variable the static type + "is more specific or equal to a comparison type. + AND oref IS INSTANCE OF zcl_demo_abap_prog_flow_logic + AND oref IS INSTANCE OF if_oo_adt_classrun + + AND IS ASSIGNED "Checks whether a memory area is assigned to a field symbol + + "The predicate expression IS SUPPLIED is dealt with further down. + + "Predicate Functions + AND contains( val = pcre = `\D` ) "Checks whether a certain value is contained; + "the example uses the pcre parameter for regular expressions; + "it checks whether there is any non-digit character contained + AND matches( val = pcre = `ha.+` ) "Compares a search range of the argument for the val parameter + "the example uses the pcre parameter for regular expressions; + "it checks whether the value matches the pattern + "'ha' and a sequence of any characters + + "Predicate functions for table-like arguments + "Checks whether a line of an internal table specified in the table expression + "exists and returns the corresponding truth value. + AND line_exists( str_table[ 2 ] ) + + "Predicative method call + "The result of the relational expression is true if the result of the functional method call + "is not initial and false if it is initial. The data type of the result of the functional method call, + "i. e. the return value of the called function method, is arbitrary. + "A check is made for the type-dependent initial value. + AND check_is_supplied( ) + "It is basically the short form of such a predicate expression: + AND check_is_supplied( ) IS NOT INITIAL + + "Boolean Functions + "Determine the truth value of a logical expression specified as an argument; + "the return value has a data type dependent on the function and expresses + "the truth value of the logical expression with a value of this type. + + "Function boolc: Returns a single-character character string of the type string. + "If the logical expression is true, X is returned. False: A blank is returned. + "Not to be compared with the constants abap_true and abap_false in relational expressions, + "since the latter converts from c to string and ignores any blanks. Note: If the logical + "expression is false, the result of boolc does not meet the condition IS INITIAL, since + "a blank and no empty string is returned. If this is desired, the function xsdbool + "can be used instead of boolc. + AND boolc( check_is_supplied( ) ) = abap_true + + "Result has the same ABAP type as abap_bool. + AND xsdbool( check_is_supplied( ) ) = abap_true + + "Examples for possible operands + + "Data objects as shown in the examples above + AND 2 = 2 + AND num = 2 + + "Built-in functions + AND to_upper( flag ) = 'X' + AND NOT to_lower( flag ) = 'X' + + "Numeric functions + AND ipow( base = num exp = 2 ) = 4 + + "Functional methods + "Method with exactly one return value + AND addition( num1 = 1 num2 = 1 ) = 2 + + "Calculation expressions + AND 4 - 3 + 1 = num + + "String expressions + AND `ha` && `llo` = + + "Constructor expression + AND CONV i( '2.03' ) = num + AND VALUE string_table( ( `a` ) ( `b` ) ( `c` ) ) = str_table + + "Table expression + AND str_table[ 2 ] = `b`. + + output->display( `All of the logical expressions are true.` ). + + ELSE. + output->display( `At least one of the logical expressions is false.` ). + ENDIF. + +********************************************************************** + + output->next_section( `5) Predicate Expression with IS SUPPLIED` ). + + "The predicate expression IS SUPPLIED is available in method implementations + "and checks whether a formal parameter of a procedure is filled or requested. + "In this example, a method includes two importing parameters that are both + "declared as non-mandatory. The method implementation includes a check with + "IS SUPPLIED and stores the result. The result is returned and displayed. + + DATA(is_supplied) = check_is_supplied( num1 = 123 ). + + output->display( input = is_supplied name = `is_supplied`). + + is_supplied = check_is_supplied( num2 = 456 ). + + output->display( input = is_supplied name = `is_supplied`). + +********************************************************************** + + output->next_section( `6) Control Structure with CASE` ). + + "CASE statements are used for case distinctions. If the content of an operand + "specified after WHEN matches the content specified after CASE, the statement + "block is executed. Constant values should be specified as operands. + "The example is basically the same as + + + DATA(op) = '-'. + DATA(n1) = 8. + DATA(n2) = 3. + + "Simple calculation + + CASE op. + WHEN '+'. + output->display( |The result of { n1 } { op } { n2 } is { n1 + n2 }. | ). + WHEN '-'. + output->display( |The result of { n1 } { op } { n2 } is { n1 - n2 }. | ). + WHEN OTHERS. + output->display( |The operator { op } is not possible.| ). + ENDCASE. + +********************************************************************** + + output->next_section( `7) CASE TYPE OF` ). + + "CASE TYPE OF: Checks the type of object reference variables + + "Creating an object reference variable and an object + DATA oref_check TYPE REF TO object. + oref_check = NEW zcl_demo_abap_prog_flow_logic( ). + + CASE TYPE OF oref_check. + WHEN TYPE zcl_demo_abap_prog_flow_logic. + output->display( |Type zcl_demo_abap_prog_flow_logic? True!| ). + WHEN TYPE if_oo_adt_classrun. + output->display( |Type if_oo_adt_classrun? True!| ). + WHEN TYPE zcl_demo_abap_sql. + output->display( |Type zcl_demo_abap_sql? True!| ). + WHEN OTHERS. + output->display( |Other type.| ). + ENDCASE. + + "The same logic as above is realized in the following IF statements + "using IS INSTANCE OF. + "In the example, the type check for if_oo_adt_classrun 'comes first'. + "This type is also true for the object reference variable. This class + "implements the interface. + IF oref_check IS INSTANCE OF if_oo_adt_classrun. + output->display( |Type if_oo_adt_classrun? True!| ). + ELSEIF oref_check IS INSTANCE OF zcl_demo_abap_prog_flow_logic. + output->display( |Type zcl_demo_abap_prog_flow_logic? True!| ). + ELSEIF oref_check IS INSTANCE OF zcl_demo_abap_sql. + output->display( |Type zcl_demo_abap_sql? True!| ). + ELSE. + output->display( |Other type.| ). + ENDIF. + +********************************************************************** + + output->next_section( `8) Excursion: SWITCH Operator` ). + + "The conditional operator SWITCH can also be used to make case + "distinctions in operand positions. Such conditional expressions have + "a result that is dependent on the logical expressions. + "The example provides an output based on the date. + + DATA(syst_date) = cl_abap_context_info=>get_system_date( ). + + DATA(switch_res) = SWITCH #( syst_date+4(2) "Extracting the month + WHEN '01' THEN `January` + WHEN '02' THEN `February` + WHEN '03' THEN `March` + WHEN '04' THEN `April` + WHEN '05' THEN `May` + WHEN '06' THEN `June` + WHEN '07' THEN `July` + WHEN '08' THEN `August` + WHEN '09' THEN `September` + WHEN '10' THEN `October` + WHEN '11' THEN `November` + WHEN '12' THEN `December` + ELSE `Oops ...` ). + + output->display( input = switch_res name = `switch_res`). + +********************************************************************** + + output->next_section( `Loops (Iterations)` ). + + output->display( `9) Unconditional Loops with DO` ). + + "The example demonstrate the restriction of loop passes by specifying + "a number (of maximum loop passes) and the TIMES addition in a DO loop. + "The value of sy-index containing the number of loop passes is output, too. + + DATA do_counter TYPE i. + DATA do_sy_index TYPE string. + + DO 10 TIMES. + do_counter += 1. + + do_sy_index = do_sy_index && sy-index && ` `. + ENDDO. + + output->display( input = do_counter name = `do_counter`). + output->display( input = do_sy_index name = `do_sy_index`). + +********************************************************************** + + output->next_section( `10) Terminating Loops Completely Using EXIT` ). + + "Using the EXIT statement, you can terminate a loop completely. + "The program flow resumes after the closing statement of the loop. + + CLEAR: do_counter, do_sy_index. + + DO. + do_counter += 1. + + do_sy_index = do_sy_index && sy-index && ` `. + IF sy-index = 5. + EXIT. + ENDIF. + ENDDO. + + output->display( input = do_counter name = `do_counter`). + output->display( input = do_sy_index name = `do_sy_index`). + +********************************************************************** + + output->next_section( `11) Terminating Loop Passes` ). + + "CONTINUE: The current loop pass is terminated immediately and the + " program flow is continued with the next loop pass. + "CHECK: Conditional termination. If the specified logical expression + " is false, the current loop pass is terminated immediately and + " the program flow is continued with the next loop pass. + + CLEAR: do_counter, do_sy_index. + + DO. + + IF sy-index = 2. "skipped + CONTINUE. + ENDIF. + + CHECK sy-index <> 5. "skipped + + IF sy-index = 8. "terminates the loop completely + EXIT. + ENDIF. + + do_counter += 1. + + do_sy_index = do_sy_index && sy-index && ` `. + + ENDDO. + + output->display( input = do_counter name = `do_counter`). + output->display( input = do_sy_index name = `do_sy_index`). + +********************************************************************** + + output->next_section( `12) Excursion: Terminating Procedures Using RETURN` ). + + "RETURN statements immediately terminate the current processing block. + "However, according to the guidelines, RETURN should only be used to exit + "procedures like methods. + "The method implementation includes a check if the passed number is positive. + "If so, the square root is calculated. Otherwise, the method call is terminated. + "That means, the returned value for the second method call is initial. + + DATA(return1) = meth_with_return( 81 ). + + output->display( input = return1 name = `return1`). + + DATA(return2) = meth_with_return( -9 ). + + output->display( input = return2 name = `return2`). + +********************************************************************** + + output->next_section( `Conditional Loops with WHILE` ). + + output->display( `13) WHILE Example 1` ). + + "The following example highlights the setting of sy-index within loop passes. + "The value is added to an internal table. The loop iteration stops when + "the internal table has 10 lines. + + DATA int_itab TYPE TABLE OF i. + + WHILE lines( int_itab ) < 10. + + int_itab = VALUE #( BASE int_itab ( sy-index ) ). + + ENDWHILE. + + output->display( input = int_itab name = `int_itab`). + +********************************************************************** + + output->display( `14) WHILE Example 2` ). + + "In the following example, all occurrences of a certain substring + "should be replaced by another string. Instead of, for example, using + "a REPLACE ALL OCCURRENCES statement, a WHILE loop is used which + "contains a FIND statement that stores offset and length of the found + "substring. Then, a REPLACE statement is used and that only replaces + "the substring according to the values. The condition for WHILE is + "tailored in a way that the loop is terminated if there are no more + "findings. The number of findings are also output - a number that + "corresponds to the number of loop passes. + + DATA(while_string) = `##abap####abap#abap######abap###abapabap##abap`. + DATA count_occ TYPE i. + DATA count_occ_via_sy_index TYPE i. + + DATA(subrc) = 0. "separate dobj to store the sy-subrc value + + WHILE subrc = 0. + + FIND FIRST OCCURRENCE OF `abap` IN while_string + MATCH LENGTH DATA(len) + MATCH OFFSET DATA(off) + MATCH COUNT DATA(cnt). + "Note: cnt is always 1 since it is always the first occurrence. + " len is always 4 -> abap + " Only offset has different values + + "A separate data object subrc gets assigned the value of sy-subrc. + "As long as there are findings in the string, the while loop continues. + subrc = sy-subrc. + + IF subrc = 0. + + count_occ_via_sy_index = sy-index. "to hold the + "To hold the total number of findings from left to right of the string + count_occ = count_occ + cnt. + + REPLACE SECTION OFFSET off LENGTH len OF while_string WITH `ABAP`. + + ENDIF. + + ENDWHILE. + + output->display( input = count_occ_via_sy_index name = `count_occ_via_sy_index`). + output->display( input = count_occ name = `count_occ`). + output->display( input = while_string name = `while_string`). + +********************************************************************** + + output->display( `15) WHILE Example 3` ). + + "The example demonstrates the 3 options for loop terminations + "in the context of a WHILE ... ENDWHILE statement. + + DATA while_cnt TYPE i. + DATA while_sy_index TYPE string. + + DATA(index) = 0. + + WHILE index <> 10. + + index = sy-index. + + IF sy-index = 2. "Skips loop pass + CONTINUE. + ENDIF. + + CHECK sy-index <> 5. "Skips loop pass + + IF sy-index = 8. "Terminates loop + EXIT. + ENDIF. + + while_cnt += 1. + + while_sy_index = while_sy_index && sy-index && ` `. + + ENDWHILE. + + output->display( input = while_cnt name = `while_cnt`). + output->display( input = while_sy_index name = `while_sy_index`). + + +********************************************************************** + + output->next_section( `Loops across Tables` ). + + output->display( `16) Loop across Internal Table Using LOOP ... ENDLOOP` ). + + "LOOP ... ENDLOOP statements are meant for loops across internal tables. + "For more examples, see the cheat sheet example on internal tables. + "Note: In contrast to the loops above, the system field sy-index is not set. + "Instead, the system field sy-tabix is set and which contains the table index + "of the current table line in the loop pass. + "The example demonstrates a loop across an internal table. It combines loops + "and control structures mentioned before: + "- An internal table includes two numbers and an operator based on which a + " a calculation is carried out. + "- The calculation is done in a method. It is implemented in a way that + " IF and CASE statements as well as COND and SWITCH operators are used + " for the calculation. + "- The calculation result is stored in an internal table which is output. + "- All four results - which are all the same, of course, just demonstrating + " the variety of options to achieve the same regarding control structures - + " are available in the table. Plus, the value in sy-tabix is also added to + " the internal table representing the table index. + + TYPES: BEGIN OF calc_struc, + num1 TYPE i, + operator TYPE c LENGTH 1, + num2 TYPE i, + END OF calc_struc. + + DATA calc_itab TYPE TABLE OF calc_struc WITH EMPTY KEY. + + "Internal table containing numbers and operators on whose basis + "calculations are to be carried out. + calc_itab = VALUE #( ( num1 = 123 operator = '+' num2 = 456 ) + ( num1 = -10 operator = '+' num2 = 9 ) + ( num1 = 12 operator = '-' num2 = 89 ) + ( num1 = -5 operator = '-' num2 = -12 ) + ( num1 = 11 operator = '*' num2 = 10 ) + ( num1 = -3 operator = '*' num2 = 3 ) + ( num1 = 1 operator = '/' num2 = 5 ) + ( num1 = -40 operator = '/' num2 = 2 ) + ( num1 = 5 operator = '/' num2 = 0 ) + ( num1 = 7 operator = '#' num2 = 4 ) ). + + DATA calc_results TYPE TABLE OF calc_results_struc WITH EMPTY KEY. + + LOOP AT calc_itab ASSIGNING FIELD-SYMBOL(). + + "Method call to calculate and return the result + DATA(res) = calc( num1 = -num1 + operator = -operator + num2 = -num2 ). + + "Adding the sy-tabix value to the table, too. + res-sy_tabix = sy-tabix. + + APPEND res TO calc_results. + + ENDLOOP. + + output->display( input = calc_results name = `calc_results`). + +********************************************************************** + + output->display( `17) SELECT Loop` ). + + "SELECT ... ENDSELECT statements loop across the result set of a database access. + "For more examples, see the cheat sheet example on ABAP SQL. + "The example is meant to give an idea. The SELECT loop is purposely done across + "an internal table which is also possible - just in the interest of not dealing + "with a database table and not ensuring that a demo database table is filled. + "As above, the example includes simple calculations. + + TYPES: BEGIN OF struc4loop, + num TYPE i, + calc_result TYPE i, + END OF struc4loop. + + TYPES ty_itab_select_loop TYPE TABLE OF struc4loop WITH EMPTY KEY. + + DATA(loop_pass) = 0. + + DATA(itab_select_loop) = VALUE ty_itab_select_loop( ( num = 1 ) + ( num = 2 ) + ( num = 3 ) + ( num = 4 ) + ( num = 5 ) + ( num = 6 ) ). + + SELECT * + FROM @itab_select_loop AS itab + INTO @DATA(wa). + + IF sy-subrc = 0. + + "Loop pass stored and representing the table index value for the + "ABAP SQL statement further down. + loop_pass += 1. + + IF loop_pass <= 3. + + wa-calc_result = wa-num * 5. + + ELSEIF loop_pass = 6. + + "No calculation for this loop pass. Loop is terminated. + EXIT. + + ELSE. + + wa-calc_result = wa-num * 100. + + ENDIF. + + "Inserting calculation result in table + MODIFY itab_select_loop FROM wa INDEX loop_pass TRANSPORTING calc_result. + + ENDIF. + + ENDSELECT. + + output->display( input = itab_select_loop name = `itab_select_loop`). + +********************************************************************** + + output->next_section( `Exception Handling` ). + + output->display( `18) TRY Control Structures` ). + + "TRY control structures are meant for handling catchable exceptions locally + "The example shows divisions. The predefined exception class cx_sy_zerodivide + "as suitable exception class is used. + "If the exception is not handled, the program is terminated and the runtime + "error COMPUTE_INT_ZERODIVIDE occurs. + "The third calculation is not carried out because the statement block is + "left due to the previous erroneous 0 division. + + TRY. + + DATA(div1) = 4 / 2. + output->display( input = div1 name = `div1`). + + DATA(div2) = 4 / 0. + output->display( input = div2 name = `div2`). + + DATA(div3) = 9 / 3. + output->display( input = div3 name = `div3`). + + CATCH cx_sy_zerodivide. + + output->display( `0 division. The exception was caught.`). + + ENDTRY. + + "The following example shows a catchable exception that is + "raised if a line is not found when using table expressions. + TRY. + + DATA(line) = str_table[ 12345 ]. + + "The predefined exception class cx_sy_itab_line_not_found + "as suitable exception class is used here. + "If the exception is not handled, the program is terminated + "and the runtime error ITAB_LINE_NOT_FOUND occurs. + CATCH cx_sy_itab_line_not_found. + + output->display( `The line was not found. The exception was caught.`). + + ENDTRY. + +********************************************************************** + + output->next_section( `19) Multiple CATCH Blocks` ). + + "It is possible to specify multiple exception classes in a list and + "multiple CATCH blocks. + "Note: If there are multiple CATCH blocks for exceptions that are in an inheritance + "relationship, you must pay attention that the more special exceptions are specified + "before the more general ones. + "The calculation example shows multiple CATCH blocks that themselves have more than + "one exception class specified. Here, local exception classes are specified just for + "demonstration purposes. They are not relevant in this TRY control structure. + + "Filling internal table of type i as basis for calculations + int_itab = VALUE #( ( 5 ) ( 0 ) ( 987654321 ) ). + + LOOP AT int_itab ASSIGNING FIELD-SYMBOL(). + + TRY. + + output->display( |--- Calculations with { } ---| ). + + DATA(calc1) = CONV decfloat34( 1 / ). + + output->display( input = calc1 name = `calc1`). + + DATA(calc2) = ipow( base = exp = 2 ). + + output->display( input = calc2 name = `calc2`). + + + CATCH cx_sy_arithmetic_overflow lcx_calc_error. + + output->display( `Arithmetic overflow. The exception was caught.`). + + CATCH cx_sy_zerodivide lcx_some_error. + + output->display( `0 division. The exception was caught.`). + + ENDTRY. + + ENDLOOP. + +********************************************************************** + + output->next_section( `20) Using an Exception Class Higher Up in the Inheritance Tree` ). + + "In the following CATCH block, the predefined exception class cx_sy_arithmetic_error + "is specified. Both cx_sy_zerodivide and cx_sy_arithmetic_overflow are derived from + "cx_sy_arithmetic_error which is an exception class higher up in the inheritance + "tree. Hence, cx_sy_arithmetic_error can be specified and handle both exceptions, too. + "The following example is basically the same as above. However, only one exception + "class is specified. + + LOOP AT int_itab ASSIGNING FIELD-SYMBOL(). + + TRY. + + output->display( |--- Calculations with { } ---| ). + + calc1 = 1 / . + + output->display( input = calc1 name = `calc1`). + + calc2 = ipow( base = exp = 2 ). + + output->display( input = calc2 name = `calc2`). + + + CATCH cx_sy_arithmetic_error. + + output->display( `Arithmetic error. The exception was caught.`). + + ENDTRY. + + ENDLOOP. + +********************************************************************** + + output->next_section( `21) Storing a Reference to the Exception Object` ). + + "You can use the addition INTO plus an object reference variable to store + "a reference to an exception object. It is, for example, relevant to + "determine the exact exception. + "The following example is the same as above using the more general exception + "class cx_sy_arithmetic_error. You can carry out certain tasks, for + "example, retrieving and displaying the exception text. To retrieve exception + "texts, you can call, for example, the method get_text. + + LOOP AT int_itab ASSIGNING FIELD-SYMBOL(). + + TRY. + + output->display( |--- Calculations with { } ---| ). + + calc1 = 1 / . + + output->display( input = calc1 name = `calc1`). + + calc2 = ipow( base = exp = 2 ). + + output->display( input = calc2 name = `calc2`). + + + CATCH cx_sy_arithmetic_error INTO exception. + "Note: + "- The object reference variable is of type cx_root. + "- You could also create the variable inline, e. g. ... INTO DATA(exc). + + "Retrieving and displaying exception text + exception_text = exception->get_text( ). + + output->display( input = exception_text name = `exception_text`). + + ENDTRY. + + ENDLOOP. + +********************************************************************** + + output->next_section( `22) Raising Exceptions Programmatically` ). + + "The following examples demonstrate the ABAP statement RAISE EXCEPTION + "using the addition TYPE. Note there are more additions available that + "are not dealt with here. + + "ABAP 'allows' zero division if the first operand is also 0 as shown + "here. + DATA(division) = 0 / 0. + + output->display( input = division name = `division`). + + "In this example, the appropriate exception - the predefined exception + "class cx_sy_zerodivide - is raised. + TRY. + division = 0 / 0. + + "raise predefined exception for 0 division + RAISE EXCEPTION TYPE cx_sy_zerodivide. + + CATCH cx_sy_zerodivide INTO DATA(error_oref). + + exception_text = error_oref->get_text( ). + + output->display( input = exception_text name = `exception_text`). + + ENDTRY. + + "The following example just demonstrates a locally defined + "exception class that must be raised programmatically. + "In ADT, see the definition of the class in the include, i. e. + "the tab 'Class-relevant Local Types'. + TRY. + RAISE EXCEPTION TYPE lcx_some_error. + + CATCH lcx_some_error INTO exception. + + exception_text = exception->get_text( ). + + output->display( `Default exception text for a local exception class:`). + output->display( input = exception_text name = `exception_text`). + + ENDTRY. + +********************************************************************** + + output->next_section( `23) Nested TRY Control Structure` ). + + TRY. + TRY. + RAISE EXCEPTION TYPE lcx_some_error. + CATCH lcx_some_error INTO DATA(error_inner_catch). + output->display( `Inner CATCH`). + RAISE EXCEPTION error_inner_catch. + ENDTRY. + CATCH lcx_some_error. + output->display( `Outer CATCH`). + ENDTRY. + +********************************************************************** + + output->next_section( `24) Raising an Exception in the Context of Conditional Expressions` ). + + "In this example, the optional addition THROW is used in a conditional + "expression. A self-defined local exception class is used. + + DATA(number) = 3. + + TRY. + DATA(cond_raise) = COND #( WHEN number = 1 THEN `one` + WHEN number = 2 THEN `two` + ELSE THROW lcx_some_error( ) ). + + output->display( input = cond_raise name = `cond_raise`). + + CATCH lcx_some_error INTO exception. + + exception_text = exception->get_text( ). + + output->display( input = exception_text name = `exception_text`). + + ENDTRY. + +********************************************************************** + + output->next_section( `25) RAISING Parameter in Method Delcarations` ). + + "In the following example, a string table is looped across. The table + "includes valid and invalid email addresses. The validity is checked + "in a method. In the method, a local exception class is raised if the + "email address is invalid. + + str_table = VALUE string_table( ( `john.doe@email.com` ) "valid + ( `john.doe@#email.com` ) "invalid + ( `jane.doe@email.com` ) "valid + ( `jane#doe@email.com` ) "invalid + ( `max.mustermann@email12.com` ) "valid + ( `max.mustermann@email12.c#m` ) "invalid + ( `some_name@email.com` ) "valid + ( `some_name@email.c` ) "invalid + ). + + TYPES: BEGIN OF struc_email_check, + email TYPE string, + is_email_valid TYPE abap_bool, + exception_raised TYPE abap_bool, + END OF struc_email_check. + + DATA itab_email_check TYPE TABLE OF struc_email_check WITH EMPTY KEY. + + LOOP AT str_table ASSIGNING FIELD-SYMBOL(). + + TRY. + DATA(email_valid) = validate_email( email = ). + DATA(exc_raised) = abap_false. + + CATCH lcx_invalid_email. + + email_valid = abap_false. + exc_raised = abap_true. + + ENDTRY. + + APPEND VALUE #( email = + is_email_valid = email_valid + exception_raised = exc_raised ) TO itab_email_check. + + ENDLOOP. + + output->display( input = itab_email_check name = `itab_email_check`). + +********************************************************************** + + output->next_section( `Exception Classes Derived from CX_STATIC_CHECK` ). + + "Exception Classes of type cx_static_check force users to handle exceptions. + "Exceptions that are declared in the method signature make users aware of which + "potential exception might occur. And the exceptions should be respected when using + "such a method. + "In this simplified example, a method has a self-defined local exception class + "specified for the RAISING addition. If an actual parameter has a certain + "value, the method raises an exception. + "You can comment in the following line of code to see the enforcement. + "A syntax warning is displayed since, there is no proper exception handling. + + "DATA(my_user_a) = whats_my_user( get_name = abap_false ). + + "Method call with a proper exception handling + TRY. + + DATA(my_user_b) = whats_my_user( get_name = abap_true ). + + output->display( input = my_user_b name = `my_user_b`). + + DATA(my_user_c) = whats_my_user( get_name = abap_false ). + + output->display( input = my_user_c name = `my_user_c`). + + CATCH lcx_static_exc_class INTO exception. + + exception_text = exception->get_text( ). + + output->display( input = exception_text name = `exception_texts`). + + ENDTRY. + +********************************************************************** + + output->next_section( `Exception Classes Derived from CX_DYNAMIC_CHECK` ). + + "Exception Classes derived from cx_dynamic_check are for exceptions that + "can be checked and avoided by preconditions. + "The checking if a local handling or an explicit declaration in procedure + "interfaces is available is carried out at runtime only ("dynamic check") + "and only in case the exception is indeed raised. + "The following example includes two TRY control structures in which a + "method is called that carries out calculations. In this case, the + "method uses the imported value and carries out two calculations (the + "value powered by 2 and the square root). + "The method signature purposely only includes the exception class + "cx_sy_arithmetic_overflow, i. e. calculation errors regarding the + "square root calculation are 'ignored' here. + "The two exception classes cx_sy_arithmetic_overflow and cx_sy_arg_out_of_domain + "are both derived from cx_dynamic_check. + + "This TRY control structure only catches exception for + "cx_sy_arithmetic_overflow. The example does purposely not + "include an error for the square root calculation. If you + "implemented the code outside of the TRY control structure, + "there would not be a syntax warning. Actually, you could + "prevent a calculation failure by setting appropriate values. + TRY. + + DATA(res1) = power2_and_sqrt( num = 4 ). + + output->display( input = res1 name = `res1`). + + DATA(res2) = power2_and_sqrt( num = 123456789 ). + + output->display( input = res2 name = `res2`). + + CATCH cx_sy_arithmetic_overflow INTO exception. + + exception_text = exception->get_text( ). + + output->display( input = exception_text name = `exception_text`). + + ENDTRY. + + "This TRY control structure demonstrates the following: + "If it is determined at runtime that an exception derived from cx_dynamic_check + "is neither locally handled nor an interface is declared appropriately - and the + "exception is raised - a new exception of type cx_sy_no_handler is raised. In this + "case, the attribute 'previous' contains a reference to the original exception. + TRY. + + DATA(res3) = power2_and_sqrt( num = 16 ). + + output->display( input = res3 name = `res3`). + + DATA(res4) = power2_and_sqrt( num = -1 ). + + output->display( input = res4 name = `res4`). + + "The specification of the suitable exception class does not help here. + "The error is raised while processing the method. + CATCH cx_sy_arg_out_of_domain INTO exception. + + exception_text = exception->get_text( ). + + output->display( input = exception_text name = `exception_text`). + + CATCH cx_sy_no_handler INTO exception. + + exception_text = exception->get_text( ). + + output->display( input = exception_text name = `exception_text`). + + "Attribute 'previous' + "In this case, the information of the actual runtime error is provided: + "Here, it is COMPUTE_SQRT_DOMAIN. + output->display( input = exception->previous name = `exception->previous`). + + "For demo purposes, RTTI is used here to retrieve the absolute name of the type, + "i. e. the exception class that was raised. The replace function is only used + "to clear the section 'sub' (which includes '\CLASS=') in 'val'. + DATA(absolute_name) = replace( + val = cl_abap_typedescr=>describe_by_object_ref( exception->previous )->absolute_name + sub = `\CLASS=` + with = `` ). + + output->display( input = absolute_name name = `absolute_name`). + + ENDTRY. + +********************************************************************** + + "Excursion: Runtime Errors and Terminating Programs + + output->next_section( `Excursion: Runtime Errors and Terminating Programs` ). + + "ASSERT statements are followed by a logical expression. If the expression is false, + "the program is terminated and an uncatchable exception is raised resulting in the + "runtime error ASSERTION_FAILED. + "You can comment out the code below to check out the effect. + "In ADT, you will see a message popping up and informing you about the runtime error. + "You can check the details by choosing the "Show" button in the pop-up. Furthermore, + "you can check the content of the "Feed Reader" tab in ADT. There, just expand your + "project and find the runtime errors caused by you. + + "Terminating a program using ASSERT + ASSERT 1 = 1. + + "ASSERT 1 + 2 = 5 - 3. + + DATA(some_flag) = abap_false. + + "ASSERT some_flag = abap_true. + + "Not handling predefined exception classes + "Caused runtime errors COMPUTE_INT_ZERODIVIDE and ITAB_LINE_NOT_FOUND + + "DATA(zero_division) = 1 / 0. + "DATA(nope) = str_table[ 12345 ]. + + "Not handling predefined exception classes + "Causes runtime error UNCAUGHT_EXCEPTION + + "DATA(is_email_valid) = validate_email( email = `john.doe@email.c##` ). + + output->display( `This text is displayed if you left all statements causing a runtime error commented out :)` ). + + ENDMETHOD. + + METHOD check_is_supplied. + IF num1 IS SUPPLIED. + APPEND `num1 is supplied` TO res. + ELSE. + APPEND `num1 is not supplied` TO res. + ENDIF. + + IF num2 IS NOT SUPPLIED. + APPEND `num2 is not supplied` TO res. + ELSE. + APPEND `num2 is supplied` TO res. + ENDIF. + ENDMETHOD. + + + METHOD addition. + res = num1 + num2. + ENDMETHOD. + + + METHOD calc. + "purposely without TRY ENDTRY block for simple example + + DATA calc_if TYPE string. + DATA calc_case TYPE string. + DATA calc_cond TYPE string. + DATA calc_switch TYPE string. + "DATA overall_results type string. + + +* IF operator = '+' +* OR operator = '-' +* OR operator = '*' +* OR operator = '/'. + + "IF statements + + IF operator = `+`. + calc_if = num1 + num2. + ELSEIF operator = `-`. + calc_if = num1 - num2. + ELSEIF operator = `*`. + calc_if = num1 * num2. + ELSEIF operator = `/`. + + IF num2 = 0. + calc_if = `Division by 0`. + ELSE. + calc_if = num1 / num2. + ENDIF. + ELSE. + calc_if = |Operator { operator } not possible.|. + ENDIF. + + + prep_calc_result( CHANGING res = calc_if ). + +* FIND PCRE `-$` IN calc_if. "trailing minus +* "negative number since it's string +* IF sy-subrc = 0. +* SHIFT calc_if BY 1 PLACES RIGHT CIRCULAR. +* ENDIF. + + "add result to itab + +* TYPES: BEGIN OF calc_results_struc, +* calculation TYPE string, +* result TYPE string, +* END OF calc_results_struc. +* +*data calc_result type table of calc_results_struc with empty key. + + + + "overall_results = overall_results && `IF: ` && result && ` / `. + + "CASE + + CASE operator. + WHEN '+'. + calc_case = num1 + num2. + WHEN '-'. + calc_case = num1 - num2. + WHEN '*'. + calc_case = num1 * num2. + WHEN '/'. + + CASE num2. + WHEN 0. + calc_case = `Division by 0`. + WHEN OTHERS. + calc_case = num1 / num2. + ENDCASE. + + WHEN OTHERS. + calc_case = |Check the operator { operator }.|. + ENDCASE. + +* FIND PCRE `-$` IN calc_case. "trailing minus +* "negative number since it's string +* IF sy-subrc = 0. +* SHIFT calc_case BY 1 PLACES RIGHT CIRCULAR. +* ENDIF. + + prep_calc_result( CHANGING res = calc_case ). + + "overall_results = overall_results && `CASE: ` && result && ` / `. + + + + "COND + + + "message = COND #( ). + + calc_cond = COND #( WHEN operator = '+' + THEN num1 + num2 + WHEN operator = '-' + THEN num1 - num2 + WHEN operator = '*' + THEN num1 * num2 + WHEN operator = '/' AND num2 = 0 THEN `Division by 0` + WHEN operator = '/' AND num2 <> 0 THEN num1 / num2 + ELSE |Check the operator { operator }.| + ). + + prep_calc_result( CHANGING res = calc_cond ). + +* FIND PCRE `-$` IN calc_cond. "trailing minus +* "negative number since it's string +* IF sy-subrc = 0. +* SHIFT calc_cond BY 1 PLACES RIGHT CIRCULAR. +* ENDIF. + + "overall_results = overall_results && `COND: ` && result && ` / `. + + + "SWITCH + + calc_switch = SWITCH #( operator + WHEN '+' THEN num1 + num2 + WHEN '-' THEN num1 - num2 + WHEN '*' THEN num1 * num2 + WHEN '/' THEN SWITCH #( num2 WHEN 0 THEN `Division by 0` ELSE num1 / num2 ) + ELSE |Check the operator { operator }.| ). + + prep_calc_result( CHANGING res = calc_switch ). + +* FIND PCRE `-$` IN calc_switch. "trailing minus +* "negative number since it's string +* IF sy-subrc = 0. +* SHIFT calc_switch BY 1 PLACES RIGHT CIRCULAR. +* ENDIF. + + "overall_results = overall_results && `SWITCH: ` && result && ` / `. + + + + + res = VALUE #( calculation = |{ num1 } { operator } { num2 }| + res_if = calc_if + res_case = calc_case + res_cond = calc_cond + res_switch = calc_switch + ). + + +* ELSE. +* result = |Hmmm... nothing was flagged in how_to_calc.|. +* ENDIF. + + ENDMETHOD. + + + METHOD meth_with_return. + + IF num >= 0. + DATA(sqr_res) = sqrt( num ). + ELSE. + RETURN. + ENDIF. + + res = `The method call was not terminated. The square root of ` && num && ` is ` && sqr_res. + + ENDMETHOD. + + + METHOD validate_email. + + IF matches( val = email + pcre = `\w+(\.\w+)*@(\w+\.)+(\w{2,4})` ). + is_valid_email = abap_true. + ELSE. + RAISE EXCEPTION TYPE lcx_invalid_email. + + ENDIF. + + + ENDMETHOD. + + + METHOD whats_my_user. + + + IF get_name = abap_true. + name = sy-uname. + ELSE. + RAISE EXCEPTION TYPE lcx_static_exc_class. + ENDIF. + + ENDMETHOD. + + METHOD power2_and_sqrt. + + result = |Calculation result:\n{ num } powered by 2 = { ipow( base = num exp = 2 ) }\nSquare root of { num } = { sqrt( num ) }|. + + ENDMETHOD. + + METHOD prep_calc_result. + + "condense res no-gaps. + + FIND PCRE `-$` IN res. "trailing minus + "negative number since it's string + IF sy-subrc = 0. + SHIFT res BY 1 PLACES RIGHT CIRCULAR. + ENDIF. + + "trailing .0 + IF res CP `*.0*`. + SHIFT res RIGHT DELETING TRAILING ` `. + SHIFT res LEFT DELETING LEADING ` `. + FIND PCRE `\.0$` IN res. + + IF sy-subrc = 0. + REPLACE `.0` IN res WITH ``. + ENDIF. + ENDIF. + + ENDMETHOD. + +ENDCLASS. diff --git a/src/zcl_demo_abap_prog_flow_logic.clas.locals_def.abap b/src/zcl_demo_abap_prog_flow_logic.clas.locals_def.abap new file mode 100644 index 0000000..557b52d --- /dev/null +++ b/src/zcl_demo_abap_prog_flow_logic.clas.locals_def.abap @@ -0,0 +1,11 @@ +CLASS lcx_calc_error DEFINITION INHERITING FROM cx_no_check. +ENDCLASS. + +CLASS lcx_some_error DEFINITION INHERITING FROM cx_no_check. +ENDCLASS. + +CLASS lcx_invalid_email DEFINITION INHERITING FROM cx_no_check. +ENDCLASS. + +CLASS lcx_static_exc_class DEFINITION INHERITING FROM cx_static_check. +ENDCLASS. diff --git a/src/zcl_demo_abap_prog_flow_logic.clas.xml b/src/zcl_demo_abap_prog_flow_logic.clas.xml new file mode 100644 index 0000000..a78892a --- /dev/null +++ b/src/zcl_demo_abap_prog_flow_logic.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_PROG_FLOW_LOGIC + E + ABAP cheat sheet: Program Flow Logic + 1 + X + X + 5 + + + +