1293 lines
48 KiB
ABAP
1293 lines
48 KiB
ABAP
"! <p class="shorttext"><strong>Program flow logic</strong><br/>ABAP cheat sheet example class</p>
|
|
"!
|
|
"! <p>The example class demonstrates program flow logic.<br/>
|
|
"! Choose F9 in ADT to run the class.</p>
|
|
"!
|
|
"! <h2>Note</h2>
|
|
"! <ul><li>Topics covered include expressions and functions for conditions,
|
|
"! control structures with IF and CASE, the COND and SWITCH operators,
|
|
"! unconditional loops with DO, conditional loops with WHILE, handling exceptions</li>
|
|
"! <li>Find information on <strong>getting started with the example class</strong> and the
|
|
"! <strong>disclaimer</strong> in the ABAP Doc comment of class {@link zcl_demo_abap_aux}.</li></ul>
|
|
CLASS zcl_demo_abap_prog_flow_logic DEFINITION
|
|
PUBLIC
|
|
FINAL
|
|
CREATE PUBLIC .
|
|
|
|
PUBLIC SECTION.
|
|
INTERFACES: if_oo_adt_classrun.
|
|
|
|
PROTECTED SECTION.
|
|
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 addition.
|
|
res = num1 + num2.
|
|
ENDMETHOD.
|
|
|
|
|
|
METHOD calc.
|
|
|
|
DATA calc_if TYPE string.
|
|
DATA calc_case TYPE string.
|
|
DATA calc_cond TYPE string.
|
|
DATA calc_switch TYPE string.
|
|
|
|
"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 = |Check the operator { operator }.|.
|
|
ENDIF.
|
|
|
|
prep_calc_result( CHANGING res = calc_if ).
|
|
|
|
"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.
|
|
|
|
prep_calc_result( CHANGING res = calc_case ).
|
|
|
|
"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 ).
|
|
|
|
"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 ).
|
|
|
|
res = VALUE #( calculation = |{ num1 } { operator } { num2 }|
|
|
res_if = calc_if
|
|
res_case = calc_case
|
|
res_cond = calc_cond
|
|
res_switch = calc_switch
|
|
).
|
|
|
|
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 if_oo_adt_classrun~main.
|
|
|
|
out->write( |ABAP Cheat Sheet Example: Program Flow Logic\n\n| ).
|
|
|
|
**********************************************************************
|
|
|
|
out->write( |1) Control Structure with IF\n| ).
|
|
|
|
"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.
|
|
|
|
"Determining some operators for a calculation
|
|
DATA(operators) = VALUE string_table( ( `+` ) ( `-` ) ( `?` ) ).
|
|
|
|
"Getting a random operator from the table
|
|
DATA(idx) = cl_abap_random_int=>create(
|
|
seed = cl_abap_random=>seed( ) min = 1
|
|
max = lines( operators ) )->get_next( ).
|
|
|
|
DATA(operator) = operators[ idx ].
|
|
DATA(num1) = 5.
|
|
DATA(num2) = 7.
|
|
|
|
IF operator = `+`.
|
|
out->write( |The result of { num1 } { operator } { num2 } is { num1 + num2 }. | ).
|
|
ELSEIF operator = `-`.
|
|
out->write( |The result of { num1 } { operator } { num2 } is { num1 - num2 }. | ).
|
|
ELSE.
|
|
out->write( |The operator { operator } is not possible.| ).
|
|
ENDIF.
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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.
|
|
out->write( |'{ to_be_found }' was found in the string '{ string_to_search }'.| ).
|
|
ELSE.
|
|
out->write( |'{ to_be_found }' was not found in the string '{ string_to_search }'.| ).
|
|
ENDIF.
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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 '120001' AND '180000'
|
|
THEN |It's { syst_time TIME = ISO }. Good afternoon, { sy-uname }.|
|
|
WHEN syst_time BETWEEN '180001' AND '220000'
|
|
THEN |It's { syst_time TIME = ISO }. Good evening, { sy-uname }.|
|
|
ELSE |It's { syst_time TIME = ISO }. Good night, { sy-uname }.| ).
|
|
|
|
out->write( data = greetings name = `greetings` ).
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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 cover 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 <fs> TYPE string.
|
|
ASSIGN `hallo` TO <fs>.
|
|
|
|
"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 <fs> 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 = <fs> 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 = <fs> 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` = <fs>
|
|
|
|
"Constructor expression
|
|
AND VALUE i( ) = 0
|
|
AND VALUE string_table( ( `a` ) ( `b` ) ( `c` ) ) = str_table
|
|
|
|
"Table expression
|
|
AND str_table[ 2 ] = `b`.
|
|
out->write( `All of the logical expressions are true.` ).
|
|
ELSE.
|
|
out->write( `At least one of the logical expressions is false.` ).
|
|
ENDIF.
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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 ).
|
|
|
|
out->write( data = is_supplied name = `is_supplied` ).
|
|
out->write( |\n| ).
|
|
|
|
is_supplied = check_is_supplied( num2 = 456 ).
|
|
|
|
out->write( data = is_supplied name = `is_supplied` ).
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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 above.
|
|
|
|
"Determining some operators for a calculation
|
|
operators = VALUE string_table( ( `+` ) ( `-` ) ( `#` ) ).
|
|
|
|
"Getting a random operator from the table
|
|
idx = cl_abap_random_int=>create(
|
|
seed = cl_abap_random=>seed( ) min = 1
|
|
max = lines( operators ) )->get_next( ).
|
|
|
|
DATA(op) = operators[ idx ].
|
|
DATA(n1) = 8.
|
|
DATA(n2) = 3.
|
|
|
|
"Simple calculation
|
|
|
|
CASE op.
|
|
WHEN '+'.
|
|
out->write( |The result of { n1 } { op } { n2 } is { n1 + n2 }. | ).
|
|
WHEN '-'.
|
|
out->write( |The result of { n1 } { op } { n2 } is { n1 - n2 }. | ).
|
|
WHEN OTHERS.
|
|
out->write( |The operator { op } is not possible.| ).
|
|
ENDCASE.
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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.
|
|
out->write( `Type zcl_demo_abap_prog_flow_logic? True!` ).
|
|
WHEN TYPE if_oo_adt_classrun.
|
|
out->write( `Type if_oo_adt_classrun? True!` ).
|
|
WHEN TYPE zcl_demo_abap_sql.
|
|
out->write( `Type zcl_demo_abap_sql? True!` ).
|
|
WHEN OTHERS.
|
|
out->write( `Other type.` ).
|
|
ENDCASE.
|
|
|
|
out->write( |\n| ).
|
|
|
|
"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.
|
|
out->write( `Type if_oo_adt_classrun? True!` ).
|
|
ELSEIF oref_check IS INSTANCE OF zcl_demo_abap_prog_flow_logic.
|
|
out->write( `Type zcl_demo_abap_prog_flow_logic? True!` ).
|
|
ELSEIF oref_check IS INSTANCE OF zcl_demo_abap_sql.
|
|
out->write( `Type zcl_demo_abap_sql? True!` ).
|
|
ELSE.
|
|
out->write( `Other type.` ).
|
|
ENDIF.
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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 ...` ).
|
|
|
|
out->write( data = switch_res name = `switch_res` ).
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `Loops (Iterations)` ) ).
|
|
|
|
out->write( |9) Unconditional Loops with DO\n| ).
|
|
|
|
"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.
|
|
|
|
out->write( data = do_counter name = `do_counter` ).
|
|
out->write( |\n| ).
|
|
out->write( data = do_sy_index name = `do_sy_index` ).
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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.
|
|
|
|
out->write( data = do_counter name = `do_counter` ).
|
|
out->write( |\n| ).
|
|
out->write( data = do_sy_index name = `do_sy_index` ).
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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.
|
|
|
|
out->write( data = do_counter name = `do_counter` ).
|
|
out->write( |\n| ).
|
|
out->write( data = do_sy_index name = `do_sy_index` ).
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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 ).
|
|
|
|
out->write( data = return1 name = `return1` ).
|
|
out->write( |\n| ).
|
|
|
|
DATA(return2) = meth_with_return( -9 ).
|
|
|
|
out->write( data = return2 name = `return2` ).
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `Conditional Loops with WHILE` ) ).
|
|
|
|
out->write( |13) WHILE Example 1\n| ).
|
|
|
|
"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.
|
|
|
|
out->write( data = int_itab name = `int_itab` ).
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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 is also displayed - 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.
|
|
|
|
out->write( data = count_occ_via_sy_index name = `count_occ_via_sy_index` ).
|
|
out->write( |\n| ).
|
|
out->write( data = count_occ name = `count_occ` ).
|
|
out->write( |\n| ).
|
|
out->write( data = while_string name = `while_string` ).
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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.
|
|
|
|
out->write( data = while_cnt name = `while_cnt` ).
|
|
out->write( |\n| ).
|
|
out->write( data = while_sy_index name = `while_sy_index` ).
|
|
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `Loops across Tables` ) ).
|
|
|
|
out->write( |16) Loop across Internal Table Using LOOP ... ENDLOOP\n\n| ).
|
|
|
|
"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(<calc>).
|
|
"Method call to calculate and return the result
|
|
DATA(res) = calc( num1 = <calc>-num1
|
|
operator = <calc>-operator
|
|
num2 = <calc>-num2 ).
|
|
|
|
"Adding the sy-tabix value to the table, too.
|
|
res-sy_tabix = sy-tabix.
|
|
APPEND res TO calc_results.
|
|
ENDLOOP.
|
|
|
|
out->write( data = calc_results name = `calc_results` ).
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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.
|
|
|
|
out->write( data = itab_select_loop name = `itab_select_loop` ).
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `Exception Handling` ) ).
|
|
|
|
out->write( |18) TRY Control Structures\n\n| ).
|
|
|
|
"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.
|
|
out->write( data = div1 name = `div1` ).
|
|
out->write( |\n| ).
|
|
|
|
DATA(div2) = 4 / 0.
|
|
out->write( data = div2 name = `div2` ).
|
|
out->write( |\n| ).
|
|
|
|
DATA(div3) = 9 / 3.
|
|
out->write( data = div3 name = `div3` ).
|
|
out->write( |\n| ).
|
|
|
|
CATCH cx_sy_zerodivide.
|
|
out->write( `0 division. The exception was caught.` ).
|
|
out->write( |\n| ).
|
|
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.
|
|
out->write( `The line was not found. The exception was caught.` ).
|
|
ENDTRY.
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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(<fs_int>).
|
|
TRY.
|
|
out->write( |--- Calculations with { <fs_int> } ---| ).
|
|
|
|
DATA(calc1) = CONV decfloat34( 1 / <fs_int> ).
|
|
|
|
out->write( data = calc1 name = `calc1` ).
|
|
out->write( |\n| ).
|
|
|
|
DATA(calc2) = ipow( base = <fs_int> exp = 2 ).
|
|
|
|
out->write( data = calc2 name = `calc2` ).
|
|
out->write( |\n| ).
|
|
CATCH cx_sy_arithmetic_overflow lcx_calc_error.
|
|
out->write( `Arithmetic overflow. The exception was caught.` ).
|
|
CATCH cx_sy_zerodivide lcx_some_error.
|
|
out->write( `0 division. The exception was caught.` ).
|
|
ENDTRY.
|
|
out->write( |\n| ).
|
|
ENDLOOP.
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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(<fs_int_inh>).
|
|
TRY.
|
|
out->write( |--- Calculations with { <fs_int_inh> } ---| ).
|
|
|
|
calc1 = 1 / <fs_int_inh>.
|
|
|
|
out->write( data = calc1 name = `calc1` ).
|
|
out->write( |\n| ).
|
|
|
|
calc2 = ipow( base = <fs_int_inh> exp = 2 ).
|
|
|
|
out->write( data = calc2 name = `calc2` ).
|
|
out->write( |\n| ).
|
|
CATCH cx_sy_arithmetic_error.
|
|
out->write( `Arithmetic error. The exception was caught.` ).
|
|
ENDTRY.
|
|
out->write( |\n| ).
|
|
ENDLOOP.
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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(<fs_int_into>).
|
|
|
|
TRY.
|
|
out->write( |--- Calculations with { <fs_int_into> } ---| ).
|
|
|
|
calc1 = 1 / <fs_int_into>.
|
|
|
|
out->write( data = calc1 name = `calc1` ).
|
|
out->write( |\n| ).
|
|
|
|
calc2 = ipow( base = <fs_int_into> exp = 2 ).
|
|
|
|
out->write( data = calc2 name = `calc2` ).
|
|
out->write( |\n| ).
|
|
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( ).
|
|
|
|
out->write( data = exception_text name = `exception_text` ).
|
|
ENDTRY.
|
|
out->write( |\n| ).
|
|
ENDLOOP.
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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.
|
|
|
|
out->write( data = division name = `division` ).
|
|
out->write( |\n| ).
|
|
|
|
"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( ).
|
|
out->write( data = exception_text name = `exception_text` ).
|
|
out->write( |\n| ).
|
|
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( ).
|
|
|
|
out->write( `Default exception text for a local exception class:` ).
|
|
out->write( data = exception_text name = `exception_text` ).
|
|
ENDTRY.
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `23) Nested TRY Control Structure` ) ).
|
|
|
|
TRY.
|
|
TRY.
|
|
RAISE EXCEPTION TYPE lcx_some_error.
|
|
CATCH lcx_some_error INTO DATA(error_inner_catch).
|
|
out->write( `Inner CATCH` ).
|
|
RAISE EXCEPTION error_inner_catch.
|
|
ENDTRY.
|
|
CATCH lcx_some_error.
|
|
out->write( `Outer CATCH` ).
|
|
ENDTRY.
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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( ) ).
|
|
|
|
out->write( data = cond_raise name = `cond_raise` ).
|
|
CATCH lcx_some_error INTO exception.
|
|
exception_text = exception->get_text( ).
|
|
out->write( data = exception_text name = `exception_text` ).
|
|
ENDTRY.
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `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(<email>).
|
|
TRY.
|
|
DATA(email_valid) = validate_email( email = <email> ).
|
|
DATA(exc_raised) = abap_false.
|
|
CATCH lcx_invalid_email.
|
|
email_valid = abap_false.
|
|
exc_raised = abap_true.
|
|
ENDTRY.
|
|
|
|
APPEND VALUE #( email = <email>
|
|
is_email_valid = email_valid
|
|
exception_raised = exc_raised ) TO itab_email_check.
|
|
ENDLOOP.
|
|
|
|
out->write( data = itab_email_check name = `itab_email_check` ).
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `26) 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 ).
|
|
|
|
out->write( data = my_user_b name = `my_user_b` ).
|
|
out->write( |\n| ).
|
|
|
|
DATA(my_user_c) = whats_my_user( get_name = abap_false ).
|
|
|
|
out->write( data = my_user_c name = `my_user_c` ).
|
|
CATCH lcx_static_exc_class INTO exception.
|
|
exception_text = exception->get_text( ).
|
|
out->write( data = exception_text name = `exception_texts` ).
|
|
ENDTRY.
|
|
|
|
**********************************************************************
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `27) 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 ).
|
|
|
|
out->write( data = res1 name = `res1` ).
|
|
out->write( |\n| ).
|
|
|
|
DATA(res2) = power2_and_sqrt( num = 123456789 ).
|
|
|
|
out->write( data = res2 name = `res2` ).
|
|
out->write( |\n| ).
|
|
|
|
CATCH cx_sy_arithmetic_overflow INTO exception.
|
|
exception_text = exception->get_text( ).
|
|
out->write( data = exception_text name = `exception_text` ).
|
|
out->write( |\n| ).
|
|
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 ).
|
|
|
|
out->write( data = res3 name = `res3` ).
|
|
out->write( |\n| ).
|
|
|
|
DATA(res4) = power2_and_sqrt( num = -1 ).
|
|
|
|
out->write( data = res4 name = `res4` ).
|
|
out->write( |\n| ).
|
|
|
|
"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( ).
|
|
|
|
out->write( data = exception_text name = `exception_text` ).
|
|
out->write( |\n| ).
|
|
CATCH cx_sy_no_handler INTO exception.
|
|
exception_text = exception->get_text( ).
|
|
|
|
out->write( data = exception_text name = `exception_text` ).
|
|
out->write( |\n| ).
|
|
|
|
"Attribute 'previous'
|
|
"In this case, the information of the actual runtime error is provided:
|
|
"Here, it is COMPUTE_SQRT_DOMAIN.
|
|
out->write( data = exception->previous name = `exception->previous` ).
|
|
out->write( |\n| ).
|
|
|
|
"For demo purposes, RTTI is used here to retrieve the relative name of the type,
|
|
"i. e. the exception class that was raised.
|
|
DATA(relative_name) = cl_abap_typedescr=>describe_by_object_ref( exception->previous )->get_relative_name( ).
|
|
|
|
out->write( data = relative_name name = `relative_name` ).
|
|
ENDTRY.
|
|
|
|
**********************************************************************
|
|
|
|
"Excursion: Runtime Errors and Terminating Programs
|
|
|
|
out->write( zcl_demo_abap_aux=>heading( `28) 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 in the code that is commented out 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 self-defined exception classes
|
|
"Causes runtime error UNCAUGHT_EXCEPTION
|
|
|
|
"DATA(is_email_valid) = validate_email( email = `john.doe@email.c##` ).
|
|
|
|
out->write( `This text is displayed if you left all statements causing a runtime error commented out :)` ).
|
|
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 power2_and_sqrt.
|
|
result = |{ num } powered by 2 = { ipow( base = num exp = 2 ) } / Square root of { num } = { sqrt( num ) }|.
|
|
ENDMETHOD.
|
|
|
|
|
|
METHOD prep_calc_result.
|
|
FIND PCRE `-$` IN res. "trailing minus
|
|
|
|
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.
|
|
|
|
|
|
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.
|
|
ENDCLASS.
|