commit 6d24d7d2699c6608fe7791c6c73aa3040b8a9c9e Author: Bruno Date: Mon Jul 7 16:01:27 2025 -0300 first commit diff --git a/.abapgit.xml b/.abapgit.xml new file mode 100644 index 0000000..1e91166 --- /dev/null +++ b/.abapgit.xml @@ -0,0 +1,20 @@ + + + + + E + /src/ + FULL + + /.gitignore + /LICENSE + /README.md + /package.json + /.travis.yml + /.gitlab-ci.yml + /abaplint.json + /azure-pipelines.yml + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/src/ezdemo_abap_lock.enqu.xml b/src/ezdemo_abap_lock.enqu.xml new file mode 100644 index 0000000..8d677f1 --- /dev/null +++ b/src/ezdemo_abap_lock.enqu.xml @@ -0,0 +1,44 @@ + + + + + + EZDEMO_ABAP_LOCK + E + E + ZDEMO_ABAP_RAPT1 + Lock on demo table + 5 + + + + EZDEMO_ABAP_LOCK + ZDEMO_ABAP_RAPT1 + 0001 + ZDEMO_ABAP_RAPT1 + E + + + + + EZDEMO_ABAP_LOCK + 0001 + CLIENT + ZDEMO_ABAP_RAPT1 + CLIENT + X + E + + + EZDEMO_ABAP_LOCK + 0002 + KEY_FIELD + ZDEMO_ABAP_RAPT1 + KEY_FIELD + X + E + + + + + diff --git a/src/package.devc.xml b/src/package.devc.xml new file mode 100644 index 0000000..f4825f9 --- /dev/null +++ b/src/package.devc.xml @@ -0,0 +1,10 @@ + + + + + + ABAP Cheat Sheets + + + + diff --git a/src/zbp_demo_abap_rap_draft_m.clas.abap b/src/zbp_demo_abap_rap_draft_m.clas.abap new file mode 100644 index 0000000..1ad11b7 --- /dev/null +++ b/src/zbp_demo_abap_rap_draft_m.clas.abap @@ -0,0 +1,20 @@ +*********************************************************************** +* +* RAP BO provider (i. e. ABAP behavior pool/ABP) +* for a RAP demo scenario +* +* See more information in the CCIMP include (local types tab in ADT). +* +********************************************************************** +"!

Behavior implementation for RAP demo scenario (draft BO)

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

Behavior implementation for RAP demo scenario (managed BO)

+"! The class represents a RAP BO provider (i. e. an ABAP behavior pool/ABP) for a RAP demo scenario +"! (managed RAP BO with external numbering). +CLASS zbp_demo_abap_rap_ro_m DEFINITION PUBLIC ABSTRACT FINAL FOR BEHAVIOR OF zdemo_abap_rap_ro_m. + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + + +CLASS ZBP_DEMO_ABAP_RAP_RO_M IMPLEMENTATION. +ENDCLASS. diff --git a/src/zbp_demo_abap_rap_ro_m.clas.locals_imp.abap b/src/zbp_demo_abap_rap_ro_m.clas.locals_imp.abap new file mode 100644 index 0000000..0826a5c --- /dev/null +++ b/src/zbp_demo_abap_rap_ro_m.clas.locals_imp.abap @@ -0,0 +1,124 @@ +*********************************************************************** +* +* RAP BO provider (i. e. ABAP behavior pool/ABP) +* for a RAP demo scenario +* +* - RAP scenario: managed RAP BO, external numbering +* - Data model: Consists of a root entity and one child entity. The BDEF +* defines the behavior for these two entities which are connected via +* a CDS composition relation. The definitions in the BDEF determine +* which methods must be implemented in this ABAP behavior pool (ABP). +* +* ----------------------------- NOTE ----------------------------------- +* This simplified example is not a real life scenario and rather +* focuses on the technical side by giving an idea how the communication +* and data exchange between a RAP BO consumer, which is a class +* in this case, and RAP BO provider can work. Additionally, it shows +* how the methods for non-standard RAP BO operations might be +* self-implemented in an ABP. The example is is intentionally kept +* short and simple and focuses on specific RAP aspects. For this reason, +* the example might not fully meet the requirements of the RAP BO contract. +* +* The code presented in this class is intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** + +CLASS lhc_root DEFINITION INHERITING FROM cl_abap_behavior_handler. + PRIVATE SECTION. + + METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION + IMPORTING REQUEST requested_authorizations FOR root RESULT result. + + METHODS multiply_by_2 FOR MODIFY + IMPORTING keys FOR ACTION root~multiply_by_2. + + METHODS det_add_text FOR DETERMINE ON SAVE + IMPORTING keys FOR root~det_add_text. + + METHODS val FOR VALIDATE ON SAVE + IMPORTING keys FOR root~val. + +ENDCLASS. + +CLASS lhc_root IMPLEMENTATION. + + METHOD get_global_authorizations. + ENDMETHOD. + + METHOD multiply_by_2. + + "Retrieving instances based on requested keys + READ ENTITIES OF zdemo_abap_rap_ro_m IN LOCAL MODE + ENTITY root + FIELDS ( field3 field4 ) WITH CORRESPONDING #( keys ) + RESULT DATA(result) + FAILED failed. + + "If read result is initial, stop further method execution. + CHECK result IS NOT INITIAL. + + "Multiply integer values by 2 + MODIFY ENTITIES OF zdemo_abap_rap_ro_m IN LOCAL MODE + ENTITY root + UPDATE FIELDS ( field3 field4 ) WITH VALUE #( FOR key IN result ( %tky = key-%tky + field3 = key-field3 * 2 + field4 = key-field4 * 2 ) ). + ENDMETHOD. + + METHOD det_add_text. + + READ ENTITIES OF zdemo_abap_rap_ro_m IN LOCAL MODE + ENTITY root + FIELDS ( field2 ) WITH CORRESPONDING #( keys ) + RESULT DATA(lt_res). + + "If read result is initial, stop further method execution. + CHECK lt_res IS NOT INITIAL. + + "field2 is changed + MODIFY ENTITIES OF zdemo_abap_rap_ro_m IN LOCAL MODE + ENTITY root + UPDATE FIELDS ( field2 ) + WITH VALUE #( FOR key IN lt_res ( %tky = key-%tky + field2 = |{ key-field2 }_#| ) ). + + ENDMETHOD. + + METHOD val. + + READ ENTITIES OF zdemo_abap_rap_ro_m IN LOCAL MODE + ENTITY root + FIELDS ( field3 ) WITH CORRESPONDING #( keys ) + RESULT DATA(lt_res). + + "If read result is initial, stop further method execution. + CHECK lt_res IS NOT INITIAL. + + LOOP AT lt_res ASSIGNING FIELD-SYMBOL(). + IF -field3 > 1000. + APPEND VALUE #( %tky = -%tky + %fail-cause = if_abap_behv=>cause-disabled + ) + TO failed-root. + + APPEND VALUE #( %tky = -%tky + %msg = new_message_with_text( + severity = if_abap_behv_message=>severity-error + text = 'Validation failed!' ) + ) TO reported-root. + ENDIF. + ENDLOOP. + + ENDMETHOD. + +ENDCLASS. diff --git a/src/zbp_demo_abap_rap_ro_m.clas.xml b/src/zbp_demo_abap_rap_ro_m.clas.xml new file mode 100644 index 0000000..919f460 --- /dev/null +++ b/src/zbp_demo_abap_rap_ro_m.clas.xml @@ -0,0 +1,18 @@ + + + + + + ZBP_DEMO_ABAP_RAP_RO_M + E + Behavior implementation for RAP demo scenario (managed BO) + 06 + 1 + X + X + X + ZDEMO_ABAP_RAP_RO_M + + + + diff --git a/src/zbp_demo_abap_rap_ro_m_as.clas.abap b/src/zbp_demo_abap_rap_ro_m_as.clas.abap new file mode 100644 index 0000000..4e51696 --- /dev/null +++ b/src/zbp_demo_abap_rap_ro_m_as.clas.abap @@ -0,0 +1,7 @@ +CLASS zbp_demo_abap_rap_ro_m_as DEFINITION PUBLIC ABSTRACT FINAL FOR BEHAVIOR OF zdemo_abap_rap_ro_m_as. + PUBLIC SECTION. + CLASS-DATA num_raised_events TYPE i. +ENDCLASS. + +CLASS zbp_demo_abap_rap_ro_m_as IMPLEMENTATION. +ENDCLASS. diff --git a/src/zbp_demo_abap_rap_ro_m_as.clas.locals_imp.abap b/src/zbp_demo_abap_rap_ro_m_as.clas.locals_imp.abap new file mode 100644 index 0000000..59c18c1 --- /dev/null +++ b/src/zbp_demo_abap_rap_ro_m_as.clas.locals_imp.abap @@ -0,0 +1,109 @@ +CLASS lhc_zdemo_abap_rap_ro_m_as DEFINITION INHERITING FROM cl_abap_behavior_handler. + PRIVATE SECTION. + + METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION + IMPORTING REQUEST requested_authorizations FOR root RESULT result. + + METHODS calc FOR MODIFY + IMPORTING keys FOR ACTION root~calc. + + METHODS det_modify FOR DETERMINE ON MODIFY + IMPORTING keys FOR root~det_modify. + +ENDCLASS. + +CLASS lhc_zdemo_abap_rap_ro_m_as IMPLEMENTATION. + + METHOD get_global_authorizations. + ENDMETHOD. + + METHOD calc. + READ ENTITY IN LOCAL MODE zdemo_abap_rap_ro_m_as + FIELDS ( num1 num2 arithm_op crea_date_time lchg_date_time ) WITH CORRESPONDING #( keys ) + RESULT DATA(lt_calc) + FAILED DATA(f). + + DATA(timestamp) = cl_abap_tstmp=>utclong2tstmp( utclong_current( ) ). + + LOOP AT lt_calc ASSIGNING FIELD-SYMBOL(). + TRY. + -calc_result = SWITCH #( -arithm_op + WHEN `+` THEN |{ CONV decfloat34( -num1 + -num2 ) STYLE = SIMPLE }| + WHEN `-` THEN |{ CONV decfloat34( -num1 - -num2 ) STYLE = SIMPLE }| + WHEN `*` THEN |{ CONV decfloat34( -num1 * -num2 ) STYLE = SIMPLE }| + WHEN `/` THEN |{ CONV decfloat34( -num1 / -num2 ) STYLE = SIMPLE }| + WHEN `P` THEN |{ CONV decfloat34( ipow( base = -num1 exp = -num2 ) ) STYLE = SIMPLE }| + ELSE `Wrong operator` ). + + "Handling the fact that ABAP allows division by zero if the dividend itself is zero. + IF -num1 = 0 AND -num2 = 0 AND -arithm_op = `/`. + -calc_result = `Division by 0`. + ENDIF. + CATCH cx_sy_zerodivide. + -calc_result = `Division by 0`. + CATCH cx_sy_arithmetic_overflow. + -calc_result = `Overflow error`. + ENDTRY. + ENDLOOP. + + MODIFY ENTITY IN LOCAL MODE zdemo_abap_rap_ro_m_as + UPDATE FIELDS ( calc_result ) + WITH CORRESPONDING #( lt_calc ). + ENDMETHOD. + + METHOD det_modify. + MODIFY ENTITY IN LOCAL MODE zdemo_abap_rap_ro_m_as + EXECUTE calc + FROM CORRESPONDING #( keys ). + ENDMETHOD. + +ENDCLASS. + +CLASS lsc_zdemo_abap_rap_ro_m_as DEFINITION INHERITING FROM cl_abap_behavior_saver. + PROTECTED SECTION. + + METHODS save_modified REDEFINITION. + + METHODS cleanup_finalize REDEFINITION. + +ENDCLASS. + +CLASS lsc_zdemo_abap_rap_ro_m_as IMPLEMENTATION. + + METHOD save_modified. + IF create-root IS NOT INITIAL. + RAISE ENTITY EVENT zdemo_abap_rap_ro_m_as~created + FROM VALUE #( FOR IN create-root ( + %key = VALUE #( id = -id ) ) ). + + zbp_demo_abap_rap_ro_m_as=>num_raised_events = zbp_demo_abap_rap_ro_m_as=>num_raised_events + lines( create-root ). + ENDIF. + + IF update-root IS NOT INITIAL. + "Demonstrating the BDEF derived type TYPE TABLE FOR EVENT + DATA evt_tab_up TYPE TABLE FOR EVENT zdemo_abap_rap_ro_m_as~updated. + + evt_tab_up = VALUE #( FOR IN update-root INDEX INTO updidx ( + %key = VALUE #( id = -id ) + %param = VALUE #( col1 = 'Event raised' + col2 = |UPDATED ({ updidx })| ) ) ). + RAISE ENTITY EVENT zdemo_abap_rap_ro_m_as~updated FROM evt_tab_up. + + zbp_demo_abap_rap_ro_m_as=>num_raised_events = zbp_demo_abap_rap_ro_m_as=>num_raised_events + lines( update-root ). + ENDIF. + + IF delete-root IS NOT INITIAL. + RAISE ENTITY EVENT zdemo_abap_rap_ro_m_as~deleted + FROM VALUE #( FOR IN delete-root INDEX INTO delidx ( + %key = VALUE #( id = -id ) + %param = VALUE #( col1 = 'Event raised' + col2 = |DELETED ({ delidx })| ) ) ). + + zbp_demo_abap_rap_ro_m_as=>num_raised_events = zbp_demo_abap_rap_ro_m_as=>num_raised_events + lines( delete-root ). + ENDIF. + ENDMETHOD. + + METHOD cleanup_finalize. + ENDMETHOD. + +ENDCLASS. diff --git a/src/zbp_demo_abap_rap_ro_m_as.clas.xml b/src/zbp_demo_abap_rap_ro_m_as.clas.xml new file mode 100644 index 0000000..9569b06 --- /dev/null +++ b/src/zbp_demo_abap_rap_ro_m_as.clas.xml @@ -0,0 +1,18 @@ + + + + + + ZBP_DEMO_ABAP_RAP_RO_M_AS + E + Behavior Implementation for ZDEMO_ABAP_RAP_RO_M_AS + 06 + 1 + X + X + X + ZDEMO_ABAP_RAP_RO_M_AS + + + + diff --git a/src/zbp_demo_abap_rap_ro_u.clas.abap b/src/zbp_demo_abap_rap_ro_u.clas.abap new file mode 100644 index 0000000..dc82b65 --- /dev/null +++ b/src/zbp_demo_abap_rap_ro_u.clas.abap @@ -0,0 +1,20 @@ +*********************************************************************** +* +* RAP BO provider (i. e. ABAP behavior pool/ABP) +* for a RAP demo scenario +* +* See more information in the CCIMP include (local types tab in ADT). +* +********************************************************************** +"!

Behavior implementation for RAP demo scenario (unmanaged BO)

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

ABAP cheat sheet: AMDP

+"! Example to demonstrate AMDP procedures and functions.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_amdp DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: + if_oo_adt_classrun, "Interface for displaying output + if_amdp_marker_hdb. "This interface specification is mandatory for an AMDP class + + "Various internal table type specifications for the parameters of AMDP methods + "Note: Only table and elementary data types are possible for the parameters. + TYPES carr_tab TYPE STANDARD TABLE OF zdemo_abap_carr_ve WITH EMPTY KEY. + + TYPES fli_tab TYPE STANDARD TABLE OF zdemo_abap_fli_ve WITH EMPTY KEY. + + TYPES: + "Structured data type as basis for the table type below + BEGIN OF carr_fli_struc, + carrname TYPE zdemo_abap_carr_ve-carrname, + connid TYPE zdemo_abap_flsch_ve-connid, + cityfrom TYPE zdemo_abap_flsch_ve-cityfrom, + cityto TYPE zdemo_abap_flsch_ve-cityto, + END OF carr_fli_struc, + + "Internal table type + carr_fli_tab TYPE STANDARD TABLE OF carr_fli_struc WITH EMPTY KEY, + + "Structured data type as basis for the table type below + BEGIN OF fli_struc, + carrid TYPE zdemo_abap_flsch_ve-carrid, + connid TYPE zdemo_abap_flsch_ve-connid, + cityfrom TYPE zdemo_abap_flsch_ve-cityfrom, + cityto TYPE zdemo_abap_flsch_ve-cityto, + fltime TYPE zdemo_abap_flsch_ve-fltime, + END OF fli_struc, + + "Internal table type + flsch_tab TYPE STANDARD TABLE OF zdemo_abap_flsch_ve WITH EMPTY KEY. + + "Various instance method declarations + "The selection for instance and static methods is irrelevant for the example. + "It is just meant to visualize that AMDP methods can be declared as either of them. + + "AMDP procedure + "It's a simple AMDP procedure having only an output parameter with tabular type. + "Note the parameter declaration that includes the mandatory passing by value. + "This is true for all of the AMDP method declarations. + METHODS select_carriers + AMDP OPTIONS READ-ONLY CDS SESSION CLIENT dependent + EXPORTING VALUE(carr_tab) TYPE carr_tab. + + "AMDP procedure to call an AMDP table function + "As can be seen in the implementation part, this example method calls the + "AMDP table function get_carr_fli. AMDP table functions can only be called + "by other AMDP methods. + METHODS select_get_carr_fli + AMDP OPTIONS READ-ONLY CDS SESSION CLIENT dependent + IMPORTING VALUE(carrid) TYPE zdemo_abap_fli_ve-carrid + EXPORTING VALUE(carr_fli_tab) TYPE carr_fli_tab. + + "Various static method declarations + + "The purpose of the implementation of the static constructor in this example is to + "fill a demo database table to have data to work with in the example. + CLASS-METHODS class_constructor. + + "AMDP procedure + "This method demonstrates the calling of an AMDP procedure from SQLScript. + "In this example, the selection of data is 'delegated' to another AMDP method get_flights_amdp + "in the same AMDP class. The method declaration includes the addition RAISING with an + "exception class for AMDP-specific exceptions. + CLASS-METHODS get_flights + AMDP OPTIONS READ-ONLY CDS SESSION CLIENT dependent + IMPORTING VALUE(carrid) TYPE zdemo_abap_fli_ve-carrid + EXPORTING VALUE(fli_tab) TYPE fli_tab + RAISING cx_amdp_execution_error. + + "AMDP Table Function for CDS Table Function + "Note that, in this case, a static method declaration is required along with the special + "syntax FOR TABLE FUNCTION. Plus, there are no parameters specified and the declaration + "is made in the PUBLIC visibility section. + CLASS-METHODS flight_analysis FOR TABLE FUNCTION zdemo_abap_table_function. + + PROTECTED SECTION. + PRIVATE SECTION. + + "AMDP procedure + "This method demonstrates the calling of an AMDP procedure from SQLScript as mentioned above. + CLASS-METHODS get_flights_amdp + AMDP OPTIONS READ-ONLY CDS SESSION CLIENT dependent + IMPORTING VALUE(carrid) TYPE zdemo_abap_fli_ve-carrid + EXPORTING VALUE(fli_tab) TYPE fli_tab + RAISING cx_amdp_execution_error. + + "AMDP table function + "AMDP table functions can only be called by other AMDP methods. In this example, + "the AMDP procedure select_get_carr_fli calls this AMDP table function. + METHODS get_carr_fli + AMDP OPTIONS READ-ONLY CDS SESSION CLIENT dependent + IMPORTING VALUE(carrid) TYPE zdemo_abap_flsch_ve-carrid + RETURNING VALUE(carr_fli_tab) TYPE carr_fli_tab. + + CONSTANTS nl TYPE string VALUE cl_abap_char_utilities=>newline. +ENDCLASS. + + + +CLASS zcl_demo_abap_amdp IMPLEMENTATION. + + + METHOD class_constructor. + "Filling demo database tables. + zcl_demo_abap_aux=>fill_dbtabs( ). + ENDMETHOD. + + + METHOD flight_analysis + BY DATABASE FUNCTION + FOR HDB + LANGUAGE SQLSCRIPT + OPTIONS READ-ONLY + USING zdemo_abap_flsch_ve + zdemo_abap_carr_ve. +* Reading data from two CDS view entities + itab_cities = + select DISTINCT + zdemo_abap_flsch_ve.mandt as client, + zdemo_abap_flsch_ve.carrid as carrier_id, + zdemo_abap_flsch_ve.airpfrom as airport_from, + zdemo_abap_flsch_ve.airpto as airport_to, + zdemo_abap_flsch_ve.fltime as flight_time, + zdemo_abap_flsch_ve.distance as flight_distance, + zdemo_abap_flsch_ve.distid as unit + from zdemo_abap_flsch_ve; + + itab_carrier_names = + select distinct + zdemo_abap_carr_ve.mandt as client, + zdemo_abap_carr_ve.carrid as carrier_id, + zdemo_abap_carr_ve.carrname as carrier_name + from zdemo_abap_carr_ve; + +* Returning joined data using an inner join + return + select fl.client, fl.carrier_id, ca.carrier_name, +* Departure and destination airports are concatenated; then all results are joined by string aggregation + string_agg( concat(concat(fl.airport_from,' -> '),fl.airport_to), ', ' ORDER BY fl.airport_from) AS connections, +* Retrieving the average flight time of all flights by carrier + AVG( fl.flight_time ) as avg_flight_time, +* Retrieving the average flight distance of all flights by carrier; miles are converted to kilometers + avg( case 'MI' + when fl.unit then fl.flight_distance * 1.609 + ELSE fl.flight_distance + END ) AS avg_distance + FROM :itab_cities AS fl + INNER JOIN :itab_carrier_names AS ca + ON ca.client = fl.client + AND ca.carrier_id = fl.carrier_id + WHERE fl.client = ca.client AND fl.carrier_id = ca.carrier_id + GROUP BY fl.client, ca.carrier_name, fl.carrier_id; + ENDMETHOD. + + + METHOD get_carr_fli + BY DATABASE FUNCTION + FOR HDB + LANGUAGE SQLSCRIPT + OPTIONS READ-ONLY + USING zdemo_abap_carr_ve zdemo_abap_flsch_ve. +* AMDP table function to be called by other AMDP methods only. +* In the example, joined data from two CDS view entities are returned. + RETURN + SELECT ca.carrname, fl.connid, fl.cityfrom, fl.cityto + FROM zdemo_abap_carr_ve as ca + INNER JOIN zdemo_abap_flsch_ve as fl + ON ca.carrid = fl.carrid + WHERE fl.carrid = :carrid + ORDER BY ca.mandt, ca.carrname, fl.connid; + ENDMETHOD. + + + METHOD get_flights + BY DATABASE PROCEDURE + FOR HDB + LANGUAGE SQLSCRIPT + OPTIONS READ-ONLY + USING zcl_demo_abap_amdp=>get_flights_amdp. +* Another AMDP procedure is called from SQLScript + CALL "ZCL_DEMO_ABAP_AMDP=>GET_FLIGHTS_AMDP"( + carrid => :carrid, + fli_tab => :fli_tab ); + ENDMETHOD. + + + METHOD get_flights_amdp + BY DATABASE PROCEDURE + FOR HDB + LANGUAGE SQLSCRIPT + OPTIONS READ-ONLY + USING zdemo_abap_fli_ve. +* Simple data selection + fli_tab = SELECT carrid, connid, fldate, price, currency, planetype, + seatsmax, seatsocc, paymentsum, seatsmax_b, seatsocc_b, + seatsmax_f, seatsocc_f + FROM "ZDEMO_ABAP_FLI_VE" + WHERE carrid = :carrid + ORDER BY carrid; + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + out->write( `ABAP Cheat Sheet Example: AMDP` ). + + out->write( |\n1) AMDP Procedure\n\n| ). + + "Declaring an internal table to store the data that are + "returned by the following method. + "You could also choose to create the internal table inline + "within the method call, + "i. e. like ... IMPORTING carr_tab = DATA(tab) ). + DATA amdp_proc_res TYPE zcl_demo_abap_amdp=>carr_tab. + + "Since the method is declared as an instance method, an instance + "has to be created. Here, the instance constructor NEW is used + "in a standalone method call that includes a chained method call. + NEW zcl_demo_abap_amdp( )->select_carriers( + IMPORTING carr_tab = amdp_proc_res ). + + out->write( data = amdp_proc_res name = `amdp_proc_res` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `2) Calling an AMDP Procedure from SQLScript` ) ). + + "As can be seen in the method implementation part, this AMDP procedure + "includes an AMDP procedure call from SQLScript. + "In this example, the AMDP procedure get_flights_amdp is called by + "get_flights which is meant to select data from a CDS view entity. + "The returned result is displayed. + TRY. + zcl_demo_abap_amdp=>get_flights( EXPORTING carrid = 'LH' + IMPORTING fli_tab = DATA(call_amdp_res) ). + CATCH cx_amdp_execution_error INTO DATA(error1). + out->write( error1->get_text( ) ). + ENDTRY. + + out->write( data = call_amdp_res name = `call_amdp_res` ). + + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `3) AMDP Table Function for AMDP Method` ) ). + + "The AMDP procedure select_get_carr_fli calls the AMDP table function + "get_carr_fli in the implementation part. AMDP table functions can + "only be called by other AMDP methods. + TRY. + NEW zcl_demo_abap_amdp( )->select_get_carr_fli( + EXPORTING carrid = 'LH' + IMPORTING carr_fli_tab = DATA(amdp_tab_func) ). + CATCH cx_amdp_execution_error INTO DATA(error2). + out->write( error2->get_text( ) ). + ENDTRY. + + out->write( data = amdp_tab_func name = `amdp_tab_func` ). + + "Note: When commented in, the following code results in a runtime + "error since you cannot call an AMDP function in ABAP directly. +* NEW zcl_demo_abap_amdp( )->get_carr_fli( +* EXPORTING carrid = 'LH' ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `4) AMDP Table Function for CDS Table Function` ) ). + + "The example demonstrates that a CDS table function can be used as a + "data source of ABAP SQL read statements. + "You might want to navigate to the DDL source after FROM by holding + "CTRL and clicking the DDL source name in ADT to see the details. + "Or, just check out the F2 help. + "In this example, the CDS table function is implemented in a way to + "return accumulated data. + "In the method implementation for flight_analysis, first two kinds of + "data sets from two CDS view entities are gathered. These data sets are + "joined using an inner join. There, some expressions are included + "(strings are aggregated, average values are determined). + + SELECT * FROM zdemo_abap_table_function + INTO TABLE @DATA(cds_tab_func). + + out->write( data = cds_tab_func name = `cds_tab_func` ). + + ENDMETHOD. + + + METHOD select_carriers + BY DATABASE PROCEDURE + FOR HDB + LANGUAGE SQLSCRIPT + OPTIONS READ-ONLY + USING zdemo_abap_carr_ve. +* Simple data selection + carr_tab = SELECT carrid, carrname, currcode, url + FROM "ZDEMO_ABAP_CARR_VE" + ORDER BY carrid; + ENDMETHOD. + + + METHOD select_get_carr_fli + BY DATABASE PROCEDURE + FOR HDB + LANGUAGE SQLSCRIPT + OPTIONS READ-ONLY + USING zcl_demo_abap_amdp=>get_carr_fli. +* AMDP procedure to call an AMDP table function as specified after USING + carr_fli_tab = SELECT * + FROM "ZCL_DEMO_ABAP_AMDP=>GET_CARR_FLI"( + carrid => :carrid ); + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_amdp.clas.xml b/src/zcl_demo_abap_amdp.clas.xml new file mode 100644 index 0000000..f24acab --- /dev/null +++ b/src/zcl_demo_abap_amdp.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_AMDP + E + ABAP cheat sheet: AMDP + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_aux.clas.abap b/src/zcl_demo_abap_aux.clas.abap new file mode 100644 index 0000000..25a2bcb --- /dev/null +++ b/src/zcl_demo_abap_aux.clas.abap @@ -0,0 +1,718 @@ +*********************************************************************** +* +* Class for ABAP cheat sheet examples designed to clear and populate +* demo database tables +* +* -------------------------- NOTE ------------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** +"!

Class supporting ABAP cheat sheet examples

+"! The class supports the ABAP cheat examples by clearing and populating demo database tables that are used there. +"! The demo database tables contain airline and flight information. +CLASS zcl_demo_abap_aux DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + CLASS-METHODS: clear_dbtabs, + fill_dbtabs, + heading IMPORTING text TYPE string + RETURNING VALUE(output) TYPE string. + + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + + +CLASS zcl_demo_abap_aux IMPLEMENTATION. + + + METHOD clear_dbtabs. + DELETE FROM zdemo_abap_flsch. + DELETE FROM zdemo_abap_carr. + DELETE FROM zdemo_abap_fli. + ENDMETHOD. + + + METHOD fill_dbtabs. + + "Clearing db tables before filling + clear_dbtabs( ). + + "Filling db table + MODIFY zdemo_abap_flsch FROM TABLE @( VALUE #( + ( carrid = 'AA' + connid = 0017 + countryfr = 'US' + cityfrom = 'NEW YORK' + airpfrom = 'JFK' + countryto = 'US' + cityto = 'SAN FRANCISCO' + airpto = 'SFO' + fltime = 361 + deptime = '110000' + arrtime = '140100' + distance = 2572 + distid = 'MI' + fltype = '' + period = 0 ) + ( carrid = 'AA' + connid = 0064 + countryfr = 'US' + cityfrom = 'SAN FRANCISCO' + airpfrom = 'SFO' + countryto = 'US' + cityto = 'NEW YORK' + airpto = 'JFK' + fltime = 321 + deptime = '090000' + arrtime = '172100' + distance = 2572 + distid = 'MI' + fltype = '' + period = 0 ) + ( carrid = 'AZ' + connid = 0555 + countryfr = 'IT' + cityfrom = 'ROME' + airpfrom = 'FCO' + countryto = 'DE' + cityto = 'FRANKFURT' + airpto = 'FRA' + fltime = 125 + deptime = '190000' + arrtime = '210500' + distance = 845 + distid = 'MI' + fltype = '' + period = 0 ) + ( carrid = 'AZ' + connid = 0788 + countryfr = 'IT' + cityfrom = 'ROME' + airpfrom = 'FCO' + countryto = 'JP' + cityto = 'TOKYO' + airpto = 'TYO' + fltime = 775 + deptime = '120000' + arrtime = '085500' + distance = 6130 + distid = 'MI' + fltype = '' + period = 1 ) + ( carrid = 'AZ' + connid = 0789 + countryfr = 'JP' + cityfrom = 'TOKYO' + airpfrom = 'TYO' + countryto = 'IT' + cityto = 'ROME' + airpto = 'FCO' + fltime = 940 + deptime = '114500' + arrtime = '192500' + distance = 6130 + distid = 'MI' + fltype = '' + period = 0 ) + ( carrid = 'AZ' + connid = 0790 + countryfr = 'IT' + cityfrom = 'ROME' + airpfrom = 'FCO' + countryto = 'JP' + cityto = 'OSAKA' + airpto = 'KIX' + fltime = 815 + deptime = '103500' + arrtime = '081000' + distance = 6030 + distid = 'MI' + fltype = 'X' + period = 1 ) + ( carrid = 'DL' + connid = 0106 + countryfr = 'US' + cityfrom = 'NEW YORK' + airpfrom = 'JFK' + countryto = 'DE' + cityto = 'FRANKFURT' + airpto = 'FRA' + fltime = 475 + deptime = '193500' + arrtime = '093000' + distance = 3851 + distid = 'MI' + fltype = '' + period = 1 ) + ( carrid = 'DL' + connid = 1699 + countryfr = 'US' + cityfrom = 'NEW YORK' + airpfrom = 'JFK' + countryto = 'US' + cityto = 'SAN FRANCISCO' + airpto = 'SFO' + fltime = 382 + deptime = '171500' + arrtime = '203700' + distance = 2572 + distid = 'MI' + fltype = '' + period = 0 ) + ( carrid = 'DL' + connid = 1984 + countryfr = 'US' + cityfrom = 'SAN FRANCISCO' + airpfrom = 'SFO' + countryto = 'US' + cityto = 'NEW YORK' + airpto = 'JFK' + fltime = 325 + deptime = '100000' + arrtime = '182500' + distance = 2572 + distid = 'MI' + fltype = '' + period = 0 ) + ( carrid = 'JL' + connid = 0407 + countryfr = 'JP' + cityfrom = 'TOKYO' + airpfrom = 'NRT' + countryto = 'DE' + cityto = 'FRANKFURT' + airpto = 'FRA' + fltime = 725 + deptime = '133000' + arrtime = '173500' + distance = 9100 + distid = 'KM' + fltype = '' + period = 0 ) + ( carrid = 'JL' + connid = 0408 + countryfr = 'DE' + cityfrom = 'FRANKFURT' + airpfrom = 'FRA' + countryto = 'JP' + cityto = 'TOKYO' + airpto = 'NRT' + fltime = 675 + deptime = '202500' + arrtime = '154000' + distance = 9100 + distid = 'KM' + fltype = 'X' + period = 1 ) + ( carrid = 'LH' + connid = 0400 + countryfr = 'DE' + cityfrom = 'FRANKFURT' + airpfrom = 'FRA' + countryto = 'US' + cityto = 'NEW YORK' + airpto = 'JFK' + fltime = 444 + deptime = '101000' + arrtime = '113400' + distance = 6162 + distid = 'KM' + fltype = '' + period = 0 ) + ( carrid = 'LH' + connid = 0401 + countryfr = 'US' + cityfrom = 'NEW YORK' + airpfrom = 'JFK' + countryto = 'DE' + cityto = 'FRANKFURT' + airpto = 'FRA' + fltime = 435 + deptime = '183000' + arrtime = '074500' + distance = 6162 + distid = 'KM' + fltype = '' + period = 1 ) + ( carrid = 'LH' + connid = 0402 + countryfr = 'DE' + cityfrom = 'FRANKFURT' + airpfrom = 'FRA' + countryto = 'US' + cityto = 'NEW YORK' + airpto = 'JFK' + fltime = 455 + deptime = '133000' + arrtime = '150500' + distance = 6162 + distid = 'KM' + fltype = 'X' + period = 0 ) + ( carrid = 'LH' + connid = 2402 + countryfr = 'DE' + cityfrom = 'FRANKFURT' + airpfrom = 'FRA' + countryto = 'DE' + cityto = 'BERLIN' + airpto = 'SXF' + fltime = 65 + deptime = '103000' + arrtime = '113500' + distance = 555 + distid = 'KM' + fltype = '' + period = 0 ) ) ). + + "Filling db table + MODIFY zdemo_abap_carr FROM TABLE @( VALUE #( + ( carrid = 'AA' + carrname = 'American Airlines' + currcode = 'USD' + url = 'http://www.aa.com' ) + ( carrid = 'LH' + carrname = 'Lufthansa' + currcode = 'EUR' + url = 'http://www.lufthansa.com' ) + ( carrid = 'JL' + carrname = 'Japan Airlines' + currcode = 'JPY' + url = 'http://www.jal.co.jp' ) + ( carrid = 'DL' + carrname = 'Delta Airlines' + currcode = 'USD' + url = 'http://www.delta-air.com' ) + ( carrid = 'AZ' + carrname = 'ITA Airways' + currcode = 'EUR' + url = 'http://www.ita-airways.com' ) ) ). + + "Filling db table + MODIFY zdemo_abap_fli FROM TABLE @( VALUE #( + ( carrid = 'AA' + connid = 0017 + fldate = '20230923' + price = '464.35' + currency = 'USD' + planetype = '747-400' + seatsmax = 385 + seatsocc = 369 + paymentsum = '191993.87' + seatsmax_b = 31 + seatsocc_b = 31 + seatsmax_f = 21 + seatsocc_f = 19 ) + ( carrid = 'AA' + connid = 0017 + fldate = '20230929' + price = '464.35' + currency = 'USD' + planetype = '747-400' + seatsmax = 385 + seatsocc = 372 + paymentsum = '193537.52' + seatsmax_b = 31 + seatsocc_b = 30 + seatsmax_f = 21 + seatsocc_f = 20 ) + ( carrid = 'AA' + connid = 0017 + fldate = '20231111' + price = '464.35' + currency = 'USD' + planetype = '747-400' + seatsmax = 385 + seatsocc = 374 + paymentsum = '193651.77' + seatsmax_b = 31 + seatsocc_b = 29 + seatsmax_f = 21 + seatsocc_f = 21 ) + ( carrid = 'AA' + connid = 0064 + fldate = '20220131' + price = '464.35' + currency = 'USD' + planetype = 'A340-600' + seatsmax = 330 + seatsocc = 313 + paymentsum = '168469.88' + seatsmax_b = 30 + seatsocc_b = 30 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'AA' + connid = 0064 + fldate = '20220215' + price = '464.35' + currency = 'USD' + planetype = 'A340-600' + seatsmax = 330 + seatsocc = 157 + paymentsum = '84846.15' + seatsmax_b = 30 + seatsocc_b = 15 + seatsmax_f = 20 + seatsocc_f = 10 ) + ( carrid = 'AZ' + connid = 0555 + fldate = '20230721' + price = '226.41' + currency = 'EUR' + planetype = 'A319-100' + seatsmax = 120 + seatsocc = 114 + paymentsum = '26519.75' + seatsmax_b = 8 + seatsocc_b = 8 + seatsmax_f = 8 + seatsocc_f = 8 ) + ( carrid = 'AZ' + connid = 0555 + fldate = '20230728' + price = '226.41' + currency = 'EUR' + planetype = 'A319-100' + seatsmax = 120 + seatsocc = 115 + paymentsum = '16695.50' + seatsmax_b = 8 + seatsocc_b = 8 + seatsmax_f = 8 + seatsocc_f = 8 ) + ( carrid = 'AZ' + connid = 0788 + fldate = '20230922' + price = '1071.41' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 456 + paymentsum = '548722.20' + seatsmax_b = 30 + seatsocc_b = 30 + seatsmax_f = 20 + seatsocc_f = 20 ) + ( carrid = 'AZ' + connid = 0788 + fldate = '20230722' + price = '1071.41' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 455 + paymentsum = '544674.30' + seatsmax_b = 30 + seatsocc_b = 28 + seatsmax_f = 20 + seatsocc_f = 20 ) + ( carrid = 'AZ' + connid = 0789 + fldate = '20231025' + price = '1071.41' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 455 + paymentsum = '545704.30' + seatsmax_b = 30 + seatsocc_b = 30 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'AZ' + connid = 0789 + fldate = '20230221' + price = '1071.41' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 459 + paymentsum = '549226.90' + seatsmax_b = 30 + seatsocc_b = 30 + seatsmax_f = 20 + seatsocc_f = 20 ) + ( carrid = 'AZ' + connid = 0790 + fldate = '20231228' + price = '1055.41' + currency = 'EUR' + planetype = '747-400' + seatsmax = 385 + seatsocc = 370 + paymentsum = '462373.86' + seatsmax_b = 31 + seatsocc_b = 30 + seatsmax_f = 21 + seatsocc_f = 21 ) + ( carrid = 'AZ' + connid = 0790 + fldate = '20231201' + price = '1055.41' + currency = 'EUR' + planetype = '747-400' + seatsmax = 385 + seatsocc = 367 + paymentsum = '463661.64' + seatsmax_b = 31 + seatsocc_b = 31 + seatsmax_f = 21 + seatsocc_f = 21 ) + ( carrid = 'DL' + connid = 0106 + fldate = '20230209' + price = '652.42' + currency = 'USD' + planetype = 'A340-600' + seatsmax = 330 + seatsocc = 178 + paymentsum = '136750.33' + seatsmax_b = 30 + seatsocc_b = 17 + seatsmax_f = 20 + seatsocc_f = 10 ) + ( carrid = 'DL' + connid = 0106 + fldate = '20240102' + price = '652.42' + currency = 'USD' + planetype = 'A340-600' + seatsmax = 330 + seatsocc = 16 + paymentsum = '12892.33' + seatsmax_b = 30 + seatsocc_b = 2 + seatsmax_f = 20 + seatsocc_f = 10 ) + ( carrid = 'DL' + connid = 1699 + fldate = '20230921' + price = '464.35' + currency = 'USD' + planetype = '767-200' + seatsmax = 260 + seatsocc = 250 + paymentsum = '126636.91' + seatsmax_b = 21 + seatsocc_b = 20 + seatsmax_f = 11 + seatsocc_f = 11 ) + ( carrid = 'DL' + connid = 1699 + fldate = '20230511' + price = '464.35' + currency = 'USD' + planetype = '767-200' + seatsmax = 260 + seatsocc = 251 + paymentsum = '126493.06' + seatsmax_b = 21 + seatsocc_b = 20 + seatsmax_f = 11 + seatsocc_f = 11 ) + ( carrid = 'DL' + connid = 1984 + fldate = '20230719' + price = '464.35' + currency = 'USD' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 460 + paymentsum = '225427.35' + seatsmax_b = 30 + seatsocc_b = 29 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'DL' + connid = 1984 + fldate = '20230213' + price = '464.35' + currency = 'USD' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 458 + paymentsum = '225088.83' + seatsmax_b = 30 + seatsocc_b = 30 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'JL' + connid = 0407 + fldate = '20231128' + price = '1102.77' + currency = 'JPY' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 458 + paymentsum = '563231.65' + seatsmax_b = 30 + seatsocc_b = 27 + seatsmax_f = 20 + seatsocc_f = 20 ) + ( carrid = 'JL' + connid = 0407 + fldate = '20231019' + price = '1102.77' + currency = 'JPY' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 452 + paymentsum = '553552.12' + seatsmax_b = 30 + seatsocc_b = 28 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'JL' + connid = 0408 + fldate = '20231128' + price = '1102.77' + currency = 'JPY' + planetype = '747-400' + seatsmax = 385 + seatsocc = 365 + paymentsum = '470129.20' + seatsmax_b = 31 + seatsocc_b = 28 + seatsmax_f = 21 + seatsocc_f = 20 ) + ( carrid = 'JL' + connid = 0408 + fldate = '20230123' + price = '1102.77' + currency = 'JPY' + planetype = '747-400' + seatsmax = 385 + seatsocc = 372 + paymentsum = '487715.90' + seatsmax_b = 31 + seatsocc_b = 31 + seatsmax_f = 21 + seatsocc_f = 20 ) + ( carrid = 'LH' + connid = 0400 + fldate = '20230628' + price = '1184.54' + currency = 'EUR' + planetype = 'A340-600' + seatsmax = 330 + seatsocc = 319 + paymentsum = '270822.24' + seatsmax_b = 30 + seatsocc_b = 30 + seatsmax_f = 20 + seatsocc_f = 20 ) + ( carrid = 'LH' + connid = 0400 + fldate = '20230323' + price = '1184.54' + currency = 'EUR' + planetype = 'A340-600' + seatsmax = 330 + seatsocc = 312 + paymentsum = '262597.14' + seatsmax_b = 30 + seatsocc_b = 28 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'LH' + connid = 0401 + fldate = '20231128' + price = '669.20' + currency = 'EUR' + planetype = '767-200' + seatsmax = 260 + seatsocc = 246 + paymentsum = '195417.72' + seatsmax_b = 21 + seatsocc_b = 19 + seatsmax_f = 11 + seatsocc_f = 10 ) + ( carrid = 'LH' + connid = 0401 + fldate = '20231229' + price = '669.20' + currency = 'EUR' + planetype = '767-200' + seatsmax = 260 + seatsocc = 252 + paymentsum = '199300.50' + seatsmax_b = 21 + seatsocc_b = 19 + seatsmax_f = 11 + seatsocc_f = 11 ) + ( carrid = 'LH' + connid = 0402 + fldate = '20230617' + price = '669.20' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 461 + paymentsum = '353526.12' + seatsmax_b = 30 + seatsocc_b = 29 + seatsmax_f = 20 + seatsocc_f = 18 ) + ( carrid = 'LH' + connid = 0402 + fldate = '20230313' + price = '669.20' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 450 + paymentsum = '349223.76' + seatsmax_b = 30 + seatsocc_b = 29 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'LH' + connid = 2402 + fldate = '20231028' + price = '245.20' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 451 + paymentsum = '127197.62' + seatsmax_b = 30 + seatsocc_b = 29 + seatsmax_f = 20 + seatsocc_f = 19 ) + ( carrid = 'LH' + connid = 2402 + fldate = '20231223' + price = '245.20' + currency = 'EUR' + planetype = 'A380-800' + seatsmax = 475 + seatsocc = 458 + paymentsum = '18944.86' + seatsmax_b = 30 + seatsocc_b = 30 + seatsmax_f = 20 + seatsocc_f = 20 ) ) ). + + ENDMETHOD. + METHOD heading. + output = |\n_________________________________________________________________________________\n\n{ text }\n\n|. + ENDMETHOD. + +ENDCLASS. + diff --git a/src/zcl_demo_abap_aux.clas.xml b/src/zcl_demo_abap_aux.clas.xml new file mode 100644 index 0000000..ac3f0f7 --- /dev/null +++ b/src/zcl_demo_abap_aux.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_AUX + E + Class supporting ABAP cheat sheet examples + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_cds_ve.clas.abap b/src/zcl_demo_abap_cds_ve.clas.abap new file mode 100644 index 0000000..f0f0b1a --- /dev/null +++ b/src/zcl_demo_abap_cds_ve.clas.abap @@ -0,0 +1,481 @@ +*********************************************************************** +* +* ABAP cheat sheet: CDS View Entities +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate CDS view entities. See the CDS view entities +* that are used in the example for more details. +* - Topics covered: Operands, expressions, and built-in functions in the +* element list of CDS view entities, input parameters, joins, +* associations +* - Note: In ADT, check out the CDS view entities used in this example +* by holding down CTRL and clicking on the CDS view entity. This will +* take you to the artifact. There you can choose F8 to open the data +* preview. + +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP development tools for Eclipse (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, refer to the +* notes included in the class as comments or refer to the respective +* topic in the ABAP Keyword Documentation. +* - Due to the amount of console output, the examples contain numbers +* (e.g. 1) ..., 2) ..., 3) ...) for the individual example sections. +* Also, the variable name is displayed in most cases. So to find +* the relevant output in the console easier and faster, just search +* for the number/variable name in the console (CTRL+F in the console) +* or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

ABAP cheat sheet: CDS view entities

+"! Example to demonstrate CDS view entities.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_cds_ve DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: + if_oo_adt_classrun. + + CLASS-METHODS class_constructor. +protected section. +private section. +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_CDS_VE IMPLEMENTATION. + + + METHOD class_constructor. + "Filling demo database tables. + zcl_demo_abap_aux=>fill_dbtabs( ). + + "Some more database table insertions for this particular example + MODIFY zdemo_abap_carr FROM TABLE @( VALUE #( + ( carrid = 'SQ' + carrname = 'Singapore Airlines' + currcode = 'SGD' + url = 'http://www.singaporeair.com ' ) + ( carrid = 'QF' + carrname = 'Qantas Airways' + currcode = 'AUD' + url = 'http://www.qantas.com.au' ) ) ). + + MODIFY zdemo_abap_flsch FROM TABLE @( VALUE #( + ( carrid = 'UA' + connid = 3517 + countryfr = 'DE' + cityfrom = 'FRANKFURT' + airpfrom = 'FRA' + countryto = 'US' + cityto = 'NEW YORK' + airpto = 'JFK' + fltime = 495 + deptime = '104000' + arrtime = '125500' + distance = 6162 + distid = 'KM' + fltype = '' + period = 0 ) ) ). + + MODIFY zdemo_abap_fli FROM TABLE @( VALUE #( ( carrid = 'UA' ) ) ). + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + out->write( |ABAP Cheat Sheet Example: CDS view entities\n\n| ). + + out->write( `1) Operands, expressions and built-in functions ` && + |in a CDS view entity\n\n| ). + + "The following ABAP SQL SELECT statement uses a CDS view entity as + "the data source. All data is retrieved. The sample CDS view entity + "uses many operands, expressions and built-in functions to demonstrate + "various syntax options. In addition, the view contains an input + "parameter that must be provided with an actual parameter and + "specified in the ABAP SQL SELECT statement. + "In ADT, check out the CDS view entity by holding down CTRL and clicking + "on the CDS view entity. This will take you to the artifact. There you + "can choose F8 to open the data preview. + + SELECT * + FROM zdemo_abap_cds_ve_sel( p_smax = 20 ) + ORDER BY CarrierId + INTO TABLE @DATA(select_from_cds). + + out->write( data = select_from_cds name = `select_from_cds` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `2) Aggregate Expressions` ) ). + + "The following ABAP SQL SELECT statement uses a CDS view entity as + "the data source. All data is retrieved. The sample CDS view entity + "uses aggregate expressions. + + SELECT * + FROM zdemo_abap_cds_ve_agg_exp + ORDER BY carrid + INTO TABLE @DATA(agg_expr). + + out->write( data = agg_expr name = `agg_expr` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `3) Joins` ) ). + + "The following ABAP SQL SELECT statement uses a CDS view entity as + "the data source. All data is retrieved. The sample CDS view entity + "contains multiple joins. + "The CDS view entity is designed to contain different join variants + "in one artifact. There, you can comment in/out code sections to + "check out the individual join variants. Therefore, the result of + "the following SELECT statement depends on which section you have + "commented in in the CDS view entity. + + SELECT * + FROM zdemo_abap_cds_ve_joins + ORDER BY carrid + INTO TABLE @DATA(cds_joins). + + out->write( data = cds_joins name = `cds_joins` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `4) Excursion: ABAP SQL and joins` ) ). + + "The following ABAP SQL SELECT statements are intended to reproduce + "the different joins that are performed by the CDS view entity. + "Inner, left and right outer, and cross joins are covered. The data + "sources for the SELECT statements are the database tables. + "Note: + "- The prefix ~ is used in the ABAP SQL statements instead of . in + " the CDS view entity. + "- To demonstrate the handling of null values, some SELECT statements + " contain the coalesce function and CASE expressions similar to the + " CDS view entity. + + out->write( `---------- Inner join ----------` ). + out->write( |\n| ). + out->write( |\n| ). + + SELECT _carr~carrid, + _carr~carrname, + _flsch_in~cityfrom AS cityfr_in, + _flsch_in~cityto AS cityto_in + FROM zdemo_abap_carr AS _carr + INNER JOIN zdemo_abap_flsch AS _flsch_in + ON _carr~carrid = _flsch_in~carrid + ORDER BY _carr~carrid + INTO TABLE @DATA(sql_inner_join). + + out->write( data = sql_inner_join name = `sql_inner_join` ). + + out->write( |\n| ). + out->write( `---------- Left outer join ----------` ). + out->write( |\n| ). + out->write( |\n| ). + + SELECT _carr~carrid, + _carr~carrname, + _flsch_lo~cityfrom AS cityfr_lo, + coalesce( _flsch_lo~cityto, '???' ) AS cityto_lo + FROM zdemo_abap_carr AS _carr + LEFT OUTER JOIN zdemo_abap_flsch AS _flsch_lo + ON _carr~carrid = _flsch_lo~carrid + ORDER BY _carr~carrid + INTO TABLE @DATA(sql_left_outer_join). + + out->write( data = sql_left_outer_join name = `sql_left_outer_join` ). + + out->write( |\n| ). + out->write( `---------- Right outer join ----------` ). + out->write( |\n| ). + out->write( |\n| ). + + SELECT _carr~carrid, + _carr~carrname, + CASE WHEN _carr~url IS NOT NULL THEN _carr~url + ELSE '!!!' + END AS url_ro, + _flsch_ro~cityfrom AS cityfr_ro, + _flsch_ro~cityto AS cityto_ro + FROM zdemo_abap_carr AS _carr + RIGHT OUTER JOIN zdemo_abap_flsch AS _flsch_ro + ON _carr~carrid = _flsch_ro~carrid + ORDER BY _carr~carrid + INTO TABLE @DATA(sql_right_outer_join). + + out->write( data = sql_right_outer_join name = `sql_right_outer_join` ). + + out->write( |\n| ). + out->write( `---------- Cross join ----------` ). + out->write( |\n| ). + out->write( |\n| ). + + SELECT _carr~carrid, + _carr~carrname, + _flsch_cr~cityfrom AS cityfr_cr, + _flsch_cr~cityto AS cityto_cr + FROM zdemo_abap_carr AS _carr + CROSS JOIN zdemo_abap_flsch AS _flsch_cr + ORDER BY _carr~carrid + INTO TABLE @DATA(sql_cross_join). + + out->write( data = sql_cross_join name = `sql_cross_join` ). + out->write( |\n| ). + + "Just a check what join example is currently commented in + IF cds_joins = sql_inner_join. + + out->write( `In the example CDS view entity, the inner join example is commented in.` ). + + ELSEIF cds_joins = sql_left_outer_join. + + out->write( `In the example CDS view entity, the left outer join example is commented in.` ). + + ELSEIF cds_joins = sql_right_outer_join. + + out->write( `In the example CDS view entity, the right outer join example is commented in.` ). + + ELSEIF cds_joins = sql_cross_join. + + out->write( `In the example CDS view entity, the cross join example is commented in.` ). + + ELSE. + + out->write( `In the example CDS view entity, there is some other code present.` ). + + ENDIF. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Associations` ) ). + + out->write( |5) Selecting data from a CDS view that contains associations\n\n| ). + + "The following ABAP SQL SELECT statement uses a CDS view entity as + "the data source. All data is retrieved. The sample CDS view entity + "contains multiple associations. + "Some fields of some associations are used in the element list of the + "CDS view entity. This data is included in the result set. Some + "associations are exposed but no fields of those associations are + "included in the element list. Therefore, no join is instantiated on + "the database and no data from these exposed assocations is included + "in the result set. + + SELECT * + FROM zdemo_abap_cds_ve_assoc + ORDER BY carrier + INTO TABLE @DATA(assoc). + + out->write( data = assoc name = `assoc` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Using exposed associations in ABAP SQL statements: ...` ) ). + + "The following examples use path expressions to access the association + "targets of exposed associations. + + out->write( |6) ... SELECT clause\n\n| ). + + "The following ABAP SQL SELECT statement uses a CDS view entity as + "the data source. The statement uses an exposed association. + "The SELECT list contains fields from the exposed association. Only in + "this case (when a consumer, such as an ABAP SQL statement, requests + "data) is the join instantiated on the database. + "Note: + "- No attributes are specified for the path expression (attributes + " are covered in examples further down). In particular, a join type is + " not explicitly specified. In such a case, the join type depends on + " the place where the path expression is used. Since the path expression + " is used in the SELECT list of an ABAP SQL SELECT statement (where + " fields are specified), a LEFT OUTER JOIN is used by default. + "- The coalesce function is included for a field to handle null values. + + SELECT carrier, + \_carr3-carrname, + coalesce( \_carr3-url, '###' ) AS cityto_lo + FROM zdemo_abap_cds_ve_assoc + ORDER BY carrier + INTO TABLE @DATA(assoc_exp_select). + + out->write( data = assoc_exp_select name = `assoc_exp_select` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `7) ... FROM clause` ) ). + + "The following ABAP SQL SELECT statement uses a CDS view entity as + "the data source. All data is retrieved. + "In this case, an exposed association is used in the FROM clause. + "Note: + "- No join type is explicitly specified. As mentioned above, the + " position of the path expression is important. In this case (when + " used in the FROM clause), an INNER JOIN is used by default. + "- You can open the data preview for the CDS view entity used and + " compare the result set with the output here. Due to the inner + " join, non-existent 'carrid' values in the association target are + " not contained in the result set. + + SELECT * + FROM zdemo_abap_cds_ve_assoc\_carr3 AS _exp + ORDER BY carrid + INTO TABLE @DATA(assoc_exp_from). + + out->write( data = assoc_exp_from name = `assoc_exp_from` ). + out->write( |\n| ). + + "The following ABAP SQL SELECT statement is intended to reproduce + "the data retrieval as above. + "The statement uses the same CDS view entity as data source that + "is used by the CDS view entity above as data source. + "An inner join is performed on a database table (the _carr3 + "association from above has this table defined as the association + "target in the CDS view entity) since the inner join is used by + "default above so as to reproduce the effect. The result set + "should be the same as above. + + SELECT _carr~mandt, + _carr~carrid, + _carr~carrname, + _carr~currcode, + _carr~url + FROM zdemo_abap_cds_ve_assoc_e AS _cds + JOIN zdemo_abap_carr AS _carr ON _cds~carrid = _carr~carrid + ORDER BY _carr~carrid + INTO TABLE @DATA(sql_repr). + + out->write( data = sql_repr name = `sql_repr` ). + out->write( |\n| ). + + IF sql_repr = assoc_exp_from. + out->write( `The result sets are the same.` ). + ELSE. + out->write( `The result sets are differrent.` ). + ENDIF. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `8) ... Specifying attributes` ) ). + + "The following ABAP SQL SELECT statement uses a CDS view entity as + "the data source. The statement uses an exposed association. + "The SELECT list contains a path expression that is specified with + "an attribute. + "Note: + "- Cardinality, join types, and filter conditions can be used as + " attributes. + "- In the example, only the cardinality is specified. + "- The cardinality can be specified to prevent syntax warnings/errors + " in cases where the cardinality of the association does not match + " the way it is used in a path expression. + "- The example does not explicitly specify a join type. As mentioned + " above, if not explicitly specified, the join type depends on where + " the path expression is used. Here, a column is specified in the + " SELECT list. This means that a LEFT OUTER JOIN is used by default. + "- The result set should contain an entry for 'UA' having an initial + " value for 'fldate'. + + SELECT carrid, + connid, + cityfrom, + cityto, + "Without specifying the cardinality, the following warning + "occurs: Using association "_FLI" can increase the cardinality + "of the results set + "\_fli-fldate AS flightdate + + \_fli[ (*) ]-fldate AS flightdate + "The specification above corresponds to the following specification + "that includes an explicit specification of LEFT OUTER + "\_fli[ (*) LEFT OUTER ]-fldate AS flightdate + FROM zdemo_abap_cds_ve_assoc_e + ORDER BY carrid, connid, flightdate + INTO TABLE @DATA(assoc_attr_card). + + out->write( data = assoc_attr_card name = `assoc_attr_card` ). + out->write( |\n| ). + + "Specifying the join type explicitly + "- INNER, LEFT/RIGHT OUTER are possible + "- The join type can only be specified together with the cardinality. + "- In the result set of the example, the 'UA' entry should not be + " contained. + + SELECT carrid, + connid, + cityfrom, + cityto, + \_fli[ (*) INNER ]-fldate AS flightdate + FROM zdemo_abap_cds_ve_assoc_e + ORDER BY carrid, connid, flightdate + INTO TABLE @DATA(assoc_attr_joty). + + out->write( data = assoc_attr_joty name = `assoc_attr_joty` ). + out->write( |\n| ). + + "Specifying conditions + "- Filter conditions can be specified for the current association + "- The addition WHERE is optional in cases where the filter condition + " is the only attribute specified in the square brackets. + "- When the association is instantiated as a join, the filter condition + " is transformed into an extended condition for the join. + "- In the example, a specific 'carrid' value is filtered for. LEFT OUTER + " is specified as join type explicitly. Not specifying the join type here + " has the same effect. The 'fldate' value is only retrieved for 'DL' + " entries. The other ones have initial values. + + SELECT carrid, + connid, + cityfrom, + cityto, + \_fli[ (*) LEFT OUTER WHERE carrid = 'DL' ]-fldate AS flightdate + "The following has the same effect in this example + "\_fli[ (*) WHERE carrid = 'DL' ]-fldate as flightdate + FROM zdemo_abap_cds_ve_assoc_e + ORDER BY carrid, connid, flightdate + INTO TABLE @DATA(assoc_attr_where). + + out->write( data = assoc_attr_where name = `assoc_attr_where` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `9) ... WHERE clause` ) ). + + "The following ABAP SQL SELECT statement uses a CDS view entity as + "the data source. The statement uses an exposed association. + "The SELECT list and the WHERE clause contain a path expression. + + SELECT carrid, + connid, + countryfr, + countryto, + \_carr_exp-carrname + FROM zdemo_abap_cds_ve_assoc_e + WHERE \_carr_exp-carrid LIKE 'A_' + ORDER BY carrid, connid + INTO TABLE @DATA(assoc_exp_where). + + out->write( data = assoc_exp_where name = `assoc_exp_where` ). + + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_cds_ve.clas.xml b/src/zcl_demo_abap_cds_ve.clas.xml new file mode 100644 index 0000000..b96cc18 --- /dev/null +++ b/src/zcl_demo_abap_cds_ve.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_CDS_VE + E + ABAP cheat sheet: CDS view entities + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_cloud_excursion.clas.abap b/src/zcl_demo_abap_cloud_excursion.clas.abap new file mode 100644 index 0000000..04b7eb8 --- /dev/null +++ b/src/zcl_demo_abap_cloud_excursion.clas.abap @@ -0,0 +1,1123 @@ +*********************************************************************** +* +* ABAP cheat sheet: Excursions into ABAP for Cloud Development +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate a selected set of released APIs and libraries +* with the restricted ABAP language version +* - NOTE: The example class includes constants in the private visbility +* section. The names specified there are used by code snippets. +* Make sure that you insert suitable values before running the +* example class. +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP development tools for Eclipse (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - 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 several 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 intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

ABAP cheat sheet: Excursions into ABAP for Cloud Development

+"! Example to demonstrate released APIs and libraries with the restricted ABAP language version.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_cloud_excursion DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. + PROTECTED SECTION. + PRIVATE SECTION. + TYPES: BEGIN OF details_struc, + name TYPE string, + details TYPE string, + END OF details_struc. + + CLASS-DATA details_tab TYPE TABLE OF details_struc WITH EMPTY KEY. + CLASS-DATA str TYPE string. + CLASS-DATA infos TYPE string_table. + CLASS-METHODS heading IMPORTING text TYPE string + RETURNING VALUE(output) TYPE string. + + "NOTE: + "The names specified for the following constants are used by several + "code snippets in the example class. Make sure that you insert suitable + "values before running the example class. + + "Package name: Use the package name in which you cloned the ABAP cheat sheets, + "for example, ZABAP_CHEAT_SHEETS. + CONSTANTS package TYPE sxco_package VALUE 'ZABAP_CHEAT_SHEETS'. + + "The following constants are relevant for the ABAP repository object generation. + "See the comments in the section further down. + "Name of the package in which you want to generate the repository objects + CONSTANTS package4gen TYPE sxco_package VALUE 'ZABAP_CHEAT_SHEETS'. + "ID of a modifiable transport request + CONSTANTS tr_req_id TYPE sxco_transport VALUE 'ID0A123456'. + "Name of the data element to be created + CONSTANTS gen_dtel TYPE sxco_ad_object_name VALUE 'ZDEMO_ABAP_DTEL'. + "Name of the domain to be created + CONSTANTS gen_doma TYPE sxco_ad_object_name VALUE 'ZDEMO_ABAP_STATUS'. + "Name of the database table to be created + CONSTANTS gen_tabl TYPE sxco_dbt_object_name VALUE 'ZDEMO_ABAP_BOOK'. + "To enable the generation, specify "abap_true" for the contant value. + CONSTANTS generation_ok TYPE abap_bool VALUE abap_false. + +ENDCLASS. + + + +CLASS zcl_demo_abap_cloud_excursion IMPLEMENTATION. + + + METHOD heading. + output = |\n_________________________________________________________________________________\n\n{ text }\n\n|. + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + out->write( |ABAP Cheat Sheet Example: Excursions into ABAP for Cloud Development\n| ). + out->write( `1) Restrictions in ABAP for Cloud Development` ). + out->write( |\nYou can check the code of this section by commenting it in and out. Note the comments in the code.| ). + "The following statements demonstrate a selection of restrictions in ABAP for Cloud Development. + "Comment in the code lines (CTRL + 7) up to BREAK-POINT and check the syntax errors/warnings + "displayed. + "Notes about the errors/warnings: + "- Using ABAP SQL statements, data is retrieved from database tables. The first table + " is a demo table provided by SAP. This table cannot be accessed directly (unlike in classic + " ABAP) and therefore cannot be used as a data source from which to select. The second + " is a database table from the ABAP Cheat Sheet GitHub repository. If you have imported + " the repository into the system, you can use it as a data source. + "- The example includes dynamic ABAP SQL statements. This is just to emphasize that such + " statements can lead to unwanted results, since in this case you cannot select from the + " first database table whose name is specified dynamically. Therefore, there is no syntax + " error or warning at compile time, but an error at runtime. You can try the following: + " Comment out all code except the lines with the dynamic statements, activate the code + " and execute the class with F9. Note: Check out the CL_ABAP_DYN_PRG class, which supports + " dynamic programming by checking the validity of dynamic specifications. + "- Further examples of obsolete and invalid syntax in ABAP for Cloud Development include + " the invalid MOVE ... TO ... statement and others. To set breakpoints in ADT, double-click + " the area to the left of the code line number. + + +* SELECT carrid, connid FROM spfli WHERE carrid = 'LH' INTO TABLE @DATA(it1). +* SELECT carrid, connid FROM zdemo_abap_fli WHERE carrid = 'LH' INTO TABLE @DATA(it2). +* SELECT SINGLE carrid, connid FROM ('SPFLI') WHERE carrid = 'LH' INTO NEW @DATA(wa1). +* SELECT SINGLE carrid, connid FROM ('ZDEMO_ABAP_FLI') WHERE carrid = 'LH' INTO NEW @DATA(wa2). +* +* DATA(num1) = 1. +* DATA(num2) = 1. +* DATA(num3) = 2. +* +* MOVE num3 TO num1. +* num2 = num3. +* +* DATA(it3) = VALUE string_table( ( `a` ) ( `b` ) ( `c` ) ). +* DESCRIBE TABLE it3 LINES DATA(num_lines1). +* DATA(num_lines2) = lines( it3 ). +* +* DATA: ref1 TYPE REF TO i, +* ref2 TYPE REF TO i. +* ref1 = REF #( num1 ). +* GET REFERENCE OF num1 INTO ref2. +* +* DATA str_itab TYPE string_table. +* READ REPORT 'ZCL_DEMO_ABAP_UNIT_TEST=======CCAU' INTO str_itab. +* WRITE 'hi'. +* BREAK-POINT. + +********************************************************************** + + out->write( heading( `2) Using released APIs` ) ). + "The following code uses several released APIs. + "You can check out Released Objects in the Project Explorer in ADT in + "ABAP Cloud. + + "In the example, a released API is used to return the current date in UTC. + "You can use forward navigation by placing the cursor on the class, + "choosing CTRL and clicking. In the opened class, go to the Properties + "tab and choose API state to find the release contract for this class. + DATA(date) = cl_abap_context_info=>get_system_date( ). + DATA(current_year) = date(4). + DATA(up_to_year) = 2050. + + "The statement retrieves the leap years between the current year and + "the specified year. A released CDS view is used as data source. + SELECT calendaryear + FROM i_calendaryear + WHERE calendaryear BETWEEN @current_year AND @up_to_year + AND IsLeapYear IS NOT INITIAL + INTO TABLE @DATA(future_leap_years). + + "To display output, the example class uses a released API. You can implement + "the interface if_oo_adt_classrun and the main method. The write method displays + "content in the console when running the class using F9 in ADT. + out->write( data = future_leap_years name = `future_leap_years` ). + + "Using a released data element + DATA number TYPE int4. + number = lines( future_leap_years ). + + out->write( |\nBetween { current_year } and { up_to_year }, there are { number } leap years.| ). + + "Using a released API as data source of a SELECT statement + "Among others, the month names of specified languages are retrieved in the example. + "The WHERE clause is specified in a way to only retrieve a selected set of months. + "For this purpose, more released APIs (classes cl_abap_random_int, cl_abap_random) + "are used. A minimum of 5 months and a maximum of all months is to be returned. + DATA(number_of_months) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 5 + max = 12 )->get_next( ). + + SELECT CalendarMonth, CalendarMonthName, Language + FROM i_calendarmonthtext + WHERE Language IN ( 'E', 'I', 'D' ) + AND CalendarMonth <= @number_of_months + ORDER BY Language, CalendarMonth + INTO TABLE @DATA(months). + + "Using a released table type + DATA string_tab TYPE string_table. + + "Inserting the retrieved data into a table of type string + LOOP AT months INTO DATA(month_wa). + APPEND |{ month_wa-CalendarMonth } { month_wa-CalendarMonthName } ({ month_wa-Language })| TO string_tab. + ENDLOOP. + + "Creating a JSON string from a data object using a released API + DATA(json_str) = xco_cp_json=>data->from_abap( months )->to_string( ). + + out->write( |\nNumber of months per language: { number_of_months }| ). + out->write( |\nMonths returned:| ). + out->write( data = string_tab name = `string_tab` ). + out->write( |\nMonths returned (JSON string):| ). + out->write( data = json_str name = `json_str` ). + + "Getting APIs for use in ABAP for Cloud Development + "The released CDS view contains the relevant information. In the example, + "the names of released classes are retrieved with a specific name pattern. + SELECT ReleasedObjectType, ReleasedObjectName, ReleaseState + FROM i_apisforclouddevelopment + WHERE releasestate = 'RELEASED' + AND ReleasedObjectType = 'CLAS' + AND ReleasedObjectName LIKE 'CL_ABAP_RANDOM_P%' + INTO TABLE @DATA(rel_cl_abap_random). + + out->write( |\nRead result:| ). + out->write( data = rel_cl_abap_random name = `rel_cl_abap_random` ). + + "Getting the number of all released classes in the system + SELECT COUNT(*) + FROM i_apisforclouddevelopment + WHERE releasestate = 'RELEASED' + AND ReleasedObjectType = 'CLAS' + INTO @DATA(num_rel_cl). + + "Getting the number of all released interfaces in the system + SELECT COUNT(*) + FROM i_apisforclouddevelopment + WHERE releasestate = 'RELEASED' + AND ReleasedObjectType = 'INTF' + INTO @DATA(num_rel_intf). + + out->write( |\nThere are { num_rel_cl } released classes and { num_rel_intf } released interfaces in the system.| ). + +********************************************************************** + + out->write( heading( `Excursions into the XCO Library` ) ). + "The following code snippets focus on the XCO library that provides + "predefined functionality and can be used in ABAP for Cloud Development. + "The examples cover a selection of options for you to explore. For more + "detailed information and more code snippets, see the SAP Help Portal + "documentation. In most cases, the examples deal with classes and methods + "for getting information about repository objects. + "Note: + "- Due to the many methods and options, method chaining comes in handy. + "- To further explore the options with the classes and methods, you can + " put the cursor behind => or -> and choose CTRL + Space to get input + " suggestions. + + out->write( |3) Getting Repository Object-Related Information| ). + + "Getting all accessible repository objects in the system (indicated by the + "value provided for "in") + "To further process the returned values, you can loop over them. + DATA(all_obj) = xco_cp_abap_repository=>objects->all->in( xco_cp_abap=>repository )->get( ). + + out->write( |\nThere are { lines( all_obj ) } accessible repository objects in the system. Note that this number also includes custom artifacts.| ). + +********************************************************************** + + "Getting all accessible database tables + "You can specify concrete artifacts (check CTRL + Space after objects->) + DATA(all_tables) = xco_cp_abap_repository=>objects->tabl->all->in( xco_cp_abap=>repository )->get( ). + + out->write( |\nThere are { lines( all_tables ) } accessible database tables in the system. Note that this number includes custom database tables.| ). + +********************************************************************** + + "Checking the existence of a certain package + "Note: The value provided for 'for' is specified in the private section + " of the class. Use the package name in which you cloned the ABAP + " cheat sheets, for example, ZABAP_CHEAT_SHEETS. + + "Creating a handler for the specified package + "You can create a break point here and check out the content of the variable + "in the debugger to find out what information is included. + DATA(package_handle) = xco_cp_abap_repository=>package->for( package ). + DATA(package_exists) = package_handle->exists( ). + "Using method chaining, you can retreive the information in one go. + DATA(package_exists_ch) = xco_cp_abap_repository=>package->for( package )->exists( ). + + IF package_exists IS NOT INITIAL + AND package_exists_ch IS NOT INITIAL. + out->write( |\nThe package { package } exists in the system.| ). + ELSE. + out->write( |\nThe package { package } does not exist in the system.| ). + ENDIF. + +********************************************************************** + + "Getting more information + IF package_exists IS NOT INITIAL. + DATA(package_name) = package_handle->name. + DATA(software_comp) = package_handle->read( )-property-software_component->name. + + out->write( |\nThe package name is { package_name } (retrieved using the "name" attribute).| ). + out->write( |\nIts software component is { software_comp }.| ). + + "Getting all repository objects contained in the package + DATA(all_objs) = xco_cp_abap_repository=>objects->all->in( package_handle )->get( ). + + "Looping over the result for further processing + "In this example, the name of the repository objects and their types are + "extracted and inserted in a table for displaying purposes. + LOOP AT all_objs ASSIGNING FIELD-SYMBOL(). + APPEND VALUE #( name = ->name->value + details = ->type->value ) TO details_tab. + ENDLOOP. + + SORT details_tab BY name. + + out->write( |\nThe package { package } includes the following { lines( details_tab ) } repository objects:| ). + out->write( details_tab ). + ENDIF. + +********************************************************************** + + "Refining the search using a filter and search pattern + "The search patterns uses a name from the ABAP cheat sheets repository objects. + DATA(filter1) = xco_cp_abap_repository=>object_name->get_filter( + xco_cp_abap_sql=>constraint->contains_pattern( 'ZDEMO_ABAP_RAP_R%' ) ). + + "Getting all accessible BDEFs that start with ZDEMO_ABAP_RAP_R in the entire system + "As shown above, loop over the handler to get more detailed information. You can also set + "a break point to check out the content of the variable in the debugger. + IF package_exists = abap_true. + DATA(bdefs_in_package) = xco_cp_abap_repository=>objects->bdef->where( VALUE #( ( filter1 ) + ) )->in( xco_cp_abap=>repository )->get( ). + + out->write( |\nThere are { lines( bdefs_in_package ) } accessible BDEFs with the specified name pattern in the entire system.| ). + ENDIF. + + "Getting all accessible interfaces with a particular name pattern in the entire system + DATA(filter2) = xco_cp_abap_repository=>object_name->get_filter( + xco_cp_abap_sql=>constraint->contains_pattern( 'IF_ABAP_BEHV%' ) ). + + DATA(all_intfs) = xco_cp_abap_repository=>objects->intf->where( VALUE #( ( filter2 ) + ) )->in( xco_cp_abap=>repository )->get( ). + + out->write( |\nThere are { lines( all_intfs ) } accessible interfaces with the specified name pattern in the entire system.| ). + + "Getting repository objects based on a particular software component + "In the example, all classes are retrieved that have a particular name pattern + "and have a particular software component. + DATA(filter3) = xco_cp_system=>software_component->get_filter( xco_cp_abap_sql=>constraint->equal( 'SAP_BASIS' ) ). + DATA(filter4) = xco_cp_abap_repository=>object_name->get_filter( xco_cp_abap_sql=>constraint->contains_pattern( '%CL_ABAP_RAND%' ) ). + DATA(filtered_classes) = xco_cp_abap_repository=>objects->clas->where( VALUE #( ( filter3 ) ( filter4 ) + ) )->in( xco_cp_abap=>repository )->get( ). + + out->write( |\nThere are { lines( filtered_classes ) } accessible classes with the specified name pattern and software component.| ). + +********************************************************************** + + out->write( heading( `4) Getting Database Table-Related Information` ) ). + + "Creating a filter with a search pattern + DATA(pattern) = 'ZDEMO_ABAP_FL%'. + DATA(filter5) = xco_cp_abap_repository=>object_name->get_filter( + xco_cp_abap_sql=>constraint->contains_pattern( pattern ) ). + + "Getting all database tables whose names begin with the specified name pattern + "and that are contained in the package specified above. + DATA(dbtabs) = xco_cp_abap_repository=>objects->tabl->database_tables->where( VALUE #( ( filter5 ) + ) )->in( package_handle )->get( ). + + IF dbtabs IS NOT INITIAL. + + infos = VALUE #( ( |Information about database tables retrieved using search pattern { pattern }| ) ). + LOOP AT dbtabs INTO DATA(db). + "Getting the name of the database table + DATA(a1_name) = db->name. + "Getting information about technical properties (selection) + DATA(a2_content) = db->content( )->get( ). + DATA(a3_descr) = a2_content-short_description. + DATA(a4_del_cl) = a2_content-delivery_class->value. + DATA(a5_data_maint) = a2_content-data_maintenance->if_xco_printable~get_text( )->get_lines( )->join( )->value. + "Getting all fields of the database table + "The handler is further processed. In the example, only the field names are retreived. + DATA(a6_fields) = db->fields->all->get( ). + + CLEAR str. + LOOP AT a6_fields INTO DATA(fields). + str = str && COND #( WHEN sy-tabix <> 1 THEN `, ` ELSE `` ) && fields->name. + ENDLOOP. + + infos = VALUE #( BASE infos + ( `*********************************` ) + ( |Db table name: { a1_name }| ) + ( |Description: { a3_descr }| ) + ( |Delivery class: { a4_del_cl }| ) + ( |Data maintenance: { a5_data_maint }| ) + ( |Field names: { str }| ) ). + + ENDLOOP. + out->write( infos ). + CLEAR infos. + ELSE. + out->write( |\nNo database tables found.| ). + ENDIF. + +********************************************************************** + + "Getting information about a particular database table + out->write( |\nInformation about a particular demo database table| ). + DATA(db_table) = CONV sxco_dbt_object_name( 'ZDEMO_ABAP_CARR' ). + DATA(b1_handler) = xco_cp_abap_dictionary=>database_table( db_table ). + DATA(b2_exists) = b1_handler->exists( ). + IF b2_exists IS NOT INITIAL. + DATA(b3_name) = b1_handler->name. + DATA(b4_descr) = b1_handler->content( )->get_short_description( ). + DATA(b5_del_cl) = b1_handler->content( )->get_delivery_class( )->value. + DATA(b6_data_maint) = a2_content-data_maintenance->if_xco_printable~get_text( )->get_lines( )->join( )->value. + DATA(b7_fields) = b1_handler->fields->all->get_names( ). + DATA(b8_keys) = b1_handler->fields->key->get_names( ). + DATA(b9_vis) = b1_handler->get_api_state( )->get_visibilities( ). + DATA(b10_rel_state) = b1_handler->get_api_state( )->get_release_state( )->value. + + CLEAR str. + "Getting information about the visibility of the repository object + LOOP AT b9_vis INTO DATA(vis_b). + CASE abap_true. + WHEN vis_b->use_in_key_user_apps( ). + str = |Key user: { vis_b->use_in_key_user_apps( ) }|. + WHEN vis_b->use_in_sap_cloud_platform( ). + str = str && | Cloud: { vis_b->use_in_sap_cloud_platform( ) }|. + ENDCASE. + ENDLOOP. + + infos = VALUE #( + ( |Information about database table { db_table }| ) + ( `*********************************` ) + ( |Db table name: { b3_name }| ) + ( |Description: { b4_descr }| ) + ( |Delivery class: { b5_del_cl }| ) + ( |Data maintenance: { b6_data_maint }| ) + ( |Visibilities: { COND #( WHEN str IS INITIAL THEN `None` ELSE str ) }| ) + ( |Release state: { b10_rel_state }| ) + ( |Field names: { concat_lines_of( table = b7_fields sep = `, ` ) }| ) + ( |Key fields: { concat_lines_of( table = b8_keys sep = `, ` ) }| ) + ). + + out->write( infos ). + CLEAR infos. + ELSE. + out->write( |\nThe database table { db_table } does not exist.| ). + ENDIF. + +********************************************************************** + + out->write( |\nInformation about a particular database table field| ). + + IF b2_exists IS NOT INITIAL. + db_table = 'ZDEMO_ABAP_CARR'. + DATA(db_field) = CONV sxco_ad_field_name( 'CARRID' ). + DATA(c1_handler) = xco_cp_abap_dictionary=>database_table( db_table )->field( db_field )->content( ). + + "Getting attributes of the field + DATA(c2_field_attributes) = c1_handler->get( ). + DATA(c3_is_key) = c2_field_attributes-key_indicator. + DATA(c4_is_not_null) = c2_field_attributes-not_null. + "You can get the information also using a different way, as shown below. + DATA(c5_built_in_t) = c2_field_attributes-type->get_built_in_type( )->abap_type->get_type_descriptor( )->type_kind. + "Getting information using the handler created initially and for getting attributes individually. + DATA(c6_field_name) = c1_handler->field->name. + DATA(c7_db_name) = c1_handler->field->database_table->name. + DATA(c8_is_key) = c1_handler->get_key_indicator( ). + "You may want to check the details for the following variable in the debugger. + DATA(c9_type_handler) = c1_handler->get_type( ). + DATA(c10_built_in_t) = c9_type_handler->get_built_in_type( )->abap_type->get_type_descriptor( )->type_kind. + DATA(c11_type_outp_len) = c9_type_handler->get_built_in_type( )->abap_type->get_type_descriptor( )->output_length. + DATA(c12_type_abs_name) = c9_type_handler->get_built_in_type( )->abap_type->get_type_descriptor( )->absolute_name. + + infos = VALUE #( + ( |Information about field { db_field } in database table { db_table }| ) + ( `*********************************` ) + ( |Field name: { c6_field_name }| ) + ( |Db table name: { c7_db_name }| ) + ( |Is key field: { c3_is_key } (Alternative retrieval: { c8_is_key })| ) + ( |Is not null: { c4_is_not_null }| ) + ( |Built-in type of field: { c5_built_in_t } (Alternative retrieval: { c10_built_in_t })| ) + ( |Output length: { c11_type_outp_len }| ) + ( |Absolute name of type: { c12_type_abs_name }| ) ). + + out->write( infos ). + CLEAR infos. + ELSE. + out->write( |\nThe database table { db_table } does not exist.| ). + ENDIF. + +********************************************************************** + + out->write( heading( `5) Getting Data Element-Related Information` ) ). + + "Getting information about a released data element + DATA(dtel) = CONV sxco_ad_object_name( 'MANDT' ). + DATA(d1_handler) = xco_cp_abap_dictionary=>data_element( dtel ). + DATA(d2_exists) = d1_handler->exists( ). + IF d2_exists IS NOT INITIAL. + DATA(d3_name) = d1_handler->name. + DATA(d4_vis) = d1_handler->get_api_state( )->get_visibilities( ). + DATA(d5_rel_state) = d1_handler->get_api_state( )->get_release_state( )->value. + DATA(d6_descr) = d1_handler->content( )->get_short_description( ). + DATA(d7_built_in_t) = d1_handler->content( )->get_underlying_built_in_type( )->abap_type->get_type_descriptor( )->type_kind. + DATA(d8_type_outp_len) = d1_handler->content( )->get_underlying_built_in_type( )->abap_type->get_type_descriptor( )->output_length. + + CLEAR str. + "Processing visibility information + LOOP AT d4_vis INTO DATA(vis_d). + CASE abap_true. + WHEN vis_d->use_in_key_user_apps( ). + str = |Key user: { vis_d->use_in_key_user_apps( ) }|. + WHEN vis_d->use_in_sap_cloud_platform( ). + str = str && | Cloud: { vis_d->use_in_sap_cloud_platform( ) }|. + ENDCASE. + ENDLOOP. + + infos = VALUE #( + ( |Information about data element { dtel }| ) + ( `*********************************` ) + ( |Data element name: { d3_name }| ) + ( |Short description: { d6_descr }| ) + ( |Visibilities: { COND #( WHEN str IS INITIAL THEN `None` ELSE str ) }| ) + ( |Release state: { d5_rel_state }| ) + ( |Built-in type: { d7_built_in_t }| ) + ( |Output length: { d8_type_outp_len }| ) ). + + out->write( infos ). + CLEAR infos. + ELSE. + out->write( |\nThe data element { dtel } does not exist.| ). + ENDIF. + +********************************************************************** + + out->write( heading( `6) Getting Table Type-Related Information` ) ). + + DATA(tab_type) = CONV sxco_ad_object_name( 'STRING_TABLE' ). + DATA(e1_handler) = xco_cp_abap_dictionary=>table_type( tab_type ). + DATA(e2_exists) = e1_handler->exists( ). + IF e2_exists IS NOT INITIAL. + DATA(e3_name) = e1_handler->name. + DATA(e4_vis) = e1_handler->get_api_state( )->get_visibilities( ). + DATA(e5_rel_state) = e1_handler->get_api_state( )->get_release_state( )->value. + DATA(e6_descr) = e1_handler->content( )->get_short_description( ). + DATA(e7_tab_key) = e1_handler->content( )->get_primary_key( )->key_definition->value. + DATA(e8_tab_key) = e1_handler->content( )->get_primary_key( )->key_definition->if_xco_printable~get_text( )->get_lines( )->join( )->value. + DATA(e9_type_abs_name) = e1_handler->content( )->get_row_type( )->get_built_in_type( )->abap_type->get_type_descriptor( )->absolute_name. + + CLEAR str. + "Processing visibility information + LOOP AT e4_vis INTO DATA(vis_e). + CASE abap_true. + WHEN vis_e->use_in_key_user_apps( ). + str = |Key user: { vis_e->use_in_key_user_apps( ) }|. + WHEN vis_e->use_in_sap_cloud_platform( ). + str = str && | Cloud: { vis_e->use_in_sap_cloud_platform( ) }|. + ENDCASE. + ENDLOOP. + + infos = VALUE #( + ( |Information about table type { tab_type }| ) + ( `*********************************` ) + ( |Table type name: { e3_name }| ) + ( |Short description: { e6_descr }| ) + ( |Visibilities: { COND #( WHEN str IS INITIAL THEN `None` ELSE str ) }| ) + ( |Release state: { e5_rel_state }| ) + ( |Table key: { e7_tab_key } (i.e. { e8_tab_key })| ) + ( |Absolute type name of underlying built-in type: { e9_type_abs_name }| ) ). + + out->write( infos ). + CLEAR infos. + ELSE. + out->write( |\nThe table type { tab_type } does not exist.| ). + ENDIF. + +********************************************************************** + + out->write( heading( `7) Getting CDS View Entity-Related Information` ) ). + + DATA(cds) = CONV sxco_cds_object_name( 'ZDEMO_ABAP_CDS_VE_ASSOC' ). + DATA(f1_handler) = xco_cp_cds=>view_entity( cds ). + DATA(f2_exists) = f1_handler->exists( ). + IF f2_exists IS NOT INITIAL. + DATA(f3_name) = f1_handler->name. + "You may want to check the details for the following variable in the debugger. + DATA(f4_content) = f1_handler->content( )->get( ). + "Fields of the CDS view entity + DATA(f5_field_handler) = f1_handler->fields->all->get( ). + DATA(f6_get_field_names) = f1_handler->fields->all->get_names( ). + DATA(f7_field_names) = concat_lines_of( table = f6_get_field_names sep = `, ` ). + + CLEAR infos. + "Getting field information using the field handler + LOOP AT f5_field_handler INTO DATA(cds_field). + DATA(f8_field_name) = cds_field->name. + APPEND f8_field_name TO infos. + "The demo CDS view entity does not have much included for displaying purposes. + "Therefore, you may create a break point here and check the variable content in + "the debugger for various pieces of information that are accessible. + DATA(f9_field_content) = cds_field->content( )->get( ). + ENDLOOP. + + DATA(f10_field_names) = concat_lines_of( table = infos sep = `, ` ). + + "Getting the associations of the view entity + DATA(f11_assoc) = f1_handler->associations->all->get( ). + + CLEAR infos. + LOOP AT f11_assoc INTO DATA(assoc). + DATA(f12_assoc_name) = assoc->name. + APPEND f12_assoc_name TO infos. + "You may want to check the details for the following variable in the debugger. + DATA(f13_assoc_details) = assoc->content( )->get( ). + ENDLOOP. + + DATA(f14_assocs) = concat_lines_of( table = infos sep = `, ` ). + + infos = VALUE #( + ( |Information about CDS view entity { cds }| ) + ( `*********************************` ) + ( |CDS view entity name: { f3_name }| ) + ( |Field names: { f7_field_names }| ) + ( |Alternative field name retrieval: { f10_field_names }| ) + ( |Association names: { f14_assocs }| ) ). + + out->write( infos ). + CLEAR infos. + ELSE. + out->write( |\nThe CDS view entity { cds } does not exist.| ). + ENDIF. + +********************************************************************** + + out->write( |\nCDS View Entity Example with Parameters| ). + + cds = 'ZDEMO_ABAP_CDS_VE_SEL'. + DATA(g1_handler) = xco_cp_cds=>view_entity( cds ). + DATA(g2_exists) = g1_handler->exists( ). + IF g2_exists IS NOT INITIAL. + DATA(g3_name) = g1_handler->name. + + "Getting parameters + DATA(g4_params) = g1_handler->parameters->all->get( ). + LOOP AT g4_params INTO DATA(param). + DATA(g5_param_name) = param->content( )->get_original_name( ). + DATA(g6_param_built_in_type) = param->content( )->get_data_type( )->is_built_in_type( ). + IF g6_param_built_in_type IS NOT INITIAL. + DATA(g7_type_abs_name) = param->content( )->get_data_type( )->get_built_in_type( )->abap_type->get_type_descriptor( )->absolute_name. + ENDIF. + APPEND |{ g5_param_name } { COND #( WHEN g6_param_built_in_type IS NOT INITIAL THEN `(Absolute type name: ` && g7_type_abs_name && `)` ELSE `` ) } | TO infos. + ENDLOOP. + + DATA(g8_all_params) = concat_lines_of( table = infos sep = `, ` ). + + infos = VALUE #( + ( |Information about CDS view entity { cds }| ) + ( `*********************************` ) + ( |CDS view entity name: { g3_name }| ) + ( |Parameter: { g8_all_params }| ) ). + + out->write( infos ). + CLEAR infos. + ELSE. + out->write( |\nThe CDS view entity { cds } does not exist.| ). + ENDIF. + +********************************************************************** + + out->write( |\nCDS View Entity Example with Compositions| ). + + cds = 'ZDEMO_ABAP_RAP_RO_M'. + DATA(h1_handler) = xco_cp_cds=>view_entity( cds ). + DATA(h2_exists) = h1_handler->exists( ). + IF h2_exists IS NOT INITIAL. + DATA(h3_name) = h1_handler->name. + + DATA(h4_comps) = h1_handler->compositions->all->get( ). + LOOP AT h4_comps INTO DATA(comp). + + DATA(h5_entity_name) = comp->entity->name. + DATA(h6_fields) = comp->entity->fields->all->get_names( ). + DATA(h7_target_name) = comp->target. + DATA(h8_cardinality_max) = comp->content( )->get( )-cardinality-max. + DATA(h9_cardinality_min) = comp->content( )->get( )-cardinality-min. + DATA(h10_alias) = comp->content( )->get( )-alias. + + APPEND |Entity: { h5_entity_name } Fields: { concat_lines_of( table = h6_fields sep = `, ` ) } | && + |Target: { h7_target_name } Cardinality: Min { h9_cardinality_min } Max { h8_cardinality_max } | && + |Alias: { h10_alias } | TO infos. + ENDLOOP. + + DATA(h11_comps) = concat_lines_of( table = infos sep = `, ` ). + + infos = VALUE #( + ( |Information about CDS view entity { cds }| ) + ( `*********************************` ) + ( |CDS view entity name: { h3_name }| ) + ( |Compositions: { h11_comps }| ) ). + + out->write( infos ). + CLEAR infos. + ELSE. + out->write( |\nThe CDS view entity { cds } does not exist.| ). + ENDIF. + +********************************************************************** + + out->write( heading( `8) Getting Interface-Related Information` ) ). + + "Getting a list of all implementations of a given interface + DATA(intf) = CONV sxco_ao_object_name( 'ZDEMO_ABAP_OBJECTS_INTERFACE' ). + DATA(i1_handler) = xco_cp_abap=>interface( intf ). + DATA(i2_exist) = i1_handler->exists( ). + IF i2_exist IS NOT INITIAL. + DATA(i3_name) = i1_handler->name. + + "Getting static data object declarations + DATA(i4_stat_dobj) = i1_handler->components->class_data->all->get( ). + + CLEAR str. + LOOP AT i4_stat_dobj INTO DATA(do). + str = str && COND #( WHEN sy-tabix <> 1 THEN `, ` ELSE `` ) && do->name. + ENDLOOP. + + DATA(i5_stat_dobjs) = str. + CLEAR str. + + "Getting instance methods + DATA(i5_in_meth) = i1_handler->components->method->all->get( ). + LOOP AT i5_in_meth INTO DATA(imeth). + DATA(i6_in_meth_name) = imeth->name. + + str = str && COND #( WHEN sy-tabix <> 1 THEN ` / ` ELSE `` ) && imeth->name. + + "Getting formal parameter names using get_names + DATA(params) = imeth->parameters->all->get_names( ). + + "Getting formal parameters + DATA(i7_formal_params) = imeth->parameters->all->get( ). + DATA i8_p TYPE string. + LOOP AT i7_formal_params INTO DATA(p). + DATA(i9_pname) = p->name. + DATA(i10_ptype) = p->content( )->get_kind( )->if_xco_printable~get_text( )->get_lines( )->join( )->value. + APPEND |{ i9_pname } ({ i10_ptype })| TO infos. + ENDLOOP. + + str = |{ str } (Parameters: { concat_lines_of( table = infos sep = ` ` ) })|. + CLEAR infos. + ENDLOOP. + + "Getting information about where the interface is implemented + "You may want to check the details for the following variable in the debugger. + DATA(i11_intf_impl_get) = i1_handler->implementations->all->get( ). + DATA(i11_intf_impl_names) = i1_handler->implementations->all->get_names( ). + + infos = VALUE #( + ( |Information about interface { intf }| ) + ( `*********************************` ) + ( |Interface name: { i3_name }| ) + ( |Static data objects (CLASS-DATA) in interface: { i5_stat_dobjs }| ) + ( |Instance methods (METHODS) in interface: { str }| ) + ( |Implementations of the interface exist here: { concat_lines_of( table = i11_intf_impl_names sep = `, ` ) }| ) ). + + out->write( infos ). + CLEAR: infos, str. + ELSE. + out->write( |\nThe interface { intf } does not exist.| ). + ENDIF. + +********************************************************************** + + out->write( heading( `9) Getting Class-Related Information` ) ). + + DATA(cl) = CONV sxco_ao_object_name( 'ZCL_DEMO_ABAP_UNIT_TEST' ). + DATA(j1_handler) = xco_cp_abap=>class( cl ). + DATA(j2_exists) = j1_handler->exists( ). + IF j2_exists IS NOT INITIAL. + DATA(j3_name) = j1_handler->name. + "Accessing definition part + "You may want to check the details for the following variable in the debugger. + DATA(j4_name) = j1_handler->definition->content( )->get( ). + "Getting instance methods in the private visibility section + DATA(j5_in_meth_priv) = j1_handler->definition->section-private->components->method->all->get( ). + + CLEAR details_tab. + LOOP AT j5_in_meth_priv INTO DATA(in_meth_priv). + DATA(j6_in_meth_priv_name) = in_meth_priv->name. + APPEND VALUE #( name = j6_in_meth_priv_name ) TO details_tab. + "Getting formal paramter information + DATA(j7_params) = in_meth_priv->parameters->all->get( ). + DATA j8_form_param TYPE string. + CLEAR j8_form_param. + LOOP AT j7_params INTO DATA(par). + DATA(j9_par_name) = par->name. + DATA(j10_par_kind) = par->content( )->get_kind( )->if_xco_printable~get_text( )->get_lines( )->join( )->value. + DATA(j11_par_type) = par->content( )->get_typing_definition( )->get_value( ). + j8_form_param = j8_form_param && |{ j9_par_name } (Kind: { j10_par_kind }, Type: { j11_par_type }) / |. + ENDLOOP. + REPLACE PCRE `/\s$` IN j8_form_param WITH ``. + details_tab[ name = j6_in_meth_priv_name ]-details = j8_form_param. + ENDLOOP. + + "Accessing impementation part of a particular method and getting source code + DATA(method_name) = CONV sxco_clas_method_name( 'CLASS_CONSTRUCTOR' ). + DATA(j12_method_content) = j1_handler->implementation->method( method_name )->content( )->get( ). + + DATA(j13_method_source_code) = xco_cp=>strings( j12_method_content-source + )->join( |{ cl_abap_char_utilities=>cr_lf }| + )->value. + + "Getting the source code using the get_source method + "You may want to check the details for the following variable in the debugger. + DATA(j14_get_source) = j1_handler->implementation->method( 'IF_OO_ADT_CLASSRUN~MAIN' )->content( )->get_source( ). + DATA(j15_method_source_code_b) = xco_cp=>strings( j14_get_source + )->join( |{ cl_abap_char_utilities=>cr_lf }| + )->value. + + out->write( |Information about class { cl }| ). + out->write( |\nInstance methods and their formal parameters| ). + out->write( details_tab ). + CLEAR details_tab. + out->write( |\nSource code of implementation part of method { method_name }| ). + out->write( j13_method_source_code ). + ELSE. + out->write( |\nThe class { cl } does not exist.| ). + ENDIF. + +********************************************************************** + + out->write( |\nGetting Subclasses| ). + + cl = 'CL_ABAP_TYPEDESCR'. + DATA(k1_handler) = xco_cp_abap=>class( cl ). + DATA(k2_exists) = k1_handler->exists( ). + IF k2_exists = abap_true. + "You may want to check the details for the following variable in the debugger. + DATA(k3_get_subclasses) = k1_handler->subclasses->all->get( ). + "Getting the names of the subclasses + DATA(k4_subclass_names) = k1_handler->subclasses->all->get_names( ). + + out->write( data = k4_subclass_names name = `k4_subclass_names` ). + ELSE. + out->write( |\nThe class { cl } does not exist.| ). + ENDIF. + +********************************************************************** + + out->write( heading( `10) Getting AMDP-Related Information` ) ). + + cl = 'ZCL_DEMO_ABAP_AMDP'. + method_name = 'SELECT_GET_CARR_FLI'. + DATA(l1_handler) = xco_cp_abap=>class( cl ). + DATA(l2_exists) = l1_handler->exists( ). + IF l2_exists IS NOT INITIAL. + DATA(l3_name) = l1_handler->name. + "Getting content of the method + DATA(l4_content) = l1_handler->implementation->method( method_name )->content( )->get( ). + DATA(l5_db_type) = l4_content-amdp-database_type->if_xco_abap_token~get_source( ). + DATA(l6_db_lang) = l4_content-amdp-database_language->if_xco_abap_token~get_source( ). + IF l4_content-amdp-database_options IS NOT INITIAL. + DATA(l4_db_opt) = l4_content-amdp-database_options[ 1 ]->if_xco_abap_token~get_source( ). + ENDIF. + + CLEAR str. + LOOP AT l4_content-amdp-database_entities INTO DATA(amdp_ent). + str = str && ` ` && amdp_ent->if_xco_printable~get_text( )->get_lines( )->join( )->value. + ENDLOOP. + + DATA(l5_method_source_code) = l4_content-source. + DATA(l6_meth_source_code) = xco_cp=>strings( l5_method_source_code )->join( |{ cl_abap_char_utilities=>cr_lf }| )->value. + + infos = VALUE #( + ( |Information about AMDP class { cl }| ) + ( `*********************************` ) + ( |Class name: { l3_name }| ) + ( |Db type: { l5_db_type }| ) + ( |Db language: { l6_db_lang }| ) + ( |Db options: { l4_db_opt }| ) + ( |Db entities: { str }| ) ). + + out->write( infos ). + CLEAR: infos, str. + out->write( |\nSource code of implementation part of method { method_name }| ). + out->write( l6_meth_source_code ). + ELSE. + out->write( |\nThe interface { intf } does not exist.| ). + ENDIF. + +********************************************************************** + + out->write( heading( `11) Getting Date and Time Information Using XCO` ) ). + "Among others, the examples cover time and date-related information. + + "Creating a time stamp + DATA(m_moment) = xco_cp_time=>moment( + iv_year = '2024' + iv_month = '01' + iv_day = '01' + iv_hour = '12' + iv_minute = '34' + iv_second = '55' ). + + "Getting user time zone + DATA(m1_user_time_zone) = xco_cp_time=>time_zone->user->value. + "Result is of type string + DATA(m2_moment_string) = m_moment->as( xco_cp_time=>format->abap )->value. + "Result of type string using other formatting + DATA(m3_moment_format_a) = m_moment->as( xco_cp_time=>format->iso_8601_basic )->value. + DATA(m4_moment_format_b) = m_moment->as( xco_cp_time=>format->iso_8601_extended )->value. + "Current moment in the time zone of the current user + DATA(m5_cur_moment4user) = xco_cp=>sy->moment( xco_cp_time=>time_zone->user )->as( xco_cp_time=>format->iso_8601_extended )->value. + "Current moment in UTC + DATA(m6_cur_moment_utc) = xco_cp=>sy->moment( xco_cp_time=>time_zone->utc )->as( xco_cp_time=>format->iso_8601_extended )->value. + "Current UNIX timestamp + DATA(m7_unix_tstmp) = xco_cp=>sy->unix_timestamp( )->value. + "For the time, you can also use the TIME method + DATA(m8_time) = xco_cp=>sy->time( xco_cp_time=>time_zone->user )->as( xco_cp_time=>format->iso_8601_extended )->value. + "Getting second, minute, hour information + DATA(m9_seconds) = xco_cp=>sy->time( xco_cp_time=>time_zone->user )->second. + DATA(m10_minutes) = xco_cp=>sy->time( xco_cp_time=>time_zone->user )->minute. + DATA(m11_hours) = xco_cp=>sy->time( xco_cp_time=>time_zone->user )->hour. + "Calculations + DATA(m12_add_time) = xco_cp=>sy->time( xco_cp_time=>time_zone->user )->add( iv_hour = 1 iv_minute = 1 iv_second = 1 )->as( xco_cp_time=>format->iso_8601_extended )->value. + DATA(m13_subtract_time) = xco_cp=>sy->time( xco_cp_time=>time_zone->user )->subtract( iv_hour = 1 iv_minute = 1 iv_second = 1 )->as( xco_cp_time=>format->iso_8601_extended )->value. + "Dates + DATA(m14_date) = xco_cp=>sy->date( )->as( xco_cp_time=>format->iso_8601_extended )->value. + DATA(m15_day) = xco_cp=>sy->date( )->day. + DATA(m16_month) = xco_cp=>sy->date( )->month. + DATA(m17_year) = xco_cp=>sy->date( )->year. + "Calculations + DATA(m18_add_date) = xco_cp=>sy->date( )->add( iv_day = 1 iv_month = 1 iv_year = 1 )->as( xco_cp_time=>format->iso_8601_extended )->value. + DATA(m19_subtract_date) = xco_cp=>sy->date( )->subtract( iv_day = 1 iv_month = 1 iv_year = 1 )->as( xco_cp_time=>format->iso_8601_extended )->value. + "You can also get more pieces of information using "sy" such as the technical name of your user (see also the class cl_abap_context_info) + DATA(m20_user_name) = xco_cp=>sy->user( )->name. + + out->write( data = m1_user_time_zone name = `m1_user_time_zone` ). + out->write( |\n| ). + out->write( data = m2_moment_string name = `m2_moment_string` ). + out->write( |\n| ). + out->write( data = m3_moment_format_a name = `m3_moment_format_a` ). + out->write( |\n| ). + out->write( data = m4_moment_format_b name = `m4_moment_format_b` ). + out->write( |\n| ). + out->write( data = m5_cur_moment4user name = `m5_cur_moment4user` ). + out->write( |\n| ). + out->write( data = m6_cur_moment_utc name = `m6_cur_moment_utc` ). + out->write( |\n| ). + out->write( data = m7_unix_tstmp name = `m7_unix_tstmp` ). + out->write( |\n| ). + out->write( data = m8_time name = `m8_time` ). + out->write( |\n| ). + out->write( data = m9_seconds name = `m9_seconds` ). + out->write( |\n| ). + out->write( data = m10_minutes name = `m10_minutes` ). + out->write( |\n| ). + out->write( data = m11_hours name = `m11_hours` ). + out->write( |\n| ). + out->write( data = m12_add_time name = `m12_add_time` ). + out->write( |\n| ). + out->write( data = m13_subtract_time name = `m13_subtract_time` ). + out->write( |\n| ). + out->write( data = m14_date name = `m14_date` ). + out->write( |\n| ). + out->write( data = m15_day name = `m15_day` ). + out->write( |\n| ). + out->write( data = m16_month name = `m16_month` ). + out->write( |\n| ). + out->write( data = m17_year name = `m17_year` ). + out->write( |\n| ). + out->write( data = m18_add_date name = `m18_add_date` ). + out->write( |\n| ). + out->write( data = m19_subtract_date name = `m19_subtract_date` ). + out->write( |\n| ). + out->write( data = m20_user_name name = `m20_user_name` ). + +********************************************************************** + + out->write( heading( `12) Generating Repository Objects Using XCO` ) ). + "The example covers the generation of a domain, data element and a + "database table using XCO. + "NOTE: + "To carry out the object generation, make sure that you meet the following + "prerequisites: + "1) You have a modifiable transport request (suitable for demo content). In + " ADT, go to the Transport Organizer tab, check that you have a transport + " request available under "Modifiable". Otherwise, create a new transport + " request. + " Copy the ID of the transport request and insert it in the constant "tr_req_id" + " that is available in the private visibility section of the class. + " + "2) You have specified a package for demo content, for example, the + " package of the ABAP cheat sheets, which is "ZABAP_CHEAT_SHEETS" in the example. + " Make sure that you have assigned the constant "package4gen" a value + " with a valid package in which you want to create the demo repository + " objects. + " + "3) You have provided suitable names for the demo objects. Check and, if need be, + " change the constant values for gen_dtel, gen_doma, gen_tabl. + " + "4) To enable the generation, assign the constant "generation_ok" the value + " "abap_true". + " + "The generation of the objects is only carried out if all of the mentioned + "prerequisites are met. + + "Checking validity of the specified transport request ID + TRY. + DATA(n1_handler) = xco_cp_cts=>transport->for( tr_req_id ). + DATA(n2_exists) = n1_handler->exists( ). + DATA(n3_status) = n1_handler->get_status( ). + CATCH cx_root INTO DATA(e). + ENDTRY. + + "Checking if repository objects with the names specified already exist in the system + DATA(n6_exists_tabl) = xco_cp_abap_dictionary=>database_table( gen_tabl )->exists( ). + DATA(n7_exists_dtel) = xco_cp_abap_dictionary=>data_element( gen_dtel )->exists( ). + DATA(n8_exists_doma) = xco_cp_abap_dictionary=>domain( gen_doma )->exists( ). + + CASE abap_true. + WHEN n6_exists_tabl. + DATA(flag) = abap_true. + WHEN n7_exists_dtel. + flag = abap_true. + WHEN n8_exists_doma. + flag = abap_true. + WHEN OTHERS. + ENDCASE. + + "Checking if package exists + DATA(n9_exists_devc) = xco_cp_abap_repository=>package->for( package4gen )->exists( ). + + IF n2_exists IS NOT INITIAL + AND n3_status = xco_cp_transport=>status->modifiable + AND flag IS INITIAL + AND n9_exists_devc = abap_true + AND generation_ok = abap_true. + + DATA(n10_handler) = xco_cp_generation=>environment->dev_system( tr_req_id ). + DATA(n11_put) = n10_handler->create_put_operation( ). + +********************************************************************** + "Domain + DATA(n12_doma_spec) = n11_put->for-doma->add_object( gen_doma + )->set_package( package4gen + )->create_form_specification( ). + n12_doma_spec->set_short_description( 'Demo domain' ). + n12_doma_spec->set_format( xco_cp_abap_dictionary=>built_in_type->char( 10 ) ). + n12_doma_spec->fixed_values->add_fixed_value( 'BOOKED' + )->set_description( 'Booked' ). + n12_doma_spec->fixed_values->add_fixed_value( 'CANCELED' + )->set_description( 'Canceled' ). +********************************************************************** + "Data element + DATA(n13_dtel_spec) = n11_put->for-dtel->add_object( gen_dtel + )->set_package( package4gen + )->create_form_specification( ). + n13_dtel_spec->set_short_description( 'Booking status' ). + + n13_dtel_spec->set_data_type( xco_cp_abap_dictionary=>domain( gen_doma ) ). +*********************************************************************** + "Database table + DATA(n14_tabl_spec) = n11_put->for-tabl-for-database_table->add_object( gen_tabl + )->set_package( package4gen + )->create_form_specification( ). + n14_tabl_spec->set_short_description( 'Booking request' + )->set_delivery_class( xco_cp_database_table=>delivery_class->a + )->set_data_maintenance( xco_cp_database_table=>data_maintenance->allowed ). + n14_tabl_spec->add_field( 'CLIENT' )->set_type( xco_cp_abap_dictionary=>built_in_type->clnt + )->set_key_indicator( + )->set_not_null( ). + n14_tabl_spec->add_field( 'ID' )->set_type( xco_cp_abap_dictionary=>built_in_type->char( 30 ) + )->set_key_indicator( + )->set_not_null( ). + "Using built-in ABAP types + n14_tabl_spec->add_field( 'FROM_DATE' )->set_type( xco_cp_abap_dictionary=>built_in_type->dats ). + n14_tabl_spec->add_field( 'TO_DATE' )->set_type( xco_cp_abap_dictionary=>built_in_type->dats ). + "Using the created data element + n14_tabl_spec->add_field( 'STATUS' )->set_type( xco_cp_abap_dictionary=>data_element( gen_dtel ) ). + "Using a released data element + n14_tabl_spec->add_field( 'USER_NAME' )->set_type( xco_cp_abap_dictionary=>data_element( 'SYUNAME' ) ). +********************************************************************** + "Executing the generation + TRY. + n11_put->execute( ). + out->write( `Generation successful. Check out the newly created repository objects in the Project Explorer. Refresh the target package.` ). + CATCH cx_xco_gen_put_exception INTO DATA(err). + out->write( err->get_text( ) ). + ENDTRY. + + ELSE. + CLEAR details_tab. + out->write( `The following information is displayed if the generation has not been carried out. Before generating the ` && + `repository objects, all of the following prerequisites must be met.` ). + details_tab = VALUE #( + ( name = `Transport request` details = `n2_exists: "` && n2_exists && `" / The value for "n2_exists" should be "X". ` && + `Otherwise, it indicates that the transport request does not exist. In that case, provide a suitable transport ` && + `request ID for the constant in the private section.` ) + ( name = `Modifiable transport request` details = COND #( WHEN e IS INITIAL THEN `n3_status: "` && n3_status->value && + `" / The value for "n3_status" should be "` && xco_cp_transport=>status->modifiable->value && + `". Otherwise, it indicates that the transport request is not modifiable. In that case, provide a suitable transport ` && + `request ID for the constant in the private section.` ELSE `There is an issue with the transport request ID provided: ` && + e->get_text( ) ) ) + ( name = `Package` details = `n9_exists_devc: "` && n9_exists_devc && `" / The value for "n9_exists_devc" should be "X". ` && + `Otherwise, it indicates that the package name as specified does not exist in the system. In that case, provide an ` && + `existing package name for the constant in the private section.` ) + ( name = `Repository object names` details = `flag: "` && flag && `" / The value for "flag" should be "" (initial). Otherwise, ` && + `it indicates that one or more artifacts with the same names as provided already exist in the system. In that case, ` && + `provide other names.` ) + ( name = `generation_ok flag` details = `generation_ok: "` && generation_ok && `" / The value for generation_ok should ` && + `be "X". Otherwise, it indicates that the execution of the generation is disabled. In that case, change the value of the constant ` && + `in the private section to "abap_true" to carry out the generation.` ) ). + out->write( details_tab ). + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_cloud_excursion.clas.xml b/src/zcl_demo_abap_cloud_excursion.clas.xml new file mode 100644 index 0000000..5f790f7 --- /dev/null +++ b/src/zcl_demo_abap_cloud_excursion.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_CLOUD_EXCURSION + E + ABAP cheat sheet: Excursions into ABAP for Cloud Development + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_constructor_expr.clas.abap b/src/zcl_demo_abap_constructor_expr.clas.abap new file mode 100644 index 0000000..55fea27 --- /dev/null +++ b/src/zcl_demo_abap_constructor_expr.clas.abap @@ -0,0 +1,1969 @@ +*********************************************************************** +* +* ABAP cheat sheet: Constructor expressions +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate the use of constructor expressions. +* - Topics covered: Operators VALUE, CORRESPONDING, NEW, CONV, EXACT, REF, +* CAST, COND, SWITCH, FILTER, REDUCE, iteration expressions with FOR, +* LET expressions +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP development tools for Eclipse (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, refer to the +* notes included in the class as comments or refer to the respective +* topic in the ABAP Keyword Documentation. +* - Due to the amount of console output, the examples contain numbers +* (e.g. 1) ..., 2) ..., 3) ...) for the individual example sections. +* Also, the variable name is displayed in most cases. So to find +* the relevant output in the console easier and faster, just search +* for the number/variable name in the console (CTRL+F in the console) +* or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

ABAP cheat sheet: Constructor expressions

+"! Example to demonstrate the use of constructor expressions.
Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_constructor_expr DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + + PROTECTED SECTION. + PRIVATE SECTION. + TYPES: BEGIN OF line1, + col1 TYPE i, + col2 TYPE i, + END OF line1, + BEGIN OF line2, + col2 TYPE i, + col3 TYPE i, + col4 TYPE i, + END OF line2, + BEGIN OF s1_type, + comp1 TYPE c LENGTH 1, + comp2 TYPE string, + comp3 TYPE i, + END OF s1_type, + BEGIN OF s2_type, + comp1 TYPE string, + comp2 TYPE c LENGTH 1, + comp3 TYPE i, + comp4 TYPE i, + END OF s2_type. + + CLASS-DATA: + "Deep structures as examples to demonstrate the CORRESPONDING operator + BEGIN OF struc1, + comp1 TYPE c LENGTH 1 VALUE 'W', + BEGIN OF struc_nested, + comp1 TYPE c LENGTH 1 VALUE 'X', + BEGIN OF comp2, + col1 TYPE c LENGTH 1 VALUE 'Y', + col2 TYPE c LENGTH 1 VALUE 'Z', + END OF comp2, + END OF struc_nested, + itab TYPE TABLE OF line1 WITH EMPTY KEY, + END OF struc1, + BEGIN OF struc2, + BEGIN OF struc_nested, + comp1 TYPE string, + comp2 TYPE string, + comp3 TYPE string, + END OF struc_nested, + itab TYPE TABLE OF line2 WITH EMPTY KEY, + comp4 TYPE i, + END OF struc2, + s1 TYPE s1_type, + s2 TYPE s2_type, + tab1 TYPE TABLE OF s1_type WITH EMPTY KEY, + tab2 TYPE TABLE OF s2_type WITH EMPTY KEY, + tab3 TYPE TABLE OF s2_type WITH EMPTY KEY, + tab4 TYPE SORTED TABLE OF s2_type WITH NON-UNIQUE KEY comp3, + nl TYPE string.. + + CLASS-METHODS: + fill_deep_structures, + fill_struc_and_tab. + +ENDCLASS. + + + +CLASS zcl_demo_abap_constructor_expr IMPLEMENTATION. + + + METHOD fill_deep_structures. + "Clearing all contents of struc2 + CLEAR struc2. + "Filling nested tables in deep structures + struc2-struc_nested = VALUE #( comp1 = `aaa` + comp2 = `bbb` + comp3 = `ccc` ). + + struc1-itab = VALUE #( + ( col1 = 111 col2 = 222 ) + ( col1 = 333 col2 = 444 + ) ). + + struc2-itab = VALUE #( + ( col2 = 1 col3 = 2 col4 = 3 ) + ( col2 = 4 col3 = 5 col4 = 6 ) + ( col2 = 7 col3 = 8 col4 = 9 ) + ). + + "Filling individual component that is not shared by both structures + struc2-comp4 = 999. + ENDMETHOD. + + + METHOD fill_struc_and_tab. + CLEAR: s1, s2, tab1, tab2, tab3. + + s1 = VALUE #( comp1 = 'A' comp2 = `bbb` comp3 = 1 ). + + s2 = VALUE #( comp1 = `ccc` comp2 = 'D' comp3 = 2 comp4 = 3 ). + + tab1 = VALUE #( + ( comp1 = 'A' comp2 = `bbb` comp3 = 1 ) + ( comp1 = 'B' comp2 = `ccc` comp3 = 2 ) + ( comp1 = 'C' comp2 = `ddd` comp3 = 3 ) ). + + tab2 = VALUE #( + ( comp1 = `eee` comp2 = 'F' comp3 = 4 comp4 = 5 ) + ( comp1 = `ggg` comp2 = 'H' comp3 = 6 comp4 = 7 ) + ( comp1 = `iii` comp2 = 'J' comp3 = 8 comp4 = 9 ) ). + + tab3 = VALUE #( + ( comp1 = `aaa` comp2 = 'B' comp3 = 1 comp4 = 2 ) + ( comp1 = `ccc` comp2 = 'D' comp3 = 3 comp4 = 4 ) + ( comp1 = `eee` comp2 = 'F' comp3 = 5 comp4 = 6 ) + ( comp1 = `ggg` comp2 = 'H' comp3 = 7 comp4 = 8 ) + ( comp1 = `iii` comp2 = 'J' comp3 = 9 comp4 = 10 ) ). + + tab4 = tab3. + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + out->write( `ABAP Cheat Sheet Example: Constructor Expressions` ). + + out->write( |\nVALUE\n| ). + out->write( |1) Structures: Populating a flat structure\n\n| ). + + "A flat structure is created based on a data type defined with a + "TYPES statement. The structure is then filled using a constructor + "expression with VALUE by specifying the components and assigning + "values. Here, the type can be inferred, hence, a # character can be used. + + TYPES: BEGIN OF struc_type, + num TYPE i, + char1 TYPE c LENGTH 3, + char2 TYPE c LENGTH 3, + END OF struc_type. + + DATA struc TYPE struc_type. + + "Filling structure + struc = VALUE #( num = 1 char1 = 'aaa' char2 = 'abc' ). + + out->write( data = struc name = `struc` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `2) Structures: Omitting value assignment to components / BASE addition` ) ). + + "The same structure is then filled purposely omitting components, i. + "e. these components remain initial. + + struc = VALUE #( char1 = 'bbb' ). + + out->write( data = struc name = `struc` ). + + "You can use the BASE addition to retain existing content + "Compare with the BASE example further down regarding internal tables: There are + "no extra parentheses within the outer pair of parentheses. + struc = VALUE #( BASE struc char2 = 'xyz' ). + + out->write( |\n| ). + out->write( data = struc name = `struc` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `3) Structures: Inline declaration, explicit type specification` ) ). + + "The example demonstrates a variable that is declared inline. Here, + "the result is a structure which is filled using a constructor + "expression with VALUE and by specifying the components and assigning + "values in parentheses. The structure type is specified explicitly. + "The # symbol would not work since no type can be inferred from the + "specified parameters. + + DATA(struc_inl) = VALUE struc_type( num = 3 + char1 = 'ccc' + char2 = 'def' ). + + out->write( data = struc_inl name = `struc_inl` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `4) Internal tables: Declaration and population` ) ). + + "The example demonstrates the declaration of an internal table. The + "internal table is then filled using a constructor expression with + "VALUE. + "The type can be inferred here and need not be specified explicitly. + "Note the extra pair of parentheses in which the components are + "specified and assigned values. In the example, 3 lines are added to + "the table. For one line, some components are purposely not assigned. + + DATA itab TYPE TABLE OF struc_type WITH EMPTY KEY. + + itab = VALUE #( ( num = 1 char1 = 'aaa' char2 = 'abc' ) + ( num = 2 char1 = 'bbb' char2 = 'def' ) + ( num = 3 char1 = 'ccc' ) ). + + out->write( data = itab name = `itab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `5) Internal tables: Inline declaration, explicit type specification` ) ). + + "The example demonstrates an internal table declared inline that is + "filled using a constructor expression with VALUE by specifying the + "internal table type explicitly. Note that the internal table type + "cannot be generic in this context. + + TYPES: itab_type TYPE STANDARD TABLE OF struc_type + WITH NON-UNIQUE KEY num. + + DATA(itab2) = VALUE itab_type( + ( num = 4 char1 = 'ddd' char2 = 'ghi' ) + ( num = 5 char1 = 'eee' char2 = 'jkl' ) ). + + + DATA(str_table) = VALUE string_table( ( `this` ) + ( `is a` ) + ( `table` ) + ( `of type string` ) ). + + out->write( data = itab2 name = `itab2` ). + out->write( |\n| ). + out->write( data = str_table name = `str_table` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `6) LINES OF addition` ) ). + + "Using the LINES OF addition, you can add lines of other tables. + "Note: The line type of the other internal table must match the one of + "the target internal table. Using FROM/TO, the table line selection can + "be further restricted. Without FROM/TO, all lines of the table are + "respected. + + itab2 = VALUE #( ( num = 6 char1 = 'fff' char2 = 'mno' ) + ( LINES OF itab ) + ( LINES OF itab FROM 1 TO 2 ) ). + + out->write( data = itab2 name = `itab2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `7) BASE addition for keeping existing data` ) ). + + "Using the BASE addition, you can keep existing content of the source + "internal table. + + itab2 = VALUE #( BASE itab2 ( num = 7 char1 = 'ggg' char2 = 'pqr' ) ). + + out->write( data = itab2 name = `itab2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `8) Assignemnt with the VALUE operator without specifying content in parentheses` ) ). + + "Using the VALUE operator without populating anything in the parentheses, + "data objects are initialized. + + "elementary types + DATA(some_num) = 123. + some_num = VALUE #( ). + + DATA(another_num) = VALUE i( ). + + DATA(some_str) = `hallo`. + some_str = VALUE #( ). + + "Initializing internal table/structure + str_table = VALUE #( ). + struc = VALUE #( ). + + out->write( data = some_num name = `some_num` ). + out->write( |\n| ). + out->write( data = another_num name = `another_num` ). + out->write( |\n| ). + out->write( data = some_str name = `some_str` ). + out->write( |\n| ). + out->write( data = str_table name = `str_table` ). + out->write( |\n| ). + out->write( data = struc name = `struc` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `9) Short form for internal tables with structured line types` ) ). + + TYPES: BEGIN OF stype, + a TYPE i, + b TYPE c LENGTH 3, + c TYPE string, + END OF stype. + TYPES tabtype TYPE TABLE OF stype WITH EMPTY KEY. + + DATA(itable) = VALUE tabtype( b = 'aaa' ( a = 1 c = `xxx` ) + ( a = 2 c = `yyy` ) + b = 'bbb' c = `zzz` ( a = 3 ) + ( a = 4 ) ). + + out->write( data = itable name = `itable` ). + out->write( |\n| ). + + "This option can be handy in various contexts, for example, in a + "ranges table. + TYPES int_tab_type TYPE TABLE OF i WITH EMPTY KEY. + "Populating an integer table with values from 1 to 20 (see iteration + "expressions with FOR further down) + DATA(inttab) = VALUE int_tab_type( FOR x = 1 WHILE x <= 20 ( x ) ). + + DATA rangetab TYPE RANGE OF i. + + "Populating a range table using VALUE and the short form + rangetab = VALUE #( sign = 'I' + option = 'BT' ( low = 1 high = 3 ) + ( low = 6 high = 8 ) + ( low = 12 high = 15 ) + option = 'GE' ( low = 18 ) ). + + "Using a SELECT statement to retrieve internal table content + "based on the range table specifications + SELECT * FROM @inttab AS tab + WHERE table_line IN @rangetab + INTO TABLE @DATA(result_tab). + + out->write( data = result_tab name = `result_tab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Excursions: VALUE operator in use with ABAP statements and ABAP SQL statements` ) ). + + "The following examples use ABAP and ABAP SQL statements in which table lines + "are constructed inline using the VALUE operator. + + out->write( `10) Modifying internal table from a structure created inline` && |\n\n| ). + + MODIFY TABLE itab2 FROM VALUE #( num = 7 char1 = 'hhh' char2 = 'stu' ). + + out->write( data = itab2 name = `itab2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `11) Inserting a table line that is created inline into an internal table` ) ). + + INSERT VALUE #( num = 8 char1 = 'iii' char2 = 'vwx' ) INTO TABLE itab2. + + out->write( data = itab2 name = `itab2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `12) Deleting a table entry based on a line created inline` ) ). + + DELETE TABLE itab2 FROM VALUE #( num = 3 ). + + out->write( data = itab2 name = `itab2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `13) Modifying a database table based on an internal table created inline` ) ). + + "Deleting demo database table entries for the following example + DELETE FROM zdemo_abap_carr. + + MODIFY zdemo_abap_carr FROM TABLE @( VALUE #( + ( carrid = 'CO' + carrname = 'Continental Airlines' + currcode = 'USD' + url = 'http://www.continental.com' ) + ( carrid = 'SQ' + carrname = 'Singapore Airlines' + currcode = 'SGD' + url = 'http://www.singaporeair.com' ) + ) ). + + "Retrieving table entries for display purposes + SELECT FROM zdemo_abap_carr + FIELDS carrid, carrname, currcode, url + ORDER BY carrid + INTO TABLE @DATA(itab_carr). + + out->write( data = itab_carr name = `itab_carr` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Excursion: Deep structures and tables` ) ). + out->write( |14) Deep structure\n| ). + + "The example demonstrates the use of constructor expressions with + "VALUE in the context of a deep structure. Here, a structure is declared + "inline and filled. The structure type includes a nested structure. The + "nested structure is filled using a nested VALUE expression. + + TYPES: BEGIN OF deep_struc_ty, + num TYPE i, + char1 TYPE c LENGTH 3, + BEGIN OF substruc, + int TYPE i, + str TYPE string, + END OF substruc, + END OF deep_struc_ty. + + DATA(deep_struc) = VALUE deep_struc_ty( num = 1 + char1 = 'aaa' + substruc = VALUE #( int = 123 str = `hallo` ) ). + + out->write( data = deep_struc name = `deep_struc` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `15) Deep internal table` ) ). + + "A deep internal table is created. Also here, nested VALUE + "expressions are demonstrated. + + TYPES: BEGIN OF deep_struc_ty2, + char TYPE c LENGTH 3, + tab TYPE TABLE OF i WITH EMPTY KEY, + END OF deep_struc_ty2. + + TYPES: itab_deep_type TYPE STANDARD TABLE OF deep_struc_ty2 + WITH NON-UNIQUE KEY char. + + DATA(deep_itab) = VALUE itab_deep_type( + ( char = 'aaa' tab = VALUE #( ( 1 ) ( 2 ) ( 3 ) ) ) + ( char = 'bbb' tab = VALUE #( ( 4 ) ( 5 ) ( 6 ) ) ) ). + + out->write( data = deep_itab name = `deep_itab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `CORRESPONDING` ) ). + out->write( |Simple Examples with structures and internal tables\n| ). + + "Method to fill demo structures and internal tables + "with values to work with + fill_struc_and_tab( ). + + out->write( `16) Original structure and table content` && |\n\n| ). + + "Displaying the original structures and tables that are filled in the + "course of a method call. The structures and tables are filled anew + "throughout the examples so that all CORRESPONDING expressions are based + "on the same values. + + out->write( data = s1 name = `s1` ). + out->write( |\n| ). + out->write( data = s2 name = `s2` ). + out->write( |\n| ). + out->write( data = tab1 name = `tab1` ). + out->write( |\n| ). + out->write( data = tab2 name = `it_st` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `17) CORRESPONDING without addition` ) ). + + "The target structure and table have a different type but identically + "named components. The identically named components are filled. Note + "that the target variables are initialized here. Also note the effect + "of an automatic conversion of a variable-length string to a + "fixed-length string (one component is typed with c, the other, + "identically named component, is of type string). + + s2 = CORRESPONDING #( s1 ). + + tab2 = CORRESPONDING #( tab1 ). + + out->write( data = s2 name = `s2` ). + out->write( |\n| ). + out->write( data = tab2 name = `tab2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `18) BASE addition for keeping original content` ) ). + + "The BASE addition keeps the original content. Structure: The non- + "identical component name retains its value. Internal table: Existing + "table lines are kept. + + fill_struc_and_tab( ). + + s2 = CORRESPONDING #( BASE ( s2 ) s1 ). + + tab2 = CORRESPONDING #( BASE ( tab2 ) tab1 ). + + out->write( data = s2 name = `s2` ). + out->write( |\n| ). + out->write( data = tab2 name = `tab2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `19) MAPPING/EXCEPT additions` ) ). + + "The example demonstrates the additions MAPPING and EXCEPT. MAPPING: + "One component of the target structure is assigned the value of a + "dedicated component of the source structure. EXCEPT: All corresponding + "components are assigned except a specific one. + + fill_struc_and_tab( ). + + s2 = CORRESPONDING #( s1 MAPPING comp4 = comp3 ). + + tab2 = CORRESPONDING #( tab1 EXCEPT comp1 ). + + out->write( data = s2 name = `s2` ). + out->write( |\n| ). + out->write( data = tab2 name = `tab2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `CORRESPONDING: Demonstrating various` && + ` additions using deep structures` ) ). + + out->write( `20) Original content of deep structures` && |\n\n| ). + + "Displaying the original deep structures and tables that are filled in + "the course of a method call. The deep structures and tables are filled + "anew throughout the examples so that all CORRESPONDING expressions are + "based on the same values. + + "Method to fill demo deep structures and internal tables + "with values to work with + fill_deep_structures( ). + + out->write( data = struc1 name = `struc1` ). + out->write( |\n| ). + out->write( data = struc2 name = `struc2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `21) CORRESPONDING without addition` ) ). + + "CORRESPONDING operator without addition + "Existing contents of identically named components are replaced. + "Existing contents of components in the target structure that are not + "available in the source structure are initialized. + "Contents of the nested structure struc_nested is converted to + "string. Note that the two components of the nested structure in + "component struc_nested-comp2 of struc1 are drawn together when being + "converted to string. + "Contents of struc2-itab are replaced by table contents of struc1- + "itab. Note the value assignment, for example, for col2 in struc2-itab. + "Despite the fact that there is no identically named component comp1 in + "the target structure, values are assigned starting with the first + "column of the source structure. Check the conversion rules for + "internal tables. + + struc2 = CORRESPONDING #( struc1 ). + + out->write( data = struc2 name = `struc2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `22) DEEP addition` ) ). + + "CORRESPONDING operator with the addition DEEP + "Existing contents of identically named components are replaced. + "Existing contents of components in the target structure that are not + "available in the source structure are initialized. + "Contents of the nested structure struc_nested is converted to + "string. Note that the two components of the nested structure in + "component struc_nested-comp2 of struc1 are drawn together when being + "converted to string. + "Contents of struc2-itab are replaced by table contents of struc1- + "itab. Due to the addition DEEP, the value assignment happens for + "identically named components in the nested table. Hence, only col2 as + "the only shared and identically named component is filled. + + fill_deep_structures( ). + + struc2 = CORRESPONDING #( DEEP struc1 ). + + out->write( data = struc2 name = `struc2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `23) BASE addition` ) ). + + "CORRESPONDING operator with the addition BASE + "Existing contents of identically named components are replaced. + "Existing contents of components in the target structure that are not + "available in the source structure are kept. + "Contents of the nested structure struc_nested is converted to + "string. Note that the two components of the nested structure in + "component struc_nested-comp2 of struc1 are drawn together when being + "converted to string. + "Contents of struc2-itab are replaced by table contents of struc1- + "itab. The value assignment in the nested table happens like using the + "CORRESPONDING operator without addition. Note the value assignment, for + "example, for col2 in struc2-itab. Despite the fact that there is no + "identically named component col1 in the target structure, values are + "assigned starting with the first column of the source structure. Check + "the conversion rules for internal tables. + + fill_deep_structures( ). + + struc2 = CORRESPONDING #( BASE ( struc2 ) struc1 ). + + out->write( data = struc2 name = `struc2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `24) DEEP BASE addition` ) ). + + "CORRESPONDING operator with the additions DEEP BASE + "Existing contents of identically named components are replaced. + "Existing contents of components in the target structure that are not + "available in the source structure are kept. + "Contents of the nested structure struc_nested is converted to + "string. Note that the two components of the nested structure in + "component struc_nested-comp2 of struc1 are drawn together when being + "converted to string. + "Contents of struc2-itab are replaced by table contents of struc1- + "itab. The value assignment in the nested table happens like using the + "CORRESPONDING operator with the addition DEEP. That is, the value + "assignment happens for identically named components in the nested table. + "Hence, only col2 as the only shared and identically named component is filled. + + fill_deep_structures( ). + + struc2 = CORRESPONDING #( DEEP BASE ( struc2 ) struc1 ). + + out->write( data = struc2 name = `struc2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `25) APPENDING addition` ) ). + + "CORRESPONDING operator with the addition APPENDING + "Existing contents of identically named components are replaced. + "Existing contents of components in the target structure that are not + "available in the source structure are kept. + "Contents of the nested structure struc_nested is converted to + "string. Note that the two components of the nested structure in + "component struc_nested-comp2 of struc1 are drawn together when being + "converted to string. + "Contents of struc2-itab are kept and contents of struc1-itab are + "added. The value assignment concerning the added lines happens like + "using the CORRESPONDING operator without addition. Note the value + "assignment, for example, for col2 in struc2-itab. Despite the fact that + "there is no identically named component col1 in the target structure, + "values are assigned starting with the first column of the source + "structure. Check the conversion rules for internal tables. + + fill_deep_structures( ). + + struc2 = CORRESPONDING #( APPENDING ( struc2 ) struc1 ). + + out->write( data = struc2 name = `struc2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `26) DEEP APPENDING` ) ). + + "CORRESPONDING operator with the additions DEEP APPENDING + "Existing contents of identically named components are replaced. + "Existing contents of components in the target structure that are not + "available in the source structure are kept. + "Contents of the nested structure struc_nested is converted to + "string. Note that the two components of the nested structure in + "component struc_nested-comp2 of struc1 are drawn together when being + "converted to string. + "Contents of struc2-itab are kept and contents of struc1-itab are + "added. The value assignment concerning the added lines happens like + "using the CORRESPONDING operator with the addition DEEP. That is, the + "value assignment happens for identically named components in the nested + "table. Hence, only col2 as the only shared and identically named + "component is filled. + + fill_deep_structures( ). + + struc2 = CORRESPONDING #( DEEP APPENDING ( struc2 ) struc1 ). + + out->write( data = struc2 name = `struc2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `NEW` ) ). + out->write( `27) Creating Anonymous Data Objects` && |\n\n| ). + + "The examples show the creation of anonymous data objects. + "First, data reference variables are declared using a DATA statement. + "Here, one variable is declared with a complete type, the other one with + "a generic type. + "Then, anonymous data objects are created using the NEW operator. For + "one example, the type can be inferred. In the other example, the type + "is specified explicitly. + "The next examples show the direct assigning of values. + "Furthermore, inline declarations can be used to avoid the prior + "declaration of a variable. + + "Declaring data reference variables + DATA dref1 TYPE REF TO i. "Complete type + DATA dref2 TYPE REF TO data. "Generic type + + "Creating anonymous data objects + "Here, no parameters are specified within the parentheses meaning the + "data objects retain their initial values. + dref1 = NEW #( ). + dref2 = NEW string( ). + + IF dref1->* = 0 AND dref2->* = ``. + DATA(val) = `Initial values`. + ELSE. + val = `No initial values`. + ENDIF. + + "Directly assigning values within the parentheses. + dref1 = NEW #( 123 ). + dref2 = NEW string( `hallo` ). + + "Inline declaration, explicit type specification + DATA(dref3) = NEW i( 456 ). + + "Another constructor expression specified within the parentheses + DATA tx TYPE string VALUE `world`. + DATA(dref4) = NEW string( `Hello ` && tx && CONV string( '!' ) ). + + DATA dref5 TYPE REF TO string_table. + dref5 = NEW #( VALUE string_table( ( `a` ) ( `b` ) ) ). + + "Structured type; named arguments within the parentheses + DATA(dref6) = NEW zdemo_abap_carr( carrid = 'AA' + carrname = 'American Airlines' ). + + out->write( data = val name = `val` ). + out->write( |\n| ). + out->write( data = dref1 name = `dref1` ). + out->write( |\n| ). + out->write( data = dref2 name = `dref2` ). + out->write( |\n| ). + out->write( data = dref3 name = `dref3` ). + out->write( |\n| ). + out->write( data = dref4 name = `dref4` ). + out->write( |\n| ). + out->write( data = dref5 name = `dref5` ). + out->write( |\n| ). + out->write( data = dref6 name = `dref6` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `28) Creating Instances of Classes` ) ). + + "The example demonstrates the creation of instances of classes. + "First, an object reference variable is declared with a DATA statement. + "As a next step, an instance of a class is created. The type can be + "inferred here. The class has a constructor method defined. Hence, the + "parentheses contain the parameter binding for the constructor method. + "Here, it is only one parameter. That means the explicit specification + "of the parameter name is not needed and the value can be specified + "directly: oref1 = NEW #( `Hallo` ). + "The next examples show object reference variables that are declared + "inline. Here, the type (i. e. the class name) must be specified + "explicitly. + "The last example shows the method chaining that is possible with + "expressions using the NEW operator. The demo class has a method that + "has a returning parameter. In this case, the parameter of the method + "is of type REF TO i. + + "Creating an object reference variable + DATA oref1 TYPE REF TO local_class. + + "Creating an instance of a class; + "providing parameter bindings for the constructor method + "in the parentheses + oref1 = NEW #( txt = `Hallo` ). + + out->write( data = oref1 name = `oref1` ). + out->write( |\n| ). + + "Creating an instance of a class, object reference variable + "is declared inline, explicit type specification + DATA(oref2) = NEW local_class( `Salut` ). + + out->write( data = oref2 name = `oref2` ). + out->write( |\n| ). + + "Method chaining + DATA(result) = NEW local_class( `Ciao` )->double( int = NEW #( 5 ) ). + + out->write( data = result name = `result` ). + out->write( |\n| ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `29) CONV` ) ). + + "The examples show the effect of the CONV operator. + "A variable of type i is declared and assigned a value. Then, + "calculations are carried out. The result is stored in a variable + "declared inline. The first result is 0 because the derived type is i. + "The second calculation returns the precise value resulting from the + "division because the CONV + "expression triggers the conversion of the result (decfloat34). + "The next examples demonstrate logical expressions using character-like + "data types. + "A variable is of type abap_bool, i. e. a single character is expected. + "In this case, the variable is initial, i. e. the content is a blank. + "Another variable is of type string. A blank is assigned to this + "variable. + "A logical expression compares the two variables. Without the conversion + "using the CONV operator, the two are not equal due to the comparison + "rules for character-like data types (trailing blanks are not respected + "in variable-length strings). When the string is converted, the + "comparison results to true. + + "Declaring data object and assign value + DATA num TYPE i VALUE 1. + + "Effect of a calculation ... + "... without conversion + DATA(i) = num / 4. + + "... with conversion using an appropriate type + DATA(dec_num) = CONV decfloat34( num / 4 ). + + out->write( data = i name = `i` ). + out->write( |\n| ). + out->write( data = dec_num name = `dec_num` ). + out->write( |\n| ). + + "Declaring data objects + DATA(txt) = VALUE abap_bool( ). + + DATA(str) = ` `. + + "Comparing the data objects with and without conversion + out->write( `Without conversion:` ). + + IF txt = str. + out->write( `txt is equal to str.` ). + ELSE. + out->write( `txt is not equal to str.` ). + ENDIF. + + out->write( |\n| ). + out->write( `With conversion:` ). + + IF txt = CONV abap_bool( str ). + out->write( `txt is equal to converted str.` ). + ELSE. + out->write( `txt is not equal to converted str.` ). + ENDIF. + + "Example with internal table types + TYPES inttab_type TYPE TABLE OF i WITH EMPTY KEY. + DATA int_itab TYPE SORTED TABLE OF i WITH NON-UNIQUE DEFAULT KEY. + FIELD-SYMBOLS TYPE inttab_type. + int_itab = VALUE #( ( 1 ) ( 2 ) ( 3 ) ). + + "The following assignment is not possible due to incompatible types. + "The internal table has the same line type, but it has a different + "table type and key. + "ASSIGN itab TO . + + "Using CONV to convert the internal table to the required table type. + DATA(conv_itab) = CONV inttab_type( int_itab ). + ASSIGN conv_itab TO . + + out->write( |\n| ). + out->write( |\n| ). + out->write( data = name = `` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `30) Constructing Data Objects with the CONV Operator` ) ). + + DATA(decnum1) = CONV decfloat34( '0.4' ). + + "Instead of + DATA decnum2 TYPE decfloat34 VALUE '0.4'. + "or + DATA decnum3 TYPE decfloat34. + decnum3 = '0.4'. + + out->write( `No output for this section. See the code.` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `31) EXACT` ) ). + + "-------------- Lossless assignments ------------- + + "Note: An assignment is made in accordance with conversion rules. Check + "the ABAP Keyword Documentation for these rules. An assignment is only + "made if no values are lost. Otherwise, an error occurs. Either it is + "detected by static code checks or at runtime raising a catchable exception. + TYPES clen3 TYPE c LENGTH 3. + DATA(as1) = EXACT clen3( abap_true ). + DATA(as2) = EXACT clen3( 'XY' ). + "DATA(as3) = EXACT clen3( 'abcd' ). + + out->write( data = as1 name = `as1` ). + out->write( |\n| ). + out->write( data = as2 name = `as2` ). + out->write( |\n| ). + + + "Catching exception + TRY. + DATA(as4) = EXACT clen3( 'abcd' ). + out->write( data = as4 name = `as4` ). + CATCH cx_sy_conversion_data_loss INTO DATA(dl_err). + out->write( data = dl_err->get_text( ) name = `dl_err->get_text( )` ). + ENDTRY. + out->write( |\n| ). + + "-------------- Lossless calculations ------------- + + "The first statement works, whereas the second statement raises an exception. + "A rounding to two decimal places is required. + TYPES packednum TYPE p LENGTH 8 DECIMALS 2. + DATA(calc1) = EXACT packednum( 1 / 4 ). + "DATA(calc2) = EXACT packednum( 1 / 3 ). + + out->write( data = calc1 name = `calc1` ). + out->write( |\n| ). + + "Catching exceptions when rounding in lossless calculations + TRY. + DATA(calc3) = EXACT packednum( 1 / 3 ). + out->write( data = calc3 name = `calc3` ). + CATCH cx_sy_conversion_rounding INTO DATA(lc_err). + out->write( data = lc_err->get_text( ) name = `lc_err->get_text( )` ). + ENDTRY. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `32) REF` ) ). + + "The example includes the declaration of a data object and some data + "reference variables. One data reference variable is typed with a + "complete type, the other one is typed with a generic type. Then, data + "references to the data object declared before are created. Using the # + "symbol means that the type can be automatically derived. + "You can also use inline declarations to omit the explicit declaration. + "Another example shows the explicit specification of the data type after + "REF. + "Furthermore, an object reference is created using inline declaration. + + "Declaring data object + DATA number TYPE i VALUE 5. + + "Declaring data reference variables + DATA dref_a TYPE REF TO i. "Complete type + DATA dref_b TYPE REF TO data. "Generic type + + "Creating data references to data objects + dref_a = REF #( number ). + dref_b = REF #( number ). + + "Data reference variable declared inline + DATA(dref_c) = REF #( number ). + + "Type specified explicitly + DATA(dref_d) = REF string( `hallo` ). + + "Object references + DATA(oref_a) = NEW local_class( `Ciao` ). + DATA(oref_b) = REF #( oref_a ). + + out->write( data = dref_a name = `dref_a` ). + out->write( |\n| ). + out->write( data = dref_b name = `dref_b` ). + out->write( |\n| ). + out->write( data = dref_c name = `dref_c` ). + out->write( |\n| ). + out->write( data = dref_d name = `dref_d` ). + out->write( |\n| ). + out->write( data = oref_b name = `oref_b` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `33) CAST` ) ). + + "The example demonstrates the CAST operator in the context of Run Time + "Type Identification (RTTI). + "First, the components of a structure are retrieved. Secondly, the + "method information of a local class is retrieved. The syntax + "particularly shows the advantages of downcasts carried out with the + "CAST operator that also enables method chaining. An example is added + "that demonstrates the use of the older ?= operator with which extra + "variables are needed. Furthermore, simple downcasts are demonstrated + "using data references. + + "RTTI examples + "Using CAST + "Retrieving structure components + DATA(components_s2) = + CAST cl_abap_structdescr( + cl_abap_typedescr=>describe_by_data( s2 ) )->components. + + "Retrieving the methods of a local class + DATA(methods) = + CAST cl_abap_objectdescr( + cl_abap_objectdescr=>describe_by_name( 'LOCAL_CLASS' ) + )->methods. + + "Excursion: Using the older cast operator ?= + "Retrieving structure components + "Note: More lines of code, helper variables needed + DATA structdescr TYPE REF TO cl_abap_structdescr. + + structdescr ?= cl_abap_typedescr=>describe_by_data( s1 ). + + DATA(components_s1) = structdescr->components. + + "Casting with data references + DATA dref_i TYPE REF TO i. "Complete type + + DATA dref_data TYPE REF TO data. "Generic type + + dref_data = NEW i( 123 ). + + dref_i = CAST #( dref_data ). + + out->write( data = components_s2 name = `components_s2` ). + out->write( |\n| ). + out->write( data = methods name = `methods` ). + out->write( |\n| ). + out->write( data = components_s1 name = `components_s1` ). + out->write( |\n| ). + out->write( data = dref_i name = `dref_i` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `34) COND` ) ). + + DATA(day_or_night) = COND #( WHEN cl_abap_context_info=>get_system_time( ) BETWEEN '050000' AND '220000' + THEN `day` + ELSE `night` ). + + out->write( data = day_or_night name = `day_or_night` ). + out->write( |\n| ). + + "A constructor expression as above instead of, for example, an IF statement as follows. + IF cl_abap_context_info=>get_system_time( ) BETWEEN '050000' AND '220000'. + day_or_night = `day`. + ELSE. + day_or_night = `night`. + ENDIF. + + out->write( data = day_or_night name = `day_or_night` ). + out->write( |\n| ). + + "Multiple logical expressions initiated by WHEN + "Also LET expressions are possible. See more details further down. + DATA(time_of_day) = COND #( LET time = cl_abap_context_info=>get_system_time( ) IN + WHEN time BETWEEN '050001' AND '120000' THEN |Good morning, it's { time TIME = ISO }.| + WHEN time BETWEEN '120001' AND '180000' THEN |Good afternoon, it's { time TIME = ISO }.| + WHEN time BETWEEN '180001' AND '220000' THEN |Good evening, it's { time TIME = ISO }.| + ELSE |Good night, it's { time TIME = ISO }.| ). + + out->write( data = time_of_day name = `time_of_day` ). + out->write( |\n| ). + + "THROW addition to raise an exception (working like RAISE EXCEPTION TYPE statements) + "by specifying an exception class + "Note: It is possible to ... + "- specify the THROW addition also after THEN. + "- make exceptions resumable using the RESUMABLE addition. + DATA(number1) = 0. + DATA(number2) = 0. + TRY. + "The example raises the exception because both operands have the value 0. + DATA(div) = COND decfloat34( WHEN number1 <> 0 AND number2 <> 0 THEN number1 / number2 + WHEN number1 = 0 AND number2 <> 0 THEN number1 / number2 + ELSE THROW cx_sy_zerodivide( ) ). + out->write( data = div name = `div` ). + CATCH cx_sy_zerodivide. + DATA(two_zeros) = `Zero division`. + out->write( data = two_zeros name = `two_zeros` ). + ENDTRY. + + out->write( |\n| ). + + "Excursion for the example above: The following statement does not result in an + "error in ABAP (zero division 'allowed' if the first operand has also the value 0). + div = 0 / 0. + + "THROW SHORTDUMP addition to raise a runtime error (working like RAISE SHORTDUMP + "TYPE statements) by specifying an exception class; a message can be also passed, + "and input parameters can be filled +* TRY. +* div = COND decfloat34( WHEN number1 <> 0 AND number2 <> 0 THEN number1 / number2 +* WHEN number1 = 0 AND number2 <> 0 THEN number1 / number2 +* ELSE THROW SHORTDUMP cx_sy_zerodivide( ) ). +* out->write( data = div name = `div` ). +* CATCH cx_sy_zerodivide. +* ENDTRY. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `35) SWITCH` ) ). + + "The example demonstrates the use of the SWITCH operator. Here, + "calculations are carried out. For this + "purpose, a string table is created that includes arithmetic operators + "and, purposely, one entry that does not fit. The internal table is looped + "across. Based on the current arithmetic operator, the calculation is + "carried out. A string is displayed accordingly. The code is embedded in + "a TRY ... CATCH ... ENDTRY block to take care of arithmetic errors. For + "example, you might insert the value 0 for the variable num2 to provoke 0 + "division. The CONV operator is also used in the example to respect + "decimals. + + DATA operator_tab TYPE TABLE OF string. + + operator_tab = VALUE #( ( `+` ) ( `-` ) ( `*` ) ( `/` ) ( `#` ) ). + + DATA(num1) = 2. + DATA(num2) = 4. + + LOOP AT operator_tab ASSIGNING FIELD-SYMBOL(). + TRY. + DATA(calc_result) = + SWITCH string( + WHEN '+' THEN |{ num1 + num2 STYLE = SIMPLE }| + WHEN '-' THEN |{ num1 - num2 STYLE = SIMPLE }| + WHEN '*' THEN |{ num1 * num2 STYLE = SIMPLE }| + WHEN '/' THEN |{ CONV decfloat34( num1 / num2 ) STYLE = SIMPLE }| + ELSE `That doesn't work.` ). + out->write( |{ num1 } { } { num2 } = { calc_result }| ). + CATCH cx_sy_arithmetic_error INTO DATA(error). + out->write( |Arithmetic error. { error->get_text( ) }| ). + ENDTRY. + ENDLOOP. + out->write( |\n| ). + out->write( |\n| ). + + "A constructor expression as above instead of, for example, a CASE statement as follows. + DATA(operator) = '+'. + CASE operator. + WHEN '+'. + calc_result = |{ num1 + num2 STYLE = SIMPLE }|. + WHEN '-'. + calc_result = |{ num1 - num2 STYLE = SIMPLE }|. + WHEN '*'. + calc_result = |{ num1 * num2 STYLE = SIMPLE }|. + WHEN '/'. + calc_result = |{ CONV decfloat34( num1 / num2 ) STYLE = SIMPLE }|. + WHEN OTHERS. + calc_result = `Wrong operator.`. + ENDCASE. + + out->write( data = calc_result name = `calc_result` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `36) FILTER` ) ). + + "This section covers multiple examples demonstrating the syntactical variety + "of the FILTER operator. + + TYPES: BEGIN OF fi_str, + a TYPE i, + b TYPE c LENGTH 3, + c TYPE c LENGTH 3, + END OF fi_str. + + "basic form, condition created with single values + "itab must have at least one sorted key or one hash key used for access. + "This variant of the filter operator is not possible for an internal table itab without a sorted key or hash key. + DATA fi_tab1 TYPE SORTED TABLE OF fi_str WITH NON-UNIQUE KEY a. + DATA fi_tab2 TYPE STANDARD TABLE OF fi_str WITH NON-UNIQUE SORTED KEY sec_key COMPONENTS a. + DATA fi_tab3 TYPE HASHED TABLE OF fi_str WITH UNIQUE KEY a. + + "Filling internal tables + fi_tab1 = VALUE #( ( a = 1 b = 'aaa' c = 'abc' ) + ( a = 2 b = 'bbb' c = 'def' ) + ( a = 3 b = 'ccc' c = 'hij' ) + ( a = 4 b = 'ddd' c = 'klm' ) + ( a = 5 b = 'eee' c = 'nop' ) ). + + fi_tab2 = fi_tab1. + fi_tab3 = fi_tab1. + + "The lines meeting the condition are respected. + "Note: The source table must have at least one sorted or hashed key. + "Here, the primary key is used + DATA(f1) = FILTER #( fi_tab1 WHERE a >= 3 ). + + out->write( data = f1 name = `f1` ). + out->write( |\n| ). + + "USING KEY primary_key explicitly specified; same as above + DATA(f2) = FILTER #( fi_tab1 USING KEY primary_key WHERE a >= 3 ). + + out->write( data = f2 name = `f2` ). + out->write( |\n| ). + + "EXCEPT addition + DATA(f3) = FILTER #( fi_tab1 EXCEPT WHERE a >= 3 ). + + out->write( data = f3 name = `f3` ). + out->write( |\n| ). + + DATA(f4) = FILTER #( fi_tab1 EXCEPT USING KEY primary_key WHERE a >= 3 ). + + out->write( data = f4 name = `f4` ). + out->write( |\n| ). + + "Secondary table key specified after USING KEY + DATA(f5) = FILTER #( fi_tab2 USING KEY sec_key WHERE a >= 4 ). + + out->write( data = f5 name = `f5` ). + out->write( |\n| ). + + DATA(f6) = FILTER #( fi_tab2 EXCEPT USING KEY sec_key WHERE a >= 3 ). + + out->write( data = f6 name = `f6` ). + out->write( |\n| ). + + "Note: In case of a hash key, exactly one comparison expression for each key + "component is allowed; only = as comparison operator possible. + DATA(f7) = FILTER #( fi_tab3 WHERE a = 3 ). + + out->write( data = f7 name = `f7` ). + out->write( |\n| ). + + "Using a filter table + "In the WHERE condition, the columns of source and filter table are compared. + "Those lines in the source table are used for which at least one line in the + "filter table meets the condition. EXCEPT and USING KEY are also possible. + + "Declaring and filling filter tables + DATA filter_tab1 TYPE SORTED TABLE OF i + WITH NON-UNIQUE KEY table_line. + + DATA filter_tab2 TYPE STANDARD TABLE OF i + WITH EMPTY KEY + WITH UNIQUE SORTED KEY line COMPONENTS table_line. + + filter_tab1 = VALUE #( ( 3 ) ( 5 ) ). + filter_tab2 = filter_tab1. + + DATA(f8) = FILTER #( fi_tab1 IN filter_tab1 WHERE a = table_line ). + + out->write( data = f8 name = `f8` ). + out->write( |\n| ). + + "EXCEPT addition + DATA(f9) = FILTER #( fi_tab1 EXCEPT IN filter_tab1 WHERE a = table_line ). + + out->write( data = f9 name = `f9` ). + out->write( |\n| ). + + "USING KEY is specified for the filter table + DATA(f10) = FILTER #( fi_tab2 IN filter_tab2 USING KEY line WHERE a = table_line ). + + out->write( data = f10 name = `f10` ). + out->write( |\n| ). + + "USING KEY is specified for the source table, including EXCEPT + DATA(f11) = FILTER #( fi_tab2 USING KEY sec_key EXCEPT IN filter_tab2 WHERE a = table_line ). + + out->write( data = f11 name = `f11` ). + out->write( |\n| ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `37) Iteration Expressions with FOR` ) ). + + "Data objects and types to work with in the examples + TYPES: BEGIN OF s, + col1 TYPE c LENGTH 5, + col2 TYPE i, + col3 TYPE i, + END OF s. + TYPES it_type TYPE TABLE OF s WITH EMPTY KEY. + DATA(itab4for) = VALUE it_type( ( col1 = 'a' col2 = 1 col3 = 30 ) + ( col1 = 'bb' col2 = 2 col3 = 10 ) + ( col1 = 'ccc' col2 = 3 col3 = 20 ) ). + + "-------------- Table iterations -------------- + + DATA(it1) = VALUE it_type( FOR wa IN itab4for ( col1 = wa-col1 && 'z' + col2 = wa-col2 + 1 ) ). + + out->write( data = it1 name = `it1` ). + out->write( |\n| ). + + "LOOP AT equivalent + CLEAR it1. + LOOP AT itab4for INTO DATA(wa_loop). + APPEND VALUE #( col1 = wa_loop-col1 && 'z' + col2 = wa_loop-col2 + 1 ) TO it1. + ENDLOOP. + + out->write( data = it1 name = `it1` ). + out->write( |\n| ). + + "The following example shows more syntax options + "- Field symbol specifed after FOR + "- LET expressions after FOR: Denotes that the LET + " expressions is evaluated for each loop pass + "- INDEX INTO addition (the variable that follows implicitly + " has the type i): Storing the sy-tabix value for each + " loop pass + DATA(it2) = VALUE it_type( FOR IN itab4for INDEX INTO idx + LET idxplus1 = idx + 1 IN + ( col1 = -col1 col2 = idx col3 = idxplus1 ) ). + + out->write( data = it2 name = `it2` ). + out->write( |\n| ). + + "Similar to the example above, the following example uses the INDEX INTO + "addition, as well as a LET expression with multiple local variables + DATA(it3) = VALUE string_table( FOR IN itab4for INDEX INTO idx + LET col1 = |COL1: "{ -col1 }"| + col2 = |COL2: "{ -col2 }"| + col3 = |COL3: "{ -col3 }"| + str_to_be_added = |Table index { idx } -> { col1 } / { col2 } / { col3 }| + IN ( str_to_be_added ) ). + + out->write( data = it3 name = `it3` ). + out->write( |\n| ). + + "---------- Excursions ---------- + + "Merging tables + "In the following example, the content of two existing internal tables is merged. + "In the simple example, the index is used for the table index. You can also imagine + "that you merge two internal tables, both having multiple columns. You could refer + "to the specific component values, for example, using a free key in a table expression + "such as ... VALUE #( some_itab[ comp_x = wa-comp_y ]-comp_z DEFAULT ... ) ... + TYPES inttabtype TYPE TABLE OF i WITH EMPTY KEY. + DATA(integertable) = VALUE inttabtype( ( 99 ) ( 100 ) ). + + DATA(it4) = VALUE it_type( FOR wa IN itab4for INDEX INTO idx + ( col1 = wa-col1 col2 = VALUE #( integertable[ idx ] DEFAULT 0 ) ) ). + + out->write( data = it4 name = `it4` ). + out->write( |\n| ). + + "Retaining non-specified column values using the BASE addition + "In the example, the original value of col3 is retained. + DATA(it5) = VALUE it_type( FOR wa IN itab4for ( VALUE #( BASE wa col1 = wa-col1 && 'y' + col2 = wa-col2 + 3 ) ) ). + + out->write( data = it5 name = `it5` ). + out->write( |\n| ). + + "Using the CORRESPONDING operator to handle different types + TYPES: BEGIN OF s2, + col1 TYPE c LENGTH 5, + col2 TYPE i, + str TYPE string, + END OF s2. + TYPES itab_type_2 TYPE TABLE OF s2 WITH EMPTY KEY. + + DATA(it6) = VALUE itab_type_2( FOR wa IN itab4for ( CORRESPONDING #( wa ) ) ). + + out->write( data = it6 name = `it6` ). + out->write( |\n| ). + + "Multiple FOR expressions that work like nested loops + DATA(it7) = VALUE string_table( FOR wa1 IN itab4for + FOR wa2 IN integertable + ( |Comp. 1st itab: "{ wa1-col1 }", comp. 2nd itab: "{ wa2 }"| ) ). + + out->write( data = it7 name = `it7` ). + out->write( |\n| ). + + "LOOP AT equivalent + CLEAR it7. + LOOP AT itab4for INTO DATA(wa3). + LOOP AT integertable INTO DATA(wa4). + it7 = VALUE #( BASE it7 ( |Comp. 1st itab: "{ wa3-col1 }", comp. 2nd itab: "{ wa4 }"| ) ). + ENDLOOP. + ENDLOOP. + + out->write( data = it7 name = `it7` ). + out->write( |\n| ). + + "More additions can be specified such as WHERE, USING KEY, FROM/TO, STEP + + "WHERE condition + "The WHERE condition must be placed in parentheses. + DATA(it8) = VALUE it_type( FOR wa IN itab4for WHERE ( col2 < 3 ) ( col1 = wa-col1 && 'w' + col2 = 5 + col3 = wa-col2 ) ). + + out->write( data = it8 name = `it8` ). + out->write( |\n| ). + + "FROM/TO additions + DATA(it9) = VALUE it_type( FOR wa IN itab4for FROM 2 TO 3 ( col1 = wa-col1 && 'v' + col2 = 6 + col3 = wa-col2 + 5 ) ). + + out->write( data = it9 name = `it9` ). + out->write( |\n| ). + + "STEP addition + DATA(it10) = VALUE it_type( FOR wa IN itab4for STEP -1 ( col1 = wa-col1 && 'u' + col2 = 7 + col3 = wa-col2 + 8 ) ). + + out->write( data = it10 name = `it10` ). + out->write( |\n| ). + + "USING KEY addition + DATA(it11) = VALUE it_type( FOR wa IN itab4for USING KEY primary_key ( col1 = wa-col1 && 't' + col2 = 9 + col3 = wa-col2 + 10 ) ). + + out->write( data = it11 name = `it11` ). + out->write( |\n| ). + + "---------- Conditional iterations ---------- + + "FOR ... WHILE ... + DATA(it12) = VALUE it_type( FOR x = 1 WHILE x < 4 + ( col1 = x col2 = x + 1 col3 = x + 2 ) ). + + out->write( data = it12 name = `it12` ). + out->write( |\n| ). + + "FOR ... UNTIL ... + "The THEN addition is also possible for ... WHILE ... + DATA(it13) = VALUE it_type( FOR y = 31 THEN y - 10 UNTIL y < 10 + ( col1 = y col2 = y + 1 col3 = y + 2 ) ). + + out->write( data = it13 name = `it13` ). + out->write( |\n| ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `38) More Examples with Iteration Expressions with FOR` ) ). + + "The examples demonstrate iteration expressions with FOR. The examples + "are based on demo internal tables that are filled using a method. The + "tables are displayed to show the original content of the internal + "tables that are to be processed. + + out->write( |Original table content\n\n| ). + + "Method to fill demo internal tables with values to work with. + "Tables are displayed showing the values. + fill_struc_and_tab( ). + + out->write( data = tab1 name = `tab1` ). + out->write( |\n| ). + out->write( data = tab2 name = `tab2` ). + out->write( |\n| ). + out->write( data = tab3 name = `tab3` ). + + out->write( |\n| ). + out->write( `************ FOR ... IN ... (LOOP Semantics) ************` ). + out->write( |\n| ). + + "Examples demonstrating FOR ... IN ... that has the semantics of LOOP. + "1) An internal table is looped across. The whole line is stored in a + "new table which is declared inline. The target table must have the + "same table type as the internal table that is looped across. The + "example is without a WHERE condition, i. e. all lines of the source + "table are considered. + "2) A new table is created that has a different table type. However, + "both table types have identically named components. The example shows + "how you can fill all identically named components of the target table. + "The example includes a WHERE condition to restrict the lines to be + "considered. Pay attention to potential type conversions. + "3) A new table is created that has a different table type. Here, it is + "shown that the components and their assignment might be specified + "individually. A WHERE clause is included. Pay attention to potential + "type conversions. + + "1) Storing the whole line in a new table. + "The target table must have the same table type as the source table. + "Without the WHERE condition, all lines are considered. + + TYPES t_type1 LIKE tab3. + + DATA(for1) = VALUE t_type1( FOR w IN tab3 ( w ) ). + + "2) Storing specific components having the same names. + "The target type is not compatible to the type of the source table. + "Identically named components exist. + + TYPES t_type2 LIKE tab1. + + DATA(for2) = VALUE t_type2( FOR w IN tab3 + WHERE ( comp4 > 7 ) + ( CORRESPONDING #( w ) ) ). + + "3) Specify components individually and providing a mapping + + DATA(for3) = VALUE t_type2( FOR w IN tab3 + WHERE ( comp4 > 7 ) + ( comp1 = w-comp1 + comp2 = `hallo` + comp3 = w-comp4 ) ). + + out->write( data = for1 name = `for1` ). + out->write( |\n| ). + out->write( data = for2 name = `for2` ). + out->write( |\n| ). + out->write( data = for3 name = `for3` ). + out->write( |\n| ). + + "The example demonstrates multiple iteration expressions with FOR. Here, + "a new table is created that is declared inline. Three tables are + "respected. The lines to be considered are restricted by a WHERE + "clause. + "A component name mapping takes care of assigning specific values to + "non-identically named components. + + "Declaring an internal table type + TYPES: BEGIN OF line_type2, + compX TYPE c LENGTH 1, + compY TYPE string, + compZ TYPE i, + END OF line_type2, + t_type3 TYPE STANDARD TABLE OF line_type2 WITH EMPTY KEY. + + "Nested iteration expressions with FOR + DATA(for4) = VALUE t_type3( + FOR w1 IN tab1 WHERE ( comp1 = 'A' ) + FOR w2 IN tab2 WHERE ( comp4 > 6 ) + FOR w3 IN tab3 WHERE ( comp3 < 4 ) + ( compX = w1-comp1 + compY = w2-comp1 + compZ = w3-comp3 ) ). + + out->write( data = for4 name = `for4` ). + + out->write( |\n| ). + out->write( `************ FOR ... WHILE/UNTIL ... (DO/WHILE Semantics) ************` ). + out->write( |\n| ). + + "Examples demonstrating FOR ... WHILE/UNTIL ... that has the semantics + "of DO/WHILE. + "The example demonstrates the construction of internal tables using + "condition iterations with a constructor expression + "and the corresponding variant of the value operator VALUE. Two internal + "tables with different iterations, one using FOR ... WHILE ..., the + "other FOR ... UNTIL ..., are created. + + "Declaring and internal table type + TYPES: + BEGIN OF line_type3, + col1 TYPE i, + col2 TYPE i, + col3 TYPE i, + END OF line_type3, + t_type4 TYPE STANDARD TABLE OF line_type3 WITH EMPTY KEY. + + "FOR ... WHILE ... + DATA(for5) = VALUE t_type4( + FOR x = 11 THEN x + 10 WHILE x < 40 + ( col1 = x col2 = x + 1 col3 = x + 2 ) ). + + "FOR ... UNTIL ... + DATA(for6) = VALUE t_type4( + FOR y = 31 THEN y - 10 UNTIL y < 10 + ( col1 = y col2 = y + 1 col3 = y + 2 ) ). + + out->write( data = for5 name = `for5` ). + out->write( |\n| ). + out->write( data = for6 name = `for6` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `39) REDUCE` ) ). + + "Data objects and types to work with in the examples + TYPES: BEGIN OF st, + col1 TYPE c LENGTH 5, + col2 TYPE i, + col3 TYPE i, + END OF st. + TYPES tab_type TYPE TABLE OF st WITH EMPTY KEY. + DATA(itab4reduce) = VALUE tab_type( ( col1 = 'a' col2 = 1 col3 = 30 ) + ( col1 = 'bb' col2 = 2 col3 = 10 ) + ( col1 = 'ccc' col2 = 3 col3 = 20 ) ). + + "---------- Table iterations ---------- + + "Calculating the sum of values in a table column + DATA(sum_val) = REDUCE i( INIT len = 0 + FOR IN itab4reduce + NEXT len = len + -col2 ). + + out->write( data = sum_val name = `sum_val` ). + out->write( |\n| ). + + "Getting the longest string in a table column + DATA(long_str) = REDUCE st-col1( INIT string = VALUE #( ) + FOR IN itab4reduce + NEXT string = COND #( WHEN strlen( -col1 ) > strlen( string ) + THEN -col1 + ELSE string ) ). + + out->write( data = long_str name = `long_str` ). + out->write( |\n| ). + + "Getting the maximum value (other than, for example, using a SORT statement) + "Unlike above, a variable is used instead of a field symbol. + DATA(max_val) = REDUCE i( INIT max = 0 + FOR line IN itab4reduce + NEXT max = COND #( WHEN line-col2 > max + THEN line-col2 + ELSE max ) ). + + out->write( data = max_val name = `max_val` ). + out->write( |\n| ). + + "Creating a new internal table using REDUCE + "In the example, the sum of two values is calculated. + "A VALUE expression with the BASE addition is used to + "add a line to a table (retaining the existing lines). + DATA(itstr) = REDUCE string_table( INIT strtab = VALUE string_table( ) + FOR workarea IN itab4reduce + NEXT strtab = VALUE #( BASE strtab + ( |The sum of { workarea-col2 } and { workarea-col3 } is { workarea-col2 + workarea-col3 }.| ) ) ). + + out->write( data = itstr name = `itstr` ). + out->write( |\n| ). + + "More additions are possible, such as specifying a WHERE condition (which + "must be specified in parentheses). The following example creates a new + "internal table based on a WHERE condition. + TYPES: BEGIN OF s3, + num1 TYPE i, + num2 TYPE i, + sum TYPE i, + END OF s3. + TYPES s3_tab_type TYPE TABLE OF s3 WITH EMPTY KEY. + DATA(itred) = REDUCE s3_tab_type( INIT tab = VALUE s3_tab_type( ) + FOR workarea IN itab4reduce + WHERE ( col2 < 3 ) + NEXT tab = VALUE #( BASE tab + ( num1 = workarea-col2 num2 = workarea-col3 sum = workarea-col2 + workarea-col3 ) ) ). + + out->write( data = itred name = `itred` ). + out->write( |\n| ). + + "---------- Conditional iterations ---------- + + "UNTIL addition + "Iteratively calculating the sum from 1 to 10 + DATA(reduce_until) = REDUCE i( INIT sum = 0 + FOR int = 1 UNTIL int > 10 + NEXT sum += int ). + + out->write( data = reduce_until name = `reduce_until` ). + out->write( |\n| ). + + "WHILE addition + "The example corresponds to the previous one. + DATA(reduce_while) = REDUCE i( INIT sum = 0 + FOR int = 1 WHILE int <= 10 + NEXT sum += int ). + + out->write( data = reduce_while name = `reduce_while` ). + out->write( |\n| ). + + "THEN addition + "The following example constructs a text string. The THEN addition is used + "to decrement the iteration variable. Additionally, a LET expression is used + "to specify a helper variable. + "Result: Counting downwards starting with 10: 10 9 8 7 6 5 4 3 2 1 + DATA(count) = REDUCE string( LET start = 10 IN + INIT text = |Counting downwards starting with { start }:| + FOR n = start THEN n - 1 WHILE n > 0 + NEXT text &&= | { n }| ). + + out->write( data = count name = `count` ). + out->write( |\n| ). + + "Example similar to the previous one. Using UNTIL, a text string is enlarged until + "it has reached a specific size. + "Result: ab abap abapap abapapap abapapapap abapapapapap abapapapapapap + DATA(abap_str) = REDUCE string( INIT text = `` + FOR t = `ab` THEN t && `ap` UNTIL strlen( t ) > 15 + NEXT text &&= |{ t } | ). + + out->write( data = abap_str name = `abap_str` ). + out->write( |\n| ). + + "---------- Excursion: Grouping lines in internal tables with VALUE/REDUCE ---------- + + "The following examples show equivalents of LOOP AT GROUP ... GROUP BY ... statements. + "Find more information and examples about grouping in the ABAP Keyword Documentation. + + "Internal table to work with in the examples + DATA(itab4grp) = VALUE tab_type( ( col1 = 'a' col2 = 1 col3 = 2 ) + ( col1 = 'a' col2 = 3 col3 = 4 ) + ( col1 = 'a' col2 = 5 col3 = 6 ) + ( col1 = 'b' col2 = 7 col3 = 8 ) + ( col1 = 'b' col2 = 9 col3 = 10 ) + ( col1 = 'c' col2 = 11 col3 = 12 ) ). + + + "Constructing a result using VALUE + "The following example returns the values of identified groups in an internal table + "Table lines are evaluated by grouping all lines that meet the condition + "specified in GROUP BY (group key binding). The group key is stored in the variable + "after FOR GROUPS (gr). The constructed result just consists of the group keys in + "the example. The content of the members is not relevant. + DATA(it_val_1) = VALUE string_table( FOR GROUPS gr OF wr IN itab4grp + GROUP BY wr-col1 ASCENDING + WITHOUT MEMBERS + ( |{ gr }| ) ). + + out->write( data = it_val_1 name = `it_val_1` ). + out->write( |\n| ). + + + "As above, the following example returns the values of identified groups in an internal table. + "Additionally, a LET expression (that itself contains an iteration expression) is specified + "to collect column values by group in an internal table. The lines of this (string) table + "are concatenated and inserted in the target table. + DATA(it_val_2) = VALUE string_table( + FOR GROUPS grp OF wr IN itab4grp + GROUP BY wr-col1 ASCENDING + LET members = VALUE string_table( + FOR grpd IN GROUP grp ( |{ grpd-col2 }, { grpd-col3 }| ) ) IN + ( |{ grp }: { concat_lines_of( table = members sep = ` / ` ) }| ) ). + + out->write( data = it_val_2 name = `it_val_2` ). + out->write( |\n| ). + + "Constructing a result using REDUCE + "The example is similar to the previous one by filling a string table. + "The example uses a group key expression specified after GROUP BY. + "In the group key expression, additional components of a structured + "group key are specified which return specific information (group size, + "group index). + DATA(it_reduced) = REDUCE string_table( + INIT li = VALUE string_table( ) + FOR GROUPS group OF grt IN itab4grp + GROUP BY ( grpkey = grt-col1 + size = GROUP SIZE + index = GROUP INDEX ) ASCENDING + LET mem = VALUE string_table( FOR grpr IN GROUP group ( |{ grpr-col2 }, { grpr-col3 }| ) ) IN + NEXT li = VALUE string_table( BASE li ( |Group key: "{ group-grpkey }" \| | && + |group size: { group-size } \| | && + |group index: { group-index } \| members: | && + |{ concat_lines_of( table = mem sep = ` / ` ) }| ) ) ). + + out->write( data = it_reduced name = `it_reduced` ). + out->write( |\n| ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `40) LET Expressions` ) ). + + "Data type and object to work with in the example + TYPES: BEGIN OF st_type, + comp1 TYPE c LENGTH 5, + comp2 TYPE i, + comp3 TYPE i, + END OF st_type. + DATA it TYPE TABLE OF st_type WITH EMPTY KEY. + it = VALUE #( ( comp1 = 'a' comp2 = 1 comp3 = 30 ) + ( comp1 = 'bb' comp2 = 2 comp3 = 10 ) + ( comp1 = 'ccc' comp2 = 3 comp3 = 20 ) ). + + "Constructing a data object with elementary data type using the CONV operator + "One or more helper variables are possible specified after LET + DATA(hi) = CONV string( + LET name = cl_abap_context_info=>get_user_technical_name( ) + date = cl_abap_context_info=>get_system_date( ) + IN |Hi { name }. Today's date is { date DATE = ISO }.| ). + + out->write( data = hi name = `hi` ). + out->write( |\n| ). + + "Construction similar to the previous example + "Depending on the time, a string is created. In the example, a LET expression + "is specified for each constructor expression. + DATA(timeofday) = CONV string( + LET time = cl_abap_context_info=>get_system_time( ) IN + COND string( LET good = `Good` ending = `ing` IN + WHEN time BETWEEN '050001' AND '120000' THEN good && ` morn` && ending "Good morning + WHEN time BETWEEN '120001' AND '180000' THEN good && ` afternoon` + WHEN time BETWEEN '180001' AND '220000' THEN good && ` even` && ending + ELSE good && ` night` ) ). + + out->write( data = timeofday name = `timeofday` ). + out->write( |\n| ). + + "Getting a particular column name of an existing internal table using RTTI + "An internal table (it contains information on the table's structured type; the + "component names, among others) is assigned to a data object that is declared + "inline. This is an example of making code more concise with constructor expressions + "and inline declarations. Assume you use extra declarations for the data objects, or + "use the older ?= operator for the casts. Many more lines of code are required. + DATA(components) = CAST cl_abap_structdescr( CAST cl_abap_tabledescr( + cl_abap_typedescr=>describe_by_data( it ) )->get_table_line_type( ) )->components. + DATA(comp2_a) = components[ 2 ]-name. + + out->write( data = comp2_a name = `comp2_a` ). + out->write( |\n| ). + + "Achieving the result from above even in one statement using LET + DATA(comp2_b) = CONV abap_compname( + LET comps = CAST cl_abap_structdescr( CAST cl_abap_tabledescr( + cl_abap_typedescr=>describe_by_data( it ) )->get_table_line_type( ) )->components + IN comps[ 2 ]-name ). + + out->write( data = comp2_b name = `comp2_b` ). + out->write( |\n| ). + + "Constructing a structure using local variables + "The example uses the NEW operator to create an anonymous data object + DATA(new_struc) = NEW st_type( LET n = 2 ch = 'AP' IN + comp1 = 'AB' && ch comp2 = 2 * n comp3 = 3 * n ). + + out->write( data = new_struc->* name = `new_struc->*` ). + out->write( |\n| ). + + "Constructing an internal table using local variables + "The example uses the VALUE operator. + "Note the parentheses ( ... ) representing table lines. + DATA(itab_value) = VALUE string_table( LET lin = 1 IN + ( |Line { lin }| ) + ( |Line { lin + 1 }| ) + ( |Line { lin + 2 }| ) ). + + out->write( data = itab_value name = `itab_value` ). + out->write( |\n| ). + + "Using a local field symbol in LET expressions + "- The right-hand side value must be the result of a writable expression, i.e. + " an operand that can be written to + "- This value is then assigned to the local field symbol (as if ASSIGN is used) + "- In the examples above, a specification such as ... LET
= 1 IN ... is not + " possible as they are not writable expressions. + "- Writable expressions: + " - Constructor expressions NEW class( ... )->attr and CAST type( ... )->dobj + " - Table expressions itab[ ... ] and their chainings, e.g. itab[ 1 ]-comp + "In the following example, an internal table is looped over. A string is created + "from the table line content. In the constructor expression, a LET expression is + "specified that uses a field symbol. It is assigned the line of the internal table. + "The sy-index value represents the table index value. + DATA str_tab TYPE string_table. + DO lines( it ) TIMES. + DATA(concatenated_tab) = CONV string( + LET
  • = it[ sy-index ] + comma = `, ` + IN |{
  • -comp1 }{ comma }{
  • -comp2 }{ comma }{
  • -comp3 }| ). + str_tab = VALUE #( BASE str_tab ( concatenated_tab ) ). + ENDDO. + + out->write( data = str_tab name = `str_tab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `41) More LET Examples` ) ). + + "The examples demonstrate LET expressions in different contexts. + + "1) LET within a constructor expression with VALUE: The temporary + "variable is assigned a value of type string and all lines of the + "resulting table (a table of type string) receive the content of this + "variable in the specified position. + + DATA(stringtable) = VALUE string_table( LET be = `be` IN + ( |To { be } is to do| ) + ( |To { be } or not to { be }| ) + ( |To do is to { be }| ) + ( |Do { be } do { be } do| ) ). + + out->write( data = stringtable name = `stringtable` ). + out->write( |\n| ). + + "2) LET within a constructor expression with COND: 12 o'clock is + "specified as value for the LET expression. Based on this value, checks + "are carried out and an appropriate result is returned. + + DATA(system_time) = cl_abap_context_info=>get_system_time( ). + + DATA(systime) = + COND #( LET tm = '120000' IN + WHEN system_time < tm THEN + |{ system_time TIME = ISO } AM| + WHEN system_time > tm AND system_time < '240000' THEN + |{ CONV t( system_time - 12 * 3600 ) TIME = ISO } PM| + WHEN system_time = tm THEN |High Noon| + ELSE |?| ). + + out->write( data = systime name = `systime` ). + out->write( |\n| ). + + "3) An internal table that includes three components is created and + "filled. A loop across this table is carried out. The purpose of the + "constructor expression is to construct a string by concatenating the + "values in the table columns. Here, the LET expression includes a field + "symbol representing the table line. The field symbol receives the + "concatenation result which is then stored in the variable that is + "declared inline. The content is then added to an internal table of type + "string which is then output to show the result of the loop. + + TYPES: + BEGIN OF date, + year TYPE c LENGTH 4, + month TYPE c LENGTH 2, + day TYPE c LENGTH 2, + END OF date, + dates TYPE TABLE OF date WITH EMPTY KEY. + + DATA stringtab TYPE TABLE OF string WITH EMPTY KEY. + + DATA(dates) = VALUE dates( + ( year = '2020' month = '07' day = '16' ) + ( year = '2021' month = '08' day = '31' ) + ( year = '2022' month = '09' day = '07' ) ). + + DO lines( dates ) TIMES. + DATA(isodate) = CONV string( + LET = dates[ sy-index ] + separator = '-' + IN -year && separator && -month && + separator && -day ). + + "Adding line to table + stringtab = VALUE #( BASE stringtab ( isodate ) ). + ENDDO. + + out->write( data = stringtab name = `stringtab` ). + ENDMETHOD. +ENDCLASS. \ No newline at end of file diff --git a/src/zcl_demo_abap_constructor_expr.clas.locals_imp.abap b/src/zcl_demo_abap_constructor_expr.clas.locals_imp.abap new file mode 100644 index 0000000..280d1a9 --- /dev/null +++ b/src/zcl_demo_abap_constructor_expr.clas.locals_imp.abap @@ -0,0 +1,27 @@ +CLASS local_class DEFINITION. + PUBLIC SECTION. + METHODS: constructor IMPORTING txt TYPE string, + double IMPORTING int TYPE + REF TO i RETURNING VALUE(res) TYPE i. + + DATA: timestamp TYPE string, + text TYPE string. + + CLASS-DATA: no_of_instances TYPE i READ-ONLY. +ENDCLASS. + +CLASS local_class IMPLEMENTATION. + METHOD constructor. + "Number of instances of the class are counted. + no_of_instances = no_of_instances + 1. + "Set a time stamp. + DATA: ts TYPE timestampl. + GET TIME STAMP FIELD ts. + timestamp = |{ ts TIMESTAMP = SPACE }|. + + text = |{ txt }, { sy-uname }.|. + ENDMETHOD. + METHOD double. + res = int->* * 2. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_constructor_expr.clas.xml b/src/zcl_demo_abap_constructor_expr.clas.xml new file mode 100644 index 0000000..45764df --- /dev/null +++ b/src/zcl_demo_abap_constructor_expr.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_CONSTRUCTOR_EXPR + E + ABAP cheat sheet: Constructor expressions + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_date_time.clas.abap b/src/zcl_demo_abap_date_time.clas.abap new file mode 100644 index 0000000..02f94d1 --- /dev/null +++ b/src/zcl_demo_abap_date_time.clas.abap @@ -0,0 +1,1631 @@ +*********************************************************************** +* +* ABAP cheat sheet: Date, Time, and Time Stamp +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntax options for working with +* date, time, and time stamp. +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP development tools for Eclipse (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, refer to the +* notes included in the class as comments or refer to the respective +* topic in the ABAP Keyword Documentation. +* - Due to the amount of console output, the examples contain numbers +* (e.g. 1) ..., 2) ..., 3) ...) for the individual example sections. +* Also, the variable name is displayed in most cases. So to find +* the relevant output in the console easier and faster, just search +* for the number/variable name in the console (CTRL+F in the console) +* or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

    ABAP cheat sheet: Date, Time, and Time Stamp

    +"! Example to demonstrate handling and procesing dates, times, and time stamps in ABAP.
    Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_date_time DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. + + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + + +CLASS zcl_demo_abap_date_time IMPLEMENTATION. + + METHOD if_oo_adt_classrun~main. + + out->write( |ABAP Cheat Sheet Example: Date, Time, and Time Stamp\n\n| ). + +********************************************************************** + + out->write( |1) Retrieving the Time Zone\n\n| ). + + "Retrieving the time zone of a given user + TRY. + DATA(tz_user) = cl_abap_context_info=>get_user_time_zone( ). + CATCH cx_abap_context_info_error. + ENDTRY. + + "Using XCO + "The reference created without '->value' is used in some XCO method calls, + "as shown in some of the following code snippets. + "User's time zone + DATA(tz_user_xco) = xco_cp_time=>time_zone->user->value. + "UTC + DATA(tz_utc_xco) = xco_cp_time=>time_zone->utc->value. + + out->write( data = tz_user name = `tz_user` ). + out->write( |\n| ). + out->write( data = tz_user_xco name = `tz_user_xco` ). + out->write( |\n| ). + out->write( data = tz_utc_xco name = `tz_utc_xco` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `2) Retrieving and Creating Dates` ) ). + + "--------------------- Retrieving the current date -------------------- + "Retrieving the current date with respect to UTC. + "The result's base type is the DDIC type dats that is mapped to the + "ABAP type d. + DATA(utc_date) = cl_abap_context_info=>get_system_date( ). + + "Using XCO + "Notes: + "- The result of the following chained statement is of type string. + "- With the 'as' method, a given format (available via xco_cp_time=>format) + " is applied to the time. + "- The 'date' method has a parameter for specifying the time zone. + " By default, the user's time zone is used. For the specification, you + " can use xco_cp_time=>time_zone. + "- The examples explore multiple formatting options that are specified in the + " 'date' and 'as' methods. + "Various formatting options + DATA(date_xco_iso_basic) = xco_cp=>sy->date( )->as( xco_cp_time=>format->iso_8601_basic )->value. + DATA(date_xco_iso_ext) = xco_cp=>sy->date( )->as( xco_cp_time=>format->iso_8601_extended )->value. + DATA(date_xco_abap) = xco_cp=>sy->date( )->as( xco_cp_time=>format->abap )->value. + "Specifying the user time zone explicitly (it is the default value) + DATA(date_xco_expl_user_tz) = xco_cp=>sy->date( xco_cp_time=>time_zone->user + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + "Specifying UTC explicitly + DATA(date_xco_expl_utc) = xco_cp=>sy->date( xco_cp_time=>time_zone->utc + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + + "--------------------- Retrieving current date values (XCO) -------------------- + DATA(this_day) = xco_cp=>sy->date( )->day. + DATA(this_month) = xco_cp=>sy->date( )->month. + DATA(this_year) = xco_cp=>sy->date( )->year. + + "--------------------- Creating dates -------------------- + DATA date_cr1 TYPE d. + date_cr1 = '20240101'. + DATA date_cr2 TYPE d VALUE '20240202'. + DATA(date_cr3) = CONV d( '20240303' ). + + "Using XCO + "The result contains a reference. It can be used for further processing + "with the XCO library. + DATA(date_w_xco) = xco_cp_time=>date( iv_year = 2024 + iv_month = 3 + iv_day = 3 ). + + "Examples + DATA(date_cr4) = date_w_xco->as( xco_cp_time=>format->iso_8601_extended )->value. + DATA(day_from_date) = date_w_xco->day. + DATA(month_from_date) = date_w_xco->month. + + out->write( data = utc_date name = `utc_date` ). + out->write( |\n| ). + out->write( data = date_xco_iso_basic name = `date_xco_iso_basic` ). + out->write( |\n| ). + out->write( data = date_xco_iso_ext name = `date_xco_iso_ext` ). + out->write( |\n| ). + out->write( data = date_xco_abap name = `date_xco_abap` ). + out->write( |\n| ). + out->write( data = date_xco_expl_user_tz name = `date_xco_expl_user_tz` ). + out->write( |\n| ). + out->write( data = date_xco_expl_utc name = `date_xco_expl_utc` ). + out->write( |\n| ). + out->write( data = this_day name = `this_day` ). + out->write( |\n| ). + out->write( data = this_month name = `this_month` ). + out->write( |\n| ). + out->write( data = this_year name = `this_year` ). + out->write( |\n| ). + out->write( data = date_cr4 name = `date_cr4` ). + out->write( |\n| ). + out->write( data = day_from_date name = `day_from_date` ). + out->write( |\n| ). + out->write( data = month_from_date name = `month_from_date` ). + out->write( |\n| ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `3) Validity of Date Fields` ) ). + "Before accessing date (or time) fields, ensure that the content of these + "fields is valid to avoid unexpected results, such as incorrect calculations. + "The ABAP runtime framework checks the validity of these fields in various + "contexts, including lossless assignments and assignments to numeric fields. + + "Lossless assignements using the EXACT operator + "Due to the invalid dates, an exception is raised. + TRY. + DATA(inv_date1) = EXACT d( '20240231' ). + CATCH cx_sy_conversion_no_date INTO DATA(error1). + out->write( data = error1->get_text( ) name = `error1->get_text( )` ). + out->write( |\n| ). + ENDTRY. + + TRY. + DATA(inv_date2) = EXACT d( '2024XY31' ). + CATCH cx_sy_conversion_no_date INTO DATA(error2). + out->write( data = error2->get_text( ) name = `error2->get_text( )` ). + out->write( |\n| ). + ENDTRY. + + "Assignment of an invalid date of type d to type i; the initial value + "is produced + "In newer ABAP releases, the following statement shows a + "syntax warning that the date (intentionally specified as + "invalid date here) does not match the type. Therefore, + "the example is provided differently to circumvent the + "syntax warning. + + "DATA(inv_date3) = CONV i( CONV d( '20240231' ) ). + + TYPES c8 TYPE c LENGTH 8. + DATA false_date TYPE c8 VALUE '20240231'. + DATA(inv_date3) = CONV i( CONV d( false_date ) ). + + IF inv_date3 = 0. + out->write( `inv_date3 = 0` ). + ENDIF. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `4) Character-Like Access to Date Fields` ) ). + "Since the content is character-like, you can use string processing functionalities + "to access date values. This also applies to time fields of type t. + "The following examples show a selection. + + "Extracting date values using the string function substring. + DATA some_date TYPE d VALUE '20240102'. + DATA(year_sub) = substring( val = some_date off = 0 len = 4 ). + DATA(month_sub) = substring( val = some_date off = 4 len = 2 ). + DATA(day_sub) = substring( val = some_date off = 6 len = 2 ). + + "Replacing using the string function replace + DATA(year_repl) = replace( val = some_date off = 0 len = 4 with = `2025` ). + + "Offset and length specifications + DATA(off_len_spec_day) = some_date+6(2). + some_date+4(2) = '10'. + + out->write( data = year_sub name = `year_sub` ). + out->write( |\n| ). + out->write( data = month_sub name = `month_sub` ). + out->write( |\n| ). + out->write( data = day_sub name = `day_sub` ). + out->write( |\n| ). + out->write( data = year_repl name = `year_repl` ). + out->write( |\n| ). + out->write( data = off_len_spec_day name = `off_len_spec_day` ). + out->write( |\n| ). + out->write( data = some_date name = `some_date` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `5) Numeric Access and Calculations` ) ). + "When converting date fields to numeric values, the type d produces an integer + "representing the number of days since 01.01.000 + "This is especially important when using date fields in calculations and converting + "the values to numeric types. + "The XCO library offers methods for performing calculations. In the code snippet, + "examples use the value attribute. The return value of the method calls is of type + "string. + + DATA date_calc_1 TYPE d VALUE '20240101'. + DATA date_calc_2 TYPE d VALUE '20231227'. + DATA date_calc_3 TYPE d VALUE '20231230'. + DATA date_calc_4 TYPE d VALUE '20240220'. + + DATA(days_diff_1) = date_calc_1 - date_calc_2. + DATA(days_diff_2) = date_calc_1 - date_calc_3. + DATA(days_since_01_01_0001) = CONV i( date_calc_1 ). + "Getting the weekday (1 = Monday, 2 = Tuesday, ...) + DATA(weekday1) = ( 5 + date_calc_1 MOD 7 ) MOD 7 + 1. + DATA(weekday2) = ( 5 + date_calc_3 MOD 7 ) MOD 7 + 1. + DATA(date_w_first_day_of_month) = CONV d( replace( val = `202403020` off = 6 len = 2 with = `01` ) ). + DATA(date_w_last_day_of_prev_month) = CONV d( date_w_first_day_of_month - 1 ). + + "------------ Performing date additions and subtractions using the XCO library ------------ + + "Adding days to the current date using the 'add' method + DATA(xco_date_add_5days) = xco_cp=>sy->date( )->add( iv_day = 5 + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + + "The 'add' method has various parameters, adding 1 day/month/year + DATA(xco_date_add_1_mult) = xco_cp=>sy->date( )->add( iv_day = 1 iv_month = 1 iv_year = 1 + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + + "Addition with a created date + DATA(xco_date_add_1day_custom) = xco_cp_time=>date( iv_year = 2024 iv_month = 02 iv_day = 28 + )->add( iv_day = 1 + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + + "Subtracting using the 'subtract' method + DATA(xco_date_subtr_1_mult) = xco_cp=>sy->date( )->subtract( iv_day = 1 iv_month = 1 iv_year = 1 + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + + "Optional parameter io_calculation + "io_calculation parameter: xco_cp_time=>date_calculation->preserving, + "i.e. the date is calculated mathmatically and preserved. It is the + "default. + "In case of an invalid resulting date, an exception is raised. + TRY. + DATA(inv_date_a) = xco_cp_time=>date( iv_year = '2024' + iv_month = '08' + iv_day = '31' + )->add( iv_month = 1 + io_calculation = xco_cp_time=>date_calculation->preserving + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + CATCH cx_root INTO DATA(err_pre). + ENDTRY. + + "io_calculation parameter: xco_cp_time=>date_calculation->ultimo, + "Here, the actual last day of the month is considered. If the calculated + "date is invalid, the ultimo is automatically used. In the example, one month is added to + "the date. However, September does not have 31 days, so the result is adjusted to the actual + "last day of September, which is 30 in this case. + TRY. + DATA(inv_date_b) = xco_cp_time=>date( iv_year = '2024' + iv_month = '08' + iv_day = '31' + )->add( iv_month = 1 + io_calculation = xco_cp_time=>date_calculation->ultimo + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + CATCH cx_root INTO DATA(err_ult). + ENDTRY. + + out->write( data = days_diff_1 name = `days_diff_1` ). + out->write( |\n| ). + out->write( data = days_diff_2 name = `days_diff_2` ). + out->write( |\n| ). + out->write( data = days_since_01_01_0001 name = `days_since_01_01_0001` ). + out->write( |\n| ). + out->write( data = weekday1 name = `weekday1` ). + out->write( |\n| ). + out->write( data = weekday2 name = `weekday2` ). + out->write( |\n| ). + out->write( data = date_w_first_day_of_month name = `date_w_first_day_of_month` ). + out->write( |\n| ). + out->write( data = date_w_last_day_of_prev_month name = `date_w_last_day_of_prev_month` ). + out->write( |\n| ). + out->write( data = xco_date_add_5days name = `xco_date_add_5days` ). + out->write( |\n| ). + out->write( data = xco_date_add_1_mult name = `xco_date_add_1_mult` ). + out->write( |\n| ). + out->write( data = xco_date_add_1day_custom name = `xco_date_add_1day_custom` ). + out->write( |\n| ). + out->write( data = xco_date_subtr_1_mult name = `xco_date_subtr_1_mult` ). + out->write( |\n| ). + IF err_pre IS INITIAL. + out->write( data = inv_date_a name = `inv_date_a` ). + ELSE. + out->write( data = err_pre->get_text( ) name = `err_pre->get_text( )` ). + ENDIF. + out->write( |\n| ). + IF err_ult IS INITIAL. + out->write( data = inv_date_b name = `inv_date_b` ). + ELSE. + out->write( data = err_ult->get_text( ) name = `err_ult->get_text( )` ). + ENDIF. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `6) CL_ABAP_DATFM: Date Conversions` ) ). + "Using the CL_ABAP_DATFM class, you can perform conversions with external + "and internal representations of a time according to the date format, e.g. + "the conversion of a date in a data object of type string to type d and vice + "versa using a specific date format. Multiple methods are available, of which + "two are covered in the example. For more information, refer to the class + "documentation. + "For the values of the date format for the conversion, see the fixed values + "by exploring the F2 help for cl_abap_datfm=>ty_datfm. + + "Data type and obect declarations for the example. The example, explores + "multiple date formats. For this purpose, each loop pass in a DO loop + "uses a different date format. First, the internal time format is converted + "to external time formats. In another loop, the conversions are performed + "the other way round. The results are stored in an internal table. + DATA(date4conversion) = CONV d( '20240202' ). + DATA conv_date_str TYPE string. + DATA conv_date_d TYPE d. + DATA date_format TYPE cl_abap_datfm=>ty_datfm. + TYPES: BEGIN OF date_ty_s, + ext_date TYPE string, + format TYPE c LENGTH 1, + int_date TYPE d, + END OF date_ty_s. + DATA date_tab TYPE TABLE OF date_ty_s WITH EMPTY KEY. + + "Conversion of d (internal) to string (external time format) + DO 7 TIMES. + TRY. + cl_abap_datfm=>conv_date_int_to_ext( + EXPORTING im_datint = date4conversion + im_datfmdes = SWITCH #( sy-index + WHEN 1 THEN cl_abap_datfm=>get_datfm( ) + WHEN 2 THEN '1' + WHEN 3 THEN '2' + WHEN 4 THEN '3' + WHEN 5 THEN '6' + WHEN 6 THEN 'A' + WHEN 7 THEN 'C' ) + IMPORTING ex_datext = conv_date_str + ex_datfmused = date_format ). + date_tab = VALUE #( BASE date_tab ( ext_date = conv_date_str format = date_format ) ). + CATCH cx_abap_datfm_format_unknown. + ENDTRY. + ENDDO. + + "Conversion of string (external) to d (internal time format) + LOOP AT date_tab REFERENCE INTO DATA(ref_date). + TRY. + cl_abap_datfm=>conv_date_ext_to_int( + EXPORTING im_datext = ref_date->ext_date + im_datfmdes = ref_date->format + IMPORTING ex_datint = conv_date_d + ex_datfmused = date_format ). + ref_date->int_date = conv_date_d. + CATCH cx_abap_datfm_no_date cx_abap_datfm_invalid_date + cx_abap_datfm_format_unknown cx_abap_datfm_ambiguous. + ENDTRY. + ENDLOOP. + + "Note: When outputting the data object with the classrun, + "20240202, which is of type d, is displayed as 2014-02-02 in + "the console for better readability. + out->write( data = date_tab name = `date_tab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `7) Examples for Time Processing` ) ). + "The code snippet below provides examples of time processing, such + "as retrieving the current time, accessing time values, creating time + "values, and performing time calculations. You can also utilize the + "XCO library in different scenarios. + + "--------------------- Retrieving the current time -------------------- + + "Retrieving the current time in UTC. + DATA(utc_time) = cl_abap_context_info=>get_system_time( ). + + "Using XCO + "Note the optional time zone specification. + DATA(time_w_xco) = xco_cp=>sy->time( xco_cp_time=>time_zone->user + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + + "--------------------- Accessing time values -------------------- + + "Note: As mentioned in a previous section on dates, the access to time fields + "works similar as date fields. As an example, seconds, minutes, and hours are + "extracted from a time field. + DATA some_time TYPE t VALUE '123456'. + + DATA(hour_extr) = substring( val = some_time off = 0 len = 2 ). + DATA(minute_extr) = substring( val = some_time off = 2 len = 2 ). + DATA(second_extr) = substring( val = some_time off = 4 len = 2 ). + + "Retrieving the current seconds, minutes, hours using XCO + DATA(sec_w_xco) = xco_cp=>sy->time( xco_cp_time=>time_zone->user )->second. + DATA(min_w_xco) = xco_cp=>sy->time( xco_cp_time=>time_zone->user )->minute. + DATA(hour_w_xco) = xco_cp=>sy->time( xco_cp_time=>time_zone->user )->hour. + + "--------------------- Creating times -------------------- + + DATA time_cr1 TYPE t. + time_cr1 = '095812'. + DATA time_cr2 TYPE t VALUE '112400'. + DATA(time_cr3) = CONV t( '120000' ). + + "Using XCO + "The result contains a reference. It can be used for further processing + "with the XCO library. + DATA(xco_time) = xco_cp_time=>time( iv_hour = '08' + iv_minute = '34' + iv_second = '05' ). + + "Examples + DATA(time_cr4) = xco_time->as( xco_cp_time=>format->iso_8601_extended )->value. + DATA(hours_from_xco_time) = xco_time->hour. + DATA(minutes_from_xco_time) = xco_time->minute. + DATA(seconds_from_xco_time) = xco_time->second. + + "------------ Performing time calculations ------------ + + "Retrieving seconds, minutes, and hours from a time value in a data object + "of type t + DATA time4calc TYPE t VALUE '115708'. + DATA(seconds_total) = CONV i( time4calc ). + DATA(calc_hours) = seconds_total DIV 3600. + DATA(min_calc) = ( seconds_total - calc_hours * 3600 ) DIV 60. + DATA(sec_calc) = ( seconds_total - calc_hours * 3600 ) - min_calc * 60. + + "Using the XCO library + "See the snippet above in the date section as well as the class documentation. + "Adding + DATA(time_xco_add) = xco_cp=>sy->time( xco_cp_time=>time_zone->user + )->add( iv_hour = 1 iv_minute = 1 iv_second = 1 + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + "Subtracting + DATA(time_xco_subtr) = xco_cp=>sy->time( xco_cp_time=>time_zone->user + )->subtract( iv_hour = 1 iv_minute = 1 iv_second = 1 + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + + "------------ Conversions with the CL_ABAP_TIMEFM class ------------ + + "Using the CL_ABAP_TIMEFM class, you can perform conversions with external + "and internal representations of a time, e.g. conversion of a time in a data + "object of type string to type t and vice versa. Multiple methods are available, + "of which two are covered in the example. For more information, refer to the + "class documentation. + DATA(time4conversion) = CONV t( '123456' ). + DATA conv_time_str TYPE string. + DATA conv_time_t TYPE t. + + "Conversion of t (internal) to string (external time format) + TRY. + cl_abap_timefm=>conv_time_int_to_ext( + EXPORTING time_int = time4conversion + without_seconds = abap_false + format_according_to = cl_abap_timefm=>iso "hh:mm:ss + IMPORTING time_ext = conv_time_str ). + CATCH cx_parameter_invalid_range. + ENDTRY. + + "Conversion of string (external) to t (internal time format) + TRY. + cl_abap_timefm=>conv_time_ext_to_int( + EXPORTING time_ext = conv_time_str + IMPORTING time_int = conv_time_t ). + CATCH cx_abap_timefm_invalid. + ENDTRY. + + out->write( data = utc_time name = `utc_time` ). + out->write( |\n| ). + out->write( data = time_w_xco name = `time_w_xco` ). + out->write( |\n| ). + out->write( data = hour_extr name = `hour_extr` ). + out->write( |\n| ). + out->write( data = minute_extr name = `minute_extr` ). + out->write( |\n| ). + out->write( data = second_extr name = `second_extr` ). + out->write( |\n| ). + out->write( data = sec_w_xco name = `sec_w_xco` ). + out->write( |\n| ). + out->write( data = min_w_xco name = `min_w_xco` ). + out->write( |\n| ). + out->write( data = hour_w_xco name = `hour_w_xco` ). + out->write( |\n| ). + out->write( data = time_cr4 name = `time_cr4` ). + out->write( |\n| ). + out->write( data = hours_from_xco_time name = `hours_from_xco_time` ). + out->write( |\n| ). + out->write( data = minutes_from_xco_time name = `minutes_from_xco_time` ). + out->write( |\n| ). + out->write( data = seconds_from_xco_time name = `seconds_from_xco_time` ). + out->write( |\n| ). + out->write( data = seconds_total name = `seconds_total` ). + out->write( |\n| ). + out->write( data = calc_hours name = `calc_hours` ). + out->write( |\n| ). + out->write( data = min_calc name = `min_calc` ). + out->write( |\n| ). + out->write( data = sec_calc name = `sec_calc` ). + out->write( |\n| ). + out->write( data = time_xco_add name = `time_xco_add` ). + out->write( |\n| ). + out->write( data = time_xco_subtr name = `time_xco_subtr` ). + out->write( |\n| ). + out->write( data = conv_time_str name = `conv_time_str` ). + out->write( |\n| ). + "Note: When outputting the data object of type t with the classrun, + "it is displayed in the format hh:mm:ss in the console for better + "readability. + out->write( data = conv_time_t name = `conv_time_t` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `8) Time Stamps of Type utclong` ) ). + + "Retrieving an UTC time stamp using the built-in function utclong_current + "The return value has the type utclong. + DATA(ts1) = utclong_current( ). + + "Using XCO + "In the case of XCO, the return value is of type string. + "Retrieving a time stamp in the user's time zone (which is the default) + DATA(ts2) = xco_cp=>sy->moment( xco_cp_time=>time_zone->user + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + "Current time stamp in UTC + DATA(ts3) = xco_cp=>sy->moment( xco_cp_time=>time_zone->utc + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + "Different formatting options + DATA(ts_xco) = xco_cp=>sy->moment( xco_cp_time=>time_zone->utc ). + DATA(ts4) = ts_xco->as( xco_cp_time=>format->abap )->value. + DATA(ts5) = ts_xco->as( xco_cp_time=>format->iso_8601_basic )->value. + DATA(ts6) = ts_xco->as( xco_cp_time=>format->iso_8601_extended )->value. + + out->write( data = ts1 name = `ts1` ). + out->write( |\n| ). + out->write( data = ts2 name = `ts2` ). + out->write( |\n| ). + out->write( data = ts3 name = `ts3` ). + out->write( |\n| ). + out->write( data = ts4 name = `ts4` ). + out->write( |\n| ). + out->write( data = ts5 name = `ts5` ). + out->write( |\n| ). + out->write( data = ts6 name = `ts6` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `9) Creating/Modifying a Time Stamp` ) ). + + "--------------------- Creating time stamps -------------------- + + DATA ts7 TYPE utclong. + ts7 = utclong_current( ). + DATA ts8 TYPE utclong VALUE '2024-11-05 15:30:00'. + DATA(ts9) = CONV utclong( '2024-11-03 05:30:00' ). + + "Using XCO + "As above, the return value is of type string. Various methods, formatting options, + "etc. are available. + DATA(ts10) = xco_cp_time=>moment( iv_year = '2024' + iv_month = '01' + iv_day = '01' + iv_hour = '12' + iv_minute = '34' + iv_second = '55' + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + + "--------------------- Modifying time stamps (XCO) -------------------- + + "As covered for date and time types, you can modify time stamps using string + "processing functionalities. They are not covered here. XCO provides, for + "example, the 'overwrite' method. Optional parameters are available. + DATA(ts11) = xco_cp_time=>moment( iv_year = '2024' + iv_month = '03' + iv_day = '05' + iv_hour = '02' + iv_minute = '54' + iv_second = '12' ). + + DATA(ts12) = ts11->overwrite( iv_year = '2025' + iv_month = '07' + iv_day = '15' + iv_hour = '01' + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + + out->write( data = ts7 name = `ts7` ). + out->write( |\n| ). + out->write( data = ts8 name = `ts8` ). + out->write( |\n| ). + out->write( data = ts9 name = `ts9` ). + out->write( |\n| ). + out->write( data = ts10 name = `ts10` ). + out->write( |\n| ). + out->write( data = ts12 name = `ts12` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `10) Time Stamp Calculations with the Built-In Function utclong_add` ) ). + + "With the built-in function utclong_add, at least one parameter must be specified + "besides 'val'. + "Note that there are no parameters for years and months. + DATA(utc4calc) = CONV utclong( '2024-01-01 15:55:14.1173220' ). + + "Adding one hour + DATA(ts13) = utclong_add( val = utc4calc + hours = 1 ). + + "Subtracting one hour by passing a negative integer value (no separate substract + "function available) + DATA(ts14) = utclong_add( val = utc4calc + hours = -1 ). + + "Using all parameters + DATA(ts15) = utclong_add( val = utc4calc + days = 1 + hours = 2 + minutes = CONV int8( '13' ) + seconds = CONV decfloat34( '53.12' ) ). + + out->write( data = ts13 name = `ts13` ). + out->write( |\n| ). + out->write( data = ts14 name = `ts14` ). + out->write( |\n| ). + out->write( data = ts15 name = `ts15` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `11) Time Stamp Calculations with XCO` ) ). + + "Creating two time stamps with XCO + DATA(ts_ref1) = xco_cp_time=>moment( iv_year = '2024' + iv_month = '01' + iv_day = '01' + iv_hour = '11' + iv_minute = '37' + iv_second = '54' ). + + DATA(ts_ref2) = xco_cp_time=>moment( iv_year = '2024' + iv_month = '02' + iv_day = '02' + iv_hour = '10' + iv_minute = '28' + iv_second = '59' ). + + "Additions; various optional parameters are available + DATA(ts18) = ts_ref1->add( iv_day = 1 iv_month = 2 iv_year = 3 + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + + DATA(ts19) = ts_ref2->add( iv_hour = 1 iv_minute = 2 iv_second = 4 + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + + "Adding an hour to the current time stamp + DATA(ts20) = xco_cp=>sy->moment( )->add( iv_hour = 1 + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + + "Substractions + DATA(ts21) = ts_ref1->subtract( iv_day = 1 iv_month = 2 iv_year = 3 + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + + DATA(ts22) = ts_ref2->subtract( iv_hour = 1 iv_minute = 2 iv_second = 4 + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + + "Substracting 1 year/month/day/hour/minute/second from the curernt time stamp + DATA(ts23) = xco_cp=>sy->moment( )->subtract( iv_day = 1 iv_month = 1 iv_year = 1 + iv_hour = 1 iv_minute = 1 iv_second = 1 + )->as( xco_cp_time=>format->iso_8601_extended + )->value. + + "---- Excursion: Defining a time interval and checking if ---- + "---- a time stamp is within that time interval -------------- + + "In the following example, a time interval is defined from the current time stamp + "retrieved with XCO to a specified one. Using the 'contains' method and providing + "another time stamp reference, it is checked whether that time stamp is contained + "in the time interval. The return value is of type abap_bool. Using the lower_bound + "and upper_bound attributes, the low and high values are retrieved. + DATA(ts_ref3) = xco_cp_time=>moment( iv_year = '2030' + iv_month = '01' + iv_day = '01' + iv_hour = '10' + iv_minute = '00' + iv_second = '00' ). + + DATA(ts_ref4) = xco_cp_time=>moment( iv_year = '2028' + iv_month = '01' + iv_day = '01' + iv_hour = '11' + iv_minute = '00' + iv_second = '00' ). + + DATA(ts_interval) = xco_cp=>sy->moment( )->interval_to( ts_ref3 ). + DATA(ts_in) = ts_interval->contains( ts_ref4 ). + DATA(ts_interval_low) = ts_interval->lower_bound->as( xco_cp_time=>format->iso_8601_extended )->value. + DATA(ts_interval_high) = ts_interval->upper_bound->as( xco_cp_time=>format->iso_8601_extended )->value. + + out->write( data = ts18 name = `ts18` ). + out->write( |\n| ). + out->write( data = ts19 name = `ts19` ). + out->write( |\n| ). + out->write( data = ts20 name = `ts20` ). + out->write( |\n| ). + out->write( data = ts21 name = `ts21` ). + out->write( |\n| ). + out->write( data = ts22 name = `ts22` ). + out->write( |\n| ). + out->write( data = ts23 name = `ts23` ). + out->write( |\n| ). + out->write( data = ts_in name = `ts_in` ). + out->write( |\n| ). + out->write( data = ts_interval_low name = `ts_interval_low` ). + out->write( |\n| ). + out->write( data = ts_interval_high name = `ts_interval_high` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `12) Calculating Time Stamp Differences Using the Built-In Function utclong_diff` ) ). + + DATA(ts16) = CONV utclong( '2024-01-01 05:30:00' ). + DATA(ts17) = CONV utclong( '2024-01-01 06:30:00' ). + "The return value has the type decfloat34. It contains the exact + "difference in seconds. + + DATA(ts_diff1) = utclong_diff( high = ts17 + low = ts16 ). + + DATA(ts_diff2) = utclong_diff( high = ts16 + low = ts17 ). + + out->write( data = ts_diff1 name = `ts_diff1` ). + out->write( |\n| ). + out->write( data = ts_diff2 name = `ts_diff2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `13) CONVERT UTCLONG: Time Stamp (utclong) -> Local Date/Time` ) ). + + DATA ts_utc TYPE utclong VALUE '2024-11-03 05:30:00'. + + CONVERT UTCLONG ts_utc + INTO DATE DATA(date_est) + TIME DATA(time_est) + TIME ZONE 'EST'. + + out->write( data = date_est name = `date_est` ). + out->write( |\n| ). + out->write( data = time_est name = `time_est` ). + out->write( |\n| ). + + "More optional additions: + "- FRACTIONAL SECONDS: Getting the fractions of seconds + "- DAYLIGHT SAVING TIME: Determining if the time stamp is + " in a daylight saving time (the example uses time stamp + " in August) + CONVERT UTCLONG CONV utclong( '2024-08-08 09:23:11.7681270' ) + INTO DATE date_est + TIME time_est + FRACTIONAL SECONDS DATA(sec_est) + DAYLIGHT SAVING TIME DATA(dsl_est) + TIME ZONE 'EST'. + + out->write( data = date_est name = `date_est` ). + out->write( |\n| ). + out->write( data = time_est name = `time_est` ). + out->write( |\n| ). + out->write( data = sec_est name = `sec_est` ). + out->write( |\n| ). + out->write( data = dsl_est name = `dsl_est` ). + out->write( |\n| ). + + "If the time zone specified is initial, there is no conversion. + CONVERT UTCLONG CONV utclong( '2024-08-08 09:23:11.7681270' ) + INTO DATE DATA(date_init) + TIME DATA(time_init) + TIME ZONE VALUE #( ). + + out->write( data = date_init name = `date_init` ). + out->write( |\n| ). + out->write( data = time_init name = `time_init` ). + out->write( |\n| ). + + "Specifying a non-existent time zone raises a catchable exception. + TRY. + CONVERT UTCLONG CONV utclong( '2024-08-08 09:23:11.7681270' ) + INTO DATE DATA(date_nope) + TIME DATA(time_nope) + TIME ZONE 'NOPE'. + CATCH cx_sy_conversion_no_date_time INTO DATA(err_conv). + ENDTRY. + + IF err_conv IS INITIAL. + out->write( data = date_nope name = `date_nope` ). + out->write( |\n| ). + out->write( data = time_nope name = `time_nope` ). + ELSE. + out->write( data = err_conv->get_text( ) name = `err_conv->get_text( )` ). + ENDIF. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `14) CONVERT INTO UTCLONG: Local Date/Time -> Time Stamp (utclong)` ) ). + + DATA date2utcl TYPE d VALUE '20240101'. + DATA time2utcl TYPE t VALUE '112458'. + DATA utcl TYPE utclong. + + CONVERT DATE date2utcl + TIME time2utcl + TIME ZONE 'EST' + INTO UTCLONG utcl. + + out->write( data = utcl name = `utcl` ). + out->write( |\n| ). + + "Using optional additions + "Check the details in the ABAP Keyword Documentation. + "Specifying 'X' for DAYLIGHT SAVING TIME in the following + "example raises a catchable exception. + CONVERT DATE date2utcl + TIME time2utcl + FRACTIONAL SECONDS CONV decfloat34( '0.768127' ) + DAYLIGHT SAVING TIME '' + TIME ZONE 'EST' + INTO UTCLONG DATA(utcl_inl1). + + out->write( data = utcl_inl1 name = `utcl_inl1` ). + out->write( |\n| ). + + + "There is no time shift in case of an initial time zone specification. + CONVERT DATE date2utcl + TIME time2utcl + TIME ZONE VALUE #( ) + INTO UTCLONG DATA(utcl_inl2). + + out->write( data = utcl_inl2 name = `utcl_inl2` ). + out->write( |\n| ). + + "Ensure that valid values are passed + "The following example explores ABAP statements with invalid values + "that are passed. The valid time stamp, the exceptions raised and + "the error messages are added to an internal table. + DATA error_checks TYPE string_table. + DATA date_test TYPE d. + DATA time_test TYPE t. + DATA frac_sec_test TYPE decfloat34. + DATA dls_test TYPE abap_bool. + DATA tz_test TYPE string. + DO 6 TIMES. + date_test = '20240101'. + time_test = '112458'. + frac_sec_test = '0.768127'. + dls_test = abap_false. + tz_test = `EST`. + + CASE sy-index. + WHEN 1. + "No data object change. The statement below should return a valid time stamp. + WHEN 2. + "Invalid date + "In newer ABAP releases, the following statement shows a + "syntax warning that the date (intentionally specified as + "invalid date here) does not match the type. Therefore, + "the example is provided differently to circumvent the + "syntax warning. + + "date_test = '20249999'. + + TYPES c_l8 TYPE c LENGTH 8. + DATA falsedate TYPE c_l8 VALUE '20240231'. + date_test = falsedate. + WHEN 3. + "Invalid time + "The following statement is commented out for the reasons + "mentioned above. + + "time_test = '992458'. + + TYPES c_l6 TYPE c LENGTH 6. + DATA falsetime TYPE c_l6 VALUE '992458'. + time_test = falsetime. + WHEN 4. + "Invalid fractions of seconds + frac_sec_test = '1'. + WHEN 5. + "Invalid specification for this particular example. + dls_test = 'X'. + WHEN 6. + "Invalid time zone + "The following statement is commented out for the reasons + "mentioned above. + + "dls_test = `NOPE`. + + TYPES c_l4 TYPE c LENGTH 4. + DATA falsetimezone TYPE c_l4 VALUE 'NOPE'. + dls_test = falsetimezone. + ENDCASE. + + TRY. + CONVERT DATE date_test + TIME time_test + FRACTIONAL SECONDS frac_sec_test + DAYLIGHT SAVING TIME dls_test + TIME ZONE tz_test + INTO UTCLONG DATA(utcl_inl3). + error_checks = VALUE #( BASE error_checks ( |({ sy-index }) Valid time stamp: { utcl_inl3 }| ) ). + CATCH cx_root INTO DATA(err). + error_checks = VALUE #( BASE error_checks + ( |({ sy-index }) Exception | && + |{ replace( val = cl_abap_typedescr=>describe_by_object_ref( err )->absolute_name + sub = `\CLASS=` + with = `` ) } was raised: | && + |{ err->get_text( ) }| ) ). + ENDTRY. + ENDDO. + + out->write( data = error_checks name = `error_checks` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `15) CL_ABAP_UTCLONG: Utilities for Time Stamps (utclong)` ) ). + + "Check the class documentation. More methods are available. + DATA(low_timestamp) = CONV utclong( '2024-01-01 05:30:00' ). + DATA(high_timestamp) = CONV utclong( '2024-01-03 10:35:12' ). + + "'diff' method: Calculating time differences + cl_abap_utclong=>diff( EXPORTING high = high_timestamp + low = low_timestamp + IMPORTING days = DATA(diff_days) + hours = DATA(diff_hours) + minutes = DATA(diff_minutes) + seconds = DATA(diff_seconds) ). + + "Converting a time stamp from a character-like format to utclong + "Creating a time stamp in a string + DATA(ts_string) = |{ utclong_current( ) TIMESTAMP = ENVIRONMENT TIMEZONE = 'UTC' }|. + TRY. + cl_abap_utclong=>read( EXPORTING string = ts_string + timezone = 'UTC' + IMPORTING value = DATA(utc_ts) ). + CATCH cx_abap_utclong_invalid. + ENDTRY. + + out->write( data = diff_days name = `diff_days` ). + out->write( |\n| ). + out->write( data = diff_hours name = `diff_hours` ). + out->write( |\n| ). + out->write( data = diff_minutes name = `diff_minutes` ). + out->write( |\n| ). + out->write( data = diff_seconds name = `diff_seconds` ). + out->write( |\n| ). + out->write( data = utc_ts name = `utc_ts` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `16) GET TIME STAMP: Retrieving the Current Time Stamp` ) ). + + "Short form + DATA ts_short TYPE timestamp. + GET TIME STAMP FIELD ts_short. + + "Long form + DATA ts_long TYPE timestampl. + GET TIME STAMP FIELD ts_long. + + "Note: When declaring the target data object inline, + "the short form (type timestamp) is automatically used. + GET TIME STAMP FIELD DATA(ts_inl). + + out->write( data = ts_short name = `ts_short` ). + out->write( |\n| ). + out->write( data = ts_long name = `ts_long` ). + out->write( |\n| ). + out->write( data = ts_inl name = `ts_inl` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `17) CONVERT TIME STAMP: Time Stamp in Packed Numbers -> Local Date/Time` ) ). + + GET TIME STAMP FIELD DATA(tsf). "Type timestamp + "Retrieving the time zone of a given user + TRY. + DATA(user_tz) = cl_abap_context_info=>get_user_time_zone( ). + CATCH cx_abap_context_info_error. + ENDTRY. + + CONVERT TIME STAMP tsf + TIME ZONE user_tz + INTO DATE DATA(dat) + TIME DATA(tim). + + out->write( data = dat name = `dat` ). + out->write( |\n| ). + out->write( data = tim name = `tim` ). + out->write( |\n| ). + + "Specifying a data object of type timestampl instead of timestamp. + "In this case, the fractions of seconds in the decimal places are ignored. + "A time zone is specified for which information is retrieved below. + CONVERT TIME STAMP CONV timestampl( '20240101081317.81011' ) + TIME ZONE 'EST' + INTO DATE dat + TIME tim. + + out->write( data = dat name = `dat` ). + out->write( |\n| ). + out->write( data = tim name = `tim` ). + out->write( |\n| ). + + "Excursion: Time zone information for EST + "Regarding the DAYLIGHT SAVING TIME additions used in some snippets with this + "particular time zone: Among others, the result shows that timezoneisactive + "is true. + SELECT SINGLE * + FROM i_timezone + WHERE TimeZoneID = 'EST' + INTO @DATA(tz_info). + + out->write( data = tz_info name = `tz_info` ). + out->write( |\n| ). + + "Addition DAYLIGHT SAVING TIME (using a time stamp in August) + CONVERT TIME STAMP CONV timestamp( '20240808112458' ) + TIME ZONE 'EST' + INTO DATE dat + TIME tim + DAYLIGHT SAVING TIME DATA(dst). + + out->write( data = dat name = `dat` ). + out->write( |\n| ). + out->write( data = tim name = `tim` ). + out->write( |\n| ). + out->write( data = dst name = `dst` ). + out->write( |\n| ). + + "sy-subrc is set. Find more details in the ABAP Keyword Documentation. + "Specified time zone is invalid -> sy-subrc = 8 + "Note that specifying all targets after INTO is not mandatory. + CONVERT TIME STAMP CONV timestamp( '20240808112458' ) + TIME ZONE 'NOPE' + INTO DATE dat. + IF sy-subrc = 8. + out->write( `sy-subrc = 8` ). + out->write( |\n| ). + ENDIF. + + "Specified time stamp is invalid -> sy-subrc = 12 + CONVERT TIME STAMP CONV timestamp( '20249999112458' ) + TIME ZONE 'NOPE' + INTO DATE dat. + IF sy-subrc = 12. + out->write( `sy-subrc = 12` ). + ENDIF. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `18) CONVERT INTO TIME STAMP: Local Date/Time -> Time Stamp in Packed Numbers` ) ). + + DATA date4conv TYPE d VALUE '20240101'. + DATA time4conv TYPE t VALUE '112458'. + DATA ts_conv TYPE timestamp. + + CONVERT DATE date4conv + TIME time4conv + INTO TIME STAMP ts_conv + TIME ZONE 'EST'. + + out->write( data = ts_conv name = `ts_conv` ). + out->write( |\n| ). + + "Using the long form with type timestampl as target type and + "setting the daylight saving time explicitly using the + "optional addition DAYLIGHT SAVING TIME. Find more + "details in the ABAP Keyword Documentation. + DATA tsl_conv TYPE timestampl. + CONVERT DATE CONV d( '20240101' ) + TIME CONV t( '112458' ) + INTO TIME STAMP tsl_conv + TIME ZONE 'EST'. + + out->write( data = tsl_conv name = `tsl_conv` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `19) CL_ABAP_TSTMP: Calculating and Converting Time Stamps in Packed Numbers` ) ). + + "The following code snippets show a selection of methods available. + + "Creating a time stamp of type timestamp + GET TIME STAMP FIELD DATA(ts4tstmp). + + "Calculations for time stamps in packed numbers + "Adding 1 hour + DATA(ts_add1h) = cl_abap_tstmp=>add( tstmp = ts4tstmp + secs = 3600 ). + + "Subtracting 2 hours + DATA(ts_subtr2h) = cl_abap_tstmp=>subtractsecs( tstmp = ts4tstmp + secs = 7200 ). + + "Type timestampl + DATA tsl TYPE timestampl. + GET TIME STAMP FIELD tsl. + + "Converting type timestampl to timestamp + DATA(long2short) = cl_abap_tstmp=>move_to_short( tsl ). + + "Converting types timestamp/timestampl to UTCLONG + DATA(ts2utcl) = cl_abap_tstmp=>tstmp2utclong( tsl ). + + "Converting type utclong to timestamp + DATA(utcl2ts) = cl_abap_tstmp=>utclong2tstmp_short( ts2utcl ). + + "Converting type utclong to timestampl + DATA(utcl2tsl) = cl_abap_tstmp=>utclong2tstmp( ts2utcl ). + + out->write( data = ts_add1h name = `ts_add1h` ). + out->write( |\n| ). + out->write( data = ts_subtr2h name = `ts_subtr2h` ). + out->write( |\n| ). + out->write( data = long2short name = `long2short` ). + out->write( |\n| ). + out->write( data = ts2utcl name = `ts2utcl` ). + out->write( |\n| ). + out->write( data = utcl2ts name = `utcl2ts` ). + out->write( |\n| ). + out->write( data = utcl2tsl name = `utcl2tsl` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `20) Excursion: Unix Time Stamps` ) ). + "Unix time stamp: Seconds passed since 1970-01-01 00:00:00 (UTC). + + "Getting the current UNIX time stamp using XCO + DATA(unix_tstmp) = xco_cp=>sy->unix_timestamp( )->value. + + "Getting the UNIX time stamp from a custom time stamp + DATA(unix_custom) = xco_cp_time=>moment( iv_year = '2024' + iv_month = '11' + iv_day = '03' + iv_hour = '07' + iv_minute = '12' + iv_second = '30' + )->get_unix_timestamp( )->value. + + "Using the unix time stamp with the utclong_add function to calculate + "the actual date + DATA(ts_from_unix1) = utclong_add( val = CONV utclong( '1970-01-01 00:00:00' ) + seconds = unix_tstmp ). + + DATA(ts_from_unix2) = utclong_add( val = CONV utclong( '1970-01-01 00:00:00' ) + seconds = unix_custom ). + + + "1704102123 (corresponds to Jan 01 2024 09:42:03, UTC) + DATA(ts_from_unix3) = utclong_add( val = CONV utclong( '1970-01-01 00:00:00' ) + seconds = 1704102123 ). + + out->write( data = unix_tstmp name = `unix_tstmp` ). + out->write( |\n| ). + out->write( data = unix_custom name = `unix_custom` ). + out->write( |\n| ). + out->write( data = ts_from_unix1 name = `ts_from_unix1` ). + out->write( |\n| ). + out->write( data = ts_from_unix2 name = `ts_from_unix2` ). + out->write( |\n| ). + out->write( data = ts_from_unix3 name = `ts_from_unix3` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `21) Date, Time, and Time Stamp in String Templates` ) ). + + "DATE: Defining the format of a date + "The output is just an example and depends on your settings. + DATA(d_str1) = |The date is { cl_abap_context_info=>get_system_date( ) DATE = USER }.|. + DATA(d_str2) = |{ cl_abap_context_info=>get_system_date( ) DATE = RAW }|. + DATA(d_str3) = |{ cl_abap_context_info=>get_system_date( ) DATE = ISO }|. + DATA(d_str4) = |{ cl_abap_context_info=>get_system_date( ) DATE = ENVIRONMENT }|. + + "TIME: Defining the format of a time + "The output is just an example and depends on your settings. + DATA(tm_str1) = |The time is { cl_abap_context_info=>get_system_time( ) TIME = ISO }.|. + DATA(tm_str2) = |{ cl_abap_context_info=>get_system_time( ) TIME = RAW }|. + DATA(tm_str3) = |{ cl_abap_context_info=>get_system_time( ) TIME = USER }|. + DATA(tm_str4) = |{ cl_abap_context_info=>get_system_time( ) TIME = ENVIRONMENT }|. + + "TIMESTAMP: Defining the format of a time stamp + "The output is just an example and depends on your settings. + DATA(ts_str1) = |{ utclong_current( ) TIMESTAMP = SPACE }|. + DATA(ts_str2) = |{ utclong_current( ) TIMESTAMP = ISO }|. + DATA(ts_str3) = |{ utclong_current( ) TIMESTAMP = USER }|. + DATA(ts_str4) = |{ utclong_current( ) TIMESTAMP = ENVIRONMENT }|. + DATA(ts_str5) = |{ utclong_current( ) }|. + + "TIMEZONE: Defining the format of a time stamp using the rules for time zones + DATA(tz_str1) = |{ utclong_current( ) TIMEZONE = 'UTC' }|. + DATA(tz_str2) = |{ utclong_current( ) TIMEZONE = 'CET' COUNTRY = 'DE ' }|. + DATA(tz_str3) = |{ utclong_current( ) TIMEZONE = 'EST' COUNTRY = 'US ' }|. + + out->write( data = d_str1 name = `d_str1` ). + out->write( |\n| ). + out->write( data = d_str2 name = `d_str2` ). + out->write( |\n| ). + out->write( data = d_str3 name = `d_str3` ). + out->write( |\n| ). + out->write( data = d_str4 name = `d_str4` ). + out->write( |\n| ). + out->write( data = tm_str1 name = `tm_str1` ). + out->write( |\n| ). + out->write( data = tm_str2 name = `tm_str2` ). + out->write( |\n| ). + out->write( data = tm_str3 name = `tm_str3` ). + out->write( |\n| ). + out->write( data = tm_str4 name = `tm_str4` ). + out->write( |\n| ). + out->write( data = ts_str1 name = `ts_str1` ). + out->write( |\n| ). + out->write( data = ts_str2 name = `ts_str2` ). + out->write( |\n| ). + out->write( data = ts_str3 name = `ts_str3` ). + out->write( |\n| ). + out->write( data = ts_str4 name = `ts_str4` ). + out->write( |\n| ). + out->write( data = ts_str5 name = `ts_str5` ). + out->write( |\n| ). + out->write( data = tz_str1 name = `tz_str1` ). + out->write( |\n| ). + out->write( data = tz_str2 name = `tz_str2` ). + out->write( |\n| ). + out->write( data = tz_str3 name = `tz_str3` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `22) Excursion: Typed Literals in ABAP SQL` ) ). + + SELECT SINGLE + FROM i_timezone + FIELDS * + WHERE TimeZoneID = char`EST` + INTO @DATA(wa_typed_literal). + + "Cast with a typed literal to cover a specification true to the + "actually expected type. In the case of the example, the data type + "char(6) is expected. + SELECT SINGLE + FROM i_timezone + FIELDS * + WHERE TimeZoneID = CAST( char`EST` AS CHAR( 6 ) ) + INTO @DATA(wa_typed_literal_cast). + + "Untyped literal + SELECT SINGLE + FROM i_timezone + FIELDS * + WHERE TimeZoneID = 'EST' + INTO @DATA(wa_untyped_literal). + + "Various typed literals + DATA(tmstamp) = CONV timestamp( '20240808112517' ). + SELECT SINGLE + FROM i_timezone + FIELDS + char`X` AS flag, + int8`32984723948723` AS int8, + raw`11` AS raw, + numc`1234` AS numc, + utclong`2024-01-01T10:01:02,2` AS utc, + tims`101507` AS tims, + curr`173.95` AS curr, + "Multiple cast expressions splitting a time stamp into date and time parts + CAST( CAST( div( @tmstamp, 1000000 ) AS CHAR ) AS DATS ) AS date, + CAST( substring( CAST( @tmstamp AS CHAR ), 9, 6 ) AS TIMS ) AS time, + 'ABAP' AS txt "Untyped literal + WHERE TimeZoneID = CAST( char`EST` AS CHAR( 6 ) ) + INTO @DATA(wa_some_typed_literals). + + out->write( data = wa_typed_literal name = `wa_typed_literal` ). + out->write( |\n| ). + out->write( data = wa_typed_literal_cast name = `wa_typed_literal_cast` ). + out->write( |\n| ). + out->write( data = wa_untyped_literal name = `wa_untyped_literal` ). + out->write( |\n| ). + out->write( data = wa_some_typed_literals name = `wa_some_typed_literals` ). + out->write( |\n| ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `23) Date and Time Functions in ABAP SQL` ) ). + + "The following demo ABAP SQL SELECT statement selects from a + "CDS view. The FIELDS list contains many functions related to + "date, time, and time stamp processing. For more details, + "see the ABAP Keyword Documentation. + "Some of the parameters are assigned typed literals. + DATA da TYPE d VALUE '20240122'. + DATA ti TYPE t VALUE '123456'. + DATA utc TYPE utclong VALUE '2024-02-15 05:30:00'. + DATA tmst TYPE timestamp VALUE '20240808112458'. + DATA tmstlong TYPE timestampl VALUE '20240101081317.81011'. + + SELECT SINGLE FROM i_timezone + FIELDS + "---------------------- Date ---------------------- + "Generic date functions (types d, utclong) + is_valid( @ti ) AS isvalid, "type t also possible + "In the following examples in this 'section', d and utclong are possible. + extract_year( @utc ) AS extr_year, + extract_month( @da ) AS extr_month, + extract_day( @utc ) AS extr_day, + dayname( @da ) AS day_name, + monthname( @utc ) AS month_name, + weekday( @utc ) AS week_day, + days_between( @utc,utclong`2024-02-25 08:14:26` ) AS days_bw, + add_days( @da,2 ) AS add_days, + add_months( @utc,3 ) AS add_months, + + "Functions for the type datn + datn_days_between( datn`20240111`,datn`20240212` ) AS days_datn_bw, + datn_add_days( datn`20240111`,4 ) AS days_datn_add, + datn_add_months( datn`20240111`,5 ) AS months_datn_add, + + "Functions for the type dats + dats_is_valid( dats`20240812` ) AS dats_valid, + dats_days_between( dats`20240812`,dats`20240817` ) AS days_dats_bw, + dats_add_days( dats`20240812`,4 ) AS days_dats_add, + dats_add_months( dats`20240812`,3 ) AS months_dats_add, + + "---------------------- Time ---------------------- + "Generic time functions (types t and utclong) + is_valid( @ti ) AS time_is_valid, "As above, types d and utclong also possible + extract_hour( @utc ) AS extr_hour, + extract_minute( @ti ) AS extr_min, + extract_second( @utc ) AS extr_sec, + + "Function for the type tims + tims_is_valid( tims`231256` ) AS tims_is_valid, + + "---------------------- Time Stamp ---------------------- + "Note: The type utclong can be used in the generic functions above. + "Functions specific to the type utclong + utcl_current( ) AS utcl_current, "generates a UTC time stamp + utcl_add_seconds( @utc,5 ) AS sec_add_utc, + utcl_seconds_between( utclong`2024-02-25 08:14:26`,utclong`2024-02-25 08:15:17` ) AS sec_bw_utc, + + "Functions specific to the type timetamp + tstmp_is_valid( @tmst ) AS ts_is_valid, + tstmp_current_utctimestamp( ) AS ts_current, + "The following two functions have an optional parameter on_error. + "Check the ABAP Keyword Documentation + tstmp_seconds_between( tstmp1 = @tmst, + tstmp2 = CAST( dec`20240808112517` AS DEC( 15,0 ) ) ) AS sec_bw_ts, + tstmp_add_seconds( tstmp = @tmst, + seconds = CAST( dec`10` AS DEC( 15,0 ) ) ) AS sec_add_ts, + + "---------------------- Functions for conversions ---------------------- + "Note: For the following functions, optional parameters are possible. + "For more details, check the ABAP Keyword Documentation. + tstmp_to_dats( tstmp = @tmst, + tzone = CAST( char`EST` AS CHAR( 6 ) ) ) AS tstmp_to_dats, + tstmp_to_tims( tstmp = @tmst, + tzone = CAST( char`EST` AS CHAR( 6 ) ) ) AS tstmp_to_tims, + tstmp_to_dst( tstmp = @tmst, + tzone = CAST( char`EST` AS CHAR( 6 ) ) ) AS tstmp_to_dst, + dats_tims_to_tstmp( date = @da, + time = @ti, + tzone = CAST( char`EST` AS CHAR( 6 ) ) ) AS dats_tims_to_tstmp, + tstmpl_to_utcl( tstmpl = @tmstlong ) AS tstmpl_to_utcl, + tstmpl_from_utcl( utcl = @utc ) AS tstmpl_from_utcl, + dats_to_datn( dats = dats`20240812` ) AS dats_to_datn, + dats_from_datn( datn = datn`20240111` ) AS dats_from_datn, + tims_to_timn( tims = tims`231256` ) AS tims_to_timn, + tims_from_timn( timn = timn`155432` ) AS tims_from_timn + + WHERE TimeZoneID = char`EST` + INTO @DATA(wa). + + out->write( data = wa name = `wa` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `24) Excursion: ABAP Stopwatch` ) ). + "This excursion example demonstrates an ABAP stopwatch. This stopwatch + "is represented by a local class implemented in the CCIMP include of the + "class ('Local Types' tab in ADT). With the stopwatch, the elapsed + "runtime can be stored and ouput. The example is intended to be a + "playground for exploring time-related functions, and does not claim + "to be a runtime timer for non-demo use cases. + "To start the stopwatch, an instance of the stopwatch class must be + "created. This is done using the 'create' method that returns an + "instance of the (local) stopwatch class. To start the stopwatch, + "the 'start' method must be called. To stop it, call the 'stop' method. + "The elapsed time can then be retrieved using the 'get_time' method. + "To get the time after starting the stopwatch, you can also directly + "call the 'get_time' method without explicitly stopping since it + "includes the stop call. + "The stopwatch also enables the storage of multiple time intervals. You + "can use the 'store' method. The resulting time intervals can be + "retrieved using the 'get_stored_times' method. It returns an internal + "table that includes the times of each interval since the start and the + "delta to previous time intervals. + + out->write( |----------- Starting and stopping -----------\n| ). + + "Creating a stopwatch instance + DATA(stopwatch) = lcl_stopwatch=>create( ). + "Starting stopwatch + stopwatch->start( ). + + "Do something + DO 100000 TIMES. + ASSERT 1 = 1. + ENDDO. + + "Stopping stopwatch + stopwatch->stop( ). + "Getting the time + DATA(sw_time1) = stopwatch->get_time( ). + out->write( sw_time1 ). + + "More stopwatch runs + stopwatch->start( ). + + DO 1000000 TIMES. + ASSERT 1 = 1. + ENDDO. + stopwatch->stop( ). + + DATA(sw_time2) = stopwatch->get_time( ). + out->write( sw_time2 ). + + stopwatch->start( ). + + DO 10000000 TIMES. + ASSERT 1 = 1. + ENDDO. + stopwatch->stop( ). + + DATA(sw_time3) = stopwatch->get_time( ). + out->write( sw_time3 ). + + out->write( |\n--- Testing read performance with secondary table keys ---\n| ). + + "The following example creates two demo internal tables. One without a secondary table + "key and the other with a secondary table key. Consider a scenario where you have an + "internal table without a secondary table key, and you want to add a secondary table + "key later to improve read performance. The tables are populated with a lot of data. + "Then, in a DO loop, many reads are performed on the internal tables. One example uses + "a free key for the read, the other uses a secondary table key. Using the stopwatch, + "the elapsed times are retrieved. There should be a significant delta of the elapsed + "time, showing that the access using the secondary table key is faster. + TYPES: BEGIN OF demo_struc, + idx TYPE i, + str TYPE string, + num TYPE i, + END OF demo_struc. + + DATA itab TYPE HASHED TABLE OF demo_struc WITH UNIQUE KEY idx. + DATA itab_sec TYPE HASHED TABLE OF demo_struc + WITH UNIQUE KEY idx + WITH NON-UNIQUE SORTED KEY sk + COMPONENTS str num. + + DO 500 TIMES. + INSERT VALUE #( idx = sy-index + str = |INDEX{ sy-index }| + num = sy-index ) INTO TABLE itab. + ENDDO. + itab_sec = itab. + + stopwatch->start( ). + DO 500 TIMES. + "Reading into a data reference variable using using a free key. + "This free key corresponds to the secondary table key specified + "for the table in the second example. + DATA(dref) = REF #( itab[ str = `INDEX250` num = 250 ] ). + ENDDO. + stopwatch->stop( ). + DATA(time_free_key) = stopwatch->get_time( ). + out->write( |{ time_free_key } (free key)| ). + + stopwatch->start( ). + DO 500 TIMES. + "Reading from an internal table using the secondary table key + dref = REF #( itab_sec[ KEY sk str = `INDEX250` num = 250 ] ). + ENDDO. + stopwatch->stop( ). + DATA(time_secondary_key) = stopwatch->get_time( ). + out->write( |{ time_secondary_key } (secondary key)| ). + + out->write( |\n----------- Another demo run -----------\n| ). + "In this demo run, the stopwatch is not explicitly stopped. + "This is done implicitly when calling the get_time method. + + stopwatch->start( ). + + DO 100000 TIMES. + ASSERT 1 = 1. + ENDDO. + + DATA(sw_time4) = stopwatch->get_time( ). + out->write( sw_time4 ). + + stopwatch->start( ). + + DO 1000000 TIMES. + ASSERT 1 = 1. + ENDDO. + + DATA(sw_time5) = stopwatch->get_time( ). + out->write( sw_time5 ). + stopwatch->start( ). + + DO 10000000 TIMES. + ASSERT 1 = 1. + ENDDO. + + DATA(sw_time6) = stopwatch->get_time( ). + out->write( sw_time6 ). + + out->write( |\n----------- Storing multiple intervals -----------\n\n| ). + stopwatch->start( ). + + DO 100000 TIMES. + ASSERT 1 = 1. + ENDDO. + + "Storing interval 1 + stopwatch->store( ). + + DO 1000000 TIMES. + ASSERT 1 = 1. + ENDDO. + + "Storing interval 2 + stopwatch->store( ). + + DO 10000000 TIMES. + ASSERT 1 = 1. + ENDDO. + + "Storing interval 3 + stopwatch->store( ). + + DO 100 TIMES. + ASSERT 1 = 1. + ENDDO. + + "Storing interval 4 + stopwatch->store( ). + + DO 10000 TIMES. + ASSERT 1 = 1. + ENDDO. + + ""Storing interval 5 + stopwatch->store( ). + stopwatch->stop( ). + + "Getting stored times + DATA(stored_times) = stopwatch->get_stored_times( ). + out->write( stored_times ). + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_date_time.clas.locals_imp.abap b/src/zcl_demo_abap_date_time.clas.locals_imp.abap new file mode 100644 index 0000000..14f6caa --- /dev/null +++ b/src/zcl_demo_abap_date_time.clas.locals_imp.abap @@ -0,0 +1,133 @@ + "Note: This ABAP stopwatch is not intended to be used productively. + "The implementation is intended to be a playground for exploring + "time-related functions. + CLASS lcl_stopwatch DEFINITION. + PUBLIC SECTION. + CLASS-METHODS create + RETURNING + VALUE(sw_inst) TYPE REF TO lcl_stopwatch. + + METHODS stop. + METHODS start. + METHODS store. + + TYPES: BEGIN OF stored_times_struc, + interval TYPE i, + time_since_start TYPE string, + time_delta TYPE string, + END OF stored_times_struc. + TYPES stored_times_type TYPE SORTED TABLE OF stored_times_struc WITH UNIQUE KEY interval. + METHODS get_stored_times RETURNING VALUE(time) TYPE stored_times_type . + METHODS get_time RETURNING VALUE(time) TYPE string . + + PROTECTED SECTION. + PRIVATE SECTION. + DATA time_start TYPE utclong. + DATA time_stop TYPE utclong. + DATA stopwatch_is_on TYPE abap_bool. + DATA interval_timer_is_on TYPE abap_bool. + TYPES: BEGIN OF time_struc, + days TYPE i, + hours TYPE i, + minutes TYPE int8, + seconds TYPE decfloat34, + END OF time_struc. + DATA time_elapsed TYPE time_struc. + TYPES cl2 TYPE c LENGTH 2. + DATA storage TYPE TABLE OF utclong WITH EMPTY KEY. + + METHODS reset. + METHODS get_elapsed_time + IMPORTING start TYPE utclong + stop TYPE utclong + RETURNING VALUE(delta) TYPE time_struc. + + METHODS time_output_prep + IMPORTING time TYPE time_struc + RETURNING VALUE(res) TYPE string. +ENDCLASS. + +CLASS lcl_stopwatch IMPLEMENTATION. + METHOD create. + sw_inst = NEW #( ). + sw_inst->reset( ). + ENDMETHOD. + + METHOD start. + "No multiple starts allowed + ASSERT stopwatch_is_on = abap_false. + ASSERT interval_timer_is_on = abap_false. + reset( ). + stopwatch_is_on = abap_true. + time_start = utclong_current( ). + ENDMETHOD. + + METHOD stop. + ASSERT stopwatch_is_on = abap_true. + time_stop = utclong_current( ). + stopwatch_is_on = abap_false. + interval_timer_is_on = abap_false. + time_elapsed = get_elapsed_time( start = time_start + stop = time_stop ). + ENDMETHOD. + + METHOD reset. + CLEAR: time_start, + time_stop, + time_elapsed, + stopwatch_is_on, + interval_timer_is_on, + storage. + ENDMETHOD. + + METHOD get_elapsed_time. + cl_abap_utclong=>diff( EXPORTING high = stop + low = start + IMPORTING days = delta-days + hours = delta-hours + minutes = delta-minutes + seconds = delta-seconds ). + ENDMETHOD. + + METHOD get_time. + "When the interval timer is on, it is not allowed to + "get the time. The get_stored_times method is to be used. + ASSERT interval_timer_is_on = abap_false. + IF stopwatch_is_on = abap_true. + stop( ). + ENDIF. + time = time_output_prep( time_elapsed ). + ENDMETHOD. + + METHOD time_output_prep. + res = |{ COND #( WHEN time-days IS NOT INITIAL THEN time-days && ` days, ` ) }| && + |{ COND #( WHEN numofchar( CONV cl2( time-hours ) ) = 1 THEN `0` && time-hours ELSE time-hours ) }:| && + |{ COND #( WHEN numofchar( CONV cl2( time-minutes ) ) = 1 THEN `0` && time-minutes ELSE time-minutes ) }:| && + |{ COND #( WHEN find( val = CONV string( time-seconds ) sub = `.` ) = 1 THEN `0` && time-seconds ELSE time-seconds ) }|. + ENDMETHOD. + + METHOD store. + ASSERT stopwatch_is_on = abap_true. + interval_timer_is_on = abap_true. + APPEND utclong_current( ) TO storage. + ENDMETHOD. + + METHOD get_stored_times. + IF interval_timer_is_on = abap_true. + stop( ). + ENDIF. + + if lines( storage ) > 0. + LOOP AT storage REFERENCE INTO DATA(ref). + DATA(tabix) = sy-tabix. + DATA(time_since_start) = get_elapsed_time( start = time_start stop = ref->* ). + DATA(prep_time_since_start) = time_output_prep( time_since_start ). + DATA(time_delta) = get_elapsed_time( start = COND #( WHEN tabix = 1 THEN time_start ELSE storage[ tabix - 1 ] ) stop = ref->* ). + DATA(prep_time_delta) = time_output_prep( time_delta ). + INSERT VALUE #( interval = tabix + time_since_start = prep_time_since_start + time_delta = prep_time_delta ) INTO TABLE time. + ENDLOOP. + endif. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_date_time.clas.xml b/src/zcl_demo_abap_date_time.clas.xml new file mode 100644 index 0000000..6675cf5 --- /dev/null +++ b/src/zcl_demo_abap_date_time.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_DATE_TIME + E + ABAP cheat sheet: Date, Time, and Time Stamp + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_dtype_dobj.clas.abap b/src/zcl_demo_abap_dtype_dobj.clas.abap new file mode 100644 index 0000000..5e8f5e4 --- /dev/null +++ b/src/zcl_demo_abap_dtype_dobj.clas.abap @@ -0,0 +1,2064 @@ +*********************************************************************** +* +* ABAP cheat sheet: Data Types and Data Objects +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate data types and data objects. +* - Note that in many cases there is no output displayed because the +* focus is on syntax options and declarations. In the class, you can +* set breakpoints and use the debugger to check out data objects. +* You can also use the F2 information for the many types and data +* objects. Simply select a type or object in the code and press F2 +* in ADT to check out the information. +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP development tools for Eclipse (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, refer to the +* notes included in the class as comments or refer to the respective +* topic in the ABAP Keyword Documentation. +* - Due to the amount of console output, the examples contain numbers +* (e.g. 1) ..., 2) ..., 3) ...) for the individual example sections. +* Also, the variable name is displayed in most cases. So to find +* the relevant output in the console easier and faster, just search +* for the number/variable name in the console (CTRL+F in the console) +* or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

    ABAP cheat sheet: Data Types and Data Objects

    +"! Example to demonstrate data types and data objects in ABAP.
    Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_dtype_dobj DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + CLASS-METHODS: + class_constructor. + + TYPES t_pub_text_c30 TYPE c LENGTH 30. + CONSTANTS: comma TYPE string VALUE `, `. + CLASS-DATA: read_only_attribute TYPE string VALUE `Hallo` READ-ONLY. + + PROTECTED SECTION. + PRIVATE SECTION. + + TYPES t_prv_text_c30 TYPE c LENGTH 30. + CLASS-DATA cl_text TYPE t_prv_text_c30. + DATA text TYPE t_pub_text_c30 VALUE '!!!'. + + METHODS adapt_text RETURNING VALUE(str) TYPE string. + + METHODS addition_with_generic_num IMPORTING num1 TYPE numeric + num2 TYPE numeric + RETURNING VALUE(result) TYPE decfloat34. + + constants no_output type string value `No output for this section. Check out the types in the code e.g. using the F2 information.`. + +********************************************************************** + + "Types and methods for demonstrating enumerated types and objects + + "The definition of an enumerated type in ABAP declares its enumerated constants (these are special enumerated objects). + "a) In the case below, no explicit base type is specified. Then, the standard base type of the constants is i. The + " enumerated values are counted up starting with 0 (a -> 0, b -> 1 ...). + + TYPES: BEGIN OF ENUM t_enum, + a, + b, + c, + d, + END OF ENUM t_enum. + + "b) For the following enumerated type, an explicit base type is specified and start values provided using the VALUE addition + " Note that one value must be initial. + + TYPES: basetype TYPE c LENGTH 2, + BEGIN OF ENUM t_enum_base BASE TYPE basetype, + "If VALUE is specified explicitly, VALUE IS INITIAL must be used exactly once. + e VALUE IS INITIAL, + f VALUE 'u', + g VALUE 'v', + h VALUE 'wx', + i VALUE 'yz', + END OF ENUM t_enum_base. + + "c) Optionally an enumerated structure can be declared in the context of the type declaration. + "Use case: If you have more than one enumerated type within one context. In doing so, you declare a constant enumeration structure. + "The components of the structure are the enumeration constants of the enumerated type. + TYPES: BEGIN OF ENUM t_enum_struc STRUCTURE en_struc BASE TYPE basetype, + j VALUE IS INITIAL, + k VALUE 'hi', + l VALUE 'ab', + m VALUE 'ap', + END OF ENUM t_enum_struc STRUCTURE en_struc. + + + METHODS enum_meth_params IMPORTING char TYPE t_enum + RETURNING VALUE(output) TYPE string. + METHODS enum_processing RETURNING VALUE(output) TYPE string_table. + METHODS rtti_enum RETURNING VALUE(output) TYPE string_table. + +ENDCLASS. + + + +CLASS zcl_demo_abap_dtype_dobj IMPLEMENTATION. + + + METHOD adapt_text. + DATA text TYPE t_pub_text_c30. + + text = cl_text && comma && sy-uname && me->text. + + str = text && | (Note: The value of me->text is "{ me->text }")|. + ENDMETHOD. + + + METHOD addition_with_generic_num. + result = num1 + num2. + ENDMETHOD. + + + METHOD class_constructor. + "Filling demo database tables. + zcl_demo_abap_aux=>fill_dbtabs( ). + ENDMETHOD. + + + METHOD enum_meth_params. + + CASE char. + WHEN a. + output = a. + WHEN b. + output = b. + WHEN OTHERS. + output = `Either c or d: ` && char. + ENDCASE. + + ENDMETHOD. + + + METHOD enum_processing. + + "Read and write positions of enumerated objects + "Enumerated objects can be used in all read positions in which the operand + "type is their enumerated type. + "Likewise, enumerated variables can only be used in write positions in which + "the operand type is the enumerated type and only the associated enumerated + "values can be written. + "So, assignments are possible only from one enumerated type to the same (with one + "exception -> assignment to character-like variables of the types c and string) + DATA do_enum TYPE t_enum. + do_enum = a. + APPEND |do_enum: { do_enum }| TO output. + + DATA do_enum_2 LIKE do_enum. + do_enum_2 = do_enum. + APPEND |do_enum_2: { do_enum_2 }| TO output. + + "Assignment to character-like variables of the types c and string. + "In this case, the target field is assigned the name of the enumerated constant or + "the component of the enumerated structure under which the enumerated value of the + "source field is defined in the enumerated type. + DATA do_a_string TYPE string. + do_a_string = do_enum. + APPEND |do_a_string: { do_a_string }| TO output. + + "Or using the CONV operator as follows + DATA(do_next_string) = CONV string( do_enum ). + APPEND |do_next_string: { do_next_string }| TO output. + + "Enumerated constants are converted implicitly to the type string + "before the concatenation in the string template. + DATA(str_from_enum) = |{ a }{ b }{ c }{ d }|. + APPEND |str_from_enum: { str_from_enum }| TO output. + + "Note that only the enumerated type itself is relevant. Usually, the content + "of an enumerated object is not of interest. + "The enumerated value in the base type can be accessed using the constructor + "operators CONV and EXACT only. The base type is i in this case. + DATA(conv_value) = CONV i( do_enum ). + APPEND |conv_value: { conv_value }| TO output. + + "Converting the other way round. + DATA(another_conv) = CONV t_enum( 3 ). + APPEND |another_conv: { another_conv }| TO output. + + "If known statically, an attempt to assign a value other than a valid enumerated value + "to an enumerated variable produces a syntax error. + "If not known statically, an exception is raised. + "The following produces a syntax error + "do_enum = f. + + "The following example shows raising an exception. + DATA dobj TYPE t_enum. + + TYPES t_int_tab TYPE TABLE OF i WITH EMPTY KEY. + DATA(int_tab) = VALUE t_int_tab( ( 0 ) ( 1 ) ( 2 ) ( 3 ) ( 4 ) ). + + DATA str_tab TYPE TABLE OF string. + LOOP AT int_tab INTO DATA(wa_en). + TRY. + dobj = CONV t_enum( wa_en ). + APPEND dobj TO str_tab. + CATCH cx_sy_conversion_no_enum_value INTO DATA(error_enum). + APPEND error_enum->get_text( ) TO str_tab. + ENDTRY. + ENDLOOP. + + APPEND `------------- START: Output for str_tab -------------` TO output. + APPEND LINES OF str_tab TO output. + APPEND `^^^^^^^^^^^^^ END: Output for str_tab ^^^^^^^^^^^^^` TO output. + + "An enumerated variable can be set to the initial value of its base type + "using CLEAR. + CLEAR do_enum. + APPEND |do_enum: { do_enum }| TO output. + + "Enumerated structures + DATA do_enum_s TYPE t_enum_struc. + + "The enumerated structure en_struc was decalred in the public section. + "Using the addition LIKE, a second structure is created referring to the enumerated structure. + "Note that the second structure is not a constant structure. + "The components of the constant structure contain the enumerated values of the enumerated type. + "All the components of the variable structure declared by LIKE contain the initial values. + DATA do_s LIKE en_struc. + APPEND |do_s: { do_s-j } / { do_s-k } / { do_s-l } / { do_s-m }| TO output. + + DATA(do_en) = en_struc. + APPEND |do_en: { do_en-j } / { do_en-k } / { do_en-l } / { do_en-m }| TO output. + + "Accessing structure components using the component selector + DATA(do_en_k) = en_struc-k. + APPEND |do_en_k: { do_en_k }| TO output. + + DATA(do_s_m) = do_s-m. + APPEND |do_s_m: { do_s_m }| TO output. + "Assigning enumerated constants to the variable structure + do_s = en_struc. + APPEND |do_s: { do_s-j } / { do_s-k } / { do_s-l } / { do_s-m }| TO output. + + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + out->write( |ABAP Cheat Sheet Example: Data Types and Data Objects\n\n| ). + +********************************************************************** + + out->write( |Declaring data types\n\n| ). + + "The following examples deal with the declaration of data types. + "They show how data types can be declared locally in an ABAP program. + "Note: + "- Data types can also be declared in the ABAP Dictionary (DDIC) or as a + " CDS entity, which is not covered in this demo example. + "- In ADT and because of the many type declarations, you may want to press + " F2 on the types to get more information. + "- The examples show a selection. + "- Only non-generic types can be used. + + out->write( |1) Declaring data types based on elementary types\n\n| ). + + "See the ABAP Keyword Documentation for the value ranges that are + "accepted by these types. + + "Data type declarations based on built-in ABAP types + + "Numeric types + TYPES te_i TYPE i. + TYPES te_int8 TYPE int8. + TYPES te_decfl16 TYPE decfloat16. + TYPES te_decfl34 TYPE decfloat34. + TYPES te_f TYPE f. + TYPES te_p_l4_d2 TYPE p LENGTH 4 DECIMALS 2. + "Note: LENGTH/DECIMALS must be specified when using the types c, p, n, x + "in ABAP Objects contexts as it is the case here in the example class. + + "Character-like types + "To combine TYPES statements, you can use chained statements, + "i.e. TYPES followed by a colon and then listing the type declarations separated + "by a comma. + TYPES: te_c5 TYPE c LENGTH 5, + te_n4 TYPE n LENGTH 4, + te_str TYPE string. + + "Byte-like types + TYPES te_do_string TYPE x LENGTH 2. + TYPES te_xstr TYPE xstring. + + "Types for date and time + TYPES te_d TYPE d. + TYPES te_t TYPE t. + TYPES te_utc TYPE utclong. + + "You might also stumble on a length specification in parentheses following the + "data type name. It is recommended that you use addition LENGTH instead of the + "parentheses. + TYPES te_cfour(4) TYPE c. + + "**** Data type declarations based on existing types or data objects **** + + "Type declaration based on an existing type visible at this location; + "all properties of the specified data type are inherited. + TYPES te_another_i TYPE te_i. + + "Anticipating the data object declaration needed to demonstrate the LIKE addition + DATA do_num TYPE i. + + "LIKE addition: + "Type declaration based on an existing data object visible at this location; + "all properties of the type of the specified data object are inherited. + TYPES te_from_int LIKE do_num. + + "**** Data type declarations based on globally available types or data objects **** + + "DDIC Types + "Note that the built-in types b and s cannot be specified for type + "declarations. However, the value range for these types can be obtained by + "referencing the built-in DDIC types INT1 and INT2. These are data elements. + "In ADT, you can check out the data elements by forward navigation (hold CTRL + "and click on the type). You can also use F2 Information (click F2 when on + "the type) to get information. + TYPES te_int1 TYPE int1. + TYPES te_int2 TYPE int2. + + "Referring to types in global classes + "Also here, check out the forward navigation or F2 information for the types. + "In the example, the type exists in a global interface. + TYPES te_elem_from_itf TYPE zdemo_abap_get_data_itf=>occ_rate. + + "Referring to a data object that exists in a global interface + TYPES te_dobj_from_itf LIKE zdemo_abap_objects_interface=>stat_str. + + "Referring to a data object that exists in the public visibility section of + "a global class + TYPES te_dobj_from_cl LIKE zcl_demo_abap_objects=>public_string. + + "Referring to a component of a DDIC table (also possible for views; + "the components have elementary types) + TYPES te_comp_ddic_tab TYPE zdemo_abap_carr-carrid. + + "Type pools (ABAP program, administrated by the ABAP Dictionary; may only be + "created in standard ABAP; but is considered obsolete). + "However, the following example is accessible in ABAP for Cloud Development. + "The type pool contains the definitions of globally visible data types and + "constants. Check it out using the forward navigation and the F2 information. + TYPES te_tp TYPE abap_bool. + TYPES te_const_in_tp LIKE abap_true. + + out->write( no_output ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `2) Declaring data types based on complex types` ) ). + + "Structure and internal table types as examples for complex types + + "Structure type, can contain any type + TYPES: BEGIN OF ts_misc_comps, + comp1 TYPE i, + comp2 TYPE string, + comp3 TYPE te_i, "Existing type + comp4 LIKE do_num, "Referring to existing data object + comp5 TYPE string_table, "Internal table type (available in DDIC) + comp6 TYPE TABLE OF zdemo_abap_carr WITH EMPTY KEY, "Internal table type (based on database table) + comp7 TYPE REF TO i, "Reference type + END OF ts_misc_comps. + + "Internal table types + "Note: The examples only use the implicit STANDARD for standard tables. + "Internal table type declaration based on a local structure type + TYPES tt_local_ts TYPE TABLE OF ts_misc_comps WITH EMPTY KEY. + + "Internal table type declaration based on an elementary data type + TYPES tt_int TYPE TABLE OF i. + + "Referring to existing types and data objects + + "Anticipating the creation of structured data objects for the LIKE addition + DATA struc_local_ts TYPE ts_misc_comps. + + "Structure type creation based on an existing structured data object + TYPES ts_w_like LIKE struc_local_ts. + + "Anticipating the creation of an internal table for the LIKE addition + DATA itab_local_ts TYPE TABLE OF ts_misc_comps WITH EMPTY KEY. + + "Internal table type declaration based on an existing internal table + TYPES tt_w_like LIKE itab_local_ts. + + "Internal table type declaration based on the existing internal table type + TYPES tt_another_type TYPE tt_w_like. + + "Structured types based on an internal table's line type + TYPES ts_type_line TYPE LINE OF tt_w_like. + TYPES ts_like_line LIKE LINE OF itab_local_ts. + + "Internal table typed with internal table as line type + TYPES tt_like_table LIKE TABLE OF itab_local_ts. + + "Referring to global types + + "Structure type based on DDIC type + "In this case, a database table is specified whose line type is used as data type + "in this type declaration. You may also use a CDS view (or classic DDIC view in + "standard ABAP) or a dedicated structured type defined in the DDIC. + TYPES ts_ddic_tab TYPE zdemo_abap_carr. + + "Internal table type based on internal type that exists in a gloabl interface + TYPES tt_tab_type_from_itf TYPE zdemo_abap_get_data_itf=>carr_tab. + + "Internal table types with an elementary line type based on globally available types + "Elementary table type + TYPES tt_strtab TYPE string_table. + "Elementary line type; the type is available in a global interface + TYPES tt_elem_type_from_itf TYPE TABLE OF zdemo_abap_get_data_itf=>occ_rate. + + out->write( no_output ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `3) Declaring reference types` ) ). + + "Declaring reference types with static types + TYPES tr_i TYPE REF TO i. + TYPES tr_str TYPE REF TO string. + TYPES tr_ddic_tab TYPE REF TO zdemo_abap_carr. + "Using the generic type data as static type + TYPES tr_data TYPE REF TO data. + + "Referring to an existing reference type + TYPES tr_ref_i TYPE tr_i. + + "Anticipating the creation of a data reference variable for showing + "the LIKE addition + DATA dref_i TYPE REF TO i. + + "Creating a reference type based on a data reference variable + TYPES tr_like_ref_i LIKE dref_i. + + "Creating a data object for the LIKE REF TO addition + DATA str TYPE string. + + "Creating a reference type whose static type is inherited from the data + "type of the specified data object + TYPES tr_like_ref2str LIKE REF TO str. + + "Reference table types + TYPES tr_tab_ref_i TYPE TABLE OF REF TO i. + DATA itab_str TYPE TABLE OF string. + TYPES tr_like_table_ref LIKE TABLE OF ref TO itab_str. + + out->write( no_output ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Declaring data objects` ) ). + + "The following examples deal with the declaration of data ojects. + "They show how data objects can be declared locally in an ABAP program. + "Note: + "- With the exception of two additions (VALUE and READ-ONLY), the syntax + " follows the pattern as for TYPES statements. + "- A data type defined using DATA, which is not derived from an existing type, + " is available only as a property of the + " declared data object and not on its own. This kind of data type is bound to + " its data object. + "- The examples show the creation of named data objects. Anonymous data objects + " are covered below. + "- In ADT and because of the many type declarations, you may want to press F2 on + " the types to get more information. + "- The examples show a selection. For more information, check out the ABAP + " Keyword Documentation. + + out->write( |4) Declaring data objects based on elementary data types\n\n| ). + + "The elementary, built-in data types can be used as shown for data type + " declarations. Chained statements are also possible with DATA. + "Note that not all types as shown above are used here. + DATA: do_i TYPE i, + do_c_l5 TYPE c LENGTH 5, + do_p_l3_d2 TYPE p LENGTH 3 DECIMALS 2, + do_decfl16 TYPE decfloat16, + do_str TYPE string, + "Specifying the length in parantheses instead of using the + "LENGTH addition is not recommended + do_ctwo(2) TYPE c. + + "Referring to locally declared data types + TYPES te_string TYPE string. + DATA do_another_str TYPE te_string. + + "Referring to other data objects + DATA do_like_dobj LIKE do_i. + + "If the length is not specified explicitly for the ABAP types c, n, p, and x, + "the standard length is defined implicitly. Check the F2 information. + DATA do_c_std TYPE c. + DATA do_p_std TYPE p. + + "If neither TYPE nor LIKE is specified, a data object with the bound + "data type 'c LENGTH 1' is created. + DATA do_c. + + "VALUE addition + "Start values can be set for the data objects when they are declared. + "Without the addition VALUE, data objects are filled with their type-specific + "initial values. The start value can either be specified as a literal or as a + "predefined constant. + "Note: The VALUE addition is not to be confused with the VALUE operator that + "can be used to construct the content of complex data objects as shown below. + DATA do_c_l2 TYPE c LENGTH 2 VALUE 'hi'. + DATA do_i_val TYPE i VALUE 123. + DATA do_like_val LIKE do_i VALUE 9. + + "Specifying a constant (data object that cannot be changed at runtime) after + "the VALUE addition + CONSTANTS con TYPE string VALUE `abcdef`. + DATA do_val_con TYPE string VALUE con. + + "VALUE IS INITIAL addition: Explicitly specifying the type-specific initial value + DATA do_i_init TYPE i VALUE IS INITIAL. + DATA do_i_like_init LIKE do_i VALUE IS INITIAL. + + "Data objects can also be created in the declaration part of classes and + "interfaces. There you can use the READ-ONLY addition for data object + "declarations in the public visibility section. In doing so, an attribute + "declared using CLASS-DATA or DATA can be read from outside of the class but + "can only be changed using methods of the class or its subclasses. + "The following attribute is taken from this executable example. It shows a + "read access in a control structure. If you wanted to assign a new value to + "the attribute outside of the class, a syntax error would be displayed. + "Note that when you are in the class itself, there is no need to specify the + "class name. + "read_only_attribute = ... would be sufficient. And changing the value would + "be possible within the class, too. + "Declaration in the example: + "CLASS-DATA: read_only_attribute TYPE string VALUE `Hallo` READ-ONLY. + IF zcl_demo_abap_dtype_dobj=>read_only_attribute = `adapt read only attribute`. + ... + "Since we are here in the very class of this example attribute, a changing + "of the value would be possible. And the class name can, in that case, be + "ommitted. + read_only_attribute = `changed`. + ELSE. + ... + ENDIF. + + out->write( no_output ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `5) Declaring structures and internal tables as examples for complex types` ) ). + + "Note: See more details and examples in the ABAP Keyword Documentations and in the + "respective ABAP cheat sheets. + + "Creating a structure with DATA and providing start values with the VALUE addition. + "If not specified, then the components have their type-specific initial value. + DATA: BEGIN OF a_structure, + comp1 TYPE i VALUE 1, + comp2 TYPE string VALUE `hi`, + comp3 TYPE string, + END OF a_structure. + + "Creating a structure based on a global type. In this case, it is a DDIC database + "table whose line type is used. You can also use a CDS view or a dedicated structured type + "from the DDIC, for example. + DATA struc_ddic_tab TYPE zdemo_abap_carr. + + "Creating a structure as a constant. Providing values is mandatory. + CONSTANTS: BEGIN OF con_struc, + comp1 TYPE i VALUE 1, + comp2 TYPE string VALUE `hallo`, + comp3 TYPE string VALUE `salut`, + END OF con_struc. + + "Using the constant as start value for a structure declaration. + DATA struc_w_val LIKE con_struc VALUE con_struc. + + "Declaring a structure and explicitly specifying the type-specific + "initial values of the structure components as start values. + DATA struc_init_val LIKE con_struc VALUE IS INITIAL. + + "Creating internal tables ... + "Based on a globally available DDIC database table whose line type is used + DATA itab_ddic_tab TYPE TABLE OF zdemo_abap_carr WITH EMPTY KEY. + "Based on an elementary type + DATA itab_tab_i TYPE TABLE OF i. + "The table type is declared in a global interface + DATA itab_tab_type_from_itf TYPE zdemo_abap_get_data_itf=>carr_tab. + "Based on globally available DDIC internal table type; explicitly specifying as initial + DATA itab_ddic_tab_type TYPE string_table VALUE IS INITIAL. + "Based on locally available structured data object + DATA itab_like_struc LIKE TABLE OF struc_w_val WITH EMPTY KEY. + "Based on locally available internal table + DATA itab_like_another_itab LIKE itab_tab_i. + + "Creating an internal table type locally + TYPES tt_ddic_tab TYPE TABLE OF zdemo_abap_fli WITH EMPTY KEY. + "... and an internal table based on it. + DATA itab_w_itab_type TYPE tt_ddic_tab. + + "Creating a structure based on the line of an internal table (type) + DATA struc_from_itab_type TYPE LINE OF tt_ddic_tab. + DATA struc_like_line LIKE LINE OF itab_ddic_tab. + + out->write( no_output ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `6) Declaring data reference variables` ) ). + + "Declaring data reference variables types with static types + DATA dref_int TYPE REF TO i. + DATA dref_str TYPE REF TO string. + DATA dref_ddic_tab TYPE REF TO zdemo_abap_carr. + "Using the generic type data as static type + DATA dref_8_dataa TYPE REF TO data. + + "Referring to an existing reference type + TYPES tr_int TYPE REF TO i. + DATA dref_tr_int TYPE tr_int. + + "Creating a data reference variable based on a data reference variable + DATA dref_like LIKE dref_int. + + "Creating a data object for the LIKE REF TO addition + DATA do_some_string TYPE string. + + "Reference type is created whose static type is inherited from the data type of + "the specified data object + DATA dref_like_ref_str LIKE REF TO do_some_string. + + "Reference tables + DATA dref_tab_i TYPE TABLE OF REF TO i. + DATA dref_tab_str LIKE TABLE OF REF TO do_some_string. + + out->write( no_output ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `7) Assigning values to data objects` ) ). + + "An assignment passes the content of a source to a target data object. + "Note: + "- There are conversion rules when assigning a source to a target data object + " that have different types. See further down. + "- In old ABAP code, you may see MOVE ... TO ... statements for value + " assignments. These statements are obsolete. This is not to be confused + " with MOVE-CORRESPONDING statements for complex types. They are not obsolete. + + "As mentioned, a start value can be directly assigned when declaring a data object. + DATA some_int TYPE i VALUE 123. + + "Assignment using the assignement operator = + "The source of the assigment on the right side (also known as general + "expressions posisiton) can be specified with many things. + + "Single data object as source of the assignment + "In the following cases, the literal and data object have the exact type + "as the data object on the left side. + + some_int = 456. + DATA num TYPE i. + num = some_int. + + DATA str_a1 TYPE string VALUE `hallo`. + DATA str_a2 TYPE string. + str_a2 = str_a1. + + "Functional method as source of the assignment + "In the following example, the method get_next of the class cl_abap_random_int + "returns an integer. Check the F2 information for get_next (return value of type i). + "A random integer that is in the specified value range is assigned to the data object + "on the left side. + num = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) + min = 1 + max = 10 )->get_next( ). + + "Built-in functions as source of the assignment + "There are plenty of functions available. + + "Built-in numeric function + "The following built-in function calculates 2 to the power of 4. The + "result is assigned to the data object on the left side. + num = ipow( base = 2 exp = 4 ). + + "Built-in string function + "The following function transforms the specified data object to upper case letters. + "The result is assigned to the data object on the left side. + str_a1 = to_upper( str_a2 ). + + "Constructor expressions as source of the assignment + "There are various options and expressions available (with many additions). + "Check the ABAP Keyword Documentation and the cheat sheet. Here, taking the VALUE + "operator as an example. This operator is very handy especially for complex types. + + "Creating a structure + DATA some_struc TYPE zdemo_abap_carr. + + "Assignment using the VALUE operator + "Note the # character that stands for the type. Here, the structure type can be + "derived from the context. Hence, the explicit name can but need not be specified. + some_struc = VALUE #( carrid = 'XY' carrname = 'XY Airways' ). + + "Creating an internal table and assigning values + "Note that components that are not specified and assigned a value retain their + "type-specific ininial value. + DATA some_itab TYPE TABLE OF zdemo_abap_carr WITH EMPTY KEY. + some_itab = VALUE #( ( carrid = 'XY' carrname = 'XY Airways' ) + ( carrid = 'ZZ' carrname = 'ZZ Airlines' ) ). + + "Table expressions as source of the assignment + "A structure is assigned an internal table line + some_struc = some_itab[ 2 ]. + + "Calculation expressions as source of the assignment + "Arithmetic expressions + num = 1 + 2. + "A calculation assignment as follows + num += 1. + "is the short form of + num = num + 1. + "Syntax options: +=, -=, *= and /= + + "String expressions as source of the assignment + str_a2 = str_a1 && ` blabla`. "Strings are appended using the && operator + str_a2 = |{ str_a1 } some more bla.|. "String templates + "Note: Data objects are specified in curly brackets. The content is converted to type string. + " It must be convertible to type string. + + "An elementary data object is assigned a component of a specific table line using + "a table expression. Note: In the following case, the types of source and target are not + "the same (type c versus type string). As shown further down, such an assignment can lead + "to unexpected or undesired results. Type-dependent conversions are made in accordance + "with the conversion rules. + str_a2 = some_itab[ 2 ]-carrname. + + out->write( no_output ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `8) Creating data objects by inline declaration` ) ). + + "The declaration operator DATA can be specified in any designated declaration position. + "The data type of the variable is determined by the operand type. It must be possible + "to derive this type completely statically. + "Note: + "- The FINAL declaration operator is available for creating + " immutable variables as shon below. + "- As shown in the previous section, there are many options for what can be placed on + " the right side. + + "Data object declarations and assignements as shown above. + DATA str_b1 TYPE string VALUE `abc`. + + DATA str_b2 TYPE string. + str_b2 = `def`. + + "Using a declaration expression with the declaration operator DATA, a data object + "can be declared inline. + "The data type of the declared variable is determined by the right side. + "In doing so, a data object is assigned a value in one go. + "In the following case, it is a string literal with backquotes on the right side. + "The data type is derived and, hence, the data object is of type string. + DATA(str_b3) = `ghi`. + + "In the following case, it is a text field literal with quotes. Type c is derived. + "The length is derived from the number of characters. + DATA(c_l3) = 'jkl'. "type c length 3 + DATA(c_l4) = 'mnop'. "type c length 4 + "Note the type conversion implications when making an assignment with these two + "data objects. + c_l3 = c_l4. "c_l3: 'mno' + + "Structures and internal tables + "In declaration expressions, structures and internal tables can be declared inline + "and filled with, for example, the help of the VALUE operator. + + "Structured data type + TYPES: BEGIN OF ts_struc, + comp1 TYPE i, + comp2 TYPE string, + END OF ts_struc. + + "In the following examples, structures are created. The structured data type is + "derived from the type specified before the parentheses. + DATA(struc_b1) = VALUE ts_struc( comp1 = 1 comp2 = `A` ). + "No components specified and values assigned means an initial structure. + "This syntax is also possible for declaring data objects with elementary types + "and explicitly specifiying initial values, but only for initial values. + "See the CONV operator below. + DATA(struc_b2) = VALUE ts_struc( ). + DATA(elem_init) = VALUE i( ). + "Note that components that are not specified and assigned a value remain initial. + DATA(struc_b3) = VALUE zdemo_abap_carr( carrid = 'AB' carrname = 'AB Airlines' ). + "An entire structure is assigned. + DATA(struc_b4) = struc_b1. + + "Note: When the structure has already been declared, and you want to assign values, + "you can use the VALUE operator followed by the # character instead of the explicit + "type name. In that case, it is possible to derive the type from the context. + struc_b4 = VALUE #( comp1 = 2 comp2 = `b` ). + + "Internal tables + "The internal table type is specified before the parentheses after the VALUE operator. + "The following example uses a table type that is globally available in the DDIC. + DATA(itab_b1) = VALUE string_table( ( `a` ) + ( `b` ) + ( `c` ) ). + + "Using a local internal table type + TYPES tt_b1 TYPE TABLE OF ts_struc WITH EMPTY KEY. + DATA(itab_b2) = VALUE tt_b1( ( comp1 = 1 comp2 = `a` ) + ( comp1 = 2 comp2 = `b` ) + ( comp1 = 3 comp2 = `c` ) ). + + "In the context of other ABAP statements such as LOOP, READ TABLE or ABAP SQL + "SELECT statements, inline declarations are useful for creating target variables with + "appropriate data types in place. This includes data reference variables and field + "symbols. Field symbols are not covered below. + + "A work area/structure to hold the current internal table line is created inline. + LOOP AT itab_b2 INTO DATA(wa_b1). + wa_b1-comp1 = 12345. + ... + ENDLOOP. + + "Using the REFERENCE addition, a data reference variable can be created inline. + LOOP AT itab_b2 REFERENCE INTO DATA(wa_ref_b1). + wa_ref_b1->comp1 = 67890. + ... + ENDLOOP. + + "A structure to hold the internal table line read is created inline. + READ TABLE itab_b2 INTO DATA(wa_b2) INDEX 2. + "Data reference variable + READ TABLE itab_b2 REFERENCE INTO DATA(wa_ref_b2) INDEX 2. + + "ABAP SQL statements + "A structure as target data object is created inline. + SELECT SINGLE * FROM zdemo_abap_carr INTO @DATA(struc_b5). + "NEW addition of the INTO clause creates a data reference variable + SELECT SINGLE * FROM zdemo_abap_carr INTO NEW @DATA(struc_ref). + + "Internal table as target data object is created inline. + SELECT * FROM zdemo_abap_carr INTO TABLE @DATA(itab_b3). + "NEW addition + SELECT * FROM zdemo_abap_carr INTO TABLE NEW @DATA(itab_ref). + + out->write( no_output ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `9) Assigning references to data reference variables` ) ). + + "Note: + "- As is true for other data object and types, there are special assignment rules + " for data reference variables. Check out the ABAP Keyword Documentation. + "- An initial reference variable contains the null reference, which does not point + " to any objects. This means that it does not have a data type or a class as + " a dynamic type. + + "Declaring data reference variables with static types + "At this stage, initial reference variables contain null references. + DATA dref_1_i TYPE REF TO i. + DATA dref_2_str TYPE REF TO string. + "Generic type as static type + DATA dref_3_data TYPE REF TO data. + + "References in data reference variables can point to existing data objects. + "For assigning the reference, you can use the REF operator. + "There is also an ABAP statement available doing the same: GET REFERENCE. + "It should not be used anymore, especially in ABAP for Cloud development. + + "Creating data objects to refer to and providing a start value + DATA do_number TYPE i VALUE 987. + DATA do_string TYPE string VALUE `abc`. + + "After the assignment, the data reference variable points to the values. + "The data type is derived (dynamic type). + dref_1_i = REF #( do_number ). "Dynamic type is the same as the static type in this case + dref_2_str = REF #( do_string ). "Dynamic type is the same as the static type in this case + "Dynamic types of the followig examples are more specific than static type, + "which is a generic type in this case. + dref_3_data = REF #( do_number ). + dref_3_data = REF #( do_string ). + + "Note: Table expressions can be also specified within the parentheses. + + "Inline declarations are also possible to create data reference variables + "and assigning values in one go. Check the F2 information. + DATA(dref_4_data) = dref_3_data. + DATA(dref_5_str) = REF #( `hi` ). + DATA(dref_6_i) = REF #( do_number ). + + "Assignments between two data reference variables mean that references are copied. + "The concepts of upcast and downcast enter the picture here. + "Two different assignment operators are used, as well as the casting operator CAST. + + "Upcast is possible for elementary data types + "- The static type of the target variable is more general or identical to the static + " type of the source variable. + "- Assignment operator used: = + "- Note that the operators for downcasts can also be used explicitly here, but it is + " usually not needed. + "- In this example, elementary data types are covered. An upcast works ... + " - if the data types have identical type properties (i.e. the built-in type match + " as well as length and decimal places). + " - the static type of the source variable is completely typed, and the static type + " of the target variable is generic. + + "The following upcasts work. Both point to data objects of type i or string. + dref_1_i = dref_6_i. + "The source on the right side is completely typed (type i), + "the target on the left side is a generic type (type data). + dref_3_data = dref_1_i. + + "Downcasts + "- The static type of the target variable is more specific than the static type of the + " source variable. + "- The assignability is not checked until runtime. + "- Must always be performed explicitly using the casting operator ?= or the more + " modern casting operator CAST. + + "The following example would result in a syntax error due to type incompatibility. + "dref_1_i = dref_3_data. + + "In the following example, the source has a generic static type (data). The target type + "has a more specific type (type i). + "To suppress the syntax error, the CAST operator is needed. + "Note: + "- The assignability is still not checked. This is done at runtime. + " In this example, it works since the dynamic type of the source is also of type i. + "- An advantage of the CAST operator compared to ?= is that the operator enables downcasts + " in operand positions, which helps reduce helper variables. + dref_1_i = CAST #( dref_3_data ). + + "If not caught, the following would result in a runtime error. + "dref_3_data points to a data object of type i, the static type of dref_2_str is string. + "So, the downcast does not work. + TRY. + dref_2_str = CAST #( dref_3_data ). + CATCH cx_sy_move_cast_error INTO DATA(e). + out->write( data = e->get_text( ) name = `e->get_text( )` ). + ENDTRY. + + "Old syntax using the ?= operator + dref_1_i ?= dref_3_data. + + "For upcasts, the operators can be used, too, but they are usually not necessary. + "So, an assignment as follows is possible but not needed. Only using = is sufficient. + dref_1_i = CAST #( dref_6_i ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `10) Creating anonymous data objects` ) ). + + "Anonymous data objects are a topic related to data reference variables. + "These data objects are unnamed data objects. + "Most of the data objects above are named data objects, i.e. they can be addressed + "by a dedicated name. + "Unnamed data objects are literals and anonymous data objects. Anonymous data objects + "can be addressed using data reference variables. + "Note: Unlike data objects created with the statement DATA, anonymous data objects + "are created at runtime. Data objects declared using DATA are created when the + "program is loaded. + + "Options to create anonymous data objects + "- CREATE DATA statements + "- Using the instance operator NEW + "- Addition NEW of the INTO clause in ABAP SQL SELECT statements + + "CREATE DATA statements + "Note that there are many additions available. The examples show a selection. + + "Creating an anonymous data object with an implicit type. + "If neither of the additions TYPE or LIKE are specified, the data reference variable + "must be completely typed. + DATA dref_7_str TYPE REF TO string. + CREATE DATA dref_7_str. + + "Note: If you want to assign a value to the data object, this can't be done directly. + "The data reference variable must be dereferenced first using the dereferencing operator. + dref_7_str->* = `hi`. + + "Creating an anonymous data object with an explicitly specified data type + DATA dref_8_data TYPE REF TO data. + CREATE DATA dref_8_data TYPE p LENGTH 8 DECIMALS 3. + dref_8_data->* = 1 / 3. + + "Creating a named data object + DATA it TYPE TABLE OF zdemo_abap_carr WITH EMPTY KEY. + + "Creating an anomyous internal table. + "Using the LIKE addition to refer to an existing internal table + CREATE DATA dref_8_data LIKE it. + + "Using the anonymous data object as target in the INTO clause of a SELECT statement. + "As above, note the dereferencing. + SELECT * + FROM zdemo_abap_carr + INTO TABLE @dref_8_data->*. + + "Creating an anonymous hashed table by specifying the entire table type + CREATE DATA dref_8_data TYPE HASHED TABLE OF zdemo_abap_carr WITH UNIQUE KEY carrid. + + "Using the anonymous data object as target in the INTO clause of a SELECT statement + SELECT * + FROM zdemo_abap_carr + INTO TABLE @dref_8_data->*. + + "Creating an anonymous structure + CREATE DATA dref_8_data TYPE zdemo_abap_fli. + + "Using the anonymous data object as target in the INTO clause of a SELECT statement + SELECT SINGLE * + FROM zdemo_abap_fli + INTO @dref_8_data->*. + + "NEW operator + "- Works like CREATE DATA dref TYPE type statements and can be used in general + " expression positions. + "- Allows to assign values to the new anonymous data objects in parentheses + + "Creating data reference variables + DATA: dref_9_data TYPE REF TO data, + dref_10_i TYPE REF TO i. + + "Assining a value to an anonymous data object of the type i + dref_9_data = NEW i( 555 ). + + "The # character can be used instead of the complete type spefication + "when the type can be derived from the context. + dref_10_i = NEW #( 653 ). + + "Inline declarations are handy. + "Creating a suitable anonymous data object in place. Here, the type must be specificed + "explicitly. + DATA(dref_11_i) = NEW i( 32 ). + + "Creating an anonymous structure + DATA(dref_12_ddic_tab) = NEW zdemo_abap_carr( carrid = 'AB' carrname = 'AB Airlines' ). + + "The # character can be omitted when they type can be derived. + DATA dref_13_ddic_tab TYPE REF TO zdemo_abap_carr. + dref_13_ddic_tab = NEW #( carrid = 'AB' carrname = 'AB Airlines' ). + + "ABAP SQL SELECT statement + "As shown above, using the NEW addition in the INTO clause, an anonymous data + "object can be created in place. + SELECT * + FROM zdemo_abap_carr + INTO TABLE NEW @DATA(dref_14_inline). + + out->write( no_output ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Excursions: Elementary types and type conversions` ) ). + + out->write( |11) Implicit and explicit conversion\n\n| ). + + "Implicit conversions are performed in assignments using the assignment operator = + "The content of a data object is converted according to the associated conversion rules. + "The following examples demonstrate implicit type conversions and their consequences + "that might be undesired. + + "Conversions with the types c and string + DATA do_1_str TYPE string VALUE `abcedf`. + DATA do_2_c3 TYPE c LENGTH 3. + do_2_c3 = do_1_str. + + out->write( data = do_2_c3 name = `do_2_c3` ). + out->write( |\n| ). + + "Conversions with the types i and decfloat34 + DATA do_4_i TYPE i. + DATA do_5_dcfl34 TYPE decfloat34 VALUE '4.56'. + do_4_i = do_5_dcfl34. + + out->write( data = do_4_i name = `do_4_i` ). + out->write( |\n| ). + + "Conversions with the types i and string + do_4_i = -5. + do_1_str = do_4_i. + + out->write( data = do_1_str name = `do_1_str` ). + out->write( |\n| ). + + "Explicit type conversions can be performed with the CONV operator + "It converts the value specified within the parentheses to the data type specified + "before the parentheses and and creates an appropriate result. + "Note: + "- CONV closes the gap where the value operator VALUE cannot be used to construct + " values for elementary data objects except for the initial value. + "- The conversion is performed in accordance with the associated conversion rule. + + "Explicitly converting decfloat34 to i + DATA do_6_dcfl34 TYPE decfloat34 VALUE '2.78'. + DATA(do_7_i) = CONV i( do_6_dcfl34 ). + + out->write( data = do_7_i name = `do_7_i` ). + out->write( |\n| ). + + "# character when the type can be derived + DATA do_8_i TYPE i. + do_8_i = CONV #( do_6_dcfl34 ). + + out->write( data = do_8_i name = `do_8_i` ). + out->write( |\n| ). + + "The following two calculations yield different results + do_8_i = sqrt( 5 ) + sqrt( 6 ). + + out->write( data = do_8_i name = `do_8_i` ). + out->write( |\n| ). + + do_8_i = CONV i( sqrt( 5 ) ) + CONV i( sqrt( 6 ) ). + + out->write( data = do_8_i name = `do_8_i` ). + + "CONV operator for creating data objects inline with elementary data types + "Assume, you want a data object typed with decfloat34. + "The following example assigns a text field literal to the left side. + DATA(do_9_c4) = '4.56'. "It is type c + + "Using the CONV operator and explicitly specifing the type, you can contruct a + "data object with an appropriate elementary data type. + DATA(do_10_dcfl34) = CONV decfloat34( '4.56' ). + "It corresponds to the following + DATA do_11_dcfl34 TYPE decfloat34 VALUE '4.56'. + + "Note that the EXACT operator is available for lossless assignments. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `12) Character strings and text field strings` ) ). + + "The following example shows the difference between text field strings + "of type c and character strings of type string when it comes to trailing + "blanks. + + DATA: text_space TYPE c LENGTH 1 VALUE ' ', + string_space TYPE string VALUE ` `, + result3 TYPE string, + result4 TYPE string. + result3 = '-->' && text_space && '<--'. + result4 = '-->' && string_space && '<--'. + + out->write( |{ result3 }\n{ result4 }| ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `13) Floating point numbers` ) ). + + "The following example shows the difference between binary and decimal + "floating point numbers. + + DATA: result1 TYPE f, + result2 TYPE decfloat34. + result1 = 815 / 1000. + result2 = 815 / 1000. + + out->write( |Binary floating point: { result1 }\n| && + |Decimal floating point: { result2 }\n| ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `14) Byte-like types` ) ). + + "The following example shows byte-like types x and xstring. + + "A byte string is filled with binary data created from a string + "using the convert_out method of the cl_abap_codepage class. + "The result is the UTF-8 representation of the string (type xstring) + "which is displayed at first. + "Using the create_in method, the data object of type xstring is converted + "back to string. + "Without knowing how it was created or what it is supposed to mean, you can + "hardly do anything with a byte string. + "The second part of the example deals with type x. + "The binary representation of a new line feed (0A) is converted to string. + "The result is compared to the actual means of previding a new line feed + "(string template with \n and the older cl_abap_char_utilities=>newline). + "The same is done using the binary representation of a blank (20). + + DATA(some_string) = `Hi there!`. + + DATA(xstr) = + cl_abap_conv_codepage=>create_out( + codepage = `UTF-8` + )->convert( source = some_string ). + + out->write( data = xstr name = `xstr` ). + out->write( |\n| ). + + DATA(xstring2string) = cl_abap_conv_codepage=>create_in( codepage = `UTF-8` + )->convert( source = xstr ). + + out->write( data = xstring2string name = `xstring2string` ). + out->write( |\n| ). + + DATA line_feed_hex TYPE x LENGTH 1 VALUE '0A'. + + DATA(line_feed_str) = cl_abap_conv_codepage=>create_in( codepage = `UTF-8` + )->convert( source = CONV xstring( line_feed_hex ) ). + + ASSERT line_feed_str = |\n|. + ASSERT line_feed_str = cl_abap_char_utilities=>newline. + + out->write( `Y-->` && line_feed_str && `<--` ). + out->write( |\n| ). + out->write( `Y-->` && |\n| && `<--` ). + out->write( |\n| ). + + DATA a_blank_x TYPE x LENGTH 1 VALUE '20'. + + DATA(blank) = cl_abap_conv_codepage=>create_in( codepage = `UTF-8` + )->convert( source = CONV xstring( a_blank_x ) ). + + ASSERT blank = ` `. + + out->write( `-->` && blank && `<--` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `15) Date and time` ) ). + + "In the example, a date field is assigned the current values + "using the cl_abap_context_info class. A calculation follows. The date of next + "day is calculated. + "Note: The data types behave like numeric values in the context of calculations. + "In assignments and in the output, they behave like character-like types. + "The second example shows an undesired result for a conversion of type i to d. + "The number is interpreted as number of days since 0001-01-01 of the Gregorian + "calendar. In this case, the date would exceeed the maximum value '99991231'. + "In such a case, the date field is assigned the invalid value '00000000'. + + DATA: today TYPE d, + tomorrow TYPE d. + today = cl_abap_context_info=>get_system_date( ). + tomorrow = today + 1. + out->write( data = today name = `today` ). + out->write( |\n| ). + out->write( data = tomorrow name = `tomorrow` ). + out->write( |\n| ). + + DATA date TYPE d. + date = '20240101'. + out->write( data = date name = `date` ). + out->write( |\n| ). + + "In newer ABAP releases, the following statement shows a syntax + "warning that the value of the the data object (intentionally + "specified here like this) is not an admissable value for the + "target type. Therefore, the example is provided differently to + "circumvent the syntax warning. + + "date = 20240101. + DATA falsedate TYPE i VALUE 20240101. + date = falsedate. + out->write( data = date name = `date` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `16) Type conversion rules` ) ). + + "The purpose of this example is to emphasize the conversion rules + "that should be noted when performing conversions. The example + "uses only a selection of elementary data types. + "The result of the conversion may be unexpected or surprising. + "For all the details, check the ABAP Keyword Documentation. + "In the following example, a structured type is created. + "The components are as follows: + "- A data object having a specific data type is to be converted. + "- The conversion results are reflected in the other components. + "- If there is a conversion error, the conv_err* components hold + " the error message. + "An internal table is created based on this structured type. + "Several data objects are inserted into the internal table, on the + "basis of which a conversion is performed. The table is looped over. + "Implicit conversions are performed using the assignment operator. + + "Some noteworthy type conversions: + "Conversion of d to decfloat34 and i: + " If the source field contains a valid date in the format yyyymmdd, + " it is used to calculate the number of days since 01.01.0001, and + " this value is then converted to the internal representation of the + " corresponding integer. If the source field contains an invalid date, + " the target field is assigned the value 0. + "Conversion of t to decfloat34 and i: + " If the source field contains only digits, the content is interpreted + " as a time specification in the format hhmmss from which the value + " hh*3600+mm*60+ss is calculated and then converted to the internal + " representation of the corresponding integer. + "Conversion of type i to c: + " The value is passed right-aligned. If the target field is too short, + " it is truncated on the left and the carachter * is set in the first + " position. + "Conversion of type i to t: + " The value of the integer is divided by the number of seconds in a day + " (86,400) and the integer remainder of the division is interpreted as + " the number of seconds since midnight. The resulting time is placed in + " the target field in the format hhmmss. + "Conversion of type i to string: + " The character - is set in the last position for a negative value and + " a blank is set in the last position for a positive value. + "Conversion of type decfloat34 to t: + " The content of the source field is first converted to data type i + " and then to type t. + + TYPES: BEGIN OF ts_conv_struc, + to_be_converted TYPE REF TO data, + type TYPE string, + conv_c_len2 TYPE c LENGTH 2, + conv_err_c_len2 TYPE string, + conv_d TYPE d, + conv_err_d TYPE string, + conv_n_len3 TYPE n LENGTH 3, + conv_err_n_len3 TYPE string, + conv_t TYPE t, + conv_err_t TYPE string, + conv_decfl34 TYPE decfloat34, + conv_err_decfl34 TYPE string, + conv_i TYPE i, + conv_err_i TYPE string, + conv_str TYPE string, + conv_err_str TYPE string, + END OF ts_conv_struc. + + DATA tt_conv_tab TYPE TABLE OF ts_conv_struc WITH EMPTY KEY. + DATA err TYPE REF TO cx_root. + + DATA d_to_conv TYPE d VALUE '20230101'. + DATA t_to_conv TYPE t VALUE '120000'. + DATA dec34_to_conv TYPE decfloat34 VALUE '36000.999'. + + "Filling internal table + tt_conv_tab = VALUE #( + "c + ( to_be_converted = REF #( 'abc' ) type = `C LENGTH 3` ) + ( to_be_converted = REF #( '11111111' ) type = `C LENGTH 8` ) + "d + ( to_be_converted = REF #( d_to_conv ) type = `D` ) + "t + ( to_be_converted = REF #( t_to_conv ) type = `T` ) + "decfloat34 + ( to_be_converted = REF #( dec34_to_conv ) type = `DECFLOAT34` ) + "i + ( to_be_converted = REF #( 12345678 ) type = `I` ) + ( to_be_converted = REF #( -321 ) type = `I` ) + "string + ( to_be_converted = REF #( `hello abap` ) type = `STRING` ) + ( to_be_converted = REF #( `12345` ) type = `STRING` ) + ). + + LOOP AT tt_conv_tab REFERENCE INTO DATA(wa_ref_con). + TRY. + wa_ref_con->conv_c_len2 = wa_ref_con->to_be_converted->*. + wa_ref_con->conv_err_c_len2 = `-`. + CATCH cx_root INTO err. + wa_ref_con->conv_err_c_len2 = err->get_text( ). + ENDTRY. + + IF wa_ref_con->type = `T`. + wa_ref_con->conv_err_d = `T to D not possible.`. + ELSE. + TRY. + wa_ref_con->conv_d = wa_ref_con->to_be_converted->*. + wa_ref_con->conv_err_d = `-`. + CATCH cx_root INTO err. + wa_ref_con->conv_err_d = err->get_text( ). + ENDTRY. + ENDIF. + + TRY. + wa_ref_con->conv_n_len3 = wa_ref_con->to_be_converted->*. + wa_ref_con->conv_err_n_len3 = `-`. + CATCH cx_root INTO err. + wa_ref_con->conv_err_n_len3 = err->get_text( ). + ENDTRY. + + IF wa_ref_con->type = `D`. + wa_ref_con->conv_err_t = `D to T not possible.`. + ELSE. + TRY. + wa_ref_con->conv_t = wa_ref_con->to_be_converted->*. + wa_ref_con->conv_err_t = `-`. + CATCH cx_root INTO err. + wa_ref_con->conv_err_t = err->get_text( ). + ENDTRY. + ENDIF. + + TRY. + wa_ref_con->conv_decfl34 = wa_ref_con->to_be_converted->*. + wa_ref_con->conv_err_decfl34 = `-`. + CATCH cx_root INTO err. + wa_ref_con->conv_err_decfl34 = err->get_text( ). + ENDTRY. + + TRY. + wa_ref_con->conv_i = wa_ref_con->to_be_converted->*. + wa_ref_con->conv_err_i = `-`. + CATCH cx_root INTO err. + wa_ref_con->conv_err_i = err->get_text( ). + ENDTRY. + + TRY. + wa_ref_con->conv_str = wa_ref_con->to_be_converted->*. + wa_ref_con->conv_err_str = `-`. + CATCH cx_root INTO err. + wa_ref_con->conv_err_str = err->get_text( ). + ENDTRY. + + ENDLOOP. + + out->write( data = tt_conv_tab name = `tt_conv_tab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `17) Excursion: RTTI` ) ). + + "Using RTTI to check type compatibility + "In the following example the applies_to_data method of the RTTI class + "cl_abap_datadescr is used to check the compatibility of generically typed + "data reference variables pointing to different data objects. + "An assignment of ref1->* to ref3->* would raise an uncatchable exception. + + DATA num1 TYPE i. + DATA num2 TYPE i. + DATA itab_i TYPE TABLE OF i WITH EMPTY KEY. + + DATA ref1 TYPE REF TO data. + DATA ref2 TYPE REF TO data. + DATA ref3 TYPE REF TO data. + + ref1 = REF #( num1 ). + ref2 = REF #( num2 ). + ref3 = REF #( itab_i ). + + IF CAST cl_abap_datadescr( cl_abap_typedescr=>describe_by_data( ref1->* ) + )->applies_to_data( ref2->* ). + ref1->* = ref2->*. + out->write( `The types of ref1->* and ref2->* are compatible.` ). + ELSE. + out->write( `The types of ref1->* and ref2->* are not compatible.` ). + ENDIF. + + out->write( |\n| ). + + IF CAST cl_abap_datadescr( cl_abap_typedescr=>describe_by_data( ref1->* ) + )->applies_to_data( ref3->* ). + ref1->* = ref3->*. + out->write( `The types of ref1->* and ref3->* are compatible.` ). + ELSE. + out->write( `The types of ref1->* and ref3->* are not compatible.` ). + ENDIF. + + out->write( |\n| ). + out->write( |\n| ). + + "Using RTTI to get type descriptions + "In the following example, an internal table that has been filled in + "a previous example is looped over. It contains references to various types + "for whom specific type descriptions are retrieved. + "You may want to set a break-point and check out the variety of information + "contained in the the data reference variable 'rtti'. + + TYPES: BEGIN OF ts_rtti, + absolute_name TYPE string, + kind TYPE string, + type_kind TYPE string, + END OF ts_rtti. + + DATA rtti_tab TYPE TABLE OF ts_rtti WITH EMPTY KEY. + + LOOP AT tt_conv_tab REFERENCE INTO DATA(wa_ref). + + DATA(rtti) = CAST cl_abap_datadescr( + cl_abap_typedescr=>describe_by_data( wa_ref->to_be_converted->* ) ). + + APPEND VALUE #( absolute_name = rtti->absolute_name + kind = rtti->kind + type_kind = rtti->type_kind ) TO rtti_tab. + + ENDLOOP. + + out->write( data = rtti_tab name = `rtti_tab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `18) Constants and immutable variables` ) ). + + "As mentioned above, constants cannot be changed at runtime. + CONSTANTS con_str TYPE string VALUE `hallo`. + + "Constants as start values for dobj declarations following value + CONSTANTS con_underscores TYPE string VALUE `__________`. + DATA str_w_con_as_start_value TYPE string VALUE con_underscores. + + "Immutable variables + FINAL(do_final_inl) = 1. + DATA(do_data_inl) = 1 + do_final_inl. + "not possible + "do_final_inl = 2. + + SELECT * FROM zdemo_abap_carr INTO TABLE @DATA(itab_data_inl). + + DATA itab_like_inline LIKE itab_data_inl. + + "Using an inline declaration as target of a LOOP statement + "A value is assigned multiple times, but it cannot be changed in any other + "write positions. + LOOP AT itab_data_inl INTO FINAL(wa_final). + + "The following is not possible + "wa_final-carrid = 'd'. + "only read access + APPEND wa_final TO itab_like_inline. + ENDLOOP. + + "SELECT statement with a an immutable target table declared inline + SELECT * FROM zdemo_abap_carr INTO TABLE @FINAL(itab_final_inl). + + out->write( no_output ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `19) Various ABAP glossary terms on data types and objects in a nutshell` ) ). + + "Standalone and bound data types + "Standalone: Data type that is defined using the statement TYPES in an ABAP program, as + " a data type of the ABAP Dictionary or as an CDS entity. + "Bound: Data type that only exists as a property of a data object. + + "Standalone data type + TYPES te_a_c10 TYPE c LENGTH 10. + + "Bound data types + DATA do_a_c20 TYPE c LENGTH 20. + DATA do_b_like LIKE do_a_c20. + TYPES te_b_like LIKE do_a_c20. + +********************************************************************** + + "Complex and elementary data type/object + "Elementary: Data type of fixed or variable length that is neither structured, nor a + " table type or a reference type. + "Complex: Made up of other data types, for example structured data type/objects, a + " table type/internal tables + + "Elementary + DATA do_c_i TYPE i. + + "Complex + DATA: BEGIN OF struc_a, + comp1 TYPE i, + comp2 TYPE string, + comp3 TYPE zdemo_abap_carr, "structure + comp4 TYPE string_table, "internal table + comp5 TYPE REF TO i, "reference type + END OF struc_a. + +********************************************************************** + + "Complete and generic data types + "Complete: Non-generic data type + "Generic: + "- Data type that does not set all properties of a data object. + "- Can only be used for the typing of formal parameters and field symbols. + + "Complete data type + DATA do_d_i TYPE i. + + "Field symbols typed with generic data types + "Note: A field symbol is a symbolic name for a data object to which actual + "memory can be assigned at runtime. A field symbol can be used as a placeholder + "for a data object at an operand position. For more information, see the ABAP + "Cheat Sheet on dynamic programming. Field symbols are used in this example + "to demonstrate generic types other than just data with which data reference + "variables can be typed. + "As the name implies, clike expects character-like data types. data can + "expect any data type. This is shown in the example. Apart from the + "character-like types, internal table types are also accepted. + FIELD-SYMBOLS TYPE clike. + FIELD-SYMBOLS TYPE data. + + "Data object declarations + DATA do_e_c5 TYPE c LENGTH 5 VALUE 'abcde'. + DATA do_f_str TYPE string VALUE `Hallo, how are you?`. + DATA(itab_a) = VALUE string_table( ( `a` ) ( `b` ) ( `c` ) ). + + "Generic type clike + "Field symbols with generic data types can be assigned appropriate values + ASSIGN do_e_c5 TO . + + out->write( data = name = `` ). + out->write( |\n| ). + + ASSIGN do_f_str TO . + + out->write( data = name = `` ). + out->write( |\n| ). + + "Generic type data + ASSIGN do_e_c5 TO . + + out->write( data = name = `` ). + out->write( |\n| ). + + ASSIGN do_f_str TO . + + out->write( data = name = `` ). + out->write( |\n| ). + + ASSIGN itab_a TO . + + out->write( data = name = `` ). + out->write( |\n| ). + +********************************************************************** + + "Variable and constant data objects + "Variable: Named data object whose value can be changed during the runtime + " of an ABAP program. + "Constant: Named data object whose value cannot be changed at runtime. + + "Variable + DATA do_g_i TYPE i VALUE 123. + do_g_i = 456. + + CONSTANTS con_a_i TYPE i VALUE 789. + "An assignment as follows is not possible. The data object cannot be + "modified. + "con_a_i = 321. + +********************************************************************** + + "Static and dynamic data objects + "Static: + "- Data object for which all attributes, including memory use, are specified + " statically by the data type. + "- Apart from reference variables, all static data objects are flat. + "Dynamic: + "- Data object for which all properties apart from the memory consumption are + " statically determined by the data type. + "- Dynamic data objects are strings and internal tables. They belong to the + " deep data objects. Structures that contain dynamic components are also + " dynamic data objects. + + "Static data object + DATA do_h_c5 TYPE c LENGTH 3. + "Dynamic data object + DATA do_i_str TYPE string. + + "Assignments + do_h_c5 = 'abc'. + + "In newer ABAP releases, the following statement shows a syntax + "warning that the value of the literal (intentionally specified + "here like this) is not an admissable value for the target type. + "Therefore, the example is provided differently to circumvent the + "syntax warning. + + "do_h_c5 = 'defghi'. + + TYPES c_l6 TYPE c LENGTH 6. + DATA some_char TYPE c_l6 VALUE 'defghi'. + "only 'def' assigned -> length and memory use do not change + do_h_c5 = some_char. + + + + "Memory consumption changes for dynamic data objects + do_i_str = `abc`. + do_i_str = `d`. + do_i_str = `efghijklmnopqrstuvwxyz`. + +********************************************************************** + + "Static type and dynamic type + "Both are data types of a reference variable (reference type) that determine + "the objects a reference variable can point to. + "Static type: For data reference variables, the static type is a data type that + " is always more general than or the same as the dynamic type. + "Dynamic type: For a reference variable, the dynamic type is always more special + " than or equal to the static type. + + "Static type + DATA dref_a_i TYPE REF TO i. "Static type is i + DATA dref_b_data TYPE REF TO data. "Static type can also be generic + + "Creating data objects to refer to + DATA do_j_i TYPE i VALUE 3. + DATA do_k_str TYPE string VALUE `hallo`. + + "Dynamic types + "Set a breakpoint here and check the Variables tab in ADT how the value and + "the information changes. + dref_a_i = REF #( do_j_i ). "Only type i possible; the dynamic type is the same + + "The dynamic type is more special than the static type (which is the generic + "type data in this case) + dref_b_data = REF #( do_j_i ). + dref_b_data = REF #( do_k_str ). + dref_b_data = REF #( dref_a_i ). + +********************************************************************** + + "Flat and deep data objects + "Flat: + "- Property of a data type, where the content of its data objects represents + " the actual work data. + "- All elementary data types except string and xstring are flat + "Deep: + "- Dynamic data objects and reference variables are deep, and they contain + " references that refer to the actual content. + "- The handling of references is implicit for dynamic data objects (strings + " and internal tables), and explicit for reference variables. + "- Structures that do not contain any deep components are flat structures. + " Structures that contain at least one deep component are deep structures. + + "Flat data object + DATA do_l_i TYPE i. + + "Flat structure + DATA: BEGIN OF struc_b_flat, + comp1 TYPE i, + comp2 TYPE c LENGTH 3, + END OF struc_b_flat. + + "Deep data object + DATA do_m_str TYPE string. + + "Deep structure + DATA: BEGIN OF struc_c_deep, + comp1 TYPE i, + comp2 TYPE c LENGTH 3, + comp3 TYPE string, "string as deep data object + comp4 TYPE string_table, "internal table as deep data object + END OF struc_c_deep. + +********************************************************************** + + "Named and unnamed data object + "Named: Data object that can be identified via a name. + "Unnamed: Data object that cannot be addressed by a name. Unnamed data + " objects are literals and anonymous data objects. + + "Named data objects + DATA do_n_i TYPE i. + CONSTANTS con_b_str TYPE string VALUE `hi`. + + "Unnamed data objects + "Literal that is output. It cannot be addressed via a dedicated name. + out->write( `I'm a literal...` ). + out->write( |\n| ). + + "Anonymous data object created using the NEW operator + "Can be addressed using reference variables or field symbols. + + DATA(dref_c_str) = NEW string( `hi` ). + + out->write( data = dref_c_str->* name = `dref_c_str->*` ). + out->write( |\n| ). + + "Anonymous data object created inline using the NEW addition to the INTO + "clause of a SELECT statement + SELECT * + FROM zdemo_abap_carr + INTO TABLE NEW @DATA(dref_d_tab) + UP TO 3 ROWS. + + out->write( data = dref_d_tab->* name = `dref_d_tab->*` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `20) Generic ABAP types for formal parameters of methods` ) ). + + "Generic data types have already been covered above. + "A generic data type is an incomplete type specification that covers multiple + "complete type specifications. + "This example demonstrates generic ABAP types in the context of formal parameters + "of methods. + "Unlike data objects, where the data type has a specific property and is always + "completely known, formal parameters and field symbols that are generically typed + "receive their complete data type only when an actual parameter is passed in a method + "call, or, regarding field symbols, when a memory area is assigned. + "In the following example, a method is called that has two importing parameters typed + "with the generic type numeric. An internal table is filled with values on whose basis + "the method is provided with actual parameters. Intentionally, various numeric values + "are included as well as values that will not work. + + TYPES: BEGIN OF str_num, + num1 TYPE REF TO data, + num2 TYPE REF TO data, + result TYPE decfloat34, + END OF str_num. + + DATA tab_num TYPE TABLE OF str_num WITH EMPTY KEY. + + tab_num = VALUE #( ( num1 = NEW i( 1 ) num2 = NEW i( 2 ) ) + ( num1 = NEW decfloat34( '1.74' ) num2 = NEW decfloat34( '4.04' ) ) + ( num1 = NEW i( 11 ) num2 = NEW decfloat34( '10.621' ) ) + ( num1 = NEW string( `Some string` ) num2 = NEW i( 2 ) ) ). + + LOOP AT tab_num REFERENCE INTO DATA(fp_ref). + TRY. + fp_ref->result = addition_with_generic_num( num1 = fp_ref->num1->* num2 = fp_ref->num2->* ). + CATCH cx_sy_dyn_call_illegal_type INTO DATA(error). + ENDTRY. + ENDLOOP. + + out->write( data = tab_num name = `tab_num` ). + out->write( |\n| ). + out->write( data = error->get_text( ) name = `error->get_text( )` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `21) Built-in data objects` ) ). + + "This example demonstrates the availability of built-in data objects in ABAP. + + "System fields are filled by the ABAP runtime framework and can be used in an ABAP + "program to query various things. + "The following example covers the values provided with sy-index and sy-tabix. + "Check both loops in the debugger and insert sy-index/sy-tabix in the + "Variables tab to see the value change. + + DATA syidx LIKE TABLE OF sy-index. + + "In DO and WHILE loops, sy-index contains the number of previous loop passes, + "including the current pass. + DO 5 TIMES. + APPEND sy-index TO syidx. + ENDDO. + + out->write( data = syidx name = `syidx` ). + out->write( |\n| ). + + DATA str_tab TYPE TABLE OF string. + + "In loops with LOOP AT, sy-tabix contains the line number in the table + "index of an index table. + LOOP AT syidx INTO DATA(wa_sy1). + APPEND |Value of processed table line: { wa_sy1 } / Table index { sy-tabix }| TO str_tab. + ENDLOOP. + + out->write( data = str_tab name = `str_tab` ). + out->write( |\n| ). + + "sy-subrc contains a return code that is set by many ABAP statements. + "In general, the value 0 means that the statement was executed without problems. + + READ TABLE syidx INDEX 6 INTO DATA(wa_sy2). + + IF sy-subrc = 0. + out->write( |Yes, the table line was found. sy-subrc value that was returned is { sy-subrc }.| ). + ELSE. + out->write( |No, the table line was not found. sy-subrc value that was returned is { sy-subrc }.| ). + ENDIF. + + out->write( |\n| ). + out->write( |\n| ). + + "The program-global constant space has the data type c, length 1, and contains a blank character. + "In the following example, the table lines are concatenated into a string, separated by a blank. + + DATA ctab TYPE TABLE OF c. + + ctab = VALUE #( ( 'a' ) ( 'b' ) ( 'c' ) ). + + DATA c_f TYPE c LENGTH 10. + + CONCATENATE LINES OF ctab INTO c_f SEPARATED BY space. + + out->write( data = c_f name = `c_f` ). + out->write( |\n| ). + + "Self-reference me + "Within the implementation of each instance method, an implicitly created local + "reference variable called me is available at runtime, which points to the instance + "in which the method is currently being executed. + "me is handled like a local constant, which means that the value of me cannot be + "changed in an instance method. The static type of me is the class in which the + "instance method is implemented. + "In the following example, an instance of this class is created. Then a method is + "called, and a string is returned. The method implementation includes a data object + "that is (intentionally) declared with the same name of the instance attribute + "available in the private section of the class (text). Using the self-reference me + "(me->text), you can explicitly refer to the instance attribute. Just 'text' means + "referring to the data object locally declared in the method. + + DATA(oref) = NEW zcl_demo_abap_dtype_dobj( ). + + cl_text = 'Hallo'. + + DATA(res_str) = oref->adapt_text( ). + + out->write( data = res_str name = `res_str` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `22) Declaration context` ) ). + + "The purpose of this example is to emphasize the importance of where + "data objects are decalred. The example deals with local declarations + "and control structures. A data object of type i is declared within + "the control structure. However, it is valid globally in this ABAP + "program, i.e. in the whole method here. + "The value of the data object is not set to 10 in each loop pass. + "Furthermore, the data object can also be used in the second DO loop. + + DO 10 TIMES. + DATA number TYPE i VALUE 10. + number = number + 1. + ENDDO. + + out->write( data = number name = `number` ). + out->write( |\n| ). + + DO 10 TIMES. + number = number + 1. + ENDDO. + + out->write( data = number name = `number` ). + out->write( |\n| ). + + "Comparing the behavior with a data object declared inline. + "In each loop pass, the value object is set to 10. Therefore, + "the value is 11 after exiting the DO loop. + "The data object declared inline can also be used in the second DO loop. + "The value of the data object is 21 when exiting the loop. + "That means you should be careful when using variables declared inline + "in control structures. It should only be used within the control structure + "in which it is declared and not beyond. + + DO 10 TIMES. + DATA(number_B) = 10. + number_B = number_B + 1. + ENDDO. + + out->write( data = number_b name = `number_b` ). + out->write( |\n| ). + + DO 10 TIMES. + number_b = number_b + 1. + ENDDO. + out->write( data = number_b name = `number_b` ). + out->write( |\n| ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `23) Enumerated Types and Objects` ) ). + + "Examples for enumerated types and objects are contained in + "separate methods. Check the comments there. + "Note that the output that is created in the methods is combined + "in a string (table). + + "The enum_meth_params method demonstrates the use of enumerated objects + "when comparing them with the respective enumeration constants to branch + "to some functionality (CASE statement in the method implementation). + "In typing the formal parameter this way, it is guaranteed that only + "enumerated values of the enumerated type can be passed to the parameter. + DATA enum_var1 TYPE t_enum VALUE a. + DATA(output_for_enum_var1) = enum_meth_params( enum_var1 ). + out->write( data = output_for_enum_var1 name = `output_for_enum_var1` ). + out->write( |\n| ). + + DATA enum_var2 TYPE t_enum VALUE b. + DATA(output_for_enum_var2) = enum_meth_params( enum_var2 ). + out->write( data = output_for_enum_var2 name = `output_for_enum_var2` ). + out->write( |\n| ). + + DATA enum_var3 TYPE t_enum VALUE d. + DATA(output_for_enum_var3) = enum_meth_params( enum_var3 ). + out->write( data = output_for_enum_var3 name = `output_for_enum_var3` ). + out->write( |\n| ). + + "The enum_processing method demonstrates various ways of processing enumerated + "objects. + DATA(output_for_enum_processing) = enum_processing( ). + out->write( data = output_for_enum_processing name = `output_for_enum_processing` ). + out->write( |\n| ). + + "The rtti_enum method demonstrates the RTTI class cl_abap_enumdescr. + DATA(output_for_rtti_enum) = rtti_enum( ). + out->write( data = output_for_rtti_enum name = `output_for_rtti_enum` ). + + ENDMETHOD. + + METHOD rtti_enum. + + DATA enum1 TYPE t_enum. + enum1 = d. + + DATA enum2 TYPE t_enum_base. + enum2 = f. + + "Return type information + DATA(enum_descr) = CAST cl_abap_enumdescr( + cl_abap_typedescr=>describe_by_data( enum1 ) ). + + APPEND `------ Properties for enum1 ------` TO output. + + APPEND ` kind: ` && enum_descr->kind TO output. + APPEND ` type_kind: ` && enum_descr->type_kind TO output. + APPEND ` base_type_kind: ` && enum_descr->base_type_kind TO output. + + DATA mem_string TYPE string. + + "For output purposes, the table content is put in a string. + "Note the object component selector -> when reading into data reference variables + "and accessing componts. You can also use the dereferencing operator followed by the + "structure component selector ref->*-comp. + LOOP AT enum_descr->members REFERENCE INTO DATA(ref_en1). + mem_string = mem_string && ` / Name: ` && ref_en1->name && `; Value: ` && ref_en1->*-value. + ENDLOOP. + + REPLACE FIRST OCCURRENCE OF PCRE `/\s` IN mem_string WITH ``. + APPEND ` members:` && mem_string TO output. + CLEAR mem_string. + + enum_descr = CAST cl_abap_enumdescr( + cl_abap_typedescr=>describe_by_data( enum2 ) ). + + APPEND `------ Properties for enum2 ------` TO output. + APPEND ` kind: ` && enum_descr->kind TO output. + APPEND ` type_kind: ` && enum_descr->type_kind TO output. + APPEND ` base_type_kind: ` && enum_descr->base_type_kind TO output. + + "For output purposes, the table content is put in a string. + LOOP AT enum_descr->members REFERENCE INTO DATA(ref_en2). + mem_string = mem_string && ` / Name: ` && ref_en2->name && `; Value: ` && ref_en2->value. + ENDLOOP. + + REPLACE FIRST OCCURRENCE OF PCRE `/\s` IN mem_string WITH ``. + APPEND ` members:` && mem_string TO output. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_dtype_dobj.clas.xml b/src/zcl_demo_abap_dtype_dobj.clas.xml new file mode 100644 index 0000000..3de7131 --- /dev/null +++ b/src/zcl_demo_abap_dtype_dobj.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_DTYPE_DOBJ + E + ABAP cheat sheet: Data Types and Data Objects + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_dynamic_prog.clas.abap b/src/zcl_demo_abap_dynamic_prog.clas.abap new file mode 100644 index 0000000..8966f99 --- /dev/null +++ b/src/zcl_demo_abap_dynamic_prog.clas.abap @@ -0,0 +1,3124 @@ +*********************************************************************** +* +* ABAP cheat sheet: Dynamic programming +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntax options and concepts related +* to dynamic programming. +* - Topics covered: Field symbols and data references (both as supporting +* elements for dynamic programming), dynamic ABAP syntax components, +* runtime type services (RTTS), i. e. runtime type identification (RTTI) +* and runtime type creation (RTTC) +* - To provide a "real" dynamic determination at runtime for several code +* examples in this class, the example class includes local classes +* in the CCIMP include (local types tab in ADT) whose methods return +* character-like content to be used in the ABAP statements. The content +* is predefined in these classes but the content that is actually used +* in the end is random. +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP development tools for Eclipse (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, refer to the +* notes included in the class as comments or refer to the respective +* topic in the ABAP Keyword Documentation. +* - Due to the amount of console output, the examples contain numbers +* (e.g. 1) ..., 2) ..., 3) ...) for the individual example sections. +* Also, the variable name is displayed in most cases. So to find +* the relevant output in the console easier and faster, just search +* for the number/variable name in the console (CTRL+F in the console) +* or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

    ABAP cheat sheet: Dynamic programming

    +"! Example to demonstrate syntax related to dynamic programming.
    Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_dynamic_prog DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + + CLASS-METHODS: + class_constructor. +ENDCLASS. + + + +CLASS zcl_demo_abap_dynamic_prog IMPLEMENTATION. + + + METHOD class_constructor. + "Filling demo database tables. + zcl_demo_abap_aux=>fill_dbtabs( ). + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + out->write( |ABAP Cheat Sheet Example: Dynamic Programming\n\n| ). + +********************************************************************** + + out->write( |Excursion: Field Symbols\n\n| ). + out->write( |1) Declaring Field Symbols\n\n| ). + + "Some data declarations and type definitions used further down + DATA: str TYPE string. + + TYPES: BEGIN OF struc, "Structured data type + num1 TYPE i, + num2 TYPE i, + END OF struc, + tab_type TYPE TABLE OF struc. "Internal table type + + "Field symbol declarations + "- Name of the field symbol goes between angle brackets + "- Type: either a complete data type or a generic type. + + "Complete types + "Here, a chained statement with a colon is used. + FIELD-SYMBOLS: TYPE i, + TYPE zdemo_abap_flsch, + TYPE LINE OF tab_type, + LIKE str. + + "Generic types + "There are plenty of options for generic ABAP types. Check the + "keyword docu. + FIELD-SYMBOLS TYPE c. "Text field with a generic length + FIELD-SYMBOLS TYPE csequence. "Text-like (c, string) + FIELD-SYMBOLS TYPE data. "Any data type + FIELD-SYMBOLS TYPE ANY TABLE. "Internal table with any table type + + "Declaring field symbols inline + "Prominent use case: Inline declaration of a field symbol for an internal table + "following ASSIGNING. + DATA demo_itab TYPE TABLE OF zdemo_abap_flsch WITH EMPTY KEY. + + LOOP AT demo_itab ASSIGNING FIELD-SYMBOL(). + ... + ENDLOOP. + + out->write( `No output for this section. You can set a break point and check the content of data objects in the debugger.` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `2) Assigning Data Objects to Field Symbols` ) ). + + "ASSIGN statements assigns the memory area of a data object to a field symbol. + "Once the memory area is assigned, you can work with the content. + "You can also assign a particular component of a structure. Either you + "specify the position of the component or the name of the component. + + "Data objects. + DATA: num_a TYPE i, + struc_a TYPE zdemo_abap_fli, + tab_a TYPE string_table. + + "Field symbols with complete types + FIELD-SYMBOLS: TYPE i, + TYPE zdemo_abap_fli, + TYPE string_table. + + "Field symbols with generic types, see more examples bwloe + FIELD-SYMBOLS TYPE data. + FIELD-SYMBOLS TYPE ANY TABLE. + + "Assigning data objects to field symbols + ASSIGN num_a TO . + ASSIGN struc_a TO . + ASSIGN tab_a TO . + ASSIGN: num_a TO , + struc_a TO , + tab_a TO , + tab_a TO . + + "Inline declaration is possible, too. The field symbol is implicitly typed + "with the generic type data. + ASSIGN num_a TO FIELD-SYMBOL(). + + "Generic typing + "- Generic types are available with which formal parameters of methods or field symbols + " can be specified. + "- At runtime, the actual data type is copied from the assigned actual parameter or + " memory area, i.e. they receive the complete data type only when an actual parameter + " is passed or a memory area is assigned. + + FIELD-SYMBOLS: + "Any data type + TYPE data, + TYPE any, + "Any data type can be assigned. Restrictions for formal parameters and 'data': no + "numeric functions, no description functions, and no arithmetic expressions can be + "passed to these parameters. However, you can bypass the restriction by applying the + "CONV operator for the actual parameter. + + "Character-like types + TYPE c, "Text field with a generic length + TYPE clike, "Character-like (c, n, string, d, t and character-like flat structures) + TYPE csequence, "Text-like (c, string) + TYPE n, "Numeric text with generic length + TYPE x, "Byte field with generic length + TYPE xsequence, "Byte-like (x, xstring) + + "Numeric types + TYPE decfloat, "decfloat16, decfloat34) + TYPE numeric, "Numeric ((b, s), i, int8, p, decfloat16, decfloat34, f) +

    TYPE p, "Packed number (generic length and number of decimal places) + + "Internal table types + TYPE ANY TABLE, "Internal table with any table type + TYPE HASHED TABLE, + TYPE INDEX TABLE, + TYPE SORTED TABLE, + TYPE STANDARD TABLE, + TYPE table, "Standard table + + "Other types + TYPE simple, "Elementary data type including enumerated types and + "structured types with exclusively character-like flat components + TYPE REF TO object. "object can only be specified after REF TO; can point to any object + + "Data objects to work with + DATA: BEGIN OF s, + c3 TYPE c LENGTH 3, + c10 TYPE c LENGTH 10, + n4 TYPE n LENGTH 4, + str TYPE string, + time TYPE t, + date TYPE d, + dec16 TYPE decfloat16, + dec34 TYPE decfloat34, + int TYPE i, + pl4d2 TYPE p LENGTH 4 DECIMALS 2, + tab_std TYPE STANDARD TABLE OF string WITH EMPTY KEY, + tab_so TYPE SORTED TABLE OF string WITH NON-UNIQUE KEY table_line, + tab_ha TYPE HASHED TABLE OF string WITH UNIQUE KEY table_line, + xl1 TYPE x LENGTH 1, + xstr TYPE xstring, + structure TYPE zdemo_abap_carr, "character-like flat structure + oref TYPE REF TO object, + END OF s. + + "The following static ASSIGN statements demonstrate various assignments + "Note: + "- The statements commented out show impossible assignments. + "- If a static assignment is not successful, sy-subrc is not set and no + " memory area is assigned. Dynamic assignments, however, set the value. + + "----- Any data type ----- + ASSIGN s-c3 TO . + ASSIGN s-time TO . + ASSIGN s-tab_std TO . + ASSIGN s-xstr TO . + ASSIGN s-pl4d2 TO . + ASSIGN s-date TO . + + "----- Character-like types ----- + ASSIGN s-c3 TO . + ASSIGN s-c10 TO . + "ASSIGN s-str TO . + + ASSIGN s-c10 TO . + ASSIGN s-str TO . + ASSIGN s-n4 TO . + ASSIGN s-date TO . + ASSIGN s-time TO . + ASSIGN s-structure TO . + + ASSIGN s-c10 TO . + ASSIGN s-str TO . + "ASSIGN s-n4 TO . + + ASSIGN s-n4 TO . + "ASSIGN s-int TO . + "ASSIGN s-time TO . + + ASSIGN s-xl1 TO . + "ASSIGN s-xstr TO . + + ASSIGN s-xl1 TO . + ASSIGN s-xstr TO . + + "----- Numeric types ----- + ASSIGN s-dec16 TO . + ASSIGN s-dec34 TO . + ASSIGN s-int TO . + ASSIGN s-pl4d2 TO . + "ASSIGN s-n4 TO . + + ASSIGN s-dec16 TO . + ASSIGN s-dec34 TO . + + ASSIGN s-pl4d2 TO

    . + "ASSIGN s-dec34 TO

    . + + "----- Internal table types ----- + ASSIGN s-tab_std TO . + ASSIGN s-tab_so TO . + ASSIGN s-tab_ha TO . + + ASSIGN s-tab_std TO . + ASSIGN s-tab_so TO . + "ASSIGN s-tab_ha TO . + + "ASSIGN s-tab_std TO . + ASSIGN s-tab_so TO . + "ASSIGN s-tab_ha TO . + + ASSIGN s-tab_std TO . + ASSIGN s-tab_std TO

    . + "ASSIGN s-tab_so TO . + "ASSIGN s-tab_so TO
    . + "ASSIGN s-tab_ha TO . + "ASSIGN s-tab_ha TO
    . + + "ASSIGN s-tab_std TO . + "ASSIGN s-tab_so TO . + ASSIGN s-tab_ha TO . + + "----- Other types ----- + ASSIGN s-c10 TO . + ASSIGN s-str TO . + ASSIGN s-dec34 TO . + ASSIGN s-date TO . + ASSIGN s-structure TO . + ASSIGN s-xl1 TO . + "ASSIGN s-tab_ha TO . + + ASSIGN s-oref TO . + s-oref = NEW zcl_demo_abap_objects( ). + ASSIGN s-oref TO . + s-oref = cl_abap_random_int=>create( ). + ASSIGN s-oref TO . + + out->write( `No output for this section. You can set a break point and check the content of data objects in the debugger.` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `3) Checking Field Symbol Assignment` ) ). + + "When working with field symbols, you should make sure that they are + "assigned. Otherwise, a runtime error occurs. + "You can make use of a logical expression with IS [NOT] ASSIGNED. + "The example includes data object declarations. One data object is + "assigned, the other is not. Consequently, the expression is + "true for the one and false for the other. + + DATA num_b TYPE i VALUE 123. + + FIELD-SYMBOLS: TYPE i, + TYPE string. + + ASSIGN num_b TO . + + IF IS ASSIGNED. + out->write( `Field symbol is assigned.` ). + ELSE. + out->write( `Field symbol not assigned.` ). + ENDIF. + + out->write( |\n| ). + + IF IS ASSIGNED. + out->write( `Field symbol is assigned.` ). + ELSE. + out->write( `Field symbol is not assigned.` ). + ENDIF. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `4) Unassigning Data Objects from Field Symbols` ) ). + + "If you use an unassigned field symbol, an exception is raised. Before + "using it, you can check the assignment with the following logical + "expression. The statement is true if the field symbol is assigned. + "Using the statement UNASSIGN, you can explicitly remove the assignment + "of the field symbol. + + DATA num_c TYPE i VALUE 123. + + FIELD-SYMBOLS: TYPE i. + + ASSIGN num_c TO . + + IF IS ASSIGNED. + out->write( `1. Field symbol is assigned.` ). + ELSE. + out->write( `1. Field symbol is not assigned.` ). + ENDIF. + + out->write( |\n| ). + + UNASSIGN . + + IF IS ASSIGNED. + out->write( `2. Field symbol is assigned.` ). + ELSE. + out->write( `2. Field symbol is not assigned.` ). + ENDIF. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `5) Type Casting with Field Symbols` ) ). + + "The example demonstrates the CASTING addition. Various additions after + "CASTING are possible. + + TYPES type_d_l9 TYPE c LENGTH 9. + + DATA: dobj_d_l5 TYPE c LENGTH 5, + dobj_d_l10 TYPE c LENGTH 10 VALUE '1234567890', + type_name_d TYPE string VALUE 'TYPE_D_L9'. + + FIELD-SYMBOLS: TYPE data, + TYPE type_d_l9. + + "Casting to a statically, completely specified type + "CASTING addition without any more additions: Field symbol inherits + "the data type of the data object. The field symbol must be either + "completely typed or with one of the generic built-in ABAP types + "c, n, p, or x. The other field symbol declared in the example + "cannot be used. + ASSIGN dobj_d_l10 TO CASTING. + + out->write( data = name = `` ). + out->write( |\n| ). + + ASSIGN dobj_d_l10 TO CASTING TYPE type_d_l9. + + out->write( data = name = `` ). + out->write( |\n| ). + + "Casting to a generic type + ASSIGN dobj_d_l10 TO CASTING TYPE c. + + out->write( data = name = `` ). + out->write( |\n| ). + + "Casting to a static field type + ASSIGN dobj_d_l10 TO CASTING LIKE dobj_d_l5. + + out->write( data = name = `` ). + out->write( |\n| ). + + "Casting to a dynamic field type + ASSIGN dobj_d_l10 TO CASTING LIKE . + + out->write( data = name = `` ). + out->write( |\n| ). + + "Anticipating dynamic specification of data types + "for the CASTING addition. + "The type name is specified as a character-like data + "object within parentheses. + ASSIGN dobj_d_l10 TO CASTING TYPE (type_name_d). + + out->write( data = name = `` ). + out->write( |\n| ). + + "Anticipating RTTS + "A type description object is created which can be + "specified after the TYPE HANDLE additions. + DATA(sometype) = CAST cl_abap_datadescr( + cl_abap_typedescr=>describe_by_name( 'TYPE_D_L9' ) ). + ASSIGN dobj_d_l10 TO CASTING TYPE HANDLE sometype. + + out->write( data = name = `` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `6) Addressing Field Symbols` ) ). + + "The example includes multiple data objects that are assigned to field + "symbols. It is demonstrated that field symbols are addressed in various + "occasions. Among them: Changing the value of data objects assigned to + "field symbols, the use of field symbols in expressions, structures, and + "internal tables. + + DATA: num_e TYPE i VALUE 456, + struc_e TYPE zdemo_abap_carr, + tab_e TYPE TABLE OF zdemo_abap_carr WITH EMPTY KEY. + + SELECT SINGLE * + FROM zdemo_abap_carr + WHERE carrid = 'LH' + INTO @struc_e. + + FIELD-SYMBOLS: TYPE i, + TYPE zdemo_abap_carr, + LIKE tab_e, + TYPE ANY TABLE. + + "Without an assignment, this would result in a runtime error: + " = 1. + + ASSIGN num_e TO . + ASSIGN struc_e TO . + ASSIGN tab_e TO . + ASSIGN tab_e TO . + + "Changing values + = 789. + + out->write( data = name = `` ). + out->write( |\n| ). + out->write( data = num_e name = `num_e` ). + out->write( |\n| ). + + "Use in expressions + DATA(calc_e) = + 211. + + out->write( data = calc_e name = `calc_e` ). + out->write( |\n| ). + + IF < 1000. + out->write( `The value of is less than 1000` ). + ELSE. + out->write( `The value of is greater than 1000` ). + ENDIF. + + out->write( |\n| ). + out->write( |\n| ). + + "Structure + out->write( data = name = `` ). + out->write( |\n| ). + + DATA(comp_e1) = -carrid. + + out->write( data = comp_e1 name = `comp_e1` ). + out->write( |\n| ). + + -url = 'www.lh.com'. + + out->write( data = -url name = `-url` ). + out->write( |\n| ). + + "Internal table + SELECT * + FROM zdemo_abap_carr + ORDER BY carrid + INTO TABLE @ + UP TO 3 ROWS. + + out->write( data = name = `` ). + out->write( |\n| ). + + TRY. + DATA(comp_e2) = [ 2 ]-carrname. + out->write( data = comp_e2 name = `comp_e2` ). + CATCH cx_sy_itab_line_not_found INTO DATA(error_e). + ENDTRY. + + out->write( |\n| ). + + SELECT * + FROM zdemo_abap_carr + ORDER BY carrid + INTO TABLE @ + UP TO 3 ROWS. + + out->write( data = name = `` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `7) Using Field Symbols when Processing Internal Tables` ) ). + + "By using field symbols in the context of loops across internal tables, + "you can avoid an actual copying of content to a work area during + "the loop. + "The example includes multiple loops. First, internal tables are + "declared. One of them is filled. Then, field symbols are declared to + "which data objects are assigned. In the first loop, a previously + "declared field symbol is used as target area to hold the table line + "that is processed. In the course of the loop, some values are changed. The + "components are accessed using the component selector '-'. At the end of + "the loop, another internal table is filled using the currently + "processed line for the second loop. The second loop (the loop is + "carried out based on + "an internal table a field symbol points to) uses a directly declared + "field symbol using ASSIGNING FIELD-SYMBOL(<...>). Also here, some + "values are changed and another internal table is filled. This table + "is of a generic type. At the end, this internal table is output, too. + + DATA: tab_f1 TYPE TABLE OF zdemo_abap_fli WITH EMPTY KEY, + tab_f2 LIKE tab_f1, + tab_f3 LIKE tab_f1. + + SELECT * + FROM zdemo_abap_fli + ORDER BY carrid + INTO TABLE @tab_f1 + UP TO 3 ROWS. + + FIELD-SYMBOLS: LIKE LINE OF tab_f1, + LIKE tab_f1, + TYPE ANY TABLE. + + ASSIGN tab_f2 TO . + ASSIGN tab_f3 TO . + + LOOP AT tab_f1 ASSIGNING . + -connid = '99'. + -fldate = cl_abap_context_info=>get_system_date( ). + -price = -price + 100. + -currency = 'EUR'. + CLEAR: -paymentsum, + -seatsocc, + -seatsocc_b, + -seatsocc_f. + + "Filling another itab + = VALUE #( BASE ( ) ). + ENDLOOP. + + out->write( data = tab_f1 name = `tab_f1` ). + out->write( |\n| ). + + "The following example shows a field symbol declared inline. + LOOP AT ASSIGNING FIELD-SYMBOL(). + -connid = '100'. + -fldate = cl_abap_context_info=>get_system_date( ) + 1. + -price = -price - 50. + -currency = 'USD'. + + "Filling another itab + = VALUE #( BASE ( ) ). + ENDLOOP. + + out->write( data = name = `` ). + out->write( |\n| ). + out->write( data = name = `` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `8) Structure Assigned to Field Symbol: Accessing Structure Components` ) ). + + "In this example, all components of a structure are processed using + "field symbols and an ASSIGN COMPONENT ... OF STRUCTURE ... statement. + "First, a field symbol is declared with a generic type. A structure is + "filled with values from a demo table. The structure is assigned to the + "field symbol. Using a DO loop, all components are processed. The + "sy-index value represents the position of the component in the + "structure. Once all components have been processed (i. e. if sy-subrc + "does not return '0' for a sy-index value), the loop is exited. The output + "shows all components and their values. + "See more examples for accessing structure components below. + + FIELD-SYMBOLS TYPE data. + + DATA comp_tab TYPE string_table. + + SELECT SINGLE carrid, carrname, currcode, url + FROM zdemo_abap_carr + WHERE carrid = 'LH' + INTO @DATA(struct). + + FIELD-SYMBOLS TYPE data. + + ASSIGN struct TO . + + DO. + "sy-index represents the position of a structure component + ASSIGN -(sy-index) TO . + + "Old syntax + "ASSIGN COMPONENT sy-index OF STRUCTURE TO . + + IF sy-subrc <> 0. + "If all components are processed, the loop is exited. + EXIT. + ELSE. + out->write( |sy-index: { sy-index }, component content:| ). + out->write( ). + out->write( |\n| ). + ENDIF. + + ENDDO. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Data References` ) ). + out->write( |9) Declaring Data References\n\n| ). + + "Like field symbols, data reference variables can be declared with both + "a complete and a generic data type using DATA statements and the + "addition REF TO. The type after REF TO represents the static data type. + "The example shows multiple ways of declaring a data reference variable + "using both complete and generic data types. + + DATA: some_string TYPE string. + + TYPES: ref_type TYPE REF TO zdemo_abap_flsch. + + DATA: ref_a1 TYPE REF TO i, "Complete data type + ref_a2 TYPE REF TO zdemo_abap_carr, "Complete data type + ref_a3 LIKE REF TO some_string, + ref_a4 LIKE ref_a1, + ref_a5 TYPE ref_type, + ref_a6 TYPE REF TO data. "Generic data type + + out->write( `No output for this section. You can set a break point and check the content of data objects in the debugger.` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `10) Creating Data References ` && + `to Existing Data Objects` ) ). + + "The example includes data reference variables with both complete and + "generic type. When using the REF operator, the '#' sign means that the + "type is derived from the data object. You can also explicitly specify + "the data type after REF before the parenthesis. Within the parentheses, + "you can provide a value. + + "Declaring data object + DATA number_b TYPE i VALUE 5. + + "Declaring data reference variables + DATA ref_b1 TYPE REF TO i. + DATA ref_data_b TYPE REF TO data. + + "Creating data references to data objects. + "The '#' sign means that the type is derived from the data object. + ref_b1 = REF #( number_b ). + ref_data_b = REF #( number_b ). + + "You can also use inline declarations to omit the explicit declaration. + DATA(ref_b2) = REF #( number_b ). + + "You can explicitly specify the data type after REF. + "DATA(ref_b3) = REF #( g ). + + out->write( `No output for this section. You can set a break point and check the content of data objects in the debugger.` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `11) Dynamically Creating Data Objects at Runtime Using Static Type Definitions` ) ). + + "The example code shows the creation of anonymous data objects. They + "can be created using the statement CREATE DATA, the instance operator + "NEW, or the addition NEW of the INTO clause in a SELECT statement. + "A data reference variable is expected when anonymous objects are + "declared. They cannot be addressed by a name (hence anonymous). + "Note: + "- The examples cover static type definitions. As shown further down, + " there are options to dynamically specify the type definitions. + "- To output the content of the data reference variables, they + " must be dereferenced first. The details are shown further down. + + "CREATE DATA statements + "Note that there are many additions available. The examples show a selection. + "Behind TYPE and LIKE, the syntax offers the same possibilities as the DATA statement. + + "Creating an anonymous data object with an implicit type. + "If neither of the additions TYPE or LIKE are specified, the data reference variable + "must be completely typed. + DATA dref_c1 TYPE REF TO string. + CREATE DATA dref_c1. + + "Creating anonymous data objects with explicit data type specification. + "Data reference variable with a generic type to be used in the following examples + "for the anonymous data object. + DATA dref_c2 TYPE REF TO data. + + "Elementary, built-in ABAP type + CREATE DATA dref_c2 TYPE p LENGTH 8 DECIMALS 3. + + "Anomyous internal table ... + "using the LIKE addition to refer to an existing internal table + DATA itab_c TYPE TABLE OF zdemo_abap_carr. + CREATE DATA dref_c2 LIKE itab_c. + + "by specifying the entire table type + CREATE DATA dref_c2 TYPE HASHED TABLE OF zdemo_abap_carr WITH UNIQUE KEY carrid. + + "Anonymous structures + CREATE DATA dref_c2 LIKE LINE OF itab_c. + CREATE DATA dref_c2 TYPE zdemo_abap_carr. + + "Creating reference variable + TYPES elem_type_c TYPE c LENGTH 3. + CREATE DATA dref_c2 TYPE REF TO elem_type_c. + + "NEW operator + "- Works like CREATE DATA dref TYPE type statements and can be used in general + " expression positions. + "- Allows to assign values to the new anonymous data objects in parentheses + + "Creating data reference variables + DATA: dref_c3 TYPE REF TO i, + dref_c4 TYPE REF TO data. + + "# character after NEW if the data type can be identified completely + "instead of the explicit type specification (only non-generic types) + dref_c3 = NEW #( 123 ). + dref_c3 = NEW i( 456 ). + dref_c4 = NEW zdemo_abap_carr( ). "not assigning any values + dref_c4 = NEW string( `hi` ). + + "Creating anonymous data objects inline + "In doing so, you can omit a prior declaration of a variable. + DATA(dref_c5) = NEW i( 789 ). + DATA(dref_c6) = NEW zdemo_abap_carr( carrid = 'AB' carrname = 'AB Airlines' ). + + "ABAP SQL SELECT statements + "Using the NEW addition in the INTO clause, an anonymous data object + "can be created in place. + SELECT * + FROM zdemo_abap_carr + INTO TABLE NEW @DATA(dref_c7) "Internal table + UP TO 3 ROWS. + + SELECT SINGLE * + FROM zdemo_abap_carr + WHERE carrid = 'LH' + INTO NEW @DATA(dref_c8). "Structure + + out->write( data = dref_c6->* name = `dref_c6->*` ). + out->write( |\n| ). + out->write( data = dref_c7->* name = `dref_c7->*` ). + out->write( |\n| ). + out->write( data = dref_c8->* name = `dref_c8->*` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `12) Data References and Assignments` ) ). + + "Regarding the assignment, note that static types of both data + "reference variables must be compatible. As a result of an assignment, + "both the target reference variable and the source reference variable + "point to the same data object. + "Upcast/downcasts: For an assignment to work, the basic rule applies: + "The static type of the target reference variable must be more general + "than or the same as the dynamic type of the source reference variable. + "In the example below: + "Upcast: The target data reference variable is of generic type, the + "source variable is of complete type. The assignment is done with the + "assignment operator '='. + "Downcast: The target data reference variable is of complete type, the + "source variable is of generic type. The assignment is done with casting + "operators, either with the constructor operator CAST or the older ?=. + + "Declaring data reference variables + DATA ref_d1 TYPE REF TO i. + DATA ref_d2 TYPE REF TO i. + + ref_d1 = NEW #( 789 ). + + "Assigning data reference + ref_d2 = ref_d1. + + "Casting + "Complete type + DATA(ref_d3) = NEW i( 321 ). + + "Generic type + DATA ref_data_d1 TYPE REF TO data. + + "Upcast + ref_data_d1 = ref_d3. + + "Downcasts + DATA ref_d5 TYPE REF TO i. + + "Generic type + DATA ref_data_d2 TYPE REF TO data. + + ref_data_d2 = NEW i( 654 ). + + ref_d5 = CAST #( ref_data_d2 ). + + ref_d5 ?= ref_data_d2. + + out->write( data = ref_d2->* name = `ref_d2->*` ). + out->write( |\n| ). + out->write( data = ref_data_d1->* name = `ref_data_d1->*` ). + out->write( |\n| ). + out->write( data = ref_d5->* name = `ref_d5->*` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `13) Addressing Data References ` ) ). + + "Before addressing the content of data objects a data reference points + "to, you must dereference data reference variables. Use the + "dereferencing operator ->*. + "The example includes multiple occasions in which data reference are + "addressed: Changing the content of a referenced data object, the use in + "logical expressions, structures, and internal tables. + + "Creating data reference variables and assigning values + DATA(ref_e1) = NEW i( 1 ). + DATA(ref_e2) = NEW zdemo_abap_carr( carrid = 'LH' + carrname = 'Lufthansa' ). + + "Generic type + DATA ref_data_e TYPE REF TO data. + + "Copying reference + ref_data_e = ref_e1. + + "Addressing + "Variable receives the content. + DATA(some_num) = ref_e1->*. + + out->write( data = ref_e1->* name = `ref_e1->*` ). + out->write( |\n| ). + + "Content of referenced data object is changed + ref_e1->* = 10. + + out->write( data = ref_e1->* name = `ref_e1->*` ). + out->write( |\n| ). + + "Data reference used in a logical expression + IF ref_e1->* > 5. + out->write( `The value of ref_e1 is greater than 5.` ). + ELSE. + out->write( `The value of ref_e1 is lower than 5.` ). + ENDIF. + + out->write( |\n| ). + + "Dereferenced generic type + DATA(calc) = 1 + ref_data_e->*. + + out->write( data = calc name = `calc` ). + out->write( |\n| ). + + "Complete structure + DATA(struc) = ref_e2->*. + + out->write( data = ref_e2->* name = `ref_e2->*` ). + out->write( |\n| ). + + "Individual structure component + "When dereferencing a data reference variable that has a structured + "data type, you can use the component selector -> to address individual components. + DATA(carrid) = ref_e2->carrid. + + ref_e2->carrid = 'UA'. + + out->write( data = ref_e2->carrid name = `ref_e2->carrid` ). + out->write( |\n| ). + + "The following syntax also works (dereferencing operator and the component selector). + ref_e2->*-carrname = 'United Airlines'. + + out->write( data = ref_e2->*-carrname name = `ref_e2->*-carrname` ). + out->write( |\n| ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `14) Checking if Data Reference ` && + `Can Be Dereferenced` ) ). + + "You can check if a data reference can be dereferenced by using + "a logical expression with IS [NOT] BOUND. + "The example shows both a data reference that is bound and not bound. + + DATA(ref_f1) = NEW string( `hello` ). + DATA ref_f2 TYPE REF TO i. + + out->write( `IF statement:` ). + IF ref_f1 IS BOUND. + out->write( `ref_f1 is bound.` ). + ELSE. + out->write( `ref_f1 is not bound.` ). + ENDIF. + + out->write( |\n| ). + out->write( `COND operator:` ). + DATA(is_bound) = COND #( WHEN ref_f1 IS BOUND THEN `ref_f1 is bound.` ELSE `ref_f1 is not bound.` ). + out->write( is_bound ). + out->write( |\n| ). + + out->write( `IF statement:` ). + IF ref_f2 IS BOUND. + out->write( `ref_f2 is bound.` ). + ELSE. + out->write( `ref_f2 is not bound.` ). + ENDIF. + + out->write( |\n| ). + out->write( `COND operator:` ). + is_bound = COND #( WHEN ref_f2 IS BOUND THEN `ref_f2 is bound.` ELSE `ref_f2 is not bound.` ). + out->write( is_bound ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `15) Explicitly Removing a Reference` ) ). + + "Note that the garbage collector takes care of removing the references + "automatically once the data is not used any more by a reference. + + DATA(ref_g1) = NEW string( `hello` ). + + IF ref_g1 IS INITIAL. + out->write( `Before CLEAR: ref_g1 is initial.` ). + ELSE. + out->write( `Before CLEAR: ref_g1 is not intial.` ). + ENDIF. + + out->write( |\n| ). + + IF ref_g1 IS BOUND. + out->write( `Before CLEAR: ref_g1 is bound.` ). + ELSE. + out->write( `Before CLEAR: ref_g1 is not bound.` ). + ENDIF. + + out->write( |\n| ). + + CLEAR ref_g1. + + IF ref_g1 IS INITIAL. + out->write( `After CLEAR: ref_g1 is initial.` ). + ELSE. + out->write( `After CLEAR: ref_g1 is not initial.` ). + ENDIF. + + out->write( |\n| ). + + IF ref_g1 IS BOUND. + out->write( `After CLEAR: ref_g1 is bound.` ). + ELSE. + out->write( `After CLEAR: ref_g1 is not bound.` ). + ENDIF. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `16) Overwriting Data Reference Variables` ) ). + + "A data reference variable is overwritten if a new object is created + "with a data reference variable already pointing to a data object + + DATA ref_h1 TYPE REF TO data. + + ref_h1 = NEW i( 111 ). + + out->write( data = ref_h1->* name = `ref_h1->*` ). + out->write( |\n| ). + + ref_h1 = NEW string( `ref_h1 overwritten.` ). + + out->write( data = ref_h1->* name = `ref_h1->*` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `17) Retaining Data References` ) ). + + "Storing data reference variables in an internal table using + "TYPE TABLE OF REF TO prevents the overwriting. + "The example demonstrates that three data references are created with + "the same reference variable in the course of a DO loop. There, the data + "reference is overwritten. However, due to saving the data reference + "variable in an internal table, the content is preserved. + + DATA: ref_data_i TYPE REF TO data, + itab_i TYPE TABLE OF REF TO data, + number_i TYPE i VALUE 0. + + DO 3 TIMES. + "Adding up 1 to demonstrate a changed data object + number_i += 1. + + "Creating a data reference and assigning value + "In the course of the loop, the variable is overwritten. + ref_data_i = NEW i( number_i ). + + "Adding the reference to an internal table + itab_i = VALUE #( BASE itab_i ( ref_data_i ) ). + ENDDO. + + out->write( data = itab_i name = `itab_i` ). + out->write( |\n| ). + out->write( data = `The derefenced value of the data reference - which ` && + `was changed in the course of the loop - in the second table ` && + `entry is ` && itab_i[ 2 ]->* && `.` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `18) Processing Internal Tables Using ` && + `Data References ` ) ). + + "Similar use case to using field symbols: In a loop across an internal table, + "you can store the content of the line in a data reference variable + "instead of actually copying the content to boost performance. + "In the example, an internal table is created including values of + "database table. A data reference variable is declared as target area of + "the loop. In the course of the loop, some values are changed. + + SELECT * + FROM zdemo_abap_flsch + WHERE distid = 'MI' + ORDER BY carrid + INTO TABLE @DATA(flsch_tab) + UP TO 3 ROWS. + + "The table line is written into a data reference variable + "that is declared inline. + LOOP AT flsch_tab REFERENCE INTO DATA(ref_k). + ref_k->connid = '123'. + ref_k->distance = ref_k->distance * '1.609344'. + ref_k->distid = 'KM' . + ENDLOOP. + + out->write( data = flsch_tab name = `flsch_tab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `19) Data References as Part of ` && + `Structures and Internal Tables` ) ). + + "In contrast to field symbols, data reference variables can be used as + "components of structures or columns in internal tables. + "The example includes the declaration of a structure that contains a + "data reference variable. + "The structure is filled. Based on this structure, an + "internal table is created. In a DO loop, the internal table is filled. + + "Declaring a structure + DATA: BEGIN OF struc_l, + number_l TYPE i, + ref_l TYPE REF TO i, + END OF struc_l, + some_int TYPE i VALUE 0. + + "Filling structure + struc_l = VALUE #( number_l = 1 ref_l = NEW #( 2 ) ). + + out->write( data = struc_l ). + out->write( |\n| ). + + "Declaring an internal table + DATA itab_l LIKE TABLE OF struc_l WITH EMPTY KEY. + + "Filling internal table. + DO 3 TIMES. + some_int += 1. + + "Filling structure + struc_l = VALUE #( number_l = some_int + ref_l = NEW #( some_int ) ). + + "Filling internal table + itab_l = VALUE #( BASE itab_l ( struc_l ) ). + ENDDO. + + out->write( data = itab_l name = `itab_l` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `20) Excursion: Generic Data References` ) ). + + "In modern ABAP, variables and field symbols of the generic types + "'any' and 'data' can be used directly, for example, in LOOP and READ statements. + DATA dref_gen TYPE REF TO data. + CREATE DATA dref_gen TYPE TABLE OF zdemo_abap_carr. + SELECT * + FROM zdemo_abap_carr + INTO TABLE @dref_gen->*. + + "Note: In case of a fully generic type, an explicit or implicit index operation + "is not possible (indicated by the examples commented out). + LOOP AT dref_gen->* ASSIGNING FIELD-SYMBOL(). + ... + ENDLOOP. + "LOOP AT dref->* ASSIGNING FIELD-SYMBOL() FROM 1 TO 4. + "ENDLOOP. + + "The following examples use a dynamic key specification. + "See more syntax examples below. + READ TABLE dref_gen->* ASSIGNING FIELD-SYMBOL() WITH KEY ('CARRID') = 'AA'. + "READ TABLE dref->* INDEX 1 ASSIGNING FIELD-SYMBOL(). + out->write( data = name = `` ). + out->write( |\n| ). + + "Table expressions + DATA(line) = CONV zdemo_abap_carr( dref_gen->*[ ('CARRID') = 'AA' ] ). + + out->write( data = line name = `line` ). + out->write( |\n| ). + + dref_gen->*[ ('CARRID') = 'AA' ] = VALUE zdemo_abap_carr( BASE dref_gen->*[ ('CARRID') = 'AA' ] carrid = 'XY' ). + + out->write( data = dref_gen->*[ ('CARRID') = 'XY' ] name = `dref_gen->*[ ('CARRID') = 'XY' ]` ). + out->write( |\n| ). + + dref_gen->*[ ('CARRID') = 'XY' ]-('CARRID') = 'AA'. + + out->write( data = dref_gen->*[ ('CARRID') = 'AA' ]-('CARRNAME') name = `dref_gen->*[ ('CARRID') = 'AA' ]-('CARRNAME')` ). + out->write( |\n| ). + + "Table functions + DATA(num_tab_lines) = lines( dref_gen->* ). + + out->write( data = num_tab_lines name = `num_tab_lines` ). + out->write( |\n| ). + + DATA(idx) = line_index( dref_gen->*[ ('CARRID') = 'LH' ] ). + + out->write( data = idx name = `idx` ). + out->write( |\n| ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Dynamic ABAP Statements` ) ). + out->write( |21) Dynamic ASSIGN Statements (1) - Specifying the Memory Area Dynamically\n\n| ). + "The memory area is not specified directly, but as content of a + "character-like data object in parentheses. + "Note: + "- When specified as unnamed data object, the compiler treats the + " specifications like static assignments. Do not use named data objects + " for ASSIGN statements in ABAP for Cloud Development. It is recommended + " that existing named data objects are put in a structure. Then, the syntax + " for assigning components dynamically can be used so as to avoid a syntax + " warning. + "- Most of the following examples use an unnamed data object. + "- The specification of the name is not case-sensitive. + + "Creating and populating various types/data objects to work with + TYPES: BEGIN OF st_type, + col1 TYPE i, + col2 TYPE string, + col3 TYPE string, + END OF st_type. + DATA structure TYPE st_type. + DATA it TYPE TABLE OF st_type WITH EMPTY KEY. + structure = VALUE #( col1 = 1 col2 = `aaa` col3 = `Z` ). + APPEND structure TO it. + DATA(struc_ref) = NEW st_type( col1 = 2 col2 = `b` col3 = `Y` ). + DATA dobj TYPE string VALUE `hallo`. + "The following examples use a field symbol with generic type + FIELD-SYMBOLS TYPE data. + + ASSIGN ('IT') TO . + ASSIGN ('SRUCTURE') TO . + + "Field symbol declared inline + "Note: The typing is performed with the generic type data. + ASSIGN ('DOBJ') TO FIELD-SYMBOL(). + + "The statements set the sy-subrc value. + ASSIGN ('DOES_NOT_EXIST') TO . + IF sy-subrc <> 0. + out->write( `Dynamic assignment not successful` ). + ENDIF. + + "The memory area can also be a dereferenced data reference + ASSIGN struc_ref->* TO . + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `22) Dynamic ASSIGN Statements (2) - Assigning Components Dynamically` ) ). + + "You can chain the names with the component selector (-), or, in + "case of reference variables, the object component selector (->). + ASSIGN structure-('COL1') TO . + ASSIGN it[ 1 ]-('COL1') TO . + ASSIGN struc_ref->('COL1') TO . + "The following example uses the dereferencing operator explicitly + "followed by the component selector. + ASSIGN struc_ref->*-('COL1') TO . + + "Using a named data object for the component specification + DATA columnname TYPE string VALUE `COL1`. + ASSIGN structure-(columnname) TO . + + "Fully dynamic specification + "If the compiler can fully determine the data object in ASSIGN structureatements + "in ABAP for Cloud Development, a warning is not issued. + ASSIGN ('STRUCTURE-COL1') TO . + + "Numeric expressions are possible. Its value is interpreted + "as the position of the component in the structureructure. + ASSIGN structure-(3) TO . + + "If the value is 0, the memory area of the entire structureructure is + "assigned to the field symbol. + ASSIGN structure-(0) TO . + + "The structureatements above replace the following, older structureatements. + ASSIGN COMPONENT 'COL1' OF STRUCTURE structure TO . + ASSIGN COMPONENT 3 OF STRUCTURE structure TO . + + "More examples + SELECT SINGLE * FROM zdemo_abap_carr INTO @DATA(wa). + "Reading into data reference variable + SELECT SINGLE * FROM zdemo_abap_carr INTO NEW @DATA(ref_m). + + DATA(comp_name) = lcl_det_at_runtime=>get_dyn_field( ). + + ASSIGN wa-(comp_name) TO FIELD-SYMBOL(). + + ASSIGN wa-('CARRNAME') TO FIELD-SYMBOL(). + + IF sy-subrc = 0. + DATA(subrc1) = sy-subrc. + ENDIF. + + "No exception occurs in case of an unsuccessful assignment. + ASSIGN wa-('CRRNM') TO FIELD-SYMBOL(). + + IF sy-subrc <> 0. + DATA(subrc2) = sy-subrc. + ENDIF. + + "Numeric expressions are possible. Its value is interpreted as the position + "of the component in the structure. + ASSIGN wa-(4) TO FIELD-SYMBOL(). + + "If the value is 0, the memory area of the entire structure is assigned to the field symbol. + ASSIGN wa-(0) TO FIELD-SYMBOL(). + + "Old syntax + ASSIGN COMPONENT 'CARRID' OF STRUCTURE wa TO FIELD-SYMBOL(). + ASSIGN COMPONENT 5 OF STRUCTURE wa TO FIELD-SYMBOL(). + + "Dynamically specifying components of structures that are referenced by + "a data reference variable + + ASSIGN ref_m->('CARRNAME') TO FIELD-SYMBOL(). + + out->write( data = name = `` ). + out->write( |\n| ). + out->write( data = name = `` ). + out->write( |\n| ). + out->write( data = subrc1 name = `subrc1` ). + out->write( |\n| ). + out->write( data = subrc2 name = `subrc2` ). + out->write( |\n| ). + out->write( data = name = `` ). + out->write( |\n| ). + out->write( data = name = `` ). + out->write( |\n| ). + out->write( data = name = `` ). + out->write( |\n| ). + out->write( data = name = `` ). + out->write( |\n| ). + out->write( data = name = `` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `23) Dynamic ASSIGN Statements (3) - Assigning Attributes of Classes or Interfaces Dynamically (1)` ) ). + "The following syntax pattern shows the possible specifications. + "... cref->(attr_name) ... "object reference variable + "... iref->(attr_name) ... "interface reference variable + "... (clif_name)=>(attr_name) ... "class/interface name + "... (clif_name)=>attr ... + "... clif=>(attr_name) ... + + "Creating an instance of a class + DATA(objref) = NEW zcl_demo_abap_objects( ). + + "Assigning instance attributes using an object reference variable + "All visible attributes of objects can be assigned. + objref->string = `ABAP`. "Assigning a value to the attribute for demo purposes + ASSIGN objref->('STRING') TO . + + "Assigning instance attributes using an interface reference variable + DATA intref TYPE REF TO zdemo_abap_objects_interface. + intref = objref. + ASSIGN intref->('STRING') TO . + intref->in_str = `hallo`. + ASSIGN intref->('IN_STR') TO . + + "Assigning static attributes + "All visible static attributes in classes and interfaces can be assigned + "In the following example, a class and an interface are specified statically, + "and the attributes are specified dynamically. + ASSIGN zcl_demo_abap_objects=>('PUBLIC_STRING') TO . + ASSIGN zdemo_abap_objects_interface=>('CONST_INTF') TO . + + "Specifying a class or interface dynamically, and attributes statically + ASSIGN ('ZCL_DEMO_ABAP_OBJECTS')=>public_string TO . + ASSIGN ('ZDEMO_ABAP_OBJECTS_INTERFACE')=>const_intf TO . + + "Specifying a class or interface as well as attributes dynamically + ASSIGN ('ZCL_DEMO_ABAP_OBJECTS')=>('PUBLIC_STRING') TO . + ASSIGN ('ZDEMO_ABAP_OBJECTS_INTERFACE')=>('CONST_INTF') TO . + + "Further dynamic syntax options are possible, for example, + "specifying the memory area after ASSIGN with a writable expression + "because the operand position after ASSIGN is a result position. + ASSIGN NEW zcl_demo_abap_objects( )->('PUBLIC_STRING') TO . + + out->write( `No output for this section. You can set a break point and check the content of data objects in the debugger.` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `24) Dynamic ASSIGN Statements (4) - Assigning Attributes of Classes or Interfaces Dynamically (2)` ) ). + + "The following examples demonstrate a selection of various dynamic specifications + "that are possible with ASSIGN statements. + + "Dynamic specification of attributes of classes/interfaces that are assigned to a field symbol. + DATA(dobj_name) = lcl_det_at_runtime=>get_dyn_dobj( ). + + ASSIGN lcl_det_at_runtime=>(dobj_name) TO FIELD-SYMBOL(). + + out->write( |Data object name determined at runtime: { dobj_name } | ). + out->write( |\n| ). + out->write( |The content of the data object is: { } | ). + out->write( |\n| ). + out->write( |\n| ). + + dobj_name = lcl_det_at_runtime=>get_dyn_dobj( ). + + "Completely dynamic assign + ASSIGN ('lcl_det_at_runtime')=>(dobj_name) TO FIELD-SYMBOL(). + out->write( data = name = `` ). + out->write( |\n| ). + + ASSIGN ('zdemo_abap_objects_interface')=>('const_intf') TO FIELD-SYMBOL(). + out->write( data = name = `` ). + out->write( |\n| ). + + "Class/interface reference variables pointing to an object that contains attributes + "and that are specified dynamically. + DATA iref TYPE REF TO zdemo_abap_objects_interface. + DATA(cl_ref) = NEW zcl_demo_abap_objects( ). + iref = cl_ref. + + ASSIGN iref->('const_intf') TO FIELD-SYMBOL(). + + out->write( data = name = `` ). + out->write( |\n| ). + + ASSIGN cl_ref->('another_string') TO FIELD-SYMBOL(). + + out->write( data = name = `` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `25) Dynamic ASSIGN Statements (5) - Setting sy-subrc/ELSE UNASSIGN Addition` ) ). + + "In dynamic assignments, the statement ASSIGN sets the return code sy-subrc. + "If ELSE UNASSIGN is specified, no memory area is assigned to the field symbol. It has the state unassigned after the ASSIGN statement. + + DATA(attr) = VALUE string_table( ( `another_string` ) ( `public_string` ) ( `this_will_fail` ) ). + + LOOP AT attr INTO DATA(attribute). + + ASSIGN cl_ref->(attribute) TO FIELD-SYMBOL() ELSE UNASSIGN. + IF sy-subrc = 0. + out->write( |Successful assignment for attribute "{ attribute }". sy-subrc = { sy-subrc }. | ). + out->write( |\n| ). + out->write( data = name = `` ). + ELSE. + out->write( |Assignment not successful for attribute "{ attribute }". sy-subrc = { sy-subrc }. | ). + ENDIF. + + out->write( |\n| ). + + IF IS ASSIGNED. + out->write( `The field symbol is assigned.` ). + out->write( `--------------------` ). + ELSE. + out->write( `The field symbol is not assigned.` ). + ENDIF. + + out->write( |\n| ). + + ENDLOOP. + + "Note: For the static variant of the ASSIGN statement, i.e. if the memory area + "to be assigned following the ASSIGN keyword is statically specified, the addition + "ELSE UNASSIGN is implicitly set and cannot be used explicitly. + DATA(hallo) = `Hallo world`. + ASSIGN ('HALLO') TO FIELD-SYMBOL() ELSE UNASSIGN. + ASSERT sy-subrc = 0 AND IS ASSIGNED. + ASSIGN ('DOES_NOT_EXIST') TO ELSE UNASSIGN. + ASSERT sy-subrc = 4 AND IS NOT ASSIGNED. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `26) Dynamic ASSIGN Statements (6) - Type Casting` ) ). + + "As covered above, the CASTING addition of the ASSIGN statement + "has dynamic syntax elements. + DATA dobj_c_l5 TYPE c LENGTH 5 VALUE 'abcde'. + TYPES dtype_c_l2 TYPE c LENGTH 2. + FIELD-SYMBOLS TYPE data. + + "A text literal with the name of a type is specified within the parentheses. + ASSIGN dobj_c_l5 TO CASTING TYPE ('DTYPE_C_L2'). + + out->write( data = name = `` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `27) Accessing Structure Components Dynamically (1)` ) ). + + "You can achieve the access using ASSIGN statements as shown above, or + "by statically specifying the structure and the (object) component selector + "followed by a character-like data object in parentheses. + "Write position + structure-('COL1') = 123. + it[ 1 ]-('COL1') = 456. + struc_ref->('COL1') = 789. + + "Read position + "The example shows how you can retrieve the textual content of any component + "of any structure. + DATA(content_col2) = CONV string( structure-('COL1') ). + DATA(content_col3) = |{ structure-('COL3') }|. + DATA content_col1 LIKE structure-col1. + content_col1 = structure-('COL1'). + + DATA dref_comp TYPE REF TO data. + CREATE DATA dref_comp LIKE structure-('COL3'). + dref_comp->* = structure-('COL3'). + + "If the component is not found, a catchable exception is raised. + TRY. + DATA(col_not_existent) = |{ structure-('COL123') }|. + CATCH cx_sy_assign_illegal_component INTO DATA(error_column). + out->write( error_column->get_text( ) ). + ENDTRY. + + "Accessing components of generic structures dynamically, + "e.g. if you have a method parameter that is typed with the generic type + "data. + "The example uses a field symbol with the generic type data which is assigned + "a structure. + FIELD-SYMBOLS TYPE data. + ASSIGN structure TO . + + "As in the examples above, specifying components dynamically is possible. + -('COL2') = `ABAP`. + DATA(gen_comp) = CONV string( -('COL2') ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `28) Accessing Structure Components Dynamically (2) - Excursion` ) ). + + "In the following example, a structure is assigned to a field symbol that + "has a generic type. The components of the structure are accessed dynamically in + "a DO loop. The sy-index value is interpreted as the position of the component + "in the structure. Plus, using RTTI - as also shown further down - the component + "names are retrieved. Component names and the values are added to a string. As a + "prerequisite, all component values must be convertible to type string. + DATA struc2string TYPE string. + FIELD-SYMBOLS TYPE data. + ASSIGN structure TO . + IF sy-subrc = 0. + TRY. + DATA(components) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_data( ) )->components. + DO. + TRY. + DATA(co) = components[ sy-index ]-name. + struc2string = struc2string && + COND #( WHEN sy-index <> 1 THEN `, ` ) && + co && `: "` && + -(sy-index) && `"`. + CATCH cx_sy_assign_illegal_component cx_sy_itab_line_not_found. + EXIT. + ENDTRY. + ENDDO. + CATCH cx_sy_move_cast_error. + ENDTRY. + ENDIF. + + out->write( data = struc2string name = `struc2string` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Dynamically Creating Data Objects at Runtime Using Dynamic Type Definitions` ) ). + out->write( |29) Miscellaneous Data Objects (1)\n\n| ). + + "In an example above, anonymous data objects are created using static + "type definitions. In this example, anonymous data objects are created + "using a type determined at runtime. + "The values of an internal table of type string represent type names. + "The type name is used for the dynamic type specification in CREATE + "DATA statements that use various additions. The following is + "created dynamically: elementary data object, structure, internal + "table, data reference. For output purposes, the newly created data + "objects are assigned values. + "Note: + "- The NEW operator cannot be used here. + "- The creation of a data object based on a type description object is shown + " below (TYPE HANDLE addition). + "- Dynamic type specifications for ASSIGN statements together with the + " CASTING addition are shown above. + + DATA(type_names) = VALUE string_table( ( `I` ) + ( `STRING` ) + ( `ZDEMO_ABAP_CARR` ) ). + + DATA dataref TYPE REF TO data. + DATA some_str TYPE string VALUE `some string`. + DATA some_structure TYPE zdemo_abap_carr. + + LOOP AT type_names REFERENCE INTO DATA(refwa). + out->write( |***** Loop iteration { sy-tabix }. Type: { refwa->* } *****| ). + + CASE refwa->*. + WHEN `I`. + CREATE DATA dataref TYPE (refwa->*). + dataref->* = 123. + + out->write( data = dataref->* name = `dataref->*` ). + out->write( |\n| ). + + CREATE DATA dataref TYPE TABLE OF (refwa->*). + + INSERT 1 INTO TABLE dataref->*. + INSERT 2 INTO TABLE dataref->*. + + out->write( data = dataref->* name = `dataref->*` ). + out->write( |\n| ). + + CREATE DATA dataref TYPE REF TO (refwa->*). + dataref->* = REF i( 456 ). + + out->write( data = dataref->* name = `dataref->*` ). + out->write( |\n| ). + WHEN `STRING`. + CREATE DATA dataref TYPE (refwa->*). + dataref->* = `hello`. + + out->write( data = dataref->* name = `dataref->*` ). + out->write( |\n| ). + + CREATE DATA dataref TYPE TABLE OF (refwa->*). + INSERT `hello` INTO TABLE dataref->*. + INSERT `abap` INTO TABLE dataref->*. + + out->write( data = dataref->* name = `dataref->*` ). + out->write( |\n| ). + + CREATE DATA dataref TYPE REF TO (refwa->*). + dataref->* = REF string( `hi` ). + + out->write( data = dataref->* name = `dataref->*` ). + out->write( |\n| ). + WHEN `ZDEMO_ABAP_CARR`. + CREATE DATA dataref TYPE (refwa->*). + SELECT SINGLE * FROM zdemo_abap_carr INTO @dataref->*. + + out->write( data = dataref->* name = `dataref->*` ). + out->write( |\n| ). + + CREATE DATA dataref TYPE TABLE OF (refwa->*). + SELECT * FROM zdemo_abap_carr INTO TABLE @dataref->* UP TO 3 ROWS. + + out->write( data = dataref->* name = `dataref->*` ). + out->write( |\n| ). + + CREATE DATA dataref TYPE REF TO (refwa->*). + SELECT SINGLE * FROM zdemo_abap_carr INTO NEW @dataref->*. + + out->write( data = dataref->* name = `dataref->*` ). + out->write( |\n| ). + ENDCASE. + ENDLOOP. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `30) Elementary Data Object (2)` ) ). + + "The example demonstrates the following: + "- The method call takes care of providing the name of a built-in data type and more + "- An elementary data object is created based on the data type + "- For demonstration purposes, RTTI (as shown further down in more detail) is used + " to check on the created data object by retrieving the type description. + + DATA(b_type) = lcl_det_at_runtime=>get_builtin_type( ). + + DATA ref_bt TYPE REF TO data. + + TRY. + CASE b_type-builtin_type. + WHEN 'd' OR 'decfloat16' OR 'decfloat34' OR 'f' OR 'i' + OR 'string' OR 't' OR 'xstring'. + + CREATE DATA ref_bt TYPE (b_type-builtin_type). + WHEN 'c' OR 'n' OR 'x'. + CREATE DATA ref_bt TYPE (b_type-builtin_type) LENGTH b_type-len. + WHEN 'p'. + CREATE DATA ref_bt TYPE p LENGTH b_type-len DECIMALS b_type-dec. + WHEN OTHERS. + out->write( `That didn't work.` ). + ENDCASE. + + "Getting type information using RTTI + DATA(descr_builtin_type) = CAST cl_abap_elemdescr( + cl_abap_typedescr=>describe_by_data( ref_bt->* ) ). + + out->write( |Built-in type determined at runtime: { b_type-builtin_type } | ). + out->write( |\n| ). + out->write( `Type information of created data object at runtime:` ). + out->write( |\n| ). + out->write( |\n| ). + out->write( data = descr_builtin_type name = `descr_builtin_type` ). + + CATCH cx_root. + out->write( `Something went wrong.` ). + ENDTRY. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `31) Structure (3)` ) ). + + "The example demonstrates the following: + "- The method call takes care of providing the name of a database table name. + "- A structured data object is created based on the dynamically determined type. + " It is used as target data object for a SELECT statement. As shown further + " down in more detail, clauses of SELECT statements can be specified + " dynamically. + + "Retrieving table name + DATA(type4struc) = lcl_det_at_runtime=>get_dyn_table_name( ). + + DATA ref_dynstruc TYPE REF TO data. + + "Creating structured data object + CREATE DATA ref_dynstruc TYPE (type4struc). + + "Dynamic specification of the FROM clause + SELECT SINGLE * + FROM (type4struc) + INTO @ref_dynstruc->*. + + out->write( |Structured data type/database table name determined at runtime: { type4struc } | ). + out->write( |\n| ). + out->write( |\n| ). + out->write( data = ref_dynstruc->* name = `ref_dynstruc->*` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `32) Internal Table (4)` ) ). + + "The example demonstrates the following: + "- The method call takes care of providing the name of a database table name. + "- An internal table is created based on the dynamically determined type. + " It is used as target data object for a SELECT statement. + "- The UP TO ... ROWS addition is provided with a random number in the example. + "- AS in the example above, the SELECT statement includes the dynamic + " specification of the FROM clause. + + "Retrieving table name + DATA(type_name) = lcl_det_at_runtime=>get_dyn_table_name( ). + + DATA ref_n TYPE REF TO data. + + "Creating internal table based on the type determined at runtime + CREATE DATA ref_n TYPE TABLE OF (type_name). + + "Specifying random number for up to clause + DATA(random_upto) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 2 + max = 6 )->get_next( ). + + "Dynamic specification of the FROM clause + SELECT * + FROM (type_name) + INTO TABLE @ref_n->* + UP TO @random_upto ROWS. + + out->write( |Table/type name determined at runtime: { type_name } | ). + out->write( |\n| ). + out->write( |At most, { random_upto } lines should have been read from the database table.| ). + out->write( |\n| ). + out->write( |\n| ). + out->write( data = ref_n->* name = `ref_n->*` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `33) Absolute Type Names for Dynamically Specifying Types` ) ). + + "In addition to character-like data objects for the type name specified within the + "parentheses, you can also use absolute type names for statements such as CREATE DATA. + "The absolute type name is retrieved using RTTI. See more on RTTI further down. + + "Type to refer to + TYPES type4abs TYPE p LENGTH 4 DECIMALS 3. + "Data and object reference variables + DATA dref4abs TYPE REF TO data. + DATA oref4abs TYPE REF TO object. + "Getting absolute names using RTTI + DATA(abs_name_type) = cl_abap_typedescr=>describe_by_name( 'TYPE4ABS' )->absolute_name. + DATA(abs_name_cl) = cl_abap_typedescr=>describe_by_name( 'ZCL_DEMO_ABAP_DYNAMIC_PROG' )->absolute_name. + + "Data references + ""Named data object holding the absolute name + CREATE DATA dref4abs TYPE (abs_name_type). + "Unnamed data object + CREATE DATA dref4abs TYPE ('\TYPE=STRING'). + + "Object references + "Named data object + CREATE OBJECT oref4abs TYPE (abs_name_cl). + "Unnamed data object + CREATE OBJECT oref4abs TYPE ('\CLASS=ZCL_DEMO_ABAP_DYNAMIC_PROG'). + + out->write( `No output for this section. You can set a break point and check the content of data objects in the debugger.` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Dynamically Specifying Components/Clauses in Statements for Processing Internal Tables with ...` ) ). + out->write( |34) SORT\n\n| ). + + "A field is determined at runtime on whose basis a sorting is done on an + "internal table. + + DATA(field_name) = lcl_det_at_runtime=>get_dyn_field( ). + + SELECT * + FROM zdemo_abap_carr + ORDER BY carrid + INTO TABLE @DATA(carr_itab) + UP TO 3 ROWS. + + SORT carr_itab BY (field_name). + + out->write( |Field name determined at runtime | && + |by which the sorting was done: { field_name } | ). + out->write( |\n| ). + out->write( |\n| ). + out->write( data = carr_itab name = `carr_itab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `35) READ TABLE (1)` ) ). + + "Dynamic key specification in READ TABLE statements + + TYPES: BEGIN OF s, + comp TYPE string, + value TYPE string, + END OF s. + + TYPES comps_type TYPE TABLE OF s WITH EMPTY KEY. + + "Providing components and values for READ TABLE statement + DATA(comps) = VALUE comps_type( ( comp = `CARRID` value = `LH` ) + ( comp = `CONNID` value = `0555` ) + ( comp = `SEATSOCC` value = `115` ) + ( comp = `CARRID` value = `XY` ) ). "not found + + SELECT * + FROM zdemo_abap_fli + INTO TABLE @DATA(itab_read_tab_dyn). + + LOOP AT comps INTO DATA(wa_comps). + READ TABLE itab_read_tab_dyn + INTO DATA(read_line) + WITH KEY (wa_comps-comp) = wa_comps-value. + + IF sy-subrc = 0. + out->write( data = wa_comps-comp name = `wa_comps-comp` ). + out->write( |\n| ). + out->write( data = wa_comps-value name = `wa_comps-value` ). + out->write( |\n| ). + out->write( data = read_line name = `read_line` ). + out->write( |\n| ). + CLEAR read_line. + ELSE. + out->write( data = wa_comps-comp name = `wa_comps-comp` ). + out->write( |\n| ). + out->write( data = wa_comps-value name = `wa_comps-value` ). + out->write( |\n| ). + out->write( `Line not found.` ). + out->write( |\n| ). + ENDIF. + + ENDLOOP. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `36) READ TABLE (2)` ) ). + + "Data objects and types to work with + TYPES: BEGIN OF demo_struct, + col1 TYPE i, + col2 TYPE string, + col3 TYPE string, + END OF demo_struct. + "Standard table and specification of primary and secondary table key + DATA itab TYPE TABLE OF demo_struct + WITH NON-UNIQUE KEY col1 + WITH UNIQUE SORTED KEY sk COMPONENTS col2. + TYPES itab_type LIKE itab. + DATA itab_ref TYPE TABLE OF REF TO demo_struct WITH EMPTY KEY. + itab = VALUE #( ( col1 = 1 col2 = `aaa` col3 = `zzz` ) + ( col1 = 2 col2 = `bbb` col3 = `yyy` ) + ( col1 = 3 col2 = `ccc` col3 = `xxx` ) ). + itab_ref = VALUE #( ( NEW demo_struct( col1 = 1 col2 = `aaa` col3 = `zzz` ) ) ). + + "Notes + "- In statements using key specifications, secondary table key names (or alias names) + " are usually specified. Also the primary table key using the predefined name + " primary_key or its alias name can be used. + "- Many of the following statements provide similar additions offering dynamic + " specifications, such as USING KEY and dynamic component name specifications. + + "Reading by specifying keys dynamically + "Implicitly specifying the table key values in a work area (USING KEY addition) + DATA(wa_read) = VALUE demo_struct( col2 = `aaa` ). + READ TABLE itab FROM wa_read USING KEY ('SK') REFERENCE INTO DATA(read_ref). + + "Explicitly specifying the key and key values (TABLE KEY addition) + "The component names can also be specified dynamically (which is done in most of the + "following examples for demonstration purposes). Note that each component of the table + "key must be specified. + READ TABLE itab WITH TABLE KEY ('SK') COMPONENTS ('COL2') = `aaa` REFERENCE INTO read_ref. + "Specifying the predefined name primary_key explicitly and dynamically + READ TABLE itab WITH TABLE KEY ('PRIMARY_KEY') COMPONENTS ('COL1') = 1 REFERENCE INTO read_ref. + "If the addition COMPONENTS is not specified, the primary table key is implicitly used. + READ TABLE itab WITH TABLE KEY ('COL1') = 1 REFERENCE INTO read_ref. + + "Reading using a free key (WITH KEY addition) + READ TABLE itab WITH KEY ('COL3') = `yyy` REFERENCE INTO read_ref. + "The addition can also be used by specifying a secondary table key name + READ TABLE itab WITH KEY ('SK') COMPONENTS ('COL2') = `ccc` REFERENCE INTO read_ref. + + "Reading based on a table index (INDEX addition) + "Not using the addition USING KEY means reading from the primary table index. + READ TABLE itab INDEX 1 USING KEY ('SK') REFERENCE INTO read_ref. + + "More dynamic specification options when specifying the target as work area + "(COMPARING/TRANSPORTING additions) + "TRANSPORTING: Specifying which components shall be respected + READ TABLE itab INDEX 1 INTO DATA(workarea) TRANSPORTING ('COL1') ('COL3'). + + "COMPARING: If the content of the compared components is identical, sy-subrc is set + "to 0, and otherwise to 2. The line found is assigned to the work area independently + "of the result of the comparison. + workarea-('COL3') = `uvw`. + READ TABLE itab INDEX 1 INTO workarea COMPARING ('COL3') TRANSPORTING ('COL1') ('COL3'). + IF sy-subrc <> 0. + ... + ENDIF. + + out->write( `No output for this section. You can set a break point and check the content of data objects in the debugger.` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `37) Table Expressions` ) ). + + "Similar to READ TABLE statements, you can specify table lines with 3 alternatives: + "index read, read using free key, table key + "Also there, dynamic specifications are possible regarding the key specifications. + + "Reading based on index with dynamic key specifications + "Specifying the secondary table index of a sorted secondary key + DATA(wa_te1) = itab[ KEY ('SK') INDEX 1 ]. + "Reading using a free key, the keys are specified dynamically + DATA(wa_te2) = itab[ ('COL2') = `bbb` ('COL3') = `yyy` ]. + + "Reading using a table key + "Specyfing the table key explicitly + "Note: Unlike READ TABLE statements, the name of the table key must be specified. The + "addition COMPONENTS can be omitted. + "In the following example, the component names are also specified dynamically. + DATA(wa_te3) = itab[ KEY ('SK') ('COL2') = `ccc` ]. + "Specifying the COMPONENTS addition explicitly + DATA(wa_te4) = itab[ KEY ('PRIMARY_KEY') COMPONENTS ('COL1') = 1 ]. + + "Accessing components + "As shown above, chaininings with the (object) component selector are possible. + "The examples use index access and write positions. + itab[ 1 ]-('COL2') = `jkl`. + itab_ref[ 1 ]->('COL2') = `mno`. + + out->write( `No output for this section. You can set a break point and check the content of data objects in the debugger.` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `38) LOOP (1)` ) ). + + "Dynamic specification of the key in LOOP statements + "In the example, the loop can be executed with the entries 'SKEY' and 'PRIMARY_KEY'. + "This is not case sensitive. Any other entries produce a runtime error. + + DATA(keys) = VALUE string_table( ( `PRIMARY_KEY` ) ( `SKEY` ) ). + + DATA itab_loop TYPE TABLE OF i + WITH NON-UNIQUE KEY primary_key COMPONENTS table_line + WITH NON-UNIQUE SORTED KEY skey COMPONENTS table_line. + + itab_loop = VALUE #( ( 3 ) ( 2 ) ( 1 ) ). + + DATA itab_dyn_key LIKE itab_loop. + + LOOP AT keys INTO DATA(k). + + LOOP AT itab_loop INTO DATA(wa_lo) USING KEY (k). + APPEND wa_lo TO itab_dyn_key. + ENDLOOP. + + out->write( |Loop over internal table using key "{ k }".| ). + out->write( |\n| ). + out->write( data = itab_dyn_key name = `itab_dyn_key` ). + out->write( |\n| ). + CLEAR itab_dyn_key. + + ENDLOOP. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `39) LOOP (2)` ) ). + + "USING KEY addition: Overriding the standard order determined by the table category + LOOP AT itab REFERENCE INTO DATA(ref) USING KEY ('SK'). + ... + ENDLOOP. + + "When the primary table key is specified, the loop behaves as if it was not specified. + "So, the following statement corresponds to the one below. + LOOP AT itab REFERENCE INTO ref USING KEY ('PRIMARY_KEY'). + ... + ENDLOOP. + + LOOP AT itab REFERENCE INTO ref. + ... + ENDLOOP. + + "Dynamic WHERE condition + "You can specify a character-like data object or a standard table with character-like + "line type. + DATA(cond_loop) = `COL1 > 1`. + LOOP AT itab REFERENCE INTO ref WHERE (cond_loop). + ... + ENDLOOP. + + out->write( `No output for this section. You can set a break point and check the content of data objects in the debugger.` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `40) INSERT` ) ). + + "The USING KEY addition (which accepts a dynamic specification) affects the order in which lines are inserted. + + "Result of the following example when using the ... + "- secondary table key: order of itab entries 5 ... /4 ... /... + "- primary table key: order of itab entries 4 ... /5 ... /... + INSERT LINES OF VALUE itab_type( ( col1 = 4 col2 = `eee` col3 = `www` ) + ( col1 = 5 col2 = `ddd` col3 = `vvv` ) ) + USING KEY ('SK') + "USING KEY ('PRIMARY_KEY') + INTO itab INDEX 1. + + "Excursion: Using LOOP AT statements with the USING KEY addition + "and exploring the table index + "Declaring demo tables to hold the internal table entries + DATA it_seckey_idx TYPE TABLE OF demo_struct WITH EMPTY KEY. + DATA it_primekey_idx LIKE it_seckey_idx. + + "Visualizing the secondary table index + LOOP AT itab INTO DATA(wa_sk) USING KEY ('SK'). + APPEND wa_sk TO it_seckey_idx. + ENDLOOP. + + out->write( data = it_seckey_idx name = `it_seckey_idx` ). + out->write( |\n| ). + + "Visualizing the primary table index + LOOP AT itab INTO DATA(wa_pk) USING KEY ('PRIMARY_KEY'). + APPEND wa_pk TO it_primekey_idx. + ENDLOOP. + + out->write( data = it_primekey_idx name = `it_primekey_idx` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `41) MODIFY (1)` ) ). + + "Dynamic WHERE condition in MODIFY statements + "Note: + "- The addition WHERE can only be specified together with the addition TRANSPORTING. + "- Invalid logical expressions raise an exception from the class CX_SY_ITAB_DYN_LOOP. + + TYPES: + BEGIN OF line, + col1 TYPE c LENGTH 1, + col2 TYPE i, + END OF line. + + DATA itab_mod_tab_dyn TYPE SORTED TABLE OF line + WITH UNIQUE KEY col1. + + itab_mod_tab_dyn = VALUE #( ( col1 = 'A' col2 = 1 ) + ( col1 = 'B' col2 = 10 ) + ( col1 = 'C' col2 = 100 ) ). + + out->write( `Internal table content before modifications:` ). + out->write( |\n| ). + out->write( data = itab_mod_tab_dyn name = `itab_mod_tab_dyn` ). + out->write( |\n| ). + + "Providing conditions for MODIFY statement + DATA(conditions) = VALUE string_table( ( `col2 < 5` ) + ( `col2 = 10` ) + ( `colxyz > 50` ) ). "to fail + + LOOP AT itab_mod_tab_dyn INTO DATA(wa_mod_dyn). + TRY. + + DATA(condition) = conditions[ sy-tabix ]. + + MODIFY itab_mod_tab_dyn + FROM VALUE line( col2 = wa_mod_dyn-col2 * 2 ) + TRANSPORTING col2 + WHERE (condition). + + CATCH cx_sy_itab_dyn_loop. + out->write( |Invalid WHERE condition "{ condition }".| ). + ENDTRY. + ENDLOOP. + + out->write( `Internal table content after modifications:` ). + out->write( |\n| ). + out->write( data = itab_mod_tab_dyn name = `itab_mod_tab_dyn` ). + + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `42) MODIFY (2)` ) ). + + "In the following example, a line is modified based on a work area and a table key. + "The component col1 is left out from the work area intentionally. + "If the primary table key was used, the value of sy-subrc would be 4, and no modification was done. + "The optional addition transporting is specified to denote what should be modified. In this example, + "the component is also specified dynamically. + MODIFY TABLE itab FROM VALUE #( col2 = `bbb` col3 = `uuu` ) USING KEY ('SK') TRANSPORTING ('COL3'). + + "In the following example, a line is modified based on a work area, an index specification and a + "table key. + "INDEX can also be positioned after FROM. + MODIFY itab INDEX 2 USING KEY ('SK') FROM VALUE #( col3 = `ttt` ) TRANSPORTING ('COL3'). + + "Dynamic WHERE clause (only to be used with the TRANSPORTING addition) + "The USING KEY addition is also possible. Check the ABAP Keyword Documentation + "for special rules that apply. + DATA(cond_mod) = `COL1 < 3`. + MODIFY itab FROM VALUE #( col3 = `sss` ) TRANSPORTING ('COL3') WHERE (cond_mod). + + out->write( data = itab name = `itab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `43) DELETE (1)` ) ). + + "Dynamic WHERE condition in DELETE statements + + DATA itab_del_tab_dyn TYPE TABLE OF i WITH EMPTY KEY + WITH NON-UNIQUE SORTED KEY skey COMPONENTS table_line. + + itab_del_tab_dyn = VALUE #( ( 100 ) + ( 200 ) + ( 300 ) + ( 400 ) + ( 500 ) + ( 600 ) ). + + out->write( `Internal table content before modifications:` ). + out->write( |\n| ). + out->write( data = itab_del_tab_dyn name = `itab_del_tab_dyn` ). + out->write( |\n| ). + + DO 3 TIMES. + TRY. + + CASE sy-index. + WHEN 1. + condition = `table_line <= 200`. + WHEN 2. + condition = `table_line >= 500`. + WHEN 3. + condition = `col1 = 600`. + ENDCASE. + + DELETE itab_del_tab_dyn + USING KEY skey + WHERE (condition). + + out->write( |Condition: { condition }| ). + out->write( |\n| ). + out->write( data = itab_del_tab_dyn name = `itab_del_tab_dyn` ). + out->write( |\n| ). + CATCH cx_sy_itab_dyn_loop. + out->write( |Invalid WHERE condition "{ condition }".| ). + out->write( |\n| ). + ENDTRY. + ENDDO. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `44) DELETE (2)` ) ). + + "A single line or multipled lines can be deleted. + "Note that DELETE ADJACENT DUPLICATES statements can also be specified using + "dynamic parts. + + "Deleting based on a dynamically specified table key + "The values can be declared either implicitly in a work area after FROM or explicitly + "by listing the components of the table key after TABLE KEY. + "If the USING KEY addition is not specified, the primary table key is used by default. + DELETE TABLE itab FROM VALUE #( col2 = `eee` col3 = `www` ) USING KEY ('SK'). + + "Each component of the table key must be listed. + DELETE TABLE itab WITH TABLE KEY ('SK') COMPONENTS ('COL2') = `ddd`. + + "Deleting based on the table index + DELETE itab INDEX 1 USING KEY ('SK'). + + "Deleting multiple lines and specifying the WHERE conditions dynamically + "The USING KEY addition is also possible. + DATA(condition_tab) = VALUE string_table( ( `COL1 < 3` ) + ( `OR` ) + ( `COL3 = ``www``` ) ). + DELETE itab WHERE (condition_tab). + + out->write( data = itab name = `itab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Dynamically Specifying Clauses in ABAP SQL SELECT Statements` ) ). + out->write( |45) SELECT List\n\n| ). + + "In the example, the SELECT list that is used in a SELECT statement is + "determined at runtime. + "Note: Check out the CL_ABAP_DYN_PRG class, which supports dynamic programming by + "checking the validity of dynamic specifications. See an example below. + + DATA(select_list) = lcl_det_at_runtime=>get_dyn_select_list( ). + + DATA sel_table TYPE TABLE OF zdemo_abap_flsch WITH EMPTY KEY. + + SELECT (select_list) + FROM zdemo_abap_flsch + ORDER BY carrid + INTO CORRESPONDING FIELDS OF TABLE @sel_table + UP TO 3 ROWS. + + out->write( |SELECT list determined at runtime: { select_list } | ). + out->write( |\n| ). + out->write( |\n| ). + out->write( data = sel_table name = `sel_table` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `46) FROM Clause` ) ). + + "In the example, the FROM clause that is used in a SELECT statement is + "determined at runtime. Here, the number of entries of a database table + "is counted. + + DATA(tab_name) = lcl_det_at_runtime=>get_dyn_table_name( ). + + SELECT COUNT(*) + FROM (tab_name) + INTO @DATA(count). + + out->write( |Table name determined at runtime: { tab_name } | ). + out->write( |\n| ). + out->write( |The table { tab_name } has { count } entries.| ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `47) WHERE Clause` ) ). + + "In the example, the WHERE clause that is used in a SELECT statement is + "determined at runtime. Here, the WHERE clause is based on a string + "table. + + DATA(where_clause) = lcl_det_at_runtime=>get_dyn_where_clause( ). + + SELECT * + FROM zdemo_abap_flsch + WHERE (where_clause) + ORDER BY carrid + INTO TABLE @DATA(where_tab) + UP TO 5 ROWS. + + out->write( |WHERE clause determined at runtime:| ). + out->write( |\n| ). + out->write( data = where_clause name = `where_clause` ). + out->write( |\n| ). + out->write( |\n| ). + out->write( data = where_tab name = `where_tab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `48) ORDER BY Clause` ) ). + + DATA(order_by) = 'FLTIME'. + + SELECT * + FROM zdemo_abap_flsch + ORDER BY (order_by) + INTO TABLE @DATA(order_by_tab) + UP TO 5 ROWS. + + out->write( |ORDER BY clause determined at runtime:| ). + out->write( |\n| ). + out->write( data = order_by name = `order_by` ). + out->write( |\n| ). + out->write( |\n| ). + out->write( data = order_by_tab name = `order_by_tab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `49) Multiple Dynamically Specified Clauses in an ABAP SQL SELECT Statement` ) ). + + "In this example, multiple clauses in a SELECT statement are + "determined at runtime to demonstrate the rich variety of possibilities. + "Note: The rows and target table specifications are not real dynamic + "specifications in the SELECT statement in the sense of syntax elements + "enclosed by parentheses. Here, they are just included to provide some + "more 'dynamic' touch of the statement :) + + "Getting all clauses of the SELECT statement + DATA(dyn_syntax_elem) = lcl_det_at_runtime=>get_dyn_syntax_elements( ). + + IF dyn_syntax_elem-table IS NOT INITIAL + AND dyn_syntax_elem-select_list IS NOT INITIAL + AND dyn_syntax_elem-where_clause IS NOT INITIAL + AND dyn_syntax_elem-order_by IS NOT INITIAL + AND dyn_syntax_elem-target IS BOUND + AND dyn_syntax_elem-rows IS NOT INITIAL. + + out->write( `Dynamically determined syntax elements:` ). + out->write( |\n| ). + out->write( data = dyn_syntax_elem name = `dyn_syntax_elem` ). + out->write( |\n| ). + + SELECT (dyn_syntax_elem-select_list) + FROM (dyn_syntax_elem-table) + WHERE (dyn_syntax_elem-where_clause) + ORDER BY (dyn_syntax_elem-order_by) + INTO CORRESPONDING FIELDS OF TABLE @dyn_syntax_elem-target->* + UP TO @dyn_syntax_elem-rows ROWS. + + out->write( data = dyn_syntax_elem-target->* name = `dyn_syntax_elem-target->*` ). + + ELSE. + out->write( `There's an issue with syntax elements.` ). + ENDIF. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `50) Dynamic Specifications in Other ABAP SQL Statements` ) ). + + "Creating a structure to be inserted into the database table + DATA(table) = 'ZDEMO_ABAP_CARR'. + DATA(wherecl) = 'CARRID = ''ZZ'''. + SELECT SINGLE * + FROM (table) + INTO NEW @DATA(refstruc). + refstruc->('CARRID') = 'ZZ'. + + INSERT (table) FROM @refstruc->*. + + SELECT SINGLE * + FROM (table) + WHERE (wherecl) + INTO NEW @DATA(res1). + out->write( data = res1->* name = `res1->*` ). + out->write( |\n| ). + + refstruc->('CARRNAME') = 'ZZ Airlines'. + UPDATE (table) FROM @refstruc->*. + + SELECT SINGLE * + FROM (table) + WHERE (wherecl) + INTO NEW @DATA(res2). + out->write( data = res2->* name = `res2->*` ). + out->write( |\n| ). + + + refstruc->('CURRCODE') = 'GBP'. + MODIFY (table) FROM @refstruc->*. + + SELECT SINGLE * + FROM (table) + WHERE (wherecl) + INTO NEW @DATA(res3). + out->write( data = res3->* name = `res3->*` ). + out->write( |\n| ). + + DELETE FROM (table) WHERE (wherecl). + + SELECT * + FROM (table) + INTO TABLE NEW @DATA(res4). + out->write( data = res4->* name = `res4->*` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `51) Validating Input for Dynamic Specifications` ) ). + "The following examples use methods of the CL_ABAP_DYN_PRG class, which supports + "dynamic programming by checking the validity for dynamic specifications. + "Check out the class documentation for more information. + + "In the following example, several table names are provided in a string table that + "is looped over. Some wrong table names are intentionally included. + "You can provide the "packages" formal parameter with a table that contains + "the names of packages in which the tables should be included. The + "assumption is that you imported the repository into the package + "specified. If you imported it into a package with a different name, + "change the value accordingly. Otherwise, none of the tables is found. + + out->write( `****** Checking if the input is a database table name, inlcuded in given packages ******` ). + out->write( |\n| ). + + DATA(table_names) = VALUE string_table( ( `ZDEMO_ABAP_CARR` ) + ( `ZDEMO_ABAP_FLI` ) + ( `NO_DBTAB` ) + ( `ZDEMO_ABAP_FLSCH` ) + ( `ZDEMO_ABAP_FLI2` ) + ( `WRONG_DBTAB` ) ). + + DATA(check_packages) = VALUE string_hashed_table( ( `ZABAP_CHEAT_SHEETS` ) ). + + DATA dbtab TYPE string. + + LOOP AT table_names INTO DATA(wa_tab). + TRY. + dbtab = cl_abap_dyn_prg=>check_table_name_tab( val = to_upper( wa_tab ) + packages = check_packages ). + + SELECT SINGLE * FROM (dbtab) INTO NEW @DATA(ref_wa). + + out->write( |Result for { wa_tab }:| ). + out->write( ref_wa->* ). + out->write( |\n| ). + CATCH cx_abap_not_a_table cx_abap_not_in_package INTO DATA(err). + out->write( |Result for { wa_tab }:| ). + out->write( err->get_text( ) ). + out->write( |\n| ). + ENDTRY. + ENDLOOP. + + "In the following example, the check_allowlist method is used to check + "whether the input is allowed or not. Similar to above, an internal + "is filled with demo input. Here, a parameter is used that expects + "a comma-separated list of allowed values. + + out->write( `****** Checking if input is allowed or not ******` ). + out->write( |\n| ). + + DATA(to_be_checked) = VALUE string_table( ( `A` ) + ( `B` ) + ( `C` ) + ( `D` ) + ( `HALLO` ) + ( `ABAP` ) ). + + LOOP AT to_be_checked INTO DATA(chk). + TRY. + DATA(value) = cl_abap_dyn_prg=>check_allowlist( val = chk + allowlist_str = `A,B,C,ABAP` ). + out->write( |{ value } is allowed.| ). + CATCH cx_abap_not_in_allowlist INTO err. + out->write( err->get_text( ) ). + ENDTRY. + ENDLOOP. + + out->write( |\n| ). + + "The following example is similar to the one above. However, in this case, + "a parameter is used that expects an internal table containing the allowed + "values. + + LOOP AT to_be_checked INTO chk. + TRY. + value = cl_abap_dyn_prg=>check_allowlist( val = chk + allowlist_htab = VALUE #( ( `X` ) + ( `B` ) + ( `HALLO` ) + ( `Y` ) + ( `ABAP` ) ) ). + out->write( |{ value } is allowed.| ). + CATCH cx_abap_not_in_allowlist INTO err. + out->write( err->get_text( ) ). + ENDTRY. + ENDLOOP. + + out->write( |\n| ). + out->write( `****** Checking if input can be a column name and does not contain invalid characters ******` ). + out->write( |\n| ). + + "The following example uses a method with which the validity of column names + "of database tables can be checked. + + to_be_checked = VALUE #( ( `CARRID` ) + ( `CONNID` ) + ( `SOME_COLUMN` ) + ( `??NOPE??` ) + ( `...!` ) ). + + LOOP AT to_be_checked INTO chk. + TRY. + DATA(col_name) = cl_abap_dyn_prg=>check_column_name( val = chk + strict = abap_true ). + out->write( |{ col_name } is allowed.| ). + CATCH cx_abap_invalid_name INTO err. + out->write( err->get_text( ) ). + ENDTRY. + ENDLOOP. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `52) Dynamic Invoke (1)` ) ). + + "In the example, both class and method are determined at runtime for + "the method call. The suitable parameter table is filled in the + "method get_dyn_class_meth. The example is implemented in a way that + "all the methods that are called store some text in a string. This + "string is displayed to see the effect of the dynamic method call. + + lcl_det_at_runtime=>get_dyn_class_meth( IMPORTING cl = DATA(cl_name) + meth = DATA(meth_name) + ptab = DATA(p_tab) ). + + CALL METHOD (cl_name)=>(meth_name) PARAMETER-TABLE p_tab. + + out->write( |Class name determined at runtime: { cl_name } | ). + out->write( |\n| ). + out->write( |Method name determined at runtime: { meth_name } | ). + out->write( |\n| ). + + out->write( `Result of method call (text stored in a variable):` ). + out->write( |\n| ). + out->write( data = lcl_det_at_runtime=>dyn_meth_call_result name = `lcl_det_at_runtime=>dyn_meth_call_result` ). + out->write( |\n| ). + + "Further method calls + "The class and method to be used is determined here by just providing + "the character-like content (the name) via a data object in a predefined way. + + DATA method TYPE string VALUE `FILL_STRING`. + + "Note that the method has no parameters in this example. + "Similar to above. The method stores some text in a string which is + "displayed to see the effect of the dynamic method call. + CALL METHOD lcl_det_at_runtime=>(method). + + out->write( data = lcl_det_at_runtime=>dyn_meth_call_result name = `lcl_det_at_runtime=>dyn_meth_call_result` ). + out->write( |\n| ). + + DATA class TYPE string VALUE `LCL_DET_AT_RUNTIME`. + + CALL METHOD (class)=>fill_string. + + out->write( data = lcl_det_at_runtime=>dyn_meth_call_result name = `lcl_det_at_runtime=>dyn_meth_call_result` ). + + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `52b) Dynamic Invoke (2)` ) ). + "Another example for dynamic invoke, using an instance method. + + "Example that uses the PARAMETER-TABLE addition + "Creating an instance by specifying the type statically + "An example class of the cheat sheet repository is used. + DATA(oref1) = NEW zcl_demo_abap_objects( ). + "Calling an instance method + "The method multiplies an integer by 3. + "The calculation result is returned. + DATA(result) = oref1->triple( i_op = 2 ). + + out->write( data = result name = `result` ). + out->write( |\n| ). + + "Dynamic equivalent + "Creating an instance of a class by specifying the type + "dynamically + DATA oref2 TYPE REF TO object. + CREATE OBJECT oref2 TYPE ('ZCL_DEMO_ABAP_OBJECTS'). + + "Creating parameter table + DATA(ptab) = VALUE abap_parmbind_tab( ( name = 'I_OP' + kind = cl_abap_objectdescr=>exporting + value = NEW i( 3 ) ) + ( name = 'R_TRIPLE' + kind = cl_abap_objectdescr=>returning + value = NEW i( ) ) ). + + "Dynamic method call and specifying a parameter table + CALL METHOD oref2->('TRIPLE') PARAMETER-TABLE ptab. + result = ptab[ name = 'R_TRIPLE' ]-('VALUE')->*. + + out->write( data = result name = `result` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `53) RTTI: Getting Type Information at Runtime/Getting a Reference to a Type Description Object` ) ). + + "Getting a reference to a type description object of a type. + "i.e. getting an instance of a type description class + "As shown below, the type decription object can be used + "to create data objects dynamically. + + "Type for which information should be retrieved + TYPES: elem_type TYPE c LENGTH 5. + + "Creating a data reference variable to hold the reference to + "the type description object + DATA type_descr_obj_elem TYPE REF TO cl_abap_elemdescr. + + "Retrieving type information by creating an instance of a type description class + "As the name implies, the describe_by_name method expects the name of the type + "The following example uses the CAST operator for the necessary downcast. + type_descr_obj_elem = CAST #( cl_abap_typedescr=>describe_by_name( 'ELEM_TYPE' ) ). + + "Using the older ?= operator + type_descr_obj_elem ?= cl_abap_typedescr=>describe_by_name( 'ELEM_TYPE' ). + + "Inline declaration is handy to avoid helper variables. + DATA(type_descr_obj_elem_inl) = CAST cl_abap_elemdescr( + cl_abap_typedescr=>describe_by_name( 'ELEM_TYPE' ) ). + + "You may also want to check the type description object in the debugger. + out->write( data = type_descr_obj_elem_inl name = `type_descr_obj_elem_inl` ). + + "Various methods/attributes (note that they vary depending on the type) provide + "you with detailed information. + "The following examples show a selection. + "Kind/Type kind/Output length + DATA(kind_elem) = type_descr_obj_elem_inl->kind. + DATA(type_kind_elem) = type_descr_obj_elem_inl->type_kind. + DATA(output_length_elem) = type_descr_obj_elem_inl->output_length. + + out->write( data = kind_elem name = `kind_elem` ). + out->write( |\n| ). + out->write( data = type_kind_elem name = `type_kind_elem` ). + out->write( |\n| ). + out->write( data = output_length_elem name = `output_length_elem` ). + out->write( |\n| ). + + "In the following example, the type properties are retrieved + "without casting. The data object has the type ref to + "cl_abap_typedescr. See the hierarchy tree of type description classes. + "The reference in the type description object references an + "object from one of the classes CL_ABAP_ELEMDESCR, CL_ABAP_ENUMDESCR, + "CL_ABAP_REFDESCR, CL_ABAP_STRUCTDESCR, CL_ABAP_TABLEDSECR, + "CL_ABAP_CLASSDESCR, or CL_ABAP_INTFDESCR. + "In the following case, it is CL_ABAP_ELEMDESCR. + "Note that in most of the RTTI examples in this class, the explicit + "casting is included when retrieving a reference to the type + "description object. + TYPES another_elem_type TYPE n LENGTH 3. + DATA(type_descr_obj_elem_inl_2) = cl_abap_typedescr=>describe_by_name( 'ANOTHER_ELEM_TYPE' ). + + out->write( data = type_descr_obj_elem_inl_2->kind name = `type_descr_obj_elem_inl_2->kind` ). + out->write( |\n| ). + out->write( data = type_descr_obj_elem_inl_2->type_kind name = `type_descr_obj_elem_inl_2->type_kind` ). + out->write( |\n| ). + + "More types + "Structured data type (here, using the name of a database table) + DATA(type_descr_obj_struc) = CAST cl_abap_structdescr( + cl_abap_typedescr=>describe_by_name( 'ZDEMO_ABAP_CARR' ) ). + + "Various attributes/methods available for detailed information + "Kind + DATA(struc_kind) = type_descr_obj_struc->kind. + "Components of the structure (e.g. the component names and type description + "objects for the individual components) + DATA(comps_struc) = type_descr_obj_struc->get_components( ). + "The attribute also lists the component names and types (but not the type + "desription objects) + DATA(comps_struc2) = type_descr_obj_struc->components. + "Kind of structure + DATA(struct_kind) = type_descr_obj_struc->struct_kind. + + out->write( data = struc_kind name = `struc_kind` ). + out->write( |\n| ). + out->write( data = comps_struc name = `comps_struc` ). + out->write( |\n| ). + out->write( data = comps_struc2 name = `comps_struc2` ). + out->write( |\n| ). + out->write( data = struct_kind name = `struct_kind` ). + out->write( |\n| ). + + "Internal table type + TYPES table_type TYPE SORTED TABLE OF zdemo_abap_carr WITH UNIQUE KEY carrid. + + DATA(type_descr_obj_tab) = CAST cl_abap_tabledescr( + cl_abap_typedescr=>describe_by_name( 'TABLE_TYPE' ) ). + + "Kind + DATA(tab_kind) = type_descr_obj_tab->kind. + "The following method returns more information than the attribute below + "(e.g. key kind (unique) etc.) + DATA(tab_keys) = type_descr_obj_tab->get_keys( ). + DATA(tab_keys2) = type_descr_obj_tab->key. "Attribute; lists the keys + "Getting internal table components + "The method get_table_line_type returns a variable of type ref to cl_abap_datadescr. + "This way you can retrieve the table components. Method chaining comes in handy. + DATA(tab_comps) = CAST cl_abap_structdescr( + type_descr_obj_tab->get_table_line_type( ) )->get_components( ). + + out->write( data = tab_kind name = `tab_kind` ). + out->write( |\n| ). + out->write( data = tab_keys name = `tab_keys` ). + out->write( |\n| ). + out->write( data = tab_keys2 name = `tab_keys2` ). + out->write( |\n| ). + out->write( data = tab_comps name = `tab_comps` ). + out->write( |\n| ). + + "Reference type + TYPES ref_str TYPE REF TO string. + DATA(type_descr_obj_ref) = CAST cl_abap_refdescr( + cl_abap_typedescr=>describe_by_name( 'REF_STR' ) ). + + "Kind + DATA(ref_kind) = type_descr_obj_ref->kind. + "Returns type description object of the referenced type + DATA(ref_type) = type_descr_obj_ref->get_referenced_type( ). + "Absolute type name + DATA(ref_type_abs_name) = + type_descr_obj_ref->get_referenced_type( )->absolute_name. + "Type kind + DATA(ref_type_type_kind) = + type_descr_obj_ref->get_referenced_type( )->type_kind. + + out->write( data = ref_kind name = `ref_kind` ). + out->write( |\n| ). + out->write( data = ref_type name = `ref_type` ). + out->write( |\n| ). + out->write( data = ref_type_abs_name name = `ref_type_abs_name` ). + out->write( |\n| ). + out->write( data = ref_type_type_kind name = `ref_type_type_kind` ). + + "Getting a reference to a type description object of an existing data object. + "Instead of referring to the name of a type, referring to a data object here. + "The relevant method is describe_by_data + + "Elementary data object + DATA dobj_elem TYPE i. + DATA(ty_des_obj_el) = CAST cl_abap_elemdescr( + cl_abap_typedescr=>describe_by_data( dobj_elem ) ). + + "Structure + DATA dobj_struc TYPE zdemo_abap_carr. + DATA(ty_des_obj_struc) = CAST cl_abap_structdescr( + cl_abap_typedescr=>describe_by_data( dobj_struc ) ). + + "Internal table + DATA dobj_itab TYPE TABLE OF zdemo_abap_carr WITH EMPTY KEY. + DATA(ty_des_obj_itab) = CAST cl_abap_tabledescr( + cl_abap_typedescr=>describe_by_data( dobj_itab ) ). + + "Reference variable + DATA dref_var TYPE REF TO string. + DATA(ty_des_obj_dref) = CAST cl_abap_refdescr( + cl_abap_typedescr=>describe_by_data( dref_var ) ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `54) RTTI: Getting Type Information at Runtime for Miscellaneous Types` ) ). + + "The example demonstrates RTTI as follows: + "- The method call takes care of providing the name of a type. It is implemented + " in a way that various types can be returned, i. e. elementary, structure, + " internal table, reference, class, interface. + "- If the retrieved type is not a class or interface, a data object is created + " based on the type determined at runtime. + "- The type description is retrieved using the method cl_abap_typedescr=>describe_by_data. + " Note the casts for the information retrieval statements. + "- Depending on the type kind, various pieces of information are retrieved. There + " are plenty of options. In ADT, you can use the input help. Just position the + " cursor after the reference variable and ->, e.g. el->, and hit CTRL-Space. + " A dropdown appears showing you the variety you can explore. Check the class + " documentation for more information. + "- If the retrieved type is a class or interface, the type description is + " retrieved using cl_abap_typedescr=>describe_by_name. + "- The example for a class type includes the creation of an object based + " on a type determined at runtime using a CREATE OBJECT statement. + + "Retrieving type + DATA(get_type) = lcl_det_at_runtime=>get_random_type( ). + + out->write( |Type name determined at runtime: { get_type }| ). + out->write( |\n| ). + + DATA dref TYPE REF TO data. + + IF get_type <> `LCL_DET_AT_RUNTIME` + AND get_type <> `IF_OO_ADT_CLASSRUN`. + TRY. + CREATE DATA dref TYPE (get_type). + CATCH cx_sy_create_data_error. + out->write( `Create data error!` ). + ENDTRY. + + "Retrieving type information + "When referring to a concrete data object name, you can use this method: + DATA(some_type) = cl_abap_typedescr=>describe_by_data( dref->* ). + + "Elementary type + IF some_type->kind = cl_abap_typedescr=>kind_elem. + DATA(el) = CAST cl_abap_elemdescr( some_type ). + out->write( data = el name = `el` ). + "Various attributes and methods possible + out->write( data = el->type_kind name = `el->type_kind` ). + out->write( |\n| ). + out->write( data = el->absolute_name name = `el->absolute_name` ). + out->write( |\n| ). + out->write( data = el->get_relative_name( ) name = `el->get_relative_name( )` ). + out->write( |\n| ). + + "Structure + ELSEIF some_type->kind = cl_abap_typedescr=>kind_struct. + DATA(stru) = CAST cl_abap_structdescr( some_type ). + out->write( data = stru->absolute_name name = `stru->absolute_name` ). + out->write( |\n| ). + out->write( data = stru->components name = `stru->components` ). + out->write( |\n| ). + out->write( data = stru->struct_kind name = `stru->struct_kind` ). + out->write( |\n| ). + out->write( data = stru->get_components( ) name = `stru->get_components( )` ). + out->write( |\n| ). + + "Internal table + ELSEIF some_type->kind = cl_abap_typedescr=>kind_table. + DATA(tab) = CAST cl_abap_tabledescr( some_type ). + out->write( data = tab->absolute_name name = `tab->absolute_name` ). + out->write( |\n| ). + out->write( data = tab->table_kind name = `tab->table_kind` ). + out->write( |\n| ). + out->write( data = tab->get_keys( ) name = `tab->get_keys` ). + out->write( |\n| ). + out->write( data = tab->get_table_line_type( ) name = `tab->get_table_line_type( )` ). + out->write( |\n| ). + + "Reference + ELSEIF some_type->kind = cl_abap_typedescr=>kind_ref. + DATA(ref_descr) = CAST cl_abap_refdescr( some_type ). + out->write( data = ref_descr->absolute_name name = `ref_descr->absolute_name` ). + out->write( |\n| ). + out->write( data = ref_descr->get_referenced_type( ) name = `ref_descr->get_referenced_type( )` ). + out->write( |\n| ). + ELSE. + out->write( `Others ...` ). + ENDIF. + + ELSE. + + "Retrieving type information + "Here, using the type name and not a concrete data object as above. + some_type = cl_abap_typedescr=>describe_by_name( get_type ). + + "Class + IF some_type->kind = cl_abap_typedescr=>kind_class. + DATA(class_desc) = CAST cl_abap_classdescr( some_type ). + out->write( data = class_desc->absolute_name name = `class_desc->absolute_name` ). + out->write( |\n| ). + out->write( data = class_desc->attributes name = `class_desc->attributes` ). + out->write( |\n| ). + out->write( data = class_desc->methods name = `class_desc->methods` ). + out->write( |\n| ). + + "Creating an object based on a type determined at runtime + DATA oref TYPE REF TO object. + + TRY. + CREATE OBJECT oref TYPE (get_type). + "Retrieving type information + DATA(descr_ref) = cl_abap_typedescr=>describe_by_object_ref( oref ). + out->write( data = descr_ref->absolute_name name = `descr_ref->absolute_name` ). + out->write( |\n| ). + out->write( data = descr_ref->kind name = `descr_ref->kind` ). + out->write( |\n| ). + CATCH cx_root. + out->write( `Error` ). + ENDTRY. + + "Interface + ELSEIF some_type->kind = cl_abap_typedescr=>kind_intf. + DATA(if_descr) = CAST cl_abap_intfdescr( some_type ). + out->write( data = if_descr->absolute_name name = `if_descr->absolute_name` ). + out->write( |\n| ). + out->write( data = if_descr->methods name = `class_desc->methods` ). + out->write( |\n| ). + ELSE. + out->write( `Others ...` ). + ENDIF. + ENDIF. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `55) RTTC: Dynamically Creating Data Types at Runtime` ) ). + + "You can create data types at program runtime using methods of the type + "description classes of RTTS. These types are only valid locally in the + "program. They are also anonymous, i.e. they are only accessible through + "type description objects. As shown above, you can get a reference to a + "type description object of a type using the static methods of the class + "CL_ABAP_TYPEDESCR. The focus here is on using RTTC methods such as get*. + + "Creating type description objects using ... + "... elementary data types + "Conceptually, all elementary, built-in ABAP types already exist and can + "be accessed by the corresponding get_* methods. + "In ADT, click CTRL + space after cl_abap_elemdescr=>... to check out the options. + "The following examples show a selection. + DATA(tdo_elem_i) = cl_abap_elemdescr=>get_i( ). + DATA(tdo_elem_string) = cl_abap_elemdescr=>get_string( ). + "For the length specification of type c, there is an importing parameter available. + DATA(tdo_elem_c_l20) = cl_abap_elemdescr=>get_c( 10 ). + "Type p with two parameters to be specified. + DATA(tdo_elem_p) = cl_abap_elemdescr=>get_p( p_length = 3 p_decimals = 2 ). + + "Instead of calling get_i() and others having no importing parameters, you could also call + "the describe_by_name( ) method and pass the type names (I‚ STRING etc.) as arguments. + "DATA(tdo_elem_i_2) = CAST cl_abap_elemdescr( + " cl_abap_typedescr=>describe_by_name( 'I' ) ). + "DATA(tdo_elem_string_2) = CAST cl_abap_elemdescr( + " cl_abap_typedescr=>describe_by_name( 'STRING' ) ). + + "... structured data types + "They are created based on a component description table. + + "A structured type such as the following shall be created using a + "type description object. + TYPES: + BEGIN OF struc_type, + a TYPE string, + b TYPE i, + c TYPE c LENGTH 5, + d TYPE p LENGTH 4 DECIMALS 3, + END OF struc_type. + + "Creating a type description object using RTTC method + "Using the get method, you can create the type description object + "dynamically based on a component table. The component table is of type + "abap_component_tab. In this example, the component table is created inline. + DATA(tdo_struc) = cl_abap_structdescr=>get( + VALUE #( + ( name = 'A' type = cl_abap_elemdescr=>get_string( ) ) + ( name = 'B' type = cl_abap_elemdescr=>get_i( ) ) + ( name = 'C' type = cl_abap_elemdescr=>get_c( 5 ) ) + ( name = 'D' type = cl_abap_elemdescr=>get_p( p_length = 4 p_decimals = 3 ) ) ) ). + + "... internal table types + "Note: Specifying the line type is mandatory, the rest is optional. + + "An internal table type such as the following shall be created using a + "type description object. + TYPES std_tab_type_std_key TYPE STANDARD TABLE OF string WITH DEFAULT KEY. + + "Creating a type description object using RTTC method + "Not specifying the other optional parameters means that the + "default values are used, for example, standard table is the + "default value for p_table_kind. + DATA(tdo_tab_1) = cl_abap_tabledescr=>get( + p_line_type = cl_abap_elemdescr=>get_string( ) ). + + "Another internal table type for which more parameter specifications are needed + "The following internal table type shall be created using a type description object. + TYPES so_table_type TYPE SORTED TABLE OF zdemo_abap_flsch WITH UNIQUE KEY carrid connid. + + "Creating a type description object using RTTC method + "The following example also demonstrates how comfortably constructor + "operators can be used at these positions. + DATA(tdo_tab_2) = cl_abap_tabledescr=>get( + p_line_type = CAST cl_abap_structdescr( cl_abap_tabledescr=>describe_by_name( 'ZDEMO_ABAP_FLSCH' ) ) + p_table_kind = cl_abap_tabledescr=>tablekind_sorted + p_key = VALUE #( ( name = 'CARRID' ) ( name = 'CONNID' ) ) + p_unique = cl_abap_typedescr=>true ). + + " ... reference types + "Reference types such as the following shall be created using a + "type description object. + TYPES some_ref_type2t TYPE REF TO t. + TYPES some_ref_type2cl TYPE REF TO zcl_demo_abap_dynamic_prog. + + "Using RTTC methods + "You can create a reference type from a base type. This base type + "may be class, interface or data type. + DATA(tdo_ref_1) = cl_abap_refdescr=>get( cl_abap_elemdescr=>get_t( ) ). + DATA(tdo_ref_2) = cl_abap_refdescr=>get( cl_abap_typedescr=>describe_by_name( 'ZCL_DEMO_ABAP_DYNAMIC_PROG' ) ). + "Alternative: get_by_name method + DATA(tdo_ref_3) = cl_abap_refdescr=>get_by_name( 'T' ). + DATA(tdo_ref_4) = cl_abap_refdescr=>get_by_name( 'ZCL_DEMO_ABAP_DYNAMIC_PROG' ). + + out->write( `No output for this section. You can set a break point and check the content of data objects in the debugger.` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `56) Dynamically Creating Data Objects at Runtime Using Type Description Objects (1) - Miscellaneous` ) ). + + "Using the TYPE HANDLE addition to CREATE DATA statements, you can + "dynamically create data objects at runtime based on type description objects. + "The following example uses type description objects from the previous example. + "For output purposes, the created data objects are assigned values. + + DATA dref_typ_obj TYPE REF TO data. + + "Elementary data object + CREATE DATA dref_typ_obj TYPE HANDLE tdo_elem_i. + dref_typ_obj->* = 5 + 4. + + out->write( data = dref_typ_obj->* name = `dref_typ_obj->*` ). + out->write( |\n| ). + + "Structured data object + CREATE DATA dref_typ_obj TYPE HANDLE tdo_struc. + dref_typ_obj->('A') = `hello`. + dref_typ_obj->('B') = 4 + 3. + dref_typ_obj->('C') = 'abcde'. + dref_typ_obj->('D') = '1.234'. + + out->write( data = dref_typ_obj->* name = `dref_typ_obj->*` ). + out->write( |\n| ). + + "Internal table + CREATE DATA dref_typ_obj TYPE HANDLE tdo_tab_2. + SELECT * FROM zdemo_abap_flsch INTO TABLE @dref_typ_obj->* UP TO 3 ROWS. + + out->write( data = dref_typ_obj->* name = `dref_typ_obj->*` ). + out->write( |\n| ). + + "Reference + CREATE DATA dref_typ_obj TYPE HANDLE tdo_ref_3. + dref_typ_obj->* = NEW t( '120000' ). + + out->write( data = dref_typ_obj->* name = `dref_typ_obj->*` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `57) Dynamically Creating Data Objects at Runtime Using Type Description Objects (2) - Structure` ) ). + + "This example includes the dynamic definition of a structure with three components + "using the GET method of the CL_ABAP_STRUCTDESCR class. + + DATA: struct_type TYPE REF TO cl_abap_structdescr, + dref_struc TYPE REF TO data. + + DATA column1 TYPE c LENGTH 5. + DATA column2 TYPE c LENGTH 5. + DATA column3 TYPE c LENGTH 5. + + "Potential component names + DATA(comp_names) = VALUE string_table( ( `A` ) ( `B` ) ( `C` ) ( `D` ) ( `E` ) ( `F` ) ). + + "The structure should contain 3 components. + DO 3 TIMES. + + "Getting a random integer that represents the table index + "The line (the component name) is deleted from the table so as to + "guarantee unique component names. + DATA(num) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 1 + max = lines( comp_names ) )->get_next( ). + CASE sy-index. + WHEN 1. + column1 = comp_names[ num ]. + WHEN 2. + column2 = comp_names[ num ]. + WHEN 3. + column3 = comp_names[ num ]. + ENDCASE. + + DELETE comp_names INDEX num. + + ENDDO. + + "All components should be typed with c length 3 + struct_type = cl_abap_structdescr=>get( + VALUE #( + ( name = column1 type = cl_abap_elemdescr=>get_c( 3 ) ) + ( name = column2 type = cl_abap_elemdescr=>get_c( 3 ) ) + ( name = column3 type = cl_abap_elemdescr=>get_c( 3 ) ) ) ). + + "Creating structured data object + CREATE DATA dref_struc TYPE HANDLE struct_type. + + "Assigning values to the structure components + dref_struc->(column1) = 'abc'. + dref_struc->(column2) = 'def'. + dref_struc->(column3) = 'ghi'. + + out->write( data = dref_struc->* name = `dref_struc->*` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `58) Dynamically Creating Data Objects at Runtime Using Type Description Objects (3) - Internal Table` ) ). + + "In the example an internal table type is created based on a DDIC type. + "See the comments in the code. + + "Retrieving table name + DATA(table_name) = lcl_det_at_runtime=>get_dyn_table_name( ). + + "Retrieving type information using RTTI + DATA(st) = CAST cl_abap_structdescr( + cl_abap_tabledescr=>describe_by_name( table_name ) ). + + "Declaring an internal table to hold the components; + "it will include the component name and the component type + DATA comp_table TYPE cl_abap_structdescr=>component_table. + + "Looping across the retrieved field list to extract information + "In principle, you could also just use method get_components( ) :) + LOOP AT st->components ASSIGNING FIELD-SYMBOL(). + + "Adding name of the component and its type, which is retrieved using the + "get_component_type method, are added to the internal table that holds the components + APPEND VALUE #( name = -name + type = st->get_component_type( -name ) ) TO comp_table. + + "The SELECT statement further down includes a dynamic specification + "of the ORDER BY clause. + "In this case, just using the second field since MANDT is the first. + IF sy-tabix = 2. + DATA(dyn_order_by) = -name. + ENDIF. + + ENDLOOP. + + "Creating an internal table type + "Note: The parameter p_key is not filled here, i. e. the default key is used. + DATA(itab_type) = cl_abap_tabledescr=>create( + p_line_type = st + p_table_kind = cl_abap_tabledescr=>tablekind_sorted + p_unique = cl_abap_typedescr=>true ). + + "Creating an internal table based on the created table type + DATA ref_tab TYPE REF TO data. + CREATE DATA ref_tab TYPE HANDLE itab_type. + + "Filling an internal table + SELECT * + FROM (table_name) + ORDER BY (dyn_order_by) + INTO CORRESPONDING FIELDS OF TABLE @ref_tab->* + UP TO 3 ROWS. + + out->write( |Type/Database table name determined at runtime: { table_name }| ). + out->write( |\n| ). + out->write( |Internal table entries (ordered by { dyn_order_by }):| ). + out->write( |\n| ). + out->write( |\n| ). + out->write( data = ref_tab->* name = `ref_tab->*` ). + + ENDMETHOD. +ENDCLASS. \ No newline at end of file diff --git a/src/zcl_demo_abap_dynamic_prog.clas.locals_imp.abap b/src/zcl_demo_abap_dynamic_prog.clas.locals_imp.abap new file mode 100644 index 0000000..0dc37f4 --- /dev/null +++ b/src/zcl_demo_abap_dynamic_prog.clas.locals_imp.abap @@ -0,0 +1,437 @@ +CLASS lcl_det_at_runtime DEFINITION. + + PUBLIC SECTION. + + CLASS-DATA: string1 TYPE string, + string2 TYPE string, + string3 TYPE string, + dyn_meth_call_result TYPE string. + + TYPES: type_p TYPE p LENGTH 8 DECIMALS 2, "elementary type + type_struc TYPE zdemo_abap_carr, "structure type + "internal table type + type_itab TYPE SORTED TABLE OF zdemo_abap_flsch WITH NON-UNIQUE KEY carrid connid "primary key + WITH UNIQUE SORTED KEY cities COMPONENTS cityfrom cityto, "secondary key + type_ref TYPE REF TO lcl_det_at_runtime. "reference type + + TYPES: BEGIN OF struc_builtin, + builtin_type TYPE c LENGTH 10, + len TYPE i, + dec TYPE i, + END OF struc_builtin. + + TYPES: BEGIN OF struc_dyn, + table TYPE string, + select_list TYPE string, + where_clause TYPE string_table, + order_by TYPE string, + target TYPE REF TO data, + rows TYPE i, + END OF struc_dyn. + + CLASS-METHODS: + get_dyn_table_name RETURNING VALUE(tab) TYPE string, + get_dyn_dobj RETURNING VALUE(dobj) TYPE string, + get_dyn_field RETURNING VALUE(field) TYPE string, + get_dyn_select_list RETURNING VALUE(list) TYPE string, + get_dyn_where_clause RETURNING VALUE(clause_tab) TYPE string_table, + get_random_type RETURNING VALUE(random_type) TYPE string, + get_builtin_type RETURNING VALUE(builtin_type) TYPE struc_builtin, + get_dyn_class_meth EXPORTING cl TYPE string + meth TYPE string + ptab TYPE abap_parmbind_tab, + get_dyn_syntax_elements RETURNING VALUE(syntax_elements) TYPE struc_dyn, + fill_string. + + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + +CLASS lcl_det_at_runtime IMPLEMENTATION. + METHOD get_dyn_table_name. + + "Providing DDIC table names in a string table to be selected from. + DATA(flight_tables) = VALUE string_table( + ( `ZDEMO_ABAP_CARR` ) ( `ZDEMO_ABAP_FLSCH` ) ( `ZDEMO_ABAP_FLI` ) ). + + "Getting random number to determine the table index at runtime. + DATA(idx) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 1 + max = lines( flight_tables ) )->get_next( ). + + "Returning parameter to receive the random table name. + tab = VALUE #( flight_tables[ idx ] DEFAULT `ZDEMO_ABAP_CARR` ). + + ENDMETHOD. + + METHOD get_builtin_type. + + "Providing the names of built-in types in a string table to be selected from. + TYPES tabtyp TYPE TABLE OF struc_builtin-builtin_type WITH EMPTY KEY. + + DATA(built) = VALUE tabtyp( + ( 'd' ) + ( 'decfloat16' ) + ( 'decfloat34' ) + ( 'f' ) + ( 'i' ) + ( 'string' ) + ( 't' ) + ( 'xstring' ) + ( 'c' ) + ( 'n' ) + ( 'x' ) + ( 'p' ) + ). + + "Getting random number to determine the table index at runtime + DATA(idx) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 1 + max = lines( built ) )->get_next( ). + + "Providing the returning parameter with a random type name + TRY. + builtin_type = VALUE #( builtin_type = built[ idx ] dec = idx len = idx ). + CATCH cx_sy_itab_line_not_found. + builtin_type = VALUE #( builtin_type = `p` dec = 5 len = 5 ). + ENDTRY. + + ENDMETHOD. + + METHOD get_dyn_dobj. + + "Providing strings with demo content + string1 = |Hallo, { sy-uname }. | && + |This is string1.|. + string2 = |Hallo, { sy-uname }. | && + |This is string2.|. + string3 = |Hallo, { sy-uname }. | && + |This is string3.|. + + "Filling table with data object names + DATA(str_tab) = VALUE string_table( + ( `STRING1` ) ( `STRING2` ) ( `STRING3` ) ). + + "Getting random number to determine the table index at runtime + DATA(idx) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 1 + max = lines( str_tab ) )->get_next( ). + + "Providing the returning parameter with a random data object name + dobj = VALUE #( str_tab[ idx ] DEFAULT |Hallo, { sy-uname }. This is a string.| ). + + ENDMETHOD. + + METHOD get_dyn_field. + + "Getting list of components of DDIC type zdemo_abap_carr + DATA(comp) = CAST cl_abap_structdescr( + cl_abap_typedescr=>describe_by_name( + 'ZDEMO_ABAP_CARR' ) + )->components. + + "Getting random number to determine the table index at runtime; + "starting from 2 to exclude MANDT field + DATA(idx) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 2 + max = lines( comp ) )->get_next( ). + + "Providing the returning parameter with a random component name + field = VALUE #( comp[ idx ]-name DEFAULT `CARRID` ). + + ENDMETHOD. + + METHOD get_dyn_select_list. + + "Providing SELECT lists in a string table to be selected from + DATA sel_list_tab TYPE string_table. + sel_list_tab = VALUE #( + ( `CARRID, CONNID, COUNTRYFR, COUNTRYTO` ) + ( `CARRID, CONNID, CITYFROM, CITYTO` ) + ( `CARRID, CONNID, AIRPFROM, AIRPTO` ) + ( `CARRID, CONNID, AIRPFROM, AIRPTO, ` && + `FLTIME, DEPTIME, ARRTIME, DISTANCE` ) + ). + + "Getting random number to determine the table index at runtime + DATA(idx) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 1 + max = lines( sel_list_tab ) )->get_next( ). + + "Providing the returning parameter with a random SELECT list + list = VALUE #( sel_list_tab[ idx ] DEFAULT `CARRID, CONNID, COUNTRYFR, COUNTRYTO` ). + + ENDMETHOD. + + METHOD get_dyn_where_clause. + + "Providing WHERE clauses in a table to be selected from + DATA: BEGIN OF where_struc, + where_clause_tab TYPE string_table, + END OF where_struc. + + DATA where_itab LIKE TABLE OF where_struc WITH EMPTY KEY. + + where_itab = VALUE #( + ( where_clause_tab = VALUE #( ( `CARRID = 'LH'` ) + ( `OR CARRID = 'AA'` ) ) ) + ( where_clause_tab = VALUE #( ( `CONNID BETWEEN 0 AND 300` ) ) ) + ( where_clause_tab = VALUE #( ( `CITYFROM LIKE '%FRA%'` ) ) ) + ( where_clause_tab = + VALUE #( ( `DISTANCE > 500 AND DISTID = 'KM'` ) ) ) ). + + "Getting random number to determine the table index at runtime + DATA(idx) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 1 + max = lines( where_itab ) )->get_next( ). + + "Providing the returning parameter with a random WHERE clause + clause_tab = VALUE #( where_itab[ idx ]-where_clause_tab DEFAULT VALUE #( ( `CARRID = 'LH'` ) ( `OR CARRID = 'AA'` ) ) ). + + ENDMETHOD. + + METHOD get_random_type. + + "Providing names of classes in a string table to be selected from + "In this example, some types are defined in the public section + "of a local class. The class name is added here since the names + "are used in the global class. + DATA(str_tab) = VALUE string_table( + ( `LCL_DET_AT_RUNTIME=>TYPE_P` ) + ( `LCL_DET_AT_RUNTIME=>TYPE_STRUC` ) + ( `LCL_DET_AT_RUNTIME=>TYPE_ITAB` ) + ( `LCL_DET_AT_RUNTIME=>TYPE_REF` ) + ( `LCL_DET_AT_RUNTIME` ) + ( `IF_OO_ADT_CLASSRUN` ) + ). + + "Getting random number to determine the table index at runtime + DATA(idx) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 1 + max = lines( str_tab ) )->get_next( ). + + "Providing the returning parameter with a random type name + random_type = VALUE #( str_tab[ idx ] DEFAULT `LCL_DET_AT_RUNTIME=>TYPE_STRUC` ). + + ENDMETHOD. + + METHOD get_dyn_class_meth. + + "Providing class names in a string table to be selected from + DATA(class_tab) = VALUE string_table( + ( `LCL_DEMO1` ) + ( `LCL_DEMO2` ) ). + + "Getting random number to determine the table index at runtime + DATA(idx) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 1 + max = lines( class_tab ) )->get_next( ). + + "Provide the exporting parameter with the random class name + cl = VALUE #( class_tab[ idx ] DEFAULT `LCL_DEMO1` ). + + "Getting method names using RTTI + DATA(methods) = CAST cl_abap_classdescr( cl_abap_typedescr=>describe_by_name( cl ) )->methods. + + "Getting random number to determine the table index at runtime + idx = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) min = 1 + max = lines( methods ) )->get_next( ). + + "Provide the exporting parameter with the random method name + meth = VALUE #( methods[ idx ]-name DEFAULT `METH_A` ). + + "Data reference objects for the value parameter in the parameter table + DATA(ref_imp) = NEW string( `hi` ). + DATA(ref_exp) = NEW string( `hallo` ). + DATA(ref_ch) = NEW string( `salut` ). + DATA(ref_ret) = NEW string( `ciao` ). + + "Filling the parameter tables + "Note: If the method signature has an importing parameter, + "it must be specified as exporting parameter here. + "Same is true for the exporting parameter in the signature + "that must be specified as importing parameter. + + CASE meth. + + WHEN `METH_A`. + + ptab = VALUE #( ( name = 'A' + kind = cl_abap_objectdescr=>exporting + value = ref_exp ) + ( name = 'B' + kind = cl_abap_objectdescr=>importing + value = ref_imp ) ). + + WHEN `METH_B`. + + ptab = VALUE #( ( name = 'C' + kind = cl_abap_objectdescr=>changing + value = ref_ch ) + ( name = 'D' + kind = cl_abap_objectdescr=>returning + value = ref_ret ) ). + + WHEN `METH_C`. + + ptab = VALUE #( ( name = 'E' + kind = cl_abap_objectdescr=>exporting + value = ref_exp ) + ( name = 'F' + kind = cl_abap_objectdescr=>importing + value = ref_imp ) ). + + WHEN `METH_D`. + + ptab = VALUE #( ( name = 'G' + kind = cl_abap_objectdescr=>changing + value = ref_ch ) + ( name = 'H' + kind = cl_abap_objectdescr=>returning + value = ref_ret ) ). + + ENDCASE. + + ENDMETHOD. + + METHOD fill_string. + dyn_meth_call_result = |Hallo { sy-uname }. The string was filled at { utclong_current( ) }.|. + ENDMETHOD. + + METHOD get_dyn_syntax_elements. + + "FROM clause + DATA(flight_tables) = VALUE string_table( + ( `ZDEMO_ABAP_CARR` ) ( `ZDEMO_ABAP_FLSCH` ) ( `ZDEMO_ABAP_FLI` ) ). + + "Getting random number to determine the table index at runtime. + DATA(idx_table) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) + min = 1 + max = lines( flight_tables ) )->get_next( ). + + syntax_elements-table = VALUE #( flight_tables[ idx_table ] DEFAULT `ZDEMO_ABAP_CARR` ). + + "SELECT list + DATA(comp) = CAST cl_abap_structdescr( + cl_abap_typedescr=>describe_by_name( syntax_elements-table ) )->components. + + "At least 3 components + DATA(idx_comp) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) + min = 3 + max = lines( comp ) )->get_next( ). + + DELETE comp FROM idx_comp + 1 TO lines( comp ). + + LOOP AT comp ASSIGNING FIELD-SYMBOL(). + syntax_elements-select_list = syntax_elements-select_list && `, ` && -name. + ENDLOOP. + + "Replacing initial comma + REPLACE PCRE `^,\s` IN syntax_elements-select_list WITH ``. + + "WHERE clause + "Excluding the client field + DELETE comp WHERE name = 'MANDT'. + + "A maximum of 4 fields are to be respected because that is the maximum of possible fields + "available in table zdemo_abap_carr (without the client field) + DATA(idx_where) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) + min = 1 + max = 4 )->get_next( ). + + "In the example, the WHERE clause consists of an internal table of type string. + "The clause is set up with fields and only IS NOT INITIAL to be on the safe side for a + "somewhat 'meaningful' clause and in the interest of simplicity. + LOOP AT comp ASSIGNING FIELD-SYMBOL() TO idx_where. + IF sy-tabix = 1. + APPEND -name && ` IS NOT INITIAL` TO syntax_elements-where_clause. + ELSE. + APPEND `OR ` && -name && ` IS NOT INITIAL` TO syntax_elements-where_clause. + ENDIF. + ENDLOOP. + + "ORDER BY clause + DATA(idx_order) = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) + min = 1 + max = lines( comp ) )->get_next( ). + + syntax_elements-order_by = VALUE #( comp[ idx_order ]-name DEFAULT `CARRID` ). + + "INTO clause + CREATE DATA syntax_elements-target TYPE TABLE OF (syntax_elements-table). + + "UP TO ... ROWS + "A minimum of 2 and a maximum of 6 rows are to be retrieved by the SELECT statement + syntax_elements-rows = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) + min = 2 + max = 5 )->get_next( ). + + ENDMETHOD. + +ENDCLASS. + +CLASS lcl_demo1 DEFINITION. +"Note that this is just a demo class with demo methods to work with in the example. + PUBLIC SECTION. + + CLASS-METHODS: + meth_a IMPORTING a TYPE string + EXPORTING b TYPE string, + meth_b CHANGING c TYPE string + RETURNING VALUE(d) TYPE string. + +ENDCLASS. + +CLASS lcl_demo1 IMPLEMENTATION. + METHOD meth_a. + b = |Hallo from meth_a. '{ a }' was input.|. + + "Filling an attribute for the output in the global class. + lcl_det_at_runtime=>dyn_meth_call_result = b. + ENDMETHOD. + + METHOD meth_b. + c = `#` && c && `#`. + d = |Hallo from meth_b. Actual parameter of changing parameter: '{ c }'|. + + "Filling an attribute for the output in the global class. + lcl_det_at_runtime=>dyn_meth_call_result = d. + ENDMETHOD. + +ENDCLASS. + +CLASS lcl_demo2 DEFINITION. +"Note that this is just a demo class with demo methods to work with in the example. + PUBLIC SECTION. + + CLASS-METHODS: + meth_c IMPORTING e TYPE string + EXPORTING f TYPE string, + meth_d CHANGING g TYPE string + RETURNING VALUE(h) TYPE string. + +ENDCLASS. + +CLASS lcl_demo2 IMPLEMENTATION. + METHOD meth_c. + f = |Hallo from meth_c. '{ e }' was input.|. + + "Filling an attribute for the output in the global class. + lcl_det_at_runtime=>dyn_meth_call_result = f. + ENDMETHOD. + + METHOD meth_d. + g = `*` && g && `*`. + h = |Hallo from meth_d. Actual parameter of changing parameter: '{ g }'|. + + "Filling an attribute for the output in the global class. + lcl_det_at_runtime=>dyn_meth_call_result = h. + ENDMETHOD. + +ENDCLASS. diff --git a/src/zcl_demo_abap_dynamic_prog.clas.xml b/src/zcl_demo_abap_dynamic_prog.clas.xml new file mode 100644 index 0000000..397ccf8 --- /dev/null +++ b/src/zcl_demo_abap_dynamic_prog.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_DYNAMIC_PROG + E + ABAP cheat sheet: Dynamic programming + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_internal_tables.clas.abap b/src/zcl_demo_abap_internal_tables.clas.abap new file mode 100644 index 0000000..59d342c --- /dev/null +++ b/src/zcl_demo_abap_internal_tables.clas.abap @@ -0,0 +1,1892 @@ +*********************************************************************** +* +* ABAP cheat sheet: Internal tables +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntax options for working with +* internal tables. +* - Topics covered: Creating, filling, reading from, sorting, modifying +* internal tables +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP development tools for Eclipse (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, refer to the +* notes included in the class as comments or refer to the respective +* topic in the ABAP Keyword Documentation. +* - Due to the amount of console output, the examples contain numbers +* (e.g. 1) ..., 2) ..., 3) ...) for the individual example sections. +* Also, the variable name is displayed in most cases. So to find +* the relevant output in the console easier and faster, just search +* for the number/variable name in the console (CTRL+F in the console) +* or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

    ABAP cheat sheet: Internal tables

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

    ABAP cheat sheet: ABAP object orientation

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

    Class supporting ABAP cheat sheet example

    + "! The class supports the ABAP cheat sheet example about object orientation. + CLASS zcl_demo_abap_objects_friend DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + CLASS-METHODS get_strings RETURNING VALUE(res_string) TYPE string_table. + +protected section. +private section. +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_OBJECTS_FRIEND IMPLEMENTATION. + + + METHOD get_strings. + "Getting the strings and adding them to the string table. + "Accessing an attribute in a public visibility section. + APPEND zcl_demo_abap_objects=>public_string TO res_string. + + "Accessing an attribute in a protected visibility section. + APPEND zcl_demo_abap_objects=>protected_string TO res_string. + + "Accessing an attribute in a private visibility section. + APPEND zcl_demo_abap_objects=>private_string TO res_string. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_objects_friend.clas.xml b/src/zcl_demo_abap_objects_friend.clas.xml new file mode 100644 index 0000000..f0de8f5 --- /dev/null +++ b/src/zcl_demo_abap_objects_friend.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_OBJECTS_FRIEND + E + Class for ABAP cheat sheet example + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_prog_flow_logic.clas.abap b/src/zcl_demo_abap_prog_flow_logic.clas.abap new file mode 100644 index 0000000..54a7cd9 --- /dev/null +++ b/src/zcl_demo_abap_prog_flow_logic.clas.abap @@ -0,0 +1,1325 @@ +*********************************************************************** +* +* 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 for Eclipse (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, refer to the +* notes included in the class as comments or refer to the respective +* topic in the ABAP Keyword Documentation. +* - Due to the amount of console output, the examples contain numbers +* (e.g. 1) ..., 2) ..., 3) ...) for the individual example sections. +* Also, the variable name is displayed in most cases. So to find +* the relevant output in the console easier and faster, just search +* for the number/variable name in the console (CTRL+F in the console) +* or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

    ABAP cheat sheet: Program flow logic

    +"! Example to demonstrate 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. + + 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 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 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(). + "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. + + 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(). + TRY. + out->write( |--- Calculations with { } ---| ). + + DATA(calc1) = CONV decfloat34( 1 / ). + + out->write( data = calc1 name = `calc1` ). + out->write( |\n| ). + + DATA(calc2) = ipow( base = 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(). + TRY. + out->write( |--- Calculations with { } ---| ). + + calc1 = 1 / . + + out->write( data = calc1 name = `calc1` ). + out->write( |\n| ). + + calc2 = ipow( base = 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(). + + TRY. + out->write( |--- Calculations with { } ---| ). + + calc1 = 1 / . + + out->write( data = calc1 name = `calc1` ). + out->write( |\n| ). + + calc2 = ipow( base = 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(). + 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. + + 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. 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..ba289e0 --- /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 + X + + + + diff --git a/src/zcl_demo_abap_rap_draft_ln_m.clas.abap b/src/zcl_demo_abap_rap_draft_ln_m.clas.abap new file mode 100644 index 0000000..a086a4a --- /dev/null +++ b/src/zcl_demo_abap_rap_draft_ln_m.clas.abap @@ -0,0 +1,514 @@ +*********************************************************************** +* +* RAP BO consumer for a RAP demo scenario +* ABAP EML in use: RAP calculator (managed, draft-enabled RAP BO with +* late numbering +* +* -------------------------- PURPOSE ---------------------------------- +* - This class is the RAP BO consumer for a RAP demo scenario that +* represents a calculator using RAP concepts, i. e. using ABAP EML in +* the context of a managed and draft-enabled RAP business object with +* RAP late numbering to carry out simple calculations. Here, a RAP BO +* instance consists of a calculation ID (which is the key that is finally +* set not until the RAP save sequence), two operands (having integer +* values), the arithmetic operator and the result plus other +* draft-related fields. +* - Underlying data model: Consists of a root entity alone. +* The BDEF defines the behavior for this entity. The definitions in the +* BDEF determine which methods must be implemented in the ABAP behavior +* pool (ABP). Note that the view contains many annotations for the SAP +* Fiori UI. +* - ABP for this scenario: zbp_demo_abap_rap_draft_m +* +* ----------------------- GETTING STARTED (1) ------------------------- +* ----------------- Using this class as RAP BO consumer --------------- +* +* - Open the class with the ABAP development tools for Eclipse (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. +* +* ----------------------- GETTING STARTED (2) ------------------------- +* Using the preview version of an SAP Fiori Elements UI as RAP BO consumer +* +* Create a service binding: +* 1. Find the service definition ZDEMO_ABAP_RAP_CALC_SD in the imported +* package in Business Services -> Service Definitions. +* 2. Right-click the service definition and choose New Service Binding. +* 3. In the New Service Binding pop-up, make the following entries: +* - Name: ZDEMO_ABAP_RAP_CALC_SB +* - Description: Service binding for demo +* - Binding type: OData V4 - UI +* - Service Definition: ZDEMO_ABAP_RAP_CALC_SD (should be already filled) +* 4. Choose Next. +* 5. Assign a transport request and choose Finish. +* 6. The service binding ZDEMO_ABAP_RAP_CALC_SB is opened. Activate the +* service binding. +* 7. In the Service Version Details section, choose the Publish button +* for the Local Service Endpoint. Once the service has been published, +* you should see ZDEMO_ABAP_RAP_DRAFT_M in the Entity Set and Association +* section. +* 8. Activate the service binding once the service has been published. +* 9. Select ZDEMO_ABAP_RAP_DRAFT_M and choose the Preview button. +* 10. The preview version of an SAP Fiori Elements app is displayed. If +* prompted, provide your credentials. +* 11. The app and the managed, draft-enabled RAP BO can be explored. If no +* columns are displayed, choose the 'Settings' button and select the +* desired columns. +* Choosing the 'Go' button refreshes the list. At first use, there +* might not be any entry. You can create an entry choosing the 'Create' +* button. +* The late numbering aspects enter the picture when you, for +* example, create a new instance, i. e. create a new calculation, and +* you keep a draft version of it instead of saving it to the database. +* The calculation ID which represents the key of the instance has an +* initial value. Only when you save the instance to the database, the +* final key is set. +* The effect of side effects can be explored as follows: Make an entry +* in an input field, click another input field (e.g. to make a new entry +* there), and check how the value for the result changes. +* +* ----------------------------- NOTE ----------------------------------- +* This simplified example is not a real life scenario and rather +* focuses on the technical side by giving an idea how the communication +* and data exchange between a RAP BO consumer, which is a class +* in this case, and RAP BO provider can work. Additionally, it shows +* how the methods for non-standard RAP BO operations might be +* self-implemented in an ABP. The example is intentionally kept +* short and simple and focuses on specific RAP aspects. For this reason, +* the example might not fully meet the requirements of the RAP BO contract. +* +* The code presented in this class is intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

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

    +"! Example to demonstrate ABAP EML in the context of a RAP demo scenario (managed and draft-enabled RAP business object with RAP late numbering). +"! The class represents a RAP BO consumer.
    Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_rap_draft_ln_m DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + + CLASS-METHODS: + class_constructor. + +protected section. + PRIVATE SECTION. + CLASS-DATA: + activate_tab TYPE TABLE FOR ACTION IMPORT + zdemo_abap_rap_draft_m~activate, + activate_tab2 TYPE TABLE FOR ACTION IMPORT + zdemo_abap_rap_draft_m~activate, + activate_tab3 TYPE TABLE FOR ACTION IMPORT + zdemo_abap_rap_draft_m~activate, + edit_tab TYPE TABLE FOR ACTION IMPORT + zdemo_abap_rap_draft_m~edit, + read_tab TYPE TABLE FOR READ IMPORT zdemo_abap_rap_draft_m, + f TYPE RESPONSE FOR FAILED zdemo_abap_rap_draft_m, + r TYPE RESPONSE FOR REPORTED zdemo_abap_rap_draft_m, + m TYPE RESPONSE FOR MAPPED zdemo_abap_rap_draft_m. + + CLASS-METHODS: + initialize_dbtabs. + +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_RAP_DRAFT_LN_M IMPLEMENTATION. + + + METHOD class_constructor. + initialize_dbtabs( ). + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + out->write( `ABAP Cheat Sheet Example: RAP Calculator Using Managed, ` && + |Draft-Enabled RAP BO (Late Numbering)\n\n| ). + out->write( |1) Creating Instances and Saving to the database\n| ). + + "Creating instances; draft indicator %is_draft is enabled + MODIFY ENTITY zdemo_abap_rap_draft_m + CREATE AUTO FILL CID + FIELDS ( num1 arithm_op num2 ) + WITH VALUE #( + ( %is_draft = if_abap_behv=>mk-on + num1 = 1 arithm_op = '+' num2 = 2 ) + ( %is_draft = if_abap_behv=>mk-on + num1 = 2 arithm_op = '*' num2 = 4 ) + ( %is_draft = if_abap_behv=>mk-on + num1 = 3 arithm_op = '-' num2 = 5 ) + ( %is_draft = if_abap_behv=>mk-on + num1 = 1 arithm_op = '/' num2 = 4 ) + ( %is_draft = if_abap_behv=>mk-on + num1 = 2 arithm_op = 'P' num2 = 5 ) ) + FAILED f + REPORTED r + MAPPED m. + + "Displaying responses only if FAILED and REPORTED + "response parameters are not initial + IF f IS NOT INITIAL OR r IS NOT INITIAL. + out->write( `Responses after MODIFY operation` ). + out->write( |\n| ). + + IF m IS NOT INITIAL. + out->write( data = m name = `m` ). + out->write( |\n| ). + ENDIF. + + IF f IS NOT INITIAL. + out->write( data = f name = `f` ). + out->write( |\n| ). + ENDIF. + + IF r IS NOT INITIAL. + out->write( data = r name = `r` ). + out->write( |\n| ). + ENDIF. + ENDIF. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving draft table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time, draftentitycreationdatetime, + draftentitylastchangedatetime + FROM zdemo_abap_draft + ORDER BY id + INTO TABLE @DATA(draft_parent_before_act). + + "Retrieving database table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time + FROM zdemo_abap_tabca + ORDER BY id + INTO TABLE @DATA(db_tab_root_before_act). + + "Filling the derived type for the ACTIVATE method by + "getting %pid values + LOOP AT m-calc + ASSIGNING FIELD-SYMBOL(). + APPEND VALUE #( %pid = -%pid ) + TO activate_tab. + ENDLOOP. + + MODIFY ENTITY zdemo_abap_rap_draft_m + EXECUTE activate AUTO FILL CID WITH activate_tab + MAPPED m + FAILED f + REPORTED r. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving draft table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time, draftentitycreationdatetime, + draftentitylastchangedatetime + FROM zdemo_abap_draft + ORDER BY id + INTO TABLE @DATA(draft_parent_afer_act). + + "Retrieving database table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time + FROM zdemo_abap_tabca + ORDER BY id + INTO TABLE @DATA(db_tab_root_after_act). + + "Displaying entries + out->write( |1a) Draft and database tables before ACTIVATE action\n| ). + out->write( `Draft table before activation` ). + out->write( |\n| ). + out->write( data = draft_parent_before_act name = `draft_parent_before_act` ). + out->write( |\n| ). + out->write( `Database table before activation` ). + out->write( |\n| ). + out->write( data = db_tab_root_before_act name = `db_tab_root_before_act` ). + + out->write( zcl_demo_abap_aux=>heading( `1b) Draft and database tables after ` && + `ACTIVATE action` ) ). + out->write( `Draft table after activation` ). + out->write( |\n| ). + out->write( data = draft_parent_afer_act name = `draft_parent_afer_act` ). + out->write( |\n| ). + out->write( `Database table after activation` ). + out->write( |\n| ). + out->write( data = db_tab_root_after_act name = `db_tab_root_after_act` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `2) Creating Invalid Instances` ) ). + + "Purposely creating invalid instances; + "draft indicator %is_draft is enabled + MODIFY ENTITY zdemo_abap_rap_draft_m + CREATE AUTO FILL CID + FIELDS ( num1 arithm_op num2 ) + WITH VALUE #( + ( %is_draft = if_abap_behv=>mk-on + num1 = 1 arithm_op = 'a' num2 = 1 ) "wrong operator + ( %is_draft = if_abap_behv=>mk-on + num1 = 1 arithm_op = '/' num2 = 0 ) "0 division + ( %is_draft = if_abap_behv=>mk-on + num1 = 2 arithm_op = 'P' num2 = 12345 ) ) "arithmetic overflow + FAILED f + REPORTED r + MAPPED m. + + "Displaying responses only if FAILED and REPORTED + "response parameters are not initial. + IF f IS NOT INITIAL OR r IS NOT INITIAL. + out->write( data = `Responses after MODIFY operation` ). + out->write( |\n| ). + + IF m IS NOT INITIAL. + out->write( data = m name = `m` ). + out->write( |\n| ). + ENDIF. + + IF f IS NOT INITIAL. + out->write( data = f name = `f` ). + out->write( |\n| ). + ENDIF. + + IF r IS NOT INITIAL. + out->write( data = r name = `r` ). + out->write( |\n| ). + ENDIF. + + ENDIF. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving draft table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time, draftentitycreationdatetime, + draftentitylastchangedatetime + FROM zdemo_abap_draft + ORDER BY id + INTO TABLE @draft_parent_before_act. + + "Retrieving database table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time + FROM zdemo_abap_tabca + ORDER BY id + INTO TABLE @db_tab_root_before_act. + + "Filling the derived type for the ACTIVATE method by + "getting %pid values; here, another table is filled for later use + LOOP AT m-calc + ASSIGNING FIELD-SYMBOL(). + + APPEND VALUE #( %pid = -%pid ) + TO activate_tab2. + + APPEND VALUE #( %pid = -%pid ) + TO activate_tab3. + ENDLOOP. + + MODIFY ENTITY zdemo_abap_rap_draft_m + EXECUTE activate AUTO FILL CID WITH activate_tab2 + MAPPED m + FAILED f + REPORTED r. + + "Displaying responses only if FAILED and REPORTED + "response parameters are not initial. + IF f IS NOT INITIAL OR r IS NOT INITIAL. + out->write( data = `Responses after MODIFY operation` ). + out->write( |\n| ). + + IF m IS NOT INITIAL. + out->write( data = m name = `m` ). + out->write( |\n| ). + ENDIF. + + IF f IS NOT INITIAL. + out->write( data = f name = `f` ). + out->write( |\n| ). + ENDIF. + + IF r IS NOT INITIAL. + out->write( data = r name = `r` ). + out->write( |\n| ). + ENDIF. + + ENDIF. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving draft table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time, draftentitycreationdatetime, + draftentitylastchangedatetime + FROM zdemo_abap_draft + ORDER BY id + INTO TABLE @draft_parent_afer_act. + + "Retrieving database table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time + FROM zdemo_abap_tabca + ORDER BY id + INTO TABLE @db_tab_root_after_act. + + "Displaying entries + out->write( zcl_demo_abap_aux=>heading( `2a) Draft and database tables before ` && + `ACTIVATE action` ) ). + out->write( `Draft table before activation` ). + out->write( |\n| ). + out->write( data = draft_parent_before_act name = `draft_parent_before_act` ). + out->write( |\n| ). + out->write( `Database table before activation` ). + out->write( |\n| ). + out->write( data = db_tab_root_before_act name = `db_tab_root_before_act` ). + + out->write( zcl_demo_abap_aux=>heading( `2b) Draft and database tables after ` && + `ACTIVATE action` ) ). + out->write( `Draft table after activation` ). + out->write( |\n| ). + out->write( data = draft_parent_afer_act name = `draft_parent_afer_act` ). + out->write( |\n| ). + out->write( `Database table after activation` ). + out->write( |\n| ). + out->write( data = db_tab_root_after_act name = `db_tab_root_after_act` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `3) Correcting and Updating Invalid Instances` ) ). + + "Preparing the derived type for the read operation to + "retrieve the field values; the draft indicator is enabled + LOOP AT activate_tab3 ASSIGNING FIELD-SYMBOL(). + + APPEND VALUE #( %pky = -%pky + %is_draft = if_abap_behv=>mk-on + %control-id = if_abap_behv=>mk-on + %control-num1 = if_abap_behv=>mk-on + %control-arithm_op = if_abap_behv=>mk-on + %control-num2 = if_abap_behv=>mk-on + %control-calc_result = if_abap_behv=>mk-on + %control-crea_date_time = if_abap_behv=>mk-on + %control-lchg_date_time = if_abap_behv=>mk-on + ) TO read_tab. + ENDLOOP. + + "Retrieving the entries of the invalid instances + READ ENTITY zdemo_abap_rap_draft_m + ALL FIELDS WITH read_tab + RESULT DATA(result). + + "Correcting and updating the invalid instances + MODIFY ENTITY zdemo_abap_rap_draft_m + UPDATE FROM VALUE #( + FOR wa IN result ( + %pid = wa-%pid + %is_draft = if_abap_behv=>mk-on + num2 = SWITCH #( wa-calc_result + WHEN `Division by 0` THEN 2 + WHEN `Overflow error` THEN 3 ) + arithm_op = SWITCH #( wa-calc_result + WHEN `Wrong operator` THEN '+' ) + %control-num2 = SWITCH #( wa-calc_result + WHEN `Division by 0` THEN if_abap_behv=>mk-on + WHEN `Overflow error` THEN if_abap_behv=>mk-on + ELSE if_abap_behv=>mk-off ) + %control-arithm_op = SWITCH #( wa-calc_result + WHEN `Wrong operator` THEN if_abap_behv=>mk-on + ELSE if_abap_behv=>mk-off ) ) ) + FAILED f + REPORTED r + MAPPED m. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + MODIFY ENTITY zdemo_abap_rap_draft_m + EXECUTE activate AUTO FILL CID WITH activate_tab3 + MAPPED m + FAILED f + REPORTED r. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving draft table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time, draftentitycreationdatetime, + draftentitylastchangedatetime + FROM zdemo_abap_draft + ORDER BY id + INTO TABLE @draft_parent_afer_act. + + "Retrieving database table entries + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, + lchg_date_time + FROM zdemo_abap_tabca + ORDER BY id + INTO TABLE @db_tab_root_after_act. + + "Displaying entries + out->write( data = `Draft and database tables after ` && + `ACTIVATE action` ). + out->write( |\n| ). + out->write( `Draft table after activation` ). + out->write( |\n| ). + out->write( data = draft_parent_afer_act name = `draft_parent_afer_act` ). + out->write( |\n| ). + out->write( `Database table after activation` ). + out->write( |\n| ). + out->write( data = db_tab_root_after_act name = `db_tab_root_after_act` ). + ENDMETHOD. + + METHOD initialize_dbtabs. + DELETE FROM zdemo_abap_tabca. + DELETE FROM zdemo_abap_draft. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_rap_draft_ln_m.clas.xml b/src/zcl_demo_abap_rap_draft_ln_m.clas.xml new file mode 100644 index 0000000..9cacfbe --- /dev/null +++ b/src/zcl_demo_abap_rap_draft_ln_m.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_RAP_DRAFT_LN_M + E + ABAP cheat sheet: ABAP EML in a RAP scenario (draft BO) + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_rap_evt_handler.clas.abap b/src/zcl_demo_abap_rap_evt_handler.clas.abap new file mode 100644 index 0000000..4e20dd6 --- /dev/null +++ b/src/zcl_demo_abap_rap_evt_handler.clas.abap @@ -0,0 +1,23 @@ +********************************************************************** +* Note: +* +* - This class is the RAP event handler class for zdemo_abap_rap_ro_m_as. +* +* - The RAP business events in this example are raised using RAISE +* ENTITY EVENT statements in the save_modified saver method that +* is implemented in the CCIMP include of the ABAP behavior pool +* zbp_demo_abap_rap_ro_m_as. +* +********************************************************************** + +CLASS zcl_demo_abap_rap_evt_handler DEFINITION + PUBLIC ABSTRACT FINAL + FOR EVENTS OF zdemo_abap_rap_ro_m_as. + + PUBLIC SECTION. + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + +CLASS zcl_demo_abap_rap_evt_handler IMPLEMENTATION. +ENDCLASS. diff --git a/src/zcl_demo_abap_rap_evt_handler.clas.locals_imp.abap b/src/zcl_demo_abap_rap_evt_handler.clas.locals_imp.abap new file mode 100644 index 0000000..601ca2e --- /dev/null +++ b/src/zcl_demo_abap_rap_evt_handler.clas.locals_imp.abap @@ -0,0 +1,75 @@ +CLASS lhe_event DEFINITION INHERITING FROM cl_abap_behavior_event_handler. + + PRIVATE SECTION. + + METHODS on_updated FOR ENTITY EVENT + updated FOR root~updated. + + METHODS on_deleted FOR ENTITY EVENT + deleted FOR root~deleted. + + METHODS on_created FOR ENTITY EVENT + created FOR root~created. + + DATA evt_log TYPE TABLE OF zdemo_abap_draft WITH EMPTY KEY. +ENDCLASS. + +CLASS lhe_event IMPLEMENTATION. + + "Note: + "- For this example, database table entries are created for the individual + " RAP BO instances that are imported into the event handler methods. + "- The transactional phases are implicitly set when RAP business events are + " consumed locally. This means that RAP event handler methods are started in + " the modify phase when called. If database modifications are to be implemented + " in RAP event handler methods, you must explicitly activate the save phase to + " avoid causing errors detected by the controlled SAP LUW. + + METHOD on_created. + cl_abap_tx=>save( ). + LOOP AT created ASSIGNING FIELD-SYMBOL(). + TRY. + APPEND VALUE #( id = cl_system_uuid=>create_uuid_x16_static( ) + draftuuid = cl_system_uuid=>create_uuid_x16_static( ) + calc_result = |Instance key: "{ -id }" / Event CREATED raised| + crea_date_time = cl_abap_tstmp=>utclong2tstmp( utclong_current( ) ) + ) TO evt_log. + CATCH cx_uuid_error INTO DATA(err). + ASSERT err IS INITIAL. + ENDTRY. + MODIFY zdemo_abap_draft FROM TABLE @evt_log. + ENDLOOP. + ENDMETHOD. + + METHOD on_updated. + cl_abap_tx=>save( ). + LOOP AT updated ASSIGNING FIELD-SYMBOL(). + TRY. + APPEND VALUE #( id = cl_system_uuid=>create_uuid_x16_static( ) + draftuuid = cl_system_uuid=>create_uuid_x16_static( ) + calc_result = |Instance key: "{ -id }" / %param: col1: "{ -%param-col1 }" col2: "{ -%param-col2 }"| + crea_date_time = cl_abap_tstmp=>utclong2tstmp( utclong_current( ) ) + ) TO evt_log. + CATCH cx_uuid_error INTO DATA(err). + ASSERT err IS INITIAL. + ENDTRY. + MODIFY zdemo_abap_draft FROM TABLE @evt_log. + ENDLOOP. + ENDMETHOD. + + METHOD on_deleted. + cl_abap_tx=>save( ). + LOOP AT deleted ASSIGNING FIELD-SYMBOL(). + TRY. + APPEND VALUE #( id = cl_system_uuid=>create_uuid_x16_static( ) + draftuuid = cl_system_uuid=>create_uuid_x16_static( ) + calc_result = |Instance key: "{ -id }" / %param: col1: "{ -%param-col1 }" col2: "{ -%param-col2 }"| + crea_date_time = cl_abap_tstmp=>utclong2tstmp( utclong_current( ) ) + ) TO evt_log. + CATCH cx_uuid_error INTO DATA(err). + ASSERT err IS INITIAL. + ENDTRY. + MODIFY zdemo_abap_draft FROM TABLE @evt_log. + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_rap_evt_handler.clas.xml b/src/zcl_demo_abap_rap_evt_handler.clas.xml new file mode 100644 index 0000000..11e59a1 --- /dev/null +++ b/src/zcl_demo_abap_rap_evt_handler.clas.xml @@ -0,0 +1,18 @@ + + + + + + ZCL_DEMO_ABAP_RAP_EVT_HANDLER + E + RAP event handler class + 07 + 1 + X + X + X + ZDEMO_ABAP_RAP_RO_M_AS + + + + diff --git a/src/zcl_demo_abap_rap_ext_num_m.clas.abap b/src/zcl_demo_abap_rap_ext_num_m.clas.abap new file mode 100644 index 0000000..b117980 --- /dev/null +++ b/src/zcl_demo_abap_rap_ext_num_m.clas.abap @@ -0,0 +1,1006 @@ +*********************************************************************** +* +* RAP BO consumer for a RAP demo scenario: +* ABAP EML in use: Managed RAP BO with external numbering +* +* -------------------------- PURPOSE ---------------------------------- +* - This class is the RAP BO consumer for a RAP demo scenario that +* demonstrates various RAP BO standard operations and non-standard +* operations using ABAP EML in the context of a managed RAP business +* object with RAP external numbering. +* - Topics covered: RAP BO operations like create (including a +* determination on save), update, delete, executing an action, validation, +* create-by-association (parent to child), read (root entity), +* read-by-association (parent to child), read (child entity), +* read-by-association (child to parent) +* - Underlying data model: Consists of a root entity and one child entity. +* The BDEF defines the behavior for these two entities which are connected +* via a CDS composition relation. The definitions in the BDEF determine +* which methods must be implemented in the ABAP behavior pool (ABP). +* - ABP for this scenario: zbp_demo_abap_rap_ro_m +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP development tools for Eclipse (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 ----------------------------------- +* This simplified example is not a real life scenario and rather +* focuses on the technical side by giving an idea how the communication +* and data exchange between a RAP BO consumer, which is a class +* in this case, and RAP BO provider can work. Additionally, it shows +* how the methods for non-standard RAP BO operations might be +* self-implemented in an ABP. The example is intentionally kept +* short and simple and focuses on specific RAP aspects. For this reason, +* the example might not fully meet the requirements of the RAP BO contract. +* +* The code presented in this class is intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

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

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

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

    +"! Example to demonstrate ABAP EML in the context of a RAP demo scenario (unmanaged RAP BO with external numbering). +"! The class represents a RAP BO consumer.
    Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_rap_ext_num_u DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + + CLASS-METHODS: + class_constructor. + +protected section. + PRIVATE SECTION. + CLASS-DATA: + failed TYPE RESPONSE FOR FAILED zdemo_abap_rap_ro_u, + reported TYPE RESPONSE FOR REPORTED zdemo_abap_rap_ro_u, + mapped TYPE RESPONSE FOR MAPPED zdemo_abap_rap_ro_u, + op TYPE string. + CLASS-METHODS: + initialize_dbtabs, + "If there are entries in the response parameters following EML + "requests, they should be processed for displaying purposes. + extract_from_reported RETURNING VALUE(messages) TYPE string_table, + extract_from_failed RETURNING VALUE(errors) TYPE string_table, + fill_db_tab. + +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_RAP_EXT_NUM_U IMPLEMENTATION. + + + METHOD class_constructor. + initialize_dbtabs( ). + ENDMETHOD. + + + METHOD extract_from_failed. + CLEAR errors. + + LOOP AT failed-root ASSIGNING FIELD-SYMBOL(). + CASE if_abap_behv=>mk-on. + WHEN -%op-%create. + op = `create operation`. + WHEN -%op-%update. + op = `update operation`. + WHEN -%op-%delete. + op = `delete operation`. + WHEN -%op-%assoc-_child. + op = `operation involving the child entity`. + WHEN -%op-%action-multiply_by_2. + op = `executing action multiply_by_2`. + WHEN -%op-%action-multiply_by_3. + op = `executing action multiply_by_3`. + WHEN -%op-%action-set_z. + op = `executing action set_z`. + WHEN OTHERS. op = `operation`. + ENDCASE. + + APPEND `Error for instance with ` && + COND #( WHEN -%cid IS NOT INITIAL THEN `%cid = ` + && -%cid + ELSE `key = ` && -key_field ) && + `: Fail cause ` && -%fail-cause && ` for ` && op + && `.` TO errors. + ENDLOOP. + + IF failed-child IS NOT INITIAL. + LOOP AT failed-child ASSIGNING FIELD-SYMBOL(). + + APPEND `Error for child instance with ` && + COND #( WHEN -%cid IS NOT INITIAL THEN `%cid = ` + && -%cid + ELSE `key_field = ` && -key_field && + ` and key_ch = ` && -key_ch ) && + `: Fail cause ` && -%fail-cause && ` for operation.` + TO errors. + + ENDLOOP. + ENDIF. + ENDMETHOD. + + + METHOD extract_from_reported. + CLEAR messages. + + LOOP AT reported-root ASSIGNING FIELD-SYMBOL(). + IF -%global = if_abap_behv=>mk-on. + APPEND -%msg->m_severity && ` ` && + -%msg->if_t100_dyn_msg~msgv1 TO messages. + ELSE. + APPEND `Message for instance with ` && + COND #( WHEN -%cid IS NOT INITIAL + THEN `%cid = ` && -%cid + ELSE `key = ` && -key_field ) && + `: ` && -%msg->m_severity && ` ` && + -%msg->if_t100_dyn_msg~msgv1 TO messages. + + ENDIF. + ENDLOOP. + + IF reported-child IS NOT INITIAL. + LOOP AT reported-child ASSIGNING FIELD-SYMBOL(). + APPEND `Message for child instance with key_field = ` && + -key_field && ` and key_ch = ` + && -key_ch && `: ` && -%msg->m_severity && + ` ` && -%msg->if_t100_dyn_msg~msgv1 TO messages. + ENDLOOP. + ENDIF. + + ENDMETHOD. + + + METHOD fill_db_tab. + + MODIFY zdemo_abap_rapt1 FROM TABLE @( VALUE #( + ( key_field = 4 + field1 = 'fff' + field2 = 'ggg' + field3 = 40 + field4 = 41 ) ) ). + + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + out->write( |ABAP Cheat Sheet Example: RAP BO Operations Using an Unmanaged RAP BO (External Numbering)\n\n| ). + +********************************************************************** +* +* Create operation +* +********************************************************************** + + out->write( |1) Create Operation\n\n| ). + + "Adding an entry to the database table to provoke an error for the + "EML create request. + fill_db_tab( ). + +********************************************************************** +* Notes: +* - field4 is purposely not included in the FIELDS list +* - Effect: +* - %control value for field4 is set to if_abap_behv=>mk-off +* - Although the derived type (created inline here) includes a +* value assignment for field4 in an instance, the field value is +* not saved. The initial value is used. +* - The instance with key_field = 4 will not be saved since an entry +* already exists in the database table with the same key. +* - Response parameters are specified to receive information. +* - A COMMIT ENTITIES statement triggers the saving of the instances. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 ) + WITH VALUE #( ( %cid = 'cid1' + key_field = 1 + field1 = 'aaa' + field2 = 'bbb' + field3 = 10 + field4 = 11 ) "Value not respected + ( %cid = 'cid2' + key_field = 2 + field1 = 'ccc' + field2 = 'ddd' + field3 = 20 ) + ( %cid = 'cid3' + key_field = 3 + field1 = 'X' + field2 = 'eee' + field3 = 30 ) + ( %cid = 'cid4' "Instance to fail + key_field = 4 + field1 = 'error' + field2 = 'error' + field3 = 99 ) ) + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @DATA(tab_root). + + out->write( data = tab_root name = `tab_root` ). + out->write( |\n| ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + out->write( `Entries in MAPPED response parameter ` && + `(root entity)` ). + out->write( |\n| ). + out->write( data = mapped-root name = `mapped-root` ). + out->write( |\n| ). + ENDIF. + + IF failed IS NOT INITIAL. + out->write( `Entries in FAILED response parameter` ). + out->write( |\n| ). + out->write( data = extract_from_failed( ) name = `extract_from_failed( )` ). + out->write( |\n| ). + ENDIF. + + IF reported IS NOT INITIAL. + out->write( `Entries in REPORTED response parameter` ). + out->write( |\n| ). + out->write( data = extract_from_reported( ) name = `extract_from_reported( )` ). + out->write( |\n| ). + ENDIF. + +********************************************************************** +* +* Update operations +* +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `2) Update Operation` ) ). + +********************************************************************** +* Notes: +* - The EML request includes a create and an update request. The +* create request is included to have a %cid to refer to for demo +* purposes. This instance has not yet been persisted. +* - The EML statement for the create operation includes the ABAP +* FROM ... (instead of FIELDS ( ... ) WITH ...) for demo purposes. +* Here, the %control values must be set explicitly. +* - The update request purposely excludes field2 so as not to update +* the value of this particular field. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + CREATE FROM VALUE #( + %control-key_field = if_abap_behv=>mk-on + %control-field1 = if_abap_behv=>mk-on + %control-field2 = if_abap_behv=>mk-on + %control-field3 = if_abap_behv=>mk-on + %control-field4 = if_abap_behv=>mk-on + ( %cid = 'cid5' + key_field = 5 + field1 = 'hhh' + field2 = 'iii' + field3 = 50 + field4 = 51 ) ) + UPDATE FIELDS ( field1 field3 field4 ) + WITH VALUE #( + "Update via cid_ref + ( %cid_ref = 'cid5' + field1 = 'up_jjj' + field2 = 'up_kkk' "Value not respected + field3 = 500 + field4 = 501 ) + "Updates via key + ( key_field = 1 + field1 = 'up_lll' + field3 = 100 + field4 = 101 ) + ( key_field = 2 + field1 = 'up_mmm' + field3 = 200 + field4 = 201 ) + ( key_field = 99 "Instance to fail + field1 = 'error' + field3 = 99 + field4 = 99 ) ) + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + out->write( data = tab_root name = `tab_root` ). + out->write( |\n| ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + out->write( `Entries in MAPPED response parameter ` && + `(root entity)` ). + out->write( |\n| ). + out->write( data = mapped-root name = `mapped-root` ). + out->write( |\n| ). + ENDIF. + + IF failed IS NOT INITIAL. + out->write( `Entries in FAILED response parameter` ). + out->write( |\n| ). + out->write( data = extract_from_failed( ) name = `extract_from_failed( )` ). + out->write( |\n| ). + ENDIF. + + IF reported IS NOT INITIAL. + out->write( `Entries in REPORTED response parameter` ). + out->write( |\n| ). + out->write( data = extract_from_reported( ) name = `extract_from_reported( )` ). + out->write( |\n| ). + ENDIF. + +********************************************************************** +* +* Delete operation +* +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `3) Delete Operation` ) ). + +********************************************************************** +* Notes: +* - The EML request includes a create and an delete request. The +* create request is included to have a %cid to refer to for demo +* purposes. This instance has not yet been persisted. +* - EML statements for delete operations can only be used with the +* ABAP word FROM .... +* - Note: Instance authorization is defined in the BDEF. In this +* example, the corresponding handler method is implemented in a way +* that disables the deletion of instances if a field has a certain +* value. If field1 has the value 'X', a deletion is disabled. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 field4 ) + WITH VALUE #( + ( %cid = 'cid_del' + key_field = 6 + field1 = 'a' + field2 = 'b' + field3 = 60 + field4 = 61 ) ) + DELETE FROM VALUE #( + "Deletion via %cid_ref + ( %cid_ref = 'cid_del' ) + "Deletions via key + ( key_field = 4 ) + ( key_field = 5 ) + "Instances to fail + ( key_field = 3 ) "Deletion disabled + ( key_field = 100 ) ) "Key not available + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + out->write( data = tab_root name = `tab_root` ). + out->write( |\n| ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + out->write( `Entries in MAPPED response parameter ` && + `(root entity)` ). + out->write( |\n| ). + out->write( data = mapped-root name = `mapped-root` ). + out->write( |\n| ). + ENDIF. + + IF failed IS NOT INITIAL. + out->write( `Entries in FAILED response parameter` ). + out->write( |\n| ). + out->write( data = extract_from_failed( ) name = `extract_from_failed( )` ). + out->write( |\n| ). + ENDIF. + + IF reported IS NOT INITIAL. + out->write( `Entries in REPORTED response parameter` ). +out->write( |\n| ). + out->write( data = extract_from_reported( ) name = `extract_from_reported( )` ). + out->write( |\n| ). + ENDIF. + +********************************************************************** +* +* Action multiply_by_2 +* +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `4) Executing Action mutliply_by_2` ) ). + +********************************************************************** +* Notes: +* - The EML request includes a create request and a request to execute +* an action. The create request is included to have a %cid to refer +* to for demo purposes. This instance has not yet been persisted. +* - EML statements for executing actions can only be used with the +* ABAP word FROM .... +* - As the name implies, the action multiplies field +* values (field3 and field4) by 2 for requested instances. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 field4 ) + WITH VALUE #( + ( %cid = 'cid_x2' + key_field = 7 + field1 = 'nnn' + field2 = 'ooo' + field3 = 70 + field4 = 71 ) ) + EXECUTE multiply_by_2 FROM VALUE #( + "Executing action via %cid_ref + ( %cid_ref = 'cid_x2' ) + "Executing action via key + ( key_field = 1 ) + ( key_field = 2 ) + ( key_field = 1234 ) ) "Instance to fail + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + out->write( data = tab_root name = `tab_root` ). + out->write( |\n| ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + out->write( `Entries in MAPPED response parameter ` && + `(root entity)` ). + out->write( |\n| ). + out->write( data = mapped-root name = `mapped-root` ). + out->write( |\n| ). + ENDIF. + + IF failed IS NOT INITIAL. + out->write( `Entries in FAILED response parameter` ). +out->write( |\n| ). + out->write( data = extract_from_failed( ) name = `extract_from_failed( )` ). + out->write( |\n| ). + ENDIF. + + IF reported IS NOT INITIAL. + out->write( `Entries in REPORTED response parameter` ). +out->write( |\n| ). + out->write( data = extract_from_reported( ) name = `extract_from_reported( )` ). + out->write( |\n| ). + ENDIF. + +********************************************************************** +* +* Action multiply_by_3 +* +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `5) Executing Action mutliply_by_3` ) ). + +********************************************************************** +* Notes: +* - The EML request includes a create request and a request to execute +* an action. The create request is included to have a %cid to refer +* to for demo purposes. This instance has not yet been persisted. +* - As the name implies, the action multiplies field +* values (field3 and field4) by 3 for requested instances. +* - Note: In the BDEF of this example, this action is defined with +* instance feature control. Here, the action execution is disabled +* if both integer values are 0. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 field4 ) + WITH VALUE #( + ( %cid = 'cid_x3' + key_field = 8 + field1 = 'ppp' + field2 = 'qqq' + field3 = 80 + field4 = 81 ) ) + EXECUTE multiply_by_3 FROM VALUE #( + "Executing action via %cid_ref + ( %cid_ref = 'cid_x3' ) + "Executing action via key + ( key_field = 1 ) + ( key_field = 2 ) + "Instances to fail + ( key_field = 3 ) "Action execution disabled + ( key_field = 1234 ) ) "Key not available + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + out->write( data = tab_root name = `tab_root` ). + out->write( |\n| ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + out->write( `Entries in MAPPED response parameter ` && + `(root entity)` ). + out->write( |\n| ). + out->write( data = mapped-root name = `mapped-root` ). + out->write( |\n| ). + ENDIF. + + IF failed IS NOT INITIAL. + out->write( `Entries in FAILED response parameter` ). + out->write( |\n| ). + out->write( data = extract_from_failed( ) name = `extract_from_failed( )` ). + out->write( |\n| ). + ENDIF. + + IF reported IS NOT INITIAL. + out->write( `Entries in REPORTED response parameter` ). +out->write( |\n| ). + out->write( data = extract_from_reported( ) name = `extract_from_reported( )` ). +out->write( |\n| ). + ENDIF. + +********************************************************************** +* +* Action set_z +* +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `6) Executing ACTION set_z` ) ). + +********************************************************************** +* Notes: +* - The EML request includes a create request and a request to execute +* an action. The create request is included to have a %cid to refer +* to for demo purposes. This instance has not yet been persisted. +* - As the name implies, the action sets the value 'Z' for a +* particular field (field2) for requested instances. +* - Note: In the BDEF of this example, this action is defined with +* global feature control. Here, the action execution is disabled +* based on a certain time frame in which you run this example. You +* might want to change the time frame values in the ABP to check the +* effect. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 field4 ) + WITH VALUE #( + ( %cid = 'cid_setz' + key_field = 9 + field1 = 'rrr' + field2 = 'sss' + field3 = 90 + field4 = 91 ) ) + EXECUTE set_z FROM VALUE #( + "Executing action via %cid_ref + ( %cid_ref = 'cid_setz' ) + "Executing action via key + ( key_field = 2 ) ) + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + out->write( data = tab_root name = `tab_root` ). + out->write( |\n| ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + out->write( `Entries in MAPPED response parameter ` && + `(root entity)` ). + out->write( |\n| ). + out->write( data = mapped-root name = `mapped-root` ). + out->write( |\n| ). + ENDIF. + + IF failed IS NOT INITIAL. + out->write( `Entries in FAILED response parameter` ). +out->write( |\n| ). + out->write( data = extract_from_failed( ) name = `extract_from_failed( )` ). + out->write( |\n| ). + ENDIF. + + IF reported IS NOT INITIAL. + out->write( `Entries in REPORTED response parameter` ). +out->write( |\n| ). + out->write( data = extract_from_reported( ) name = `extract_from_reported( )` ). +out->write( |\n| ). + ENDIF. + +********************************************************************** +* +* Create-by-Association Operation (from Root to Child Entity) +* +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `7) Create-by-Association Operation (from Root to Child Entity)` ) ). + +********************************************************************** +* Notes: +* - The EML request includes a create and create-by-association +* request, i. e. a "deep create". An instance is created for the +* root entity and, in the same request and based on this root +* instance, instances are created for the child entity, too. +********************************************************************** + + MODIFY ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + CREATE FIELDS ( key_field field1 field2 field3 field4 ) + WITH VALUE #( + ( %cid = 'cid_cba' + key_field = 10 + field1 = 'ttt' + field2 = 'uuu' + field3 = 100 + field4 = 101 ) ) + CREATE BY \_child + FIELDS ( key_ch field_ch1 field_ch2 ) WITH VALUE #( + "CBA operation via %cid_ref + ( %cid_ref = 'cid_cba' + %target = VALUE #( ( %cid = 'cid_ch1' + key_ch = 11 + field_ch1 = 'aaa_ch' + field_ch2 = 110 ) + ( %cid = 'cid_ch2' + key_ch = 12 + field_ch1 = 'bbb_ch' + field_ch2 = 120 ) ) ) + "CBA operation via root key + ( key_field = 1 + %target = VALUE #( ( %cid = 'cid_ch3' + key_ch = 1 + field_ch1 = 'ccc_ch' + field_ch2 = 11 ) + ( %cid = 'cid_ch4' + key_ch = 2 + field_ch1 = 'ddd_ch' + field_ch2 = 22 ) ) ) + ( key_field = 2 + %target = VALUE #( ( %cid = 'cid_ch5' + key_ch = 3 + field_ch1 = 'ccc_ch' + field_ch2 = 33 ) + ( %cid = 'cid_ch6' + key_ch = 4 + field_ch1 = 'ddd_ch' + field_ch2 = 44 ) ) ) + "Instance to fail + ( key_field = 123 + %target = VALUE #( ( %cid = 'cid_ch7' + key_ch = 1 + field_ch1 = 'error' + field_ch2 = 2 ) + ( %cid = 'cid_ch8' + key_ch = 2 + field_ch1 = 'error' + field_ch2 = 3 ) ) ) ) + MAPPED mapped + FAILED failed + REPORTED reported. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Retrieving and displaying database content + SELECT FROM zdemo_abap_rapt1 + FIELDS key_field, field1, field2, field3, field4 + ORDER BY key_field + INTO TABLE @tab_root. + + SELECT FROM zdemo_abap_rapt2 + FIELDS key_field, key_ch, field_ch1, field_ch2 + ORDER BY key_field, key_ch + INTO TABLE @DATA(tab_child). + + out->write( data = tab_root name = `tab_root` ). + out->write( |\n| ). + out->write( data = tab_child name = `tab_child` ). + out->write( |\n| ). + + "Displaying response information + IF mapped IS NOT INITIAL. + out->write( `Entries in MAPPED response parameter ` && + `(root and child entity)` ). +out->write( |\n| ). + out->write( data = mapped name = `mapped` ). + out->write( |\n| ). + ENDIF. + + IF failed IS NOT INITIAL. + out->write( `Entries in FAILED response parameter` ). +out->write( |\n| ). + out->write( data = extract_from_failed( ) name = `extract_from_failed( )` ). + out->write( |\n| ). + ENDIF. + + IF reported IS NOT INITIAL. + out->write( `Entries in REPORTED response parameter` ). +out->write( |\n| ). + out->write( data = extract_from_reported( ) name = `extract_from_reported( )` ). + out->write( |\n| ). + ENDIF. + +********************************************************************** +* +* Read operation +* +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `8) Read Operation (Root Entity)` ) ). + +********************************************************************** +* Notes: +* - The EML request includes a read request. The EML statement uses +* the ABAP words ALL FIELDS WITH. In this case, as the name implies, +* all field values are retrieved. The %control values for all fields +* are set to if_abap_behv=>mk-on. +* - When using the ABAP words FIELDS ( ... ) WITH and specifying the +* concrete fields to be read, only for those fields %control is +* set accordingly. +* - Filling the parameter for RESULT is mandatory. +********************************************************************** + + READ ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + ALL FIELDS WITH VALUE #( + ( key_field = 1 ) + ( key_field = 2 ) + ( key_field = 5 ) ) "Instance to fail + RESULT DATA(result) + FAILED failed + REPORTED reported. + + "Displaying the read result and response information + out->write( data = result name = `result` ). + out->write( |\n| ). + + IF failed IS NOT INITIAL. + out->write( `Entries in FAILED response parameter` ). + out->write( |\n| ). + out->write( data = extract_from_failed( ) name = `extract_from_failed( )` ). + out->write( |\n| ). + ENDIF. + + IF reported IS NOT INITIAL. + out->write( `Entries in REPORTED response parameter` ). +out->write( |\n| ). + out->write( data = extract_from_reported( ) name = `extract_from_reported( )` ). + out->write( |\n| ). + ENDIF. + +********************************************************************* +* +* Read operation (child entity) +* +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `9) Read Operation (Child Entity)` ) ). + +********************************************************************** +* Notes: +* - The EML request includes a read request. The read operation is +* executed on the child entity directly by specifying the alias, as +* it is defined in the BDEF, following the ABAP word ENTITY. +* - All field values are read using the addition ALL FIELDS WITH. +********************************************************************** + + READ ENTITIES OF zdemo_abap_rap_ro_u + ENTITY child + ALL FIELDS WITH VALUE #( + ( key_field = 1 key_ch = 1 ) + ( key_field = 2 key_ch = 4 ) + "Instances to fail + ( key_field = 9 ) + ( key_field = 9 key_ch = 11 ) ) + RESULT DATA(read_ch) + FAILED failed + REPORTED reported. + + "Displaying read result + out->write( data = read_ch name = `read_ch` ). + + "Displaying response information + IF failed IS NOT INITIAL. + out->write( `Entries in FAILED response parameter` ). +out->write( |\n| ). + out->write( data = extract_from_failed( ) name = `extract_from_failed( )` ). + out->write( |\n| ). + ENDIF. + + IF reported IS NOT INITIAL. + out->write( `Entries in REPORTED response parameter` ). +out->write( |\n| ). + out->write( data = extract_from_reported( ) name = `extract_from_reported( )` ). + out->write( |\n| ). + ENDIF. + +********************************************************************** +* +* Read-by-association operation (from parent to child) +* +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `10) Read-by-Association Operation (from Parent to Child)` ) ). + +********************************************************************** +* Notes: +* - The EML request includes a read-by-association request from the +* parent to the child entity. +* - All field values are read using the addition ALL FIELDS WITH. +* - Specifying the parameter for RESULT is mandatory. +* - Additionally, the optional association links are retrieved. +********************************************************************** + + READ ENTITIES OF zdemo_abap_rap_ro_u + ENTITY root + BY \_child + ALL FIELDS WITH VALUE #( + ( key_field = 2 ) + ( key_field = 10 ) + ( key_field = 111 ) ) "Instance to fail + RESULT DATA(rba_result) + LINK DATA(association_links) + FAILED failed + REPORTED reported. + + "Displaying read result and association links + out->write( data = rba_result name = `rba_result` ). + out->write( |\n| ). + out->write( data = association_links name = `association_links` ). + out->write( |\n| ). + + "Displaying response information + IF failed IS NOT INITIAL. + out->write( `Entries in FAILED response parameter` ). +out->write( |\n| ). + out->write( data = extract_from_failed( ) name = `extract_from_failed( )` ). + out->write( |\n| ). + ENDIF. + + IF reported IS NOT INITIAL. + out->write( `Entries in REPORTED response parameter` ). +out->write( |\n| ). + out->write( data = extract_from_reported( ) name = `extract_from_reported( )` ). + out->write( |\n| ). + ENDIF. + +********************************************************************** +* +* Read-by-association operation (from child to parent) +* +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `11) Read-by-Association Operation (from Child to Parent)` ) ). + +********************************************************************** +* Notes: +* - The EML request includes a read-by-association request from the +* child to the parent. +* - All field values are read using the addition ALL FIELDS WITH. +* - Specifying the parameter for RESULT is mandatory. +* - Additionally, the optional association links are retrieved. +********************************************************************** + + READ ENTITIES OF zdemo_abap_rap_ro_u + ENTITY child + BY \_parent ALL FIELDS WITH VALUE #( + ( key_field = 1 key_ch = 1 ) + ( key_field = 2 key_ch = 4 ) + "Instances to fail + ( key_field = 1 key_ch = 3 ) + ( key_field = 543 key_ch = 1 ) ) + RESULT DATA(rba_parent) + LINK DATA(association_links_parent) + FAILED failed + REPORTED reported. + + "Displaying read result and association links + out->write( data = rba_parent name = `rba_parent` ). + out->write( |\n| ). + out->write( data = association_links_parent name = `association_links_parent` ). + out->write( |\n| ). + + "Displaying response information + IF failed IS NOT INITIAL. + out->write( `Entries in FAILED response parameter` ). +out->write( |\n| ). + out->write( data = extract_from_failed( ) name = `extract_from_failed( )` ). + out->write( |\n| ). + ENDIF. + + IF reported IS NOT INITIAL. + out->write( `Entries in REPORTED response parameter` ). +out->write( |\n| ). + out->write( data = extract_from_reported( ) name = `extract_from_reported( )` ). + out->write( |\n| ). + ENDIF. + +********************************************************************** +* +* Excursion: Read and read-by-association operation using dynamic +* EML statements +* +* Note: +* If the parameter for FULL is not flagged, only the association +* links are returned. The parameter for RESULT will be empty. +********************************************************************** + +out->write( zcl_demo_abap_aux=>heading( `12) Excursion: Read and read-by-association ` && + `operations using dynamic EML` ) ). + DATA: + op_tab TYPE abp_behv_retrievals_tab, + read_dyn TYPE TABLE FOR READ IMPORT zdemo_abap_rap_ro_u, + read_dyn_result TYPE TABLE FOR READ RESULT zdemo_abap_rap_ro_u, + rba_dyn TYPE TABLE FOR READ IMPORT + zdemo_abap_rap_ro_u\_child, + rba_dyn_result TYPE TABLE FOR READ RESULT + zdemo_abap_rap_ro_u\_child, + rba_dyn_link TYPE TABLE FOR READ LINK zdemo_abap_rap_ro_u\_child. + + read_dyn = VALUE #( + ( %key-key_field = 1 + %control = VALUE #( + field1 = if_abap_behv=>mk-on + field2 = if_abap_behv=>mk-on + field3 = if_abap_behv=>mk-on + field4 = if_abap_behv=>mk-on ) ) + ( %key-key_field = 2 + %control = VALUE #( + field1 = if_abap_behv=>mk-on + field2 = if_abap_behv=>mk-on + field3 = if_abap_behv=>mk-on + field4 = if_abap_behv=>mk-on ) ) ). + + rba_dyn = VALUE #( + ( %key-key_field = 1 + %control = VALUE #( + key_ch = if_abap_behv=>mk-on + field_ch1 = if_abap_behv=>mk-on + field_ch2 = if_abap_behv=>mk-on ) ) + ( %key-key_field = 2 + %control = VALUE #( + key_ch = if_abap_behv=>mk-on + field_ch1 = if_abap_behv=>mk-on + field_ch2 = if_abap_behv=>mk-on ) ) ). + + out->write( `Result if FULL parameter is ` && + `not flagged for RBA` ). +out->write( |\n| ). + + op_tab = VALUE #( + ( op = if_abap_behv=>op-r-read + entity_name = 'ZDEMO_ABAP_RAP_RO_U' + instances = REF #( read_dyn ) + results = REF #( read_dyn_result ) ) + ( op = if_abap_behv=>op-r-read_ba + entity_name = 'ZDEMO_ABAP_RAP_RO_U' + sub_name = '_CHILD' + full = abap_false + instances = REF #( rba_dyn ) + results = REF #( rba_dyn_result ) + links = REF #( rba_dyn_link ) ) ). + + READ ENTITIES OPERATIONS op_tab. + + out->write( data = read_dyn_result name = `read_dyn_result` ). + out->write( |\n| ). + out->write( data = rba_dyn_result name = `rba_dyn_result` ). + out->write( |\n| ). + out->write( data = rba_dyn_link name = `rba_dyn_link` ). + out->write( |\n| ). + out->write( `Result if FULL parameter is ` && + `flagged for RBA` ). +out->write( |\n| ). + + op_tab = VALUE #( + ( op = if_abap_behv=>op-r-read + entity_name = 'ZDEMO_ABAP_RAP_RO_U' + instances = REF #( read_dyn ) + results = REF #( read_dyn_result ) ) + ( op = if_abap_behv=>op-r-read_ba + entity_name = 'ZDEMO_ABAP_RAP_RO_U' + sub_name = '_CHILD' + full = abap_true + instances = REF #( rba_dyn ) + results = REF #( rba_dyn_result ) + links = REF #( rba_dyn_link ) ) ). + + READ ENTITIES OPERATIONS op_tab. + + out->write( data = read_dyn_result name = `read_dyn_result` ). + out->write( |\n| ). + out->write( data = rba_dyn_result name = `rba_dyn_result` ). + out->write( |\n| ). + out->write( data = rba_dyn_link name = `rba_dyn_link` ). + out->write( |\n| ). + ENDMETHOD. + + + METHOD initialize_dbtabs. + DELETE FROM zdemo_abap_rapt1. + DELETE FROM zdemo_abap_rapt2. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_rap_ext_num_u.clas.xml b/src/zcl_demo_abap_rap_ext_num_u.clas.xml new file mode 100644 index 0000000..8a82b32 --- /dev/null +++ b/src/zcl_demo_abap_rap_ext_num_u.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_RAP_EXT_NUM_U + E + ABAP cheat sheet: ABAP EML in a RAP scenario (unmanaged BO) + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_rap_m_as.clas.abap b/src/zcl_demo_abap_rap_m_as.clas.abap new file mode 100644 index 0000000..9210043 --- /dev/null +++ b/src/zcl_demo_abap_rap_m_as.clas.abap @@ -0,0 +1,318 @@ +*********************************************************************** +* +* RAP BO consumer for a RAP demo scenario: +* Managed RAP BO with managed internal numbering and additional save +* demonstrating the local consumption of RAP business events +* +* -------------------------- PURPOSE ---------------------------------- +* - Primarily, the example demonstrates the local consumption of RAP +* business events. +* - For that purpose, the BDEF defines three events. Two of them are +* specified with a parameter. The events are raised for create, update +* and delete operations. +* - The example implementation in this class (the RAP BO consumer) +* contains three ABAP EML modify requests: a RAP create, update and +* delete operation. For each of the operations, an event is raised +* using a RAISE ENTITY EVENT statement. The events are raised in the +* save_modified RAP saver method in the CCIMP include of the behavior +* pool. +* - When the events are raised, the RAP event handler methods are called +* asynchronously. To demonstrate the effect of the events, a database +* table representing a log table is populated. +* - In the output of the example, the content of internal tables is +* displayed to demonstrate the effect of the RAP operations by +* selecting from the database table where the RAP BO instances are +* persisted to after each RAP operation. Additionally, the content +* of an internal table is displayed including the entries that have been +* inserted into the log database table by the event handler methods. +* In this self-contained example, this 'log database table' is just a +* database table that is used to store some entries triggered by the RAP +* events for visualization purposes. You can imagine that, for example, +* the sending of an email is triggered there, or the application log is +* filled, and so on. The log table is used in another RAP example as +* draft table. The draft concept is not relevant for this simplified +* example here. +* - Note the comments in the example code. You can check out the +* asynchronity by commenting out the WAIT statement further down. +* +* ----------------- RELATED ARTIFACTS OF THE EXAMPLE ------------------* +* - RAP BO consumer: zcl_demo_abap_rap_m_as (this class here) +* - RAP BO provider (ABAP behavior pool): zbp_demo_abap_rap_ro_m_as +* - RAP event handler: zcl_demo_abap_rap_evt_handler +* - BDEF: zdemo_abap_rap_ro_m_as +* - More artifacts are related such as database tables, CDS views, and +* an abstract entity (zdemo_abap_abstract_ent; used for the parameter +* specifications for the events in the BDEF) +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP development tools for Eclipse (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 ----------------------------------- +* This simplified example is not a real life scenario and rather +* focuses on the technical side by giving an idea how the communication +* and data exchange between a RAP BO consumer, which is a class +* in this case, and RAP BO provider can work. Additionally, it shows +* how the methods for non-standard RAP BO operations might be +* self-implemented in an ABP. The example is intentionally kept +* short and simple and focuses on specific RAP aspects. For this reason, +* the example might not fully meet the requirements of the RAP BO contract. +* +* The code presented in this class is intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

    ABAP cheat sheet: Local consumption of RAP Business Events

    +"! Example to demonstrate local consumption of RAP business events in the context of a RAP demo scenario (managed RAP BO with managed internal numbering and additional save). +"! The class represents a RAP BO consumer.
    Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_rap_m_as DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + + CLASS-METHODS: + class_constructor. + + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + +CLASS zcl_demo_abap_rap_m_as IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + out->write( |ABAP Cheat Sheet Example: Local Consumption of RAP Business Events\n\n| ). + "RAP create operation that creates multiple calculations + "Note the AUTO FILL CID addition with which the required %cid values + "for RAP BO instances are created automatically. Since the scenario is + "managed internal numbering, the 'id' field is assigned an appropriate value + "automatically by the RAP framework. + MODIFY ENTITY zdemo_abap_rap_ro_m_as + CREATE AUTO FILL CID + FIELDS ( num1 arithm_op num2 ) + WITH VALUE #( ( num1 = 1 + arithm_op = '+' + num2 = 2 ) + ( num1 = 5 + arithm_op = '-' + num2 = 30 ) + ( num1 = 3 + arithm_op = '*' + num2 = 3 ) + ( num1 = 7 + arithm_op = '/' + num2 = 5 ) + ( num1 = 2 + arithm_op = 'P' + num2 = 4 ) + ( num1 = 10 + arithm_op = 'P' + num2 = 1000000000 ) + ( num1 = 2 + arithm_op = '/' + num2 = 0 ) ) + MAPPED DATA(m_cr) + FAILED DATA(f_cr) + REPORTED DATA(r_cr). + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Getting and displaying persisted instances to show the effect of the + "EML MODIFY statement + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, lchg_date_time + FROM zdemo_abap_tabca + ORDER BY lchg_date_time + INTO TABLE @DATA(dbtab_entries). + + out->write( `Database table entries after the create operation` + )->write( dbtab_entries + )->write( |\n| ). + +********************************************************************** + + "RAP update operation + "In the example, all RAP BO instances that were created above are + "updated. Here, the second number is updated with a random + "integer (from the value range 1 - 10). For this purpose, the + "cl_abap_random_int class is used. + "The reference to existing instances is made using entries in the + "mapped reponse table from above (it contains all keys of the created + "RAP BO instances). + IF m_cr-root IS NOT INITIAL. + MODIFY ENTITY zdemo_abap_rap_ro_m_as + UPDATE FIELDS ( num2 ) + WITH VALUE #( FOR wa IN m_cr-root ( id = wa-id + num2 = cl_abap_random_int=>create( + seed = cl_abap_random=>seed( ) + min = 1 + max = 10 )->get_next( ) ) ) + FAILED DATA(f_upd) + REPORTED DATA(r_upd). + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Getting and displaying persisted instances to show the effect of the + "EML MODIFY statement + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, lchg_date_time + FROM zdemo_abap_tabca + ORDER BY lchg_date_time + INTO TABLE @dbtab_entries. + + out->write( `Database table entries after the update operation` + )->write( dbtab_entries + )->write( |\n| ). + ENDIF. + +********************************************************************** + + "RAP delete operation + "As above, the reference to existing RAP BO instances is made + "using entries in the mapped response table. In the example, + "the first three instances created are deleted. + IF lines( m_cr-root ) > 3. + MODIFY ENTITY zdemo_abap_rap_ro_m_as + DELETE FROM VALUE #( ( id = m_cr-root[ 1 ]-id ) + ( id = m_cr-root[ 2 ]-id ) + ( id = m_cr-root[ 3 ]-id ) ) + FAILED DATA(f_del) + REPORTED DATA(r_del). + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + out->write( `An issue occurred in the RAP save sequence.` ). + ENDIF. + + "Getting and displaying persisted instances to show the effect of the + "EML MODIFY statement + SELECT id, num1, arithm_op, num2, calc_result, crea_date_time, lchg_date_time + FROM zdemo_abap_tabca + ORDER BY lchg_date_time + INTO TABLE @dbtab_entries. + + out->write( `Database table entries after the delete operation` + )->write( dbtab_entries + )->write( |\n| ). + ENDIF. + +********************************************************************** + + "Note: + "- Due to the asynchronous call of the events, a WAIT statement + " is included to give the events some time in this self-contained example, + " i.e. so that all the database table entries that are created in the + " RAP event handler method can be retrieved and displayed. + "- In the RAP event handler method implementation, note the cl_abap_tx=>save( ). + " method call. This explicit activation of the 'save' transactional phase + " is required because, when called, the methods are started in the + " 'modify' transactional phase. In the modify phase, database modification + " statements are not allowed. If the save phase is not activated, + " a following database modification statement results in an error. + + "To explore the asynchronity of the event raising, you can comment out the + "following WAIT statement. + WAIT UP TO 2 SECONDS. + DATA(tmstmp_after_wait) = cl_abap_tstmp=>utclong2tstmp( utclong_current( ) ). + out->write( |Timestamp (e.g. for comparing with the timestamps of the events raised): { tmstmp_after_wait }| ). + out->write( |\n| ). + + "The global class of the behavior pool contains a static attribute of type i. + "The example is implemented as follows: When events are raised in the save_modified + "method, this number is increased by 1 per event raised. The number represents + "the expected events to be raised. It is used in the following example implementation. + "As mentioned above, you can comment out the WAIT statement to potentially see + "a different number here compared to the number of entries in the log table. + out->write( |Number of expected events raised: { zbp_demo_abap_rap_ro_m_as=>num_raised_events }| ). + out->write( |\n| ). + + "Getting and displaying database table entries that were inserted + "in the event handler method implementations to demonstrate that + "events have been triggered. + SELECT calc_result, crea_date_time + FROM zdemo_abap_draft + ORDER BY crea_date_time + INTO TABLE @DATA(evt_log_entries). + + out->write( |Entries in log table at this stage (after SELECT from database table): { lines( evt_log_entries ) }| ). + out->write( |\n| ). + + DATA(flag) = abap_false. + IF evt_log_entries IS INITIAL. + out->write( `There are no entries in the log table.` && + ` Try and run the example again.` ). + flag = abap_true. + ELSEIF lines( evt_log_entries ) BETWEEN 1 AND zbp_demo_abap_rap_ro_m_as=>num_raised_events - 1. + out->write( `Log database table entries created by the ` && + `raised events` )->write( `Note that not all expected log database table entries have been created yet by the ` && + `raised events` + )->write( evt_log_entries ). + flag = abap_true. + ELSE. + out->write( `Log database table entries created by the ` && + `raised events` + )->write( evt_log_entries ). + ENDIF. + + "The following implementation is included for exploring the asynchronity in the self-contained example, if you + "have commented out the WAIT statement above, or if not all expected entries are available in the database table + "yet. This is just to give it some more time and select from the database table again. + IF flag = abap_true. + out->write( |\n| ). + out->write( |******************************************************| ). + out->write( |\n| ). + out->write( |Out of { zbp_demo_abap_rap_ro_m_as=>num_raised_events } events that are expected to be raised in the example implementation, | && + |only { lines( evt_log_entries ) } events are available in the database table at this stage. So, waiting a bit more ...| ). + out->write( |\n| ). + + WAIT UP TO 1 SECONDS. + + SELECT calc_result, crea_date_time + FROM zdemo_abap_draft + ORDER BY crea_date_time + INTO TABLE @evt_log_entries. + + IF lines( evt_log_entries ) = zbp_demo_abap_rap_ro_m_as=>num_raised_events. + out->write( `Log database table entries created by the ` && + `raised events after waiting some more time` + )->write( evt_log_entries ). + ELSE. + out->write( |Hmm... still not all events are available in the database table.| ). + out->write( evt_log_entries ). + ENDIF. + ENDIF. + ENDMETHOD. + + METHOD class_constructor. + DELETE FROM zdemo_abap_tabca. + DELETE FROM zdemo_abap_draft. + CLEAR zbp_demo_abap_rap_ro_m_as=>num_raised_events. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_rap_m_as.clas.xml b/src/zcl_demo_abap_rap_m_as.clas.xml new file mode 100644 index 0000000..388976f --- /dev/null +++ b/src/zcl_demo_abap_rap_m_as.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_RAP_M_AS + E + ABAP cheat sheet: Local consumption of RAP Business Events + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_sql.clas.abap b/src/zcl_demo_abap_sql.clas.abap new file mode 100644 index 0000000..133f6d2 --- /dev/null +++ b/src/zcl_demo_abap_sql.clas.abap @@ -0,0 +1,1618 @@ +*********************************************************************** +* +* ABAP cheat sheet: ABAP SQL +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntax options for working with +* persisted data in database tables using ABAP SQL. +* - Topics covered: reading from database tables using SELECT, changing +* data in database tables using INSERT, UPDATE, MODIFY and DELETE +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP development tools for Eclipse (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, refer to the +* notes included in the class as comments or refer to the respective +* topic in the ABAP Keyword Documentation. +* - Due to the amount of console output, the examples contain numbers +* (e.g. 1) ..., 2) ..., 3) ...) for the individual example sections. +* Also, the variable name is displayed in most cases. So to find +* the relevant output in the console easier and faster, just search +* for the number/variable name in the console (CTRL+F in the console) +* or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

    ABAP cheat sheet: ABAP SQL

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

    ABAP cheat sheet: ABAP SQL - Grouping internal tables

    +"! Example to demonstrate grouping internal tables.
    Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_sql_group_by DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + + CLASS-METHODS: class_constructor. + +protected section. + PRIVATE SECTION. + CLASS-DATA: + wa TYPE zdemo_abap_flsch, + member TYPE zdemo_abap_flsch, + members TYPE STANDARD TABLE OF zdemo_abap_flsch WITH EMPTY KEY. + +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_SQL_GROUP_BY IMPLEMENTATION. + + + METHOD class_constructor. + "Fill demo database tables. + zcl_demo_abap_aux=>fill_dbtabs( ). + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + out->write( |ABAP Cheat Sheet Example: Grouping Internal Tables\n\n| ). + + SELECT * + FROM zdemo_abap_flsch + INTO TABLE @DATA(fli_tab). + + out->write( |1) Representative Binding\n| ). + out->write( |1a) Grouping by one column\n| ). + + LOOP AT fli_tab INTO wa + GROUP BY wa-carrid. + out->write( wa-carrid ). + ENDLOOP. + + out->write( zcl_demo_abap_aux=>heading( `1b) Members of one column groups` ) ). + + LOOP AT fli_tab INTO wa + GROUP BY wa-carrid. + CLEAR members. + LOOP AT GROUP wa INTO member. + members = VALUE #( BASE members ( member ) ). + ENDLOOP. + + out->write( members ). + out->write( |\n| ). + ENDLOOP. + + out->write( zcl_demo_abap_aux=>heading( `1c) Grouping by two columns` ) ). + + LOOP AT fli_tab INTO wa + GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom ). + + out->write( |{ wa-carrid } { wa-airpfrom }| ). + out->write( |\n| ). + ENDLOOP. + + out->write( zcl_demo_abap_aux=>heading( `1d) Members of two column groups` ) ). + + LOOP AT fli_tab INTO wa + GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom ). + CLEAR members. + LOOP AT GROUP wa INTO member. + members = VALUE #( BASE members ( member ) ). + ENDLOOP. + + out->write( members ). + out->write( |\n| ). + ENDLOOP. + + out->write( zcl_demo_abap_aux=>heading( `2) Group Key Binding` ) ). + out->write( |2a) Grouping by one column\n| ). + + LOOP AT fli_tab INTO wa + GROUP BY wa-carrid + INTO DATA(key). + + out->write( key ). + out->write( |\n| ). + ENDLOOP. + + out->write( zcl_demo_abap_aux=>heading( `2b) Members of one column groups` ) ). + + LOOP AT fli_tab INTO wa + GROUP BY wa-carrid + INTO key. + CLEAR members. + LOOP AT GROUP key INTO member. + members = VALUE #( BASE members ( member ) ). + ENDLOOP. + + out->write( members ). + out->write( |\n| ). + ENDLOOP. + + out->write( zcl_demo_abap_aux=>heading( `2c) Grouping by two columns` ) ). + + LOOP AT fli_tab INTO wa + GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom ) + INTO DATA(keys). + + out->write( keys ). + out->write( |\n| ). + ENDLOOP. + + out->write( zcl_demo_abap_aux=>heading( `2d) Members of two column groups` ) ). + + LOOP AT fli_tab INTO wa + GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom ) + INTO keys. + CLEAR members. + LOOP AT GROUP keys INTO member. + members = VALUE #( BASE members ( member ) ). + ENDLOOP. + + out->write( members ). + out->write( |\n| ). + ENDLOOP. + + out->write( zcl_demo_abap_aux=>heading( `2e) Two column groups without members` ) ). + + LOOP AT fli_tab INTO wa + GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom + index = GROUP INDEX size = GROUP SIZE ) + WITHOUT MEMBERS + INTO DATA(keysplus). + + out->write( keysplus ). + out->write( |\n| ). + ENDLOOP. + + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_sql_group_by.clas.xml b/src/zcl_demo_abap_sql_group_by.clas.xml new file mode 100644 index 0000000..697073e --- /dev/null +++ b/src/zcl_demo_abap_sql_group_by.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_SQL_GROUP_BY + E + ABAP cheat sheet: ABAP SQL - Grouping Internal Tables + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_string_proc.clas.abap b/src/zcl_demo_abap_string_proc.clas.abap new file mode 100644 index 0000000..683ff90 --- /dev/null +++ b/src/zcl_demo_abap_string_proc.clas.abap @@ -0,0 +1,2251 @@ +*********************************************************************** +* +* ABAP cheat sheet: String processing +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntax options for processing +* character strings. +* - Topics covered: Creating strings and assigning values, chaining strings, +* string templates, concatenating/splitting/modifying strings, searching +* and replacing, regular expressions +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP development tools for Eclipse (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, refer to the +* notes included in the class as comments or refer to the respective +* topic in the ABAP Keyword Documentation. +* - Due to the amount of console output, the examples contain numbers +* (e.g. 1) ..., 2) ..., 3) ...) for the individual example sections. +* Also, the variable name is displayed in most cases. So to find +* the relevant output in the console easier and faster, just search +* for the number/variable name in the console (CTRL+F in the console) +* or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

    ABAP cheat sheet: String processing

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

    Hallo!

    `. + + "Escaping a backquote using another backquote + str_a3 = `This is a backquote: ``.`. + + "If possible, avoid unnecessary type conversion; in principle, + "every convertible type can be specified + "Assigning a fixed-length string to a variable-length string. + str_a2 = 'abc'. + + DATA str_a4 TYPE string VALUE 'X'. "Type c length 1 + + DATA str_a5 TYPE string VALUE -1. "Type i + + "Inline declaration: data object declaration and + "value assignment + "Data type is automatically derived + DATA(char_a2) = 'abcd'. "Type c length 4 + + DATA(str_a6) = `efgh`. "Type string + + "Note: Variable is of type c length 4. Characters are truncated. + "In newer ABAP releases, the following statement shows a syntax + "warning that the value of the literal (intentionally specified + "here like this) is not an admissable value for the target type. + "Therefore, the example is provided differently to circumvent the + "syntax warning. + + "char_a2 = 'ijklmnopq'. + + TYPES c_l9 TYPE c LENGTH 9. + DATA some_char TYPE c_l9 VALUE 'ijklmnopq'. + char_a2 = some_char. + + "Treating trailing blanks + DATA(char_a3) = 'ab '. + + DATA(str_a7) = `cdefgh`. + + str_a7 = char_a3. "Trailing blanks are not respected. + + "Excursion: Chaining strings + "Note the conversion result of str_a5 above (i to string) + DATA(str_a8) = str_a4 && ` ` && str_a5 && `!`. + + out->write( data = str_a3 name = `str_a3` ). + out->write( |\n| ). + out->write( data = char_a2 name = `char_a2` ). + out->write( |\n| ). + out->write( data = str_a7 name = `str_a7` ). + out->write( |\n| ). + out->write( data = str_a8 name = `str_a8` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `2) Chaining Strings` ) ). + + DATA(str_b1) = `Hallo`. + DATA(str_b2) = `how`. + DATA(str_b3) = `are`. + + "Chaining using && operator + DATA(str_b4) = str_b1 && ` ` && sy-uname && `, ` && str_b2 && ` ` && str_b3 && ` you?`. + + "Chaining only character literals of the same type using & operator + "Note: Such a combination of literals is possible up to 255 chars. + DATA(char_b1) = 'AB' & 'AP '. "Trailing blanks are ignored + + DATA(str_b5) = `AB` & `AP `. + + out->write( data = str_b4 name = `str_b4` ). + out->write( |\n| ). + out->write( data = char_b1 name = `char_b1` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `3a) String Templates (1): Constructing Strings` ) ). + + "The expression must be convertible to a string. A blank (not + "within the curly brackets) means a blank in the resulting string. + DATA(str_c1) = `Hallo`. + DATA(str_c2) = `how`. + DATA(str_c3) = `are`. + DATA(str_c4) = |{ str_c1 } { sy-uname }, | && + |{ str_c2 } { str_c3 } you?|. + + out->write( data = str_c4 name = `str_c4` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `3b) String Templates (2): Control Characters` ) ). + + "Interpretation of character combinations as control characters + "\n interpreted as a line break + DATA(str_c5) = |{ str_c1 }\n{ sy-uname },| && + |\n{ str_c2 }\n{ str_c3 }\nyou?|. + + out->write( data = str_c5 name = `str_c5` ). + out->write( |\n| ). + + "Excursion: Class CL_ABAP_CHAR_UTILITIES provides attributes and methods as utilities for string processing. + "See the class documentation. + "The following examples demonstrate that attributes that contain control characters can be replaced by + "a representation of control characters in a string template. + DATA(str_c6) = |{ str_c1 }{ cl_abap_char_utilities=>newline }{ sy-uname }|. + DATA(str_c7) = |{ str_c1 }\n{ sy-uname }|. + DATA(str_c8) = |{ str_c1 }{ cl_abap_char_utilities=>horizontal_tab }{ sy-uname }|. + DATA(str_c9) = |{ str_c1 }\t{ sy-uname }|. + DATA(str_c10) = |{ str_c1 }{ cl_abap_char_utilities=>cr_lf }{ sy-uname }|. + DATA(str_c11) = |{ str_c1 }\r\n{ sy-uname }|. + ASSERT str_c10 = str_c11. + + out->write( data = str_c6 name = `str_c6` ). + out->write( |\n| ). + out->write( data = str_c7 name = `str_c7` ). + out->write( |\n| ). + out->write( data = str_c8 name = `str_c8` ). + out->write( |\n| ). + out->write( data = str_c9 name = `str_c9` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `4) String Templates (3): Formatting Options` ) ). + "Time, date + DATA(str_d1a) = |Date: { cl_abap_context_info=>get_system_date( ) DATE = USER }|. + DATA(str_d1b) = |Time: { cl_abap_context_info=>get_system_time( ) TIME = ISO }|. + DATA(str_d1c) = |Timestamp: { utclong_current( ) TIMESTAMP = SPACE }|. + + "Upper, lower case + DATA(str_d2) = |AbCdEfG|. + DATA(str_d3) = |{ str_d2 CASE = LOWER }|. + DATA(str_d4) = |{ str_d2 CASE = UPPER }|. + + "Width and alignment + DATA(str_d5) = |{ 'Left' WIDTH = 20 ALIGN = LEFT }<---|. + DATA(str_d6) = |{ 'Center' WIDTH = 20 ALIGN = CENTER }<---|. + DATA(str_d7) = |{ 'Right' WIDTH = 20 ALIGN = RIGHT }<---|. + DATA(str_d8) = |{ 'Left' WIDTH = 20 ALIGN = LEFT PAD = '.' }<---|. + DATA(str_d9) = |{ 'Center' WIDTH = 20 ALIGN = CENTER PAD = '.' }<---|. + DATA(str_d10) = |{ 'Right' WIDTH = 20 ALIGN = RIGHT PAD = '.' }<---|. + + "Numbers + DATA(str_d11) = |{ - 2 / 3 DECIMALS = 3 }, { + CONV decfloat34( - 2 / 3 ) DECIMALS = 3 }, { + CONV f( - 2 / 3 ) DECIMALS = 3 }|. + + "Escaping \|{} in string templates + DATA(str_d14) = |\\ \| \{ \}|. + + out->write( data = str_d1a name = `str_d1a` ). + out->write( |\n| ). + out->write( data = str_d1b name = `str_d1b` ). + out->write( |\n| ). + out->write( data = str_d1c name = `str_d1c` ). + out->write( |\n| ). + out->write( data = str_d3 name = `str_d3` ). + out->write( |\n| ). + out->write( data = str_d4 name = `str_d4` ). + out->write( |\n| ). + out->write( data = str_d5 name = `str_d5` ). + out->write( |\n| ). + out->write( data = str_d6 name = `str_d6` ). + out->write( |\n| ). + out->write( data = str_d7 name = `str_d7` ). + out->write( |\n| ). + out->write( data = str_d8 name = `str_d8` ). + out->write( |\n| ). + out->write( data = str_d9 name = `str_d9` ). + out->write( |\n| ). + out->write( data = str_d10 name = `str_d10` ). + out->write( |\n| ). + out->write( data = str_d11 name = `str_d11` ). + out->write( |\n| ). + out->write( data = str_d14 name = `str_d14` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `5) Determining the Length of Strings` ) ). + + DATA(str_e1) = `abc def ghi `. + DATA(char_e1) = 'abc def ghi '. + + "strlen + "Result depends on the type of the data object + "Fixed-length string ignores trailing blanks + DATA(length_e1) = strlen( str_e1 ). + DATA(length_e2) = strlen( char_e1 ). + + "numofchar + "To exclude trailing blanks in any case. + DATA(length_e3) = numofchar( str_e1 ). + DATA(length_e4) = numofchar( char_e1 ). + + "Excursion: + "To emphasizes modern, expression-enabled ABAP, the expression + "with the string function can be placed directly in the DO + "statement instead of having an extra variable. + DATA(str_e3) = `abcde`. + DATA(length_e5) = strlen( str_e3 ). + DATA(int_e1) = 0. + + DO length_e5 TIMES. + int_e1 += 1. + ENDDO. + + DATA(int_e2) = 0. + + DO strlen( str_e3 ) TIMES. + int_e2 += 1. + ENDDO. + + out->write( data = length_e1 name = `length_e1` ). + out->write( |\n| ). + out->write( data = length_e2 name = `length_e2` ). + out->write( |\n| ). + out->write( data = length_e3 name = `length_e3` ). + out->write( |\n| ). + out->write( data = length_e4 name = `length_e4` ). + out->write( |\n| ). + out->write( data = int_e1 name = `int_e1` ). + out->write( |\n| ). + out->write( data = int_e2 name = `int_e2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `6) Concatenating Strings` ) ). + + DATA(str_f1) = `Hallo`. + DATA(str_f2) = `world`. + + "Concatenation using && operator and string templates + DATA(str_f3) = str_f1 && str_f2. + DATA(str_f4) = str_f1 && ` ` && str_f2. + DATA(str_f5) = |{ str_f1 } { str_f2 }|. + + "CONCATENATE statements + CONCATENATE str_f1 str_f2 INTO DATA(str_f6). + "Adding a separation sign using the addition SEPARATED BY + CONCATENATE str_f1 str_f2 INTO DATA(str_f7) SEPARATED BY ` `. + CONCATENATE str_f1 str_f2 INTO DATA(str_f8) SEPARATED BY `#`. + + DATA(char_f1) = '2 trailing blanks: '. + DATA(char_f2) = '3 trailing blanks: '. + DATA(char_f3) = '<-'. + "Keeping trailing blanks in the result when concatenating + "fixed-length strings. The ones of variable-length strings are + "respected by default + CONCATENATE char_f1 char_f2 char_f3 + INTO DATA(char_f4) RESPECTING BLANKS. + "Trailing blanks are ignored + CONCATENATE char_f1 char_f2 char_f3 INTO DATA(char_f5). + + "Example use case: Concatenating smaller text fragments + "sequentially into a longer character sequence. + DATA: itab_g TYPE TABLE OF string, + alphabet1 TYPE string. + + itab_g = VALUE #( ( `abc` ) ( `def` ) ( `ghi` ) ). + + LOOP AT itab_g ASSIGNING FIELD-SYMBOL(). + alphabet1 = alphabet1 && . + "Alternative: + "CONCATENATE alphabet INTO alphabet. + ENDLOOP. + + "Avoiding loops if your use case is to concatenate lines of an + "internal table into a string in one go + CONCATENATE LINES OF itab_g INTO DATA(alphabet2). + + ""Adding a separation sign using the addition SEPARATED BY + CONCATENATE LINES OF itab_g INTO DATA(alphabet3) + SEPARATED BY ` `. + + "String function concat_lines_of + DATA(alphabet4) = concat_lines_of( table = itab_g ). + "sep parameter specifying the separation sign + DATA(alphabet5) = concat_lines_of( table = itab_g sep = `,` ). + + out->write( data = str_f3 name = `str_f3` ). + out->write( |\n| ). + out->write( data = str_f4 name = `str_f4` ). + out->write( |\n| ). + out->write( data = str_f5 name = `str_f5` ). + out->write( |\n| ). + out->write( data = str_f6 name = `str_f6` ). + out->write( |\n| ). + out->write( data = str_f7 name = `str_f7` ). + out->write( |\n| ). + out->write( data = str_f8 name = `str_f8` ). + out->write( |\n| ). + out->write( data = char_f4 name = `char_f4` ). + out->write( |\n| ). + out->write( data = char_f5 name = `char_f5` ). + out->write( |\n| ). + out->write( data = alphabet1 name = `alphabet1` ). + out->write( |\n| ). + out->write( data = alphabet2 name = `alphabet2` ). + out->write( |\n| ). + out->write( data = alphabet3 name = `alphabet3` ). + out->write( |\n| ). + out->write( data = alphabet4 name = `alphabet4` ). + out->write( |\n| ). + out->write( data = alphabet5 name = `alphabet5` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `7) Splitting Strings` ) ). + + DATA(str_g1) = `Hallo,world,12345`. + + SPLIT str_g1 AT `,` INTO DATA(str_g2) DATA(str_g3) DATA(str_g4). + + "Less data objects than possible splittings + SPLIT str_g1 AT `,` INTO DATA(str_g5) DATA(str_g6). + + "Splitting string into an internal table + DATA itab_g1 TYPE TABLE OF string. + + SPLIT str_g1 AT ',' INTO TABLE itab_g1. + + "Getting the value of a specific segment + DATA(str_g7) = segment( val = str_g1 index = 2 sep = `,` ). + + "Example with segment + "A string is split and the values of segments are retrieved. Here, + "all segments are retrieved and inserted into an internal table + "using a DO loop. If you specify an empty string, an exception of + "the class CX_SY_STRG_PAR_VAL is raised. This is true for this + "example since the DO loop inevitably runs into the error because + "of not specifying an appropriate number of loops. Note that + "if the index parameter of the segment function is positive, the + "occurrences are counted from the left. If index is negative, the + "occurrences are counted from the right. + DATA itab_g2 TYPE TABLE OF string. + DO. + TRY. + DATA(str_g8) = segment( val = str_g1 + index = sy-index + sep = `,` ). + + APPEND |Segment value: '{ str_g8 }' | && + |Segment index: '{ sy-index }'| TO itab_g2. + + CATCH cx_sy_strg_par_val. + DATA(seg_nom) = |There are { sy-index - 1 } | && + |segments in the string.|. + EXIT. + ENDTRY. + ENDDO. + + out->write( data = str_g2 name = `str_g2` ). + out->write( |\n| ). + out->write( data = str_g3 name = `str_g3` ). + out->write( |\n| ). + out->write( data = str_g4 name = `str_g4` ). + out->write( |\n| ). + out->write( data = str_g5 name = `str_g5` ). + out->write( |\n| ). + out->write( data = str_g6 name = `str_g6` ). + out->write( |\n| ). + out->write( data = itab_g1 name = `itab_g1` ). + out->write( |\n| ). + out->write( data = str_g7 name = `str_g7` ). + out->write( |\n| ). + out->write( data = str_g8 name = `str_g8` ). + out->write( |\n| ). + out->write( data = itab_g2 name = `itab_g2` ). + out->write( |\n| ). + out->write( data = seg_nom name = `seg_nom` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Modifying Strings` ) ). + out->write( |8) Transforming to Lower and Upper Case\n\n| ). + + DATA(str_h1) = `It's a string`. + DATA(str_h2) = str_h1. + + "The string functions store the result in a target variable. + DATA(str_h3) = to_upper( str_h1 ). + DATA(str_h4) = to_lower( str_h1 ). + + "TRANSLATE does the transformation on the source variable. + TRANSLATE str_h1 TO UPPER CASE. + TRANSLATE str_h2 TO LOWER CASE. + + "to_mixed/from_mixed functions + "sep: Specifies the separator + "case: A character-like text field. A small character specifies + "that the first character of the string is in lower case. If the + "specification is, for example, case = 'X', the first character + "is capitalized. + "min: A positive number that specifies the minimum number of + "characters that must appear before the separator. The default + "value is 1. + DATA(str_h5) = `A_GREAT_STRING`. + DATA(str_h6) = to_mixed( val = str_h5 sep = `_` ). + DATA(str_h7) = to_mixed( val = str_h5 sep = `_` case = 'x' ). + DATA(str_h8) = to_mixed( val = str_h5 sep = `_` + case = 'a' min = 3 ). + + DATA(str_h9) = from_mixed( val = `someGreatString` sep = ` ` + case = 'a' min = 4 ). + + out->write( data = str_h3 name = `str_h3` ). + out->write( |\n| ). + out->write( data = str_h4 name = `str_h4` ). + out->write( |\n| ). + out->write( data = str_h1 name = `str_h1` ). + out->write( |\n| ). + out->write( data = str_h2 name = `str_h2` ). + out->write( |\n| ). + out->write( data = str_h6 name = `str_h6` ). + out->write( |\n| ). + out->write( data = str_h7 name = `str_h7` ). + out->write( |\n| ). + out->write( data = str_h8 name = `str_h8` ). + out->write( |\n| ). + out->write( data = str_h9 name = `str_h9` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `9) Shifting Content in Strings` ) ). + + DATA(str_i1) = `hallo`. + DATA(str_i2) = str_i1. + DATA(str_i3) = str_i1. + DATA(str_i4) = str_i1. + + "No addition; string is shifted one place to the left + SHIFT str_i2. + + "Shifting string by n places; without direction, + "left by default + SHIFT str_i3 BY 2 PLACES. + + "Direction explicitly specified + "Variable-length strings are extended + SHIFT str_i4 BY 3 PLACES RIGHT. + + DATA(char_i1) = 'world '. + DATA(char_i2) = char_i1. + DATA(char_i3) = char_i1. + DATA(str_i5) = `world `. + + "Comparison of behavior for fixed- and variable-length strings + SHIFT char_i1 BY 3 PLACES RIGHT. + SHIFT str_i5 BY 3 PLACES RIGHT. + + "CIRCULAR addition: characters that are moved out of the string are + "added at the other end again + SHIFT char_i2 BY 3 PLACES RIGHT CIRCULAR. + SHIFT char_i3 BY 2 PLACES LEFT CIRCULAR. + + DATA(str_i6) = ` hallo world `. + DATA(str_i7) = str_i6. + + "Moving characters up to a specific character set + SHIFT str_i6 UP TO 'or'. + + "Deleting leading and trailing characters with this sequence + "of statements + SHIFT str_i7 RIGHT DELETING TRAILING ` `. + SHIFT str_i7 LEFT DELETING LEADING ` `. + + "String functions storing the result in a target variable + DATA(str_i8) = `some string`. + + "shift_left + DATA(str_i9) = shift_left( val = str_i8 places = 3 ). + + DATA(str_i10) = shift_left( val = str_i8 circular = 7 ). + + "shift_right + "Note: When the parameter places is specified, the function + "shift_right has a different behavior than the SHIFT statement. + "Here, the length of the string is reduced. SHIFT extends the + "length or it remains the same. + DATA(str_i11) = shift_right( val = str_i8 places = 3 ). + + DATA(str_i12) = `shift_right and trailing blanks `. + + "sub: Specifying a substring; all substrings in the string that + "match the value are removed (sub also available for shift_left) + DATA(str_i13) = shift_right( val = str_i12 + sub = ` and trailing blanks ` ). + + DATA(str_i14) = shift_right( val = str_i12 sub = ` ` ). + + DATA(str_i15) = shift_right( val = str_i12 ). "Same effect as above + + out->write( |SHIFT statements:\n\n| ). + out->write( data = str_i2 name = `str_i2` ). + out->write( |\n| ). + out->write( data = str_i3 name = `str_i3` ). + out->write( |\n| ). + out->write( data = str_i4 name = `str_i4` ). + out->write( |\n| ). + out->write( data = char_i1 name = `char_i1` ). + out->write( |\n| ). + out->write( data = str_i5 name = `str_i5` ). + out->write( |\n| ). + out->write( data = char_i2 name = `char_i2` ). + out->write( |\n| ). + out->write( data = char_i3 name = `char_i3` ). + out->write( |\n| ). + out->write( data = str_i6 name = `str_i6` ). + out->write( |\n| ). + out->write( data = str_i7 name = `str_i7` ). + out->write( |\n| ). + + out->write( |String functions:\n\n| ). + out->write( |\n| ). + out->write( data = str_i9 name = `str_i9` ). + out->write( |\n| ). + out->write( data = str_i10 name = `str_i10` ). + out->write( |\n| ). + out->write( data = str_i11 name = `str_i11` ). + out->write( |\n| ). + out->write( data = str_i13 name = `str_i13` ). + out->write( |\n| ). + out->write( data = str_i14 name = `str_i14` ). + out->write( |\n| ). + out->write( data = str_i15 name = `str_i15` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `10) Condensing Strings` ) ). + + DATA(char_j1) = ' some text '. + DATA(char_j2) = ' some more text '. + DATA(char_j3) = ' a third text field literal '. + + "No addition: Removes leading and trailing blanks. This is also + "true for multiple blanks. It also replaces sequences of multiple + "blanks with a single blank. + CONDENSE char_j1. + CONDENSE char_j2. + + "NO-GAPS: Removes all blanks, also between words. When NO-GAPS + "is used with variable-length strings, trailing blanks remain + "removed. + CONDENSE char_j3 NO-GAPS. + + "RESPECTING BLANKS: Avoiding condensing + "A use case might be the assignment of strings with fixed- to + "variable-length strings. + DATA(char_j4) = ' abcef '. + DATA(char_j5) = ' ghij '. + DATA str_j TYPE string. + + "Result: ' abcef ghij ' + CONCATENATE char_j4 char_j5 INTO str_j RESPECTING BLANKS. + + "String function condense + "The advantage of using the string functions is + "that you can also specify random characters to be removed and + "not only blanks. + DATA(str_j1) = ` hi there `. + + "No parameters specified (i. e. their default values are provided); + "works like CONDENSE statements without the NO-GAPS addition + DATA(str_j2) = condense( str_j1 ). + + "Parameter 'from' specified with an initial string, 'del'/'to' not + "specified: Removes leading and trailing blanks. The 'from' + "parameter could also be specified with a text field literal: + "from = ' ' + DATA(str_j3) = condense( val = str_j1 from = `` ). + + "Parameter 'to' specified with an initial string, 'from'/'del' not + "specified: works like CONDENSE statements with the NO-GAPS + "addition + DATA(str_j4) = condense( val = str_j1 to = `` ). + + DATA(str_j5) = `ZZseeZZZyouZZ`. + DATA(str_j6) = condense( val = str_j5 del = `Z` ). + + "Parameters 'from', 'to' and 'del' are specified: Leading and + "trailing characters specified in 'del' are first removed. Then, + "in the remaining string, all substrings composed of characters + "specified in 'from' are replaced with the first character of the + "string specified in the 'to' parameter (in the example, it is a + "blank; the characters 'a', 'b', 'c' are not respected at all). + DATA(str_j7) = condense( val = str_j5 + del = `Z` + from = `Z` + to = ` abc` ). + + out->write( |CONDENSE statements:\n| ). + out->write( |\n| ). + out->write( data = char_j1 name = `char_j1` ). + out->write( |\n| ). + out->write( data = char_j2 name = `char_j2` ). + out->write( |\n| ). + out->write( data = char_j3 name = `char_j3` ). + out->write( |\n| ). + out->write( data = str_j name = `str_j` ). + out->write( |\n| ). + out->write( zcl_demo_abap_aux=>heading( `String function condense:` ) ). + out->write( data = str_j2 name = `str_j2` ). + out->write( |\n| ). + out->write( data = str_j3 name = `str_j3` ). + out->write( |\n| ). + out->write( data = str_j4 name = `str_j4` ). + out->write( |\n| ). + out->write( data = str_j6 name = `str_j6` ). + out->write( |\n| ). + out->write( data = str_j7 name = `str_j7` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `11) Reversing Strings` ) ). + + DATA(str_k) = reverse( `ollah` ). + + out->write( data = str_k name = `str_k` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `12) Inserting Substrings into Strings` ) ). + + DATA(str_l1) = `abcghi`. + + "Inserting into specific position + DATA(str_l2) = insert( val = str_l1 sub = `def` off = 3 ). + + "off is optional. If not specified (default value off = 0) + "the result is like concatenating a string with && + DATA(str_l3) = insert( val = str_l1 sub = `def` ). + + DATA(str_l4) = `def` && str_l1. + + out->write( data = str_l2 name = `str_l2` ). + out->write( |\n| ). + out->write( data = str_l3 name = `str_l3` ). + out->write( |\n| ). + out->write( data = str_l4 name = `str_l4` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `13) Overlaying Content` ) ). + + DATA(incl) = '==============================CP'. + DATA(cl_name) = 'CL_SOME_CLASS '. + + "Addition ONLY is not specified: All blanks are replaced + OVERLAY cl_name WITH incl. + + DATA(t1) = 'a.b.c.a.b.c.A'. + DATA(t2) = 'z.x.y.Z.x.y.z'. + + "Addition ONLY is specified: All characters that are specified after ONLY and that + "occur in the operand are replaced. Note that this is case-sensitive. + OVERLAY t1 WITH t2 ONLY 'ab'. + + out->write( data = cl_name name = `cl_name` ). + out->write( |\n| ). + out->write( data = t1 name = `t1` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `14) Processing Substrings` ) ). + + DATA(str_m1) = `Lorem ipsum dolor sit amet`. + + "Extracting substring starting at a specific position + "'len' not specified means the rest of the remaining characters are + "respected + DATA(str_m2) = substring( val = str_m1 off = 6 ). + + "Extracting substring with a specific length + "'off' is not specified and has the default value 0. + DATA(str_m3) = substring( val = str_m1 len = 5 ). + + "Specifying both off and len parameters + DATA(str_m4) = substring( val = str_m1 off = 6 len = 5 ). + + "Excursion: Getting last character of a string + DATA(str_m5) = substring( val = str_m1 + off = strlen( str_m1 ) - 1 + len = 1 ). "t + + "Offset and length specification using the + sign after a variable + "After +, it is the offset, length is specified within parentheses. + DATA(str_m6) = str_m1+0(5). + + "* means respecting the rest of the remaining string + DATA(str_m7) = str_m1+12(*). + + "Excursion: Write access on substrings in fixed-length strings + DATA(char_m1) = 'Lorem ipsum dolor sit amet'. + DATA(char_m2) = char_m1. + DATA(char_m3) = char_m1. + + "Deleting content + CLEAR char_m2+11(*). + "Modifying string + char_m3+0(5) = 'abcde'. + + "More string functions to access substrings + "Note that lots of parameters are possible (not all covered here). + DATA(str_m8) = `aa1bb2aa3bb4`. + + "Extracting a substring ... + "... after a specified substring + DATA(str_m9) = substring_after( val = str_m8 sub = `aa` ). + + "... after a specified substring specifying the occurence in a + "string and restricting the length + DATA(str_m10) = substring_after( val = str_m8 sub = `aa` + occ = 2 len = 4 ). + + "... before a specified substring + DATA(str_m11) = substring_before( val = str_m8 sub = `b2` ). + + "... from a specified substring on. It includes the substring + "specified in sub. len/off and other parameters are possible. + DATA(str_m12) = substring_from( val = str_m8 sub = `a3` ). + + "... up to a specified substring. It includes the substring + "specified in sub. len/off and other parameters are possible. + "aa1bb2aa3b + DATA(str_m13) = substring_to( val = str_m8 sub = `3b` ). + + out->write( data = str_m2 name = `str_m2` ). + out->write( |\n| ). + out->write( data = str_m3 name = `str_m3` ). + out->write( |\n| ). + out->write( data = str_m4 name = `str_m4` ). + out->write( |\n| ). + out->write( data = str_m5 name = `str_m5` ). + out->write( |\n| ). + out->write( data = str_m6 name = `str_m6` ). + out->write( |\n| ). + out->write( data = str_m7 name = `str_m7` ). + out->write( |\n| ). + out->write( data = char_m2 name = `char_m2` ). + out->write( |\n| ). + out->write( data = char_m3 name = `char_m3` ). + out->write( |\n| ). + out->write( data = str_m9 name = `str_m9` ). + out->write( |\n| ). + out->write( data = str_m10 name = `str_m10` ). + out->write( |\n| ). + out->write( data = str_m11 name = `str_m11` ). + out->write( |\n| ). + out->write( data = str_m12 name = `str_m12` ). + out->write( |\n| ). + out->write( data = str_m13 name = `str_m13` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Searching and Replacing in Strings` ) ). + out->write( |15) Searching Specific Characters in Strings Using Comparison Operators and String Functions\n| ). + + DATA(str_n1) = `cheers`. + + "CA (contains any) + "sy-fdpos contains the offset of the first found character. + "If nothing is found, sy-fdpos contains the length of the string. + "Note that position 0 stands for the very first position. + IF str_n1 CA `aeiou`. + out->write( |CA: str_n1 contains any of the characters. | && + |The position of the first found character is { sy-fdpos }.| ). + out->write( |\n| ). + ELSE. + out->write( |CA: str_n1 does not contain any of the characters. | && + |The length of str_n1 is { sy-fdpos }.| ). + out->write( |\n| ). + ENDIF. + + "NA (contains not any) + IF str_n1 NA `xyz`. + out->write( |NA: str_n1 does not contain any of the characters.| && + |The length of str_n1 is { sy-fdpos }.| ). + out->write( |\n| ). + ELSE. + out->write( |NA: str_n1 contains any of the characters. | && + |The position of the first found character is { sy-fdpos }.| ). + out->write( |\n| ). + ENDIF. + + "Determining if a string is exclusively composed of a certain + "character set + IF str_n1 CO `rs`. + out->write( |CO: str_n1 contains only the characters. | + && |The length of str_n1 is { sy-fdpos }.| ). + out->write( |\n| ). + ELSE. + out->write( |CO: str_n1 does not contain only the characters. | + && |Offset of the first character in str_n1 that is not | + && |contained in the second operand: { sy-fdpos }.| ). + out->write( |\n| ). + ENDIF. + + "Negation of CO + IF str_n1 CN `chers`. + out->write( |CN: str_n1 does not contain only the characters. | + && |Offset of the first character in str_n1 that is | + && |not contained in the second operand: { sy-fdpos }.| ). + out->write( |\n| ). + ELSE. + out->write( |CN: str_n1 contains only the characters. | + && |The length of str_n1 is { sy-fdpos }.| ). + out->write( |\n| ). + ENDIF. + + "String functions + DATA(str_n2) = `Pieces of cakes.`. + + "find_end returns the sum of the offset of the occurrence + DATA(res_n3) = find_end( val = str_n2 sub = `of` ). "9 + + "find_any_of returns the offset of the occurrence of any character contained in substring + "The search is always case-sensitive. + DATA(res_n4) = find_any_of( val = str_n2 sub = `x523z4e` ). + DATA(res_n5) = find_any_of( val = str_n2 sub = `zwq85t` ). + + "find_any_not_of: Negation of the one above + "The search is always case-sensitive. + DATA(res_n6) = find_any_not_of( val = str_n2 sub = `ieces` ). + DATA(res_n7) = find_any_not_of( val = str_n2 sub = `P` ). + + "count returns the number of all occurrences + DATA(res_n8) = count( val = str_n2 sub = `e` ). + DATA(res_n9) = count( val = str_n2 sub = `x` ). + + "count_any_of + DATA(res_n10) = count_any_of( val = str_n2 sub = `x523z4e` ). + DATA(res_n11) = count_any_of( val = str_n2 sub = `eco` ). + + "count_any_not_of + DATA(res_n12) = count_any_not_of( val = str_n2 sub = `fP` ). + DATA(res_n13) = count_any_not_of( val = str_n2 sub = `Piecs ofak.` ). + + out->write( data = res_n3 name = `res_n3` ). + out->write( |\n| ). + out->write( data = res_n4 name = `res_n4` ). + out->write( |\n| ). + out->write( data = res_n5 name = `res_n5` ). + out->write( |\n| ). + out->write( data = res_n6 name = `res_n6` ). + out->write( |\n| ). + out->write( data = res_n7 name = `res_n7` ). + out->write( |\n| ). + out->write( data = res_n8 name = `res_n8` ). + out->write( |\n| ). + out->write( data = res_n9 name = `res_n9` ). + out->write( |\n| ). + out->write( data = res_n10 name = `res_n10` ). + out->write( |\n| ). + out->write( data = res_n11 name = `res_n11` ). + out->write( |\n| ). + out->write( data = res_n12 name = `res_n12` ). + out->write( |\n| ). + out->write( data = res_n13 name = `res_n13` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `16) Replacing Specific Characters in Strings` ) ). + + DATA(str_o1) = `___abc_def_____ghi_`. + + "The replacement is done as follows: Each character specified in + "'from' is replaced by the character in 'to' that is on the same + "position, i. e. the second character in 'from' is replaced by the + "second character specified in 'to'. If there is no equivalent in + "'to', the character in 'from' is removed from the result. + + "abcdefgZZ + DATA(str_o2) = translate( val = str_o1 from = `hi_` to = `ZZ` ). + + "ZZZabcZdefZZZZZghiZ + DATA(str_o3) = translate( val = str_o1 from = `_` to = `ZZ` ). + + "TRANSLATE statement. The value after USING is interpreted as a + "string composed of character pairs. Starting with the first pair, + "a search is performed in text for the first character in every + "pair and each occurrence is replaced with the second character of + "the pair. + "...Zbc.def.....Yhi. + TRANSLATE str_o1 USING `_.aZgY`. + + out->write( data = str_o2 name = `str_o2` ). + out->write( |\n| ). + out->write( data = str_o3 name = `str_o3` ). + out->write( |\n| ). + out->write( data = str_o1 name = `str_o1` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Searching for Substrings in Strings` ) ). + out->write( |17) Substring Search: Simple Search Using Comparison Operators\n| ). + + DATA(str_p1) = `cheers`. + + "CS (contains string) + "sy-fdpos contains the offset of the found substring. + "If the substring is not found, sy-fdpos contains the length of the + "searched string. + IF str_p1 CS `rs`. + out->write( |CS: The string contains the substring. | + && |The offset is { sy-fdpos }.| ). + out->write( |\n| ). + ELSE. + out->write( |CS: The string does not contain the substring. | + && |The length of the string is { sy-fdpos }.| ). + out->write( |\n| ). + ENDIF. + + "NS (contains no string) + IF str_p1 NS `abc`. + out->write( |NS: The string does not contain the substring. | + && |The length of the string is { sy-fdpos }.| ). + out->write( |\n| ). + ELSE. + out->write( |NS: The string contains the substring. | + && |The offset is { sy-fdpos }.| ). + out->write( |\n| ). + ENDIF. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `18) Substring Search in Strings ` && + `Using FIND Statements` ) ). + "The code examples demonstrate different additions. + + DATA(str_qa) = `She sells seashells by the seashore.`. + + "Determining if a substring is found + "Simple find statement + FIND `se` IN str_qa. + + IF sy-subrc = 0. + out->write( `'se' found in the string` ). + ELSE. + out->write( `'se' not found in the string` ). + ENDIF. + + out->write( |\n| ). + + "Addition SUBSTRING is optional + FIND SUBSTRING `hi` IN str_qa. + + IF sy-subrc = 0. + out->write( `'hi' Found in the string` ). + ELSE. + out->write( `'hi' not found in the string` ). + ENDIF. + + out->write( |\n| ). + out->write( |\n| ). + + "The following examples use the additions MATCH COUNT and MATCH OFFSET + "to determine the number of occurrences and offset and for display purposes. + + "Addition FIRST OCCURRENCE OF: Explicit specification to search + "for the first occurrence + FIND FIRST OCCURRENCE OF `se` IN str_qa + MATCH COUNT DATA(cnt_q1) + MATCH OFFSET DATA(off_q2). + + "Omitting FIRST OCCURRENCE OF and ALL OCCURRENCES OF addition means + "searching for the first occurrence by default; same effect as the + "previous statement + FIND `se` IN str_qa + MATCH COUNT DATA(cnt_q3) + MATCH OFFSET DATA(off_q4). + + "Addition ALL OCCURRENCES: Searching for all occurrences + FIND ALL OCCURRENCES OF `se` IN str_qa + MATCH COUNT DATA(cnt_q5) + MATCH OFFSET DATA(off_q6). "value for the last occurrence + + "Addition IN SECTION ... OF: + "Searching in a specified section; both additions OFFSET and LENGTH + "are specified + FIND ALL OCCURRENCES OF `se` + IN SECTION OFFSET 9 LENGTH 5 OF str_qa + MATCH COUNT DATA(cnt_q7) + MATCH OFFSET DATA(off_q8). + + "Only LENGTH specified (OFFSET is 0 by default) + FIND ALL OCCURRENCES OF `se` + IN SECTION LENGTH 7 OF str_qa + MATCH COUNT DATA(cnt_q9) + MATCH OFFSET DATA(off_q10). + + "Only OFFSET specified (LENGTH: up to end of string) + FIND ALL OCCURRENCES OF `se` + IN SECTION OFFSET 7 OF str_qa + MATCH COUNT DATA(cnt_q11). + + "Another string to be searched + DATA(str_qb) = `abap ABAP abap`. + + "Further additional options for advanced evaluation options + + "Specifying the case-sensitivity of the search + "Not specifying the CASE addition means RESPECTING CASE is used by default. + "Here, it is explicitly specified. + FIND FIRST OCCURRENCE OF `A` IN str_qb + MATCH OFFSET DATA(off_q12) + RESPECTING CASE. + + "Making search case-insensitive + FIND FIRST OCCURRENCE OF `A` IN str_qb + MATCH OFFSET DATA(off_q13) + IGNORING CASE. + + "MATCH LENGTH addition + "The example uses a regular expression: Non-greedy search for + "a substring starting with lower case a up to an upper case P + FIND FIRST OCCURRENCE OF PCRE `a.*?P` IN str_qb + MATCH LENGTH DATA(len_q14) "9 + RESPECTING CASE. + + "RESULTS addition + "Example: Because of using ALL OCCURRENCES, the data object declared + "inline automatically has the type match_result_tab + FIND ALL OCCURRENCES OF `ab` IN str_qb + RESULTS DATA(res_q15) + IGNORING CASE. + + "Because of searching for the first occurrence, the data object declared + "inline automatically has the type match_result + FIND FIRST OCCURRENCE OF `ab` IN str_qb + RESULTS DATA(res_q16) + IGNORING CASE. + + out->write( data = cnt_q1 name = `cnt_q1` ). + out->write( |\n| ). + out->write( data = off_q2 name = `off_q2` ). + out->write( |\n| ). + out->write( data = cnt_q3 name = `cnt_q3` ). + out->write( |\n| ). + out->write( data = off_q4 name = `off_q4` ). + out->write( |\n| ). + out->write( data = cnt_q5 name = `cnt_q5` ). + out->write( |\n| ). + out->write( data = off_q6 name = `off_q6` ). + out->write( |\n| ). + out->write( data = cnt_q7 name = `cnt_q7` ). + out->write( |\n| ). + out->write( data = off_q8 name = `off_q8` ). + out->write( |\n| ). + out->write( data = cnt_q9 name = `cnt_q9` ). + out->write( |\n| ). + out->write( data = off_q10 name = `off_q10` ). + out->write( |\n| ). + out->write( data = cnt_q11 name = `cnt_q11` ). + out->write( |\n| ). + out->write( data = off_q12 name = `off_q12` ). + out->write( |\n| ). + out->write( data = off_q13 name = `off_q13` ). + out->write( |\n| ). + out->write( data = len_q14 name = `len_q14` ). + out->write( |\n| ). + out->write( data = res_q15 name = `res_q15` ). + out->write( |\n| ). + out->write( data = res_q16 name = `res_q16` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `19) Substring Search in Internal Tables ` && + `Using FIND ... IN TABLE Statements` ) ). + + DATA(str_table_r) = VALUE string_table( ( `aZbzZ` ) ( `cdZze` ) ( `Zzzf` ) ( `ghz` ) ). + + "Finding all occurrences in a table + "Note: res_tab is of type match_result_tab + "You can also restrict the search range in an internal table; see an example + "in the context of REPLACE ... IN TABLE + FIND ALL OCCURRENCES OF `Z` + IN TABLE str_table_r + RESULTS DATA(res_r1) + RESPECTING CASE. + + "Finding the first occurrence in a table + "Note: res_struc, which is declared inline here, is of type match_result + FIND FIRST OCCURRENCE OF `Z` + IN TABLE str_table_r + RESULTS DATA(res_r2) + RESPECTING CASE. + + "Alternative to the statement above (storing the information in individual data objects) + FIND FIRST OCCURRENCE OF `Z` + IN TABLE str_table_r + MATCH LINE DATA(line_r3) + MATCH OFFSET DATA(off_r4) + MATCH LENGTH DATA(len_r5) + RESPECTING CASE. + + out->write( data = res_r1 name = `res_r1` ). + out->write( |\n| ). + out->write( data = res_r2 name = `res_r2` ). + out->write( |\n| ). + out->write( data = line_r3 name = `line_r3` ). + out->write( |\n| ). + out->write( data = off_r4 name = `off_r4` ). + out->write( |\n| ). + out->write( data = len_r5 name = `len_r5` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `20) Substring Search in Strings ` && + `Using the String Function find` ) ). + + DATA(str_s) = `Pieces of cakes.`. + + "Searching for substring + "Returns offset of substring found + DATA(res_s1) = find( val = str_s sub = `ca` ). + + "Substring not found returns -1 + DATA(res_s2) = find( val = str_s sub = `xy` ). + + "Actual parameter of sub must not be initial when using the find function + TRY. + DATA(res_s3) = find( val = str_s sub = `` ). + CATCH cx_sy_strg_par_val. + "Nope! + ENDTRY. + + "The search is case-sensitive by default + DATA(res_s4) = find( val = str_s sub = `OF` ). + "Making search case-insensitive + DATA(res_s5) = find( val = str_s sub = `OF` case = abap_false ). + + "Specifying occ + DATA(res_s6) = find( val = str_s sub = `c` ). + DATA(res_s7) = find( val = str_s sub = `c` occ = 2 ). + DATA(res_s8) = find( val = str_s sub = `e` occ = -1 ). + DATA(res_s9) = find( val = str_s sub = `e` occ = -3 ). + + "Specifying off and len + "Specifying a subarea in which a string is searched + DATA(res_s10) = find( val = str_s sub = `e` off = 5 ). + DATA(res_s11) = find( val = str_s sub = `e` off = 5 len = 7 ). + DATA(res_s12) = find( val = str_s sub = `e` len = 2 ). + + out->write( data = res_s1 name = `res_s1` ). + out->write( |\n| ). + out->write( data = res_s2 name = `res_s2` ). + out->write( |\n| ). + out->write( data = res_s3 name = `res_s3` ). + out->write( |\n| ). + out->write( data = res_s4 name = `res_s4` ). + out->write( |\n| ). + out->write( data = res_s5 name = `res_s5` ). + out->write( |\n| ). + out->write( data = res_s6 name = `res_s6` ). + out->write( |\n| ). + out->write( data = res_s7 name = `res_s7` ). + out->write( |\n| ). + out->write( data = res_s8 name = `res_s8` ). + out->write( |\n| ). + out->write( data = res_s9 name = `res_s9` ). + out->write( |\n| ). + out->write( data = res_s10 name = `res_s10` ). + out->write( |\n| ). + out->write( data = res_s11 name = `res_s11` ). + out->write( |\n| ). + out->write( data = res_s12 name = `res_s12` ). + out->write( |\n| ). + + "Demonstrating a false range to be searched + TRY. + DATA(res_s13) = find( val = str_s sub = `e` off = 5 len = 15 ). + CATCH cx_sy_range_out_of_bounds. + out->write( `The exception cx_sy_range_out_of_bounds was raised.` ). + ENDTRY. + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `21) Replacing Substrings in Strings Using REPLACE Statments` ) ). + + DATA(str_t) = `abap ABAP abap`. + DATA(str_t1) = str_t. + + "Simple REPLACE statement + "Omitting the FIRST OCCURRENCE and ALL OCCURRENCES OF additions means + "replacing the first occurrence by default. + REPLACE `ab` IN str_t1 WITH `##`. + + out->write( data = str_t1 name = `str_t1` ). + out->write( |\n| ). + DATA(str_t2) = str_t. + + "Addition SUBSTRING is optional; same effect as the statement above + REPLACE SUBSTRING `ab` IN str_t2 WITH `##`. + + out->write( data = str_t2 name = `str_t2` ). + out->write( |\n| ). + DATA(str_t3) = str_t. + + "Addition FIRST OCCURRENCE OF: Explicit specification to replace the + "first occurrence; same effect as the statements above + REPLACE FIRST OCCURRENCE OF `ab` IN str_t3 WITH `##`. + + out->write( data = str_t3 name = `str_t3` ). + out->write( |\n| ). + DATA(str_t4) = str_t. + + "Addition ALL OCCURRENCES OF: All occurrences are replaced + "Note that the replacement is case-sensitive by default. + REPLACE ALL OCCURRENCES OF `ab` IN str_t4 WITH `##`. + + out->write( data = str_t4 name = `str_t4` ). + out->write( |\n| ). + DATA(str_t5) = str_t. + + "Further additional options for advanced evaluation options + + "IGNORING CASE addition: Making replacements case-insensitive + REPLACE ALL OCCURRENCES OF `ab` + IN str_t5 WITH `##` + IGNORING CASE. + + out->write( data = str_t5 name = `str_t5` ). + out->write( |\n| ). + DATA(str_t6) = str_t. + + "REPLACEMENT COUNT addition + REPLACE ALL OCCURRENCES OF `ab` + IN str_t6 WITH `##` + REPLACEMENT COUNT DATA(cnt_t7) + IGNORING CASE. + + out->write( data = str_t6 name = `str_t6` ). + out->write( |\n| ). + out->write( data = cnt_t7 name = `cnt_t7` ). + out->write( |\n| ). + DATA(str_t8) = str_t. + + "REPLACEMENT OFFSET and LENGTH additions + REPLACE FIRST OCCURRENCE OF `ap` + IN str_t8 WITH `##` + REPLACEMENT COUNT DATA(cnt_t9) "always 1 for replaced first occurrence + REPLACEMENT OFFSET DATA(off_t10) + REPLACEMENT LENGTH DATA(len_t11) + IGNORING CASE. + + out->write( data = str_t8 name = `str_t8` ). + out->write( |\n| ). + out->write( data = cnt_t9 name = `cnt_t9` ). + out->write( |\n| ). + out->write( data = off_t10 name = `off_t10` ). + out->write( |\n| ). + out->write( data = len_t11 name = `len_t11` ). + out->write( |\n| ). + DATA(str_t12) = str_t. + + "SECTION ... OF addition: Replacing within a specified area + REPLACE ALL OCCURRENCES OF `ap` + IN SECTION OFFSET 4 LENGTH 5 + OF str_t12 WITH `##` + REPLACEMENT COUNT DATA(cnt_t13) + REPLACEMENT OFFSET DATA(off_t14) + REPLACEMENT LENGTH DATA(len_t15) + IGNORING CASE. + + out->write( data = str_t12 name = `str_t12` ). + out->write( |\n| ). + out->write( data = cnt_t13 name = `cnt_t13` ). + out->write( |\n| ). + out->write( data = off_t14 name = `off_t14` ). + out->write( |\n| ). + out->write( data = len_t15 name = `len_t15` ). + out->write( |\n| ). + DATA(str_t16) = str_t. + + "RESULTS additions with ... + "... ALL OCCURRENCES OF + "Note: The data object, which is declared inline here, is of type repl_result_tab. + REPLACE ALL OCCURRENCES OF `ap` + IN str_t16 WITH `##` + RESULTS DATA(tab_t17) + IGNORING CASE. + + out->write( data = str_t16 name = `str_t16` ). + out->write( |\n| ). + out->write( data = tab_t17 name = `tab_t17` ). + out->write( |\n| ). + DATA(str_t18) = str_t. + + "... FIRST OCCURRENCE OF + "Note: The data object, which is declared inline here, is of type repl_result. + REPLACE FIRST OCCURRENCE OF `ap` + IN str_t18 WITH `##` + RESULTS DATA(struc_t19) + IGNORING CASE. + + out->write( data = str_t18 name = `str_t18` ). + out->write( |\n| ). + out->write( data = struc_t19 name = `struc_t19` ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `21) Position-Based Replacements with REPLACE SECTION ... OF` ) ). + + DATA(str_u) = `abap ABAP abap`. + DATA(str_u1) = str_u. + + "OFFSET and LENGTH specified + REPLACE SECTION OFFSET 5 LENGTH 4 OF str_u1 WITH `#`. + + out->write( data = str_u1 name = `str_u1` ). + out->write( |\n| ). + DATA(str_u2) = str_u. + + "Only OFFSET specified, LENGTH: up to the end of the string + REPLACE SECTION OFFSET 5 OF str_u2 WITH `#`. + + out->write( data = str_u2 name = `str_u2` ). + out->write( |\n| ). + DATA(str_u3) = str_u. + + "Only LENGTH specified, OFFSET: starting from the leftmost position + REPLACE SECTION LENGTH 6 OF str_u3 WITH `#`. + + out->write( data = str_u3 name = `str_u3` ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `22) Replacements in Internal Tables with REPLACE ... IN TABLE` ) ). + + DATA(tab_v) = VALUE string_table( ( `aZbzZ` ) ( `cdZze` ) ( `Zzzf` ) ( `ghz` ) ). + DATA(tab_v1) = tab_v. + + "Replacing all occurrences in a table + "RESULTS addition: Storing information in an internal table of type repl_result_tab + REPLACE ALL OCCURRENCES OF `Z` + IN TABLE tab_v1 + WITH `#` + RESULTS DATA(res_v2) + RESPECTING CASE. + + out->write( data = tab_v1 name = `tab_v1` ). + out->write( |\n| ). + out->write( data = res_v2 name = `res_v2` ). + out->write( |\n| ). + DATA(tab_v3) = tab_v. + + "Replacing the first occurrence in a table + "RESULTS addition: Storing information in a structure of type repl_result + REPLACE FIRST OCCURRENCE OF `Z` + IN TABLE tab_v3 + WITH `#` + RESULTS DATA(res_v4) + RESPECTING CASE. + + out->write( data = tab_v3 name = `tab_v3` ). + out->write( |\n| ). + out->write( data = res_v4 name = `res_v4` ). + out->write( |\n| ). + DATA(tab_v5) = tab_v. + + "Restricting the search range in an internal table + REPLACE ALL OCCURRENCES OF `Z` + IN TABLE tab_v5 + FROM 1 TO 2 + WITH `#` + RESPECTING CASE. + + out->write( data = tab_v5 name = `tab_v5` ). + out->write( |\n| ). + DATA(tab_v6) = tab_v. + + "Offsets can be optionally specified (also only the offset of start or end line possible) + REPLACE ALL OCCURRENCES OF `Z` + IN TABLE tab_v6 + FROM 1 OFFSET 3 TO 2 OFFSET 2 + WITH `#` + RESPECTING CASE. + + out->write( data = tab_v6 name = `tab_v6` ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `23) Replacing Substrings in Strings Using the String Function replace` ) ). + + DATA(str_w) = `abap ABAP abap`. + + "Note that here only the first occurrence is replaced. + DATA(str_w_1) = replace( val = str_w sub = `ap` with = `#` ). + + "Making the search case-insensitive + DATA(str_w_2) = replace( val = str_w sub = `AB` with = `#` case = abap_false ). + + "Setting occ + DATA(str_w_3) = replace( val = str_w sub = `ab` with = `#` occ = 2 case = abap_false ). + + "Replacing all occurrences: Setting occ to 0 + DATA(str_w_4) = replace( val = str_w sub = `ab` with = `#` occ = 0 case = abap_false ). + + "Negative value for occ: Occurrences are counted from the right + DATA(str_w_5) = replace( val = str_w sub = `ab` with = `#` occ = -1 ). + + "Setting off and len for determining a subarea for replacements + "Note: When using off/len, sub and occ cannot be specified. + "Specifying both off and len + DATA(str_w_6) = replace( val = str_w with = `#` off = 5 len = 3 ). + + "Specifying only off (len is 0 by default) + DATA(str_w_7) = replace( val = str_w with = `#` off = 2 ). + + "Note: When specifying only off and not specifying len or len = 0, + "replace works like insert + DATA(str_w_8) = insert( val = str_w sub = `#` off = 2 ). + + "Specifying only len (off is 0 by default): First segment of length in len is replaced + DATA(str_w_9) = replace( val = str_w with = `#` len = 3 ). + + "Special case + "- off: equal to the length of the string + "- len: not specified or 0 + "- Result: Value specified for 'with' is appended to the end of the string + DATA(str_w_10) = replace( val = str_w with = `#` off = strlen( str_w ) ). + + out->write( data = str_w_1 name = `str_w_1` ). + out->write( |\n| ). + out->write( data = str_w_2 name = `str_w_2` ). + out->write( |\n| ). + out->write( data = str_w_3 name = `str_w_3` ). + out->write( |\n| ). + out->write( data = str_w_4 name = `str_w_4` ). + out->write( |\n| ). + out->write( data = str_w_5 name = `str_w_5` ). + out->write( |\n| ). + out->write( data = str_w_6 name = `str_w_6` ). + out->write( |\n| ). + out->write( data = str_w_7 name = `str_w_7` ). + out->write( |\n| ). + out->write( data = str_w_8 name = `str_w_8` ). + out->write( |\n| ). + out->write( data = str_w_9 name = `str_w_9` ). + out->write( |\n| ). + out->write( data = str_w_10 name = `str_w_10` ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Pattern-Based Searching and Replacing in Strings` ) ). + out->write( `24) Simple Pattern-Based Searching ` && + `Using Logical Operators` ). + + DATA(str_x) = `abc_def_ghi`. + + "CP (conforms to pattern) + "*: Any character sequence (including blanks). + "+: Any character (only one character, including blanks). + "#: Escaping symbol. The following character is marked for an exact + "comparison. + "Note: Patterns are not case sensitive except for characters marked + "by #. If a pattern is found, the system variable sy-fdpos returns + "the offset of the first finding. Otherwise, it contains the length + "of the searched string. + IF str_x CP `*f#_*`. + out->write( |CP: The string covers the pattern. | + && |The offset is { sy-fdpos }.| ). + ELSE. + out->write( |CP: The string does not cover the pattern. | + && |The length of the string is { sy-fdpos }.| ). + ENDIF. + + out->write( |\n| ). + + "NP (does not conform to pattern) + IF str_x NP `i+`. + out->write( |NP: The string does not cover the pattern. | + && |The length of the string is { sy-fdpos }.| ). + ELSE. + out->write( |NP: The string covers the pattern. | + && |The offset is { sy-fdpos }.| ). + ENDIF. + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `25) Complex Searching Using ` && + `Regular Expressions` ) ). + + DATA(str_y) = `Cathy's black cat was fast asleep on the mat. ` && + `Later that day, the cat played with Matt.`. + + "Using string functions + + "Determining the position of the first finding + "here, parameter occ is 1 by default + DATA(off_y1) = find( val = str_y pcre = `at.` ). + + "Determining the number of findings + "Considers all 'a' characters not followed by 't', + "all 'at' plus 'att' + DATA(cnt_y2) = count( val = str_y pcre = `at*` ). + "Considers all 'at' plus 'att' and so on + DATA(cnt_y3) = count( val = str_y pcre = `at+` ). + + "String function match + "Extracting a substring matching a given pattern + DATA(str_y_email1) = `The email address is jon.doe@email.com.`. + DATA(str_y4) = match( val = str_y_email1 + pcre = `\w+(\.\w+)*@(\w+\.)+(\w{2,4})` ). + + "Predicate function matches + "Checking a string if it matches a given pattern + DATA(str_y_email2) = `jon.doe@email.com`. + + IF matches( val = str_y_email2 + pcre = `\w+(\.\w+)*@(\w+\.)+(\w{2,4})` ). + DATA(str_y5) = |{ str_y_email2 } is a valid email address.|. + ELSE. + str_y5 = |{ str_y_email2 } is not a valid email address.|. + ENDIF. + + "Example with a false email + DATA(str_y_email3) = `jon.doe@email.abcdef`. + + IF matches( val = str_y_email3 + pcre = `\w+(\.\w+)*@(\w+\.)+(\w{2,4})` ). + DATA(str_y6) = |{ str_y_email3 } is a valid email address.|. + ELSE. + str_y6 = |{ str_y_email3 } is not a valid email address.|. + ENDIF. + + "Examples with the FIND statement + "Storing submatches in variables. + "Pattern: anything before and after ' on ' + FIND PCRE `(.*)\son\s(.*)` IN str_y + SUBMATCHES DATA(subm_y7) DATA(subm_y8) + IGNORING CASE. + + "Determining the number of letters in a string + FIND ALL OCCURRENCES OF PCRE `[A-Za-z]` + IN str_y + MATCH COUNT DATA(cnt_y9). + + "Extracting all findings of a certain pattern in a string and + "storing them in an internal table + DATA tab_y10 TYPE string_table. + + "Pattern: An 'a' followed by any two characters + FIND ALL OCCURRENCES OF PCRE `a..` IN str_y + RESULTS DATA(res_y11). + + "The internal table includes the offset and length information of the individual findings. + "The substrings are extracted from the original string based on that information and + "added to an internal table of type string. + LOOP AT res_y11 ASSIGNING FIELD-SYMBOL(). + APPEND substring( val = str_y off = -offset len = -length ) TO tab_y10. + ENDLOOP. + + "Searching in an internal table and retrieving line, offset, length information + DATA(tab_y12) = VALUE string_table( ( `Cathy's black cat on the mat played with the friend of Matt.` ) + ( `Later that day, the cat played with Matt.` ) ). + + "Pattern: any character + 'y' followed by any character that is not a word character + "Only the first occurrence is searched. The search is specified as case-insensitive (which is not relevant here). + FIND FIRST OCCURRENCE OF PCRE `.y\W` IN TABLE tab_y12 + MATCH LINE DATA(line_y13) + MATCH OFFSET DATA(off_y14) + MATCH LENGTH DATA(len_y15) + IGNORING CASE. + + "Pattern: any character + 'y' followed by any character that is not a word character + "Here, all occurrences are searched and the result is stored in an internal table specified + "after the RESULTS addition. Since a group is included in the PCRE pattern denoted by the + "parentheses (\W), the resulting internal table includes entries in the 'submatches' + "component holding offset/length information for the particular match. + FIND ALL OCCURRENCES OF PCRE `.y(\W)` IN TABLE tab_y12 + RESULTS DATA(res_y16) + IGNORING CASE. + + "Extracting all findings of certain patterns in a string and + "storing them in an internal table; the capturing groups are + "also evaluated + DATA tab_y17 TYPE string_table. + DATA(str_y18) = `az.ay.bx.bw.cv.cu.dt.ds.ar.bq`. + + FIND ALL OCCURRENCES OF PCRE `(a.)|(b.)|(c.)` + IN str_y18 + RESULTS DATA(res_y19) + IGNORING CASE. + + LOOP AT res_y19 ASSIGNING FIELD-SYMBOL(). + LOOP AT -submatches ASSIGNING FIELD-SYMBOL(). + IF -offset <> -1. + APPEND |Substring "{ substring( val = str_y18 off = -offset len = -length ) }" found, capturing group { sy-tabix }| TO tab_y17. + ENDIF. + ENDLOOP. + ENDLOOP. + + out->write( data = off_y1 name = `off_y1` ). + out->write( |\n| ). + out->write( data = cnt_y2 name = `cnt_y2` ). + out->write( |\n| ). + out->write( data = cnt_y3 name = `cnt_y3` ). + out->write( |\n| ). + out->write( data = str_y4 name = `str_y4` ). + out->write( |\n| ). + out->write( data = str_y5 name = `str_y5` ). + out->write( |\n| ). + out->write( data = str_y6 name = `str_y6` ). + out->write( |\n| ). + out->write( data = subm_y7 name = `subm_y7` ). + out->write( |\n| ). + out->write( data = subm_y8 name = `subm_y8` ). + out->write( |\n| ). + out->write( data = cnt_y9 name = `cnt_y9` ). + out->write( |\n| ). + out->write( data = tab_y10 name = `tab_y10` ). + out->write( |\n| ). + out->write( data = line_y13 name = `line_y13` ). + out->write( |\n| ). + out->write( data = off_y14 name = `off_y14` ). + out->write( |\n| ). + out->write( data = len_y15 name = `len_y15` ). + out->write( |\n| ). + out->write( data = res_y16 name = `res_y16` ). + out->write( |\n| ). + out->write( data = tab_y17 name = `tab_y17` ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `26) Replacing Using Regular Expressions` ) ). + + DATA(str_z) = `Cathy's black cat was fast asleep on the mat. ` && + `Later that day, the cat played with Matt.`. + + "Considers all 'a' characters not followed by 't', + "all 'at' plus 'att' + "occ = 0 -> replaces all occurrences + DATA(str_z1) = replace( val = str_z + pcre = `at*` + with = `#` + occ = 0 ). + + "Considers all 'at' plus 'att' + DATA(str_z2) = replace( val = str_z + pcre = `at+` + with = `#` + occ = 0 ). + + "Replaces 2 'e' characters in a row + DATA(str_z3) = replace( val = str_z + pcre = `e{2}` + with = `#` + occ = 0 ). + + "Replaces 'ay'. Preceding d is optional ('day' is replaced too) + DATA(str_z4) = replace( val = str_z + pcre = `d?ay` + with = `#` + occ = 0 ). + + "Subgroup specified, replacement happens if 'at' is followed + "by 'h' or 't' + DATA(str_z5) = replace( val = str_z + pcre = `at(h|t)` + with = `#` + occ = 0 ). + + "Replaces 'at' when followed by any whitespace character + DATA(str_z6) = replace( val = str_z + pcre = `at\s` + with = `#` + occ = 0 ). + + "Replacement starts at beginning of string that is followed by 'c' + "Marked as not case sensitive + "Instead of ^, you could also use \A + DATA(str_z7) = replace( val = str_z + pcre = `^c` + with = `#` + case = abap_false ). + + "Replacement starts at end of string + "Instead of $, you could also use \Z + DATA(str_z8) = replace( val = str_z + pcre = `$` + with = ` Awesome!` ). + + "Replaces words starting with 'ma', ending with another character + DATA(str_z9) = replace( val = str_z + pcre = `\bma.\b` + with = `#` + case = abap_false + occ = 0 ). + + "Replaces the beginning of words with 'ma' followed by another + "character. + "Marked as not case sensitive, hence 'Mat' is considered, too. + DATA(str_z10) = replace( val = str_z + pcre = `\bma.` + with = `#` + case = abap_false + occ = 0 ). + + "Replaces a specified set of literals; if 'case = abap_false' + "is not specified, case sensitivity is respected + DATA(str_z11) = replace( val = str_z + pcre = `[eC'.,]` + with = `#` + occ = 0 ). + + "Replaces a specified value range + DATA(str_z12) = replace( val = str_z + pcre = `[a-eA-C0-9]` + with = `#` + occ = 0 ). + + "Replaces a specified value range. The example is the negation + "of the previous example. + DATA(str_z13) = replace( val = str_z + pcre = `[^a-eA-C0-9]` + with = `#` + occ = 0 ). + + DATA(str_zb) = `

    Date: 12/16/2022

    ` && + `

    Time: 10:30

    `. + + "Replacements considering subgroups + "Example switches the date format from US to European + "Sequences of digits are specified as subgroups followed by / + DATA(str_z15) = replace( val = str_zb + pcre = `(\d+)/(\d+)/` + with = `$2.$1.` ). + + "Regex pitfall: Watch greediness when using PCRE expressions + "Example: Replacing all HTML tags in a string + DATA(str_z16) = replace( val = str_zb + pcre = `<.*>` + with = `` + occ = 0 ). "Whole string replaced + + "The following pattern considers '<' not followed by '>' which is + "specified in a negated definition for a single character in the + "brackets. Then '*' greedily, matches anything until the next '>'. + DATA(str_z17) = replace( val = str_zb + pcre = `<[^>]*>` + with = `` + occ = 0 ). + + "Positive lookahead: Replaces colons followed by digits + DATA(str_z18) = replace( val = str_zb + pcre = `:(?=\d+)` + with = `.` + occ = 0 ). + + "Negative lookahead: Removes colons not followed by digits + ":(?!(\d+)) + DATA(str_z19) = replace( val = str_zb + pcre = `:(?!\d+)` + with = `` + occ = 0 ). + + "Positive Lookbehind: Replaces a digit preceded by a blank + DATA(str_z20) = replace( val = str_zb + pcre = `(?<=\s)\d` + with = `0` + occ = 0 ). + + "Negative lookbehind: Determines the position before closing p tags + "not preceded by 4 digits + DATA(str_z21) = replace( val = str_zb + pcre = `(?)` + with = `:00$1` + occ = 0 ). + + DATA(str_zc) = `ab apppc app`. + + "Greedy search + "The pattern matches anything before 'p'. The matching is carried + "out as often as possible. Hence, in this example the match + "stretches until the end of the string since 'p' is the final + "character, i. e. this 'p' and anything before is replaced). + DATA(str_z23) = replace( val = str_zc + pcre = `.*p` + with = `#` ). + + "Non-greedy search (denoted by '?' below) + "The pattern matches anything before 'p'. The matching proceeds + "until the first 'p' is found and does not go beyond (matching as + "few as possible). Hence, the first found 'p' including the content + "before is replaced. + DATA(str_z24) = replace( val = str_zc + pcre = `.*?p` + with = `#` ). + + "Replacements with subgroups + "Replaces 'PP' (case-insensitive) with '#', the content before and + "after 'PP' is switched + DATA(str_z25) = replace( val = str_zc + pcre = `(.*?)PP(.*)` + with = `$2#$1` + case = abap_false ). + + "REPLACE statement: Changing the source field directly + REPLACE PCRE `(.*?)PP(.*)` IN str_zc WITH `$2#$1` IGNORING CASE. + + "Replacements in internal tables + DATA(itab_z) = VALUE string_table( ( `Cathy's black cat was fast asleep on the mat.` ) + ( `Later that day, the cat played with Matt.` ) + ( `How about that?` ) ). + + "Replaces all 'th' occurrences in words beginning with 'th' + REPLACE ALL OCCURRENCES OF PCRE `\bth` + IN TABLE itab_z WITH `#` + REPLACEMENT COUNT DATA(cnt_z26) + IGNORING CASE . + + out->write( data = |Original str_z: { str_z }\n| ). + out->write( |\n| ). + out->write( data = str_z1 name = `str_z1` ). + out->write( |\n| ). + out->write( data = str_z2 name = `str_z2` ). + out->write( |\n| ). + out->write( data = str_z3 name = `str_z3` ). + out->write( |\n| ). + out->write( data = str_z4 name = `str_z4` ). + out->write( |\n| ). + out->write( data = str_z5 name = `str_z5` ). + out->write( |\n| ). + out->write( data = str_z6 name = `str_z6` ). + out->write( |\n| ). + out->write( data = str_z7 name = `str_z7` ). + out->write( |\n| ). + out->write( data = str_z8 name = `str_z8` ). + out->write( |\n| ). + out->write( data = str_z9 name = `str_z9` ). + out->write( |\n| ). + out->write( data = str_z10 name = `str_z10` ). + out->write( |\n| ). + out->write( data = str_z11 name = `str_z11` ). + out->write( |\n| ). + out->write( data = str_z12 name = `str_z12` ). + out->write( |\n| ). + out->write( data = str_z3 name = `str_z13` ). + out->write( |\n| ). + out->write( data = |Original str_zb: { str_zb }\n| ). + out->write( |\n| ). + out->write( data = str_z15 name = `str_z15` ). + out->write( |\n| ). + out->write( data = str_z16 name = `str_z16` ). + out->write( |\n| ). + out->write( data = str_z17 name = `str_z17` ). + out->write( |\n| ). + out->write( data = str_z18 name = `str_z18` ). + out->write( |\n| ). + out->write( data = str_z19 name = `str_z19` ). + out->write( |\n| ). + out->write( data = str_z20 name = `str_z20` ). + out->write( |\n| ). + out->write( data = str_z21 name = `str_z21` ). + out->write( |\n| ). + out->write( data = |Original str_zc: { str_zc }\n| ). + out->write( |\n| ). + out->write( data = str_z23 name = `str_z23` ). + out->write( |\n| ). + out->write( data = str_z24 name = `str_z24` ). + out->write( |\n| ). + out->write( data = str_z25 name = `str_z25` ). + out->write( |\n| ). + out->write( data = str_zc name = `str_zc` ). + out->write( |\n| ). + out->write( data = itab_z name = `itab_z` ). + out->write( |\n| ). + out->write( data = |Number of replacements in itab (cnt_z26): { cnt_z26 }| ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `27) Excursion: System Classes for Regular Expressions` ) ). + + "Searching for all occurrences + DATA(some_string) = `a1 # B2 ? cd . E3`. + + "Creating a regex instance for PCRE regular expressions + "In the example, regex_inst has the type ref to cl_abap_regex. + DATA(regex_inst) = cl_abap_regex=>create_pcre( pattern = `\D\d` "any-non digit followed by a digit + ignore_case = abap_true ). + + "Creating an instance of CL_ABAP_MATCHER using the method CREATE_MATCHER of the class CL_ABAP_REGEX + DATA(matcher_1) = regex_inst->create_matcher( text = some_string ). + + "Searching for all occurrences using the 'find_all' method + "In the example, result has the type match_result_tab containing the findings. + DATA(result1) = matcher_1->find_all( ). + + out->write( data = result1 name = `result1` ). + out->write( |\n| ). + + "You can also use method chaining to save lines of code + DATA(result2) = cl_abap_regex=>create_pcre( pattern = `\s\w` "any blank followed by any word character + ignore_case = abap_true )->create_matcher( text = some_string )->find_all( ). + + out->write( data = result2 name = `result2` ). + out->write( |\n| ). + + "Retrieving submatches using the 'get_submatch' method + DATA str_tab_reg_find TYPE string_table. + + DATA(matcher_2) = cl_abap_regex=>create_pcre( pattern = 'A(.*?)B(.*?)C(.*?)' + ignore_case = abap_false )->create_matcher( text = 'A.12az.B.34by.C.56cx.D.78dw.E' ). + + IF matcher_2->match( ). + DO. + TRY. + APPEND matcher_2->get_submatch( sy-index ) TO str_tab_reg_find. + CATCH cx_sy_invalid_submatch cx_sy_no_current_match. + EXIT. + ENDTRY. + ENDDO. + ENDIF. + + out->write( data = str_tab_reg_find name = `str_tab_reg_find` ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `More String Functions` ) ). + out->write( `29) Checking the Similarity of Strings` ). + out->write( |\n| ). + + DATA(str_to_check) = `abap`. + DATA(dist1) = distance( val1 = str_to_check val2 = `abap` ). + DATA(dist2) = distance( val1 = str_to_check val2 = `axbap` ). + DATA(dist3) = distance( val1 = str_to_check val2 = `yabyyapy` ). + DATA(dist4) = distance( val1 = str_to_check val2 = `zabapzzzzzzzzzzzz` max = 5 ). + + out->write( data = dist1 name = `dist1` ). + out->write( |\n| ). + out->write( data = dist2 name = `dist2` ). + out->write( |\n| ). + out->write( data = dist3 name = `dist3` ). + out->write( |\n| ). + out->write( data = dist4 name = `dist4` ). + out->write( |\n| ). + + "If the value of max is 0 or less, an exception is raised. + TRY. + DATA(dist5) = distance( val1 = str_to_check val2 = `#ab#ap#` max = 0 ). + CATCH cx_sy_strg_par_val INTO DATA(dist_err). + out->write( data = dist_err->get_text( ) name = `dist_err->get_text( )` ). + ENDTRY. + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `30) Repeating Strings` ) ). + + DATA(repeat1) = repeat( val = `abap` occ = 5 ). + DATA(repeat2) = |#{ repeat( val = ` ` occ = 10 ) }#|. + DATA(repeat3) = COND #( WHEN repeat( val = `a` occ = 0 ) = `` THEN `Y` ELSE `Z` ). + + out->write( data = repeat1 name = `repeat1` ). + out->write( |\n| ). + out->write( data = repeat2 name = `repeat2` ). + out->write( |\n| ). + out->write( data = repeat3 name = `repeat2` ). + out->write( |\n| ). + + "If occ has a negative value, an exception is raised. + TRY. + DATA(repeat4) = repeat( val = `X` occ = -3 ). + CATCH cx_sy_strg_par_val INTO DATA(rep_err). + out->write( data = rep_err->get_text( ) name = `rep_err->get_text( )` ). + ENDTRY. + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `31) Returning the Smallest/Biggest of a Set of Character-Like Arguments` ) ). + + DATA(min) = cmin( val1 = `zzzzzzz` + val2 = `zzazzzzzzzz` + val3 = `zzzzabc` ). + + DATA(max) = cmax( val1 = `abcdef` + val2 = `aaghij` + val3 = `aaaaklmn` + val4 = `aaaaaaopqrs` + val5 = `aaaaaaaaaatuvwxy` + val6 = `aaaaaaaaaaaaaz` ). + + out->write( data = min name = `min` ). + out->write( |\n| ). + out->write( data = max name = `max` ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `32) Escaping Special Characters` ) ). + + "Context: URLs + DATA(esc1) = escape( val = '...test: 5@8...' + format = cl_abap_format=>e_url_full ). + + "Context: JSON + DATA(esc2) = escape( val = 'some "test" json \ with backslash and double quotes' + format = cl_abap_format=>e_json_string ). + + "Context: String templates + DATA(esc3) = escape( val = 'Special characters in string templates: |, \, {, }' + format = cl_abap_format=>e_string_tpl ). + + out->write( data = esc1 name = `esc1` ). + out->write( |\n| ). + out->write( data = esc2 name = `esc2` ). + out->write( |\n| ). + out->write( data = esc3 name = `esc3` ). + out->write( |\n| ). + + "Invalid value for the format parameter + TRY. + DATA(esc4) = escape( val = 'This will raise an exception due to an invalid format value.' + format = 123 ). + CATCH cx_sy_strg_par_val INTO DATA(esc_err). + out->write( data = esc_err->get_text( ) name = `esc_err->get_text( )` ). + ENDTRY. + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `33) Excursion: String Processing Using the XCO Library` ) ). + + "--------- Extracting a substring from a string --------- + DATA(abc) = `abcdefghijklmnopqrstuvwxyz`. + + "Creating an encapsulation of a string using XCO + DATA(str) = xco_cp=>string( abc ). + + "Using the FROM and TO methods, you can determine + "the character position. Note that the value includes the + "character at the position specified. + "The character index pattern for the example string above + "is (the string has 26 characters in total): + "a = 1, b = 2, c = 3 ... z = 26 + "a = -26, b = -25, c = -24 ... z = -1 + "Providing a value that is out of bounds means that + "the first (or the last) character of the string is used + "by default. + "Note: When combining FROM and TO, e.g. with method + "chaining ...->from( ...)->to( ... ), note that another + "instance is created with the first 'from', and another + "character index pattern is created based on the new + "and adjusted string value. + + DATA(sub1) = str->from( 2 )->value. + DATA(sub2) = str->from( -23 )->value. + DATA(sub3) = str->from( -5 )->value. + DATA(sub4) = str->to( 5 )->value. + DATA(sub5) = str->to( -25 )->value. + DATA(sub6) = str->from( 2 )->to( 6 )->value. + DATA(sub7) = str->to( -10 )->from( 4 )->value. + "Values that are out of bounds. + DATA(sub8) = str->from( 0 )->to( 100 )->value. + + out->write( data = sub1 name = `sub1` ). + out->write( |\n| ). + out->write( data = sub2 name = `sub2` ). + out->write( |\n| ). + out->write( data = sub3 name = `sub3` ). + out->write( |\n| ). + out->write( data = sub4 name = `sub4` ). + out->write( |\n| ). + out->write( data = sub5 name = `sub5` ). + out->write( |\n| ). + out->write( data = sub6 name = `sub6` ). + out->write( |\n| ). + out->write( data = sub7 name = `sub7` ). + out->write( |\n| ). + out->write( data = sub8 name = `sub8` ). + out->write( |\n| ). + + "--------- Splitting and joining --------- + + "Splitting a string into a string table + DATA(str_table) = xco_cp=>string( `Hello.World.ABAP` )->split( `.` )->value. + + "Concatenating a string table into a string; specifying a delimiter + str_table = VALUE #( ( `a` ) ( `b` ) ( `c` ) ). + + DATA(conc_str1) = xco_cp=>strings( str_table )->join( `, ` )->value. + + "Concatenating a string table into a string; specifying a delimiter and + "reversing the table order + DATA(conc_str2) = xco_cp=>strings( str_table )->reverse( )->join( ` / ` )->value. + + out->write( data = str_table name = `str_table` ). + out->write( |\n| ). + out->write( data = conc_str1 name = `conc_str1` ). + out->write( |\n| ). + out->write( data = conc_str2 name = `conc_str2` ). + out->write( |\n| ). + + "--------- Prepending and appending strings --------- + DATA(name) = xco_cp=>string( `Max Mustermann` ). + DATA(address) = name->append( `, Some Street 1, 12345 Someplace` )->value. + DATA(title) = name->prepend( `Mr. ` )->value. + + out->write( data = address name = `address` ). + out->write( |\n| ). + out->write( data = title name = `title` ). + out->write( |\n| ). + + "--------- Transforming to lowercase and uppercase --------- + DATA(to_upper) = xco_cp=>string( `abap` )->to_upper_case( )->value. + DATA(to_lower) = xco_cp=>string( `HALLO WORLD` )->to_lower_case( )->value. + + out->write( data = to_upper name = `to_upper` ). + out->write( |\n| ). + out->write( data = to_lower name = `to_lower` ). + out->write( |\n| ). + + "--------- Checking if a string starts/ends with a specific string --------- + DATA check TYPE string. + DATA(str_check) = xco_cp=>string( `Max Mustermann` ). + + "yes + IF str_check->ends_with( `mann` ). + check = `yes`. + ELSE. + check = `no`. + ENDIF. + + out->write( data = check name = `check` ). + out->write( |\n| ). + + "no + IF str_check->starts_with( `John` ). + check = `yes`. + ELSE. + check = `no`. + ENDIF. + + out->write( data = check name = `check` ). + out->write( |\n| ). + + "--------- Converting strings to xstrings using a codepage --------- + DATA(xstr) = xco_cp=>string( `Some string` )->as_xstring( xco_cp_character=>code_page->utf_8 )->value. + + out->write( data = xstr name = `xstr` ). + out->write( |\n| ). + + "--------- Camel case compositions and decompositions with split and join operations --------- + "Pascal case is also possible + DATA(comp) = xco_cp=>string( `some_value` )->split( `_` )->compose( xco_cp_string=>composition->camel_case )->value. + DATA(decomp) = xco_cp=>string( `someValue` )->decompose( xco_cp_string=>decomposition->camel_case )->join( `_` )->value. + + out->write( data = comp name = `comp` ). + out->write( |\n| ). + out->write( data = decomp name = `decomp` ). + out->write( |\n| ). + + "--------- Matching string against regular expression --------- + DATA match TYPE string. + + "yes + IF xco_cp=>string( ` 1` )->matches( `\s\d` ). + match = 'yes'. + ELSE. + match = 'no'. + ENDIF. + + out->write( data = match name = `match` ). + out->write( |\n| ). + + "no + IF xco_cp=>string( ` X` )->matches( `\s\d` ). + match = 'yes'. + ELSE. + match = 'no'. + ENDIF. + + out->write( data = match name = `match` ). + ENDMETHOD. +ENDCLASS. \ No newline at end of file diff --git a/src/zcl_demo_abap_string_proc.clas.xml b/src/zcl_demo_abap_string_proc.clas.xml new file mode 100644 index 0000000..03dfe51 --- /dev/null +++ b/src/zcl_demo_abap_string_proc.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_STRING_PROC + E + ABAP cheat sheet: String processing + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_structures.clas.abap b/src/zcl_demo_abap_structures.clas.abap new file mode 100644 index 0000000..6a6ade6 --- /dev/null +++ b/src/zcl_demo_abap_structures.clas.abap @@ -0,0 +1,1180 @@ +*********************************************************************** +* +* ABAP cheat sheet: Structures +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntax options for working with +* structures. +* - Topics covered: creating structures and structured types, variants +* of structures, accessing components of structures, filling structures, +* clearing structures, structures in use in the context of tables +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP development tools for Eclipse (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, refer to the +* notes included in the class as comments or refer to the respective +* topic in the ABAP Keyword Documentation. +* - Due to the amount of console output, the examples contain numbers +* (e.g. 1) ..., 2) ..., 3) ...) for the individual example sections. +* Also, the variable name is displayed in most cases. So to find +* the relevant output in the console easier and faster, just search +* for the number/variable name in the console (CTRL+F in the console) +* or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

    ABAP cheat sheet: Structures

    +"! Example to demonstrate working with structures.
    Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_structures DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun. + + CLASS-METHODS: class_constructor. + + PROTECTED SECTION. + PRIVATE SECTION. + "Creating structured data types + TYPES: "Flat structure + BEGIN OF gty_struc, + num1 TYPE i, + num2 TYPE i, + char1 TYPE c LENGTH 10, + char2 TYPE c LENGTH 5, + pnum TYPE p LENGTH 8 DECIMALS 2, + END OF gty_struc, + "Structures within deep structure + BEGIN OF line1, + col1 TYPE i, + col2 TYPE i, + END OF line1, + BEGIN OF line2, + col2 TYPE i, + col3 TYPE i, + col4 TYPE i, + END OF line2. + + CLASS-DATA: + "Flat structure + gs_struc TYPE gty_struc, + "Deep structure 1 + BEGIN OF gs_deep1, + comp1 TYPE c LENGTH 1 VALUE 'W', + BEGIN OF substruc, + comp1 TYPE c LENGTH 1 VALUE 'X', + BEGIN OF comp2, + col1 TYPE c LENGTH 1 VALUE 'Y', + col2 TYPE c LENGTH 1 VALUE 'Z', + END OF comp2, + END OF substruc, + itab TYPE TABLE OF line1 WITH EMPTY KEY, + END OF gs_deep1, + "Deep structure 2 + BEGIN OF gs_deep2, + BEGIN OF substruc, + comp1 TYPE string, + comp2 TYPE string, + comp3 TYPE string, + END OF substruc, + itab TYPE TABLE OF line2 WITH EMPTY KEY, + comp4 TYPE i, + END OF gs_deep2, + "Creating internal table for displaying purposes + gt_tab TYPE STANDARD TABLE OF zdemo_abap_tab1 + WITH NON-UNIQUE KEY key_field. + + CLASS-METHODS: + initialize_dbtabs, + fill_deep_structures, + select_from_dbtab. + +ENDCLASS. + + + +CLASS zcl_demo_abap_structures IMPLEMENTATION. + + + METHOD class_constructor. + initialize_dbtabs( ). + fill_deep_structures( ). + "Filling demo database tables. + zcl_demo_abap_aux=>fill_dbtabs( ). + ENDMETHOD. + + + METHOD fill_deep_structures. + "Clearing all content of gs_deep2 + CLEAR gs_deep2. + "Filling nested tables in deep structures + gs_deep2-substruc = VALUE #( comp1 = `aaa` + comp2 = `bbb` + comp3 = `ccc` ). + + gs_deep1-itab = VALUE #( + ( col1 = 111 col2 = 222 ) + ( col1 = 333 col2 = 444 + ) ). + + gs_deep2-itab = VALUE #( + ( col2 = 1 col3 = 2 col4 = 3 ) + ( col2 = 4 col3 = 5 col4 = 6 ) + ( col2 = 7 col3 = 8 col4 = 9 ) + ). + + "Filling individual component that is not shared by both structures + gs_deep2-comp4 = 999. + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + out->write( |ABAP Cheat Sheet Example: Structures\n\n| ). + +********************************************************************** + + out->write( |1) Creating structures and structured types\n| ). + + "The following declarations are just included for demonstration purposes + "to show how declarations of local structures and structured + "types can look like. + + "Declaring structured type locally (flat structure) + TYPES: BEGIN OF lty_struc, + num1 TYPE i, + num2 TYPE i, + char1 TYPE c LENGTH 10, + char2 TYPE c LENGTH 5, + pnum TYPE p LENGTH 8 DECIMALS 2, + END OF lty_struc. + + "Alternatively, you could use the following syntax. + "However, a chained statement as above provides better readability. + TYPES BEGIN OF gs_struc_alt. + TYPES num1 TYPE i. + TYPES num2 TYPE i. + TYPES char1 TYPE c LENGTH 10. + TYPES char2 TYPE c LENGTH 5. + TYPES pnum TYPE p LENGTH 8 DECIMALS 2. + TYPES END OF gs_struc_alt. + + "Creating local structures + "a. Based on a local structured type. + DATA ls_struc TYPE lty_struc. + + "b. Based on global types in the DDIC + DATA ls_glo_tab TYPE zdemo_abap_flsch. "database table + + "c. Directly declaring a structure with DATA and specifying the components + DATA: BEGIN OF ls_direct_decl, + num1 TYPE i, + num2 TYPE i, + char1 TYPE c LENGTH 10, + char2 TYPE c LENGTH 5, + pnum TYPE p LENGTH 8 DECIMALS 2, + END OF ls_direct_decl. + + "d. Alternatively, you could use the following syntax. + "However, a chained statement as above provides better readability. + DATA BEGIN OF ls_direct_decl_alt. + DATA num1 TYPE i. + DATA num2 TYPE i. + DATA char1 TYPE c LENGTH 10. + DATA char2 TYPE c LENGTH 5. + DATA pnum TYPE p LENGTH 8 DECIMALS 2. + DATA END OF ls_direct_decl_alt. + + "e. Based on structure and internal table (type) + DATA ls_like_dobj LIKE ls_struc. + DATA ls_like_line_of_itab LIKE LINE OF gt_tab. + DATA ls_type_line_of_itab TYPE LINE OF string_table. + + "f. Using inline declaration. + "Type is inferred from the right-hand structure; the content is also assigned + DATA(struc_inl1) = ls_struc. + + "Declaring structure inline and populating it using the VALUE operator + DATA(struc_inl2) = VALUE lty_struc( num1 = 1 num2 = 2 ). + + out->write( `No output for this section. See the code.` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Variants of structures` ) ). + out->write( |2) Flat structure with default values\n\n| ). + + "Flat structures only contain elementary data types + + "Flat structure with default values + DATA: BEGIN OF ls_flat, + num1 TYPE i VALUE 1, + num2 TYPE i VALUE 2, + char1 TYPE c LENGTH 10 VALUE 'abcdefghij', + char2 TYPE c LENGTH 5 VALUE 'klmno', + pnum TYPE p LENGTH 8 DECIMALS 2 VALUE '123.45', + END OF ls_flat. + + out->write( data = ls_flat name = `ls_flat` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `3) Nested structure` ) ). + + "Nested structures contain at least one structure as component + + "Nested structure with default values + DATA: BEGIN OF ls_nested_address, + BEGIN OF name, + title TYPE string VALUE `Mr.`, + first_name TYPE string VALUE `Duncan`, + surname TYPE string VALUE `Pea`, + END OF name, + BEGIN OF street, + name TYPE string VALUE `Vegetable Lane`, + number TYPE string VALUE `11`, + END OF street, + BEGIN OF city, + zipcode TYPE string VALUE `349875`, + name TYPE string VALUE `Botanica`, + END OF city, + END OF ls_nested_address. + + out->write( data = ls_nested_address name = `ls_nested_address` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `4) Deep structure with strings` ) ). + + "Deep structures contain at least one deep component, for + "example, internal tables, strings. + + "Deep structure with strings and with default values. + DATA: BEGIN OF ls_flat_address, + name TYPE string VALUE `Mr. Duncan Pea`, + street TYPE string VALUE `Vegetable Lane 11`, + city TYPE string VALUE `349875 Botanica`, + END OF ls_flat_address. + + out->write( data = ls_flat_address name = `ls_flat_address` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `5) Deep structure with internal table as component` ) ). + + "Structured type for nested internal table + TYPES: BEGIN OF lty_flights, + connid TYPE zdemo_abap_flsch-connid, + countryfr TYPE zdemo_abap_flsch-countryfr, + cityfrom TYPE zdemo_abap_flsch-cityfrom, + airpfrom TYPE zdemo_abap_flsch-airpfrom, + countryto TYPE zdemo_abap_flsch-countryto, + cityto TYPE zdemo_abap_flsch-cityto, + airpto TYPE zdemo_abap_flsch-airpto, + END OF lty_flights. + + "Creating deep structure + DATA: BEGIN OF ls_flights, + carrier TYPE zdemo_abap_flsch-carrid VALUE 'LH', + carrier_name TYPE zdemo_abap_carr-carrname VALUE 'Lufthansa', + lt_flights TYPE TABLE OF lty_flights WITH EMPTY KEY, + END OF ls_flights. + + "Filling nested internal table for the output + SELECT * + FROM zdemo_abap_flsch + WHERE carrid = 'LH' + INTO CORRESPONDING FIELDS OF TABLE @ls_flights-lt_flights + UP TO 4 ROWS. + + out->write( data = ls_flights name = `ls_flights` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Accessing and populating structures` ) ). + out->write( |6) Populating structure components using the component selector\n\n| ). + + gs_struc-num1 = 1. + gs_struc-num2 = 2. + gs_struc-char1 = 'aaa'. + gs_struc-char2 = 'bbb'. + gs_struc-pnum = '333.33'. + + out->write( data = gs_struc name = `gs_struc` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `7) Populating structure components ` && + `using the VALUE operator` ) ). + + "Value assignments by addressing the structure components individually + "can be very bulky. Hence, the use of the VALUE operator is + "very handy for the value assignment, especially for filling structure + "components at operand position. In below examples the # sign is used + "before the parentheses which means that the type of the operand can be + "implicitly derived. + + "Flat structure + gs_struc = VALUE #( num1 = 3 + num2 = 4 + char1 = 'ccc' + char2 = 'ddd' + pnum = '555.55' ). + + "Nested structure + ls_nested_address = VALUE #( + name = VALUE #( title = `Mrs.` + first_name = `Jane` + surname = `Doe` ) + street = VALUE #( name = `Main Street` + number = 1 ) + city = VALUE #( zipcode = 12345 + name = `London` ) ). + + "Deep structure + ls_flights = VALUE #( + carrier = 'AA' + carrier_name = 'American Airlines' + lt_flights = VALUE #( ( connid = 17 countryfr = 'US' + cityfrom = 'New York' + airpfrom = 'JFK' + countryto = 'US' + cityto = 'San Francisco' + airpto = 'SFO' ) + ( connid = 64 + countryfr = 'US' + cityfrom = 'San Francisco' + airpfrom = 'SFO' + countryto = 'US' + cityto = 'New York' + airpto = 'JFK' ) ) ). + + out->write( data = gs_struc name = `gs_struc` ). + out->write( |\n| ). + out->write( data = ls_nested_address name = `ls_nested_address` ). + out->write( |\n| ). + out->write( data = ls_flights name = `ls_flights` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `8) Creating and populating a new structure ` && + `using the VALUE operator` ) ). + + "In the example below in which a new structure is created by declaring + "a variable inline the '#' sign cannot be used before the parentheses + "because a type cannot be derived. Instead, the type must be + "specified before the parentheses explicitly. + + DATA(ls_copy) = VALUE gty_struc( num1 = 5 + num2 = 6 + char1 = 'ggg' + char2 = 'hhh' + pnum = '555.55' ). + + out->write( data = ls_copy name = `ls_copy` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `9) Accessing individual components using the ` && + `component selector` ) ). + + "Assigning value of individual component to a variable + DATA(lv_copy) = gs_struc-num1. + + "Assigning a value to a component in a nested structure. + ls_nested_address-name-first_name = 'Emma'. + + "Assigning a value to a component in a deep structure. + "The table line is determined using a table expression. + ls_flights-lt_flights[ 1 ]-cityto = 'San Fran'. + + out->write( data = lv_copy name = `lv_copy` ). + out->write( |\n| ). + out->write( data = ls_nested_address-name-first_name name = `ls_nested_address-name-first_name` ). + out->write( |\n| ). + out->write( data = ls_flights-lt_flights[ 1 ]-cityto name = `ls_flights-lt_flights[ 1 ]-cityto` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `10) Excursion: Addressing components of a variable` && + ` referring to a structure ` ) ). + + "Creating a data reference variable. + DATA(ref) = NEW gty_struc( ). + + "Assigning a structure to the data reference + ref->* = gs_struc. + + "Accessing a component using the object component selector + DATA(ref_comp1) = ref->char1. + + "The following syntax is also possible but less comfortable. + DATA(ref_comp2) = ref->*-char2. + + out->write( data = ref_comp1 name = `ref_comp1` ). + out->write( |\n| ). + out->write( data = ref_comp2 name = `ref_comp2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `11) Using structure components for ` && + `data type and data object declarations` ) ). + + TYPES: lty_1 TYPE gty_struc-num1, + lty_2 LIKE gs_struc-num2. + + DATA: lv_num1 TYPE gty_struc-num1 VALUE 123, + lv_num2 LIKE gs_struc-num2 VALUE 456. + + out->write( data = lv_num1 name = `lv_num1` ). + out->write( |\n| ). + out->write( data = lv_num2 name = `lv_num2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `12) Copying content of a structure to another ` && + ` that has the same type using the assignment operator` ) ). + + "Note: In the case below, a MOVE-CORRESPONDING statement as shown + "further down would have the same effect: + "MOVE-CORRESPONDING gs_struc TO gs_struc_2. + + DATA gs_struc_2 TYPE gty_struc. + + gs_struc_2 = gs_struc. + + out->write( data = gs_struc_2 name = `gs_struc_2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `13) Copying content of a structure to another` && + ` that has an incompatible type using` && + ` MOVE-CORRESPONDING statemtns and the CORRESPONDING operator` ) ). + + "Both statements with MOVE-CORRESPONDING and the CORRESPONDING + "operator are used to assign identically named components of + "structures to each other. + "Note: For value assignments, generally bear in mind that there are + "special conversion and comparison rules that apply to assignments. + "The following examples focus on flat structures. + + "Creating flat structure with different type and assigning + "default values. + DATA: BEGIN OF gs_struc_diff, + num1 TYPE i VALUE 111, + num2 TYPE i VALUE 222, + char1 TYPE c LENGTH 10 VALUE 'AAA', + c1 TYPE c LENGTH 1 VALUE 'B', + END OF gs_struc_diff. + + "Copying structure to have the same values for another syntax variant. + DATA(gs_struc_diff2) = gs_struc_diff. + DATA(gs_struc_diff3) = gs_struc_diff. + DATA(gs_struc_diff4) = gs_struc_diff. + DATA(gs_struc_diff5) = gs_struc_diff. + + out->write( |Original content of structures:\n\n| ). + out->write( data = gs_struc name = `gs_struc` ). + out->write( |\n| ). + out->write( data = gs_struc_diff name = `gs_struc_diff` ). + out->write( |\n| ). + + "Identically named components are moved... + "... and the content in nonidentical components of the target + "structure are kept. + MOVE-CORRESPONDING gs_struc TO gs_struc_diff. + + "... and the content in nonidentical components in the target + "structure are initialized. + gs_struc_diff2 = CORRESPONDING #( gs_struc ). + + "... and the content in nonidentical components of the target + "structure are kept. Same as MOVE-CORRESPONDING without additions. + gs_struc_diff3 = CORRESPONDING #( BASE ( gs_struc_diff3 ) + gs_struc ). + + "MAPPING addition: Specifying components of a source structure that + "are assigned to the components of a target structure in mapping + "relationships. Note the conversion and assignement rules. + gs_struc_diff4 = CORRESPONDING #( BASE ( gs_struc_diff4 ) + gs_struc MAPPING c1 = char2 ). + + "EXCEPT addition: Excluding components from the assignment. + gs_struc_diff5 = CORRESPONDING #( BASE ( gs_struc_diff5 ) + gs_struc EXCEPT num2 ). + + out->write( |Results of statements:\n\n| ). + out->write( data = gs_struc_diff name = `gs_struc_diff` ). + out->write( |\n| ). + out->write( data = gs_struc_diff2 name = `gs_struc_diff2` ). + out->write( |\n| ). + out->write( data = gs_struc_diff3 name = `gs_struc_diff3` ). + out->write( |\n| ). + out->write( data = gs_struc_diff4 name = `gs_struc_diff4` ). + out->write( |\n| ). + out->write( data = gs_struc_diff5 name = `gs_struc_diff5` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `14) Copying content of a deep ` && + `structure to another` ) ). + out->write( |Original content of deep structures:\n\n| ). + + "Note: The example purposely uses non-fitting components + "to emphasize conversion and assignment rules. + + out->write( data = gs_deep1 name = `gs_deep1` ). + out->write( |\n| ). + out->write( data = gs_deep2 name = `gs_deep2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `15) MOVE-CORRESPONDING without additions` ) ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc is converted to string. Note that the two + " components in component substruc-comp2 of gs_deep1 are drawn + " together when being converted to string. + "- Content of gs_deep2-itab is replaced by table content of + " gs_deep1-itab. Value assignment, for example, + " for col2 in gs_deep2-itab: Despite the fact that there is no + " identically named component col1 in the target structure, + " values are assigned starting with the first column of the source + " structure. + + MOVE-CORRESPONDING gs_deep1 TO gs_deep2. + + out->write( data = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `16) MOVE-CORRESPONDING with the ` && + `EXPANDING NESTED TABLES addition` ) ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is replaced by table content of + " gs_deep1-itab. Due to the addition EXPANDING NESTED TABLES, the + " value assignment happens for identically named components. Hence, + " only col2 as the only shared and identically named component is + " filled. + + MOVE-CORRESPONDING gs_deep1 TO gs_deep2 EXPANDING NESTED TABLES. + + out->write( data = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `17) MOVE-CORRESPONDING with the` && + ` KEEPING TARGET LINES addition` ) ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is kept due to the addition KEEPING + " TARGET LINES and content of gs_deep1-itab is added. The value + " assignment concerning the added lines happens like the + " MOVE-CORRESPONDING statement without addition. That is, despite + " the fact that there is no identically named component col1 in + " the target structure, values are assigned starting with the + " first column of the source structure. + + MOVE-CORRESPONDING gs_deep1 TO gs_deep2 KEEPING TARGET LINES. + + out->write( data = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `18) MOVE-CORRESPONDING with the ` && + `EXPANDING NESTED TABLES KEEPING TARGET LINES addition` ) ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is kept due to the addition KEEPING + " TARGET LINES. Content of gs_deep1-itab is added. The value + " assignment concerning the added lines happens like the + " MOVE-CORRESPONDING statement with the addition EXPANDING NESTED + " TABLES. That is, the value assignment happens for identically + " named components. Hence, only col2 as the only shared and + " identically named component is filled. + + MOVE-CORRESPONDING gs_deep1 TO gs_deep2 + EXPANDING NESTED TABLES KEEPING TARGET LINES. + + out->write( data = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `19) CORRESPONDING operator without additions` ) ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " initialized. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is replaced by table content of + " gs_deep1-itab. Note the value assignment, for example, for col2 + " in gs_deep2-itab. Despite the fact that there is no identically + " named component comp1 in the target structure, values are + " assigned starting with the first column of the source structure. + + gs_deep2 = CORRESPONDING #( gs_deep1 ). + + out->write( data = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `20) CORRESPONDING operator with the` && + ` DEEP addition` ) ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " initialized. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is replaced by table content of + " gs_deep1-itab. Due to the addition DEEP, the value assignment + " happens for identically named components in the nested table. + " Hence, only col2 as the only shared and identically named + " component is filled. + + gs_deep2 = CORRESPONDING #( DEEP gs_deep1 ). + + out->write( data = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `21) CORRESPONDING operator with the` && + ` BASE addition` ) ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is replaced by table content of + " gs_deep1-itab. The value assignment in the nested table happens + " like using the CORRESPONDING operator without addition. Note the + " value assignment, for example, for col2 in gs_deep2-itab. + " Despite the fact that there is no identically named component + " col1 in the target structure, values are assigned starting with + " the first column of the source structure. + + gs_deep2 = CORRESPONDING #( BASE ( gs_deep2 ) gs_deep1 ). + + out->write( data = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `22) CORRESPONDING operator with the ` && + `DEEP BASE addition` ) ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is replaced by table content of + " gs_deep1-itab. The value assignment in the nested table happens + " like using the CORRESPONDING operator with the addition DEEP. + " That is, the value assignment happens for identically named + " components in the nested table. Hence, only col2 as the only + " shared and identically named component is filled. + + gs_deep2 = CORRESPONDING #( DEEP BASE ( gs_deep2 ) gs_deep1 ). + + out->write( data = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `23) CORRESPONDING operator with the ` && + `APPENDING addition` ) ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is kept and content of gs_deep1-itab is + " added. The value assignment concerning the added lines happens + " like using the CORRESPONDING operator without addition. Note the + " value assignment, for example, for col2 in gs_deep2- itab. + " Despite the fact that there is no identically named component + " col1 in the target structure, values are assigned starting with + " the first column of the source structure. + + gs_deep2 = CORRESPONDING #( APPENDING ( gs_deep2 ) gs_deep1 ). + + out->write( data = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `24) CORRESPONDING operator with the ` && + `DEEP APPENDING addition` ) ). + + "Notes on the result: + "- Existing content of identically named components is replaced. + "- Content in nonidentical components of the target structure is + " kept. + "- Substructure substruc: Same as above + "- Content of gs_deep2-itab is kept and content of gs_deep1-itab is + " added. The value assignment concerning the added lines happens + " like using the CORRESPONDING operator with the addition DEEP. + " That is, the value assignment happens for identically named + " components in the nested table. Hence, only col2 as the only + " shared and identically named component is filled. + "- It has the same effect as using DEEP APPENDING BASE. + + gs_deep2 = CORRESPONDING #( DEEP APPENDING ( gs_deep2 ) + gs_deep1 ). + + out->write( data = gs_deep2 name = `gs_deep2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `25) Clearing individual components of a ` && + `structure and the complete structure` ) ). + + "Clearing individual component + CLEAR gs_struc-char1. + + out->write( data = gs_struc name = `gs_struc` ). + out->write( |\n| ). + + "Clearing the whole structure + CLEAR gs_struc. + + out->write( data = gs_struc name = `gs_struc` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Processing structures` ) ). + out->write( |Reading a row from a database table into a structure ...\n\n| ). + out->write( |26) ... that has a compatible type\n\n| ). + + "The first entry that is found according to the WHERE condition is + "returned. Instead of creating a structure having a compatible type, + "the structure can be declared inline. + + DATA ls_flsch1 TYPE zdemo_abap_flsch. + + SELECT SINGLE FROM zdemo_abap_flsch + FIELDS * + WHERE carrid = 'LH' AND connid = '0400' + INTO @ls_flsch1. + + SELECT SINGLE FROM zdemo_abap_flsch + FIELDS * + WHERE carrid = 'LH' AND connid = '0400' + INTO @DATA(ls_flsch2). + + out->write( data = ls_flsch1 name = `ls_flsch1` ). + out->write( |\n| ). + out->write( data = ls_flsch2 name = `ls_flsch2` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `27) ... that has a different type` ) ). + + "Creating structure having a different type. + DATA: BEGIN OF ls_fli_diff, + carrid TYPE zdemo_abap_flsch-carrid, + connid TYPE zdemo_abap_flsch-connid, + countryfr TYPE zdemo_abap_flsch-countryfr, + cityfrom TYPE zdemo_abap_flsch-cityfrom, + countryto TYPE zdemo_abap_flsch-countryto, + cityto TYPE zdemo_abap_flsch-cityto, + fldate TYPE zdemo_abap_fli-fldate, + END OF ls_fli_diff. + + SELECT SINGLE FROM zdemo_abap_flsch + FIELDS * + WHERE carrid = 'JL' AND connid = '0408' + INTO CORRESPONDING FIELDS OF @ls_fli_diff. + + out->write( data = ls_fli_diff name = `ls_fli_diff` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Reading a line from an internal table into a structure ...` ) ). + out->write( |28) ... using a SELECT statement\n\n| ). + + "Creating and filling an internal table to be read from + DATA itab TYPE TABLE OF zdemo_abap_flsch WITH EMPTY KEY. + SELECT FROM zdemo_abap_flsch + FIELDS * + WHERE carrid = 'LH' ORDER BY PRIMARY KEY + INTO TABLE @itab + UP TO 4 ROWS. + + "Reading from an internal table + SELECT SINGLE FROM @itab AS itab + FIELDS * + WHERE carrid = 'LH' + INTO @DATA(ls_select_itab). + + out->write( data = ls_select_itab name = `ls_select_itab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `29) ... using a READ TABLE statement` ) ). + + "The example shows the reading of one line into a work area, field + "symbol and a data reference variable, all representing structured + "data objects and declared inline below. Here, the reading of a + "line is based on the line number by specifying INDEX. + + "Copying line into a work area + READ TABLE itab INTO DATA(ls_read_table) INDEX 1. + + "Assignment to a field symbol + READ TABLE itab ASSIGNING FIELD-SYMBOL() INDEX 2. + + "Reading into a data reference variable + READ TABLE itab REFERENCE INTO DATA(dref) INDEX 3. + + out->write( data = ls_read_table name = `ls_read_table` ). + out->write( |\n| ). + out->write( data = name = `` ). + out->write( |\n| ). + out->write( data = dref->* name = `dref->*` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `30) ... using a table expression` ) ). + "The line number, that is, the index, is specified in square + "brackets. + + DATA(ls_table_exp) = itab[ 3 ]. + + out->write( data = ls_table_exp name = `ls_table_exp` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `Sequentially reading ...` ) ). + out->write( |31) ... a row from a database table into a structure\n\n| ). + + "In the given simple example, the line that is found and returned + "in a structure, that is declared inline, is simply added to an + "internal table. + + SELECT FROM zdemo_abap_flsch + FIELDS * + WHERE carrid = 'AZ' + INTO @DATA(ls_sel_loop). + IF sy-subrc = 0. + APPEND ls_sel_loop TO itab. + ENDIF. + ENDSELECT. + + out->write( data = itab name = `itab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `32) ... a line from an internal table into a structure` ) ). + + "The given example covers the reading of a line into a field symbol. + "Within the loop, a modification is carried out on a component + "of the structures. + + LOOP AT itab ASSIGNING FIELD-SYMBOL() WHERE carrid <> 'LH'. + -carrid = 'XY'. + ENDLOOP. + + out->write( data = itab name = `itab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `33) Inserting a single row ` && + `into a database table from a structure` ) ). + + "The statements in the given example can be considered as + "alternatives. The third statement demonstrates that the structure + "might also be created and filled in place instead of inserting a + "line from an existing structure. + + DATA ls_struc_db TYPE zdemo_abap_tab1. + + ls_struc_db = VALUE #( key_field = 1 + char1 = 'aaa' + char2 = 'bbb' + num1 = 2 + num2 = 3 ). + + INSERT INTO zdemo_abap_tab1 VALUES @ls_struc_db. + + "Structure filled anew with new primary key to + "avoid duplicate key error. + ls_struc_db = VALUE #( key_field = 2 + char1 = 'ccc' + char2 = 'ddd' + num1 = 4 + num2 = 5 ). + + INSERT zdemo_abap_tab1 FROM @ls_struc_db. + + INSERT zdemo_abap_tab1 FROM @( VALUE #( key_field = 3 + char1 = 'eee' + char2 = 'fff' + num1 = 6 + num2 = 7 ) ). + + select_from_dbtab( ). + out->write( data = gt_tab name = `gt_tab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `34) Updating a single row ` && + `in a database table from a structure` ) ). + + ls_struc_db = VALUE #( key_field = 2 + char1 = 'GGG' + char2 = 'HHH' + num1 = 8 + num2 = 9 ). + + UPDATE zdemo_abap_tab1 FROM @ls_struc_db. + + UPDATE zdemo_abap_tab1 FROM @( VALUE #( key_field = 3 + char1 = 'III' + char2 = 'JJJ' + num1 = 10 + num2 = 11 ) ). + + select_from_dbtab( ). + out->write( data = gt_tab name = `gt_tab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `35) Updating a single row ` && + `in a database table from a structure without overwriting specific ` && + `components` ) ). + + "If you want to update a database table row from a structure by + "specifying components to be changed without overwriting other + "components, you might choose the following way. First, read the + "intended line from the database table into a structure. + "Then, use the VALUE operator with the addition BASE and specify + "the components to be changed. + + SELECT SINGLE * + FROM zdemo_abap_tab1 + WHERE key_field = 2 + INTO @DATA(wa). + + UPDATE zdemo_abap_tab1 FROM @( VALUE #( BASE wa char2 = '###' ) ). + + select_from_dbtab( ). + out->write( data = gt_tab name = `gt_tab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `36) Updating or creating a single` && + ` row in a database table from a structure using MODIFY` ) ). + + "You can update or create an individual row in a database table + "from a structure using ABAP SQL statements with MODIFY. If a + "line in the database table already exists having the same keys as + "specified in the structure, the line gets updated. If a line does + "not exist with the keys specified in the structure, a new line is + "created in the database table. In the given example, the first + "statement demonstrates a modification of an existing line in the + "database table.The second and third statements create a new line + "in the database table. The third statement demonstrates that the + "structure might also be created and filled in place instead of + "inserting a line based on an existing structure. + + ls_struc_db = VALUE #( key_field = 1 + char1 = 'kkk' + char2 = 'lll' + num1 = 12 + num2 = 13 ). + + MODIFY zdemo_abap_tab1 FROM @ls_struc_db. + + ls_struc_db = VALUE #( key_field = 4 + char1 = 'mmm' + char2 = 'nnn' + num1 = 14 + num2 = 15 ). + + MODIFY zdemo_abap_tab1 FROM @ls_struc_db. + + MODIFY zdemo_abap_tab1 FROM @( VALUE #( key_field = 5 + char1 = 'ooo' + char2 = 'ppp' + num1 = 16 + num2 = 17 ) ). + + select_from_dbtab( ). + out->write( data = gt_tab name = `gt_tab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `36) Adding rows to and updating single rows` && + ` in an internal table from a structure` ) ). + + "INSERT and MODIFY are ABAP statements in this context, not ABAP SQL + "statements. Both INSERT and APPEND add one line (or more) to an + "internal table. While APPEND adds at the bottom of the internal + "table, INSERT can be used to add lines at a specific position in + "tables. MODIFY changes the content of an internal table entry. + + ls_struc_db = VALUE #( key_field = 6 + char1 = 'ZZZ' + char2 = 'YYY' + num1 = 18 + num2 = 19 ). + + INSERT ls_struc_db INTO TABLE gt_tab. + + INSERT VALUE #( key_field = 7 + char1 = 'XXX' + char2 = 'WWW' + num1 = 20 + num2 = 21 ) INTO TABLE gt_tab. + + ls_struc_db = VALUE #( key_field = 8 + char1 = 'VVV' + char2 = 'UUU' + num1 = 22 + num2 = 23 ). + + APPEND ls_struc_db TO gt_tab. + + APPEND VALUE #( key_field = 9 + char1 = 'TTT' + char2 = 'SSS' + num1 = 24 + num2 = 25 ) TO gt_tab. + + ls_struc_db = VALUE #( key_field = 1 + char1 = 'RRR' + char2 = 'QQQ' + num1 = 26 + num2 = 27 ). + + MODIFY TABLE gt_tab FROM ls_struc_db. + + MODIFY TABLE gt_tab FROM VALUE #( key_field = 2 + char1 = 'PPP' + char2 = 'OOO' + num1 = 28 + num2 = 29 ). + + out->write( data = gt_tab name = `gt_tab` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `37) Including structures` ) ). + + "The example shows the inclusion of structured types and data + "objects in another structure. First, three structured types as + "well as a structured data object based on one of those types are + "created. Then, the types and the structure are included in the + "structured type address_type. With the optional addition AS and + "the specification of a name, the included components can be + "addressed by this common name as if the components are actually + "components of a substructure. With the optional addition + "RENAMING WITH SUFFIX and the specification of a name, the included + "components get a suffix name to avoid naming conflicts with other + "components. + + TYPES: BEGIN OF name_type, + title TYPE string, + prename TYPE string, + surname TYPE string, + END OF name_type, + BEGIN OF street_type, + name TYPE string, + number TYPE string, + END OF street_type, + BEGIN OF city_type, + zipcode TYPE string, + name TYPE string, + END OF city_type. + + DATA: city_struc TYPE city_type. + + TYPES BEGIN OF address_type. + INCLUDE TYPE name_type AS name. + INCLUDE TYPE street_type AS street RENAMING WITH SUFFIX _street. + INCLUDE STRUCTURE city_struc AS city RENAMING WITH SUFFIX _city. + TYPES END OF address_type. + + DATA: name TYPE name_type, + address TYPE address_type. + + name-title = `Mr.`. + name-prename = `Duncan`. + name-surname = `Pea`. + address-name = name. + address-street-name = `Vegetable Lane`. + address-street-number = `11`. + address-zipcode_city = `349875`. + address-name_city = `Botanica`. + + out->write( data = address name = `address` ). + ENDMETHOD. + + + METHOD initialize_dbtabs. + DELETE FROM zdemo_abap_tab1. + ENDMETHOD. + + + METHOD select_from_dbtab. + + SELECT FROM zdemo_abap_tab1 + FIELDS * + WHERE key_field <> 0 + ORDER BY key_field + INTO TABLE @gt_tab. + + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_structures.clas.xml b/src/zcl_demo_abap_structures.clas.xml new file mode 100644 index 0000000..4e720ff --- /dev/null +++ b/src/zcl_demo_abap_structures.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_STRUCTURES + E + ABAP cheat sheet: Structures + 1 + X + X + X + + + + diff --git a/src/zcl_demo_abap_unit_test.clas.abap b/src/zcl_demo_abap_unit_test.clas.abap new file mode 100644 index 0000000..945e959 --- /dev/null +++ b/src/zcl_demo_abap_unit_test.clas.abap @@ -0,0 +1,678 @@ +*********************************************************************** +* +* ABAP Cheat Sheet: ABAP Unit Tests +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate ABAP unit tests. +* - Topics covered: Test classes and test methods, special methods, +* implementing and injecting test doubles (constructor injection, +* back door injection, test seams) +* +* ----------------------- RUN ABAP UNIT TEST--------------------------- +* - Open the class with the ABAP development tools for Eclipse (ADT). +* - Choose Ctrl/Cmd + Shift + F10 to launch all tests in the class. +* You can also right-click somewhere in the class and choose +* Run as -> ABAP Unit Test. +* - The results of a test run are displayed in the ABAP Unit tab in ADT +* and can be evaluated. The Failure Trace section provides information +* on errors found. +* - If you are interested in test coverage, you can choose +* Ctrl/Cmd + Shift + F11, or make a right-click, choose Run as -> +* ABAP Unit Test With..., select the Coverage checkbox and choose +* Execute. You can then check the results in the ABAP Coverage tab, +* what code is tested and what not. +* +* ----------------------- RUN CLASS ----------------------------- +* - Open the class with the ABAP development tools for Eclipse (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 intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

    ABAP cheat sheet: Unit tests

    +"! Example to demonstrate ABAP unit tests.
    Choose F9 in ADT to run the class. +"! To run all unit tests of the class, choose Ctrl/Cmd + Shift + F10. +CLASS zcl_demo_abap_unit_test DEFINITION +PUBLIC +CREATE PUBLIC. + + PUBLIC SECTION. + + INTERFACES: if_oo_adt_classrun. + + CLASS-METHODS: class_constructor. + + "Optional parameter for the instance constructor for the purpose of + "constructor injection + METHODS constructor + IMPORTING iref_data_prov TYPE REF TO zdemo_abap_get_data_itf OPTIONAL. + + PROTECTED SECTION. + + TYPES carr_tab TYPE TABLE OF zdemo_abap_fli WITH EMPTY KEY. + + METHODS: select_flight_data IMPORTING carrier TYPE zdemo_abap_fli-carrid + RETURNING VALUE(flight_data) TYPE carr_tab. + + PRIVATE SECTION. + + TYPES: int_tab_so TYPE SORTED TABLE OF i WITH UNIQUE KEY table_line, + int_tab_st TYPE STANDARD TABLE OF i WITH EMPTY KEY, + occ_rate TYPE p LENGTH 4 DECIMALS 2, + + BEGIN OF nums_struc, + num1 TYPE i, + num2 TYPE i, + END OF nums_struc, + + nums_tab TYPE TABLE OF nums_struc WITH EMPTY KEY. + + DATA: seats_table TYPE zdemo_abap_get_data_itf=>carr_tab, + flight_tab TYPE TABLE OF zdemo_abap_fli, + + "Reference variable for back door injection + "Note: In the example, it is a local interface declared in the + "Local Types tab (the CCIMP include). To make the type known to + "the global class, see the Class-relevant Local Types tab (the CCDEF include). + data_provider_local_itf TYPE REF TO lif_get_data, + + "Reference variable for constructor injection + "In the example, the type refers to a global interface. + data_provider_global_itf TYPE REF TO zdemo_abap_get_data_itf, + + "For demonstrating setter injection + data_provider_setter_inj TYPE REF TO zdemo_abap_get_data_itf, + + "For demonstrating parameter injection + data_provider_param_inj TYPE REF TO zdemo_abap_get_data_itf.. + + METHODS: + "Calculates the sum of two numbers + "This method demonstrates the use of the setup and teardown methods in the test class. + get_sum IMPORTING key TYPE zdemo_abap_tab1-key_field + char TYPE zdemo_abap_tab1-char1 + RETURNING VALUE(sum) TYPE i, + + "Calculates the common divisors and the greatest common divisor of two numbers + get_common_div_and_gcd IMPORTING a TYPE i + b TYPE i + EXPORTING common_divisors TYPE int_tab_so + gcd TYPE i, + + "Calculates the digit sum of a number + get_digit_sum IMPORTING num TYPE i + RETURNING VALUE(digit_sum) TYPE i, + + "Multiple methods that all do the same (they calculate the occupancy rate of flights) + "but serve different demonstration purposes for the ABAP unit tests in the example. + "The method implementations involve a depended-on component (DOC). In this case, + "it is a database access. + "The methods are intentionally implemented in a similar way. Therefore, almost + "all of the following methods will display the same output in the console when the + "class is executed using F9. + + "Method to demonstrate test double injection using inheritance and method redefinition + get_occ_rate_using_meth IMPORTING carrier_id TYPE zdemo_abap_fli-carrid + RETURNING VALUE(occupancy_rate) TYPE occ_rate, + + "Method to demonstrate test double injection using test seams + get_occ_rate_test_seam IMPORTING carrier_id TYPE zdemo_abap_fli-carrid + EXPORTING occupancy_rate TYPE occ_rate + num1 TYPE i + num2 TYPE i, + + "Method to demonstrate test double injection using back door injection and a local interface + get_occ_rate_local_itf IMPORTING carrier_id TYPE zdemo_abap_fli-carrid + RETURNING VALUE(occupancy_rate) TYPE occ_rate, + + "Method to demonstrate test double injection using constructor injection and a global interface + get_occ_rate_global_itf IMPORTING carrier_id TYPE zdemo_abap_fli-carrid + RETURNING VALUE(occupancy_rate) TYPE occ_rate, + + "Method to demonstrate test double injection using setter injection and a global interface + get_occ_rate_setter_inj IMPORTING carrier_id TYPE zdemo_abap_fli-carrid + RETURNING VALUE(occupancy_rate) TYPE occ_rate, + + "Method for setter injection + setter_meth IMPORTING data_prov TYPE REF TO zdemo_abap_get_data_itf, + + "Method to demonstrate test double injection using parameter injection and a global interface + "An optional parameter is specified for passing the test double if the method is tested. + get_occ_rate_param_inj IMPORTING carrier_id TYPE zdemo_abap_fli-carrid + data_prov TYPE REF TO zdemo_abap_get_data_itf OPTIONAL + RETURNING VALUE(occupancy_rate) TYPE occ_rate. + +ENDCLASS. + + + +CLASS zcl_demo_abap_unit_test IMPLEMENTATION. + + + METHOD class_constructor. + "Filling demo database tables. + zcl_demo_abap_aux=>fill_dbtabs( ). + + "Preparing a demo database table for this example (get_sum method) + DELETE FROM zdemo_abap_tab1. + INSERT zdemo_abap_tab1 FROM @( + VALUE #( key_field = 1 char1 = 'aaa' char2 = 'bbb' num1 = 25 num2 = 75 ) ). + ENDMETHOD. + + + METHOD constructor. + + "For demonstrating the back door injection + data_provider_local_itf = NEW lcl_data_prov_local_itf( ). + + "For demonstrating the constructor injection + IF iref_data_prov IS BOUND. + "Note: The parameter is only bound when you run the unit test. + "When you run the unit test and you debug, you will see that iref_data_prov + "has a type reference to LTD_TEST_DATA_GLOBAL_INTF. + + data_provider_global_itf = iref_data_prov. + + ELSE. + + data_provider_global_itf = NEW lcl_data_prov_glo_itf( ). + + ENDIF. + + "Object creation for the method call in the get_occ_rate_setter_inj method + data_provider_setter_inj = NEW lcl_data_prov_glo_itf( ). + + ENDMETHOD. + + + METHOD get_common_div_and_gcd. + "Calculates the common divisors and the greatest common divisor of two numbers + + CLEAR: common_divisors, gcd. + + CHECK a >= 1. + CHECK b >= 1. + + IF a >= b. + DATA(greater_num) = a. + DATA(lower_num) = b. + ELSE. + greater_num = b. + lower_num = a. + ENDIF. + + "Getting common divisors + DATA(div) = 1. + + WHILE div <= lower_num. + IF lower_num MOD div = 0. + DATA(divisor) = lower_num / div. + INSERT divisor INTO TABLE common_divisors. + ENDIF. + + div += 1. + ENDWHILE. + + LOOP AT common_divisors ASSIGNING FIELD-SYMBOL(). + + IF greater_num MOD <> 0. + DELETE common_divisors WHERE table_line = . + ENDIF. + + ENDLOOP. + + "Extracting the greatest common divisor from the list of common divisors + gcd = common_divisors[ lines( common_divisors ) ]. + + ENDMETHOD. + + + METHOD get_digit_sum. + "Calculates the digit sum of a number + + CLEAR digit_sum. + + CHECK num >= 0. + + DATA(converted_int) = CONV string( num ). + DATA(len) = strlen( converted_int ). + + DO len TIMES. + DATA(idx) = sy-index - 1. + digit_sum = digit_sum + converted_int+idx(1). + ENDDO. + + ENDMETHOD. + + + METHOD get_occ_rate_global_itf. + "Method to demonstrate test double injection using constructor injection + "and a global interface + + DATA total_seatsmax_global_itf TYPE i. + DATA total_seatsocc_global_itf TYPE i. + + "Assumption: The original code in this method was as follows (the line commented out). + "It was identified as DOC (reading data from a database table). + + "DATA(flight_data) = select_flight_data( carrier = carrier_id ). + + "Instead of a method call like above and for a proper unit testing, a global interface + "is provided. + "In the example, an interface method is implemented in a local class in the local types + "tab (CCIMP include): lcl_data_prov_glo_itf + + "When the class is executed using F9, the object used here refers to type lcl_data_prov_glo_itf. + "When the unit test is executed, the object used here refers to type ltd_test_data_global_intf, + "i.e. the local test double is injected. + DATA(flight_data) = data_provider_global_itf->select_flight_data( carrier = carrier_id ). + + LOOP AT flight_data ASSIGNING FIELD-SYMBOL(). + + total_seatsmax_global_itf = total_seatsmax_global_itf + -seatsmax. + total_seatsocc_global_itf = total_seatsocc_global_itf + -seatsocc. + + ENDLOOP. + + occupancy_rate = total_seatsocc_global_itf / total_seatsmax_global_itf * 100. + + ENDMETHOD. + + + METHOD get_occ_rate_local_itf. + "Method to demonstrate test double injection using back door + "injection and a local interface + + DATA total_seatsmax_local_itf TYPE i. + DATA total_seatsocc_local_itf TYPE i. + + "Assumption: The original code in this method was as follows (the line commented out). + "It was identified as DOC (reading data from a database table). + + "DATA(flight_data) = select_flight_data( carrier = carrier_id ). + + "Instead of a method call like above and for a proper unit testing - a global interface + "is not available - a local interface is created, and + "an interface method is implemented. In this example, the local interface is + "created in the local types tab (CCIMP include): lif_get_data + "A local class (lcl_data_prov_local_itf) is also created in the CCIMP include. It + "implements the local interface. + + "When the class is executed using F9, the object used here refers to type lcl_data_prov_local_itf. + "When the unit test is executed, the object used here refers to type ltd_test_data_local_itf, + "i.e. the local test double is injected. + DATA(flight_data) = data_provider_local_itf->select_flight_data( carrier = carrier_id ). + + LOOP AT flight_data ASSIGNING FIELD-SYMBOL(). + + total_seatsmax_local_itf = total_seatsmax_local_itf + -seatsmax. + total_seatsocc_local_itf = total_seatsocc_local_itf + -seatsocc. + + ENDLOOP. + + occupancy_rate = total_seatsocc_local_itf / total_seatsmax_local_itf * 100. + + ENDMETHOD. + + + METHOD get_occ_rate_param_inj. + "This method demonstrates test double injection using parameter injection. + + DATA total_seatsmax_param_inj TYPE i. + DATA total_seatsocc_param_inj TYPE i. + + "Assumption: The original code in this method was as follows (the line commented out). + "It was identified as DOC (reading data from a database table). + + "DATA(flight_data) = select_flight_data( carrier = carrier_id ). + + "Instead of a method call like above and for a proper unit testing, a global interface + "is provided. + "In the example, an interface method is implemented in a local class in the local types + "tab (CCIMP include): lcl_data_prov_glo_itf + + "The method has an optional importing parameter. When the unit test is executed, + "the parameter is bound. An object of the test double class is passed in that case. + "Otherwise, when the class is executed using F9, an object of the actual data provider + "is created. + IF data_prov IS BOUND. + data_provider_param_inj = data_prov. + ELSE. + data_provider_param_inj = NEW lcl_data_prov_glo_itf( ). + ENDIF. + + DATA(flight_data) = data_provider_param_inj->select_flight_data( carrier = carrier_id ). + + LOOP AT flight_data ASSIGNING FIELD-SYMBOL(). + + total_seatsmax_param_inj = total_seatsmax_param_inj + -seatsmax. + total_seatsocc_param_inj = total_seatsocc_param_inj + -seatsocc. + + ENDLOOP. + + occupancy_rate = total_seatsocc_param_inj / total_seatsmax_param_inj * 100. + + ENDMETHOD. + + + METHOD get_occ_rate_setter_inj. + "This method demonstrates test double injection using setting injection. + "See the setter_meth method. + + DATA total_seatsmax_setter_inj TYPE i. + DATA total_seatsocc_setter_inj TYPE i. + + "Assumption: The original code in this method was as follows (the line commented out). + "It was identified as DOC (reading data from a database table). + + "DATA(flight_data) = select_flight_data( carrier = carrier_id ). + + "Instead of a method call like above and for a proper unit testing, a global interface + "is provided. + "In the example, an interface method is implemented in a local class in the local types + "tab (CCIMP include): lcl_data_prov_glo_itf + + "See the comment in the setter_meth method + DATA(flight_data) = data_provider_setter_inj->select_flight_data( carrier = carrier_id ). + + LOOP AT flight_data ASSIGNING FIELD-SYMBOL(). + + total_seatsmax_setter_inj = total_seatsmax_setter_inj + -seatsmax. + total_seatsocc_setter_inj = total_seatsocc_setter_inj + -seatsocc. + + ENDLOOP. + + occupancy_rate = total_seatsocc_setter_inj / total_seatsmax_setter_inj * 100. + + ENDMETHOD. + + + METHOD get_occ_rate_test_seam. + "Method to demonstrate test double injection using test seams + "Note: The code is just for demonstration purposes. Of course, the result can be + "achieved more elegantly using SQL expressions, for example. + + TEST-SEAM select_flights. + "DOC + SELECT seatsmax, seatsocc + FROM zdemo_abap_fli + WHERE carrid = @carrier_id + INTO CORRESPONDING FIELDS OF TABLE @seats_table. + END-TEST-SEAM. + + DATA total_seatsmax_tm TYPE i. + DATA total_seatsocc_tm TYPE i. + + LOOP AT seats_table ASSIGNING FIELD-SYMBOL(). + + total_seatsmax_tm = total_seatsmax_tm + -seatsmax. + total_seatsocc_tm = total_seatsocc_tm + -seatsocc. + + ENDLOOP. + + occupancy_rate = total_seatsocc_tm / total_seatsmax_tm * 100. + + "Further examples for test seams + DATA(var) = 0. + + "Empty test seam; code is injected during unit test + "Check the output when running the class using F9 and + "the test results when running the unit test. + TEST-SEAM num1. + END-TEST-SEAM. + + IF var = 0. + num1 = 1. + ELSE. + num1 = 999. + ENDIF. + + num2 = 0. + + "Empty injection + "See the test class: The code that is included in the test + "seam should be excluded from the test. Therefore, the + "test injection block in the test class is empty. + "Check the output when running the class using F9 and + "the test results when running the unit test. + TEST-SEAM num2. + num2 = 123. + END-TEST-SEAM. + + ENDMETHOD. + + + METHOD get_occ_rate_using_meth. + "This method demonstrates test double injection using inheritance and method redefinition. + + DATA total_seatsmax_no TYPE i. + DATA total_seatsocc_no TYPE i. + + "During the unit test, the redefined method in the test class is called. + DATA(flight_data) = select_flight_data( carrier = carrier_id ). + + LOOP AT flight_data ASSIGNING FIELD-SYMBOL(). + + total_seatsmax_no = total_seatsmax_no + -seatsmax. + total_seatsocc_no = total_seatsocc_no + -seatsocc. + + ENDLOOP. + + occupancy_rate = total_seatsocc_no / total_seatsmax_no * 100. + + ENDMETHOD. + + + METHOD get_sum. + "The method selects a record from a database table and sums the values + "of two fields, both are of type i. + + SELECT SINGLE + FROM zdemo_abap_tab1 + FIELDS num1 + num2 AS sum + WHERE key_field = @key + AND char1 = @char + INTO @sum. + + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + "Note: The example includes a couple of implementations for the methods + "declared above. And by choosing F9 in ADT, you can run the class and check the + "output in the console. + "However, the focus of the example is unit tests. Therefore, check the + "test classes and methods in the test include (Test Classes tab in ADT). + "Choose Ctrl/Cmd + Shift + F10 to launch all tests in the class and check the + "test results in the ABAP Unit tab in ADT. + + out->write( |ABAP Cheat Sheet Example: ABAP Unit Tests\n\n| ). + + out->write( `************************************************************************` ). + out->write( `---> Choose Ctrl/Cmd + Shift + F10 to launch all tests in the class <---` ). + out->write( |************************************************************************\n| ). + + out->write( |1) get_sum Method\n\n| ). + "This method demonstrates the use of the setup and teardown methods in the test class. + + DATA(sum) = get_sum( key = 1 char = 'aaa' ). + + out->write( data = sum name = `sum` ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `2) get_common_div_and_gcd Method` ) ). + + "Filling an internal table with numbers on whose bases the common divisors and the + "greatest common divisor are to be calculated + DATA(tab) = VALUE nums_tab( ( num1 = 10 num2 = 20 ) + ( num1 = 100 num2 = 200 ) + ( num1 = 12 num2 = 6 ) + ( num1 = 5 num2 = 1 ) + ( num1 = 50 num2 = 50 ) + ( num1 = 4 num2 = 8 ) ). + + LOOP AT tab ASSIGNING FIELD-SYMBOL(). + DATA(tabix) = sy-tabix. + get_common_div_and_gcd( EXPORTING a = -num1 + b = -num2 + IMPORTING common_divisors = DATA(common_divs) gcd = DATA(gcd) ). + + out->write( |Common divisors of { -num1 } and { -num2 }\n| ). + out->write( |\n| ). + out->write( data = common_divs name = `common_divs` ). + out->write( |\n| ). + out->write( |Greatest common divisor of { -num1 } and { -num2 }: { gcd } | ). + out->write( |\n| ). + IF tabix < lines( tab ). + out->write( `--------------------------` ). + out->write( |\n| ). + ENDIF. + ENDLOOP. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `3) get_digit_sum Method` ) ). + + "Filling an internal table with numbers on whose bases the digit sum is to be calculated + DATA(tab_i) = VALUE int_tab_so( ( 12 ) + ( 123 ) + ( 3 ) + ( 8246 ) + ( 1001001 ) + ( 0 ) ). + + LOOP AT tab_i ASSIGNING FIELD-SYMBOL(). + DATA(digit_sum) = get_digit_sum( ). + + out->write( |The digit sum of { } is { digit_sum }.| ). + out->write( |\n| ). + ENDLOOP. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `4) get_occ_rate_using_meth Method` ) ). + "In the test class, this method demonstrates test double injection + "using inheritance and method redefinition. + + "Filling an internal table with carrier ids on whose bases the occupancy + "rate is to be calculated. + DATA(tab_str) = VALUE zdemo_abap_get_data_itf=>carr_tab( ( carrid = 'LH' ) + ( carrid = 'AA' ) + ( carrid = 'DL' ) ). + + LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). + DATA(occupancy_rate) = get_occ_rate_using_meth( -carrid ). + + out->write( |The occupancy rate for airline { -carrid } is { occupancy_rate }%.| ). + out->write( |\n| ). + ENDLOOP. + out->write( zcl_demo_abap_aux=>heading( `5) get_occ_rate_test_seam Method` ) ). + "This method demonstrates test double injection using test seams. + + LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). + get_occ_rate_test_seam( EXPORTING carrier_id = -carrid + IMPORTING occupancy_rate = DATA(occupancy_rate_test_seam) + num1 = DATA(num1) + num2 = DATA(num2) ). + + out->write( |The occupancy rate for airline { -carrid } is { occupancy_rate_test_seam }%.| ). + out->write( |\n| ). + out->write( |num1: { num1 }| ). + out->write( |\n| ). + out->write( |num2: { num2 }| ). + out->write( |\n| ). + ENDLOOP. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `6) get_occ_rate_local_itf Method` ) ). + "This method demonstrates test double injection using back door + "injection and a local interface. + + LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). + DATA(occupancy_rate_local_itf) = get_occ_rate_local_itf( -carrid ). + + out->write( |The occupancy rate for airline { -carrid } is { occupancy_rate_local_itf }%.| ). + out->write( |\n| ). + ENDLOOP. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `7) get_occ_rate_global_itf Method` ) ). + "This method demonstrates test double injection using constructor + "injection and a global interface. + + LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). + DATA(occupancy_rate_global_itf) = get_occ_rate_global_itf( -carrid ). + + out->write( |The occupancy rate for airline { -carrid } is { occupancy_rate_global_itf }%.| ). + out->write( |\n| ). + ENDLOOP. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `8) get_occ_rate_setter_inj Method` ) ). + "This method demonstrates test double injection using setter injection and a global interface. + + LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). + DATA(occupancy_rate_setter_inj) = get_occ_rate_setter_inj( -carrid ). + + out->write( |The occupancy rate for airline { -carrid } is { occupancy_rate_setter_inj }%.| ). + out->write( |\n| ). + ENDLOOP. + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `9) get_occ_rate_param_inj Method` ) ). + "This method demonstrates test double injection using parameter injection and a global interface. + + LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). + DATA(occupancy_rate_param_inj) = get_occ_rate_param_inj( carrier_id = -carrid ). + + out->write( |The occupancy rate for airline { -carrid } is { occupancy_rate_param_inj }%.| ). + out->write( |\n| ). + ENDLOOP. + ENDMETHOD. + + METHOD select_flight_data. + "Method that is identified as DOC in the method implementations above. + "This method is also used for demonstrating test double injection and method redefinition. + + SELECT seatsmax, seatsocc + FROM zdemo_abap_fli + WHERE carrid = @carrier + INTO CORRESPONDING FIELDS OF TABLE @flight_data. + ENDMETHOD. + + + METHOD setter_meth. + "Method to demonstrate the test double injection using setter injection + + "When the unit test is executed, an object of the test double class is passed as + "a parameter. Then, the object used here refers to type ltd_test_data_setter_inj, + "i.e. the local test double is injected. + data_provider_setter_inj = data_prov. + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_unit_test.clas.locals_def.abap b/src/zcl_demo_abap_unit_test.clas.locals_def.abap new file mode 100644 index 0000000..de73f08 --- /dev/null +++ b/src/zcl_demo_abap_unit_test.clas.locals_def.abap @@ -0,0 +1,4 @@ +*"* use this source file for any type of declarations (class +*"* definitions, interfaces or type declarations) you need for +*"* components in the private section +INTERFACE lif_get_data DEFERRED. diff --git a/src/zcl_demo_abap_unit_test.clas.locals_imp.abap b/src/zcl_demo_abap_unit_test.clas.locals_imp.abap new file mode 100644 index 0000000..f9e8bf2 --- /dev/null +++ b/src/zcl_demo_abap_unit_test.clas.locals_imp.abap @@ -0,0 +1,73 @@ +****************************************************** +* Local interface +****************************************************** +INTERFACE lif_get_data. + + TYPES: carr_tab TYPE TABLE OF zdemo_abap_fli WITH EMPTY KEY, + occ_rate TYPE p LENGTH 4 DECIMALS 2. + + METHODS: + select_flight_data IMPORTING carrier TYPE zdemo_abap_fli-carrid + RETURNING VALUE(flight_data) TYPE carr_tab, + + "This method is included to demonstrate the PARTIALLY IMPLEMENTED + "addition in the test class when implementing the test double + say_hello RETURNING VALUE(hi) TYPE string. + +ENDINTERFACE. + +****************************************************** +* Local class +* The class implements the local interface. +****************************************************** +CLASS lcl_data_prov_local_itf DEFINITION. + PUBLIC SECTION. + INTERFACES lif_get_data. +ENDCLASS. + +CLASS lcl_data_prov_local_itf IMPLEMENTATION. + + METHOD lif_get_data~select_flight_data. + + SELECT seatsmax, seatsocc + FROM zdemo_abap_fli + WHERE carrid = @carrier + INTO CORRESPONDING FIELDS OF TABLE @flight_data. + + ENDMETHOD. + + METHOD lif_get_data~say_hello. + hi = `Hello, ` && sy-uname && `.`. + ENDMETHOD. + +ENDCLASS. + +****************************************************** +* Local class that implements a global interface +* It serves the purpose of a data provider. It can be +* imagined as a global class, and a method that is +* implemented there and called by the class under test +* is identified as DOC. A local class is used in the +* example to keep the number of separate artifacts small. +****************************************************** +CLASS lcl_data_prov_glo_itf DEFINITION. + PUBLIC SECTION. + INTERFACES zdemo_abap_get_data_itf. +ENDCLASS. + +CLASS lcl_data_prov_glo_itf IMPLEMENTATION. + + METHOD zdemo_abap_get_data_itf~select_flight_data. + + SELECT seatsmax, seatsocc + FROM zdemo_abap_fli + WHERE carrid = @carrier + INTO CORRESPONDING FIELDS OF TABLE @flight_data. + + ENDMETHOD. + + METHOD zdemo_abap_get_data_itf~say_hello. + hi = `Hello, ` && sy-uname && `.`. + ENDMETHOD. + +ENDCLASS. diff --git a/src/zcl_demo_abap_unit_test.clas.testclasses.abap b/src/zcl_demo_abap_unit_test.clas.testclasses.abap new file mode 100644 index 0000000..4710705 --- /dev/null +++ b/src/zcl_demo_abap_unit_test.clas.testclasses.abap @@ -0,0 +1,1384 @@ +*********************************************************************** +* Test Class Overview +* +* Note: +* - This test include contains multiple test classes and methods +* for unit testing the production code in the global class. +* - To run the unit test, choose CTRL/CMD + Shift + F10. See +* also the information in the global class (RUN ABAP UNIT TEST at the top). +* - Almost all test classes deliberately contain test methods which +* produce errors in the test and that can be checked in the ABAP Unit tab. +* Likewise, test methods are included for which the test does not fail. +* - All assertion methods include the parameters MSG (for displaying an +* error text in the test results) and QUIT (which is specified to not +* terminate test). +* +* Test classes +* Note the comments before the individual class declaration parts. +* +* - ltc_test_simple_1 +* - Simple example testing only one method; no special methods, +* no dependent-on component (DOC). +* +* - ltc_test_simple_2 +* - Testing multiple simple methods, no DOCs +* - Special methods setup and teardown +* +*- ltc_test_dummy +* - Does not contain any methods of the production code to be tested. +* - Demonstrates the use of various static methods of the cl_abap_unit_assert class +* +*- ltc_test_seam +* - Testing one method for which a DOC has been identified +* - Demonstrates the use of test seams and their injection into the production code. +* +*- ltc_test_doc_global_itf +* - Testing one method for which a DOC has been identified and for which a global +* interface exists +* - A local test double class is included +* - Demonstrates constructor injection as injection mechanism +* +*- ltc_test_local_itf +* - Testing one method for which a DOC has been identified +* - There is no global interface available. Instead, a local interface is created. +* - A local test double class is included +* - Demonstrates back door injection as injection mechanism +* +*- ltc_test_redef +* - Testing one method for which a DOC has been identified +* - A local test double class is created by redefining a method of +* the class under test. +* +*- ltc_test_doc_setter_inj +* - Testing one method for which a DOC has been identified and for which a global +* interface exists +* - A local test double class is included +* - Demonstrates setter injection as injection mechanism +* +*- ltc_test_doc_param_inj +* - Testing one method for which a DOC has been identified and for which a global +* interface exists +* - A local test double class is included +* - Demonstrates parameter injection as injection mechanism +* +*********************************************************************** + +"In this example, multiple test classes are created in the test include. +"Because private attributes are not accessible in local test classes, +"the local test classes are declared as local friends of the global class. +"In the example, a combined friendship declaration for all test classes is placed +"at the top of the test include. Prepending the friendship declaration with +"test class definitions and the DEFERRED addition makes the the test classes +"'known' at this stage and can thus be specified as local friends there. +CLASS ltc_test_simple_1 DEFINITION DEFERRED. +CLASS ltc_test_simple_2 DEFINITION DEFERRED. +CLASS ltc_test_doc_seam DEFINITION DEFERRED. +CLASS ltc_test_doc_global_itf DEFINITION DEFERRED. +CLASS ltc_test_doc_local_itf DEFINITION DEFERRED. +CLASS ltc_test_doc_redef DEFINITION DEFERRED. +CLASS ltc_test_doc_setter_inj DEFINITION DEFERRED. +CLASS ltc_test_doc_param_inj DEFINITION DEFERRED. + +CLASS zcl_demo_abap_unit_test DEFINITION LOCAL FRIENDS ltc_test_simple_1 + ltc_test_simple_2 + ltc_test_doc_seam + ltc_test_doc_global_itf + ltc_test_doc_local_itf + ltc_test_doc_redef + ltc_test_doc_setter_inj + ltc_test_doc_param_inj. + +*********************************************************************** +* Test class ltc_test_simple_1 +* +* - Simple example testing only one method; no special methods are declared +* - The tested method does not have a dependent-on component (DOC). It has one +* importing and a returning parameter. +* - The implementation of the test method provides some values against which +* the production method is tested a few times (using a loop). +* Expected values are also provided, on the basis of which the assertion is +* performed. +* - There are values included for which the assertion deliberately fails to +* demonstrate the errors in the ABAP Unit tab. +*********************************************************************** +CLASS ltc_test_simple_1 DEFINITION FOR TESTING +RISK LEVEL HARMLESS +DURATION SHORT. + +PRIVATE SECTION. + + METHODS: test_get_digit_sum FOR TESTING. + +ENDCLASS. + +CLASS ltc_test_simple_1 IMPLEMENTATION. + +METHOD test_get_digit_sum. + "The method to be tested calculates the digit sum of an number. + + "Creating an object of class under test + DATA(ref_cut) = NEW zcl_demo_abap_unit_test( ). + + "Filling an internal table and providing some values against which the production + "method is tested a few times. There are values included for which the assertion + "deliberately fails. + "num1 = actual parameter for the method call + "num2 = expected result of the method call + DATA(tab_digit_sum) = VALUE zcl_demo_abap_unit_test=>nums_tab( ( num1 = 124 num2 = 7 ) + ( num1 = 57 num2 = 12 ) + ( num1 = 11111 num2 = 5 ) + ( num1 = 1000 num2 = 1 ) + ( num1 = 0 num2 = 0 ) + + "To fail: + ( num1 = 124 num2 = 8 ) "correct: 7 + ( num1 = 57 num2 = 13 ) "correct: 12 + ( num1 = 11111 num2 = 6 ) ). "correct: 5 + + "Looping across the internal table and test the method with the values provided. + LOOP AT tab_digit_sum ASSIGNING FIELD-SYMBOL(). + + "Calling method that is to be tested + DATA(digit_sum) = ref_cut->get_digit_sum( -num1 ). + + "Assertion + cl_abap_unit_assert=>assert_equals( + act = digit_sum + exp = -num2 + msg = |The digit sum of { -num1 } is not { -num2 }. It is { digit_sum }.| + quit = if_abap_unit_constant=>quit-no ). + + ENDLOOP. + +ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Test class ltc_test_simple_2 +* +* - Test class used to test simple methods that do not have DOCs in the +* production code. +* - Similar to the test class above, the implementation of the test method +* provides some values against which the production method is tested a +* few times (in some cases using a loop). Expected values are also provided, +* on the basis of which the assertion is executed. There are values +* included for which the assertion intentionally fails to display the errors +* in the ABAP Unit tab. +* - The test class contains the declaration and implementation of the +* special methods setup and teardown. The setup method involves creating +* an object of the class under test. This object is used by all test methods. +* - The get_sum method in the production code is tested here. The original +* implementation of the method includes a SELECT statement to retrieve +* a single record. The statement sums the values of two fields, both are of +* type i. For proper testability, the database table is prepared in the setup +* method (an appropriate record is added to the table). +* - In the teardown method, the newly added record is deleted again. +* - Because of this change in persistent data, the test class should have the +* RISK LEVEL DANGEROUS property. However, in this example, it is set to +* HARMLESS because, depending on your system settings, you may not be allowed +* to run unit tests with RISK LEVEL DANGEROUS. +*********************************************************************** +CLASS ltc_test_simple_2 DEFINITION FOR TESTING +RISK LEVEL HARMLESS +DURATION SHORT. + +PRIVATE SECTION. + + TYPES: BEGIN OF nums, + num1 TYPE i, + num2 TYPE i, + common_div TYPE zcl_demo_abap_unit_test=>int_tab_so, + gcd TYPE i, + END OF nums, + tab_type TYPE TABLE OF nums WITH EMPTY KEY. + + DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test. + + METHODS: test_get_common_div_gcd_ok FOR TESTING, + test_get_common_div_gcd_fail FOR TESTING, + + test_get_digit_sum_ok FOR TESTING, + test_get_digit_sum_fail FOR TESTING, + + test_get_sum_ok FOR TESTING, + test_get_sum_fail FOR TESTING, + + setup, + teardown. + +ENDCLASS. + + +CLASS ltc_test_simple_2 IMPLEMENTATION. + +METHOD setup. + + "Creating an object of the class under test + ref_cut = NEW #( ). + + "Preparing demo database table for testing the get_sum method + DELETE FROM zdemo_abap_tab1 + WHERE key_field = 987654321 + AND char1 = '@'. + + MODIFY zdemo_abap_tab1 FROM @( + VALUE #( key_field = 987654321 + char1 = '@' + num1 = 444 + num2 = 555 ) ). + +ENDMETHOD. + +METHOD teardown. + "Removing test data inserted into the demo database table for testing the get_sum method + DELETE FROM zdemo_abap_tab1 + WHERE key_field = 987654321 + AND char1 = '@'. +ENDMETHOD. + +METHOD test_get_common_div_gcd_ok. + "The method to be tested calculates the common divisors of two numbers + "and the greatest common divisor. + + "Filling an internal table and providing some values against which the production + "method is tested a few times (using a loop). + "num1/num2 = numbers on which the calculation is based + "common_div = table containing the expected common divisors + "gcd = expected greatest common divisor + DATA(tab) = VALUE tab_type( + ( num1 = 6 num2 = 3 common_div = VALUE #( ( 1 ) ( 3 ) ) gcd = 3 ) + ( num1 = 24 num2 = 36 common_div = VALUE #( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 6 ) ( 12 ) ) gcd = 12 ) + ( num1 = 5 num2 = 7 common_div = VALUE #( ( 1 ) ) gcd = 1 ) + ( num1 = 21 num2 = 35 common_div = VALUE #( ( 1 ) ( 7 ) ) gcd = 7 ) ). + + "Looping across the internal table and test the method with the values provided. + LOOP AT tab ASSIGNING FIELD-SYMBOL(). + + "Calling method that is to be tested + ref_cut->get_common_div_and_gcd( EXPORTING a = -num1 + b = -num2 + IMPORTING common_divisors = DATA(a) + gcd = DATA(b) ). + + "Assertion + cl_abap_unit_assert=>assert_equals( + act = VALUE nums( common_div = a + gcd = b ) + exp = VALUE nums( common_div = -common_div + gcd = -gcd ) + msg = |Wrong result for { -num1 } and { -num2 }.| + quit = if_abap_unit_constant=>quit-no ). + + ENDLOOP. + +ENDMETHOD. + +METHOD test_get_common_div_gcd_fail. + "This method intentionally includes values to make the unit test fail. + + DATA(num1) = 6. + DATA(num2) = 3. + DATA(c_div) = VALUE zcl_demo_abap_unit_test=>int_tab_st( ( 1 ) ( 2 ) ). "correct: 1, 3 + DATA(g_c_d) = 4. "correct: 3 + + "Calling method that is to be tested + ref_cut->get_common_div_and_gcd( EXPORTING a = num1 + b = num2 + IMPORTING common_divisors = DATA(cd) + gcd = DATA(gr) ). + + "Assertion + cl_abap_unit_assert=>assert_equals( + act = VALUE nums( common_div = cd + gcd = gr ) + exp = VALUE nums( common_div = c_div + gcd = g_c_d ) + msg = |Wrong result for { num1 } and { num2 }.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +METHOD test_get_digit_sum_ok. + "The method to be tested calculates the digit sum of a number. + + + "Filling an internal table and providing some values against which the production + "method is tested a few times (using a loop). + "num1: Number on which the digit sum is calculated + "num2: Expected result + DATA(tab_digit_sum) = VALUE zcl_demo_abap_unit_test=>nums_tab( ( num1 = 124 num2 = 7 ) + ( num1 = 57 num2 = 12 ) + ( num1 = 11111 num2 = 5 ) + ( num1 = 1000 num2 = 1 ) + ( num1 = 0 num2 = 0 ) ). + + "Looping across the internal table and test the method with the values provided. + LOOP AT tab_digit_sum ASSIGNING FIELD-SYMBOL(). + + "Calling method that is to be tested + DATA(digit_sum) = ref_cut->get_digit_sum( -num1 ). + + "Assertion + cl_abap_unit_assert=>assert_equals( + act = digit_sum + exp = -num2 + msg = |The digit sum of { -num1 } is not { digit_sum }.| + quit = if_abap_unit_constant=>quit-no ). + + ENDLOOP. + +ENDMETHOD. + +METHOD test_get_digit_sum_fail. + "This method intentionally includes values to make the unit test fail. + + DATA(num) = 123. + DATA(dsum) = 7. "correct: 6 + + "Calling method that is to be tested + DATA(digit_sum) = ref_cut->get_digit_sum( num ). + + "Assertion + cl_abap_unit_assert=>assert_equals( + act = digit_sum + exp = dsum + msg = |The digit sum of { num } is not { dsum }.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +METHOD test_get_sum_ok. + "The method to be tested calculates the sum of two numbers. + "Note: The setup method has prepared data in the database table. + "See the comments above. + + DATA(exp_sum) = 999. + + "Calling method that is to be tested + DATA(act_sum) = ref_cut->get_sum( key = 987654321 char = '@' ). + + "Assertion + cl_abap_unit_assert=>assert_equals( + act = act_sum + exp = exp_sum + msg = |The expected sum { exp_sum } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +METHOD test_get_sum_fail. + "This method intentionally includes values to make the unit test fail. + + DATA(exp_sum) = 998. "correct: 999 + DATA(act_sum) = ref_cut->get_sum( key = 987654321 char = '@' ). + + cl_abap_unit_assert=>assert_equals( + act = act_sum + exp = exp_sum + msg = |The expected sum { exp_sum } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Test class ltc_test_dummy +* +* - This test class does not contain any methods of the production code +* to be tested. +* - It only demonstrates the use of various static methods of the +* cl_abap_unit_assert class. +* - Since no (private) components of the production code are used, the +* test class is not made a local friend of the class under test. +*********************************************************************** +CLASS ltc_test_dummy DEFINITION FOR TESTING +RISK LEVEL HARMLESS +DURATION SHORT. + +PRIVATE SECTION. + + METHODS: test_multiple_asserts_ok FOR TESTING, + test_multiple_asserts_fail FOR TESTING. + +ENDCLASS. + +CLASS ltc_test_dummy IMPLEMENTATION. + +METHOD test_multiple_asserts_ok. + "This method does not test a method of the class under test. + + DATA(ref) = NEW zcl_demo_abap_unit_test( ). + + DATA(is_true) = cl_abap_unit_assert=>assert_equals( + act = 1 + exp = 1 + msg = |assert_equals: Issue.| + quit = if_abap_unit_constant=>quit-no ). + + is_true = cl_abap_unit_assert=>assert_bound( + act = ref + msg = |assert_bound: Issue.| + quit = if_abap_unit_constant=>quit-no ). + + is_true = cl_abap_unit_assert=>assert_differs( + act = `hallo` + exp = `hallo!` + msg = |assert_differs: Issue.| + quit = if_abap_unit_constant=>quit-no ). + + is_true = cl_abap_unit_assert=>assert_initial( + act = 0 + msg = |assert_initial: Issue.| + quit = if_abap_unit_constant=>quit-no ). + + is_true = cl_abap_unit_assert=>assert_not_initial( + act = 1 + msg = |assert_not_initial: Issue.| + quit = if_abap_unit_constant=>quit-no ). + + FIND `A` IN `ABAP`. + + is_true = cl_abap_unit_assert=>assert_subrc( + exp = 0 + act = sy-subrc + msg = |assert_subrc: Issue.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +METHOD test_multiple_asserts_fail. + "This method intentionally includes values to make the unit test fail. + + DATA ref TYPE REF TO zcl_demo_abap_unit_test. + + DATA(is_true) = cl_abap_unit_assert=>assert_equals( + exp = 'This a string that is checked.' + act = 'This a string that is checked ... but it is not the expected string.' + msg = |assert_equals: Issue.| + quit = if_abap_unit_constant=>quit-no ). + + is_true = cl_abap_unit_assert=>assert_bound( + act = ref + msg = |assert_bound: Issue.| + quit = if_abap_unit_constant=>quit-no ). + + is_true = cl_abap_unit_assert=>assert_differs( + act = `hallo` + exp = `hallo` + msg = |assert_differs: Issue.| + quit = if_abap_unit_constant=>quit-no ). + + is_true = cl_abap_unit_assert=>assert_initial( + act = 1 + msg = |assert_initial: Issue.| + quit = if_abap_unit_constant=>quit-no ). + + is_true = cl_abap_unit_assert=>assert_not_initial( + act = 0 + msg = |assert_not_initial: Issue.| + quit = if_abap_unit_constant=>quit-no ). + + FIND `C` IN `ABAP`. + + is_true = cl_abap_unit_assert=>assert_subrc( + exp = 0 + act = sy-subrc + msg = |assert_subrc: Issue.| + quit = if_abap_unit_constant=>quit-no ). + + IF 1 <> 2. + cl_abap_unit_assert=>fail( + msg = |fail: Issue.| + quit = if_abap_unit_constant=>quit-no ). + ENDIF. + +ENDMETHOD. + +ENDCLASS. + + +*********************************************************************** +* Test class ltc_test_seam +* +* - This test class demonstrates the use of test seams and their injection +* into the production code. +* - In the production code, the method implementation includes a +* depended-on component (DOC), which in this case is a database access. +* The DOC is replaced by a test double using a test seam. +* - The method implementation contains further simple examples for +* test seam injections. +*********************************************************************** +CLASS ltc_test_doc_seam DEFINITION FOR TESTING +RISK LEVEL HARMLESS +DURATION SHORT. + +PRIVATE SECTION. + + DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test, + carrier_id TYPE zdemo_abap_fli-carrid. + + METHODS: test_get_occ_rate_seam_ok FOR TESTING, + test_get_occ_rate_seam_fail FOR TESTING, + setup. + +ENDCLASS. + + +CLASS ltc_test_doc_seam IMPLEMENTATION. + +METHOD setup. + + "Creating an object of the class under test + ref_cut = NEW #( ). + +ENDMETHOD. + +METHOD test_get_occ_rate_seam_ok. + "The method to be tested calculates the occupancy rate of flights + + "Creating test data + carrier_id = 'AB'. + DATA(expected_occupancy_rate) = '50.00'. + + "Injecting test seam into production code by replacing the code that is + "contained in the TEST-SEAM ... TEST-SEAM-END block + TEST-INJECTION select_flights. + seats_table = VALUE #( + ( carrid = carrier_id seatsmax = 100 seatsocc = 80 ) + ( carrid = carrier_id seatsmax = 100 seatsocc = 20 ) + ( carrid = carrier_id seatsmax = 200 seatsocc = 100 ) ). + END-TEST-INJECTION. + + "Further test seam injections + "The following example demonstrates an empty test seam in the production + "code. During the unit test run, the following code is injected. + TEST-INJECTION num1. + var = 1. + END-TEST-INJECTION. + + "empty injection + + "The following example demonstrates an empty test seam injection. + "There is code in a TEST-SEAM ... END-TEST-SEAM block in the + "production code. This code is replaced, and nothing is included + "for the test, i.e. during the unit test 'unwanted' code in the production + "code is ignored. + TEST-INJECTION num2. + END-TEST-INJECTION. + + "Calling method that is to be tested + ref_cut->get_occ_rate_test_seam( EXPORTING carrier_id = carrier_id + IMPORTING occupancy_rate = DATA(occupancy_rate) + num1 = DATA(num1) + num2 = DATA(num2) ). + + "Assertions + cl_abap_unit_assert=>assert_equals( + act = occupancy_rate + exp = expected_occupancy_rate + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + + cl_abap_unit_assert=>assert_equals( + act = num1 + exp = 999 + msg = |num1: { num1 }| + quit = if_abap_unit_constant=>quit-no ). + + cl_abap_unit_assert=>assert_equals( + act = num2 + exp = 0 + msg = |num2: { num2 }| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +METHOD test_get_occ_rate_seam_fail. + "This method intentionally includes values to make the unit test fail. + + "Creating test data + carrier_id = 'CD'. + DATA(expected_occupancy_rate) = '60.00'. + + "Code injection + TEST-INJECTION select_flights. + seats_table = VALUE #( + ( carrid = carrier_id seatsmax = 100 seatsocc = 50 ) + ( carrid = carrier_id seatsmax = 200 seatsocc = 100 ) + ( carrid = carrier_id seatsmax = 300 seatsocc = 100 ) ). + END-TEST-INJECTION. + + TEST-INJECTION num1. + var = 1. + END-TEST-INJECTION. + + TEST-INJECTION num2. + END-TEST-INJECTION. + + "Calling method that is to be tested + ref_cut->get_occ_rate_test_seam( EXPORTING carrier_id = carrier_id + IMPORTING occupancy_rate = DATA(occupancy_rate) + num1 = DATA(num1) + num2 = DATA(num2) ). + + "Assertions + cl_abap_unit_assert=>assert_equals( + act = occupancy_rate + exp = expected_occupancy_rate + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + + cl_abap_unit_assert=>assert_equals( + act = num1 + exp = 1 + msg = |num1: { num1 }| + quit = if_abap_unit_constant=>quit-no ). + + cl_abap_unit_assert=>assert_equals( + act = num2 + exp = 123 + msg = |num2: { num2 }| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Test class ltc_test_doc_global_itf +* +* - Tests one method of the global class; demonstrates constructor injection +* - In this case, a dependent-on component (DOC) has been identified (a +* database access). +* - It is assumed that there is a global interface to overcome the DOC. +* - A local test double class is created. It implements the global +* interface. The method implementation contains manually created test +* data. +* +* Notes on constructor injection in this example: +* - This means that a test double is passed as a parameter to the instance +* constructor of the class under test. +* - An interface reference variable is declared in the private section of +* the class under test, and its type references the global interface. +* - A local test class is created here for the test double. It implements +* the interface method required by the test. Note the PARTIALLY IMPLEMENTED +* addition to the interface. +* - In this method implementation, local test data are created. +* - The global class/class under test has the following instance constructor +* declaration: +* - Has an optional importing parameter for the DOC. +* - The parameter is typed with reference to the test double, i.e. an +* object of the test double is passed. +* - Instance constructor implementation: When the unit test is executed, an +* object of the test double is created in the test method. This object is +* then passed to the constructor. A check is implemented to determine if +* the reference variable is bound. It is indeed bound during the unit test, +* and the test double is injected. +*********************************************************************** + +*********************************************************************** +* Local test double class +*********************************************************************** +CLASS ltd_test_data_global_intf DEFINITION FOR TESTING. +PUBLIC SECTION. + + "Note: Usually, you must implement all non-optional methods of interfaces. + "Without the addition PARTIALLY IMPLEMENTED, there would be a syntax error. + INTERFACES zdemo_abap_get_data_itf PARTIALLY IMPLEMENTED. + +ENDCLASS. + +CLASS ltd_test_data_global_intf IMPLEMENTATION. +METHOD zdemo_abap_get_data_itf~select_flight_data. + + CLEAR flight_data. + + "Providing test data + flight_data = SWITCH #( carrier + WHEN 'EF' THEN VALUE #( ( carrid = carrier seatsmax = 100 seatsocc = 50 ) + ( carrid = carrier seatsmax = 200 seatsocc = 150 ) + ( carrid = carrier seatsmax = 300 seatsocc = 100 ) ) + WHEN 'GH' THEN VALUE #( ( carrid = carrier seatsmax = 350 seatsocc = 200 ) + ( carrid = carrier seatsmax = 350 seatsocc = 250 ) + ( carrid = carrier seatsmax = 300 seatsocc = 150 ) ) ). + +ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Test class ltc_test_doc_global_itf +*********************************************************************** +CLASS ltc_test_doc_global_itf DEFINITION FOR TESTING +RISK LEVEL HARMLESS +DURATION SHORT. + +PRIVATE SECTION. + DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test, + ref_data_prov TYPE REF TO zdemo_abap_get_data_itf, + carrier_id TYPE zdemo_abap_fli-carrid. + + METHODS: setup, + test_get_occ_rate_glo_if_ok FOR TESTING, + test_get_occ_rate_glo_if_fail FOR TESTING. + +ENDCLASS. + +CLASS ltc_test_doc_global_itf IMPLEMENTATION. + +METHOD setup. + + "Creating an instance of the local test double + ref_data_prov = NEW ltd_test_data_global_intf( ). + + "Instance is provided for the constructor injection + ref_cut = NEW #( ref_data_prov ). + +ENDMETHOD. + +METHOD test_get_occ_rate_glo_if_ok. + "The method to be tested calculates the occupancy rate of flights. + + "(1) Calling method that is to be tested + "Due to constructor injection, the test double is used. + carrier_id = 'EF'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_global_itf( carrier_id ). + + "Assertion + DATA(exp_value) = '50.00'. + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + + "(2) Calling method that is to be tested + carrier_id = 'GH'. + act_occ_rate = ref_cut->get_occ_rate_global_itf( carrier_id ). + + "Assertion + exp_value = '60.00'. + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +METHOD test_get_occ_rate_glo_if_fail. + "This method intentionally includes values to make the unit test fail. + + "(1) Calling method that is to be tested + carrier_id = 'EF'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_global_itf( carrier_id ). + + "Assertion to fail + DATA(exp_value) = '40.00'. "correct: 50.00 + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + + "(2) Calling method that is to be tested + carrier_id = 'GH'. + act_occ_rate = ref_cut->get_occ_rate_global_itf( carrier_id ). + + "Assertion to fail + exp_value = '90.00'. "correct: 60.00 + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Test class ltc_test_local_itf +* +* - Tests one method of the global class; demonstrates back door injection +* - In this case, a dependent-on component (DOC) has been identified (a +* database access). The method is similar to the one above. +* - It is assumed that there is no global interface to overcome the DOC. +* Instead, a local interface is created in the local types +* (CCIMP include): lif_get_data +* - Additionally, a local class is implemented in the CCIMP include that +* implements the local interface. This local class provides the data +* for the method call in the global class (get_occ_rate_local_itf). +* - A local test double class is created here. It implements the local +* interface. The method implementation contains manually created test +* data. +* - In addition, the test class includes a helper method to demonstrate +* the separation of code recurring tasks into separate methods. +* +* Notes on back door injection in this example: +* - This means that a back door is created to inject a test double into +* the class under test. +* - This back door is implemented by granting friendship to the test +* class. +* This makes internal attributes of the class under test accessible +* to the test class. +* - In the production code, a reference variable with type reference to +* the data provider is declared in the private section. +* - When the unit test is executed, the private attribute of the class +* under test is changed. +* - An object of the test double is injected. +*********************************************************************** + +*********************************************************************** +* Local test double class +*********************************************************************** +CLASS ltd_test_data_local_itf DEFINITION FOR TESTING. +PUBLIC SECTION. + INTERFACES lif_get_data PARTIALLY IMPLEMENTED. +ENDCLASS. + +CLASS ltd_test_data_local_itf IMPLEMENTATION. +METHOD lif_get_data~select_flight_data. + + CLEAR flight_data. + + "Providing test data + flight_data = SWITCH #( carrier + WHEN 'IJ' THEN VALUE #( ( carrid = carrier seatsmax = 300 seatsocc = 200 ) + ( carrid = carrier seatsmax = 350 seatsocc = 200 ) + ( carrid = carrier seatsmax = 350 seatsocc = 300 ) ) + WHEN 'KL' THEN VALUE #( ( carrid = carrier seatsmax = 350 seatsocc = 300 ) + ( carrid = carrier seatsmax = 350 seatsocc = 250 ) + ( carrid = carrier seatsmax = 300 seatsocc = 250 ) ) ). + +ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Test class ltc_test_doc_local_itf +*********************************************************************** +CLASS ltc_test_doc_local_itf DEFINITION FOR TESTING +RISK LEVEL HARMLESS +DURATION SHORT. + +PRIVATE SECTION. + + DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test, + ref_data_prov TYPE REF TO lif_get_data. + + METHODS: setup, + + "Helper method + assert_occ_rate IMPORTING carrier_id TYPE zdemo_abap_fli-carrid + occ_rate TYPE lif_get_data=>occ_rate, + + "Test methods + test_get_occ_rate_lo_itf_ok FOR TESTING, + test_get_occ_rate_lo_itf_fail FOR TESTING. + +ENDCLASS. + + +CLASS ltc_test_doc_local_itf IMPLEMENTATION. + +METHOD setup. + + ref_cut = NEW #( ). + + ref_data_prov = NEW ltd_test_data_local_itf( ). + + "Back door injection + ref_cut->data_provider_local_itf = ref_data_prov. + +ENDMETHOD. + +METHOD assert_occ_rate. + + DATA(act_occ_rate) = ref_cut->get_occ_rate_local_itf( carrier_id ). + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = occ_rate + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + + +METHOD test_get_occ_rate_lo_itf_ok. + + assert_occ_rate( carrier_id = 'IJ' + occ_rate = '70.00' ). + + assert_occ_rate( carrier_id = 'KL' + occ_rate = '80.00' ). + +ENDMETHOD. + +METHOD test_get_occ_rate_lo_itf_fail. + "This method intentionally includes values to make the unit test fail. + + assert_occ_rate( carrier_id = 'IJ' + occ_rate = '20.00' ). "correct: 70.00 + + assert_occ_rate( carrier_id = 'KL' + occ_rate = '30.00' ). "correct: 80.00 + +ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Test class ltc_test_redef +* +* - Tests one method of the global class; demonstrates constructor injection +* - In this case, a dependent-on component (DOC) has been identified (a +* database access). +* - It is assumed that there are no global and local interfaces to overcome +* the DOC. +* - A local test double class is created by redefining a method of +* the class under test. The method implementation contains manually +* created test data. +* +* Notes: +* - A local test double is created in a separate test class. +* - The test double is created by redefining a method of a class. +* - In this simple and self-contained example, the global class is +* deliberately not declared as a final class, so that inheritance +* from it is allowed. The global class implements a method that +* particularly serves the purpose of the test. It is there only +* to have a self-contained example and to keep the number +* of separate artifacts low. You might imagine that the method is +* contained in another global class, and the global class under test +* uses that method. +********************************************************************** + +*********************************************************************** +* Local test double class +*********************************************************************** +CLASS ltd_test_data_redef DEFINITION FOR TESTING +INHERITING FROM zcl_demo_abap_unit_test. + +PROTECTED SECTION. + METHODS select_flight_data REDEFINITION. + +ENDCLASS. + +CLASS ltd_test_data_redef IMPLEMENTATION. +METHOD select_flight_data. + + CLEAR flight_data. + + "Providing test data + flight_data = SWITCH #( carrier + WHEN 'MN' THEN VALUE #( ( carrid = carrier seatsmax = 100 seatsocc = 50 ) + ( carrid = carrier seatsmax = 200 seatsocc = 150 ) + ( carrid = carrier seatsmax = 300 seatsocc = 100 ) ) + WHEN 'OP' THEN VALUE #( ( carrid = carrier seatsmax = 350 seatsocc = 200 ) + ( carrid = carrier seatsmax = 350 seatsocc = 250 ) + ( carrid = carrier seatsmax = 300 seatsocc = 150 ) ) ). + +ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Test class ltc_test_doc_redef +*********************************************************************** +CLASS ltc_test_doc_redef DEFINITION FOR TESTING +RISK LEVEL HARMLESS +DURATION SHORT. + +PRIVATE SECTION. + DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test, + carrier_id TYPE zdemo_abap_fli-carrid. + + METHODS: setup, + test_get_occ_rate_redef_ok FOR TESTING, + test_get_occ_rate_redef_fail FOR TESTING. + +ENDCLASS. + +CLASS ltc_test_doc_redef IMPLEMENTATION. + +METHOD setup. + + ref_cut = NEW ltd_test_data_redef( ). + +ENDMETHOD. + +METHOD test_get_occ_rate_redef_ok. + + carrier_id = 'MN'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_using_meth( carrier_id ). + + DATA(exp_value) = '50.00'. + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + + carrier_id = 'OP'. + act_occ_rate = ref_cut->get_occ_rate_using_meth( carrier_id ). + + exp_value = '60.00'. + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +METHOD test_get_occ_rate_redef_fail. + + carrier_id = 'MN'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_using_meth( carrier_id ). + + DATA(exp_value) = '40.00'. "correct: 50.00 + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + + carrier_id = 'OP'. + act_occ_rate = ref_cut->get_occ_rate_using_meth( carrier_id ). + + exp_value = '90.00'. "correct: 60.00 + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Test class ltc_test_doc_setter_inj +* +* - Tests one method of the global class; demonstrates setter injection +* - In this case, a dependent-on component (DOC) has been identified (a +* database access). +* - It is assumed that there is a global interface to overcome the DOC. +* - A local test double class is created. It implements the global +* interface. The method implementation contains manually created test +* data. +* +* Notes on setter injection in this example: +* - This means that an object of the test double class is passed as a +* parameter to a setter method in the class under test. +* - An interface reference variable is declared in the private section of +* the class under test, and its type references the global interface. +* - A local test class is created here for the test double. It implements +* the interface method required by the test. Note the PARTIALLY IMPLEMENTED +* addition to the interface. +* - In this method implementation, local test data are created. +* - The global class/class under test contains a setter method (setter_meth) +* that has an importing parameter of type reference to the global interface. +* - When the unit test is executed (and only then), the setter method is +* called in the test class. Before the call is made, an object of the +* test double class is created. This object is passed to the setter method. +* - In the implementation of the setter method, the interface variable declared in +* the private section of the class under test is assigned the object of +* the test double class. When the method is 'usually' called +* (i.e., not in the context of a unit test), for example, when the +* class is run using F9, the instance constructor implementation involves +* creating an object of the actual data provider that is assigned to +* the interface variable. Therefore, when the unit test is executed, the +* assigned object is replaced by the setter method and the test double +* is used instead. +*********************************************************************** + +*********************************************************************** +* Local test double class +*********************************************************************** +CLASS ltd_test_data_setter_inj DEFINITION FOR TESTING. +PUBLIC SECTION. + + "Note: Usually, you must implement all non-optional methods of interfaces. + "Without the addition PARTIALLY IMPLEMENTED, there would be a syntax error. + INTERFACES zdemo_abap_get_data_itf PARTIALLY IMPLEMENTED. + +ENDCLASS. + +CLASS ltd_test_data_setter_inj IMPLEMENTATION. +METHOD zdemo_abap_get_data_itf~select_flight_data. + + CLEAR flight_data. + + "Providing test data + flight_data = SWITCH #( carrier + WHEN 'QR' THEN VALUE #( ( carrid = carrier seatsmax = 100 seatsocc = 50 ) + ( carrid = carrier seatsmax = 200 seatsocc = 150 ) + ( carrid = carrier seatsmax = 300 seatsocc = 100 ) ) + WHEN 'ST' THEN VALUE #( ( carrid = carrier seatsmax = 350 seatsocc = 200 ) + ( carrid = carrier seatsmax = 350 seatsocc = 250 ) + ( carrid = carrier seatsmax = 300 seatsocc = 150 ) ) ). + +ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Test class ltc_test_doc_setter_inj +*********************************************************************** +CLASS ltc_test_doc_setter_inj DEFINITION FOR TESTING +RISK LEVEL HARMLESS +DURATION SHORT. + +PRIVATE SECTION. + DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test, + ref_data_prov TYPE REF TO zdemo_abap_get_data_itf, + carrier_id TYPE zdemo_abap_fli-carrid. + + METHODS: setup, + test_get_occ_rate_set_inj_ok FOR TESTING, + test_get_occ_rate_set_inj_fail FOR TESTING. + +ENDCLASS. + +CLASS ltc_test_doc_setter_inj IMPLEMENTATION. + +METHOD setup. + + ref_cut = NEW #( ). + + "Creating an instance of the local test double + ref_data_prov = NEW ltd_test_data_setter_inj( ). + + "Setter injection + "Passing the test double as a parameter of a setter method + ref_cut->setter_meth( ref_data_prov ). + +ENDMETHOD. + +METHOD test_get_occ_rate_set_inj_ok. + "The method to be tested calculates the occupancy rate of flights. + + "(1) Calling method that is to be tested + "Due to constructor injection, the test double is used. + carrier_id = 'QR'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_setter_inj( carrier_id ). + + "Assertion + DATA(exp_value) = '50.00'. + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + + "(2) Calling method that is to be tested + carrier_id = 'ST'. + act_occ_rate = ref_cut->get_occ_rate_setter_inj( carrier_id ). + + "Assertion + exp_value = '60.00'. + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +METHOD test_get_occ_rate_set_inj_fail. + "This method intentionally includes values to make the unit test fail. + + "(1) Calling method that is to be tested + carrier_id = 'QR'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_setter_inj( carrier_id ). + + "Assertion to fail + DATA(exp_value) = '40.00'. "correct: 50.00 + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + + "(2) Calling method that is to be tested + carrier_id = 'ST'. + act_occ_rate = ref_cut->get_occ_rate_setter_inj( carrier_id ). + + "Assertion to fail + exp_value = '90.00'. "correct: 60.00 + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Test class ltc_test_doc_param_inj +* +* - Tests one method of the global class; demonstrates parameter injection +* - In this case, a dependent-on component (DOC) has been identified (a +* database access). +* - It is assumed that there is a global interface to overcome the DOC. +* - A local test double class is created. It implements the global +* interface. The method implementation contains manually created test +* data. +* +* Notes on parameter injection in this example: +* - This means that an object of the test double class is passed as a +* parameter of the tested method in the class under test. +* - This parameter is optional. When the unit test is run, the parameter +* is bound. An object of the test double class is passed in this case. +* Otherwise, when the method is 'usually' called (i.e., not in the +* context of a unit test), for example, when the class is run using F9, +* the parameter is not bound. Then, an object of the actual data +* provider is created. The method in the class under test contains a +* check in the implementation (IF ... IS BOUND ...). +*********************************************************************** + +*********************************************************************** +* Local test double class +*********************************************************************** +CLASS ltd_test_data_param_inj DEFINITION FOR TESTING. +PUBLIC SECTION. + + "Note: Usually, you must implement all non-optional methods of interfaces. + "Without the addition PARTIALLY IMPLEMENTED, there would be a syntax error. + INTERFACES zdemo_abap_get_data_itf PARTIALLY IMPLEMENTED. + +ENDCLASS. + +CLASS ltd_test_data_param_inj IMPLEMENTATION. +METHOD zdemo_abap_get_data_itf~select_flight_data. + + CLEAR flight_data. + + "Providing test data + flight_data = SWITCH #( carrier + WHEN 'UV' THEN VALUE #( ( carrid = carrier seatsmax = 100 seatsocc = 50 ) + ( carrid = carrier seatsmax = 200 seatsocc = 150 ) + ( carrid = carrier seatsmax = 300 seatsocc = 100 ) ) + WHEN 'WX' THEN VALUE #( ( carrid = carrier seatsmax = 350 seatsocc = 200 ) + ( carrid = carrier seatsmax = 350 seatsocc = 250 ) + ( carrid = carrier seatsmax = 300 seatsocc = 150 ) ) ). + +ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Test class ltc_test_doc_param_inj +*********************************************************************** +CLASS ltc_test_doc_param_inj DEFINITION FOR TESTING +RISK LEVEL HARMLESS +DURATION SHORT. + +PRIVATE SECTION. + DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test, + ref_data_prov TYPE REF TO zdemo_abap_get_data_itf, + carrier_id TYPE zdemo_abap_fli-carrid. + + METHODS: setup, + test_get_occ_rate_par_inj_ok FOR TESTING, + test_get_occ_rate_par_inj_fail FOR TESTING. + +ENDCLASS. + +CLASS ltc_test_doc_param_inj IMPLEMENTATION. + +METHOD setup. + + ref_cut = NEW #( ). + + "Creating an instance of the local test double + ref_data_prov = NEW ltd_test_data_param_inj( ). + +ENDMETHOD. + +METHOD test_get_occ_rate_par_inj_ok. + "The method to be tested calculates the occupancy rate of flights. + + "(1) Calling method that is to be tested + "Due to constructor injection, the test double is used. + carrier_id = 'UV'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_param_inj( carrier_id = carrier_id + data_prov = ref_data_prov ). + + "Assertion + DATA(exp_value) = '50.00'. + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + + "(2) Calling method that is to be tested + carrier_id = 'WX'. + act_occ_rate = ref_cut->get_occ_rate_param_inj( carrier_id = carrier_id + data_prov = ref_data_prov ). + + "Assertion + exp_value = '60.00'. + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +METHOD test_get_occ_rate_par_inj_fail. + "This method intentionally includes values to make the unit test fail. + + "(1) Calling method that is to be tested + carrier_id = 'UV'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_param_inj( carrier_id = carrier_id + data_prov = ref_data_prov ). + + "Assertion to fail + DATA(exp_value) = '40.00'. "correct: 50.00 + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + + "(2) Calling method that is to be tested + carrier_id = 'WX'. + act_occ_rate = ref_cut->get_occ_rate_param_inj( carrier_id = carrier_id + data_prov = ref_data_prov ). + + "Assertion to fail + exp_value = '90.00'. "correct: 60.00 + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +ENDCLASS. diff --git a/src/zcl_demo_abap_unit_test.clas.xml b/src/zcl_demo_abap_unit_test.clas.xml new file mode 100644 index 0000000..00173f5 --- /dev/null +++ b/src/zcl_demo_abap_unit_test.clas.xml @@ -0,0 +1,17 @@ + + + + + + ZCL_DEMO_ABAP_UNIT_TEST + E + ABAP cheat sheet: Unit tests + 1 + X + X + X + X + + + + diff --git a/src/zcl_demo_abap_xml_json.clas.abap b/src/zcl_demo_abap_xml_json.clas.abap new file mode 100644 index 0000000..4006f26 --- /dev/null +++ b/src/zcl_demo_abap_xml_json.clas.abap @@ -0,0 +1,1585 @@ +*********************************************************************** +* +* ABAP cheat sheet: Working with XML and JSON in ABAP +* +* -------------------------- PURPOSE ---------------------------------- +* - Example that demonstrates working with XML and JSON in ABAP. +* - The following topics are covered:* +* - Processing XML using class libraries (iXML, sXML) +* - XML Transformations using XSLT and Simple Transformations +* - CALL TRANSFORMATION syntax +* - Dealing with JSON data, XCO classes for JSON +* - Excursions: Converting string <-> xstring, compressing and +* decompressing binary data +* +* ----------------------- GETTING STARTED ----------------------------- +* - Open the class with the ABAP development tools for Eclipse (ADT). +* - Choose F9 to run the class. +* - Check the console output. +* - To understand the context and the ABAP syntax used, refer to the +* notes included in the class as comments or refer to the respective +* topic in the ABAP Keyword Documentation. +* - Due to the amount of console output, the examples contain numbers +* (e.g. 1) ..., 2) ..., 3) ...) for the individual example sections. +* Also, the variable name is displayed in most cases. So to find +* the relevant output in the console easier and faster, just search +* for the number/variable name in the console (CTRL+F in the console) +* or use the debugger. +* +* ----------------------------- NOTE ----------------------------------- +* The code presented in this class is intended only to support 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, not to solve concrete +* programming tasks. For production application programs, you should +* always work out your own solution for each individual case. There is +* no guarantee for the correctness or completeness of the code. +* Furthermore, there is no legal responsibility or liability for any +* errors or their consequences that may occur when using the the example +* code. +* +*********************************************************************** +"!

    ABAP cheat sheet: Working with XML and JSON in ABAP

    +"! Example that demonstrates working with XML and JSON in ABAP..
    Choose F9 in ADT to run the class. +CLASS zcl_demo_abap_xml_json DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES: if_oo_adt_classrun, + "This interface is implemented for serializing and + "deserialzing instances of classes (objects). + if_serializable_object. + CLASS-METHODS class_constructor. + + PROTECTED SECTION. + PRIVATE SECTION. + CLASS-METHODS format IMPORTING input TYPE data + xml TYPE abap_bool DEFAULT abap_true + RETURNING VALUE(string) TYPE string. + TYPES c50 TYPE c LENGTH 50. + TYPES c50_tab_type TYPE TABLE OF c50 WITH EMPTY KEY. + TYPES x30 TYPE x LENGTH 30. + TYPES x30_tab_type TYPE TABLE OF x30 WITH EMPTY KEY. + + "The following instance attribues and methods are used for serializing and + "deserialzing instances of classes (objects) + DATA: attr_string_a TYPE string, + attr_string_b TYPE string, + attr_concat_string TYPE string, + attr_lowercase_str TYPE string. + METHODS: concatenate_string, + lowercase_string, + "The following method can only have output parameters. + "For each output parameter of the serialize_helper method, you must specify + "an identically named input parameter of the deserialize_helper method + "with the same type. + serialize_helper EXPORTING attr_string_a TYPE string + attr_string_b TYPE string + attr_concat_string TYPE string, + "This method can only have input parameters. + deserialize_helper IMPORTING attr_string_a TYPE string + attr_string_b TYPE string + attr_concat_string TYPE string. +ENDCLASS. + + + +CLASS zcl_demo_abap_xml_json IMPLEMENTATION. + + METHOD if_oo_adt_classrun~main. + + out->write( |ABAP Cheat Sheet Example: Working with XML and JSON in ABAP\n\n| ). + out->write( |1) Excursion: Converting string <-> xstring| ). + "In the following examples, many operations are performed using binary data. + "This excursion shows the conversion of string to xstring and the other way round + "using a codepage. The examples use UTF-8. + "For this purpose, you can, for example, use the cl_abap_conv_codepage class + "and the XCO library. + + "Storing XML data in a data object of type string + DATA(xml_string) = + `` && + ` ` && + ` LH` && + ` 400` && + ` ` && + ` Frankfurt` && + ` FRA` && + ` ` && + ` ` && + ` ` && + ` Berlin` && + ` SXF` && + ` ` && + ` ` && + ` ` && + ` ` && + ` DL` && + ` 1984` && + ` ` && + ` San Francisco` && + ` SFO` && + ` ` && + ` ` && + ` ` && + ` New York` && + ` JFK` && + ` ` && + ` ` && + ` ` && + ``. + + "string -> xstring + "Note: UTF-8 is used by default. Here, it is specified explicitly. + "Conversion errors are caught using the cx_sy_conversion_codepage class. + TRY. + DATA(conv_xstring) = cl_abap_conv_codepage=>create_out( codepage = `UTF-8` )->convert( xml_string ). + CATCH cx_sy_conversion_codepage. + ENDTRY. + + "xstring -> string + DATA(conv_string) = cl_abap_conv_codepage=>create_in( )->convert( conv_xstring ). + + "As an alternative, you can use methods of the XCO library. + "string -> xstring + DATA(conv_xstring_xco) = xco_cp=>string( xml_string + )->as_xstring( xco_cp_character=>code_page->utf_8 + )->value. + + "xstring -> string + DATA(conv_string_xco) = xco_cp=>xstring( conv_xstring_xco + )->as_string( xco_cp_character=>code_page->utf_8 + )->value. + + conv_string = format( conv_string ). + conv_string_xco = format( conv_string_xco ). + out->write( |\n| ). + out->write( `Results of the xstring to string conversions:` ). + out->write( |\n| ). + out->write( conv_string ). + out->write( |\n| ). + out->write( conv_string_xco ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `2) Processing XML Using Class Libraries` ) ). + out->write( |Excursion: Usable iXML/sXML Classes and Interfaces\n\n| ). + "Using a released CDS view, classes and interfaces are retrieved that + "have 'ixml' and 'sxml' in the name for you to explore the released classes + "and interfaces in this context. + + SELECT ReleasedObjectType, ReleasedObjectName, ReleaseState + FROM i_apisforclouddevelopment + WHERE releasestate = 'RELEASED' + AND + ( ReleasedObjectType = 'CLAS' OR ReleasedObjectType = 'INTF' ) + AND + ( ReleasedObjectName LIKE '%IXML%' OR ReleasedObjectName LIKE '%SXML%' ) + INTO TABLE @DATA(released_xml_libs). + + out->write( `No output. You can check the internal table content in the debugger to view the usable artifacts.` ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `3) Creating XML Data Using iXML` ) ). + "In the following example, XML data is created using the iXML library. + "This is done by creating DOM nodes step by step. The nodes are created as + "elements and attributes. Content is inserted into the XML data. + "The example uses an appropriate attribute setting so that the result + "represents XML data in asXML format. + "Note: You can check out more examples in the system by choosing CTRL + + "SHIFT + A in ADT and search for *demo_ixml*. + "For more information on the classes/methods used, check the documentation + "by choosing F2 in ADT when placing the cursor on a class/methods. + + "Creating one factory object of the access class cl_ixml_core using the + "create method. The object is used to access the iXML library. + DATA(ixml_cr) = cl_ixml_core=>create( ). + + "Creating a document + DATA(document_cr) = ixml_cr->create_document( ). + + "Step-by-step creation of DOM nodes + "Creating a root node + DATA(root) = document_cr->create_element_ns( name = 'abap' + prefix = 'asx' ). + root->set_attribute_ns( name = 'asx' + prefix = 'xmlns' + value = 'http://www.sap.com/abapxml' ). + root->set_attribute_ns( name = 'version' + value = '1.0' ). + document_cr->append_child( root ). + DATA(xml_node1) = document_cr->create_element_ns( prefix = 'asx' + name = 'values' ). + root->append_child( xml_node1 ). + DATA(xml_node2) = document_cr->create_element_ns( name = 'STRING' ). + xml_node1->append_child( xml_node2 ). + xml_node2->append_child( document_cr->create_text( 'Hello ABAP' ) ). + + "Creating a renderer (for rendering the XML document into the output stream) + DATA xml_doc TYPE xstring. + ixml_cr->create_renderer( document = document_cr + ostream = ixml_cr->create_stream_factory( )->create_ostream_xstring( string = xml_doc ) + )->render( ). + + "Getting XML data + DATA(xml_output) = format( cl_abap_conv_codepage=>create_in( )->convert( xml_doc ) ). + out->write( xml_output ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `4) Parsing XML Data Using iXML` ) ). + "The example covers the following aspects: + "- Parsing XML data to a DOM object in one go + "- Directly reading nodes using various iXML methods + "- Directly reading nodes using element names + "- Reading using iterators, i.e. going over the XML nodes one after another + "- During the iteration, ... + " ... node properties are extracted using various iXML methods + " (and stored in an internal table for display purposes). + " ... the XML data is modified. + " ... a new element is created. + "- Rendering XML data + + "Internal table to store node properties for display purposes + DATA properties TYPE string_table. + + "Creating simple demo XML data to be used in the example + TRY. + DATA(some_xml) = cl_abap_conv_codepage=>create_out( )->convert( + `` && + ` hallo` && + ` how` && + ` are` && + `` ). + CATCH cx_sy_conversion_codepage. + ENDTRY. + + "Creating one factory object of the access class cl_ixml_core using the + "create method. It is used to access the iXML library. + DATA(ixml_pa) = cl_ixml_core=>create( ). + "Creaing an input stream that is used for the input of XML data + DATA(stream_factory_pa) = ixml_pa->create_stream_factory( ). + "Creating an XML document stored in DOM format in the memory + DATA(document_pa) = ixml_pa->create_document( ). + "Creating a parser + "It requires the following input parameters: input stream to be parsed, + "the XML document to which the stream is parsed, a factory required to create a stream + DATA(parser_pa) = ixml_pa->create_parser( + istream = stream_factory_pa->create_istream_xstring( string = some_xml ) + document = document_pa + stream_factory = stream_factory_pa ). + + "Parsing XML data to a DOM representation in one go. It is put in the memory. + "Note: You can also parse sequentially, and not in one go. + DATA(parsing_check) = parser_pa->parse( ). + IF parsing_check = 0. "Parsing was successful + + "Directly reading nodes using various iXML methods + + "Accessing the root element of the DOM. It can be used as the initial node + "for accessing subnodes. + "You can check the content of the variables in the debugger. + "Note: Multiple methods are available to further process the nodes. + DATA(root_element) = document_pa->get_root_element( ). + "First subnode + DATA(child_element) = root_element->get_first_child( ). + "Getting the value of that node + DATA(child_element_value) = child_element->get_value( ). + "Next adjacent node/getting the value + DATA(next_element_value) = child_element->get_next( )->get_value( ). + + "Directly reading nodes using element names + "The result is the first element searched for. + DATA(element_by_name) = document_pa->find_from_name( name = `word3` )->get_value( ). + "A lot more options are available such as access by attributes. + + "Reading using iterators, i.e. going over the XML nodes sequentially + + "Creating an iterator + DATA(iterator_pa) = document_pa->create_iterator( ). + DO. + "For the iteration, you can use the get_next method to process the nodes one after another. + "Note: Here, all nodes are respected. You can also create filters to go over specific nodes. + DATA(node_i) = iterator_pa->get_next( ). + IF node_i IS INITIAL. + EXIT. + ELSE. + "Extracting properties + "For display purposes, the properties are stored in an internal table. + APPEND |gid: { node_i->get_gid( ) } / type: { node_i->get_type( ) } / name: { node_i->get_name( ) } / value: { node_i->get_value( ) }| TO properties. + ENDIF. + + IF node_i->get_type( ) = if_ixml_node=>co_node_text. + "Modifying values + "Here, the values are capitalized. + node_i->set_value( to_upper( node_i->get_value( ) ) ). + ENDIF. + + "Creating a new element + IF node_i->get_value( ) = 'are'. + document_pa->create_simple_element_ns( name = 'word4' + value = 'you' + parent = node_i->get_parent( ) ). + ENDIF. + ENDDO. + + "Creating a renderer + DATA xml_pa TYPE xstring. + ixml_pa->create_renderer( document = document_pa + ostream = ixml_pa->create_stream_factory( )->create_ostream_xstring( string = xml_pa ) + )->render( ). + + "Getting XML + DATA(output_ixml_parsing) = format( cl_abap_conv_codepage=>create_in( )->convert( xml_pa ) ). + + out->write( output_ixml_parsing ). + out->write( |\n| ). + out->write( `Node properties:` ). + out->write( properties ). + ELSE. + out->write( `Parsing was not successful.` ). + ENDIF. + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `5) Creating XML Data Using sXML (Token-Based Rendering)` ) ). + "For sXML, there are specialized writer classes, such as CL_SXML_STRING_WRITE. + "Writers created with this class render XML data to a byte string. + "The XML 1.0 format and UTF-8 are used by default in the create method. + "Here, the parameters are specified explicitly. + "Note: The interface IF_SXML_WRITER contains the components that are valid + "for all readers (the abstract superclass CL_SXML_WRITER includes this + "interface as well as implementations for all readers). In the example below, + "a cast is required so as to access special methods (such as open_element). + + DATA(writer) = CAST if_sxml_writer( cl_sxml_string_writer=>create( type = if_sxml=>co_xt_xml10 + encoding = 'UTF-8' ) ). + + TRY. + "Creating nodes (the order of the nodes is important) + writer->open_element( name = 'flights' ). + writer->open_element( name = 'flight' ). + writer->open_element( name = 'carrier' ). + writer->write_value( 'LH' ). + writer->close_element( ). + writer->open_element( name = 'flightnumber' ). + writer->write_value( '400' ). + writer->close_element( ). + writer->close_element( ). + writer->open_element( name = 'flight' ). + writer->open_element( name = 'carrier' ). + writer->write_value( 'DL' ). + writer->close_element( ). + writer->open_element( name = 'flightnumber' ). + writer->write_value( '1984' ). + writer->close_element( ). + writer->close_element( ). + writer->close_element( ). + CATCH cx_sxml_state_error INTO DATA(error_token). + out->write( error_token->get_text( ) ). + ENDTRY. + + "Getting XML data + "The XML data can be retrieved with the GET_OUTPUT method. + "Also here, a cast is required. The result is of type xstring. + DATA(xml_sxml) = CAST cl_sxml_string_writer( writer )->get_output( ). + + DATA(output_sxml_token_rendering) = format( cl_abap_conv_codepage=>create_in( )->convert( xml_sxml ) ). + out->write( output_sxml_token_rendering ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `6) Creating XML Data using sXML (Object-Oriented Rendering)` ) ). + + DATA(writer_oo) = CAST if_sxml_writer( cl_sxml_string_writer=>create( type = if_sxml=>co_xt_xml10 + encoding = 'UTF-8' ) ). + + TRY. + writer_oo->write_node( writer_oo->new_open_element( name = 'flights' ) ). + + writer_oo->write_node( writer_oo->new_open_element( name = 'flight' ) ). + writer_oo->write_node( writer_oo->new_open_element( name = 'carrier' ) ). + DATA(val) = writer_oo->new_value( ). + val->set_value( 'AZ' ). + writer_oo->write_node( val ). + writer_oo->write_node( writer_oo->new_close_element( ) ). + writer_oo->write_node( writer_oo->new_open_element( name = 'flightnumber' ) ). + val = writer_oo->new_value( ). + val->set_value( '788' ). + writer_oo->write_node( val ). + writer_oo->write_node( writer_oo->new_close_element( ) ). + writer_oo->write_node( writer_oo->new_close_element( ) ). + + writer_oo->write_node( writer_oo->new_open_element( name = 'flight' ) ). + writer_oo->write_node( writer_oo->new_open_element( name = 'carrier' ) ). + val = writer_oo->new_value( ). + val->set_value( 'JL' ). + writer_oo->write_node( val ). + writer_oo->write_node( writer_oo->new_close_element( ) ). + writer_oo->write_node( writer_oo->new_open_element( name = 'flightnumber' ) ). + val = writer_oo->new_value( ). + val->set_value( '407' ). + writer_oo->write_node( val ). + writer_oo->write_node( writer_oo->new_close_element( ) ). + writer_oo->write_node( writer_oo->new_close_element( ) ). + + writer_oo->write_node( writer_oo->new_close_element( ) ). + CATCH cx_sxml_state_error INTO DATA(error_oo). + out->write( error_oo->get_text( ) ). + ENDTRY. + + DATA(xml_sxml_oo_rendering) = CAST cl_sxml_string_writer( writer_oo )->get_output( ). + + DATA(output_sxml_oo_rendering) = format( cl_abap_conv_codepage=>create_in( )->convert( xml_sxml_oo_rendering ) ). + out->write( output_sxml_oo_rendering ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `7) Parsing XML Data using sXML (Token-Based Parsing)` ) ). + + "Creating demo XML data to be used in the example + TRY. + DATA(xml_to_parse) = cl_abap_conv_codepage=>create_out( )->convert( + `` && + `` && + ` ` && + ` A` && + ` 01-01-2024` && + ` ` && + ` ` && + ` abc` && + ` def` && + ` ghi` && + ` jkl` && + ` ` && + `` ). + CATCH cx_sy_conversion_codepage. + ENDTRY. + + "Creating an internal table for display purposes + DATA: BEGIN OF node_info, + node_type TYPE string, + prefix TYPE string, + name TYPE string, + nsuri TYPE string, + value_type TYPE string, + value TYPE string, + value_raw TYPE xstring, + END OF node_info, + nodes_tab LIKE TABLE OF node_info. + + "Creating reader + "Note: See the comments for the writer above which is similar. For readers, + "the interface IF_SXML_READER exists. In this example, no special methods + "are used. Therefore, a cast is not carried out. + DATA(reader) = cl_sxml_string_reader=>create( xml_to_parse ). + "DATA(reader_cast) = CAST if_sxml_reader( cl_sxml_string_reader=>create( xml_oo ) ). + + "To iterate accros all nodes, you can call the NEXT_NODE method. + TRY. + DO. + "Check out other available methods in ADT by placing the cursor behind -> + "and choosing CTRL + Space. + reader->next_node( ). + + "When reaching the end of the XML data, the loop is exited. + IF reader->node_type = if_sxml_node=>co_nt_final. + EXIT. + ENDIF. + + "You can access the properties of the node directly. + "For display purposes, the property information is stored in an internal table. + "The demo XML data that is used here does not include all properties. Therefore, + "the values for these are initial. + + "Node type, see the interface if_sxml_node + DATA(node_type) = SWITCH #( reader->node_type WHEN if_sxml_node=>co_nt_initial THEN `CO_NT_INITIAL` + WHEN if_sxml_node=>co_nt_element_open THEN `CO_NT_ELEMENT_OPEN` + WHEN if_sxml_node=>co_nt_element_close THEN `CO_NT_ELEMENT_CLOSE` + WHEN if_sxml_node=>co_nt_value THEN `CO_NT_VALUE` + WHEN if_sxml_node=>co_nt_attribute THEN `CO_NT_ATTRIBUTE` + ELSE `Error` ). + + DATA(prefix) = reader->prefix. "Namespace prefix + DATA(name) = reader->name. "Name of the element + DATA(nsuri) = reader->nsuri. "Namespace URI + + "Value type, see the interface if_sxml_value + DATA(value_type) = SWITCH #( reader->value_type WHEN 0 THEN `Initial` + WHEN if_sxml_value=>co_vt_none THEN `CO_VT_NONE` + WHEN if_sxml_value=>co_vt_text THEN `CO_VT_TEXT` + WHEN if_sxml_value=>co_vt_raw THEN `CO_VT_RAW` + WHEN if_sxml_value=>co_vt_any THEN `CO_VT_ANY` + ELSE `Error` ). + + DATA(value) = reader->value. "Character-like value (if it is textual data) + DATA(value_raw) = reader->value_raw. "Byte-like value (if it is raw data) + + APPEND VALUE #( node_type = node_type + prefix = prefix + name = name + nsuri = nsuri + value_type = value_type + value = value + value_raw = value_raw ) TO nodes_tab. + + "Once the method is called, you can directly access the attributes of the reader with the required + "properties of the node. When the parser is on the node of an element opening, you can use the method + "NEXT_ATTRIBUTE to iterate across the XML element attributes. + IF reader->node_type = if_sxml_node=>co_nt_element_open. + DO. + reader->next_attribute( ). + IF reader->node_type <> if_sxml_node=>co_nt_attribute. + EXIT. + ENDIF. + APPEND VALUE #( node_type = `attribute` + prefix = reader->prefix + name = reader->name + nsuri = reader->nsuri + value = reader->value + value_raw = reader->value_raw ) TO nodes_tab. + ENDDO. + ENDIF. + ENDDO. + CATCH cx_sxml_state_error INTO DATA(error_parse_token). + out->write( error_parse_token->get_text( ) ). + ENDTRY. + + out->write( `Node properties:` ). + out->write( nodes_tab ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `8) Parsing XML Data using sXML (Object-Oriented Parsing)` ) ). + + CLEAR nodes_tab. + DATA(reader_oo) = cl_sxml_string_reader=>create( xml_to_parse ). + + TRY. + DO. + "To iterate accros all nodes, you can call the READ_NEXT_NODE method. + "When the end of the XML data is reached, the returned value is initial. + DATA(node_oo) = reader_oo->read_next_node( ). + IF node_oo IS INITIAL. + EXIT. + ENDIF. + + "In object-oriented parsing, methods for token-based parsing are wrapped. + "An object-oriented access to the node is provided. + "References to objects that represent the current node are returned. + + "Getting the node type + DATA(n_type) = node_oo->type. + + "If the parser is currently on the node of an element opening, + "the node object has the class CL_SXML_OPEN_ELEMENT that implements the + "interface IF_SXML_OPEN_ELEMENT. With the methods included, you can + "access the XML attributes of the element, e.g. using the GET_ATTRIBUTES + "method to put the references for all attributes into an internal table. + "To access the attributes, a downcast is required. + + CASE n_type. + WHEN if_sxml_node=>co_nt_element_open. + DATA(open_element) = CAST if_sxml_open_element( node_oo ). + + APPEND VALUE #( node_type = `open element` + prefix = open_element->prefix + name = open_element->qname-name + nsuri = open_element->qname-namespace + ) TO nodes_tab. + + DATA(attributes) = open_element->get_attributes( ). + + LOOP AT attributes INTO DATA(attribute). + APPEND VALUE #( node_type = `attribute` + prefix = open_element->prefix + name = open_element->qname-name + nsuri = open_element->qname-namespace + value = SWITCH #( attribute->value_type WHEN if_sxml_value=>co_vt_text THEN attribute->get_value( ) ) + value_raw = SWITCH #( attribute->value_type WHEN if_sxml_value=>co_vt_raw THEN attribute->get_value_raw( ) ) + ) TO nodes_tab. + ENDLOOP. + + WHEN if_sxml_node=>co_nt_element_close. + DATA(close_element) = CAST if_sxml_close_element( node_oo ). + + APPEND VALUE #( node_type = `close element` + prefix = open_element->prefix + name = open_element->qname-name + nsuri = open_element->qname-namespace + ) TO nodes_tab. + + WHEN if_sxml_node=>co_nt_value. + DATA(value_node_oo) = CAST if_sxml_value_node( node_oo ). + + APPEND VALUE #( node_type = `value` + value = SWITCH #( value_node_oo->value_type WHEN if_sxml_value=>co_vt_text THEN value_node_oo->get_value( ) ) + value_raw = SWITCH #( value_node_oo->value_type WHEN if_sxml_value=>co_vt_raw THEN value_node_oo->get_value_raw( ) ) + ) TO nodes_tab. + + WHEN OTHERS. + APPEND VALUE #( node_type = `Error` ) TO nodes_tab. + ENDCASE. + ENDDO. + CATCH cx_sxml_state_error INTO DATA(error_parse_oo). + out->write( error_parse_oo->get_text( ) ). + ENDTRY. + + out->write( `Node properties:` ). + out->write( nodes_tab ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `XML Transformations` ) ). + + out->write( |9) Excursion: Available ABAP Cheat Sheet Transformations in the System\n\n| ). + "Excursion using the XCO library. In this example, tranformation programs are retrieved. + "A filter is applied. Because of a filter that is applied, only the ABAP cheat sheet + "transformation programs are returned. + + DATA(filter) = xco_cp_abap_repository=>object_name->get_filter( + xco_cp_abap_sql=>constraint->contains_pattern( 'ZDEMO_ABAP_%' ) ). + + DATA(filtered_transformations) = xco_cp_abap_repository=>objects->xslt->where( VALUE #( ( filter ) + ) )->in( xco_cp_abap=>repository )->get( ). + + IF filtered_transformations IS NOT INITIAL. + TYPES cst TYPE TABLE OF sxco_tf_object_name WITH EMPTY KEY. + DATA(cheat_sheet_transformations) = VALUE cst( FOR tr IN filtered_transformations ( tr->name ) ). + out->write( cheat_sheet_transformations ). + out->write( |\n| ). + out->write( `The code contains an implementation that gets all the transformations in the system.` ). + out->write( `You can check the content of the variable in the debugger. Among the transformations is the predefined identity transformation ID.` ). + ENDIF. + + "Getting all transformations in the system + "You can check the table content in the debugger. + DATA(all_transformations) = xco_cp_abap_repository=>objects->xslt->all->in( xco_cp_abap=>repository )->get( ). + IF all_transformations IS NOT INITIAL. + DATA(all_transformations_in_system) = VALUE cst( FOR tr IN all_transformations ( tr->name ) ). + ENDIF. + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `10) Transforming XML to XML Using XSLT` ) ). + "In this example, XML is transformed to XML. For this purpose, a simple XSLT + "program does the following: + "- All nodes and attributes are copied from the source XML to the target XML + " without any changes. + "- A match on two elements is performed. If matched, two new elements are created + " in the target XML. In this operation, calculations are carried out (free seats + " and the occupancy rate based on the values of maximum and occupied seats). + "Note: + "- The element names in the demo XML data are capitalized on purpose because the + " XML data is used in another example that uses the asXML format. In deserializations + " of XML data to ABAP data, the elements that are deserialized must be capitalized + " so that they can be identified. + "Tranformations are performed using CALL TRANSFORMATION statements. + + "Creating demo XML data to be used in the example + TRY. + DATA(xml_flights) = cl_abap_conv_codepage=>create_out( codepage = `UTF-8` )->convert( + `` && + ` ` && + ` AA` && + ` 17` && + ` 385` && + ` 369` && + ` ` && + ` ` && + ` LH` && + ` 400` && + ` 330` && + ` 319` && + ` ` && + `` ). + CATCH cx_sy_conversion_codepage. + ENDTRY. + + DATA xml_a TYPE xstring. + + CALL TRANSFORMATION zdemo_abap_xslt_fl + SOURCE XML xml_flights + RESULT XML xml_a. + + DATA(conv_xml_a) = format( cl_abap_conv_codepage=>create_in( )->convert( xml_a ) ). + out->write( conv_xml_a ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `11) Transforming ABAP to XML Using XSLT` ) ). + "In the example, data entries are retrieved from a database table. Appropriate names for the + "table columns are used with the AS addition so that the transformation can be carried + "out based on the selected data. + "Note: The resulting XML has the asXML format. + + SELECT carrid AS carrier, + connid AS connectionid, + seatsmax AS maxSeats, + seatsocc AS occSeats + FROM zdemo_abap_fli + WHERE carrid = 'AZ' + INTO TABLE @DATA(fli_itab) + UP TO 2 ROWS. + + DATA xml_b TYPE xstring. + + CALL TRANSFORMATION zdemo_abap_xslt_fl + SOURCE flights = fli_itab + RESULT XML xml_b. + + DATA(conv_xml_b) = format( cl_abap_conv_codepage=>create_in( )->convert( xml_b ) ). + out->write( conv_xml_b ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `12) ABAP <-> XML using Simple Transformations (1)` ) ). + "The following simple transformation examples transform ABAP to XML and back. The + "Simple Transformation is implemented in a way to transform to the HTML format. + "This example transforms string tables to html. + + DATA(string_table_a) = VALUE string_table( ( `abc` ) ( `def` ) ( `ghi` ) ). + + DATA xml_c TYPE xstring. + + "ABAP -> XML + CALL TRANSFORMATION zdemo_abap_st_strhtml + SOURCE string_table = string_table_a + RESULT XML xml_c. + + DATA(conv_xml_c) = format( cl_abap_conv_codepage=>create_in( )->convert( xml_c ) ). + out->write( `ABAP -> XML` ). + out->write( conv_xml_c ). + out->write( |\n| ). + + "XML -> ABAP + DATA string_table_b TYPE string_table. + CALL TRANSFORMATION zdemo_abap_st_strhtml + SOURCE XML xml_c + RESULT string_table = string_table_b. + + out->write( `XML -> ABAP` ). + out->write( string_table_b ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `13) ABAP <-> XML using Simple Transformations (2)` ) ). + "In this example, an internal table is transformed to XML using Simple Transformation. + "HTML tags are inserted into the XML data as literals. + + SELECT carrid, carrname, currcode, url + FROM zdemo_abap_carr + INTO TABLE @DATA(carr_tab_a) + UP TO 2 ROWS. + + "ABAP -> XML + DATA str_a TYPE string. + + "The following CALL TRANSFORMATION statement includes the OPTIONS + "addition. In this example, the XML header should not be added. + "So, you can take the resulting html and test it in an HTML viewer. + + CALL TRANSFORMATION zdemo_abap_st_carrhtml + SOURCE carrier_info = carr_tab_a + RESULT XML str_a + OPTIONS xml_header = 'NO'. + + out->write( `ABAP -> XML` ). + out->write( format( str_a ) ). + out->write( |\n| ). + + "XML -> ABAP + DATA carr_tab_b LIKE carr_tab_a. + + CALL TRANSFORMATION zdemo_abap_st_carrhtml + SOURCE XML str_a + RESULT carrier_info = carr_tab_b. + + out->write( `XML -> ABAP` ). + out->write( carr_tab_b ). + +*********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `ABAP <-> XML using XSLT (Using the Predefined Identity Transformation ID)` ) ). + "The following examples demonstrate serializations (ABAP to XML) and deserializations (XML to ABAP) + "using the predefined identity transformation ID. + "Note: In doing so, ABAP data is transformed to their asXML representations that can be used as an + " intermediate format and which define a mapping between ABAP data and XML. + + out->write( |14) Elementary type\n\n| ). + "The example uses type string as an elementary type. + + "ABAP -> XML + DATA xml_d TYPE xstring. + DATA(str_b) = `This is some string.`. + + CALL TRANSFORMATION id SOURCE txt = str_b + RESULT XML xml_d. + + DATA(conv_xml_d) = cl_abap_conv_codepage=>create_in( )->convert( xml_d ). + out->write( `ABAP -> XML` ). + out->write( format( conv_xml_d ) ). + out->write( |\n| ). + + "XML -> ABAP + DATA str_c TYPE string. + CALL TRANSFORMATION id SOURCE XML xml_d + RESULT txt = str_c. + + out->write( `XML -> ABAP` ). + out->write( str_c ). + +************************************************************************ + + out->write( zcl_demo_abap_aux=>heading( `15) Structures` ) ). + + SELECT SINGLE carrid, carrname, currcode, url + FROM zdemo_abap_carr + WHERE carrid = 'LH' + INTO @DATA(carr_struc_a). + + "ABAP -> XML + DATA xml_e TYPE xstring. + CALL TRANSFORMATION id SOURCE structure = carr_struc_a + RESULT XML xml_e. + + DATA(conv_xml_e) = cl_abap_conv_codepage=>create_in( )->convert( xml_e ). + out->write( `ABAP -> XML` ). + out->write( format( conv_xml_e ) ). + out->write( |\n| ). + + "XML -> ABAP + DATA carr_struc_b LIKE carr_struc_a. + CALL TRANSFORMATION id SOURCE XML xml_e + RESULT structure = carr_struc_b. + + out->write( `XML -> ABAP` ). + out->write( carr_struc_b ). + out->write( |\n| ). + +************************************************************************ + + out->write( zcl_demo_abap_aux=>heading( `16) Internal tables` ) ). + + SELECT carrid, connid, fldate, price, currency + FROM zdemo_abap_fli + WHERE carrid = 'JL' + INTO TABLE @DATA(fli_tab_a) + UP TO 2 ROWS. + + "ABAP -> XML + DATA xml_f TYPE xstring. + CALL TRANSFORMATION id SOURCE itab = fli_tab_a + RESULT XML xml_f. + + DATA(conv_xml_f) = cl_abap_conv_codepage=>create_in( )->convert( xml_f ). + out->write( `ABAP -> XML` ). + out->write( format( conv_xml_f ) ). + out->write( |\n| ). + + "XML -> ABAP + DATA fli_tab_b LIKE fli_tab_a. + CALL TRANSFORMATION id SOURCE XML xml_f + RESULT itab = fli_tab_b. + + out->write( `XML -> ABAP` ). + out->write( fli_tab_b ). + +************************************************************************ + + out->write( zcl_demo_abap_aux=>heading( `17) Data References` ) ). + + DATA(dref_a) = NEW i( 123 ). + + "ABAP -> XML + DATA xml_g TYPE xstring. + CALL TRANSFORMATION id SOURCE dref = dref_a + RESULT XML xml_g. + + DATA(conv_xml_g) = cl_abap_conv_codepage=>create_in( )->convert( xml_g ). + + out->write( `ABAP -> XML` ). + out->write( format( conv_xml_g ) ). + out->write( |\n| ). + + "XML -> ABAP + DATA dref_b LIKE dref_a. + CALL TRANSFORMATION id SOURCE XML xml_g + RESULT dref = dref_b. + + out->write( `XML -> ABAP` ). + out->write( dref_b->* ). + +************************************************************************ + + out->write( zcl_demo_abap_aux=>heading( `18) Object References` ) ). + "The following example demonstrates the serialization and deserialization of + "instances of classes (objects). For example, to serialize instance attributes, + "classes must implement the if_serializable_object interface. By default, all instance + "attributes of an object are serialized, regardless of their visibility section. + "However, you can change this behavior using the serialize_helper and deserialize_helper + "instance methods. As a result of the transformation, you get an asXML representation of + "the object. + "The example is implemented as follows: + "- The class implements the if_serializable_object interface. + "- There are 4 instance attributes of type string. + "- There are two instance methods: + " - One method concatenates two of the strings and assigns the resulting string to + " another string. + " - Another method concatenates two strings and converts the string to lowercase. + " The result is assigned to another string. + "- Serialization preserves instance attribute values in the asXML representation of the + " object. + "- During deserialization, the instance attribute values are transformed back and can + " be accessed. + "- The example includes the implementation of the serialize_helper and deserialize_helper + " instance methods. Without implementation, all instance attributes would be + " serialized/deserialized. The sample implementation limits serialization/deserialization. + " The fourth instance attribute, which is converted to a lowercase string when calling the + " method, is not part of the serialization/deserialization. See the implementation of the + " serialize_helper and deserialize_helper instance methods. They explicitly specify what + " to serialize and deserialize. + "- Note: For each output parameter of the serialize_helper method, you must specify an + " identically-named input parameter of the deserialize_helper method. The parameters must + " have the same type. + + DATA(oref_a) = NEW zcl_demo_abap_xml_json( ). + oref_a->attr_string_a = `AB`. + oref_a->attr_string_b = `AP`. + oref_a->concatenate_string( ). + oref_a->lowercase_string( ). + + out->write( `Value of instance attribute attr_lowercase_str for the created instance (before serialization/deserialization):` ). + out->write( oref_a->attr_lowercase_str ). + out->write( |\n| ). + + "ABAP -> XML + DATA xml_oref_a TYPE xstring. + CALL TRANSFORMATION id SOURCE oref = oref_a + RESULT XML xml_oref_a. + + DATA(conv_xml_oref_a) = cl_abap_conv_codepage=>create_in( )->convert( xml_oref_a ). + + out->write( `ABAP -> XML` ). + out->write( format( conv_xml_oref_a ) ). + out->write( |\n| ). + + "XML -> ABAP + DATA oref_b LIKE oref_a. + CALL TRANSFORMATION id SOURCE XML xml_oref_a + RESULT oref = oref_b. + + out->write( `XML -> ABAP` ). + out->write( oref_b->attr_string_a ). + out->write( oref_b->attr_string_b ). + out->write( oref_b->attr_concat_string ). + out->write( |\n| ). + IF oref_b->attr_lowercase_str IS INITIAL. + out->write( `The instance attribute attr_lowercase_str is initial. The serialization/deserialization is restricted.` ). + ENDIF. + +************************************************************************ + + out->write( zcl_demo_abap_aux=>heading( `19) CALL TRANSFORMATION Syntax: Specifying Transformations` ) ). + "As already covered in the examples above, transformations are specified after + "CALL TRANSFORMATION. They are either ... + + "... XSLT programs + CALL TRANSFORMATION zdemo_abap_st_carrhtml SOURCE carrier_info = carr_tab_a + RESULT XML str_a. + + "... predefined identity transformations + CALL TRANSFORMATION id SOURCE dref = dref_a + RESULT XML xml_g. + + "... dynamically specified transformation (valid for both XSLT and ST). In the examples above, + "the transformation is specified statically. Dynamic specifications are possible. A + "character-like data object in uppercase letters is expected in parentheses (either a named + "or unnamed data objects). + CALL TRANSFORMATION ('ID') SOURCE dref = dref_a + RESULT XML xml_g. + + "If a dynamically specified transformation is not found, an exception of the class + "CX_INVALID_TRANSFORMATION is raied. The example uses a named data object. + DATA(notr) = 'NON_EXISTENT_TRANSFORMATION'. + TRY. + CALL TRANSFORMATION (notr) SOURCE dref = dref_a + RESULT XML xml_g. + CATCH cx_invalid_transformation INTO DATA(error_non). + out->write( error_non->get_text( ) ). + ENDTRY. + + "... Simple Transformation + CALL TRANSFORMATION zdemo_abap_st_carrhtml SOURCE XML str_a + RESULT carrier_info = carr_tab_b. + +************************************************************************ + + out->write( zcl_demo_abap_aux=>heading( `20) CALL TRANSFORMATION Syntax: Sources of Transformations` ) ). + "The following examples use the predefined identity transformation ID. + "The result is asXML data and stored in a variable of type xstring. + "Multiple options and variants are possible. The examples cover a selection. + + out->write( `********** Source options for transforming XML data **********` ). + out->write( |\n| ). + + "Source: XML data in a data object of type string. It is implicitly transformed to + "XML in this case. + "Note: It must have character-like XML data in XML 1.0 format. + DATA(str_d) = `Hello world`. + DATA xml_h TYPE xstring. + CALL TRANSFORMATION id SOURCE XML str_d + RESULT XML xml_h. + + DATA(conv_xml_h) = cl_abap_conv_codepage=>create_in( )->convert( xml_h ). + + out->write( `Source: XML data in data object of type string` ). + out->write( format( conv_xml_h ) ). + out->write( |\n| ). + + "Source: XML data in a data object of type xstring + DATA(xml_i) = cl_abap_conv_codepage=>create_out( )->convert( `Hi ABAP` ). + DATA xml_j TYPE xstring. + CALL TRANSFORMATION id SOURCE XML xml_i + RESULT XML xml_j. + + DATA(conv_xml_j) = cl_abap_conv_codepage=>create_in( )->convert( xml_j ). + out->write( `Source: XML data in a data object of type xstring` ). + out->write( format( conv_xml_j ) ). + out->write( |\n| ). + + "Source: Standard table with character-like data type + DATA(stdtab_clike) = VALUE c50_tab_type( ( 'Hi' ) ( 'ABAP' ) ). + DATA xml_k TYPE xstring. + CALL TRANSFORMATION id SOURCE tab = stdtab_clike + RESULT XML xml_k. + + DATA(conv_xml_k) = cl_abap_conv_codepage=>create_in( )->convert( xml_k ). + out->write( `Source: Standard table with character-like data type` ). + out->write( format( conv_xml_k ) ). + out->write( |\n| ). + + "Source: Standard table with byte-like data type + DATA(stdtab_bytelike) = VALUE x30_tab_type( ( cl_abap_conv_codepage=>create_out( )->convert( `Hello` ) ) + ( cl_abap_conv_codepage=>create_out( )->convert( `ABAP` ) ) ). + DATA xml_l TYPE xstring. + CALL TRANSFORMATION id SOURCE xtab = stdtab_bytelike + RESULT XML xml_l. + + DATA(conv_xml_l) = cl_abap_conv_codepage=>create_in( )->convert( xml_l ). + out->write( `Source: Standard table with byte-like data type` ). + out->write( format( conv_xml_l ) ). + out->write( |\n| ). + + "Furthermore, some references to iXML and sXML libraries are possible. + "The following example covers sXML (an interface reference variable of type if_sxml_reader). + DATA(sxml_reader) = cl_sxml_string_reader=>create( xml_h ). + DATA xml_m TYPE xstring. + CALL TRANSFORMATION id SOURCE XML sxml_reader + RESULT XML xml_m. + + DATA(conv_xml_m) = cl_abap_conv_codepage=>create_in( )->convert( xml_m ). + out->write( `Source: Interface reference variable with TYPE REF TO if_sxml_reader` ). + out->write( format( conv_xml_m ) ). + out->write( |\n| ). + + out->write( `********** Source options for transforming ABAP data **********` ). + out->write( |\n| ). + "Using ... SOURCE ... without specifying XML + "One or multiple ABAP data objects can be specified. + + "Source: Data object of type string + "elem stands for the name of an XML element. + DATA(str_e) = `abcdef`. + DATA xml_n TYPE xstring. + CALL TRANSFORMATION id SOURCE elem = str_e + RESULT XML xml_n. + + DATA(conv_xml_n) = cl_abap_conv_codepage=>create_in( )->convert( xml_n ). + out->write( `Source: Character string in data object of type string` ). + out->write( format( conv_xml_n ) ). + out->write( |\n| ). + + "Source: Data object of type string + DATA(str_f) = `some string`. + DATA xml_o TYPE xstring. + CALL TRANSFORMATION id SOURCE txt = str_f + RESULT XML xml_o. + + DATA(conv_xml_o) = cl_abap_conv_codepage=>create_in( )->convert( xml_o ). + out->write( `Source: Character string of type string` ). + out->write( format( conv_xml_o ) ). + out->write( |\n| ). + + "Source: Multiple data objects + DATA a_i TYPE i VALUE 123. + DATA b_str TYPE string VALUE `Hallo`. + DATA c_p TYPE p LENGTH 5 DECIMALS 2 VALUE `4.56`. + DATA xml_p TYPE xstring. + + CALL TRANSFORMATION id SOURCE x = a_i + y = b_str + z = c_p + RESULT XML xml_p. + + DATA(conv_xml_p) = cl_abap_conv_codepage=>create_in( )->convert( xml_p ). + out->write( `Source: Multiple ABAP data objects in a static parameter list` ). + out->write( format( conv_xml_p ) ). + out->write( |\n| ). + + "Source: Dynamic specification of ABAP data objects in an internal table of + "type abap_trans_srcbind_tab + DATA(srctab) = VALUE abap_trans_srcbind_tab( + ( name = 'X' value = REF #( a_i ) ) + ( name = 'Y' value = REF #( b_str ) ) + ( name = 'Z' value = REF #( c_p ) ) ). + DATA xml_q TYPE xstring. + + CALL TRANSFORMATION id SOURCE (srctab) + RESULT XML xml_q. + + DATA(conv_xml_q) = cl_abap_conv_codepage=>create_in( )->convert( xml_q ). + out->write( `Source: Multiple ABAP data objects in an internal table` ). + out->write( format( conv_xml_q ) ). + +************************************************************************* + + out->write( zcl_demo_abap_aux=>heading( `21) CALL TRANSFORMATION Syntax: Results of Transformations` ) ). + "As in the examples above, the predefined identity transformation is used here. + + "Creating demo XML data to be used in the example as the source. + DATA(demo_xml) = cl_abap_conv_codepage=>create_out( )->convert( `Hi ABAP` ). + + out->write( `********** Result options for transforming to XML data ********** ` ). + out->write( |\n| ). + + "Result: Data object of type string + "The example uses the OPTIONS addition. The XML header should not be added. + DATA str_g TYPE string. + CALL TRANSFORMATION id SOURCE XML demo_xml + RESULT XML str_g + OPTIONS xml_header = 'NO'. + + out->write( `Result: Data object of type string` ). + out->write( str_g ). + out->write( |\n| ). + + "Data object of type xstring + DATA xml_r TYPE xstring. + CALL TRANSFORMATION id SOURCE XML demo_xml + RESULT XML xml_r. + + DATA(conv_xml_r) = cl_abap_conv_codepage=>create_in( )->convert( xml_r ). + out->write( `Result: Data object of type xstring` ). + out->write( format( conv_xml_r ) ). + out->write( |\n| ). + + "Data object declared inline (e.g. DATA(a) or FINAL(b)), which has then the type xstring + CALL TRANSFORMATION id SOURCE XML demo_xml + RESULT XML DATA(xml_s). + + DATA(conv_xml_s) = cl_abap_conv_codepage=>create_in( )->convert( xml_s ). + out->write( `Result: Data object declared inline (type xstring)` ). + out->write( format( conv_xml_s ) ). + out->write( |\n| ). + + "Standard table with character-like line type + DATA stdtab_clike_b TYPE c50_tab_type. + CALL TRANSFORMATION id SOURCE XML demo_xml + RESULT XML stdtab_clike_b + OPTIONS xml_header = 'NO'. + + out->write( `Result: Standard table with character-like line type` ). + out->write( stdtab_clike_b ). + out->write( |\n| ). + + "Standard table with byte-like line type + DATA stdtab_bytelike_b TYPE x30_tab_type. + CALL TRANSFORMATION id SOURCE XML demo_xml + RESULT XML stdtab_bytelike_b. + + out->write( `Result: Standard table with byte-like line type` ). + out->write( stdtab_bytelike_b ). + out->write( |\n| ). + + "Furthermore, some references to iXML and sXML libraries are possible. + "The following example covers sXML (an object reference variable of type ref to cl_sxml_string_writer) + "Other types are possible, for example JSON writers. + DATA(writer4tr) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_xml10 ). + CALL TRANSFORMATION id SOURCE XML demo_xml + RESULT XML writer4tr. + + DATA(xml_t) = writer4tr->get_output( ). + DATA(conv_xml_t) = cl_abap_conv_codepage=>create_in( )->convert( xml_t ). + out->write( `Result: Object reference variable with type ref to cl_sxml_string_writer` ). + out->write( format( conv_xml_t ) ). + out->write( |\n| ). + + out->write( `********** Result options for transforming to ABAP data **********` ). + out->write( |\n| ). + + "Similar to above, multiple ABAP data objects can be specified as a static parameter list. + "Here, the example from above is used. The tranformation is performed the other way round. + DATA d_i LIKE a_i. + DATA e_str LIKE b_str. + DATA f_p LIKE c_p. + + CALL TRANSFORMATION id SOURCE XML xml_p + RESULT x = d_i + y = e_str + z = f_p. + + out->write( `Result: Multiple ABAP data objects in a static parameter list` ). + out->write( d_i ). + out->write( e_str ). + out->write( f_p ). + out->write( |\n| ). + + "Specifying an internal table of type abap_trans_resbind_tab + DATA g_i LIKE a_i. + DATA h_str LIKE b_str. + DATA i_p LIKE c_p. + + "Note: Only bound parts are deserialized, i.e. the result table must be + "filled accordingly. + DATA(restab) = VALUE abap_trans_resbind_tab( + ( name = 'X' value = REF #( d_i ) ) + ( name = 'Y' value = REF #( e_str ) ) + ( name = 'Z' value = REF #( f_p ) ) ). + + CALL TRANSFORMATION id SOURCE XML xml_q + RESULT (restab). + + out->write( `Result: Multiple ABAP data objects in an internal table` ). + out->write( restab ). + +************************************************************************ + + out->write( zcl_demo_abap_aux=>heading( `22) Dealing with JSON Data` ) ). + "Note: When the identity transformation ID is used, the format is asJSON. + + "Elementary type + DATA str_h TYPE string VALUE `Hello`. + "ABAP -> JSON + "Creating a JSON writer + DATA(json_wr_a) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ). + + CALL TRANSFORMATION id SOURCE hi = str_h + RESULT XML json_wr_a. + + DATA(json_a) = cl_abap_conv_codepage=>create_in( )->convert( json_wr_a->get_output( ) ). + + out->write( `ABAP -> JSON: Elementary type` ). + out->write( json_a ). + out->write( |\n| ). + + "JSON -> ABAP + DATA str_i TYPE string. + "Note: CALL TRANSFORMATION handles JSON sources implicitly. + CALL TRANSFORMATION id SOURCE XML json_a + RESULT hi = str_i. + + out->write( `JSON -> ABAP: Elementary type` ). + out->write( str_i ). + out->write( |\n| ). + + "Structure + SELECT SINGLE carrid, carrname, currcode, url + FROM zdemo_abap_carr + WHERE carrid = 'AZ' + INTO @DATA(carr_struc_c). + + "ABAP -> JSON + DATA(json_wr_b) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ). + + CALL TRANSFORMATION id SOURCE structure = carr_struc_c + RESULT XML json_wr_b. + + DATA(json_b) = cl_abap_conv_codepage=>create_in( )->convert( json_wr_b->get_output( ) ). + out->write( `ABAP -> JSON: Structure` ). + out->write( json_b ). + out->write( |\n| ). + + "JSON -> ABAP + DATA carr_struc_d LIKE carr_struc_c. + CALL TRANSFORMATION id SOURCE XML json_b + RESULT structure = carr_struc_d. + + out->write( `JSON -> ABAP: Structure` ). + out->write( carr_struc_d ). + out->write( |\n| ). + + "Internal table + SELECT carrid, carrname, currcode, url + FROM zdemo_abap_carr + INTO TABLE @DATA(carr_tab_c) + UP TO 2 ROWS. + + "ABAP -> JSON + "This examples uses a cast to get access to further methods. + DATA(json_wr_c) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ). + DATA(json_wr_cast) = CAST if_sxml_writer( json_wr_c ). + + "With the following method calls, the result is formatted. + json_wr_cast->set_option( option = if_sxml_writer=>co_opt_linebreaks ). + json_wr_cast->set_option( option = if_sxml_writer=>co_opt_indent ). + + CALL TRANSFORMATION id SOURCE itab = carr_tab_c + RESULT XML json_wr_c. + + DATA(json_c) = cl_abap_conv_codepage=>create_in( )->convert( json_wr_c->get_output( ) ). + out->write( `ABAP -> JSON: Internal table` ). + out->write( json_c ). + out->write( |\n| ). + + "JSON -> ABAP + DATA carr_tab_d LIKE carr_tab_c. + + CALL TRANSFORMATION id SOURCE XML json_c + RESULT itab = carr_tab_d. + + out->write( `JSON -> ABAP: Internal table` ). + out->write( carr_tab_d ). + out->write( |\n| ). + + "JSON -> XML + DATA(str_j) = + `{` && + `"flights": [` && + ` {` && + ` "carrier": "LH",` && + ` "connectionid": "400",` && + ` "from": "Frankfurt",` && + ` "to": "Berlin"` && + ` },` && + ` {` && + ` "carrier": "DL",` && + ` "connectionid": "400",` && + ` "from": "San Francisco",` && + ` "to": "New York"` && + ` }` && + `]` && + `}`. + + DATA(json_d) = cl_abap_conv_codepage=>create_out( )->convert( str_j ). + DATA(json_wr_d) = cl_sxml_string_reader=>create( json_d ). + DATA json2xml_ct TYPE xstring. + + "JSON -> XML using CALL TRANSFORMATION + CALL TRANSFORMATION id SOURCE XML json_wr_d + RESULT XML json2xml_ct. + + DATA(conv_json2xml_ct) = cl_abap_conv_codepage=>create_in( )->convert( json2xml_ct ). + + out->write( `JSON -> XML using CALL TRANSFORMATION` ). + out->write( format( conv_json2xml_ct ) ). + out->write( |\n| ). + + "JSON -> XML using sXML + DATA(reader_a) = cl_sxml_string_reader=>create( cl_abap_conv_codepage=>create_out( + )->convert( str_j ) ). + "XML writer (note the type specification in contrast to the previous examples) + DATA(xml_wr) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_xml10 ). + + TRY. + reader_a->next_node( ). + "The reader parses the data in one go by calling the SKIP_NODE method. + "The data is passed to an XML writer. + reader_a->skip_node( xml_wr ). + CATCH cx_sxml_parse_error INTO DATA(err_pa). + out->write( err_pa->get_text( ) ). + ENDTRY. + + DATA(json2xml_sxml) = cl_abap_conv_codepage=>create_in( )->convert( xml_wr->get_output( ) ). + out->write( `JSON -> XML using sXML` ). + out->write( format( json2xml_sxml ) ). + out->write( |\n| ). + + "XML -> JSON using CALL TRANSFORMATION + DATA(json_wr_f) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ). + + CALL TRANSFORMATION id SOURCE XML json2xml_sxml + RESULT XML json_wr_f. + + DATA(xml2json_ct) = cl_abap_conv_codepage=>create_in( )->convert( json_wr_f->get_output( ) ). + out->write( `XML -> JSON using CALL TRANSFORMATION` ). + out->write( xml2json_ct ). + out->write( |\n| ). + + "XML -> JSON using sXML + DATA(reader_b) = cl_sxml_string_reader=>create( cl_abap_conv_codepage=>create_out( )->convert( xml2json_ct ) ). + DATA(json_wr) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ). + + TRY. + reader_b->next_node( ). + reader_b->skip_node( json_wr ). + CATCH cx_sxml_parse_error INTO DATA(err_xj). + out->write( err_xj->get_text( ) ). + ENDTRY. + + DATA(xml2json_sxml) = cl_abap_conv_codepage=>create_in( )->convert( json_wr->get_output( ) ). + out->write( `XML -> JSON using sXML` ). + out->write( xml2json_sxml ). + +************************************************************************ + + out->write( zcl_demo_abap_aux=>heading( `23) XCO Classes for JSON` ) ). + "Note: Unlike above, the following snippets do not work with asJSON as intermediate + "format. + + DATA: BEGIN OF carrier_struc, + carrier_id TYPE c length 3, + connection_id TYPE n length 4, + city_from TYPE c length 20, + city_to TYPE c length 20, + END OF carrier_struc. + + DATA carriers_tab like TABLE OF carrier_struc WITH EMPTY KEY. + + carrier_struc = VALUE #( carrier_id = 'AA' connection_id = '17' city_from = 'New York' city_to = 'San Francisco' ). + carriers_tab = VALUE #( ( carrier_id = 'AZ' connection_id = '788' city_from = 'Rome' city_to = 'Tokyo' ) + ( carrier_id = 'JL' connection_id = '408' city_from = 'Frankfurt' city_to = 'Tokyo' ) + ( carrier_id = 'LH' connection_id = '2402' city_from = 'Frankfurt' city_to = 'Berlin' ) ). + + "ABAP (structure) -> JSON using XCO + DATA(struc2json_xco) = xco_cp_json=>data->from_abap( carrier_struc )->to_string( ). + out->write( `ABAP (structure) -> JSON using XCO` ). + out->write( format( input = struc2json_xco xml = abap_false ) ). + out->write( |\n| ). + + "ABAP (internal table) -> JSON using XCO + DATA(itab2json_xco) = xco_cp_json=>data->from_abap( carriers_tab )->to_string( ). + out->write( `ABAP (internal table) -> JSON using XCO` ). + out->write( format( input = itab2json_xco xml = abap_false ) ). + out->write( |\n| ). + + "JSON -> ABAP (structure) using XCO + DATA json2struc_xco LIKE carrier_struc. + xco_cp_json=>data->from_string( struc2json_xco )->write_to( REF #( json2struc_xco ) ). + out->write( `JSON -> ABAP (structure) using XCO` ). + out->write( json2struc_xco ). + out->write( |\n| ). + + "JSON -> ABAP (internal table) using XCO + DATA json2itab_xco LIKE carriers_tab. + xco_cp_json=>data->from_string( itab2json_xco )->write_to( REF #( json2itab_xco ) ). + out->write( `JSON -> ABAP (internal table) using XCO` ). + out->write( json2itab_xco ). + out->write( |\n| ). + + "Creating JSON using XCO + "Check out more methods that offer more options to build the JSON by clicking + "CTRL + Space after '->' in ADT. + DATA(json_builder_xco) = xco_cp_json=>data->builder( ). + json_builder_xco->begin_object( + )->add_member( 'CarrierId' )->add_string( 'DL' + )->add_member( 'ConnectionId' )->add_string( '1984' + )->add_member( 'CityFrom' )->add_string( 'San Francisco' + )->add_member( 'CityTo' )->add_string( 'New York' + )->end_object( ). + + "Getting JSON data + DATA(json_created_xco) = json_builder_xco->get_data( )->to_string( ). + + out->write( `Creating JSON using XCO` ). + out->write( format( input = json_created_xco xml = abap_false ) ). + out->write( |\n| ). + + "Transforming the created JSON to ABAP (structure) + "Note: The JSON was intentionally created without the underscores in the + "name to demonstrate the 'apply' method. The following example demonstrates + "a transformation of camel case and underscore notation. As above, check out + "more options by clicking CTRL + Space after '...transformation->'. + CLEAR json2struc_xco. + xco_cp_json=>data->from_string( json_created_xco )->apply( VALUE #( + ( xco_cp_json=>transformation->pascal_case_to_underscore ) ) )->write_to( REF #( json2struc_xco ) ). + + out->write( `JSON -> ABAP (structure) using XCO demonstrating the apply method` ). + out->write( json2struc_xco ). + +************************************************************************ + + out->write( zcl_demo_abap_aux=>heading( `24) Excursion: Compressing and Decompressing Binary Data` ) ). + "You may want to process or store binary data. The data can be very large. + "You can compress the data in gzip format and decompress it for further processing using + "the cl_abap_gzip class. Check out appropriate exceptions to be caught. The simple example + "just specifies cx_root. See the class documentation for more information. + "This example uses a data object of type xstring from a previous example. + + "Compressing binary data + DATA xstr_comp TYPE xstring. + TRY. + cl_abap_gzip=>compress_binary( EXPORTING raw_in = xml_oref_a + IMPORTING gzip_out = xstr_comp ). + CATCH cx_root INTO DATA(error_comp). + out->write( error_comp->get_text( ) ). + ENDTRY. + + "Decompressing binary data + DATA xstr_decomp TYPE xstring. + TRY. + cl_abap_gzip=>decompress_binary( EXPORTING gzip_in = xstr_comp + IMPORTING raw_out = xstr_decomp ). + + CATCH cx_root INTO DATA(error_decomp). + out->write( error_decomp->get_text( ) ). + ENDTRY. + + "Checking the xstring length of the variables used and comparing the result + DATA(strlen_original_xstring) = xstrlen( xml_oref_a ). + out->write( |Length of original binary data object: { strlen_original_xstring }| ). + DATA(strlen_comp) = xstrlen( xstr_comp ). + out->write( |Length of compressed binary data object: { strlen_comp }| ). + DATA(strlen_decomp) = xstrlen( xstr_decomp ). + out->write( |Length of decompressed binary data object: { strlen_decomp }| ). + IF xml_oref_a = xstr_decomp. + out->write( `The decompressed binary data object has the same value as the original binary data object.` ). + ENDIF. + ENDMETHOD. + METHOD format. + TRY. + DATA(xstr) = cl_abap_conv_codepage=>create_out( )->convert( input ). + DATA(reader) = cl_sxml_string_reader=>create( xstr ). + DATA(writer) = CAST if_sxml_writer( cl_sxml_string_writer=>create( + type = COND #( WHEN xml = abap_true THEN if_sxml=>co_xt_xml10 ELSE if_sxml=>co_xt_json ) ) ). + writer->set_option( option = if_sxml_writer=>co_opt_linebreaks ). + writer->set_option( option = if_sxml_writer=>co_opt_indent ). + reader->next_node( ). + reader->skip_node( writer ). + string = cl_abap_conv_codepage=>create_in( )->convert( CAST cl_sxml_string_writer( writer )->get_output( ) ). + CATCH cx_root. + string = `Issue when formatting.`. + ENDTRY. + + ENDMETHOD. + + METHOD class_constructor. + "Filling demo database tables. + zcl_demo_abap_aux=>fill_dbtabs( ). + ENDMETHOD. + + METHOD concatenate_string. + attr_concat_string = attr_string_a && attr_string_b. + ENDMETHOD. + + METHOD deserialize_helper. + me->attr_string_a = attr_string_a. + me->attr_string_b = attr_string_b. + me->attr_concat_string = attr_concat_string. + ENDMETHOD. + + METHOD serialize_helper. + attr_string_a = me->attr_string_a. + attr_string_b = me->attr_string_b. + attr_concat_string = me->attr_concat_string. + ENDMETHOD. + + METHOD lowercase_string. + attr_lowercase_str = to_lower( attr_string_a && attr_string_b ). + ENDMETHOD. + +ENDCLASS. \ No newline at end of file diff --git a/src/zcl_demo_abap_xml_json.clas.xml b/src/zcl_demo_abap_xml_json.clas.xml new file mode 100644 index 0000000..78768dd --- /dev/null +++ b/src/zcl_demo_abap_xml_json.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_XML_JSON + E + ABAP cheat sheet: Working with XML and JSON in ABAP + 1 + X + X + X + + + + diff --git a/src/zdemo_abap_abstract_ent.ddls.asddls b/src/zdemo_abap_abstract_ent.ddls.asddls new file mode 100644 index 0000000..b690e28 --- /dev/null +++ b/src/zdemo_abap_abstract_ent.ddls.asddls @@ -0,0 +1,6 @@ +@EndUserText.label: 'Demo abstract entity' +define abstract entity zdemo_abap_abstract_ent +{ + col1: abap.char(25); + col2: abap.char(25); +} diff --git a/src/zdemo_abap_abstract_ent.ddls.baseinfo b/src/zdemo_abap_abstract_ent.ddls.baseinfo new file mode 100644 index 0000000..8a07ae1 --- /dev/null +++ b/src/zdemo_abap_abstract_ent.ddls.baseinfo @@ -0,0 +1,17 @@ +{ +"BASEINFO": +{ +"FROM": +[], +"ASSOCIATED": +[], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_abstract_ent.ddls.xml b/src/zdemo_abap_abstract_ent.ddls.xml new file mode 100644 index 0000000..46ee502 --- /dev/null +++ b/src/zdemo_abap_abstract_ent.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_ABSTRACT_ENT + E + Demo abstract entity + A + + + + diff --git a/src/zdemo_abap_carr.tabl.xml b/src/zdemo_abap_carr.tabl.xml new file mode 100644 index 0000000..d5c1fd0 --- /dev/null +++ b/src/zdemo_abap_carr.tabl.xml @@ -0,0 +1,84 @@ + + + + + + ZDEMO_ABAP_CARR + E + TRANSP + X + Demo table: Airline + E + A + 1 + + + ZDEMO_ABAP_CARR + A + 0 + APPL0 + N + + + + MANDT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + CARRID + X + 0 + C + 000006 + X + CHAR + 000003 + CHAR + + + CARRNAME + 0 + C + 000040 + X + CHAR + 000020 + CHAR + + + CURRCODE + 0 + C + 000010 + X + CUKY + 000005 + CUKY + + + URL + 0 + C + 000510 + X + CHAR + 000255 + CHAR + + + + + ZDEMO_ABAP_CARR + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_carr_ve.ddls.asddls b/src/zdemo_abap_carr_ve.ddls.asddls new file mode 100644 index 0000000..42f2cc7 --- /dev/null +++ b/src/zdemo_abap_carr_ve.ddls.asddls @@ -0,0 +1,9 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +define view entity ZDEMO_ABAP_CARR_VE + as select from zdemo_abap_carr +{ + key carrid, + carrname, + currcode, + url +} diff --git a/src/zdemo_abap_carr_ve.ddls.baseinfo b/src/zdemo_abap_carr_ve.ddls.baseinfo new file mode 100644 index 0000000..71a819c --- /dev/null +++ b/src/zdemo_abap_carr_ve.ddls.baseinfo @@ -0,0 +1,19 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_CARR" +], +"ASSOCIATED": +[], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_carr_ve.ddls.xml b/src/zdemo_abap_carr_ve.ddls.xml new file mode 100644 index 0000000..f1a9a18 --- /dev/null +++ b/src/zdemo_abap_carr_ve.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_CARR_VE + E + Demo CDS view entity + W + + + + diff --git a/src/zdemo_abap_cds_ve_agg_exp.ddls.asddls b/src/zdemo_abap_cds_ve_agg_exp.ddls.asddls new file mode 100644 index 0000000..a58eac3 --- /dev/null +++ b/src/zdemo_abap_cds_ve_agg_exp.ddls.asddls @@ -0,0 +1,63 @@ +// ABAP CDS cheat sheet example: +// Aggregate expressions in the element list of CDS view entities +// +//////////////////////////------ NOTES ------////////////////////////////////// +// - CDS view entity selects from a demo database table +// - Demonstrates various aggregate expressions in the element list +// - As a prerequisite, run the class zcl_demo_abap_cds_ve to populate the +// database tables of the example. Otherwise, no data is displayed. +// +//////////////////////------ DATA PREVIEW ------/////////////////////////////// +// - Choose F8 in ADT to open the data preview and check out the data displayed +// - For comparing and checking the output, you can also open the data preview +// for the database table. In ADT, press and hold CTRL and click the database +// table name. In the opened table artifact, choose F8 to open the data preview. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'CDS view entity: Aggregate expressions' +define view entity zdemo_abap_cds_ve_agg_exp + as select from zdemo_abap_fli + +{ + + // The element list intentionally includes few fields only to focus on the effect of aggregate expressions. + key carrid, + currency, + + // -------- Aggregate expressions -------- + // - Aggregate expressions can be used as elements of an element list. Other positions are possible. + // - An alias name must be specified. + // - A GROUP BY clause is required. It must list all non-aggregated fields from the element list. + // - Additions: If ALL is used, all rows in the result set are respected. This is the default setting. + // If DISTINCT is used, only distinct values of an argument are respected. + // - Note: There may or may not be spaces between the parentheses following avg, min, etc., and the + // content specified within. + + // AVG (Returns the average value of an argument) + avg( seatsocc as abap.dec(15,2)) as avg_seats_occ, + avg( cast(paymentsum as abap.dec(15, 2)) as abap.dec(15,2)) as avg_paysum, + + // SUM (Returns the sum of an argument) + // Since a currency field is used in the example, an annotatin is required. + @Semantics.amount.currencyCode: 'currency' + sum(paymentsum) as total_paysum, + + // MIN (Returns the least value of an argument) + min( seatsocc ) as min_occ_seats, + + // MAX (Returns the greatest value of an argument) + max( seatsocc ) as max_occ_seats, + max( all seatsocc ) as max_occ_seats_all, //Same result as above, ALL is optional + + // COUNT (Returns counted lines) + count(*) as cnt, // * means that all lines are respected + count(distinct planetype) as cnt_planetype //DISTINCT means that the number of dinstinct values if an argument is counted; + //e.g. if 3 is returned, it means there are 3 different plane types among the result set + +} +//GROUP BY clause that lists all non-aggregated fields from the element list +group by + carrid, + currency diff --git a/src/zdemo_abap_cds_ve_agg_exp.ddls.baseinfo b/src/zdemo_abap_cds_ve_agg_exp.ddls.baseinfo new file mode 100644 index 0000000..cdb7267 --- /dev/null +++ b/src/zdemo_abap_cds_ve_agg_exp.ddls.baseinfo @@ -0,0 +1,19 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_FLI" +], +"ASSOCIATED": +[], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_cds_ve_agg_exp.ddls.xml b/src/zdemo_abap_cds_ve_agg_exp.ddls.xml new file mode 100644 index 0000000..32446c5 --- /dev/null +++ b/src/zdemo_abap_cds_ve_agg_exp.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_CDS_VE_AGG_EXP + E + CDS view entity: Aggregate expressions + W + + + + diff --git a/src/zdemo_abap_cds_ve_assoc.ddls.asddls b/src/zdemo_abap_cds_ve_assoc.ddls.asddls new file mode 100644 index 0000000..7274f2d --- /dev/null +++ b/src/zdemo_abap_cds_ve_assoc.ddls.asddls @@ -0,0 +1,144 @@ +// ABAP CDS cheat sheet example: Associations +// +//////////////////////////------ NOTES ------////////////////////////////////// +// - CDS view entity that selects from a demo CDS view entity and demonstrates +// associations. A selection of use cases of associations is covered. For +// more information and examples, see the ABAP Keyword Documentation. +// - As a prerequisite, run the class zcl_demo_abap_cds_ve to populate the +// database tables of the example. Otherwise, no data is displayed. +// - For further notes on associations, see the commented out information further +// down. +// - To see how the joins are realized on the database, you can right-click anywhere +// in the source code and choose 'Show SQL Create Statement'. The example +// includes associations where only left outer joins are performed. +// +//////////////////////------ DATA PREVIEW ------/////////////////////////////// +// - Choose F8 in ADT to open the data preview and check out the data displayed +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'CDS view entity: Associations 1' +define view entity zdemo_abap_cds_ve_assoc + // In this example, the data source, from which data is read, is another CDS view entity. + // This source CDS view entity selects all fields from the demo database table zdemo_abap_flsch. + // It also contains exposed associations that are used here. + as select from zdemo_abap_cds_ve_assoc_e as _flsch + + // 1) One use case of assocations, which is not covered here, can be the use in the FROM clause. + // - That is, you can use an association as data source by specifying a path expression. + // - For example, the CDS view entity zdemo_abap_cds_ve_assoc_e exposes an association + // _carr_exp. Therefore, a FROM clause as follows is possible: + // ... as select from zdemo_abap_cds_ve_assoc_e._carr_exp as _flsch ... + + // 2) Adding fields of an association target to the element list (of the same CDS view entity) + // - In this case, since data is requested, a join is carried out. + // - The data preview (choose F8) shows the values for the added fields. + // - In this example, the association is used, but not exposed. Therefore, it cannot be used + // in path expressions in other CDS entities as shown in 3). + association [1..1] to zdemo_abap_carr as _carr1 on _flsch.Carrid = _carr1.carrid + association [1..1] to zdemo_abap_carr as _carr2 on _flsch.Carrid = _carr2.carrid + association [1..*] to zdemo_abap_fli as _fli on _flsch.Carrid = _fli.carrid + + // 3) Exposing associations + // - In this example, no fields of the association target are added to the element list. + // But the assocation is exposed in the element list. Therefore, it can be used + // in path expressions in other CDS entities. + // - In this case, since data is not requested, a join is not carried out. It is up to + // to the consumer (e.g. an ABAP SQL statement or other CDS view entities) to request + // fields (one field, multiple fields, all fields ...). Only then, a join is performed. + // - The data preview (choose F8) does not show any data of the association target. + // - Compare the ON condition to the ones above. For demonstration purposes, the carrid + // field is specified with an alias name in the element list. To refer to this name, + // the prefix $projection. is required. As above, you can also use the original name. + association [1..1] to zdemo_abap_carr as _carr3 on $projection.carrier = _carr3.carrid + +{ + // Including fields from the data source + // The prefix _flsch. is actually not necessary here in the example. It's intentionally included to emphasize the data source. + key _flsch.Carrid as carrier, + key _flsch.Connid as connection_id, + _flsch.Cityfrom, + _flsch.Cityto, + + // Regarding 2) Adding fields of an association target to the element list + // - Two fields are added in the element list. + // - You can make a right-click and choose 'Show SQL Create Statement' to check how the join is performed. + // - Because a left outer join is performed, the coalesce function is included for a field. + // If there are null values, the specified character literal is inserted. + // - Only these two fields are respected from the association target. + _carr1.carrname as carrier_name, + coalesce(_carr1.url, 'NULL') as carr_url, + + // Regarding 3) Exposing associations + // - The association is exposed in the element list (but no fields of the association target). + // - As demonstrated in the executable class of this cheat sheet, an ABAP SQL statement + // can request data of the association target. + // - The data preview does not include any fields from the association target. Likewise, + // the 'SQL Create Statement' does not show any join with this assocation. + _carr3, + + // 4) Using associations that are exposed in external views + // - The CDS view entity that is used as data source in the SELECT list exposes + // associations. + // - Such an association is used in a path expression here to include a field from the + // association target in the element list. In this case, this CDS view entity is + // a consumer of the association. A join is performed, as can be seen in the + // 'SQL Create Statement' view. + _flsch._carr_exp.currcode as curr_exposed, + + + // 5) Attributes of path expression + // - Attributes are specified in angle brackets after each CDS association to define + // further properties. These can be the cardinality, join type that is implemented when used, + // and filter conditions + + // Speciying filter conditions and the join type + // - Only if a join type is specified explicitly, the addition WHERE must be specified + // - The example specifies the join type. Therefore, WHERE is required. Here in the example, + // the specification can also be the one commented out below because it implicitly has + // the same effect. Here, a left outer join is performed by default. + // - The operand specified on the left side of the condition must be a field of the association target. + _carr2[left outer where $projection.carrier = 'LH'].url as fcond_url, + //_carr2[$projection.carrier = 'LH'].url as fcond_url + + // Specifying the cardinality of the current CDS association + // - The values 1: (current CDS association is declared as unique) and *: (non-unique) are possible. + // - You can use 1: to prevent a syntax warning in case of a path specified with filter conditions, for example. + // - In the example, an association is specified with to many (there can be multiple flights for a flight route). + // Without the cardinality specification 1:, a warning would be displayed ("The association _fli can modify + // the cardinality of the results set"). + // - Similar to the example above, the first line commented out below is also possible in this example. + // The second line commented out results in a syntax warning because it does not specify the + // cardinality 1:. + + _fli[1:$projection.connection_id = connid].fldate as fcond_fldate + //_fli[1:left outer where $projection.connection_id = connid].fldate as fcond_fldate + //_fli[$projection.connection_id = connid].fldate as fcond_fldate +} + +////////////////////////// --------- Notes on associations --------- ///////////////////////////////////////// +// - They offer an advanced modelling capability for CDS data models. They define relationships between +// CDS entities (association source and target). On the database, associations are internally transformed into joins. +// - Compared to regular joins, associations differ in the following respects: +// - Joins are only performed on demand, i.e. when data is requested from the assocation by a consumer, e.g. +// using another CDS view entity or an ABAP SQL statement. In the regular joins (e.g. inner, outer joins), the +// join is always performed. When a CDS association is instantiated as join on the database, the association +// source represents the left side and the association target represents the right side of the join. +// - In the SELECT list, specifying the data source as a prefix is mandatory for all fields of the association. +// For joins, it is mandatory only for for non-unique names. +// - Unlike regular joins, associations can be reused in different positions and basically replace very complex +// join expressions. +// - Use of associations: +// - Including fields from the association target in the current view +// - Exposing associations so that they can be used in other CDS entities or in ABAP SQL +// - Associations can be used in path expressions (a sequence of associations) at different operand positions. +// - Associations can be specified with additional semantic information, such as cardinality. +// - Specifying the (optional) cardinality is a means of documenting the semantics of the data model. +// - Cardinality is specified in square brackets [ ]. Minimum and maximum can be specified, for example +// one to one [1..1], one to many [1..*]. The default is [0..1]. Specifying the minimum value is optional (the +// default value is 0), i.e. [0..1] is [1], [0..*] is [*]. The minimum cannot be *, the maximum not 0. +// - Maximum values greater than 1 can lead to syntax errors or warnings. Generally, a non-matching cardinality +// usually produces a warning. +// - If the cardinality is not specified, it is to one by default [x..1]. +// - Compositions and to-parent associations are special kinds of CDS associations. See the ABAP Keyword Documentation. diff --git a/src/zdemo_abap_cds_ve_assoc.ddls.baseinfo b/src/zdemo_abap_cds_ve_assoc.ddls.baseinfo new file mode 100644 index 0000000..45720cc --- /dev/null +++ b/src/zdemo_abap_cds_ve_assoc.ddls.baseinfo @@ -0,0 +1,23 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_CDS_VE_ASSOC_E", +"ZDEMO_ABAP_CARR", +"ZDEMO_ABAP_FLI" +], +"ASSOCIATED": +[ +"ZDEMO_ABAP_CARR" +], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_cds_ve_assoc.ddls.xml b/src/zdemo_abap_cds_ve_assoc.ddls.xml new file mode 100644 index 0000000..ea8f6f5 --- /dev/null +++ b/src/zdemo_abap_cds_ve_assoc.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_CDS_VE_ASSOC + E + CDS view entity: Associations + W + + + + diff --git a/src/zdemo_abap_cds_ve_assoc_e.ddls.asddls b/src/zdemo_abap_cds_ve_assoc_e.ddls.asddls new file mode 100644 index 0000000..1d5ca27 --- /dev/null +++ b/src/zdemo_abap_cds_ve_assoc_e.ddls.asddls @@ -0,0 +1,40 @@ +// ABAP CDS cheat sheet example: Associations +// +//////////////////////////------ NOTES ------////////////////////////////////// +// - CDS view entity that selects from a demo database table. +// - The purpose of this CDS view entity is to demonstrate associations. The view +// is used as a data source in another CDS view entity. +// - As a prerequisite, run the class zcl_demo_abap_cds_ve to populate the +// database tables of the example. Otherwise, no data is displayed. +// +//////////////////////------ DATA PREVIEW ------/////////////////////////////// +// - Choose F8 in ADT to open the data preview and check out the data displayed +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'CDS view entity: Associations' +define view entity zdemo_abap_cds_ve_assoc_e + as select from zdemo_abap_flsch as _flsch + association [1..1] to zdemo_abap_carr as _carr_exp on _flsch.carrid = _carr_exp.carrid + association [1..*] to zdemo_abap_fli as _fli on _flsch.carrid = _fli.carrid and _flsch.connid = _fli.connid + +{ + key _flsch.carrid as Carrid, + key _flsch.connid as Connid, + _flsch.countryfr as Countryfr, + _flsch.cityfrom as Cityfrom, + _flsch.airpfrom as Airpfrom, + _flsch.countryto as Countryto, + _flsch.cityto as Cityto, + _flsch.airpto as Airpto, + _flsch.fltime as Fltime, + _flsch.deptime as Deptime, + _flsch.arrtime as Arrtime, + _flsch.distance as Distance, + _flsch.distid as Distid, + _flsch.fltype as Fltype, + _flsch.period as Period, + _carr_exp, + _fli +} diff --git a/src/zdemo_abap_cds_ve_assoc_e.ddls.baseinfo b/src/zdemo_abap_cds_ve_assoc_e.ddls.baseinfo new file mode 100644 index 0000000..3d11a26 --- /dev/null +++ b/src/zdemo_abap_cds_ve_assoc_e.ddls.baseinfo @@ -0,0 +1,22 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_FLSCH" +], +"ASSOCIATED": +[ +"ZDEMO_ABAP_CARR", +"ZDEMO_ABAP_FLI" +], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_cds_ve_assoc_e.ddls.xml b/src/zdemo_abap_cds_ve_assoc_e.ddls.xml new file mode 100644 index 0000000..40309a1 --- /dev/null +++ b/src/zdemo_abap_cds_ve_assoc_e.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_CDS_VE_ASSOC_E + E + CDS view entity: Associations + W + + + + diff --git a/src/zdemo_abap_cds_ve_joins.ddls.asddls b/src/zdemo_abap_cds_ve_joins.ddls.asddls new file mode 100644 index 0000000..19a0b1b --- /dev/null +++ b/src/zdemo_abap_cds_ve_joins.ddls.asddls @@ -0,0 +1,132 @@ +// ABAP CDS cheat sheet example: Joins +// +//////////////////////////------ NOTES ------////////////////////////////////// +// - CDS view entity selects from a demo database table +// - Demonstrates various joins +// - As a prerequisite, run the class zcl_demo_abap_cds_ve to populate the +// database tables of the example. Otherwise, no data is displayed. +// It is particularly needed in this case because it contains entries to +// visualize the effect of outer joins. +// - HOW TO: +// - To reduce the number of separate artifacts, this example CDS view entity +// contains the code for multiple joins, which is commented out. +// - To test out various joins, you can comment out and comment in the +// respective code sections (select the lines and choose CTRL + 7). +// - The example for inner joins is commented in by default. The relevant +// code sections are marked with "COMMENT IN/OUT ... START" and +// "COMMENT IN/OUT ... END" in both SELECT and element list +// (inner joins -> 1a / 1b). To test the left outer joins, for example, +// comment out the respective sections of the inner join and comment in +// the sections for left outer joins (2a, 2b), and so on. +// - Once done, activate the view and choose F8 to open the data preview. +// You must activate the view and choose F8 again for every change here +// because the alias names are different. +// +//////////////////////------ DATA PREVIEW ------/////////////////////////////// +// - Choose F8 in ADT to open the data preview and check out the data displayed +// - Note the hints above regarding commenting in/out of code. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'CDS view entity: Joins' +define view entity zdemo_abap_cds_ve_joins + as select from zdemo_abap_carr as _carr + + // Notes on joins: + // - Joins combine two data sources into one result set, consisting of columns of both data sources + // - Rows of the result set are determined by the join type and by join conditions between columns of the data sources + // - Joins can be nested + // - Inner or outer join must contain a join condition after ON + // - Each element of the SELECT list must have the name of its data source as prefix + // - When joins are used, a WHERE condition affects the result set + // - The cardinality can be specified for left outer joins. For more details, see the ABAP Keyword Documentation. + + /////////////////// ----- Example for INNER JOIN ----- ///////////////////////////////// + // Notes: + // - Joins all entries of the data sources whose fields match the ON condition + // - In the table zdemo_abap_carr, there are entries in carrid that are not present in zdemo_abap_flsch. + // The same is true the other way round. The result only contains those entries that exist in both data sources + // based on the ON condition. + + //// ----> COMMENT IN/OUT 1a START <----- + inner join zdemo_abap_flsch as _flsch_in on _carr.carrid = _flsch_in.carrid + //// ----> COMMENT IN/OUT 1a END <----- + + //Alternative syntax: Specifying 'inner' is optional + // join zdemo_abap_flsch + // as _flsch_in on _carr.carrid = _flsch_in.carrid + + /////////////////// ----- Example for LEFT OUTER JOIN ----- ///////////////////////////////// + // Notes: + // - The join selects all entries on the left side. Entries that match the ON condition have the same content as in the inner join. + // In entries that do not match the ON condition, the elements on the right side have the null value. + // - To demonstrate the effect, the table zdemo_abap_carr contains entries for carrid that are not present in zdemo_abap_flsch. + // - Example in the element list: + // - The coalesce function can be used to prevent null values in the result set. + // - In the example, the function is used for an element to prevent null values. + + //// ----> COMMENT IN/OUT 2A START <----- + // left outer join zdemo_abap_flsch as _flsch_lo on _carr.carrid = _flsch_lo.carrid + //// ----> COMMENT IN/OUT 2A END <----- + + /////////////////// ----- Example for RIGHT OUTER JOIN ----- ///////////////////////////////// + // Notes: + // - The join selects all entries on the right side. Entries that match the ON condition have the same content as in the inner join. + // In entries that do not match the ON condition, the elements on the left side have the null value. + // - To demonstrate the effect, the table zdemo_abap_carr contains entries for carrid that are not present in zdemo_abap_flsch. + // - Example in the element list: + // - Instead of the coalesce function, you can also use a CASE expression using a logical expression with IS [NOT] NULL + // to prevent null values in the result set. + // - In the example, a CASE expression is used for an element to prevent null values. + + //// ----> COMMENT IN/OUT 3A START <----- + // right outer join zdemo_abap_flsch as _flsch_ro on _carr.carrid = _flsch_ro.carrid + //// ----> COMMENT IN/OUT 3A END <----- + + /////////////////// ----- Example for CROSS JOIN ----- ///////////////////////////////// + // Notes: + // - All entries on the left side are combined with all entries on the right side. + // - It is not possible to specify an ON condition. + //- The number of lines of the result is the number of left lines multiplied by the number of lines of the right table. + + //// ----> COMMENT IN/OUT 4A START <----- + // cross join zdemo_abap_flsch as _flsch_cr + //// ----> COMMENT IN/OUT 4A END <----- + +{ + key _carr.carrid, + _carr.carrname, + + /////////////////// ----- Example for INNER JOIN ----- ///////////////////////////////// + + // ----> COMMENT IN/OUT 1b START <----- + _flsch_in.cityfrom as cityfr_in, + _flsch_in.cityto as cityto_in + // ----> COMMENT IN/OUT 1b END <----- + + /////////////////// ----- Example for LEFT OUTER JOIN ----- ///////////////////////////////// + + //// ----> COMMENT IN/OUT 2b START <----- + // _flsch_lo.cityfrom as cityfr_lo, + // coalesce(_flsch_lo.cityto, '???') as cityto_lo + //// ----> COMMENT IN/OUT 2b END <----- + + /////////////////// ----- Example for RIGHT OUTER JOIN ----- ///////////////////////////////// + + //// ----> COMMENT IN/OUT 3b START <----- + // case when _carr.url is not null then _carr.url + // else '!!!' + // end as url_ro, + // _flsch_ro.cityfrom as cityfr_ro, + // _flsch_ro.cityto as cityto_ro + //// ----> COMMENT IN/OUT 3b END <----- + + /////////////////// ----- Example for CROSS JOIN ----- ///////////////////////////////// + + //// ----> COMMENT IN/OUT 4b START <----- + // _flsch_cr.cityfrom as cityfr_cr, + // _flsch_cr.cityto as cityto_cr + //// ----> COMMENT IN/OUT 4b END <----- + +} diff --git a/src/zdemo_abap_cds_ve_joins.ddls.baseinfo b/src/zdemo_abap_cds_ve_joins.ddls.baseinfo new file mode 100644 index 0000000..676fa35 --- /dev/null +++ b/src/zdemo_abap_cds_ve_joins.ddls.baseinfo @@ -0,0 +1,20 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_CARR", +"ZDEMO_ABAP_FLSCH" +], +"ASSOCIATED": +[], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_cds_ve_joins.ddls.xml b/src/zdemo_abap_cds_ve_joins.ddls.xml new file mode 100644 index 0000000..6dea722 --- /dev/null +++ b/src/zdemo_abap_cds_ve_joins.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_CDS_VE_JOINS + E + CDS view entity: Joins + W + + + + diff --git a/src/zdemo_abap_cds_ve_sel.ddls.asddls b/src/zdemo_abap_cds_ve_sel.ddls.asddls new file mode 100644 index 0000000..e982878 --- /dev/null +++ b/src/zdemo_abap_cds_ve_sel.ddls.asddls @@ -0,0 +1,237 @@ +// ABAP CDS cheat sheet example: +// Operands and expressions in CDS view entities +// +//////////////////////////------ NOTES ------////////////////////////////////// +// - CDS view entity selects from a demo database table +// - Demonstrates various syntax options regarding operands and expressions +// - As a prerequisite, run the class zcl_demo_abap_cds_ve to populate the +// database tables of the example. Otherwise, no data is displayed. +// +//////////////////////------ DATA PREVIEW ------/////////////////////////////// +// - Choose F8 in ADT to open the data preview and check out the data displayed +// - The example includes parameters. Therefore, you are prompted to insert a +// value. In this example, the parameter (maximum seats in a plane) is used for +// the WHERE clause (the lower the number entered, the more entries in the result +// set). +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////------ Annotations ------/////////////////////////////// +// Annotations add metadata to a CDS object that expands the syntax options of SQL. +// There's a predefined set of SAP annotations. Their specification is optional. + +// Example for a view entity annotation (only possible in CDS view entities) +// The following annotation defines implicit access control when ABAP SQL is used +// to access the CDS view entity. Here, it is determined that no access control +// is required. +@AccessControl.authorizationCheck: #NOT_REQUIRED + +// Example for an entity annotation (annotations that can be used in all CDS entities) +// The following annotation provides a translatable short text of the CDS entity. +@EndUserText.label: 'CDS view entity: Operands/Expressions' +define view entity zdemo_abap_cds_ve_sel + with parameters + p_smax : abap.int4 //Input parameter typed with an elementary data type; + //can also be a DDIC data element + + as select from zdemo_abap_fli //Selection from a demo database table; + //an alias name can be also specified ... as _fli, for example + + // The following element list demonstrates possible elements, operands and expressions. + // Note: Many of the operands and expressions demonstrated below can occur in multiple positions. + // Refer to the ABAP Keyword Documentation for the details. +{ + // -------- Specifying fields of the data source -------- + //- Multiple fields of the data source from which to be selected are specified. + //- Field names can be prefixed with the name of the data source (or, if specified, with the alias name). + //- Alias names can be specified for the elements. Note: In case of joins (selection from multiple sources) + // all elements must be prefixed with the name of the data source. See the view entity demonstrating joins. + //- KEY defines the current element as a key element; must be placed at the beginning; in CDS entities the key + // elements are mainly used to document the semantics of the data model (Note: They do not define unique lines + // in its result with regard to the key.) + + key carrid as CarrierId, //Alias name specified + key zdemo_abap_fli.connid as Connid, //Element prefixed with data source; alias name specified + key fldate, + price, + currency, + paymentsum, + planetype, + seatsmax, + seatsocc, + + // -------- Literals -------- + // Typed literals (cover most built-in types of the ABAP Dictionary) + // As is true for all of the following elements, an alias name must be specified. + abap.int4'12345' as int4, //Typed numeric literal + abap.char'hallo' as c5, //Typed character literal + abap.dats'20240101' as date_lit, //Date + + // Note: In case of a currency or quantity field specified with a typed literal, a reference + // to a currency key or unit is mandatory, which requires an annotation. The currency example + // here uses the following annotation. The element 'currency' from above is referenced. + @Semantics.amount.currencyCode: 'currency' + abap.curr'12.34' as curr, + abap.dec'0.9' as discnt, + abap.unit'KM' as kilometers, + + // Untyped literals + 'Minutes' as fltime_ut, //Untyped character literal + 1 as num_lit, //Untyped numeric literal + + // -------- Parameters -------- + // Parameters can be specified in an operand position. + // The name of the parameter must be prefixed by $parameters. + $parameters.p_smax as param, + + // -------- Session variables -------- + // Among other sesion variables, there are built-in session variables available. + $session.user as usr, + $session.client as clnt, + $session.system_language as langu, + $session.system_date as sys_date, + $session.user_timezone as usr_time, + $session.user_date as usr_date, + + // -------- Expressions -------- + //Note: Aggregate and path expressions are covered in a separate CDS view entity. + + // -------- Cast expressions -------- + // Convert the value of operands to a specified type after as + // The examples use built-in data types only. + // Note: Regarding which conversion combinations of types are possible, see the ABAP Keyword Documentation. + // There are special conversion rules for every combination. + cast( price as abap.dec(15,2) ) as cast_curr2dec, + + // The following example uses the prefix $projection. which defines reuse expressions. + // That is, you can refer to an element defined previously in the element list of the same CDS view entity. + // Note that this is only possible in dedicated positions. One of them is a cast expression. + cast( $projection.date_lit as abap.char(8) ) as cast_dats2c, + + // -------- Arithmetic expressions -------- + seatsocc_b + seatsocc_f as occ_seats_classes, + seatsmax - seatsocc as free_seats, + 2 * 2 as mult, + 9 / 3 as div, + //Arithmetic expressions using cast and reuse expressions + cast( paymentsum as abap.dec(17,2) ) * abap.dec'0.75' as discount_1, + $projection.cast_curr2dec * $projection.discnt as discount_2, + cast( seatsocc / seatsmax * 100 as abap.dec(10, 2) ) as occupancy_rate, + + // -------- Case expressions -------- + // Simple case distinction for comparing values of operands with other operands. + // The first operand specified after THEN for which the comparison is true is + // returned as a result. No match: Result is determined by the ELSE branch. + + case currency + when 'EUR' then 'X' + when 'USD' then 'Y' + else 'Z' + end as case1, + + // You can use the ELSE NULL addition that returns the null value. + // Note: If ELSE is not specified, the null value is returned as a result. + case $projection.case1 + when 'X' then 'A' + else null + end as case2, + + // Complex case distinction (searched case) for evaluating conditions + case + when seatsmax <= 150 then 'small' + when seatsmax > 150 and seatsmax < 300 then 'middle' + when seatsmax >= 300 then 'large' + else '?' + end as case3, + + // -------- Excursion: Logical expressions -------- + // The following nonsense example using a CASE expression just visualizes the rich variety of options. + + //Comparison operators + // Boolean operators AND, OR, NOT as well as parenthesized expressions are possible. + case + when seatsmax = 385 and not ( seatsocc > 380 and seatsocc <> 379 or seatsocc <= 120 or paymentsum >= 200000 ) then 'A' + //Interval comparisons + when seatsmax between 250 and 350 and seatsocc not between 1 and 100 then 'B' + //Pattern comparisons ('%' -> wildcard character, represents any character string, '_' -> stands for any character) + when carrid like '_L' then 'C' + //Checking for null and initial value + when currency is not null or carrid is not initial then 'D' + else '?' + end as case4, + + // -------- Built-in functions -------- + // SQL functions (only a selection is covered here) + + // Numeric functions + // The example uses typed and untyped literals only as arguments. + abs( abap.int4'-1' ) as nf_abs, + ceil( abap.decfloat34'3.333' ) as nf_ceil, + floor( abap.decfloat34'3.333' ) as nf_floor, + div( 25, 5 ) as nf_div, + mod( 11, 3 ) as nf_mod, + division( 1, 3, 2 ) as nf_division, + round( abap.decfloat34'1.337', 2 ) as nf_round, + + // String functions + concat(planetype, '-#') as sf_concat, + concat_with_space(carrid, '#', 1) as sf_conc_ws, //3rd argument: number of spaces + instr(currency, 'U') as sf_instr, //Position of the first occurrence of the string from the substring in the argument (case-sensitive) + left(currency, 2) as sf_left, //String of length n starting from the left of an expression + length(planetype) as sf_len, //Number of characters in an argument ignoring trailing blanks + lower(carrid) as sf_lower, //String with a length of an argument in which all uppercase letters are transformed to lowercase letters + lpad(carrid, 5, '#') as sf_lpad, //String of a length with the right-aligned content of an argument without trailing blanks and in which leading blanks produced by the expanded string are replaced by the characters from an argument (respecting all blanks) + ltrim(planetype, 'A') as sf_ltrim, //String with the content of an argument in which all trailing blanks and leading characters are removed that match the specified character. + replace(currency, 'U', '#') as sf_repl, //String in which all instances of the second argument are replaced by the content from the third argument + replace_regexpr(PCRE => '\\d', //More optional parameters are possible; the example replaces all digits + VALUE => planetype, + WITH => '#', + RESULT_LENGTH => 10) as sf_repl_regex, + right(currency, 2) as sf_right, //String of length n starting from the right of an expression + rpad(carrid, 5, '#') as sf_rpad, //See lpad; here, right-algined content + rtrim(planetype, '0') as sf_rtrim, //See ltrim; here, from the right + substring(planetype, 4, 3) as sf_sub, //Returns a substring; second argument: position from where to start; third argument: length of the extracted substring + upper( 'abap' ) as sf_upper, //Transforms to upper case + + // Coalesce function + // Checks whether the argument contains a null value. If it contains it, it returns the value of the second argument + // Otherwise, it returns the value of the first argument. + // This example has no null values in carrid, therefore the carrid value is output. See the example view about joins. + coalesce(carrid, 'N') as coalesced, + + // Special functions + // Type conversion functions + fltp_to_dec( abap.fltp'12.34' as abap.dec(10,1) ) as fltp2dec, + + // Unit conversion + // In the following example, the number that is input as parameter is used as the value for a distance in miles. It is converted to kilometers. + @Semantics.quantity.unitOfMeasure: 'kilometers' + unit_conversion( quantity => $parameters.p_smax, + source_unit => abap.unit'MI', + target_unit => $projection.kilometers ) as converted_value, + + // Date/Time functions + // The function in the example calculates the days between to dates. The actual parameters must have the built-in data type DATS. + dats_days_between(fldate,$projection.date_lit) as days_bw1, + + // The following example is similar to the example above. Here, the function expects the type DATN. + // It also shows the use of another function (dats_to_datn) that converts the types. + // Note: Only literals can be passed to the final two parameters on_error, on_initial. + datn_days_between(dats_to_datn(fldate,'INITIAL','INITIAL') , dats_to_datn($session.user_date,'INITIAL','INITIAL')) as days_bw2, + + // The following function adds days to a date. Here, the date a week from today is calculated. + dats_add_days($session.user_date,7,'INITIAL') as in1week, + + // Time stamp functions + // Getting the current time stamp + utcl_current() as ts, + // Adding seconds to a time stamp + utcl_add_seconds($projection.ts,60) as in1minute +} +// -------- Clauses for the SELECT statement -------- +// SELECT statements of a CDS view entitiy can be specified with optional clauses +// Among them, there are WHERE (to restrict the rows of the result set), GROUP BY (grouping the result set( +// HAVING (further restriction after the GROUP BY clause) clauses and other set operators like EXCEPT, INTERSECT and UNION. +// This example uses a simple WHERE clause. It uses a condition that includes the input parameter to restrict the result set. +where seatsmax > $parameters.p_smax diff --git a/src/zdemo_abap_cds_ve_sel.ddls.baseinfo b/src/zdemo_abap_cds_ve_sel.ddls.baseinfo new file mode 100644 index 0000000..cdb7267 --- /dev/null +++ b/src/zdemo_abap_cds_ve_sel.ddls.baseinfo @@ -0,0 +1,19 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_FLI" +], +"ASSOCIATED": +[], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_cds_ve_sel.ddls.xml b/src/zdemo_abap_cds_ve_sel.ddls.xml new file mode 100644 index 0000000..aa5944c --- /dev/null +++ b/src/zdemo_abap_cds_ve_sel.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_CDS_VE_SEL + E + CDS view entity: Operands and expressions + W + + + + diff --git a/src/zdemo_abap_draft.tabl.xml b/src/zdemo_abap_draft.tabl.xml new file mode 100644 index 0000000..17acf58 --- /dev/null +++ b/src/zdemo_abap_draft.tabl.xml @@ -0,0 +1,117 @@ + + + + + + ZDEMO_ABAP_DRAFT + E + TRANSP + X + X + Draft table for RAP calculator + E + X + A + 1 + + + ZDEMO_ABAP_DRAFT + A + 0 + APPL0 + N + + + + CLIENT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + ID + X + SYSUUID_X16 + 0 + X + E + + + DRAFTUUID + X + SYSUUID_X16 + 0 + X + E + + + NUM1 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + ARITHM_OP + 0 + C + 000002 + CHAR + 000001 + CHAR + + + NUM2 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + CALC_RESULT + 0 + g + 000008 + STRG + STRG + + + CREA_DATE_TIME + TIMESTAMPL + 0 + E + + + LCHG_DATE_TIME + TIMESTAMPL + 0 + E + + + .INCLUDE + 0 + SYCH_BDL_DRAFT_ADMIN_INC + S + Standard Include for Draft Administration (BDL Syntax Check) + S + %ADMIN + + + + + ZDEMO_ABAP_DRAFT + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_fli.tabl.xml b/src/zdemo_abap_fli.tabl.xml new file mode 100644 index 0000000..1a1a285 --- /dev/null +++ b/src/zdemo_abap_fli.tabl.xml @@ -0,0 +1,182 @@ + + + + + + ZDEMO_ABAP_FLI + E + TRANSP + X + Demo table: Flight + E + A + 1 + + + ZDEMO_ABAP_FLI + A + 0 + APPL0 + N + + + + MANDT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + CARRID + X + 0 + C + 000006 + X + CHAR + 000003 + CHAR + + + CONNID + X + 0 + N + 000008 + X + NUMC + 000004 + NUMC + + + FLDATE + X + 0 + D + 000016 + X + DATS + 000008 + DATS + T + + + PRICE + 0 + P + 000008 + ZDEMO_ABAP_FLI + CURRENCY + CURR + 000015 + 000002 + CURR + + + CURRENCY + 0 + C + 000010 + X + CUKY + 000005 + CUKY + + + PLANETYPE + 0 + C + 000020 + X + CHAR + 000010 + CHAR + + + SEATSMAX + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + SEATSOCC + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + PAYMENTSUM + 0 + P + 000009 + ZDEMO_ABAP_FLI + CURRENCY + X + CURR + 000017 + 000002 + CURR + + + SEATSMAX_B + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + SEATSOCC_B + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + SEATSMAX_F + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + SEATSOCC_F + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + + + ZDEMO_ABAP_FLI + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_fli_ve.ddls.asddls b/src/zdemo_abap_fli_ve.ddls.asddls new file mode 100644 index 0000000..baab96b --- /dev/null +++ b/src/zdemo_abap_fli_ve.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +define view entity ZDEMO_ABAP_FLI_VE + as select from zdemo_abap_fli +{ + key carrid, + key connid, + key fldate, + price, + currency, + planetype, + seatsmax, + seatsocc, + paymentsum, + seatsmax_b, + seatsocc_b, + seatsmax_f, + seatsocc_f +} diff --git a/src/zdemo_abap_fli_ve.ddls.baseinfo b/src/zdemo_abap_fli_ve.ddls.baseinfo new file mode 100644 index 0000000..cdb7267 --- /dev/null +++ b/src/zdemo_abap_fli_ve.ddls.baseinfo @@ -0,0 +1,19 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_FLI" +], +"ASSOCIATED": +[], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_fli_ve.ddls.xml b/src/zdemo_abap_fli_ve.ddls.xml new file mode 100644 index 0000000..25af7ee --- /dev/null +++ b/src/zdemo_abap_fli_ve.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_FLI_VE + E + Demo CDS view entity + W + + + + diff --git a/src/zdemo_abap_flsch.tabl.xml b/src/zdemo_abap_flsch.tabl.xml new file mode 100644 index 0000000..dc1d057 --- /dev/null +++ b/src/zdemo_abap_flsch.tabl.xml @@ -0,0 +1,199 @@ + + + + + + ZDEMO_ABAP_FLSCH + E + TRANSP + X + Demo table: Flight schedule + E + X + A + 1 + + + ZDEMO_ABAP_FLSCH + A + 0 + APPL0 + N + + + + MANDT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + CARRID + X + 0 + C + 000006 + X + CHAR + 000003 + CHAR + + + CONNID + X + 0 + N + 000008 + X + NUMC + 000004 + NUMC + + + COUNTRYFR + 0 + C + 000006 + CHAR + 000003 + CHAR + + + CITYFROM + 0 + C + 000040 + X + CHAR + 000020 + CHAR + + + AIRPFROM + 0 + C + 000006 + X + CHAR + 000003 + CHAR + + + COUNTRYTO + 0 + C + 000006 + CHAR + 000003 + CHAR + + + CITYTO + 0 + C + 000040 + X + CHAR + 000020 + CHAR + + + AIRPTO + 0 + C + 000006 + X + CHAR + 000003 + CHAR + + + FLTIME + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + DEPTIME + 0 + T + 000012 + X + TIMS + 000006 + TIMS + T + + + ARRTIME + 0 + T + 000012 + X + TIMS + 000006 + TIMS + T + + + DISTANCE + 0 + P + 000005 + ZDEMO_ABAP_FLSCH + DISTID + X + QUAN + 000009 + 000004 + QUAN + + + DISTID + 0 + C + 000006 + X + UNIT + 000003 + UNIT + + + FLTYPE + 0 + C + 000002 + X + CHAR + 000001 + CHAR + + + PERIOD + 0 + X + 000001 + X + INT1 + 000003 + INT1 + + + + + ZDEMO_ABAP_FLSCH + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_flsch_ve.ddls.asddls b/src/zdemo_abap_flsch_ve.ddls.asddls new file mode 100644 index 0000000..522a46d --- /dev/null +++ b/src/zdemo_abap_flsch_ve.ddls.asddls @@ -0,0 +1,20 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +define view entity ZDEMO_ABAP_FLSCH_VE + as select from zdemo_abap_flsch +{ + key carrid, + key connid, + countryfr, + cityfrom, + airpfrom, + countryto, + cityto, + airpto, + fltime, + deptime, + arrtime, + distance, + distid, + fltype, + period +} diff --git a/src/zdemo_abap_flsch_ve.ddls.baseinfo b/src/zdemo_abap_flsch_ve.ddls.baseinfo new file mode 100644 index 0000000..98986e5 --- /dev/null +++ b/src/zdemo_abap_flsch_ve.ddls.baseinfo @@ -0,0 +1,19 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_FLSCH" +], +"ASSOCIATED": +[], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_flsch_ve.ddls.xml b/src/zdemo_abap_flsch_ve.ddls.xml new file mode 100644 index 0000000..d1822de --- /dev/null +++ b/src/zdemo_abap_flsch_ve.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_FLSCH_VE + E + Demo CDS view entity + W + + + + diff --git a/src/zdemo_abap_get_data_itf.intf.abap b/src/zdemo_abap_get_data_itf.intf.abap new file mode 100644 index 0000000..126a553 --- /dev/null +++ b/src/zdemo_abap_get_data_itf.intf.abap @@ -0,0 +1,31 @@ +*********************************************************************** +* ---------------------------- PURPOSE -------------------------------- +* Interface to support the ABAP cheat sheet: ABAP Unit Tests +* +* ----------------------------- NOTE ---------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** +"!

    Interface for ABAP cheat sheet example

    +"! The interface supports the ABAP cheat sheet: ABAP Unit Tests. +INTERFACE zdemo_abap_get_data_itf + PUBLIC. + + TYPES: carr_tab TYPE TABLE OF zdemo_abap_fli WITH EMPTY KEY, + occ_rate TYPE p LENGTH 4 DECIMALS 2. + + METHODS: select_flight_data IMPORTING carrier TYPE zdemo_abap_fli-carrid + RETURNING VALUE(flight_data) TYPE carr_tab, + say_hello RETURNING VALUE(hi) TYPE string. + +ENDINTERFACE. diff --git a/src/zdemo_abap_get_data_itf.intf.xml b/src/zdemo_abap_get_data_itf.intf.xml new file mode 100644 index 0000000..e40e8b9 --- /dev/null +++ b/src/zdemo_abap_get_data_itf.intf.xml @@ -0,0 +1,15 @@ + + + + + + ZDEMO_ABAP_GET_DATA_ITF + E + Interface for ABAP cheat sheet example + 2 + 1 + X + + + + diff --git a/src/zdemo_abap_objects_interface.intf.abap b/src/zdemo_abap_objects_interface.intf.abap new file mode 100644 index 0000000..dc9fbb8 --- /dev/null +++ b/src/zdemo_abap_objects_interface.intf.abap @@ -0,0 +1,39 @@ +*********************************************************************** +* ---------------------------- PURPOSE -------------------------------- +* Interface to support the ABAP cheat sheet ABAP object orientation. +* +* ----------------------------- NOTE ---------------------------------- +* The code presented in this class is only meant for supporting the ABAP +* cheat sheets. It is not intended for direct use in a +* production system environment. The code examples in the ABAP cheat +* sheets are primarily intended to provide a better explanation and +* visualization of the syntax and semantics of ABAP statements and not to +* solve concrete programming tasks. For production application programs, +* a dedicated solution should therefore always be worked out for each +* individual case. There is no guarantee for either the correctness or +* the completeness of the code. In addition, there is no legal +* responsibility or liability for possible errors or their consequences +* which occur through the use of the example code. +* +*********************************************************************** +"!

    Interface for ABAP cheat sheet example

    +"! The interface supporta the ABAP cheat sheet on object orientation and demonstrates the use of interfaces. +INTERFACE zdemo_abap_objects_interface + PUBLIC. + + METHODS: double IMPORTING i_op TYPE i + RETURNING VALUE(r_double) TYPE i, + + triple DEFAULT IGNORE IMPORTING i_op TYPE i + RETURNING VALUE(r_triple) TYPE i . + + DATA in_str TYPE string. + + CLASS-METHODS: halve IMPORTING i_op TYPE i + RETURNING VALUE(r_halve) TYPE i. + + CLASS-DATA: stat_str TYPE string. + + CONSTANTS const_intf TYPE i VALUE 987. + +ENDINTERFACE. diff --git a/src/zdemo_abap_objects_interface.intf.xml b/src/zdemo_abap_objects_interface.intf.xml new file mode 100644 index 0000000..fc31bfe --- /dev/null +++ b/src/zdemo_abap_objects_interface.intf.xml @@ -0,0 +1,15 @@ + + + + + + ZDEMO_ABAP_OBJECTS_INTERFACE + E + Interface for ABAP cheat sheet example + 2 + 1 + X + + + + diff --git a/src/zdemo_abap_rap_calc_sd.srvd.srvdsrv b/src/zdemo_abap_rap_calc_sd.srvd.srvdsrv new file mode 100644 index 0000000..d94e533 --- /dev/null +++ b/src/zdemo_abap_rap_calc_sd.srvd.srvdsrv @@ -0,0 +1,4 @@ +@EndUserText.label: 'Service definition for RAP Calculator' +define service ZDEMO_ABAP_RAP_CALC_SD { + expose ZDEMO_ABAP_RAP_DRAFT_M; +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_calc_sd.srvd.xml b/src/zdemo_abap_rap_calc_sd.srvd.xml new file mode 100644 index 0000000..92217d0 --- /dev/null +++ b/src/zdemo_abap_rap_calc_sd.srvd.xml @@ -0,0 +1,19 @@ + + + + + + ZDEMO_ABAP_RAP_CALC_SD + SRVD/SRV + Service definition for RAP Calculator + EN + EN + ./zdemo_abap_rap_calc_sd/source/main + ABAP_SOURCE + ABAP Development Tools + S + Definition + + + + diff --git a/src/zdemo_abap_rap_ch_m.ddls.asddls b/src/zdemo_abap_rap_ch_m.ddls.asddls new file mode 100644 index 0000000..07c5a11 --- /dev/null +++ b/src/zdemo_abap_rap_ch_m.ddls.asddls @@ -0,0 +1,12 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +define view entity ZDEMO_ABAP_RAP_CH_M +as select from zdemo_abap_rapt2 +association to parent ZDEMO_ABAP_RAP_RO_M + as _parent on $projection.key_field = _parent.key_field +{ + _parent, + key key_field, + key key_ch, + field_ch1, + field_ch2 +} diff --git a/src/zdemo_abap_rap_ch_m.ddls.baseinfo b/src/zdemo_abap_rap_ch_m.ddls.baseinfo new file mode 100644 index 0000000..37140bc --- /dev/null +++ b/src/zdemo_abap_rap_ch_m.ddls.baseinfo @@ -0,0 +1,21 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_RAPT2" +], +"ASSOCIATED": +[ +"ZDEMO_ABAP_RAP_RO_M" +], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_ch_m.ddls.xml b/src/zdemo_abap_rap_ch_m.ddls.xml new file mode 100644 index 0000000..d9b7eca --- /dev/null +++ b/src/zdemo_abap_rap_ch_m.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_RAP_CH_M + E + CDS view entity (child) + W + + + + diff --git a/src/zdemo_abap_rap_ch_u.ddls.asddls b/src/zdemo_abap_rap_ch_u.ddls.asddls new file mode 100644 index 0000000..346ee49 --- /dev/null +++ b/src/zdemo_abap_rap_ch_u.ddls.asddls @@ -0,0 +1,12 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +define view entity ZDEMO_ABAP_RAP_CH_U +as select from zdemo_abap_rapt2 +association to parent ZDEMO_ABAP_RAP_RO_U + as _parent on $projection.key_field = _parent.key_field +{ + _parent, + key key_field, + key key_ch, + field_ch1, + field_ch2 +} diff --git a/src/zdemo_abap_rap_ch_u.ddls.baseinfo b/src/zdemo_abap_rap_ch_u.ddls.baseinfo new file mode 100644 index 0000000..e955a94 --- /dev/null +++ b/src/zdemo_abap_rap_ch_u.ddls.baseinfo @@ -0,0 +1,21 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_RAPT2" +], +"ASSOCIATED": +[ +"ZDEMO_ABAP_RAP_RO_U" +], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_ch_u.ddls.xml b/src/zdemo_abap_rap_ch_u.ddls.xml new file mode 100644 index 0000000..8d78b95 --- /dev/null +++ b/src/zdemo_abap_rap_ch_u.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_RAP_CH_U + E + CDS view entity (child) + W + + + + diff --git a/src/zdemo_abap_rap_draft_m.bdef.asbdef b/src/zdemo_abap_rap_draft_m.bdef.asbdef new file mode 100644 index 0000000..94ef029 --- /dev/null +++ b/src/zdemo_abap_rap_draft_m.bdef.asbdef @@ -0,0 +1,34 @@ +managed implementation in class zbp_demo_abap_rap_draft_m unique; +strict(2); +with draft; + +define behavior for ZDEMO_ABAP_RAP_DRAFT_M alias calc +persistent table zdemo_abap_tabca +draft table zdemo_abap_draft +lock master +total etag crea_date_time +etag master lchg_date_time +authorization master ( global ) +late numbering +{ + create; + update; + delete; + field ( readonly ) id, calc_result, crea_date_time, lchg_date_time; + field ( mandatory ) num1, num2, arithm_op; + static action delete_all; + internal action calculation; + side effects { field num1 affects field calc_result; + field num2 affects field calc_result; + field arithm_op affects field calc_result; } + validation validate on save { create; field num1, arithm_op, num2; } + determination det_modify on modify { field num1, num2, arithm_op; } + draft action Resume; + draft action Edit; + draft action Activate optimized; + draft action Discard; + draft determine action Prepare + { + validation validate; + } +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_draft_m.bdef.xml b/src/zdemo_abap_rap_draft_m.bdef.xml new file mode 100644 index 0000000..f41ce70 --- /dev/null +++ b/src/zdemo_abap_rap_draft_m.bdef.xml @@ -0,0 +1,39 @@ + + + + + + ZDEMO_ABAP_RAP_DRAFT_M + BDEF/BDO + BDEF, managed, draft, late numbering + 60 + EN + + + ./zdemo_abap_rap_draft_m/source/main/versions + http://www.sap.com/adt/relations/versions + Historic versions + + + ./zdemo_abap_rap_draft_m/source/main + http://www.sap.com/adt/relations/source + text/plain + Source Content + + + ./zdemo_abap_rap_draft_m/source/main + http://www.sap.com/adt/relations/source + text/html + Source Content (HTML) + + + EN + 5 + ./zdemo_abap_rap_draft_m/source/main + ABAP_SOURCE + true + true + + + + diff --git a/src/zdemo_abap_rap_draft_m.ddls.asddls b/src/zdemo_abap_rap_draft_m.ddls.asddls new file mode 100644 index 0000000..f7e4f08 --- /dev/null +++ b/src/zdemo_abap_rap_draft_m.ddls.asddls @@ -0,0 +1,67 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED + +@ObjectModel.semanticKey: ['id'] +@UI: { + headerInfo: { + typeName: 'Calculation', + typeNamePlural: 'Calculations', + title: { type: #STANDARD, value: 'id' } + } +} + +define root view entity ZDEMO_ABAP_RAP_DRAFT_M + as select from zdemo_abap_tabca +{ + + @UI.facet: [ + { + id: 'calc', + purpose: #STANDARD, + type: #IDENTIFICATION_REFERENCE, + label: 'Calculation', + position: 10 + }] + + @EndUserText.label: 'Calculation ID' + @UI: { lineItem: [ { importance: #HIGH, position: 10, + label: 'Calculation ID' }, + { type: #FOR_ACTION, dataAction: 'delete_all', + label: 'Delete All Persisted Calculations' } ], + fieldGroup: [ { qualifier: 'Fieldgroup:HeaderItems', + position: 10 } ] } + key id, + @UI: { lineItem: [ { importance: #HIGH, position: 20, + label: '1st Operand' } ], + identification: [ { position: 20, + label: '1st Operand' } ], + fieldGroup: [ { qualifier: 'CaluclationItems', + position: 10 } ] } + num1, + @UI: { lineItem: [ { importance: #HIGH, position: 30, + label: 'Operator' } ], + identification: [ { position: 30, label: 'Operator' } ], + fieldGroup: [ { qualifier: 'CaluclationItems', + position: 20 } ] } + arithm_op, + @UI: { lineItem: [ { importance: #HIGH, position: 40, + label: '2nd Operand' } ], + identification: [ { position: 40, + label: '2nd Operand' } ], + fieldGroup: [ { qualifier: 'CaluclationItems', + position: 30 } ] } + num2, + @UI: { lineItem: [ { importance: #HIGH, position: 50, + label: 'Result' } ], + identification: [ { position: 50, label: 'Result' } ], + fieldGroup: [ { qualifier: 'CaluclationItems', + position: 40 } ] } + calc_result, + @UI: { hidden: true } + @Semantics.systemDateTime.lastChangedAt: true + crea_date_time, + @EndUserText.label: 'Last Changed At' + @UI: { fieldGroup: [ { qualifier: 'Fieldgroup:HeaderItems', + position: 20 } ] } + @Semantics.systemDateTime.localInstanceLastChangedAt: true + lchg_date_time +} diff --git a/src/zdemo_abap_rap_draft_m.ddls.baseinfo b/src/zdemo_abap_rap_draft_m.ddls.baseinfo new file mode 100644 index 0000000..5884140 --- /dev/null +++ b/src/zdemo_abap_rap_draft_m.ddls.baseinfo @@ -0,0 +1,19 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_TABCA" +], +"ASSOCIATED": +[], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_draft_m.ddls.xml b/src/zdemo_abap_rap_draft_m.ddls.xml new file mode 100644 index 0000000..09455dc --- /dev/null +++ b/src/zdemo_abap_rap_draft_m.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_RAP_DRAFT_M + E + CDS root view entity + W + + + + diff --git a/src/zdemo_abap_rap_ro_m.bdef.asbdef b/src/zdemo_abap_rap_ro_m.bdef.asbdef new file mode 100644 index 0000000..2501e12 --- /dev/null +++ b/src/zdemo_abap_rap_ro_m.bdef.asbdef @@ -0,0 +1,30 @@ +managed implementation in class zbp_demo_abap_rap_ro_m unique; +strict(2); + +define behavior for ZDEMO_ABAP_RAP_RO_M alias root +persistent table zdemo_abap_rapt1 +lock master +authorization master ( global ) + +{ + create; + update; + delete; + association _child { create; } + action multiply_by_2; + validation val on save { field field3; } + determination det_add_text on save { create; } + field ( readonly:update ) key_field; +} + +define behavior for ZDEMO_ABAP_RAP_CH_M alias child +persistent table zdemo_abap_rapt2 +lock dependent by _parent +authorization dependent by _parent +{ + update; + delete; + field ( readonly ) key_field; + field ( readonly:update ) key_ch; + association _parent; +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_ro_m.bdef.xml b/src/zdemo_abap_rap_ro_m.bdef.xml new file mode 100644 index 0000000..7c4446e --- /dev/null +++ b/src/zdemo_abap_rap_ro_m.bdef.xml @@ -0,0 +1,39 @@ + + + + + + ZDEMO_ABAP_RAP_RO_M + BDEF/BDO + BDEF, managed, external numbering + 60 + EN + + + ./zdemo_abap_rap_ro_m/source/main/versions + http://www.sap.com/adt/relations/versions + Historic versions + + + ./zdemo_abap_rap_ro_m/source/main + http://www.sap.com/adt/relations/source + text/plain + Source Content + + + ./zdemo_abap_rap_ro_m/source/main + http://www.sap.com/adt/relations/source + text/html + Source Content (HTML) + + + EN + 5 + ./zdemo_abap_rap_ro_m/source/main + ABAP_SOURCE + true + true + + + + diff --git a/src/zdemo_abap_rap_ro_m.ddls.asddls b/src/zdemo_abap_rap_ro_m.ddls.asddls new file mode 100644 index 0000000..1a747b5 --- /dev/null +++ b/src/zdemo_abap_rap_ro_m.ddls.asddls @@ -0,0 +1,12 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +define root view entity ZDEMO_ABAP_RAP_RO_M +as select from zdemo_abap_rapt1 +composition [0..*] of ZDEMO_ABAP_RAP_CH_M as _child +{ + key key_field, + field1, + field2, + field3, + field4, + _child +} diff --git a/src/zdemo_abap_rap_ro_m.ddls.baseinfo b/src/zdemo_abap_rap_ro_m.ddls.baseinfo new file mode 100644 index 0000000..c566316 --- /dev/null +++ b/src/zdemo_abap_rap_ro_m.ddls.baseinfo @@ -0,0 +1,21 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_RAPT1" +], +"ASSOCIATED": +[ +"ZDEMO_ABAP_RAP_CH_M" +], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_ro_m.ddls.xml b/src/zdemo_abap_rap_ro_m.ddls.xml new file mode 100644 index 0000000..21c2a2d --- /dev/null +++ b/src/zdemo_abap_rap_ro_m.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_RAP_RO_M + E + CDS root view entity + W + + + + diff --git a/src/zdemo_abap_rap_ro_m_as.bdef.asbdef b/src/zdemo_abap_rap_ro_m_as.bdef.asbdef new file mode 100644 index 0000000..efe5527 --- /dev/null +++ b/src/zdemo_abap_rap_ro_m_as.bdef.asbdef @@ -0,0 +1,21 @@ +managed with additional save with full data +implementation in class zbp_demo_abap_rap_ro_m_as unique; +strict ( 2 ); + +define behavior for ZDEMO_ABAP_RAP_RO_M_AS alias root +persistent table zdemo_abap_tabca +lock master +authorization master ( global ) + +{ + create; + update; + delete; + internal action calc; + event created; + event updated parameter zdemo_abap_abstract_ent; + event deleted parameter zdemo_abap_abstract_ent; + field ( numbering : managed, readonly : update ) id; + //determination det_save on save { field calc_result; } + determination det_modify on modify { field num1, num2, arithm_op; } +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_ro_m_as.bdef.xml b/src/zdemo_abap_rap_ro_m_as.bdef.xml new file mode 100644 index 0000000..0825a43 --- /dev/null +++ b/src/zdemo_abap_rap_ro_m_as.bdef.xml @@ -0,0 +1,39 @@ + + + + + + ZDEMO_ABAP_RAP_RO_M_AS + BDEF/BDO + BDEF: managed BO, unmanaged save, external numbering + 60 + EN + + + ./zdemo_abap_rap_ro_m_as/source/main/versions + http://www.sap.com/adt/relations/versions + Historic versions + + + ./zdemo_abap_rap_ro_m_as/source/main + http://www.sap.com/adt/relations/source + text/plain + Source Content + + + ./zdemo_abap_rap_ro_m_as/source/main + http://www.sap.com/adt/relations/source + text/html + Source Content (HTML) + + + EN + 5 + ./zdemo_abap_rap_ro_m_as/source/main + ABAP_SOURCE + true + true + + + + diff --git a/src/zdemo_abap_rap_ro_m_as.ddls.asddls b/src/zdemo_abap_rap_ro_m_as.ddls.asddls new file mode 100644 index 0000000..cd59483 --- /dev/null +++ b/src/zdemo_abap_rap_ro_m_as.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +define root view entity ZDEMO_ABAP_RAP_RO_M_AS + as select from zdemo_abap_tabca +{ + key id, + num1, + arithm_op, + num2, + calc_result, + @Semantics.systemDateTime.createdAt: true + crea_date_time, + @Semantics.systemDateTime.lastChangedAt: true + lchg_date_time +} diff --git a/src/zdemo_abap_rap_ro_m_as.ddls.baseinfo b/src/zdemo_abap_rap_ro_m_as.ddls.baseinfo new file mode 100644 index 0000000..5884140 --- /dev/null +++ b/src/zdemo_abap_rap_ro_m_as.ddls.baseinfo @@ -0,0 +1,19 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_TABCA" +], +"ASSOCIATED": +[], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_ro_m_as.ddls.xml b/src/zdemo_abap_rap_ro_m_as.ddls.xml new file mode 100644 index 0000000..c52205e --- /dev/null +++ b/src/zdemo_abap_rap_ro_m_as.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_RAP_RO_M_AS + E + CDS root view entity + W + + + + diff --git a/src/zdemo_abap_rap_ro_u.bdef.asbdef b/src/zdemo_abap_rap_ro_u.bdef.asbdef new file mode 100644 index 0000000..da5f875 --- /dev/null +++ b/src/zdemo_abap_rap_ro_u.bdef.asbdef @@ -0,0 +1,26 @@ +unmanaged implementation in class zbp_demo_abap_rap_ro_u unique; +strict(2); + +define behavior for ZDEMO_ABAP_RAP_RO_U alias root +lock master +authorization master ( global, instance ) + +{ + create; + update; + delete; + association _child { create; } + action multiply_by_2; + action ( features : instance ) multiply_by_3; + action ( features : global ) set_z; + field ( readonly:update ) key_field; +} + +define behavior for ZDEMO_ABAP_RAP_CH_U alias child +lock dependent by _parent +authorization dependent by _parent +{ + field ( readonly ) key_field; + field ( readonly : update ) key_ch; + association _parent; +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_ro_u.bdef.xml b/src/zdemo_abap_rap_ro_u.bdef.xml new file mode 100644 index 0000000..4430d7a --- /dev/null +++ b/src/zdemo_abap_rap_ro_u.bdef.xml @@ -0,0 +1,39 @@ + + + + + + ZDEMO_ABAP_RAP_RO_U + BDEF/BDO + BDEF, unmanaged, external numbering + 60 + EN + + + ./zdemo_abap_rap_ro_u/source/main/versions + http://www.sap.com/adt/relations/versions + Historic versions + + + ./zdemo_abap_rap_ro_u/source/main + http://www.sap.com/adt/relations/source + text/plain + Source Content + + + ./zdemo_abap_rap_ro_u/source/main + http://www.sap.com/adt/relations/source + text/html + Source Content (HTML) + + + EN + 5 + ./zdemo_abap_rap_ro_u/source/main + ABAP_SOURCE + true + true + + + + diff --git a/src/zdemo_abap_rap_ro_u.ddls.asddls b/src/zdemo_abap_rap_ro_u.ddls.asddls new file mode 100644 index 0000000..13df7b3 --- /dev/null +++ b/src/zdemo_abap_rap_ro_u.ddls.asddls @@ -0,0 +1,12 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +define root view entity ZDEMO_ABAP_RAP_RO_U +as select from zdemo_abap_rapt1 +composition [0..*] of ZDEMO_ABAP_RAP_CH_U as _child +{ + key key_field, + field1, + field2, + field3, + field4, + _child +} diff --git a/src/zdemo_abap_rap_ro_u.ddls.baseinfo b/src/zdemo_abap_rap_ro_u.ddls.baseinfo new file mode 100644 index 0000000..536a946 --- /dev/null +++ b/src/zdemo_abap_rap_ro_u.ddls.baseinfo @@ -0,0 +1,21 @@ +{ +"BASEINFO": +{ +"FROM": +[ +"ZDEMO_ABAP_RAPT1" +], +"ASSOCIATED": +[ +"ZDEMO_ABAP_RAP_CH_U" +], +"BASE": +[], +"ANNO_REF": +[], +"SCALAR_FUNCTION": +[], +"VERSION":0, +"ANNOREF_EVALUATION_ERROR":"" +} +} \ No newline at end of file diff --git a/src/zdemo_abap_rap_ro_u.ddls.xml b/src/zdemo_abap_rap_ro_u.ddls.xml new file mode 100644 index 0000000..26e03d4 --- /dev/null +++ b/src/zdemo_abap_rap_ro_u.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_RAP_RO_U + E + CDS root view entity + W + + + + diff --git a/src/zdemo_abap_rapt1.tabl.xml b/src/zdemo_abap_rapt1.tabl.xml new file mode 100644 index 0000000..67007ba --- /dev/null +++ b/src/zdemo_abap_rapt1.tabl.xml @@ -0,0 +1,91 @@ + + + + + + ZDEMO_ABAP_RAPT1 + E + TRANSP + X + Demo table for RAP + E + X + A + 1 + + + ZDEMO_ABAP_RAPT1 + A + 0 + APPL0 + N + + + + CLIENT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + KEY_FIELD + X + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + FIELD1 + 0 + C + 000020 + CHAR + 000010 + CHAR + + + FIELD2 + 0 + C + 000020 + CHAR + 000010 + CHAR + + + FIELD3 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + FIELD4 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + + + ZDEMO_ABAP_RAPT1 + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_rapt2.tabl.xml b/src/zdemo_abap_rapt2.tabl.xml new file mode 100644 index 0000000..e4fc463 --- /dev/null +++ b/src/zdemo_abap_rapt2.tabl.xml @@ -0,0 +1,84 @@ + + + + + + ZDEMO_ABAP_RAPT2 + E + TRANSP + X + Demo table for RAP + E + X + A + 1 + + + ZDEMO_ABAP_RAPT2 + A + 0 + APPL0 + N + + + + CLIENT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + KEY_FIELD + X + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + KEY_CH + X + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + FIELD_CH1 + 0 + C + 000020 + CHAR + 000010 + CHAR + + + FIELD_CH2 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + + + ZDEMO_ABAP_RAPT2 + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_st_carrhtml.xslt.source.xml b/src/zdemo_abap_st_carrhtml.xslt.source.xml new file mode 100644 index 0000000..5c1c909 --- /dev/null +++ b/src/zdemo_abap_st_carrhtml.xslt.source.xml @@ -0,0 +1,48 @@ + + + + + + + +

    Carrier Information

    +
    + + + + + + + + + + + + + + +
    + ID + + Name + + Currency + + Website +
    + + + + + + + + + + +
    + + + + + diff --git a/src/zdemo_abap_st_carrhtml.xslt.xml b/src/zdemo_abap_st_carrhtml.xslt.xml new file mode 100644 index 0000000..c8cac34 --- /dev/null +++ b/src/zdemo_abap_st_carrhtml.xslt.xml @@ -0,0 +1,12 @@ + + + + + + ZDEMO_ABAP_ST_CARRHTML + E + ST Demo + + + + diff --git a/src/zdemo_abap_st_strhtml.xslt.source.xml b/src/zdemo_abap_st_strhtml.xslt.source.xml new file mode 100644 index 0000000..0e11f31 --- /dev/null +++ b/src/zdemo_abap_st_strhtml.xslt.source.xml @@ -0,0 +1,20 @@ + + + + + + +

    String Table Content

    + + + + + + +
    + +
    + + + + diff --git a/src/zdemo_abap_st_strhtml.xslt.xml b/src/zdemo_abap_st_strhtml.xslt.xml new file mode 100644 index 0000000..8b6c4b3 --- /dev/null +++ b/src/zdemo_abap_st_strhtml.xslt.xml @@ -0,0 +1,12 @@ + + + + + + ZDEMO_ABAP_ST_STRHTML + E + ST Demo + + + + diff --git a/src/zdemo_abap_tab1.tabl.xml b/src/zdemo_abap_tab1.tabl.xml new file mode 100644 index 0000000..bb3611f --- /dev/null +++ b/src/zdemo_abap_tab1.tabl.xml @@ -0,0 +1,91 @@ + + + + + + ZDEMO_ABAP_TAB1 + E + TRANSP + X + Demo table + E + X + A + 1 + + + ZDEMO_ABAP_TAB1 + A + 0 + APPL0 + N + + + + CLIENT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + KEY_FIELD + X + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + CHAR1 + 0 + C + 000020 + CHAR + 000010 + CHAR + + + CHAR2 + 0 + C + 000020 + CHAR + 000010 + CHAR + + + NUM1 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + NUM2 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + + + ZDEMO_ABAP_TAB1 + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_tab2.tabl.xml b/src/zdemo_abap_tab2.tabl.xml new file mode 100644 index 0000000..ef04e39 --- /dev/null +++ b/src/zdemo_abap_tab2.tabl.xml @@ -0,0 +1,82 @@ + + + + + + ZDEMO_ABAP_TAB2 + E + TRANSP + X + Demo table + E + X + A + 1 + + + ZDEMO_ABAP_TAB2 + A + 0 + APPL0 + N + + + + CLIENT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + KEY_FIELD + X + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + CHAR1 + 0 + C + 000020 + CHAR + 000010 + CHAR + + + NUM1 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + NUMLONG + 0 + 8 + 000008 + INT8 + 000019 + INT8 + + + + + ZDEMO_ABAP_TAB2 + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_tabca.tabl.xml b/src/zdemo_abap_tabca.tabl.xml new file mode 100644 index 0000000..a14546f --- /dev/null +++ b/src/zdemo_abap_tabca.tabl.xml @@ -0,0 +1,99 @@ + + + + + + ZDEMO_ABAP_TABCA + E + TRANSP + X + Demo table for RAP calculator + E + X + A + 1 + + + ZDEMO_ABAP_TABCA + A + 0 + APPL0 + N + + + + CLIENT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + ID + X + SYSUUID_X16 + 0 + X + E + + + NUM1 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + ARITHM_OP + 0 + C + 000002 + CHAR + 000001 + CHAR + + + NUM2 + 0 + X + 000004 + INT4 + 000010 + INT4 + + + CALC_RESULT + 0 + g + 000008 + STRG + STRG + + + CREA_DATE_TIME + TIMESTAMPL + 0 + E + + + LCHG_DATE_TIME + TIMESTAMPL + 0 + E + + + + + ZDEMO_ABAP_TABCA + CUS_DEV_SUP_DA + + + + + diff --git a/src/zdemo_abap_table_function.ddls.asddls b/src/zdemo_abap_table_function.ddls.asddls new file mode 100644 index 0000000..7556515 --- /dev/null +++ b/src/zdemo_abap_table_function.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@ClientHandling.type: #CLIENT_DEPENDENT +@ClientHandling.algorithm: #SESSION_VARIABLE +define table function ZDEMO_ABAP_TABLE_FUNCTION + returns + { + client : abap.clnt; + carrier_id : abap.char(3); + carrier_name : abap.char(20); + connections : abap.string; + avg_flight_time : abap.decfloat34; + avg_distance : abap.decfloat34; + } + implemented by method + zcl_demo_abap_amdp=>flight_analysis; diff --git a/src/zdemo_abap_table_function.ddls.baseinfo b/src/zdemo_abap_table_function.ddls.baseinfo new file mode 100644 index 0000000..e69de29 diff --git a/src/zdemo_abap_table_function.ddls.xml b/src/zdemo_abap_table_function.ddls.xml new file mode 100644 index 0000000..86574ef --- /dev/null +++ b/src/zdemo_abap_table_function.ddls.xml @@ -0,0 +1,13 @@ + + + + + + ZDEMO_ABAP_TABLE_FUNCTION + E + ABAP Demo: Table function + F + + + + diff --git a/src/zdemo_abap_xslt_fl.xslt.source.xml b/src/zdemo_abap_xslt_fl.xslt.source.xml new file mode 100644 index 0000000..9ad17d0 --- /dev/null +++ b/src/zdemo_abap_xslt_fl.xslt.source.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/zdemo_abap_xslt_fl.xslt.xml b/src/zdemo_abap_xslt_fl.xslt.xml new file mode 100644 index 0000000..a712e2f --- /dev/null +++ b/src/zdemo_abap_xslt_fl.xslt.xml @@ -0,0 +1,12 @@ + + + + + + ZDEMO_ABAP_XSLT_FL + E + XSLT Demo + + + +