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/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSES/Apache-2.0.txt b/LICENSES/Apache-2.0.txt new file mode 100644 index 0000000..137069b --- /dev/null +++ b/LICENSES/Apache-2.0.txt @@ -0,0 +1,73 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md index 0ea1a7b..658501d 100644 --- a/README.md +++ b/README.md @@ -1 +1,5 @@ -"initial commit" +ABAP cheat sheets for classic ABAP + +ABAP version: 757 + +More information: [https://github.com/SAP-samples/abap-cheat-sheets](https://github.com/SAP-samples/abap-cheat-sheets) \ No newline at end of file diff --git a/src/ezdemo_abap_lock.enqu.xml b/src/ezdemo_abap_lock.enqu.xml new file mode 100644 index 0000000..ae3ef5e --- /dev/null +++ b/src/ezdemo_abap_lock.enqu.xml @@ -0,0 +1,43 @@ + + + + + + EZDEMO_ABAP_LOCK + E + E + ZDEMO_ABAP_RAPT1 + Lock on demo table + + + + 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..9bd9bf7 --- /dev/null +++ b/src/package.devc.xml @@ -0,0 +1,10 @@ + + + + + + test + + + + diff --git a/src/test_abap_cheat_sheets_classic/package.devc.xml b/src/test_abap_cheat_sheets_classic/package.devc.xml new file mode 100644 index 0000000..eca76f1 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/package.devc.xml @@ -0,0 +1,10 @@ + + + + + + ABAP Cheat Sheets: Classic ABAP + + + + diff --git a/src/test_abap_cheat_sheets_classic/zcl_demo_abap_sap_luw_helper.clas.abap b/src/test_abap_cheat_sheets_classic/zcl_demo_abap_sap_luw_helper.clas.abap new file mode 100644 index 0000000..ddb7585 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zcl_demo_abap_sap_luw_helper.clas.abap @@ -0,0 +1,139 @@ +CLASS zcl_demo_abap_sap_luw_helper DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + TYPES log_type TYPE SORTED TABLE OF zdemo_abap_logt WITH UNIQUE KEY id. + + CLASS-DATA: itab TYPE STANDARD TABLE OF zdemo_abap_luw_t, + log_entries TYPE STANDARD TABLE OF zdemo_abap_logt. + + CLASS-METHODS: get_work_process_info RETURNING VALUE(id_no) TYPE string, + create_log_entries IMPORTING log TYPE log_type, + checking_transaction_state RETURNING VALUE(tx_state) TYPE string, + cc_alv_display IMPORTING container TYPE c + log TYPE abap_bool DEFAULT abap_false, + subr_delete, + subr_insert IMPORTING values TYPE zdemo_abap_luw_t, + subr_update_log IMPORTING log TYPE log_type. + PROTECTED SECTION. + PRIVATE SECTION. + CLASS-DATA: wp_pid TYPE wppid, + wp_index TYPE wpindex. +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_SAP_LUW_HELPER IMPLEMENTATION. + + + METHOD cc_alv_display. + DATA(cont) = NEW cl_gui_custom_container( container_name = container ). + + IF log = abap_false. + SELECT key_field, ch, num, time_stamp + FROM zdemo_abap_luw_t + INTO CORRESPONDING FIELDS OF TABLE @Itab + ORDER BY key_field. + + TRY. + cl_salv_table=>factory( EXPORTING r_container = cont + container_name = CONV string( container ) + IMPORTING r_salv_table = DATA(alv) + CHANGING t_table = itab ). + + DATA(columns) = alv->get_columns( ). + DATA(col_tab) = columns->get( ). + LOOP AT col_tab ASSIGNING FIELD-SYMBOL(). + DATA(column) = columns->get_column( -columnname ). + column->set_medium_text( CONV scrtext_m( -columnname ) ). + + IF -columnname = 'CLIENT'. + -r_column->set_visible( abap_false ). + ELSE. + -r_column->set_visible( abap_true ). + ENDIF. + ENDLOOP. + alv->display( ). + CATCH cx_salv_msg cx_salv_not_found. + MESSAGE 'ALV display not possible' TYPE 'I' + DISPLAY LIKE 'E'. + ENDTRY. + + ELSE. + + SELECT id, context, name, details, timestamp + FROM zdemo_abap_logt + INTO CORRESPONDING FIELDS OF TABLE @log_entries + ORDER BY id. + + TRY. + cl_salv_table=>factory( EXPORTING r_container = cont + container_name = CONV string( container ) + IMPORTING r_salv_table = alv + CHANGING t_table = log_entries ). + + columns = alv->get_columns( ). + col_tab = columns->get( ). + LOOP AT col_tab ASSIGNING FIELD-SYMBOL(). + column = columns->get_column( -columnname ). + column->set_medium_text( CONV scrtext_m( -columnname ) ). + + IF -columnname = `DETAILS`. + column->set_output_length( 70 ). + ENDIF. + + IF -columnname = 'CLIENT'. + -r_column->set_visible( abap_false ). + ELSE. + -r_column->set_visible( abap_true ). + ENDIF. + ENDLOOP. + alv->display( ). + CATCH cx_salv_msg cx_salv_not_found. + MESSAGE 'ALV display not possible' TYPE 'I' + DISPLAY LIKE 'E'. + ENDTRY. + ENDIF. + ENDMETHOD. + + + METHOD checking_transaction_state. + tx_state = `Tx state: ` && + `Update task (` && SWITCH #( cl_system_transaction_state=>get_in_update_task( ) WHEN 1 THEN `1)` ELSE `0)` ) && + ` Local update (` && SWITCH #( cl_system_transaction_state=>get_update_task_local( ) WHEN 1 THEN `1)` ELSE `0)` ) && + ` Perf. on commit (` && SWITCH #( cl_system_transaction_state=>get_on_commit( ) WHEN 1 THEN `1)` ELSE `0)` ) && + ` Perf. on rollback (` && SWITCH #( cl_system_transaction_state=>get_on_rollback( ) WHEN 1 THEN `1)` ELSE `0)` ). + ENDMETHOD. + + + METHOD create_log_entries. + INSERT zdemo_abap_logt FROM TABLE @log. + ENDMETHOD. + + + METHOD get_work_process_info. + CALL FUNCTION 'TH_GET_OWN_WP_NO' + IMPORTING + wp_pid = wp_pid + wp_index = wp_index. + + id_no = |Work process ID: { wp_pid } / work process index: { wp_index }|. + ENDMETHOD. + + + METHOD subr_delete. + DELETE FROM zdemo_abap_luw_t. + ENDMETHOD. + + + METHOD subr_insert. + INSERT zdemo_abap_luw_t FROM @values. + ENDMETHOD. + + + METHOD subr_update_log. + INSERT zdemo_abap_logt FROM TABLE @log. + ENDMETHOD. +ENDCLASS. diff --git a/src/test_abap_cheat_sheets_classic/zcl_demo_abap_sap_luw_helper.clas.xml b/src/test_abap_cheat_sheets_classic/zcl_demo_abap_sap_luw_helper.clas.xml new file mode 100644 index 0000000..cad0596 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zcl_demo_abap_sap_luw_helper.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_SAP_LUW_HELPER + E + Helper class for SAP LUW example + 1 + X + X + X + + + + diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_dbluw.tabl.xml b/src/test_abap_cheat_sheets_classic/zdemo_abap_dbluw.tabl.xml new file mode 100644 index 0000000..991a690 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_dbluw.tabl.xml @@ -0,0 +1,76 @@ + + + + + + ZDEMO_ABAP_DBLUW + E + TRANSP + X + Demo database table + E + X + A + 1 + + + ZDEMO_ABAP_DBLUW + A + 0 + APPL0 + N + + + + CLIENT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + KEY_FIELD + X + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + CH + 0 + C + 000020 + CHAR + 000010 + CHAR + + + NUM + 0 + X + 000004 + INT4 + 000010 + INT4 + + + TIME_STAMP + 0 + p + 000008 + UTCL + 000027 + UTCL + + + + + diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_dynpro.prog.abap b/src/test_abap_cheat_sheets_classic/zdemo_abap_dynpro.prog.abap new file mode 100644 index 0000000..e7af612 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_dynpro.prog.abap @@ -0,0 +1,826 @@ +*&---------------------------------------------------------------------* +*& ABAP cheat sheet: Dynpro +*& +*&-------------------------- PURPOSE ----------------------------------* +*& - Example to demonstrate dynpro-related statements +*& - Topics covered: Dynpro flow logic and related statements (MODULE, +*& FIELD, CHAIN/ENDCHAIN, LOOP/ENDLOOP, CALL SUBSCREEN), ABAP statements +*& for calling and leaving dynpros (SET SCREEN, CALL SCREEN, LEAVE +*& SCREEN), modifying static attributes (LOOP AT SCREEN, MODIFY SCREEN), +*& statements related to the GUI status and title (GET/SET PF-STATUS, +*& SET TITLEBAR), controls (table and tabstrip controls) +*& +*&----------------------- GETTING STARTED -----------------------------* +*& - Open the program with the ABAP development tools for Eclipse (ADT). +*& - Choose F8 to run the program. +*& - Select the radio buttons and/or provide input in input fields etc. +*& to check out dynpro-related syntax in action. +*& +*&----------------------------- 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. +*&---------------------------------------------------------------------* +REPORT zdemo_abap_dynpro. + +DATA: + "OK field + ok_code LIKE sy-ucomm, + "Auxiliary variable for storing the value of the OK field + save_ok LIKE ok_code, + dyn_num LIKE sy-dynnr, + dyn_title LIKE sy-title. + +"Note: This demo database table does not have any semantic information +"that may be used for input helps etc. +TABLES zdemo_abap_flsch. + +"----- Related to dynpro 9850 (Home page) ----- +DATA: screen_elements TYPE abap_bool, + stmts TYPE abap_bool, + stmts2 TYPE abap_bool, + controls TYPE abap_bool. + +"----- Related to dynpro 9860 (Screen elements) ----- + +DATA: "Icons + icon TYPE icons-text, + icon_name TYPE c LENGTH 20, + icon_text TYPE c LENGTH 10, + "check boxes + ch_a TYPE abap_bool, + ch_b TYPE abap_bool, + ch_c TYPE abap_bool, + checkbox_msg TYPE c LENGTH 4, + input_field TYPE c LENGTH 20, + "radio buttons + rb_x TYPE abap_bool, + rb_y TYPE abap_bool, + rb_z TYPE abap_bool, + radiobutton_msg TYPE c LENGTH 1. + +"----- Related to dynpro 9870 (Statements I) ----- + +DATA: call_screen TYPE abap_bool, + call_screen_starting TYPE abap_bool, + call_screen_st_end TYPE abap_bool, + set_screen TYPE abap_bool, + leave_screen_next TYPE abap_bool, + leave_screen_set TYPE abap_bool, + leave_to_screen TYPE abap_bool, + set_pfstatus TYPE abap_bool, + set_pf_excluding TYPE abap_bool, + get_pfstatus TYPE abap_bool, + get_pfstatsus_excl TYPE abap_bool, + set_titlebar TYPE abap_bool, + set_titlebar_with TYPE abap_bool, + with_title TYPE c LENGTH 15, + col1 TYPE i, + lin1 TYPE i, + col2 TYPE i, + lin2 TYPE i, + col3 TYPE i, + lin3 TYPE i, + pf TYPE c LENGTH 1, + pfkey TYPE syst_pfkey, + title TYPE c LENGTH 1. + +"Data type and objects for the EXCLUDING addition of GET/SET PF-STATUS +"statements +TYPES fu_key_tab TYPE TABLE OF sy-ucomm WITH NON-UNIQUE KEY table_line. +DATA: excl_list1 TYPE fu_key_tab, + excl_list2 TYPE fu_key_tab, + excl_list3 TYPE fu_key_tab. + +"----- Related to dynpro 9880 (Statements II) ----- + +DATA: loop_modify TYPE abap_bool, + field_stmt TYPE abap_bool, + mod_at_exit TYPE abap_bool, + selected TYPE abap_bool, + set TYPE abap_bool, + field_a TYPE c LENGTH 1, + field_b TYPE c LENGTH 1, + field_c TYPE c LENGTH 1, + field_d TYPE c LENGTH 1, + field_e TYPE c LENGTH 1, + field_f TYPE c LENGTH 1, + output_field_1 TYPE c LENGTH 100, + output_field_2 TYPE c LENGTH 100, + output_field_3 TYPE c LENGTH 100, + field_help TYPE c LENGTH 30, + input_help TYPE c LENGTH 30, + "For GET/SET CURSOR statements + fld TYPE c LENGTH 20, + off TYPE i, + val TYPE string, + len TYPE i. + +"For field and input help +TYPES: BEGIN OF values, + carrid TYPE zdemo_abap_flsch-carrid, + connid TYPE zdemo_abap_flsch-connid, + END OF values. + +DATA: progname TYPE sy-repid, + dynnum TYPE sy-dynnr, + dynpro_values TYPE TABLE OF dynpread, + field_value LIKE LINE OF dynpro_values, + values_tab TYPE TABLE OF values, + tab TYPE TABLE OF zdemo_abap_flsch-carrid, + carr TYPE zdemo_abap_flsch-carrid, + conn TYPE zdemo_abap_flsch-connid. + +DATA itab_flsch TYPE TABLE OF zdemo_abap_flsch-carrid. + +TYPES: BEGIN OF carrid_line, + carrid TYPE zdemo_abap_flsch-carrid, + END OF carrid_line. + +DATA carrid_list TYPE STANDARD TABLE OF carrid_line. + +"----- Related to dynpro 9890 (Exit command) ----- + +"For demonstration purposes, implementations for the +"related dialog modules are done in methods of a local +"class. + +"----- Related to dynpro 9990 (Controls) ----- + +"For table controls +DATA lines TYPE i. +DATA fill TYPE i. +CONTROLS flights TYPE TABLEVIEW USING SCREEN 9900. + +"Internal table to hold data that is to be displayed. +DATA itab_flights TYPE TABLE OF zdemo_abap_flsch WITH EMPTY KEY. + +"Filling the internal table +SELECT * + FROM zdemo_abap_flsch + ORDER BY carrid, connid + INTO TABLE @itab_flights. + +"For tabstrip controls +CONTROLS tabstr TYPE TABSTRIP. +DATA number LIKE sy-dynnr. + +"For ALV Grid control +TYPES: BEGIN OF flight_struc, + carrid TYPE zdemo_abap_flsch-carrid, + 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, + fltime TYPE zdemo_abap_flsch-fltime, + END OF flight_struc. + +DATA itab4alv TYPE TABLE OF flight_struc WITH EMPTY KEY. + +"----- For other dialog modules ----- + +DATA: links TYPE TABLE OF tline, + field_tab TYPE TABLE OF dfies WITH EMPTY KEY. + +"----- Local class ----- + +CLASS local_class DEFINITION. + PUBLIC SECTION. + CLASS-METHODS: provide_fu_keys IMPORTING example TYPE i + RETURNING VALUE(fu_keys) TYPE fu_key_tab, + + cc_alv_display IMPORTING container TYPE c, + "Implementations for dialog modules for dynpro 9890 + pbo9890, + pai9890, + cancel. + + "Variable declarations for dialog modules for dynpro 9890 + CLASS-DATA: mandatory_field TYPE c LENGTH 20, + btn_exit_1 TYPE abap_bool, + btn_exit_2 TYPE abap_bool. +ENDCLASS. + +CLASS local_class IMPLEMENTATION. + + METHOD provide_fu_keys. + CASE example. + WHEN 1. + fu_keys = VALUE #( ( 'CANCEL' ) ( 'SAP' ) ). + WHEN 2. + fu_keys = VALUE #( ( 'CANCEL' ) ). + WHEN OTHERS. + ENDCASE. + ENDMETHOD. + METHOD cc_alv_display. + DATA(cont) = NEW cl_gui_custom_container( container_name = container ). + + SELECT carrid, connid, countryfr, cityfrom, airpfrom, countryto, cityto, airpto, fltime + FROM zdemo_abap_flsch + ORDER BY carrid, connid + INTO CORRESPONDING FIELDS OF TABLE @itab4alv. + + TRY. + cl_salv_table=>factory( EXPORTING r_container = cont + container_name = CONV string( container ) + IMPORTING r_salv_table = DATA(alv) + CHANGING t_table = itab4alv ). + alv->get_columns( )->set_optimize( abap_true ). + alv->get_display_settings( )->set_list_header( 'Flights' ). + LOOP AT alv->get_columns( )->get( ) ASSIGNING FIELD-SYMBOL(). + DATA(column) = -r_column. + column->set_long_text( |{ column->get_columnname( ) }| ). + ENDLOOP. + alv->display( ). + CATCH cx_salv_msg cx_salv_not_found. + MESSAGE `ALV display not possible` TYPE 'I' + DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. + METHOD pai9890. + save_ok = ok_code. + CLEAR ok_code. + + CASE save_ok. + WHEN 'CLOSE1'. + MESSAGE `Processing continues. It does so only after the input field is filled.` TYPE 'I'. + SET SCREEN 0. + LEAVE SCREEN. + WHEN 'CLOSE2'. + MESSAGE `Superfluouse message :)` TYPE 'I'. + WHEN OTHERS. + MESSAGE `Processing continues. It does so only after the input field is filled.` TYPE 'I'. + LEAVE PROGRAM. + ENDCASE. + ENDMETHOD. + + METHOD pbo9890. + SET PF-STATUS 'STATUS9890'. + SET TITLEBAR 'TITLE9890'. + + dyn_num = sy-dynnr. + ENDMETHOD. + + METHOD cancel. + MESSAGE `Module "cancel" was called. The dynpro will be left without the need to fill in the mandatory input field.` TYPE 'I'. + + IF ok_code = 'CLOSE2'. + CLEAR: ok_code, mandatory_field. + SET SCREEN 0. + LEAVE SCREEN. + ENDIF. + ENDMETHOD. + +ENDCLASS. + +END-OF-SELECTION. + + "Filling demo database tables + zcl_demo_abap_flight_tables=>fill_dbtabs( ). + + CALL SCREEN 9850. + +********************************************************************** + +***************** Dialog modules for 9850 **************************** + +MODULE status_9850 OUTPUT. + SET PF-STATUS 'STATUS9850'. + SET TITLEBAR 'TITLE9850'. + + CLEAR: screen_elements, stmts, stmts2. + dyn_num = sy-dynnr. +ENDMODULE. + +MODULE user_command_9850 INPUT. + save_ok = ok_code. + CLEAR ok_code. + + CASE save_ok. + WHEN 'ENTER' OR 'GO'. + CASE abap_true. + WHEN screen_elements. + CALL SCREEN 9860. + WHEN stmts. + CALL SCREEN 9870. + WHEN stmts2. + CALL SCREEN 9880. + WHEN controls. + CALL SCREEN 9900. + ENDCASE. + WHEN OTHERS. + LEAVE PROGRAM. + ENDCASE. +ENDMODULE. + +***************** Dialog modules for 9860 **************************** + +MODULE status_9860 OUTPUT. + SET PF-STATUS 'STATUS9860'. + SET TITLEBAR 'TITLE9860'. + + "Modifying static attributes + IF loop_modify = 'X'. + LOOP AT SCREEN INTO DATA(scr). + IF scr-name = 'ICON3'. + scr-invisible = '1'. + MODIFY SCREEN FROM scr. + ENDIF. + IF scr-name = 'INPUT_FIELD'. + scr-length = 10. + scr-intensified = '1'. + "scr-required = '1'. + MODIFY SCREEN FROM scr. + ENDIF. + IF scr-name = 'RB_Z'. + scr-active = '0'. + MODIFY SCREEN FROM scr. + ENDIF. + IF scr-name = 'CH_B'. + scr-input = '0'. + MODIFY SCREEN FROM scr. + ENDIF. + ENDLOOP. + ENDIF. + + dyn_num = sy-dynnr. +ENDMODULE. + +MODULE user_command_9860 INPUT. + save_ok = ok_code. + CLEAR ok_code. + + CASE save_ok. + "Icons + WHEN 'ICON1'. + icon_name = 'ICON_GREEN_LIGHT'. + icon_text = `green`. + WHEN 'ICON2'. + icon_name = 'ICON_YELLOW_LIGHT'. + icon_text = `yellow`. + WHEN 'ICON3'. + icon_name = 'ICON_RED_LIGHT'. + icon_text = `red`. + "Input field + WHEN 'BTN_NO'. + MESSAGE |The text "{ input_field }" in the input field has { numofchar( input_field ) } characters. Trailing blanks are ignored.| TYPE 'I'. + "Check boxes + WHEN 'CH_BTN'. + IF ch_a = abap_true. + checkbox_msg = 'A'. + ENDIF. + IF ch_b = abap_true. + checkbox_msg = checkbox_msg && 'B'. + ENDIF. + IF ch_c = abap_true. + checkbox_msg = checkbox_msg && 'C'. + ENDIF. + IF checkbox_msg IS INITIAL. + checkbox_msg = 'None'. + ENDIF. + checkbox_msg = checkbox_msg. + MESSAGE checkbox_msg TYPE 'I'. + CLEAR checkbox_msg. + "Radio buttons + WHEN 'RB_BTN'. + CASE abap_true. + WHEN rb_x. + radiobutton_msg = 'X'. + WHEN rb_y. + radiobutton_msg = 'Y'. + WHEN rb_z. + radiobutton_msg = 'Z'. + WHEN OTHERS. + radiobutton_msg = ' '. + ENDCASE. + MESSAGE radiobutton_msg TYPE 'I'. + CLEAR radiobutton_msg. + WHEN 'HOME'. + SET SCREEN 0. + LEAVE SCREEN. + WHEN OTHERS. + LEAVE PROGRAM. + ENDCASE. + + IF save_ok = 'ICON1' OR save_ok = 'ICON2' OR save_ok = 'ICON3'. + CALL FUNCTION 'ICON_CREATE' + EXPORTING + name = icon_name + text = icon_text + info = 'Status' + add_stdinf = 'X' + IMPORTING + result = icon + EXCEPTIONS + icon_not_found = 1 + outputfield_too_short = 2 + OTHERS = 3. + IF sy-subrc <> 0. + MESSAGE `Issue.` TYPE 'I' DISPLAY LIKE 'E'. + ENDIF. + ENDIF. + +ENDMODULE. + +***************** Dialog modules for 9870 **************************** + +MODULE status_9870 OUTPUT. + + "Control structures in case of calling the dynpro again + IF set_pfstatus IS NOT INITIAL AND pfkey = 'STATUS9870'. + SET PF-STATUS 'DEMOSTATUS'. + ELSEIF set_pfstatus IS NOT INITIAL AND pfkey = 'DEMOSTATUS'. + SET PF-STATUS 'STATUS9870'. + ELSEIF set_pf_excluding IS NOT INITIAL AND pfkey = 'STATUS9870'. + excl_list1 = local_class=>provide_fu_keys( 1 ). + SET PF-STATUS 'DEMOSTATUS' EXCLUDING excl_list1. + ELSEIF set_pf_excluding IS NOT INITIAL AND pfkey = 'DEMOSTATUS'. + excl_list2 = local_class=>provide_fu_keys( 2 ). + SET PF-STATUS 'STATUS9870' EXCLUDING excl_list2. + ELSE. + SET PF-STATUS 'STATUS9870'. + ENDIF. + + IF set_titlebar IS NOT INITIAL. + IF dyn_title CS 'Demo title'. + SET TITLEBAR 'TITLE9870'. + ELSE. + SET TITLEBAR 'DEMOTITLE'. + ENDIF. + ELSEIF SET_TITLEBAR_with IS NOT INITIAL. + SET TITLEBAR 'DEMOTITLE' WITH '"' with_title '"'. + ELSE. + SET TITLEBAR 'TITLE9870'. + ENDIF. + + "Prepopulating values + col1 = 5. + lin1 = 10. + col2 = 1. + lin2 = 1. + col3 = 80. + lin3 = 20. + dyn_num = sy-dynnr. + dyn_title = sy-title. +ENDMODULE. + +MODULE user_command_9870 INPUT. + save_ok = ok_code. + CLEAR ok_code. + + CASE save_ok. + WHEN 'ENTER' OR 'GO'. + CASE abap_true. + WHEN set_screen. + MESSAGE |A SET SCREEN statement is about to get called. The static next dynpro of dynpro { sy-dynnr } is 0.| && + ` It is dynamically overwritten. Another dynpro will be displayed. A new dynpro sequence is not started.` TYPE 'I'. + SET SCREEN 9860. + WHEN call_screen. + MESSAGE `A CALL SCREEN statement is about to get called. You will switch to another dynpro. A new dynpro sequence is started.` TYPE 'I'. + CALL SCREEN 9860. + WHEN call_screen_starting. + IF col1 < 0 OR lin1 < 0. + MESSAGE `Only positive integers can be specified.` TYPE 'E'. + ELSE. + MESSAGE |The statement "CALL SCREEN ... STARTING AT { col1 } { lin1 }." is about to get called. | && + `Note: In the dialog box that opens, click the icon in the popup footer to remain in the program. Clicking the X leaves the program.` TYPE 'I'. + CALL SCREEN 9860 STARTING AT col1 lin1. + ENDIF. + WHEN call_screen_st_end. + IF col2 < 0 OR lin2 < 0 + OR col3 < 0 OR lin3 < 0. + MESSAGE `Only positive integers can be specified.` + TYPE 'E'. + ELSE. + MESSAGE |The statement "CALL SCREEN ... STARTING AT { col2 } { lin2 } ENDING AT { col3 } { lin3 }| && + `." is about to get called. Note: In the dialog box that opens, click the icon in the popup footer ` && + `to remain in the program. Clicking the X leaves the program.` TYPE 'I'. + CALL SCREEN 9860 STARTING AT col2 lin2 ENDING AT col3 lin3. + ENDIF. + WHEN leave_screen_next. + MESSAGE `A LEAVE SCREEN statement is about to get called. The current dynpro is exited, and the statically ` && + `defined next dynpro is called, which is 0 in this case. Hence, the current dynpro sequence is terminated. ` && + `Since the dynpro sequence is nested, you return to the home page and the parent dynpro sequence.` TYPE 'I'. + LEAVE SCREEN. + WHEN leave_screen_set. + MESSAGE `A LEAVE SCREEN statement is about to get called. In this case, a SET SCREEN statement before the LEAVE ` && + `SCREEN statement overwrites the next dynpro (0 here). A new dynpro sequence is not started, instead the statement ` && + `branches to another dynpro in the same sequence.` TYPE 'I'. + SET SCREEN 9900. + LEAVE SCREEN. + WHEN leave_to_screen. + MESSAGE `A LEAVE TO SCREEN statement is about to get called. A new dynpro sequence is not started, instead the ` && + `statement branches to another dynpro in the same sequence. It has the same effect as the previous example.` TYPE 'I'. + LEAVE TO SCREEN 9900. + WHEN set_pfstatus. + pfkey = sy-pfkey. + MESSAGE |The current GUI status is { pfkey }. The current dynpro { sy-dynnr } will be set as the next dynpro.| && + ` It will have a different GUI status.` TYPE 'I'. + SET SCREEN 9870. + WHEN set_pf_excluding. + GET PF-STATUS pfkey. + MESSAGE |The current GUI status is { pfkey }. The current dynpro will be set as the next dynpro.| && + ` It will have a different GUI status (set at PBO) but excluding function codes (for example, the CANCEL button).` TYPE 'I'. + SET SCREEN 9870. + WHEN get_pfstatus. + GET PF-STATUS pfkey. + MESSAGE |The current GUI status is { pfkey }, which was retrieved using GET-PF-STATUS. Value of sy-pfkey: { sy-pfkey }.| TYPE 'I'. + SET SCREEN 9870. + WHEN get_pfstatsus_excl. + GET PF-STATUS pfkey EXCLUDING excl_list3. + MESSAGE |The current GUI status is { pfkey }. The exluded function codes are: { + COND #( WHEN excl_list3 IS INITIAL THEN `None are excluded. Try to run SET PF-STATUS ... EXCLUDING ... first before this one.` + ELSE concat_lines_of( table = excl_list3 sep = `, ` ) ) }| TYPE 'I'. + SET SCREEN 9870. + CLEAR excl_list3. + WHEN set_titlebar. + MESSAGE |The current title is "{ dyn_title }". The current dynpro is set as the next dynpro.| && + `The text in the title bar should change.` TYPE 'I'. + SET SCREEN 9870. + WHEN set_titlebar_with. + MESSAGE |The current title is "{ dyn_title }". The current dynpro is set as the next dynpro.| && + `The text in the title bar should change according to your input in the input field.` TYPE 'I'. + SET SCREEN 9870. + ENDCASE. + WHEN 'SAP'. + cl_gui_frontend_services=>execute( document = 'https://www.sap.com' ). + SET SCREEN 9870. + WHEN 'SAYHI'. + MESSAGE `Hallo ` && sy-uname && `.` TYPE 'I'. + SET SCREEN 9870. + WHEN 'HOME'. + LEAVE TO SCREEN 9850. + WHEN 'LEAVE' OR 'CANCEL'. + LEAVE PROGRAM. + WHEN OTHERS. + ENDCASE. +ENDMODULE. + +***************** Dialog modules for 9880 **************************** + +MODULE status_9880 OUTPUT. + SET PF-STATUS 'STATUS9880'. + SET TITLEBAR 'TITLE9880'. + + "Clearing variables in case the dynpro is called again + IF field_stmt IS INITIAL. + CLEAR: output_field_1, output_field_2, output_field_3, + field_a, field_b, field_c, field_d, field_e, field_f. + ENDIF. + IF field_d IS INITIAL. + CLEAR output_field_2. + ENDIF. + IF field_e IS INITIAL OR field_f IS INITIAL. + CLEAR output_field_3. + ENDIF. + + "Prepopulating fields for input help + progname = sy-repid. + dynnum = sy-dynnr. + CLEAR: field_value, dynpro_values. + field_value-fieldname = 'CARR'. + APPEND field_value TO dynpro_values. + + "Setting the cursor field + IF set = abap_true. + SET CURSOR FIELD 'CONN'. + CLEAR set. + ENDIF. + + dyn_num = sy-dynnr. +ENDMODULE. + +MODULE user_command_9880 INPUT. + save_ok = ok_code. + CLEAR ok_code. + + CASE save_ok. + WHEN 'ENTER' OR 'GO'. + CASE abap_true. + WHEN loop_modify. + MESSAGE `Another dynpro will be set as the next dynpro. This dynpro includes a LOOP AT SCREEN ` && + ` and MODIFY SCREEN statements in the PBO dialog module. Some screen elements have their static ` && + `attributes changed. For example, they are made invisible or inactive, or the length is changed.` TYPE 'I'. + SET SCREEN 9860. + LEAVE SCREEN. + WHEN field_stmt. + MESSAGE `The current dynpro will be set as the next dynpro. Depending on which checkboxes you ` && + `have selected, text is displayed in the fields below showing the effect of FIELD statements.` TYPE 'I'. + SET SCREEN 9880. + WHEN mod_at_exit. + SET SCREEN 9890. + ENDCASE. + WHEN 'SELECTED'. + CLEAR conn. + SET SCREEN 9880. + WHEN 'GET'. + GET CURSOR FIELD fld OFFSET off VALUE val LENGTH len. + MESSAGE |Current position of cursor: Field: "{ fld }", value: "{ val }", offset: "{ off }", length "{ len }"| TYPE 'I'. + SET SCREEN 9880. + WHEN 'SET'. + set = abap_true. + MESSAGE `Clicking the pushbutton triggers a SET CURSOR statement to be called at PBO. This sets the cursor on ` && + `a specific input field (the one in the lower left corner).` TYPE 'I'. + SET SCREEN 9880. + WHEN 'HOME'. + LEAVE TO SCREEN 9850. + WHEN 'LEAVE' OR 'CANCEL'. + LEAVE PROGRAM. + WHEN OTHERS. + ENDCASE. +ENDMODULE. + +***************** Dialog modules for 9890 **************************** +"For demonstration purposes, the dialog module implementations are +"done in methods of a local class. + +MODULE status_9890 OUTPUT. + local_class=>pbo9890( ). +ENDMODULE. + +MODULE user_command_9890 INPUT. + local_class=>pai9890( ). +ENDMODULE. + +***************** Dialog modules for 9900 **************************** + +MODULE status_9900 OUTPUT. + SET PF-STATUS 'STATUS9900'. + SET TITLEBAR 'TITLE9900'. + + "For vertically scrolling in the table control + "To do this, the LINES component of the scxtab_control structure is set + "to the correct line number. Each time the scroll bar is scrolled, the + "PAI event is triggered with an empty function code, and the top_line + "component of the cxtab_control structure is automatically set to the + "new top line before PBO. + flights-lines = lines( itab_flights ). + + "Setting a tab to be active by default in case of calling the + "dynpro again after leaving it. + IF tabstr-activetab IS INITIAL + OR number IS INITIAL. + number = '9920'. + tabstr-activetab = 'TAB2'. + ENDIF. + local_class=>cc_alv_display( 'ALVCONT' ). + dyn_num = sy-dynnr. +ENDMODULE. + +MODULE user_command_9900 INPUT. + save_ok = ok_code. + CLEAR ok_code. + + CASE save_ok. + WHEN 'TAB1'. + number = '9910'. + tabstr-activetab = save_ok. + WHEN 'TAB2'. + number = '9920'. + tabstr-activetab = save_ok. + WHEN 'HOME'. + "REFRESH statement: You can check out the effect, for example, by setting + "the vertical scroll bar somewhere. If you then go back and call the dynpro + "again, the control is refreshed (the scroll bar is again at the top). + REFRESH CONTROL 'FLIGHTS' FROM SCREEN sy-dynnr. + CLEAR: number, tabstr-activetab. + SET SCREEN 0. + LEAVE SCREEN. + WHEN 'CANCEL' OR 'LEAVE'. + LEAVE PROGRAM. + ENDCASE. +ENDMODULE. + +***************** Further dialog modules to support the example **************************** + +MODULE module_1 INPUT. + output_field_1 = 'Available in module_1:' && ` ` && + COND #( WHEN field_a = abap_true THEN 'A' ELSE '' ) && + COND #( WHEN field_b = abap_true THEN 'B' ELSE '' ) && + COND #( WHEN field_c = abap_true THEN 'C' ELSE '' ) && + ' /'. +ENDMODULE. + +MODULE module_2 INPUT. + output_field_1 = output_field_1 && + ' Available in module_2:' && ` ` && + COND #( WHEN field_a = abap_true THEN 'A' ELSE '' ) && + COND #( WHEN field_b = abap_true THEN 'B' ELSE '' ) && + COND #( WHEN field_c = abap_true THEN 'C' ELSE '' ) && + ' /'. +ENDMODULE. + +MODULE module_3 INPUT. + output_field_1 = output_field_1 && + ' Available in module_3:' && ` ` && + COND #( WHEN field_a = abap_true THEN 'A' ELSE '' ) && + COND #( WHEN field_b = abap_true THEN 'B' ELSE '' ) && + COND #( WHEN field_c = abap_true THEN 'C' ELSE '' ). + CLEAR: field_a, field_b, field_c. +ENDMODULE. + +MODULE module_4 INPUT. + output_field_2 = 'module_4 was called because field_d was selected.'. +ENDMODULE. + +MODULE module_5 INPUT. + output_field_3 = 'module_5 was called because field_e and field_f were selected.'. +ENDMODULE. + +MODULE module_f1 INPUT. + "In this example, a demo object from the ABAP Keyword + "Documentation examples is used. + CALL FUNCTION 'HELP_OBJECT_SHOW' + EXPORTING + dokclass = 'TX' + doklangu = sy-langu + dokname = 'DEMO_FOR_F1_HELP' + TABLES + links = links. +ENDMODULE. + +MODULE module_f4 INPUT. + CALL FUNCTION 'DYNP_VALUES_READ' + EXPORTING + dyname = progname + dynumb = dynnum + translate_to_upper = 'X' + TABLES + dynpfields = dynpro_values. + + field_value = dynpro_values[ 1 ]. + + SELECT carrid, connid + FROM zdemo_abap_flsch + WHERE carrid = @( CONV #( field_value-fieldvalue ) ) + INTO CORRESPONDING FIELDS OF TABLE @values_tab. + + "Note: To save extra artifacts, this example goes without a dedicated DDIC + "structure with which input help can be created comfortably. Hence, creating + "a table manually for the field_tab parameter of the function module below. + field_tab = VALUE #( + ( tabname = 'ZDEMO_ABAP_FLSCH' fieldname = 'CARRID' langu = 'E' position = '1' + offset = '0' leng = '3' intlen = '6' outputlen = '3' datatype = 'CHAR' inttype = 'C' ) + ( tabname = 'ZDEMO_ABAP_FLSCH' fieldname = 'CONNID' langu = 'E' position = '2' + offset = '6' leng = '4' intlen = '8' outputlen = '4' datatype = 'NUMC' inttype = 'N' ) ). + + CALL FUNCTION 'F4IF_INT_TABLE_VALUE_REQUEST' + EXPORTING + retfield = 'CONNID' + dynpprog = progname + dynpnr = dynnum + dynprofield = 'CONN' + value_org = 'S' + window_title = 'Selection' + TABLES + value_tab = values_tab + field_tab = field_tab. +ENDMODULE. + +MODULE module_dropdown INPUT. + SELECT DISTINCT carrid + FROM zdemo_abap_flsch + INTO CORRESPONDING FIELDS OF TABLE @carrid_list. + + CALL FUNCTION 'F4IF_INT_TABLE_VALUE_REQUEST' + EXPORTING + retfield = 'CARRID' + value_org = 'S' + TABLES + value_tab = carrid_list + EXCEPTIONS + parameter_error = 1 + no_values_found = 2 + OTHERS = 3. + IF sy-subrc <> 0. + carr = 'LH'. + ENDIF. +ENDMODULE. + +MODULE module_fill_table OUTPUT. + TRY. + zdemo_abap_flsch = itab_flights[ flights-current_line ]. + CATCH cx_sy_itab_line_not_found. + RETURN. + ENDTRY. +ENDMODULE. + +MODULE module_read_table INPUT. + lines = sy-loopc. + MODIFY itab_flights FROM zdemo_abap_flsch INDEX flights-current_line. +ENDMODULE. + +MODULE cancel INPUT. + local_class=>cancel( ). +ENDMODULE. diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_dynpro.prog.xml b/src/test_abap_cheat_sheets_classic/zdemo_abap_dynpro.prog.xml new file mode 100644 index 0000000..ffa8ed9 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_dynpro.prog.xml @@ -0,0 +1,2793 @@ + + + + + + ZDEMO_ABAP_DYNPRO + S + 1 + X + D$S + X + + + +
+ ZDEMO_ABAP_DYNPRO + 9850 + E + Demo dynpro + N + 9850 + 027 + 128 +
+ + + SCREEN + SCREEN + + + RADIOGROUP + %A_SCREEN_ELEMENTS + SCREEN + 003 + 006 + 029 + 004 + + + + + SCREEN + SCREEN + TEXT + DYNNR_TEXT1 + Dynpro_Number: + 010 + 006 + 014 + 014 + 001 + CHAR + N + X + + + SCREEN + SCREEN + TEMPLATE + DYN_NUM + _______ + 010 + 021 + 007 + 007 + 001 + CHAR + X + X + N + + + SCREEN + SCREEN + OKCODE + OK_CODE + ____________________ + 020 + 020 + 001 + CHAR + X + + + RADIOGROUP + %A_SCREEN_ELEMENTS + RADIO + SCREEN_ELEMENTS + Examples_of_screen_elements + 003 + 006 + 029 + 029 + 001 + CHAR + X + X + + + RADIOGROUP + %A_SCREEN_ELEMENTS + RADIO + STMTS + Statements_I + 004 + 006 + 014 + 014 + 001 + CHAR + X + X + + + RADIOGROUP + %A_SCREEN_ELEMENTS + RADIO + STMTS2 + Statements_II + 005 + 006 + 015 + 015 + 001 + CHAR + X + X + + + RADIOGROUP + %A_SCREEN_ELEMENTS + RADIO + CONTROLS + Controls + 006 + 006 + 010 + 010 + 001 + CHAR + X + X + + + + + PROCESS BEFORE OUTPUT. + + + MODULE STATUS_9850. + + + + PROCESS AFTER INPUT. + + + MODULE USER_COMMAND_9850. + + +
+ +
+ ZDEMO_ABAP_DYNPRO + 9860 + E + Screen elements + N + 9860 + 027 + 123 +
+ + + SCREEN + SCREEN + + + RADIOGROUP + %A_RB_X + SCREEN + 009 + 041 + 003 + 003 + + + + + SCREEN + SCREEN + FRAME + %#AUTOTEXT001 + ________________________________________________________________________ + 002 + 003 + 073 + 073 + 006 + CHAR + N + + + SCREEN + SCREEN + TEMPLATE + ICON + __________________ + X + 004 + 005 + 023 + 021 + 001 + CHAR + X + N + <_--32_DIMENS>X + + + SCREEN + SCREEN + TEMPLATE + INPUT_FIELD + ____________________ + 004 + 034 + 020 + 020 + 001 + CHAR + X + X + + + SCREEN + SCREEN + PUSH + ICON1 + green + 006 + 005 + 005 + 005 + 001 + ICON1 + CHAR + N + + + SCREEN + SCREEN + PUSH + ICON2 + yellow + 006 + 012 + 006 + 006 + 001 + ICON2 + CHAR + N + + + SCREEN + SCREEN + PUSH + ICON3 + red + 006 + 020 + 003 + 003 + 001 + ICON3 + CHAR + N + + + SCREEN + SCREEN + PUSH + BTN_OUTPUT + Number_of_characters? + 006 + 034 + 021 + 021 + 001 + BTN_NO + CHAR + N + + + SCREEN + SCREEN + FRAME + %#AUTOTEXT002 + ________________________________________________________________________ + 008 + 003 + 073 + 073 + 007 + CHAR + N + + + SCREEN + SCREEN + CHECK + CH_A + A + 009 + 005 + 003 + 003 + 001 + CHAR + X + X + + + SCREEN + SCREEN + CHECK + CH_B + B + 010 + 005 + 003 + 003 + 001 + CHAR + X + X + + + SCREEN + SCREEN + CHECK + CH_C + C + 011 + 005 + 003 + 003 + 001 + CHAR + X + X + + + SCREEN + SCREEN + PUSH + CHECKBOX_BTN + Which_checkboxes_are_selected? + 013 + 005 + 030 + 030 + 001 + CH_BTN + CHAR + N + + + SCREEN + SCREEN + PUSH + RADIOBUTTON_BTN + Which_radio_button_is_selected? + 013 + 041 + 031 + 031 + 001 + RB_BTN + CHAR + N + + + SCREEN + SCREEN + TEXT + DYNNR_TEXT2 + Dynpro_Number: + 017 + 004 + 014 + 014 + 001 + CHAR + N + X + + + SCREEN + SCREEN + TEMPLATE + DYN_NUM + _________ + 017 + 019 + 009 + 009 + 001 + CHAR + X + X + N + + + SCREEN + SCREEN + OKCODE + OK_CODE + ____________________ + 020 + 020 + 001 + CHAR + X + + + RADIOGROUP + %A_RB_X + RADIO + RB_X + X + 009 + 041 + 003 + 003 + 001 + CHAR + X + X + + + RADIOGROUP + %A_RB_X + RADIO + RB_Y + Y + 010 + 041 + 003 + 003 + 001 + CHAR + X + X + + + RADIOGROUP + %A_RB_X + RADIO + RB_Z + Z + 011 + 041 + 003 + 003 + 001 + CHAR + X + X + + + + + PROCESS BEFORE OUTPUT. + + + MODULE STATUS_9860. + + + + PROCESS AFTER INPUT. + + + MODULE USER_COMMAND_9860. + + +
+ +
+ ZDEMO_ABAP_DYNPRO + 9870 + E + Statements I + N + 0000 + 027 + 129 +
+ + + SCREEN + SCREEN + + + RADIOGROUP + %A_SET_SCREEN + SCREEN + 002 + 004 + 047 + 015 + + + + + SCREEN + SCREEN + TEXT + DYNNR_TEXT3 + Dynpro_Number: + 002 + 089 + 014 + 014 + 001 + CHAR + N + X + + + SCREEN + SCREEN + TEMPLATE + DYN_NUM + _________ + 002 + 104 + 009 + 009 + 001 + CHAR + X + X + N + + + SCREEN + SCREEN + TEXT + %#AUTOTEXT001 + CALL_SCREEN_dynpro_STARTING_AT + 005 + 006 + 030 + 030 + 001 + CHAR + N + X + + + SCREEN + SCREEN + TEMPLATE + COL1 + ___ + 005 + 038 + 003 + 003 + 001 + INT4 + X + X + + + SCREEN + SCREEN + TEMPLATE + LIN1 + ___ + 005 + 043 + 003 + 003 + 001 + INT4 + X + X + + + SCREEN + SCREEN + TEXT + %#AUTOTEXT002 + CALL_SCREEN_dynpro_STARTING_AT + 007 + 006 + 030 + 030 + 001 + CHAR + N + X + + + SCREEN + SCREEN + TEMPLATE + COL2 + ___ + 007 + 038 + 003 + 003 + 001 + INT4 + X + X + + + SCREEN + SCREEN + TEMPLATE + LIN2 + ___ + 007 + 043 + 003 + 003 + 001 + INT4 + X + X + + + SCREEN + SCREEN + TEXT + %#AUTOTEXT003 + ENDING_AT + 007 + 048 + 009 + 009 + 001 + CHAR + N + X + + + SCREEN + SCREEN + TEMPLATE + COL3 + ___ + 007 + 059 + 003 + 003 + 001 + INT4 + X + X + + + SCREEN + SCREEN + TEMPLATE + LIN3 + ___ + 007 + 064 + 003 + 003 + 001 + INT4 + X + X + + + SCREEN + SCREEN + TEMPLATE + WITH_TITLE + _______________ + 016 + 033 + 015 + 015 + 001 + CHAR + X + X + X + + + SCREEN + SCREEN + OKCODE + OK_CODE + ____________________ + 020 + 020 + 001 + CHAR + X + + + RADIOGROUP + %A_SET_SCREEN + RADIO + SET_SCREEN + SET_SCREEN + 002 + 004 + 012 + 012 + 001 + CHAR + X + X + + + RADIOGROUP + %A_SET_SCREEN + RADIO + CALL_SCREEN + CALL_SCREEN + 003 + 004 + 013 + 013 + 001 + CHAR + X + X + + + RADIOGROUP + %A_SET_SCREEN + RADIO + CALL_SCREEN_STARTING + Insert_values: + 004 + 004 + 016 + 016 + 001 + CHAR + X + X + + + RADIOGROUP + %A_SET_SCREEN + RADIO + CALL_SCREEN_ST_END + Insert_values: + 006 + 004 + 016 + 016 + 001 + CHAR + X + X + + + RADIOGROUP + %A_SET_SCREEN + RADIO + LEAVE_SCREEN_NEXT + LEAVE_SCREEN_(statically_defined_next_dynpro) + 008 + 004 + 047 + 047 + 001 + CHAR + X + X + + + RADIOGROUP + %A_SET_SCREEN + RADIO + LEAVE_SCREEN_SET + LEAVE_SCREEN_(dynpro_set_beforehand) + 009 + 004 + 038 + 038 + 001 + CHAR + X + X + + + RADIOGROUP + %A_SET_SCREEN + RADIO + LEAVE_TO_SCREEN + LEAVE_TO_SCREEN + 010 + 004 + 017 + 017 + 001 + CHAR + X + X + + + RADIOGROUP + %A_SET_SCREEN + RADIO + SET_PFSTATUS + SET_PF-STATUS + 011 + 004 + 015 + 015 + 001 + CHAR + X + X + + + RADIOGROUP + %A_SET_SCREEN + RADIO + SET_PF_EXCLUDING + SET_PF-STATUS_..._EXCLUDING_... + 012 + 004 + 033 + 033 + 001 + CHAR + X + X + + + RADIOGROUP + %A_SET_SCREEN + RADIO + GET_PFSTATUS + GET_PF-STATUS + 013 + 004 + 015 + 015 + 001 + CHAR + X + X + + + RADIOGROUP + %A_SET_SCREEN + RADIO + GET_PFSTATSUS_EXCL + GET_PF-STATUS_..._EXCLUDING_... + 014 + 004 + 033 + 033 + 001 + CHAR + X + X + + + RADIOGROUP + %A_SET_SCREEN + RADIO + SET_TITLEBAR + SET_TITLEBAR + 015 + 004 + 014 + 014 + 001 + CHAR + X + X + + + RADIOGROUP + %A_SET_SCREEN + RADIO + SET_TITLEBAR_WITH + SET_TITLEBAR_..._WITH_... + 016 + 004 + 027 + 027 + 001 + CHAR + X + X + + + + + PROCESS BEFORE OUTPUT. + + + MODULE STATUS_9870. + + + + PROCESS AFTER INPUT. + + + MODULE USER_COMMAND_9870. + + +
+ +
+ ZDEMO_ABAP_DYNPRO + 9880 + E + Statements II + N + 0000 + 027 + 139 +
+ + + SCREEN + SCREEN + + + RADIOGROUP + %A_LOOP_MODIFY + SCREEN + 002 + 004 + 037 + 016 + + + + + SCREEN + SCREEN + FRAME + %#AUTOTEXT001 + ___________________________________________________________________________________________________________________________ + 001 + 002 + 124 + 124 + 018 + CHAR + N + + + SCREEN + SCREEN + TEXT + DYNNR_TEXT4 + Dynpro_Number: + 002 + 099 + 014 + 014 + 001 + CHAR + N + X + + + SCREEN + SCREEN + TEMPLATE + DYN_NUM + _________ + 002 + 114 + 009 + 009 + 001 + CHAR + X + X + N + + + SCREEN + SCREEN + TEXT + TEXT2 + 1)_FIELD_statement_(select_all_3_checkboxes) + 005 + 007 + 044 + 044 + 001 + CHAR + N + + + SCREEN + SCREEN + CHECK + FIELD_A + A + 006 + 007 + 003 + 003 + 001 + CHAR + X + X + + + SCREEN + SCREEN + CHECK + FIELD_B + B + 006 + 011 + 003 + 003 + 001 + CHAR + X + X + + + SCREEN + SCREEN + CHECK + FIELD_C + C + 006 + 016 + 003 + 003 + 001 + CHAR + X + X + + + SCREEN + SCREEN + TEMPLATE + OUTPUT_FIELD_1 + _____________________________________________________________________________________________________________ + 007 + 007 + 109 + 109 + 001 + CHAR + X + N + + + SCREEN + SCREEN + TEXT + TEXT1 + 2)_FIELD_..._MODULE_..._ON_INPUT_(Conditional_dialog_module_call) + 009 + 007 + 065 + 065 + 001 + CHAR + N + + + SCREEN + SCREEN + CHECK + FIELD_D + D + 010 + 007 + 003 + 003 + 001 + CHAR + X + X + + + SCREEN + SCREEN + TEMPLATE + OUTPUT_FIELD_2 + _____________________________________________________________________________________________________________ + 011 + 007 + 109 + 109 + 001 + CHAR + X + N + + + SCREEN + SCREEN + TEXT + TEXT3 + 3)_CHAIN_/_MODULE_..._ON_CHAIN-INPUT + 013 + 007 + 036 + 036 + 001 + CHAR + N + + + SCREEN + SCREEN + CHECK + FIELD_E + E + 014 + 007 + 003 + 003 + 001 + CHAR + X + X + + + SCREEN + SCREEN + CHECK + FIELD_F + F + 014 + 012 + 003 + 003 + 001 + CHAR + X + X + + + SCREEN + SCREEN + TEMPLATE + OUTPUT_FIELD_3 + _____________________________________________________________________________________________________________ + 015 + 007 + 109 + 109 + 001 + CHAR + X + N + + + SCREEN + SCREEN + FRAME + %#AUTOTEXT002 + ___________________________________________________________________________________________________________________________ + 019 + 002 + 124 + 124 + 008 + CHAR + N + + + SCREEN + SCREEN + TEXT + TEXT5 + Field_help_(choose_F1_for_the_input_field) + 020 + 007 + 042 + 042 + 001 + CHAR + N + + + SCREEN + SCREEN + TEMPLATE + FIELD_HELP + ______________________________ + 021 + 007 + 030 + 030 + 001 + CHAR + X + X + + + SCREEN + SCREEN + PUSH + GET_CURSOR + Get_cursor_position + 021 + 071 + 019 + 019 + 001 + GET + CHAR + N + + + SCREEN + SCREEN + TEXT + TEXT6 + Input_help_ + 023 + 007 + 011 + 011 + 001 + CHAR + N + + + SCREEN + SCREEN + PUSH + SET_CURSOR + Set_cursor_position + 023 + 071 + 019 + 019 + 001 + SET + CHAR + N + + + SCREEN + SCREEN + TEMPLATE + CARR + ___ + L + 024 + 007 + 003 + 005 + 001 + SELECTED + CHAR + X + X + + + SCREEN + SCREEN + TEXT + TEXT_DD + Dropdown_list_box + 024 + 014 + 017 + 017 + 001 + CHAR + N + + + SCREEN + SCREEN + TEMPLATE + CONN + ____ + 025 + 007 + 004 + 004 + 001 + NUMC + X + X + + + SCREEN + SCREEN + TEXT + TEXT_F4 + Choose_F4_or_the_icon + 025 + 014 + 021 + 021 + 001 + CHAR + N + + + SCREEN + SCREEN + OKCODE + OK_CODE + ____________________ + 020 + 020 + 001 + CHAR + X + + + RADIOGROUP + %A_LOOP_MODIFY + RADIO + LOOP_MODIFY + LOOP_AT_SCREEN_/_MODIFY_SCREEN_FROM + 002 + 004 + 037 + 037 + 001 + CHAR + X + X + + + RADIOGROUP + %A_LOOP_MODIFY + RADIO + FIELD_STMT + FIELD,_MODULE_and_CHAIN_statements + 003 + 004 + 036 + 036 + 001 + CHAR + X + X + + + RADIOGROUP + %A_LOOP_MODIFY + RADIO + MOD_AT_EXIT + MODULE_..._AT_EXIT-COMMAND. + 017 + 005 + 029 + 029 + 001 + CHAR + X + X + + + + + PROCESS BEFORE OUTPUT. + + + MODULE STATUS_9880. + + + + PROCESS AFTER INPUT. + + + MODULE USER_COMMAND_9880. + + + "Field statement + + + MODULE module_1. + + + FIELD field_a. + + + MODULE module_2. + + + FIELD field_b. + + + MODULE module_3. + + + + "FIELD statement, conditional dialog module call + + + FIELD field_d MODULE module_4 ON INPUT. + + + + "CHAIN/FIELD statements + + + CHAIN. + + + FIELD field_e. + + + FIELD field_f. + + + MODULE module_5 ON CHAIN-INPUT. + + + ENDCHAIN. + + + + PROCESS ON HELP-REQUEST. + + + FIELD field_help MODULE module_f1. + + + + PROCESS ON VALUE-REQUEST. + + + FIELD conn MODULE module_f4. + + + FIELD carr MODULE module_dropdown. + + +
+ +
+ ZDEMO_ABAP_DYNPRO + 9890 + E + Exit command + N + 0000 + 027 + 125 +
+ + + SCREEN + SCREEN + + + + + SCREEN + SCREEN + TEMPLATE + LOCAL_CLASS=>MANDATORY_FIELD + ?___________________ + 002 + 006 + 020 + 020 + 001 + CHAR + X + X + O + + + SCREEN + SCREEN + PUSH + LOCAL_CLASS=>BTN_EXIT_1 + Close_1 + 004 + 006 + 007 + 007 + 001 + CLOSE1 + CHAR + N + + + SCREEN + SCREEN + PUSH + LOCAL_CLASS=>BTN_EXIT_2 + Close_2 + 004 + 016 + 007 + 007 + 001 + CLOSE2 + E + CHAR + N + + + SCREEN + SCREEN + TEXT + DYNNR_TEXT5 + Dynpro_Number: + 009 + 006 + 014 + 014 + 001 + CHAR + N + X + + + SCREEN + SCREEN + TEMPLATE + DYN_NUM + _________ + 009 + 021 + 009 + 009 + 001 + CHAR + X + X + N + + + SCREEN + SCREEN + OKCODE + OK_CODE + ____________________ + 020 + 020 + 001 + CHAR + X + + + + + PROCESS BEFORE OUTPUT. + + + MODULE STATUS_9890. + + + + PROCESS AFTER INPUT. + + + + MODULE USER_COMMAND_9890. + + + MODULE CANCEL AT EXIT-COMMAND. + + +
+ +
+ ZDEMO_ABAP_DYNPRO + 9900 + E + Controls + N + 9900 + 027 + 126 +
+ + + SCREEN + SCREEN + + + TABLE_CTRL + FLIGHTS + SCREEN + 003 + 002 + 056 + 010 + X + X + X + X + ENTRY + X + X + X + X + X + MULTIPLE + SINGLE + 002 + + + STRIP_CTRL + TABSTR + SCREEN + 003 + 063 + 053 + 010 + + + SUBSCREEN + SUB + TABSTR + 005 + 064 + 049 + 007 + X + X + + + CUST_CTRL + ALVCONT + SCREEN + 016 + 002 + 057 + 010 + + + + + SCREEN + SCREEN + TEXT + TEXT_TC + Table_control + 001 + 002 + 013 + 013 + 001 + CHAR + N + + + SCREEN + SCREEN + TEXT + TEXT_TSC + Tabstrip_control + 001 + 063 + 016 + 016 + 001 + CHAR + N + + + SCREEN + SCREEN + TEXT + TEXT_ALV + ALV_Grid_as_example_for_a_GUI_control + 014 + 002 + 037 + 037 + 001 + CHAR + N + + + SCREEN + SCREEN + TEXT + DYNNR_TEXT6 + Dynpro_Number: + 016 + 092 + 014 + 014 + 001 + CHAR + N + X + + + SCREEN + SCREEN + TEMPLATE + DYN_NUM + _________ + 016 + 107 + 009 + 009 + 001 + CHAR + X + X + N + + + SCREEN + SCREEN + OKCODE + OK_CODE + ____________________ + 020 + 020 + 001 + CHAR + X + + + TABLE_CTRL + FLIGHTS + TEXT + TAB + Flights__ + 001 + 009 + 053 + 001 + CHAR + N + X + + + TABLE_CTRL + FLIGHTS + TEXT + ZDEMO_ABAP_FLSCH-CARRID + ID + 001 + 001 + 002 + 003 + 001 + CHAR + X + F + X + N + X + X + + + TABLE_CTRL + FLIGHTS + TEXT + ZDEMO_ABAP_FLSCH-CONNID + Num + 001 + 002 + 003 + 004 + 001 + CHAR + X + F + X + N + X + X + + + TABLE_CTRL + FLIGHTS + TEXT + ZDEMO_ABAP_FLSCH-CITYFROM + From_ + 001 + 003 + 005 + 020 + 001 + CHAR + X + F + N + X + + + TABLE_CTRL + FLIGHTS + TEXT + ZDEMO_ABAP_FLSCH-CITYTO + To_ + 001 + 004 + 003 + 020 + 001 + CHAR + X + F + N + X + + + TABLE_CTRL + FLIGHTS + TEMPLATE + ZDEMO_ABAP_FLSCH-CARRID + 001 + 001 + 003 + 003 + 001 + CHAR + X + X + X + X + N + + + TABLE_CTRL + FLIGHTS + TEMPLATE + ZDEMO_ABAP_FLSCH-CONNID + 001 + 002 + 004 + 004 + 001 + NUMC + X + X + X + X + N + + + TABLE_CTRL + FLIGHTS + TEMPLATE + ZDEMO_ABAP_FLSCH-CITYFROM + 001 + 003 + 020 + 020 + 001 + CHAR + X + X + N + + + TABLE_CTRL + FLIGHTS + TEMPLATE + ZDEMO_ABAP_FLSCH-CITYTO + 001 + 004 + 020 + 020 + 001 + CHAR + X + X + N + + + STRIP_CTRL + TABSTR + PUSH + TAB1 + Tab_1__ + 001 + 001 + 007 + 005 + 001 + TAB1 + CHAR + N + SUB + + + STRIP_CTRL + TABSTR + PUSH + TAB2 + Tab_2__ + 001 + 002 + 007 + 005 + 001 + TAB2 + CHAR + N + SUB + + + + + PROCESS BEFORE OUTPUT. + + + MODULE STATUS_9900. + + + LOOP WITH CONTROL flights. + + + MODULE module_fill_table. + + + ENDLOOP. + + + + CALL SUBSCREEN sub INCLUDING sy-repid number. + + + + PROCESS AFTER INPUT. + + + CALL SUBSCREEN sub. + + + + LOOP WITH CONTROL FLIGHTS. + + + MODULE module_read_table. + + + ENDLOOP. + + + + MODULE USER_COMMAND_9900. + + +
+ +
+ ZDEMO_ABAP_DYNPRO + 9910 + E + Subscreen 1 + I + 9910 + 027 + 122 +
+ + + SCREEN + SCREEN + + + + + SCREEN + SCREEN + TEXT + TEXT_SC_1 + This_is_subscreen_1. + 002 + 003 + 020 + 020 + 001 + CHAR + N + + + SCREEN + SCREEN + OKCODE + ____________________ + 020 + 020 + 001 + CHAR + X + + + + + PROCESS BEFORE OUTPUT. + + + + PROCESS AFTER INPUT. + + +
+ +
+ ZDEMO_ABAP_DYNPRO + 9920 + E + Subscreen 2 + I + 9920 + 027 + 122 +
+ + + SCREEN + SCREEN + + + + + SCREEN + SCREEN + TEXT + TEXT_SC_2 + This_is_subscreen_2. + 007 + 003 + 020 + 020 + 001 + CHAR + N + + + SCREEN + SCREEN + OKCODE + ____________________ + 020 + 020 + 001 + CHAR + X + + + + + PROCESS BEFORE OUTPUT. + + + + PROCESS AFTER INPUT. + + +
+
+ + + 000007 + 000011 + 000007 + + + + DEMOSTATUS + D + 000004 + 000004 + 0001 + Demo status + + + STATUS9850 + D + 000001 + 000001 + 0001 + Demo dynpro + + + STATUS9860 + D + 000002 + 000002 + 0001 + Demo + + + STATUS9870 + D + 000003 + 000003 + 0001 + STATUS9870 + + + STATUS9880 + D + 000005 + 000005 + 0001 + Demo status + + + STATUS9890 + D + 000006 + 000006 + 0001 + STATUS9890 + + + STATUS9900 + D + 000007 + 000007 + 0001 + STATUS9900 + + + + + BACK + 001 + S + Back + Home + Home + + + CANCEL + 001 + S + ICON_CANCEL + @0W@ + Cancel + + + ENTER + 001 + S + Enter + + + GO + 001 + S + ICON_EXECUTE_OBJECT + @15@ + Go + Go + + + GOBACK + 001 + S + ICON_COLUMN_LEFT + @0D@ + Go back + Go back + + + HOME + 001 + S + @B9@ + Home + + + LEAVE + 001 + S + Exit + + + SAP + 001 + S + Open sap.com + + + SAYHI + 001 + S + Say hi + + + START + 001 + S + ICON_PREVIOUS_STEP + @B9@ + Go to first dynpro + + + STARTPAGE + 001 + S + ICON_PREVIOUS_STEP + @B9@ + STARTPAGE + Go to start + + + SWITCH + 001 + S + ICON_COLUMN_RIGHT + @0E@ + Switch + Go to other dynpro + + + + + 000011 + S + Go + + + + + 000001 + 0001 + 01 + 08 + + + 000002 + 0001 + 01 + 05 + + + 000003 + 0001 + 01 + 08 + + + 000003 + 0001 + 02 + 05 + + + 000004 + 0001 + 01 + 08 + + + 000004 + 0001 + 02 + 05 + + + 000004 + 0001 + 03 + 06 + + + 000004 + 0001 + 04 + 07 + + + 000005 + 0001 + 01 + 08 + + + 000005 + 0001 + 02 + 05 + + + 000007 + 0001 + 01 + 05 + + + + + 000001 + 00 + ENTER + 001 + + + 000001 + 08 + GO + 001 + + + 000001 + 12 + CANCEL + 001 + + + 000001 + 15 + LEAVE + 001 + + + 000002 + 05 + HOME + 001 + + + 000002 + 12 + CANCEL + 001 + + + 000002 + 15 + LEAVE + 001 + + + 000003 + 00 + ENTER + 001 + + + 000003 + 05 + HOME + 001 + + + 000003 + 08 + GO + 001 + + + 000003 + 12 + CANCEL + 001 + + + 000003 + 15 + LEAVE + 001 + + + 000004 + 00 + ENTER + 001 + + + 000004 + 05 + HOME + 001 + + + 000004 + 06 + SAYHI + 001 + + + 000004 + 07 + SAP + 001 + + + 000004 + 08 + GO + 001 + + + 000004 + 12 + CANCEL + 001 + + + 000004 + 15 + LEAVE + 001 + + + 000005 + 00 + ENTER + 001 + + + 000005 + 05 + HOME + 001 + + + 000005 + 08 + GO + 001 + + + 000005 + 12 + CANCEL + 001 + + + 000005 + 15 + LEAVE + 001 + + + 000006 + 12 + CANCEL + 001 + + + 000007 + 05 + HOME + 001 + + + 000007 + 12 + CANCEL + 001 + + + 000007 + 15 + LEAVE + 001 + + + + + DEMOSTATUS + BACK + + + DEMOSTATUS + CANCEL + + + DEMOSTATUS + ENTER + + + DEMOSTATUS + GO + + + DEMOSTATUS + HOME + + + DEMOSTATUS + LEAVE + + + DEMOSTATUS + SAP + + + DEMOSTATUS + SAYHI + + + DEMOSTATUS + SWITCH + + + STATUS9850 + CANCEL + + + STATUS9850 + ENTER + + + STATUS9850 + GO + + + STATUS9850 + LEAVE + + + STATUS9860 + BACK + + + STATUS9860 + CANCEL + + + STATUS9860 + HOME + + + STATUS9860 + LEAVE + + + STATUS9860 + START + + + STATUS9870 + BACK + + + STATUS9870 + CANCEL + + + STATUS9870 + ENTER + + + STATUS9870 + GO + + + STATUS9870 + HOME + + + STATUS9870 + LEAVE + + + STATUS9870 + START + + + STATUS9880 + BACK + + + STATUS9880 + CANCEL + + + STATUS9880 + ENTER + + + STATUS9880 + GO + + + STATUS9880 + HOME + + + STATUS9880 + LEAVE + + + STATUS9890 + CANCEL + + + STATUS9900 + BACK + + + STATUS9900 + CANCEL + + + STATUS9900 + HOME + + + STATUS9900 + LEAVE + + + + + A + 000001 + D + STATUS9850 + + + P + 000001 + D + STATUS9850 + + + B + 000001 + 0001 + D + STATUS9850 + + + A + 000002 + D + STATUS9860 + + + P + 000002 + D + STATUS9860 + + + B + 000002 + 0001 + D + STATUS9860 + + + A + 000003 + D + STATUS9870 + + + P + 000003 + D + STATUS9870 + + + B + 000003 + 0001 + D + STATUS9870 + + + A + 000004 + D + Demo status + + + P + 000004 + D + Demo status + + + B + 000004 + 0001 + D + Demo status + + + A + 000005 + D + Demo status + + + P + 000005 + D + Demo status + + + B + 000005 + 0001 + D + Demo status + + + A + 000006 + D + STATUS9890 + + + P + 000006 + D + STATUS9890 + + + B + 000006 + 0001 + D + STATUS9890 + + + A + 000007 + D + STATUS9900 + + + P + 000007 + D + STATUS9900 + + + B + 000007 + 0001 + D + STATUS9900 + + + + + DEMOTITLE + Demo title &1&2&3 + + + TITLE9850 + Dynpro Demo + + + TITLE9860 + Screen Elements + + + TITLE9870 + Statements I + + + TITLE9880 + Statements II + + + TITLE9890 + Exit command + + + TITLE9900 + Controls + + + + + + R + Demo example + 12 + + +
+
+
diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.lzdemo_abap_fugrtop.abap b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.lzdemo_abap_fugrtop.abap new file mode 100644 index 0000000..e3f3880 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.lzdemo_abap_fugrtop.abap @@ -0,0 +1,3 @@ +FUNCTION-POOL ZDEMO_ABAP_FUGR. "MESSAGE-ID .. + +* INCLUDE LZDEMO_ABAP_FUGRD... " Local class definition diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.lzdemo_abap_fugrtop.xml b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.lzdemo_abap_fugrtop.xml new file mode 100644 index 0000000..14f4f7e --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.lzdemo_abap_fugrtop.xml @@ -0,0 +1,17 @@ + + + + + + LZDEMO_ABAP_FUGRTOP + S + D$ + I + S + X + D$S + X + + + + diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.saplzdemo_abap_fugr.abap b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.saplzdemo_abap_fugr.abap new file mode 100644 index 0000000..e6a2dc9 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.saplzdemo_abap_fugr.abap @@ -0,0 +1,15 @@ +******************************************************************* +* System-defined Include-files. * +******************************************************************* + INCLUDE LZDEMO_ABAP_FUGRTOP. " Global Declarations + INCLUDE LZDEMO_ABAP_FUGRUXX. " Function Modules + +******************************************************************* +* User-defined Include-files (if necessary). * +******************************************************************* +* INCLUDE LZDEMO_ABAP_FUGRF... " Subroutines +* INCLUDE LZDEMO_ABAP_FUGRO... " PBO-Modules +* INCLUDE LZDEMO_ABAP_FUGRI... " PAI-Modules +* INCLUDE LZDEMO_ABAP_FUGRE... " Events +* INCLUDE LZDEMO_ABAP_FUGRP... " Local class implement. +* INCLUDE LZDEMO_ABAP_FUGRT99. " ABAP Unit tests diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.saplzdemo_abap_fugr.xml b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.saplzdemo_abap_fugr.xml new file mode 100644 index 0000000..422bb97 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.saplzdemo_abap_fugr.xml @@ -0,0 +1,18 @@ + + + + + + SAPLZDEMO_ABAP_FUGR + S + D$ + F + S + E + X + D$S + X + + + + diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.xml b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.xml new file mode 100644 index 0000000..1beb82e --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.xml @@ -0,0 +1,63 @@ + + + + + Demo function group + + LZDEMO_ABAP_FUGRTOP + SAPLZDEMO_ABAP_FUGR + + + + ZDEMO_ABAP_DELETE_FU + 1 + Demo update function module + + + ZDEMO_ABAP_LOG_FU + 1 + Demo update function module + + + LOG + ZCL_DEMO_ABAP_SAP_LUW_HELPER=>LOG_TYPE + + + CAUSE_ERROR + ABAP_FALSE + X + ABAP_BOOL + + + + + LOG + P + + + CAUSE_ERROR + P + + + + + ZDEMO_ABAP_MODIFY_FU + 1 + Demo update function module + + + VALUES + ZDEMO_ABAP_LUW_T + + + + + VALUES + P + + + + + + + diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.zdemo_abap_delete_fu.abap b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.zdemo_abap_delete_fu.abap new file mode 100644 index 0000000..75820c6 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.zdemo_abap_delete_fu.abap @@ -0,0 +1,34 @@ +FUNCTION zdemo_abap_delete_fu. +*"---------------------------------------------------------------------- +*"*"Update Function Module: +*" +*"*"Local Interface: +*"---------------------------------------------------------------------- + + + DELETE FROM zdemo_abap_luw_t. + + zcl_demo_abap_sap_luw_helper=>create_log_entries( + VALUE #( ( id = 70 + context = 'Function module' + name = 'zdemo_abap_delete_fu' + details = `Function module called` + timestamp = utclong_current( ) ) + ( id = 71 + context = 'Function module' + name = 'zdemo_abap_delete_fu' + details = zcl_demo_abap_sap_luw_helper=>get_work_process_info( ) + timestamp = utclong_current( ) ) + ( id = 72 + context = 'Function module' + name = 'zdemo_abap_delete_fu' + details = `SAP LUW Key: ` && cl_system_transaction_state=>get_sap_luw_key( ) + timestamp = utclong_current( ) ) + ( id = 73 + context = 'Function module' + name = 'zdemo_abap_delete_fu' + details = zcl_demo_abap_sap_luw_helper=>checking_transaction_state( ) + timestamp = utclong_current( ) ) ) ). + + +ENDFUNCTION. diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.zdemo_abap_log_fu.abap b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.zdemo_abap_log_fu.abap new file mode 100644 index 0000000..011cb70 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.zdemo_abap_log_fu.abap @@ -0,0 +1,41 @@ +FUNCTION ZDEMO_ABAP_LOG_FU. +*"---------------------------------------------------------------------- +*"*"Update Function Module: +*" +*"*"Local Interface: +*" IMPORTING +*" VALUE(LOG) TYPE ZCL_DEMO_ABAP_SAP_LUW_HELPER=>LOG_TYPE +*" VALUE(CAUSE_ERROR) TYPE ABAP_BOOL DEFAULT ABAP_FALSE +*"---------------------------------------------------------------------- + + + MODIFY zdemo_abap_logt FROM TABLE @log. + + zcl_demo_abap_sap_luw_helper=>create_log_entries( + VALUE #( ( id = 90 + context = `Function module` + name = `zdemo_abap_log_fu` + details = `Function module called` + timestamp = utclong_current( ) ) + ( id = 91 + context = `Function module` + name = `zdemo_abap_log_fu` + details = zcl_demo_abap_sap_luw_helper=>get_work_process_info( ) + timestamp = utclong_current( ) ) + ( id = 92 + context = `Function module` + name = `zdemo_abap_log_fu` + details = `SAP LUW Key: ` && cl_system_transaction_state=>get_sap_luw_key( ) + timestamp = utclong_current( ) ) + ( id = 93 + context = `Function module` + name = `zdemo_abap_log_fu` + details = zcl_demo_abap_sap_luw_helper=>checking_transaction_state( ) + timestamp = utclong_current( ) ) ) ). + + IF cause_error = abap_true. + DATA(error_not_caught) = 1 / 0. + ENDIF. + + +ENDFUNCTION. diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.zdemo_abap_modify_fu.abap b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.zdemo_abap_modify_fu.abap new file mode 100644 index 0000000..86fe044 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_fugr.fugr.zdemo_abap_modify_fu.abap @@ -0,0 +1,38 @@ +FUNCTION ZDEMO_ABAP_MODIFY_FU. +*"---------------------------------------------------------------------- +*"*"Update Function Module: +*" +*"*"Local Interface: +*" IMPORTING +*" VALUE(VALUES) TYPE ZDEMO_ABAP_LUW_T +*"---------------------------------------------------------------------- + + values-time_stamp = utclong_current( ). + + MODIFY zdemo_abap_luw_t FROM @values. + + zcl_demo_abap_sap_luw_helper=>create_log_entries( + VALUE #( ( id = 80 + context = 'Function module' + name = 'zdemo_abap_modify_fu' + details = `Function module called` + timestamp = utclong_current( ) ) + ( id = 81 + context = 'Function module' + name = 'zdemo_abap_modify_fu' + details = zcl_demo_abap_sap_luw_helper=>get_work_process_info( ) + timestamp = utclong_current( ) ) + ( id = 82 + context = 'Function module' + name = 'zdemo_abap_modify_fu' + details = `SAP LUW Key: ` && cl_system_transaction_state=>get_sap_luw_key( ) + timestamp = utclong_current( ) ) + ( id = 83 + context = 'Function module' + name = 'zdemo_abap_modify_fu' + details = zcl_demo_abap_sap_luw_helper=>checking_transaction_state( ) + timestamp = utclong_current( ) ) ) ). + + + +ENDFUNCTION. diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_logt.tabl.xml b/src/test_abap_cheat_sheets_classic/zdemo_abap_logt.tabl.xml new file mode 100644 index 0000000..b6a96ad --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_logt.tabl.xml @@ -0,0 +1,83 @@ + + + + + + ZDEMO_ABAP_LOGT + E + TRANSP + X + Log table + E + A + 1 + + + ZDEMO_ABAP_LOGT + A + 0 + APPL0 + N + + + + CLIENT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + ID + X + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + CONTEXT + 0 + C + 000030 + CHAR + 000015 + CHAR + + + NAME + 0 + C + 000040 + CHAR + 000020 + CHAR + + + DETAILS + 0 + g + 000008 + STRG + STRG + + + TIMESTAMP + 0 + p + 000008 + UTCL + 000027 + UTCL + + + + + diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_luw_t.tabl.xml b/src/test_abap_cheat_sheets_classic/zdemo_abap_luw_t.tabl.xml new file mode 100644 index 0000000..9ac5344 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_luw_t.tabl.xml @@ -0,0 +1,75 @@ + + + + + + ZDEMO_ABAP_LUW_T + E + TRANSP + X + Demo database table + E + A + 1 + + + ZDEMO_ABAP_LUW_T + A + 0 + APPL0 + N + + + + CLIENT + X + 0 + C + 000006 + X + CLNT + 000003 + CLNT + + + KEY_FIELD + X + 0 + X + 000004 + X + INT4 + 000010 + INT4 + + + CH + 0 + C + 000020 + CHAR + 000010 + CHAR + + + NUM + 0 + X + 000004 + INT4 + 000010 + INT4 + + + TIME_STAMP + 0 + p + 000008 + UTCL + 000027 + UTCL + + + + + diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_sap_luw.prog.abap b/src/test_abap_cheat_sheets_classic/zdemo_abap_sap_luw.prog.abap new file mode 100644 index 0000000..3745aaa --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_sap_luw.prog.abap @@ -0,0 +1,513 @@ +*&---------------------------------------------------------------------* +*& ABAP cheat sheet: SAP LUW +*& +*&-------------------------- PURPOSE ----------------------------------* +*& - Example to demonstrate ABAP syntax in the context of the SAP LUW. +*& - Topics covered: Using update function modules and subroutines as +*& bundling techniques, synchronous/asynchronous/local updates +*& +*&----------------------- GETTING STARTED -----------------------------* +*& - Open the program with the ABAP development tools for Eclipse (ADT). +*& - Choose F8 to run the program. +*& - Select the radio buttons and choose 'Go' to progress with the program. +*& +*&----------------------------- 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. +*&---------------------------------------------------------------------* +REPORT zdemo_abap_sap_luw. + +DATA: tab_struc TYPE zdemo_abap_luw_t, + ok_code LIKE sy-ucomm, + save_ok LIKE sy-ucomm, + del_insert TYPE abap_bool, + perform TYPE abap_bool, + commit TYPE abap_bool, + commit_wait TYPE abap_bool, + local_update TYPE abap_bool, + err TYPE abap_bool, + rollback TYPE abap_bool, + terminate TYPE abap_bool, + dynnr TYPE sy-dynnr, + counter TYPE i, + log_itab TYPE zcl_demo_abap_sap_luw_helper=>log_type. + +CLASS lcl DEFINITION. + PUBLIC SECTION. + + TYPES: BEGIN OF request4log, + log TYPE zdemo_abap_logt, + called TYPE abap_bool, + wp_info TYPE abap_bool, + luw_key TYPE abap_bool, + tx_state TYPE abap_bool, + END OF request4log. + + CLASS-METHODS: prep, + create_log_entries IMPORTING req TYPE request4log. +ENDCLASS. + +CLASS lcl IMPLEMENTATION. + + METHOD prep. + DELETE FROM zdemo_abap_luw_t. + + INSERT zdemo_abap_luw_t FROM TABLE @( VALUE #( + ( key_field = 1 + ch = 'DEMO DATA' + num = 11 + time_stamp = utclong_current( ) ) + ( key_field = 2 + ch = 'THAT WAS' + num = 22 + time_stamp = utclong_current( ) ) + ( key_field = 3 + ch = 'CREATED' + num = 33 + time_stamp = utclong_current( ) ) + ( key_field = 4 + ch = 'IN ADVANCE' + num = 44 + time_stamp = utclong_current( ) ) ) ). + + DELETE FROM zdemo_abap_logt. + ENDMETHOD. + + METHOD create_log_entries. + + IF req-called = abap_true. + counter += 1. + INSERT VALUE #( id = counter + context = req-log-context + name = req-log-name + details = `Called!` + timestamp = utclong_current( ) + ) INTO TABLE log_itab. + ENDIF. + + IF req-luw_key = abap_true. + counter += 1. + INSERT VALUE #( id = counter + context = req-log-context + name = req-log-name + details = `SAP LUW key: ` && + cl_system_transaction_state=>get_sap_luw_key( ) + timestamp = utclong_current( ) + ) INTO TABLE log_itab. + + ENDIF. + + IF req-wp_info = abap_true. + counter += 1. + INSERT VALUE #( id = counter + context = req-log-context + name = req-log-name + details = + zcl_demo_abap_sap_luw_helper=>get_work_process_info( ) + timestamp = utclong_current( ) + ) INTO TABLE log_itab. + ENDIF. + + IF req-tx_state = abap_true. + counter += 1. + INSERT VALUE #( + id = counter + context = req-log-context + name = req-log-name + details = + zcl_demo_abap_sap_luw_helper=>checking_transaction_state( ) + timestamp = utclong_current( ) + ) INTO TABLE log_itab. + ENDIF. + ENDMETHOD. +ENDCLASS. + +FORM delete. + zcl_demo_abap_sap_luw_helper=>subr_delete( ). + + lcl=>create_log_entries( + VALUE #( log = VALUE #( context = 'Subroutine' + name = 'delete' ) + called = abap_true + luw_key = abap_true + tx_state = abap_true + wp_info = abap_true ) ). +ENDFORM. + +FORM insert. + + DATA values TYPE zdemo_abap_luw_t. + IMPORT values = values FROM MEMORY ID 'DEMO_INSERT'. + IF sy-subrc = 0. + values-time_stamp = utclong_current( ). + zcl_demo_abap_sap_luw_helper=>subr_insert( values ). + ENDIF. + + lcl=>create_log_entries( + VALUE #( log = VALUE #( context = 'Subroutine' + name = 'insert' ) + called = abap_true + luw_key = abap_true + tx_state = abap_true + wp_info = abap_true ) ). +ENDFORM. + +FORM update_log. + lcl=>create_log_entries( + VALUE #( log = VALUE #( context = 'Subroutine' + name = 'update_log' ) + called = abap_true + luw_key = abap_true + tx_state = abap_true + wp_info = abap_true ) ). + + zcl_demo_abap_sap_luw_helper=>subr_update_log( log_itab ). +ENDFORM. + +FORM call_on_rollback. + + "No implementation here. Only to demonstrate that the + "subroutine is called in the event of triggering a + "rollback. + + lcl=>create_log_entries( + VALUE #( log = VALUE #( context = 'Subroutine' + name = 'call_on_rollback' ) + called = abap_true + luw_key = abap_true + tx_state = abap_true ) ). +ENDFORM. + +END-OF-SELECTION. + + lcl=>prep( ). + + counter += 1. + INSERT VALUE #( + id = counter + context = '-' + name = '-' + details = |Program { sy-repid } has been started.| && + `The first dynpro is about to be called.` + timestamp = utclong_current( ) + ) INTO TABLE log_itab. + + counter += 1. + INSERT VALUE #( id = counter + context = '-' + name = '-' + details = `SAP LUW Key: ` && + cl_system_transaction_state=>get_sap_luw_key( ) + timestamp = utclong_current( ) + ) INTO TABLE log_itab. + + CALL SCREEN 9750. + +********************************************************************** + +***************** Dialog modules for 9750 **************************** + +MODULE status_9750 OUTPUT. + SET PF-STATUS 'STATUS9750'. + SET TITLEBAR 'TITLE9750'. + + lcl=>create_log_entries( + VALUE #( log = VALUE #( context = 'PBO' + name = 'status_9750' ) + called = abap_true + luw_key = abap_true + tx_state = abap_true + wp_info = abap_true ) ). + + zcl_demo_abap_sap_luw_helper=>cc_alv_display( 'DBENTRIES' ). + dynnr = sy-dynnr. +ENDMODULE. + +MODULE user_command_9750 INPUT. + lcl=>create_log_entries( + VALUE #( log = VALUE #( context = 'PAI' + name = 'user_command_9750' ) + called = abap_true + luw_key = abap_true + tx_state = abap_true + wp_info = abap_true ) ). + + save_ok = ok_code. + CLEAR ok_code. + + CASE save_ok. + WHEN 'GO' OR 'ENTER'. + + IF del_insert = abap_true. + CALL FUNCTION 'ZDEMO_ABAP_DELETE_FU' IN UPDATE TASK. + + MESSAGE `A function module has been registered ` && + `that clears the database table.` TYPE 'I'. + + ELSEIF local_update = abap_true. + + SET UPDATE TASK LOCAL. + + lcl=>create_log_entries( + VALUE #( log = VALUE #( context = 'PAI' + name = 'user_command_9750' ) + luw_key = abap_true + tx_state = abap_true + wp_info = abap_true ) ). + + CALL FUNCTION 'ZDEMO_ABAP_DELETE_FU' IN UPDATE TASK. + + MESSAGE `Local update has been activated. A function ` && + `module has been registered that clears the database table.` && + ` No matter if you choose COMMIT WORK or COMMIT WORK AND ` && + `WAIT in the next step, it will be a synchronous update.` TYPE 'I'. + + ELSEIF perform = abap_true. + + PERFORM delete ON COMMIT. + PERFORM call_on_rollback ON ROLLBACK. + + lcl=>create_log_entries( + VALUE #( log = VALUE #( context = 'PAI' + name = 'user_command_9750' ) + luw_key = abap_true + tx_state = abap_true + wp_info = abap_true ) ). + + MESSAGE `Subroutines have been registered for commit ` && + `and rollback.` TYPE 'I'. + + ENDIF. + + WHEN OTHERS. + LEAVE PROGRAM. + ENDCASE. +ENDMODULE. + +***************** Dialog modules for 9760 **************************** + +MODULE status_9760 OUTPUT. + SET PF-STATUS 'STATUS9760'. + SET TITLEBAR 'TITLE9760'. + + lcl=>create_log_entries( + VALUE #( log = VALUE #( context = 'PBO' + name = 'status_9760' ) + called = abap_true + luw_key = abap_true + tx_state = abap_true + wp_info = abap_true ) ). + + dynnr = sy-dynnr. +ENDMODULE. + +MODULE user_command_9760 INPUT. + lcl=>create_log_entries( + VALUE #( log = VALUE #( context = 'PAI' + name = 'user_command_9760' ) + called = abap_true + luw_key = abap_true + tx_state = abap_true + wp_info = abap_true ) ). + + save_ok = ok_code. + CLEAR ok_code. + + CASE save_ok. + WHEN 'GO' OR 'ENTER'. + + IF perform = abap_true. + "Registered subroutines cannot have any parameter interface. + "Therefore, data can only be passed through external interfaces, + "for example, the ABAP memory. + EXPORT values = tab_struc TO MEMORY ID 'DEMO_INSERT'. + PERFORM insert ON COMMIT. + + MESSAGE `A subroutine that inserts the entries in the ` && + `database table has been registered. ` && + `When you select a commit option in the next step, ` && + `COMMIT WORK and COMMIT WORK AND WAIT have ` && + `the same effect. The subroutines are executed in ` && + `the current work process in this case.` TYPE 'I'. + ELSE. + CALL FUNCTION 'ZDEMO_ABAP_MODIFY_FU' IN UPDATE TASK + EXPORTING + values = tab_struc. + MESSAGE `An update function module that inserts the ` && + `entries in the database table has ` && + `been registered.` TYPE 'I'. + ENDIF. + WHEN OTHERS. + LEAVE PROGRAM. + ENDCASE. +ENDMODULE. + +***************** Dialog modules for 9770 **************************** + +MODULE status_9770 OUTPUT. + SET PF-STATUS 'STATUS9770'. + SET TITLEBAR 'TITLE9770'. + + lcl=>create_log_entries( + VALUE #( log = VALUE #( context = 'PBO' + name = 'status_9770' ) + called = abap_true + luw_key = abap_true + tx_state = abap_true + wp_info = abap_true ) ). + + dynnr = sy-dynnr. + + IF perform = 'X'. + LOOP AT SCREEN INTO DATA(scr). + IF scr-name = 'EXCURSION_WP' + OR scr-name = 'WAIT' + OR scr-name = 'FAIL_LUW' + OR scr-name = 'ERR' + OR scr-name = 'TERMINATE' + OR scr-name = 'ERROR_A'. + scr-invisible = '1'. + MODIFY SCREEN FROM scr. + ENDIF. + ENDLOOP. + ENDIF. +ENDMODULE. + +MODULE user_command_9770 INPUT. + lcl=>create_log_entries( + VALUE #( log = VALUE #( context = 'PAI' + name = 'user_command_9770' ) + called = abap_true + luw_key = abap_true + tx_state = abap_true + wp_info = abap_true ) ). + + save_ok = ok_code. + CLEAR ok_code. + + CASE save_ok. + + WHEN 'GO' OR 'ENTER' OR 'OK'. + + CASE 'X'. + + WHEN commit. + IF perform = abap_false. + CALL FUNCTION 'ZDEMO_ABAP_LOG_FU' IN UPDATE TASK + EXPORTING + log = log_itab. + MESSAGE `An update function module that inserts ` && + `entries for the log has been registered. ` && + `COMMIT WORK is about to be executed.` TYPE 'I'. + ELSE. + PERFORM update_log ON COMMIT. + + MESSAGE `A subroutine that inserts entries for the ` && + `log has been registered ` && + `COMMIT WORK is about to be executed.` TYPE 'I'. + ENDIF. + + COMMIT WORK. + + SELECT COUNT(*) FROM zdemo_abap_luw_t INTO @DATA(entries). + + MESSAGE `COMMIT WORK has been executed. Current ` && + `number of database table entries: ` && + entries && + `. If it is 4 and not 1, the update will be done ` && + `asynchronously.` TYPE 'I'. + WHEN commit_wait. + IF perform = abap_false. + CALL FUNCTION 'ZDEMO_ABAP_LOG_FU' IN UPDATE TASK + EXPORTING + log = log_itab. + MESSAGE `An update function module that inserts ` && + `entries for the log has been registered. ` && + `COMMIT WORK AND WAIT is about to be executed.` TYPE 'I'. + ELSE. + PERFORM update_log ON COMMIT. + + MESSAGE `A subroutine that inserts entries for the ` && + `log has been registered ` && + `COMMIT WORK AND WAIT is about to be executed.` TYPE 'I'. + ENDIF. + + COMMIT WORK AND WAIT. + + SELECT COUNT(*) FROM zdemo_abap_luw_t INTO @entries. + + MESSAGE `COMMIT WORK AND WAIT has been executed. Current ` && + `number of database table entries: ` && + entries && + `. If it is 1, the update has been done synchronously.` TYPE 'I'. + WHEN rollback. + ROLLBACK WORK. + + MESSAGE `ROLLBACK WORK has been executed. A rollback has ` && + `been triggered. The original database table content ` && + `should be displayed.` + TYPE 'I'. + + MESSAGE `Note: As an exception, and only for this demo ` && + `example, the sample implementation includes a ` && + `database update to the log table after the ` && + `table after the rollback, just to show the progress of the ` && + `execution up to that point.` TYPE 'I' DISPLAY LIKE 'E'. + + "Update log table + zcl_demo_abap_sap_luw_helper=>create_log_entries( log_itab ). + + "Log information for triggering a rollback + zcl_demo_abap_sap_luw_helper=>create_log_entries( + VALUE #( ( id = 99 + context = '-' + name = '-' + details = `Rollback triggered` + timestamp = utclong_current( ) ) ) ). + WHEN err. + CALL FUNCTION 'ZDEMO_ABAP_LOG_FU' IN UPDATE TASK + EXPORTING + log = log_itab + cause_error = abap_true. + + IF local_update = abap_true. + MESSAGE `A function module has been registered that ` && + `causes an error (zero division). COMMIT WORK AND WAIT ` && + `is about to be executed. You are directly informed ` && + `about the error and the program is terminated. All ` && + `changes are implicitly rolled back. You can check ` && + `transaction ST22 for the error caused.` TYPE 'I'. + ELSE. + MESSAGE `Function module was registered that ` && + `causes an error (zero division). COMMIT WORK AND WAIT` && + ` will be excetued. All changes are implicitly rolled back.` && + ` When you start over or leave the example in the next dynpro, ` && + `you should be informed about the issue in the Business Workplace. ` && + `Also check ST22.` TYPE 'I'. + ENDIF. + + COMMIT WORK AND WAIT. + + WHEN terminate. + MESSAGE `This is an error message of type A. ` && + `It termiantes the program. All changes are ` && + `implicitly rolled back. ` && + `You may want to check the database table entries of ` && + `the example that remain unchanged.` TYPE 'A'. + ENDCASE. + + WHEN OTHERS. + LEAVE PROGRAM. + ENDCASE. + + SUBMIT ('ZDEMO_ABAP_SAP_LUW_DISPL'). +ENDMODULE. diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_sap_luw.prog.xml b/src/test_abap_cheat_sheets_classic/zdemo_abap_sap_luw.prog.xml new file mode 100644 index 0000000..ea8c7f2 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_sap_luw.prog.xml @@ -0,0 +1,864 @@ + + + + + + ZDEMO_ABAP_SAP_LUW + S + 1 + X + D$S + X + + + +
+ ZDEMO_ABAP_SAP_LUW + 9750 + E + Delete database table entries + N + 9760 + 027 + 121 +
+ + + SCREEN + SCREEN + + + RADIOGROUP + %A_DEL_INSERT + SCREEN + 006 + 004 + 076 + 003 + + + CUST_CTRL + DBENTRIES + SCREEN + 012 + 005 + 068 + 007 + + + + + SCREEN + SCREEN + TEXT + TX1 + The_SAP_LUW_is_started._In_this_example,_all_the_entries_in_a_database_table_(see_below)_are_to_be_deleted. + 002 + 004 + 107 + 107 + 001 + CHAR + N + + + SCREEN + SCREEN + TEXT + TX2 + The_ABAP_SQL_statement_is_implemented_in_an_update_function_module_and_a_subroutine._ + 003 + 004 + 085 + 085 + 001 + CHAR + N + + + SCREEN + SCREEN + TEXT + TX3 + Choose_one_of_the_following_options: + 004 + 004 + 036 + 036 + 001 + CHAR + N + + + SCREEN + SCREEN + TEXT + TX4 + Current_database_table_entries: + 010 + 004 + 031 + 031 + 001 + CHAR + N + + + SCREEN + SCREEN + TEXT + TX5 + Dynpro_number: + 021 + 005 + 014 + 014 + 001 + CHAR + N + + + SCREEN + SCREEN + TEMPLATE + DYNNR + ___________ + 021 + 021 + 011 + 011 + 001 + CHAR + X + X + N + + + SCREEN + SCREEN + OKCODE + OK_CODE + ____________________ + 020 + 020 + 001 + CHAR + X + + + RADIOGROUP + %A_DEL_INSERT + RADIO + DEL_INSERT + Use_update_task_(CALL_FUNCTION_..._IN_UPDATE_TASK_statement) + 006 + 004 + 062 + 062 + 001 + CHAR + X + X + + + RADIOGROUP + %A_DEL_INSERT + RADIO + LOCAL_UPDATE + Use_update_task_and_perform_local_update_(SET_UPDATE_TASK_LOCAL_statement) + 007 + 004 + 076 + 076 + 001 + CHAR + X + X + + + RADIOGROUP + %A_DEL_INSERT + RADIO + PERFORM + Use_subroutines_(PERFORM_statement) + 008 + 004 + 037 + 037 + 001 + CHAR + X + X + + + + + PROCESS BEFORE OUTPUT. + + + MODULE STATUS_9750. + + + + PROCESS AFTER INPUT. + + + MODULE USER_COMMAND_9750. + + +
+ +
+ ZDEMO_ABAP_SAP_LUW + 9760 + E + Create database table entry + N + 9770 + 027 + 121 +
+ + + SCREEN + SCREEN + + + + + SCREEN + SCREEN + TEXT + TX7 + To_create_a_database_table_entry,_make_entries_in_the_following_input_fields: + 002 + 003 + 077 + 077 + 001 + CHAR + N + + + SCREEN + SCREEN + TEXT + TX8 + KEY_FIELD + 004 + 003 + 009 + 009 + 001 + CHAR + N + X + + + SCREEN + SCREEN + TEMPLATE + TAB_STRUC-KEY_FIELD + _______.__ + 004 + 015 + 010 + 010 + 001 + INT4 + X + X + + + SCREEN + SCREEN + TEXT + TX11 + Type_i + 004 + 028 + 006 + 006 + 001 + CHAR + N + + + SCREEN + SCREEN + TEXT + TX9 + CH + 006 + 003 + 002 + 002 + 001 + CHAR + N + X + + + SCREEN + SCREEN + TEMPLATE + TAB_STRUC-CH + __________ + 006 + 015 + 010 + 010 + 001 + CHAR + X + X + + + SCREEN + SCREEN + TEXT + TX12 + Type_c_length_10 + 006 + 028 + 016 + 016 + 001 + CHAR + N + + + SCREEN + SCREEN + TEXT + TX10 + NUM + 008 + 003 + 003 + 003 + 001 + CHAR + N + X + + + SCREEN + SCREEN + TEMPLATE + TAB_STRUC-NUM + _______.__ + 008 + 015 + 010 + 010 + 001 + INT4 + X + X + + + SCREEN + SCREEN + TEXT + TX13 + Type_i + 008 + 028 + 006 + 006 + 001 + CHAR + N + + + SCREEN + SCREEN + TEXT + TX14 + Dynpro_number: + 011 + 003 + 014 + 014 + 001 + CHAR + N + + + SCREEN + SCREEN + TEMPLATE + DYNNR + ___________ + 011 + 019 + 011 + 011 + 001 + CHAR + X + X + N + + + SCREEN + SCREEN + OKCODE + OK_CODE + ____________________ + 020 + 020 + 001 + CHAR + X + + + + + PROCESS BEFORE OUTPUT. + + + MODULE STATUS_9760. + + + + PROCESS AFTER INPUT. + + + MODULE USER_COMMAND_9760. + + +
+ +
+ ZDEMO_ABAP_SAP_LUW + 9770 + E + End SAP LUW + N + 0000 + 027 + 121 +
+ + + SCREEN + SCREEN + + + RADIOGROUP + %A_COMMIT + SCREEN + 004 + 003 + 048 + 005 + + + + + SCREEN + SCREEN + TEXT + TX6 + Select_one_of_the_following_options_to_proceed: + 002 + 003 + 047 + 047 + 001 + CHAR + N + X + + + SCREEN + SCREEN + TEXT + %#AUTOTEXT009 + Dynpro_number: + 012 + 003 + 014 + 014 + 001 + CHAR + N + + + SCREEN + SCREEN + TEMPLATE + DYNNR + ___________ + 012 + 018 + 011 + 011 + 001 + CHAR + X + X + N + + + SCREEN + SCREEN + OKCODE + OK_CODE + ____________________ + 020 + 020 + 001 + CHAR + X + + + RADIOGROUP + %A_COMMIT + RADIO + COMMIT + Asynchronous_update_with_COMMIT_WORK + 004 + 003 + 038 + 038 + 001 + 1 + CHAR + X + X + + + RADIOGROUP + %A_COMMIT + RADIO + COMMIT_WAIT + Synchronous_update_with_COMMIT_WORK_AND_WAIT + 005 + 003 + 046 + 046 + 001 + 1 + CHAR + X + X + + + RADIOGROUP + %A_COMMIT + RADIO + ROLLBACK + Rollback_changes_using_ROLLBACK_WORK + 006 + 003 + 038 + 038 + 001 + CHAR + X + X + + + RADIOGROUP + %A_COMMIT + RADIO + ERR + Cause_a_failure_in_the_current_SAP_LUW + 007 + 003 + 040 + 040 + 001 + CHAR + X + X + + + RADIOGROUP + %A_COMMIT + RADIO + TERMINATE + Terminate_program_with_error_message_of_type_A + 008 + 003 + 048 + 048 + 001 + CHAR + X + X + + + + + PROCESS BEFORE OUTPUT. + + + MODULE STATUS_9770. + + + + PROCESS AFTER INPUT. + + + MODULE USER_COMMAND_9770. + + +
+
+ + + 000003 + 000003 + + + + STATUS9750 + D + 000001 + 000001 + 0001 + Delete database table entries + + + STATUS9760 + D + 000002 + 000002 + 0001 + Create database table entry + + + STATUS9770 + D + 000003 + 000003 + 0001 + End SAP LUW + + + + + ENTER + 001 + S + Enter + + + GO + 001 + S + ICON_MAPPED_RELATION + @EY@ + Go + Go + Go + + + LEAVE + 001 + S + Exit + + + + + 000001 + 0001 + 01 + 08 + + + 000002 + 0001 + 01 + 08 + + + 000003 + 0001 + 01 + 08 + + + + + 000001 + 00 + ENTER + 001 + + + 000001 + 08 + GO + 001 + + + 000001 + 15 + LEAVE + 001 + + + 000002 + 00 + ENTER + 001 + + + 000002 + 08 + GO + 001 + + + 000002 + 15 + LEAVE + 001 + + + 000003 + 00 + ENTER + 001 + + + 000003 + 08 + GO + 001 + + + 000003 + 15 + LEAVE + 001 + + + + + STATUS9750 + ENTER + + + STATUS9750 + GO + + + STATUS9750 + LEAVE + + + STATUS9760 + ENTER + + + STATUS9760 + GO + + + STATUS9760 + LEAVE + + + STATUS9770 + ENTER + + + STATUS9770 + GO + + + STATUS9770 + LEAVE + + + + + A + 000001 + D + Delete database table entries + + + P + 000001 + D + Delete database table entries + + + B + 000001 + 0001 + D + Delete database table entries + + + A + 000002 + D + Create database table entry + + + P + 000002 + D + Create database table entry + + + B + 000002 + 0001 + D + Create database table entry + + + A + 000003 + D + End SAP LUW + + + P + 000003 + D + End SAP LUW + + + B + 000003 + 0001 + D + End SAP LUW + + + + + TITLE9750 + Delete database table entries + + + TITLE9760 + Create database table entry + + + TITLE9770 + End SAP LUW + + + + + + R + Demonstrating SAP LUW + 21 + + +
+
+
diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_sap_luw_displ.prog.abap b/src/test_abap_cheat_sheets_classic/zdemo_abap_sap_luw_displ.prog.abap new file mode 100644 index 0000000..7071994 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_sap_luw_displ.prog.abap @@ -0,0 +1,139 @@ +*&---------------------------------------------------------------------* +*& Demo program used in the context of the SAP LUW ABAP cheat sheet +*& +*&----------------------------- 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. +*&---------------------------------------------------------------------* +REPORT zdemo_abap_sap_luw_displ. + +DATA: ok_code LIKE sy-ucomm, + save_ok LIKE sy-ucomm, + dynnr LIKE sy-dynnr. + +END-OF-SELECTION. + + "Checks if program is started separately + SELECT COUNT(*) + FROM zdemo_abap_logt + INTO @DATA(entries). + + SELECT SINGLE id + FROM zdemo_abap_logt + WHERE id = 100 + INTO @DATA(exists). + + IF sy-subrc = 0 OR entries = 0. + DELETE FROM zdemo_abap_logt. + + DATA(check) = abap_true. + + zcl_demo_abap_sap_luw_helper=>create_log_entries( + VALUE #( ( id = 97 + context = '-' + name = '-' + details = `Choose Start over from the menu.` + timestamp = utclong_current( ) ) ) ). + ENDIF. + + zcl_demo_abap_sap_luw_helper=>create_log_entries( + VALUE #( ( id = 100 + context = '-' + name = '-' + details = |New program executed: { sy-repid }| + timestamp = utclong_current( ) ) ) ). + + CALL SCREEN 9780. + +********************************************************************** + +***************** Dialog modules for 9780 **************************** + +MODULE status_9780 OUTPUT. + SET PF-STATUS 'STATUS9780'. + SET TITLEBAR 'TITLE9780'. + + "Checks for asynchronous update + IF check = abap_false. + DATA(idx) = 0. + + DO 5 TIMES. "A maximum of 5 seconds of waiting time + SELECT SINGLE id, context + FROM zdemo_abap_logt + WHERE + "Entry inserted ... + id = 93 "by the final update function module + OR id = 99 "in case of ROLLBACK WORK + OR id = 96 "in case of the intended failure in SAP LUW + OR context = 'Subroutine' "if subroutines are used + INTO @DATA(wa). "#EC CI_CMPLX_WHERE + + IF sy-subrc = 0. + EXIT. + ELSE. + idx = sy-index. + WAIT UP TO 1 SECONDS. + ENDIF. + ENDDO. + + IF idx = 5. + MESSAGE `The asynchronous update takes too long. ` && + `You may want to restart the example and try again.` + TYPE 'I'. + ENDIF. + + ENDIF. + + zcl_demo_abap_sap_luw_helper=>create_log_entries( + VALUE #( + ( id = 101 + context = 'PBO' + name = 'status_9780' + details = `Module called` + timestamp = utclong_current( ) ) + ( id = 102 + context = 'PBO' + name = 'status_9780' + details = zcl_demo_abap_sap_luw_helper=>get_work_process_info( ) + timestamp = utclong_current( ) ) + ( id = 103 + context = 'PBO' + name = 'status_9780' + details = `SAP LUW Key: ` && + cl_system_transaction_state=>get_sap_luw_key( ) + timestamp = utclong_current( ) ) + ( id = 104 + context = 'PBO' + name = 'status_9780' + details = zcl_demo_abap_sap_luw_helper=>checking_transaction_state( ) + timestamp = utclong_current( ) ) ) ). + + zcl_demo_abap_sap_luw_helper=>cc_alv_display( 'CC_DB_FINAL' ). + zcl_demo_abap_sap_luw_helper=>cc_alv_display( container = 'CC_WP' + log = abap_true ). + dynnr = sy-dynnr. +ENDMODULE. + +MODULE user_command_9780 INPUT. + save_ok = ok_code. + CLEAR ok_code. + + CASE save_ok. + WHEN 'STARTOVER'. + ROLLBACK WORK. + DELETE FROM zdemo_abap_logt. + SUBMIT ('ZDEMO_ABAP_SAP_LUW'). + WHEN OTHERS. + DELETE FROM zdemo_abap_logt. + LEAVE PROGRAM. + ENDCASE. +ENDMODULE. diff --git a/src/test_abap_cheat_sheets_classic/zdemo_abap_sap_luw_displ.prog.xml b/src/test_abap_cheat_sheets_classic/zdemo_abap_sap_luw_displ.prog.xml new file mode 100644 index 0000000..5466da3 --- /dev/null +++ b/src/test_abap_cheat_sheets_classic/zdemo_abap_sap_luw_displ.prog.xml @@ -0,0 +1,240 @@ + + + + + + ZDEMO_ABAP_SAP_LUW_DISPL + S + 1 + X + D$S + X + + + +
+ ZDEMO_ABAP_SAP_LUW_DISPL + 9780 + E + Display database table and log + N + 9780 + 027 + 186 +
+ + + SCREEN + SCREEN + + + CUST_CTRL + CC_DB_FINAL + SCREEN + 003 + 002 + 074 + 007 + + + CUST_CTRL + CC_WP + SCREEN + 013 + 002 + 156 + 014 + + + + + SCREEN + SCREEN + TEXT + TX15 + Database_table_entries: + 001 + 002 + 023 + 023 + 001 + CHAR + N + + + SCREEN + SCREEN + TEXT + %#AUTOTEXT003 + Dynpro_number: + 001 + 130 + 014 + 014 + 001 + CHAR + N + + + SCREEN + SCREEN + TEMPLATE + DYNNR + ___________ + 001 + 146 + 011 + 011 + 001 + CHAR + X + X + N + + + SCREEN + SCREEN + TEXT + TX16 + Log_database_table_entries: + 011 + 002 + 027 + 027 + 001 + CHAR + N + + + SCREEN + SCREEN + OKCODE + OK_CODE + ____________________ + 020 + 020 + 001 + CHAR + X + + + + + PROCESS BEFORE OUTPUT. + + + MODULE STATUS_9780. + + + + PROCESS AFTER INPUT. + + + MODULE USER_COMMAND_9780. + + +
+
+ + + 000001 + 000001 + + + + STATUS9780 + D + 000001 + 000001 + 0001 + Display database table and log + + + + + LEAVE + 001 + S + Exit + + + STARTOVER + 001 + S + ICON_SYSTEM_UNDO + @2W@ + STARTOVER + Start over + Start over + + + + + 000001 + 0001 + 01 + 08 + + + + + 000001 + 08 + STARTOVER + 001 + + + 000001 + 15 + LEAVE + 001 + + + + + STATUS9780 + LEAVE + + + STATUS9780 + STARTOVER + + + + + A + 000001 + D + Display database table and log + + + P + 000001 + D + Display database table and log + + + B + 000001 + 0001 + D + Display database table and log + + + + + TITLE9780 + Display database table and log + + + + + + R + Demonstrating SAP LUW - Display + 31 + + +
+
+
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..7f3815c --- /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. +* +* In newer ABAP releases, you can 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_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..e1b4489 --- /dev/null +++ b/src/zbp_demo_abap_rap_ro_u.clas.locals_imp.abap @@ -0,0 +1,1022 @@ +*********************************************************************** +* +* 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 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. +* +*********************************************************************** + +*********************************************************************** +* 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, + 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. + + 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. + + "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. + + "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 on purpose. No global authorization restriction. + 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 = lr_key->* + %fail-cause = if_abap_behv=>cause-locked ) + TO failed-root. + CATCH cx_abap_lock_failure. + APPEND VALUE #( %key = lr_key->* + %fail-cause = if_abap_behv=>cause-unspecific ) + TO failed-root. + ENDTRY. + ENDLOOP. + CATCH cx_abap_lock_failure. + APPEND VALUE #( %fail-cause = if_abap_behv=>cause-unspecific ) + 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 ) ). + + "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. + + 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 ] ). + + 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 #( %tky = CORRESPONDING #( -%tky ) + key_ch = COND #( WHEN -%control-key_ch NE if_abap_behv=>mk-off + THEN -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 ] ). + + 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 ) ). + 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. + + 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. + + 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 ] ). + + "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. 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..58d4ea3 --- /dev/null +++ b/src/zcl_demo_abap_amdp.clas.abap @@ -0,0 +1,376 @@ +*********************************************************************** +* +* 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 the SAP BTP ABAP +* environment. For example, in SAP BTP ABAP environment only +* read-only operations are possible. In general, there are more +* syntax options available in the unrestricted ABAP language scope +* in on-premise ABAP systems. 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 WITH EMPTY KEY. + + TYPES fli_tab TYPE STANDARD TABLE OF zdemo_abap_fli WITH EMPTY KEY. + + TYPES: + "Structured data type as basis for the table type below + BEGIN OF carr_fli_struc, + carrname TYPE zdemo_abap_carr-carrname, + connid TYPE zdemo_abap_flsch-connid, + cityfrom TYPE zdemo_abap_flsch-cityfrom, + cityto TYPE zdemo_abap_flsch-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-carrid, + connid TYPE zdemo_abap_flsch-connid, + cityfrom TYPE zdemo_abap_flsch-cityfrom, + cityto TYPE zdemo_abap_flsch-cityto, + fltime TYPE zdemo_abap_flsch-fltime, + END OF fli_struc, + + "Internal table type + flsch_tab TYPE STANDARD TABLE OF zdemo_abap_flsch 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 + 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 + IMPORTING VALUE(carrid) TYPE zdemo_abap_fli-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 + IMPORTING VALUE(carrid) TYPE zdemo_abap_fli-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 + IMPORTING VALUE(carrid) TYPE zdemo_abap_fli-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 + IMPORTING VALUE(carrid) TYPE zdemo_abap_flsch-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_flight_tables=>fill_dbtabs( ). + ENDMETHOD. + + + METHOD flight_analysis + BY DATABASE FUNCTION + FOR HDB + LANGUAGE SQLSCRIPT + OPTIONS READ-ONLY + USING zdemo_abap_flsch "Two database tables are used and must both be specified here. + zdemo_abap_carr. +* Reading data from two database tables + itab_cities = + select DISTINCT + zdemo_abap_flsch.mandt as client, + zdemo_abap_flsch.carrid as carrier_id, + zdemo_abap_flsch.airpfrom as airport_from, + zdemo_abap_flsch.airpto as airport_to, + zdemo_abap_flsch.fltime as flight_time, + zdemo_abap_flsch.distance as flight_distance, + zdemo_abap_flsch.distid as unit + from zdemo_abap_flsch; + + itab_carrier_names = + select distinct + zdemo_abap_carr.mandt as client, + zdemo_abap_carr.carrid as carrier_id, + zdemo_abap_carr.carrname as carrier_name + from zdemo_abap_carr; + +* 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 zdemo_abap_flsch. +* AMDP table function to be called by other AMDP methods only. +* In the example, joined data from two database table are returned. + RETURN + SELECT ca.carrname, fl.connid, fl.cityfrom, fl.cityto + FROM zdemo_abap_carr as ca + INNER JOIN zdemo_abap_flsch 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. +* Simple data selection + fli_tab = SELECT * + FROM "ZDEMO_ABAP_FLI" + WHERE carrid = :carrid + ORDER BY carrid; + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: ABAP AMDP` ). + + output->display( `1) AMDP procedure` ). + + "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 ). + + output->display( input = amdp_proc_res name = `amdp_proc_res` ). + +********************************************************************** + + output->next_section( `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 database table. + "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). + + output->display( error1->get_text( ) ). + + ENDTRY. + + output->display( input = call_amdp_res name = `call_amdp_res` ). + + +********************************************************************** + + output->next_section( `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. + + output->display( input = 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' ). + +********************************************************************** + + output->next_section( `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 database tables 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). + + output->display( input = 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. +* Simple data selection + carr_tab = SELECT * + FROM "ZDEMO_ABAP_CARR" + 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_cds_ve.clas.abap b/src/zcl_demo_abap_cds_ve.clas.abap new file mode 100644 index 0000000..bb5d15f --- /dev/null +++ b/src/zcl_demo_abap_cds_ve.clas.abap @@ -0,0 +1,465 @@ +*********************************************************************** +* +* 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. +ENDCLASS. + + + +CLASS zcl_demo_abap_cds_ve IMPLEMENTATION. + + + METHOD class_constructor. + "Filling demo database tables. + zcl_demo_abap_flight_tables=>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. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: CDS view entities` ). + + output->display( `1) Operands, expressions and built-in functions ` && + `in a CDS view entity` ). + + "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). + + output->display( input = select_from_cds name = `select_from_cds` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = agg_expr name = `agg_expr` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = cds_joins name = `cds_joins` ). + +********************************************************************** + + output->next_section( `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. + + output->display( `---------- Inner join ----------` ). + + 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). + + output->display( input = sql_inner_join name = `sql_inner_join` ). + + output->display( `---------- Left outer join ----------` ). + + 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). + + output->display( input = sql_left_outer_join name = `sql_left_outer_join` ). + + output->display( `---------- Right outer join ----------` ). + + 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). + + output->display( input = sql_right_outer_join name = `sql_right_outer_join` ). + + output->display( `---------- Cross join ----------` ). + + 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). + + output->display( input = sql_cross_join name = `sql_cross_join` ). + + "Just a check what join example is currently commented in + IF cds_joins = sql_inner_join. + + output->display( `In the example CDS view entity, the inner join example is commented in.` ). + + ELSEIF cds_joins = sql_left_outer_join. + + output->display( `In the example CDS view entity, the left outer join example is commented in.` ). + + ELSEIF cds_joins = sql_right_outer_join. + + output->display( `In the example CDS view entity, the right outer join example is commented in.` ). + + ELSEIF cds_joins = sql_cross_join. + + output->display( `In the example CDS view entity, the cross join example is commented in.` ). + + ELSE. + + output->display( `In the example CDS view entity, there is some other code present.` ). + + ENDIF. + +********************************************************************** + + output->next_section( `Associations` ). + + output->display( `5) Selecting data from a CDS view that contains associations` ). + + "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). + + output->display( input = assoc name = `assoc` ). + +********************************************************************** + + output->next_section( `Using exposed associations in ABAP SQL statements: ...` ). + + "The following examples use path expressions to access the association + "targets of exposed associations. + + output->display( `6) ... SELECT 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 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). + + output->display( input = assoc_exp_select name = `assoc_exp_select` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = assoc_exp_from name = `assoc_exp_from` ). + + "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). + + output->display( input = sql_repr name = `sql_repr` ). + + IF sql_repr = assoc_exp_from. + output->display( `The result sets are the same.` ). + ELSE. + output->display( `The result sets are differrent.` ). + ENDIF. + +********************************************************************** + + output->next_section( `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). + + output->display( input = assoc_attr_card name = `assoc_attr_card` ). + + "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). + + output->display( input = assoc_attr_joty name = `assoc_attr_joty` ). + + "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). + + output->display( input = assoc_attr_where name = `assoc_attr_where` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = 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_constructor_expr.clas.abap b/src/zcl_demo_abap_constructor_expr.clas.abap new file mode 100644 index 0000000..e550065 --- /dev/null +++ b/src/zcl_demo_abap_constructor_expr.clas.abap @@ -0,0 +1,1456 @@ +*********************************************************************** +* +* 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. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: Constructor expressions` ). + + output->display( `VALUE` ). + output->display( `1) Structures: Populating a flat structure` ). + + "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' ). + + output->display( input = struc name = `struc` ). + +********************************************************************** + + output->next_section( `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' ). + + output->display( input = 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' ). + + output->display( input = struc name = `struc` ). + +********************************************************************** + + output->next_section( `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' ). + + output->display( input = struc_inl name = `struc_inl` ). + +********************************************************************** + + output->next_section( `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' ) ). + + output->display( input = itab name = `itab` ). + +********************************************************************** + + output->next_section( `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` ) ). + + output->display( input = itab2 name = `itab2` ). + output->display( input = str_table name = `str_table` ). + +********************************************************************** + + output->next_section( `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 ) ). + + output->display( input = itab2 name = `itab2` ). + +********************************************************************** + + output->next_section( `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' ) ). + + output->display( input = itab2 name = `itab2` ). + +********************************************************************** + + output->next_section( `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 #( ). + + output->display( input = some_num name = `some_num` ). + output->display( input = another_num name = `another_num` ). + output->display( input = some_str name = `some_str` ). + output->display( input = str_table name = `str_table` ). + output->display( input = struc name = `struc` ). + +********************************************************************** + + output->next_section( `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. + + output->display( `9) Modifying internal table from a structure created inline` ). + + MODIFY TABLE itab2 FROM VALUE #( num = 7 char1 = 'hhh' char2 = 'stu' ). + + output->display( input = itab2 name = `itab2` ). + +********************************************************************** + + output->next_section( `10) Inserting a table line that is created inline into an internal table` ). + + INSERT VALUE #( num = 8 char1 = 'iii' char2 = 'vwx' ) INTO TABLE itab2. + + output->display( input = itab2 name = `itab2` ). + +********************************************************************** + + output->next_section( `11) Deleting a table entry based on a line created inline` ). + + DELETE TABLE itab2 FROM VALUE #( num = 3 ). + + output->display( input = itab2 name = `itab2` ). + +********************************************************************** + + output->next_section( `12) 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). + + output->display( input = itab_carr name = `itab_carr` ). + +********************************************************************** + + output->next_section( `Excursion: Deep structures and tables` ). + output->display( `13) Deep structure` ). + + "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` ) ). + + output->display( input = deep_struc name = `deep_struc` ). + +********************************************************************** + + output->next_section( `14) 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 ) ) ) ). + + output->display( input = deep_itab name = `deep_itab` ). + +********************************************************************** + + output->next_section( `CORRESPONDING` ). + output->display( `Simple Examples with structures and internal tables` ). + + "Method to fill demo structures and internal tables + "with values to work with + fill_struc_and_tab( ). + + output->display( `15) Original structure and table content` ). + + "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. + + output->display( input = s1 name = `s1` ). + output->display( input = s2 name = `s2` ). + output->display( input = tab1 name = `tab1` ). + output->display( input = tab2 name = `it_st` ). + +********************************************************************** + + output->next_section( `16) 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 ). + + output->display( input = s2 name = `s2` ). + output->display( input = tab2 name = `tab2` ). + +********************************************************************** + + output->next_section( `17) 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 ). + + output->display( input = s2 name = `s2` ). + output->display( input = tab2 name = `tab2` ). + +********************************************************************** + + output->next_section( `18) 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 ). + + output->display( input = s2 name = `s2` ). + output->display( input = tab2 name = `tab2` ). + +********************************************************************** + + output->next_section( `CORRESPONDING: Demonstrating various` && + ` additions using deep structures` ). + + output->display( `19) Original content of deep structures` ). + + "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( ). + + output->display( input = struc1 name = `struc1` ). + output->display( input = struc2 name = `struc2` ). + +********************************************************************** + + output->next_section( `20) 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 ). + + output->display( input = struc2 name = `struc2` ). + +********************************************************************** + + output->next_section( `21) 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 ). + + output->display( input = struc2 name = `struc2` ). + +********************************************************************** + + output->next_section( `22) 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 ). + + output->display( input = struc2 name = `struc2` ). + +********************************************************************** + + output->next_section( `23) 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 ). + + output->display( input = struc2 name = `struc2` ). + +********************************************************************** + + output->next_section( `24) 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 ). + + output->display( input = struc2 name = `struc2` ). + +********************************************************************** + + output->next_section( `25) 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 ). + + output->display( input = struc2 name = `struc2` ). + +********************************************************************** + + output->next_section( `NEW` ). + output->display( `26) Creating Anonymous Data Objects` ). + + "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' ). + + output->display( input = val name = `val` ). + output->display( input = dref1 name = `dref1` ). + output->display( input = dref2 name = `dref2` ). + output->display( input = dref3 name = `dref3` ). + output->display( input = dref4 name = `dref4` ). + output->display( input = dref5 name = `dref5` ). + output->display( input = dref6 name = `dref6` ). + +********************************************************************** + + output->next_section( `27) 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` ). + + output->display( input = oref1 name = `oref1` ). + + "Creating an instance of a class, object reference variable + "is declared inline, explicit type specification + DATA(oref2) = NEW local_class( `Salut` ). + + output->display( input = oref2 name = `oref2` ). + + "Method chaining + DATA(result) = NEW local_class( `Ciao` )->double( int = NEW #( 5 ) ). + + output->display( input = result name = `result` ). + +********************************************************************** + + output->next_section( `28) 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 ). + + output->display( input = i name = `i` ). + output->display( input = dec_num name = `dec_num` ). + + "Declaring data objects + DATA(txt) = VALUE abap_bool( ). + + DATA(str) = ` `. + + "Comparing the data objects with and without conversion + output->display( `Without conversion:` ). + + IF txt = str. + output->display( `txt is equal to str.` ). + ELSE. + output->display( `txt is not equal to str.` ). + ENDIF. + + output->display( `With conversion:` ). + + IF txt = CONV abap_bool( str ). + output->display( `txt is equal to converted str.` ). + ELSE. + output->display( `txt is not equal to converted str.` ). + ENDIF. + +********************************************************************** + + output->next_section( `29) EXACT` ). + + "The examples show the effect of the EXACT operator that enforces either + "a lossless assignment or a lossless calculation. + "1) Demonstrates data loss when converting to a data object that expects + "only a single character. + "2) Demonstrates an impossible lossless calculation. A rounding is + "necessary in this case. + "3) The example compares the effect of the EXACT and CONV operator. + + "Example 1 + TRY. + DATA(ex1) = EXACT abap_bool( 'XY' ). + CATCH cx_sy_conversion_data_loss INTO DATA(e1). + DATA(ex2) = e1->value. + DATA(t1) = e1->get_text( ). + ENDTRY. + + "Example 2 + TRY. + DATA(ex3) = EXACT decfloat34( 1 / 3 ). + CATCH cx_sy_conversion_rounding INTO DATA(e2). + DATA(ex4) = e2->value. + DATA(t2) = e2->get_text( ). + ENDTRY. + + "Example 3 + "Comparing the effect of CONV and EXACT + TYPES numtext TYPE n LENGTH 20. + + TRY. + DATA(ex5) = EXACT numtext( '2 Apples + 5 Oranges' ). + CATCH cx_sy_conversion_error INTO DATA(e3). + DATA(t3) = e3->get_text( ). + ENDTRY. + + DATA(conv_comp) = CONV numtext( '2 Apples + 5 Oranges' ). + + IF ex1 IS INITIAL. + output->display( |ex2: { ex2 }; { t1 }| ). + ELSE. + output->display( ex1 ). + ENDIF. + + IF ex3 IS INITIAL. + output->display( |ex4: { ex4 }; { t2 }| ). + ELSE. + output->display( input = ex3 name = `ex3` ). + ENDIF. + + IF ex5 IS INITIAL. + output->display( input = t3 name = `t3` ). + ELSE. + output->display( input = ex5 name = `ex5` ). + ENDIF. + + output->display( input = conv_comp name = `conv_comp` ). + +********************************************************************** + + output->next_section( `30) 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 ). + + output->display( input = dref_a name = `dref_a` ). + output->display( input = dref_b name = `dref_b` ). + output->display( input = dref_c name = `dref_c` ). + output->display( input = dref_d name = `dref_d` ). + output->display( input = oref_b name = `oref_b` ). + +********************************************************************** + + output->next_section( `31) 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 ). + + output->display( input = components_s2 name = `components_s2` ). + output->display( input = methods name = `methods` ). + output->display( input = components_s1 name = `components_s1` ). + output->display( input = dref_i name = `dref_i` ). + +********************************************************************** + + output->next_section( `32) COND` ). + + "The example demonstrates the use of the COND operator. The syntax + "includes several WHEN and THEN expressions. + "The example returns a string based on the current time. + + DATA(syst_time) = cl_abap_context_info=>get_system_time( ). + + DATA(greets) = + 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 '170000' + THEN |It's { syst_time TIME = ISO }. | && + |Good afternoon, { sy-uname }.| + WHEN syst_time BETWEEN '170001' AND '210000' + THEN |It's { syst_time TIME = ISO }. | && + |Good evening, { sy-uname }.| + WHEN syst_time BETWEEN '210001' AND '050000' + THEN |It's { syst_time TIME = ISO }. | && + |Good night, { sy-uname }.| + ELSE |Hallo, { sy-uname }.| + ). + + output->display( input = greets name = `greets` ). + +********************************************************************** + + output->next_section( `33) 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 CONV decfloat34( num1 + num2 ) + WHEN '-' THEN CONV decfloat34( num1 - num2 ) + WHEN '*' THEN CONV decfloat34( num1 * num2 ) + WHEN '/' THEN CONV decfloat34( num1 / num2 ) + ELSE `That doesn't work.` ). + output->display( |{ num1 } { } { num2 } = { calc_result }| ). + CATCH cx_sy_arithmetic_error INTO DATA(error). + output->display( |Arithmetic error. { error->get_text( ) }| ). + ENDTRY. + ENDLOOP. + +********************************************************************** + + output->next_section( `34) 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 ). + + output->display( input = f1 name = `f1` ). + + "USING KEY primary_key explicitly specified; same as above + DATA(f2) = FILTER #( fi_tab1 USING KEY primary_key WHERE a >= 3 ). + + output->display( input = f2 name = `f2` ). + + "EXCEPT addition + DATA(f3) = FILTER #( fi_tab1 EXCEPT WHERE a >= 3 ). + + output->display( input = f3 name = `f3` ). + + DATA(f4) = FILTER #( fi_tab1 EXCEPT USING KEY primary_key WHERE a >= 3 ). + + output->display( input = f4 name = `f4` ). + + "Secondary table key specified after USING KEY + DATA(f5) = FILTER #( fi_tab2 USING KEY sec_key WHERE a >= 4 ). + + output->display( input = f5 name = `f5` ). + + DATA(f6) = FILTER #( fi_tab2 EXCEPT USING KEY sec_key WHERE a >= 3 ). + + output->display( input = f6 name = `f6` ). + + "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 ). + + output->display( input = f7 name = `f7` ). + + "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 ). + + output->display( input = f8 name = `f8` ). + + "EXCEPT addition + DATA(f9) = FILTER #( fi_tab1 EXCEPT IN filter_tab1 WHERE a = table_line ). + + output->display( input = f9 name = `f9` ). + + "USING KEY is specified for the filter table + DATA(f10) = FILTER #( fi_tab2 IN filter_tab2 USING KEY line WHERE a = table_line ). + + output->display( input = f10 name = `f10` ). + + "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 ). + + output->display( input = f11 name = `f11` ). + +********************************************************************** + + output->next_section( `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. + + output->display( `35) Original table content` ). + + "Method to fill demo internal tables with values to work with. + "Tables are displayed showing the values. + fill_struc_and_tab( ). + + output->display( input = tab1 name = `tab1` ). + output->display( input = tab2 name = `tab2` ). + output->display( input = tab3 name = `tab3` ). + +********************************************************************** + + output->next_section( `36) FOR ... IN ... (LOOP Semantics)` ). + + "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 wa IN tab3 ( wa ) ). + + "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 wa IN tab3 + WHERE ( comp4 > 7 ) + ( CORRESPONDING #( wa ) ) ). + + "3) Specify components individually and providing a mapping + + DATA(for3) = VALUE t_type2( FOR wa IN tab3 + WHERE ( comp4 > 7 ) + ( comp1 = wa-comp1 + comp2 = `hallo` + comp3 = wa-comp4 ) ). + + output->display( input = for1 name = `for1` ). + output->display( input = for2 name = `for2` ). + output->display( input = for3 name = `for3` ). + + "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 wa1 IN tab1 WHERE ( comp1 = 'A' ) + FOR wa2 IN tab2 WHERE ( comp4 > 6 ) + FOR wa3 IN tab3 WHERE ( comp3 < 4 ) + ( compX = wa1-comp1 + compY = wa2-comp1 + compZ = wa3-comp3 ) ). + + output->display( input = for4 name = `for4` ). + +********************************************************************** + + output->next_section( `37) FOR ... WHILE/UNTIL ... ` && + `(DO/WHILE Semantics)` ). + + "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 ) ). + + output->display( input = for5 name = `for5` ). + output->display( input = for6 name = `for6` ). + +********************************************************************** + + output->next_section( `38) REDUCE (1)` ). + + "The examples demonstrate the REDUCE operator using values contained in + "an internal table column. Here, the table is of type string. + "1) The values of the columns are sequentially concatenated into a + "string. + "INIT ...: A temporary variable is specified that sets an initial + "value for the result variable + "FOR ...: Represents a loop, the loop is carried out for all table + "entries. + "NEXT ...: Represents the assignment to the temporary variable after + "every iteration. + "Once the loop has finished, the target variable is assigned the + "resulting value. + "2) Also here, the table rows are reduced to a text string using a + "chaining after NEXT. The auxiliary variable sep declared after + "INIT is initial for the first read row and is filled with a blank here + "for the evaluation of further rows. + + "Creating and filling a string table + DATA tab TYPE STANDARD TABLE OF string WITH EMPTY KEY. + + tab = VALUE #( ( `h` ) ( `a` ) ( `l` ) ( `l` ) ( `o` ) ). + + "Example 1 + DATA(a_word) = + REDUCE string( INIT text = `` + FOR word IN tab + NEXT text = |{ text }{ word }| ). + + "Example 2 + tab = VALUE #( ( `Some` ) ( `cool` ) ( `stuff` ) + ( `using` ) ( `REDUCE` ) ). + + DATA(sentence) = + REDUCE string( INIT text = `` sep = `` + FOR word IN tab + NEXT text = |{ text }{ sep }{ word }| sep = ` ` ) && '.'. + + output->display( input = a_word name = `a_word` ). + output->display( input = sentence name = `sentence` ). + +********************************************************************** + + output->next_section( `39) REDUCE (2)` ). + + "The examples demonstrate summations using the REDUCE operator. + "1) Example using FOR ... UNTIL .... It calculates the total of the + "numbers from 1 to 10. The resulting number is stored in a variable that + "is declared inline. + "2) The example has the same output as the first example. Here, a table + "column is reduced. The table that is of type i is filled with numbers + "from 1 to 10 (using a constructor expression with FOR ... WHILE ...). + "The reduction is then carried out based on the numbers contained in + "this table. + + "Example 1 + DATA(sum1) = REDUCE i( INIT b = 0 + FOR n = 1 UNTIL n > 10 + NEXT b += n ). + + "Example 2 + DATA itab_i TYPE STANDARD TABLE OF i WITH EMPTY KEY. + + itab_i = VALUE #( FOR j = 1 WHILE j <= 10 ( j ) ). + + DATA(sum2) = REDUCE i( INIT x = 0 + FOR z IN itab_i + NEXT x = x + z ). + + output->display( input = sum1 name = `sum1` ). + output->display( input = sum2 name = `sum2` ). + +********************************************************************** + + output->next_section( `40) REDUCE (3)` ). + + "The examples demonstrate the concatenation of strings + "1) without the addition THEN + "2) with the addition THEN + "3) in the context of a non-arithmetic expression. + + "1) Concatenation without THEN + DATA(conc1) = REDUCE string( INIT text = `Count up:` + FOR n = 1 UNTIL n > 10 + NEXT text &&= | { n }| ). + + "2) Concatenation with THEN + DATA(conc2) = REDUCE string( INIT text = `Count down:` + FOR n = 10 THEN n - 1 WHILE n > 0 + NEXT text &&= | { n }| ). + + "3) Non-arithmetic expression + DATA(conc3) = REDUCE string( INIT text = `` + FOR t = `x` THEN t && `y` + UNTIL strlen( t ) > 10 + NEXT text &&= |{ t } | ). + + output->display( input = conc1 name = `conc1` ). + output->display( input = conc2 name = `conc2` ). + output->display( input = conc3 name = `conc3` ). + +********************************************************************** + + output->next_section( `41) LET Expressions (1)` ). + + "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(str_tab) = VALUE string_table( LET it = `be` IN + ( |To { it } is to do| ) + ( |To { it } or not to { it }| ) + ( |To do is to { it }| ) + ( |Do { it } do { it } do| ) ). + + output->display( input = str_tab name = `str_tab` ). + +********************************************************************** + + output->next_section( `42) LET Expressions (2)` ). + + "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(time) = + 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 |?| ). + + output->display( input = time name = `time` ). + +********************************************************************** + + output->next_section( `43) LET Expressions (3)` ). + + "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. + + output->display( input = stringtab name = `stringtab` ). + + ENDMETHOD. +ENDCLASS. 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_display.clas.abap b/src/zcl_demo_abap_display.clas.abap new file mode 100644 index 0000000..67ed4da --- /dev/null +++ b/src/zcl_demo_abap_display.clas.abap @@ -0,0 +1,160 @@ +*********************************************************************** +* +* Class for ABAP cheat sheet examples designed to support displaying +* output in the ADT console +* +* -------------------------- NOTE ------------------------------------- +* This helper class is only used to display complex types contained in +* the example classes of the ABAP cheat sheets in older ABAP releases. +* In newer ABAP releases, this helper class is, in principle, not needed. +* You can use the write method of the classrun interface directly and +* display all types. +* +* 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 examples

+"! The class supports displaying output of the ABAP cheat sheet examples in the ADT console. +CLASS zcl_demo_abap_display DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + METHODS: + constructor + IMPORTING + io_out TYPE REF TO if_oo_adt_classrun_out, + display + IMPORTING + input TYPE data + name TYPE string DEFAULT `` + RETURNING + VALUE(output) TYPE string, + next_section + IMPORTING + heading TYPE string. + + PROTECTED SECTION. + PRIVATE SECTION. + DATA: + mo_out TYPE REF TO if_oo_adt_classrun_out, + offset TYPE i. + + CONSTANTS nl TYPE string VALUE cl_abap_char_utilities=>newline. +ENDCLASS. + + + +CLASS zcl_demo_abap_display IMPLEMENTATION. + + + METHOD constructor. + mo_out = io_out. + ENDMETHOD. + + + METHOD display. + "Checking data type + DATA(type_descr) = cl_abap_typedescr=>describe_by_data( input ). + CASE type_descr->kind. + WHEN cl_abap_typedescr=>kind_struct. + DATA(struct_descr) = CAST cl_abap_structdescr( type_descr ). + "Checking for complex output + IF struct_descr->struct_kind = cl_abap_structdescr=>structkind_nested + OR line_exists( struct_descr->components[ type_kind = cl_abap_typedescr=>typekind_table ] ) + OR line_exists( struct_descr->components[ type_kind = cl_abap_typedescr=>typekind_dref ] ) + OR line_exists( struct_descr->components[ type_kind = cl_abap_typedescr=>typekind_oref ] ). + DATA(to_be_serialized) = abap_true. + ELSE. + DATA(display) = mo_out->get( data = input name = name ). + ENDIF. + WHEN cl_abap_typedescr=>kind_table. + DATA(table_descr) = CAST cl_abap_tabledescr( type_descr ). + TRY. + DATA(line_type_struct_descr) = CAST cl_abap_structdescr( table_descr->get_table_line_type( ) ). + "Checking for complex output + IF line_type_struct_descr->struct_kind = cl_abap_structdescr=>structkind_nested + OR line_exists( line_type_struct_descr->components[ type_kind = cl_abap_typedescr=>typekind_table ] ) + OR line_exists( line_type_struct_descr->components[ type_kind = cl_abap_typedescr=>typekind_dref ] ) + OR line_exists( line_type_struct_descr->components[ type_kind = cl_abap_typedescr=>typekind_oref ] ). + to_be_serialized = abap_true. + ELSE. + display = mo_out->get( data = input name = name ). + ENDIF. + CATCH cx_sy_move_cast_error. + to_be_serialized = abap_true. + ENDTRY. + WHEN cl_abap_typedescr=>kind_class. + to_be_serialized = abap_true. + WHEN cl_abap_typedescr=>kind_intf. + to_be_serialized = abap_true. + WHEN cl_abap_typedescr=>kind_elem. + display = mo_out->get( data = COND string( WHEN name IS INITIAL THEN input ELSE `"` && name && `":` && nl && input ) ). + WHEN cl_abap_typedescr=>kind_ref. + "Checking for data references + IF type_descr->type_kind = cl_abap_typedescr=>typekind_dref. + "Checking type of dereferenced data object + DATA(type_check_dref) = cl_abap_typedescr=>describe_by_data( input->* ). + "Processing (non-)elementary types + IF type_check_dref->kind = type_descr->kind_elem. + display = mo_out->get( data = COND string( WHEN name IS INITIAL THEN input->* ELSE `"` && name && `":` && nl && input->* ) ). + ELSE. + to_be_serialized = abap_true. + ENDIF. + ELSE. + to_be_serialized = abap_true. + ENDIF. + ENDCASE. + + "Processing complex output by serializiation + FIND SUBSTRING `Data type not yet supported ...` IN display MATCH OFFSET DATA(off) MATCH LENGTH DATA(len). + IF sy-subrc = 0 OR to_be_serialized = abap_true. + "ABAP JSON serializing + DATA(json) = /ui2/cl_json=>serialize( data = input + pretty_name = /ui2/cl_json=>pretty_mode-low_case + compress = abap_false + hex_as_base64 = abap_false + format_output = abap_true + assoc_arrays = abap_true + assoc_arrays_opt = abap_true ). + IF to_be_serialized = abap_true. + IF name IS INITIAL. + REPLACE PCRE `^` IN display WITH json && nl. + ELSE. + REPLACE PCRE `^` IN display WITH `"` && name && `":` && nl && json && nl. + ENDIF. + "substring found + ELSE. + IF name IS INITIAL. + REPLACE SECTION OFFSET off LENGTH len OF display WITH json && nl. + ELSE. + REPLACE SECTION OFFSET off LENGTH len OF display WITH `"` && name && `":` && nl && json && nl. + ENDIF. + ENDIF. + mo_out->write( display && nl ). + ELSE. + mo_out->write( display && nl ). + ENDIF. + ENDMETHOD. + + + METHOD next_section. + mo_out->write( `_________________________________________________________________________________` + && nl + && nl + && heading + && nl + && nl ). + ENDMETHOD. +ENDCLASS. diff --git a/src/zcl_demo_abap_display.clas.xml b/src/zcl_demo_abap_display.clas.xml new file mode 100644 index 0000000..e171bfc --- /dev/null +++ b/src/zcl_demo_abap_display.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_DISPLAY + E + Class supporting ABAP cheat sheet examples + 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..fcdf10a --- /dev/null +++ b/src/zcl_demo_abap_dtype_dobj.clas.abap @@ -0,0 +1,2026 @@ +*********************************************************************** +* +* 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. + +********************************************************************** + + "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 && |\n(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_flight_tables=>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. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: Data Types and Data Objects` ). + +********************************************************************** + + output->display( `Declaring data types` ). + + "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. + + output->display( `1) Declaring data types based on elementary types` ). + + "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. + + output->display( `No output for this section. Check out the code, ` + && `for example, when running the class in the debugger after setting ` + && `a breakpoint, or the F2 information in ADT when selecting a type.` ). + +********************************************************************** + + output->next_section( `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. + + output->display( `No output for this section. Check out the code, ` + && `for example, when running the class in the debugger after setting ` + && `a breakpoint, or the F2 information in ADT when selecting a type.` ). + +********************************************************************** + + output->next_section( `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. + + output->display( `No output for this section. Check out the code, ` + && `for example, when running the class in the debugger after setting ` + && `a breakpoint, or the F2 information in ADT when selecting a type.` ). + +********************************************************************** + + output->next_section( `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. + + output->display( `4) Declaring data objects based on elementary data types` ). + + "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. + + output->display( `No output for this section. Check out the code, ` + && `for example, when running the class in the debugger after setting ` + && `a breakpoint, or the F2 information in ADT when selecting a type.` ). + +********************************************************************** + + output->next_section( `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. + + output->display( `No output for this section. Check out the code, ` + && `for example, when running the class in the debugger after setting ` + && `a breakpoint, or the F2 information in ADT when selecting a type.` ). + +********************************************************************** + + output->next_section( `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. + + output->display( `No output for this section. Check out the code, ` + && `for example, when running the class in the debugger after setting ` + && `a breakpoint, or the F2 information in ADT when selecting a type.` ). + +********************************************************************** + + output->next_section( `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. + + output->display( `No output for this section. Check out the code, ` + && `for example, when running the class in the debugger after setting ` + && `a breakpoint, or the F2 information in ADT when selecting a type.` ). + +********************************************************************** + + output->next_section( `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). + + output->display( `No output for this section. Check out the code, ` + && `for example, when running the class in the debugger after setting ` + && `a breakpoint, or the F2 information in ADT when selecting a type.` ). + +********************************************************************** + + output->next_section( `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). + output->display( input = 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 ). + +********************************************************************** + + output->next_section( `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 with suitable type can be created in place. + SELECT * + FROM zdemo_abap_carr + INTO TABLE NEW @DATA(dref_14_inline). + + output->display( `No output for this section. Check out the code, ` + && `for example, when running the class in the debugger after setting ` + && `a breakpoint, or the F2 information in ADT when selecting a type.` ). + +********************************************************************** + + output->next_section( `Excursions: Elementary types and type conversions` ). + + output->display( `11) Implicit and explicit conversion` ). + + "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. + + output->display( input = do_2_c3 name = `do_2_c3` ). + + "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. + + output->display( input = do_4_i name = `do_4_i` ). + + "Conversions with the types i and string + do_4_i = -5. + do_1_str = do_4_i. + + output->display( input = do_1_str name = `do_1_str` ). + + "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 ). + + output->display( input = do_7_i name = `do_7_i` ). + + "# character when the type can be derived + DATA do_8_i TYPE i. + do_8_i = CONV #( do_6_dcfl34 ). + + output->display( input = do_8_i name = `do_8_i` ). + + "The following two calculations yield different results + do_8_i = sqrt( 5 ) + sqrt( 6 ). + + output->display( input = do_8_i name = `do_8_i` ). + + do_8_i = CONV i( sqrt( 5 ) ) + CONV i( sqrt( 6 ) ). + + output->display( input = 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. + +********************************************************************** + + output->next_section( `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 && '<--'. + + output->display( |{ result3 }\n{ result4 }| ). + +********************************************************************** + + output->next_section( `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. + + output->display( |Binary floating point: { result1 }\n| && + |Decimal floating point: { result2 }\n| ). + +********************************************************************** + + output->next_section( `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 ). + + output->display( input = xstr name = `xstr` ). + + DATA(xstring2string) = cl_abap_conv_codepage=>create_in( codepage = `UTF-8` + )->convert( source = xstr ). + + output->display( input = xstring2string name = `xstring2string` ). + + 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. + + output->display( `Y-->` && line_feed_str && `<--` ). + output->display( `Y-->` && |\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 = ` `. + + output->display( `-->` && blank && `<--` ). + +********************************************************************** + + output->next_section( `15) Date and time` ). + + "In the example, a date field is assigned the current value + "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. + output->display( + |Today: { today }\n| && + |Tommorow: { tomorrow }\n| ). + + DATA date TYPE d. + date = '20240101'. + output->display( input = date name = `date` ). + + date = 20240101. + output->display( input = date name = `date` ). + +********************************************************************** + + output->next_section( `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 = `Move error: T to D. Otherwise, a runtime error is caused.`. + 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 = `Move error: D to T. Otherwise, a runtime error is caused.`. + 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. + + output->display( input = tt_conv_tab name = `tt_conv_tab` ). + +********************************************************************** + + output->next_section( `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->*. + output->display( `The types of ref1->* and ref2->* are compatible.` ). + ELSE. + output->display( `The types of ref1->* and ref2->* are not compatible.` ). + ENDIF. + + IF CAST cl_abap_datadescr( cl_abap_typedescr=>describe_by_data( ref1->* ) + )->applies_to_data( ref3->* ). + ref1->* = ref3->*. + output->display( `The types of ref1->* and ref3->* are compatible.` ). + ELSE. + output->display( `The types of ref1->* and ref3->* are not compatible.` ). + ENDIF. + + "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. + + output->display( input = rtti_tab name = `rtti_tab` ). + +********************************************************************** + + output->next_section( `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). + + output->display( `No output for this section. Check out the code, ` + && `for example, when running the class in the debugger after setting ` + && `a breakpoint, or the F2 information in ADT when selecting a type.` ). + +********************************************************************** + + output->next_section( `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 . + + output->display( input = name = `` ). + + ASSIGN do_f_str TO . + + output->display( input = name = `` ). + + "Generic type data + ASSIGN do_e_c5 TO . + + output->display( input = name = `` ). + + ASSIGN do_f_str TO . + + output->display( input = name = `` ). + + ASSIGN itab_a TO . + + output->display( input = name = `` ). + +********************************************************************** + + "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'. + do_h_c5 = 'defghi'. "only 'def' assigned -> length and memory use do not change + + "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. + output->display( `I'm a literal...` ). + + "Anonymous data object created using the NEW operator + "Can be addressed using reference variables or field symbols. + + DATA(dref_c_str) = NEW string( `hi` ). + + output->display( input = dref_c_str->* name = `dref_c_str->*` ). + + "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. + + output->display( input = dref_d_tab->* name = `dref_d_tab->*` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = tab_num name = `tab_num` ). + + output->display( input = error->get_text( ) name = `error->get_text( )` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = syidx name = `syidx` ). + + 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. + + output->display( input = str_tab name = `str_tab` ). + + "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. + output->display( |Yes, the table line was found. sy-subrc value that was returned is { sy-subrc }.| ). + ELSE. + output->display( |No, the table line was not found. sy-subrc value that was returned is { sy-subrc }.| ). + ENDIF. + + "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. + + output->display( input = c_f name = `c_f` ). + + "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( ). + + output->display( input = res_str name = `res_str` ). + + +********************************************************************** + + output->next_section( `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. + + output->display( input = number name = `number` ). + + DO 10 TIMES. + number = number + 1. + ENDDO. + + output->display( input = number name = `number` ). + + "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. + + output->display( input = number_b name = `number_b` ). + + DO 10 TIMES. + number_b = number_b + 1. + ENDDO. + output->display( input = number_b name = `number_b` ). + +********************************************************************** + + output->next_section( `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 ). + output->display( input = output_for_enum_var1 name = `output_for_enum_var1` ). + + DATA enum_var2 TYPE t_enum VALUE b. + DATA(output_for_enum_var2) = enum_meth_params( enum_var2 ). + output->display( input = output_for_enum_var2 name = `output_for_enum_var2` ). + + DATA enum_var3 TYPE t_enum VALUE d. + DATA(output_for_enum_var3) = enum_meth_params( enum_var3 ). + output->display( input = output_for_enum_var3 name = `output_for_enum_var3` ). + + "The enum_processing method demonstrates various ways of processing enumerated + "objects. + DATA(output_for_enum_processing) = enum_processing( ). + output->display( input = output_for_enum_processing name = `output_for_enum_processing` ). + + "The rtti_enum method demonstrates the RTTI class cl_abap_enumdescr. + DATA(output_for_rtti_enum) = rtti_enum( ). + output->display( input = 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..967117a --- /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..4dcdaec --- /dev/null +++ b/src/zcl_demo_abap_dynamic_prog.clas.abap @@ -0,0 +1,2105 @@ +*********************************************************************** +* +* 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_flight_tables=>fill_dbtabs( ). + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: Dynamic Programming` ). + +********************************************************************** + + output->display( `Excursion: Field Symbols` ). + output->display( `1) Declaring Field Symbols` ). + + "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. + + output->display( `No output for this section. See the code.` ). + +********************************************************************** + + output->next_section( `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 complete types + 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 type is derived automatically. + ASSIGN num_a TO FIELD-SYMBOL(). + + output->display( `No output for this section. See the code.` ). + +********************************************************************** + + output->next_section( `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. + output->display( `Field symbol is assigned.` ). + ELSE. + output->display( `Field symbol not assigned.` ). + ENDIF. + + IF IS ASSIGNED. + output->display( `Field symbol is assigned.` ). + ELSE. + output->display( `Field symbol is not assigned.` ). + ENDIF. + +********************************************************************** + + output->next_section( `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. + output->display( `1. Field symbol is assigned.` ). + ELSE. + output->display( `1. Field symbol is not assigned.` ). + ENDIF. + + UNASSIGN . + + IF IS ASSIGNED. + output->display( `2. Field symbol is assigned.` ). + ELSE. + output->display( `2. Field symbol is not assigned.` ). + ENDIF. + +********************************************************************** + + output->next_section( `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. + + output->display( input = name = `` ). + + ASSIGN dobj_d_l10 TO CASTING TYPE type_d_l9. + + output->display( input = name = `` ). + + "Casting to a generic type + ASSIGN dobj_d_l10 TO CASTING TYPE c. + + output->display( input = name = `` ). + + "Casting to a static field type + ASSIGN dobj_d_l10 TO CASTING LIKE dobj_d_l5. + + output->display( input = name = `` ). + + "Casting to a dynamic field type + ASSIGN dobj_d_l10 TO CASTING LIKE . + + output->display( input = name = `` ). + + "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). + + output->display( input = name = `` ). + + "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. + + output->display( input = name = `` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = name = `` ). + output->display( input = num_e name = `num_e` ). + + "Use in expressions + DATA(calc_e) = + 211. + + output->display( input = calc_e name = `calc_e` ). + + IF < 1000. + output->display( `The value of is less than 1000` ). + ELSE. + output->display( `The value of is greater than 1000` ). + ENDIF. + + "Structure + output->display( input = name = `` ). + + DATA(comp_e1) = -carrid. + + output->display( input = comp_e1 name = `comp_e1` ). + + -url = 'www.lh.com'. + + output->display( input = -url name = `-url` ). + + "Internal table + SELECT * + FROM zdemo_abap_carr + ORDER BY carrid + INTO TABLE @ + UP TO 3 ROWS. + + output->display( input = name = `` ). + + TRY. + DATA(comp_e2) = [ 2 ]-carrname. + output->display( input = comp_e2 name = `comp_e2` ). + CATCH cx_sy_itab_line_not_found INTO DATA(error_e). + ENDTRY. + + SELECT * + FROM zdemo_abap_carr + ORDER BY carrid + INTO TABLE @ + UP TO 3 ROWS. + + output->display( input = name = `` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = tab_f1 name = `tab_f1` ). + + "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. + + output->display( input = name = `` ). + output->display( input = name = `` ). + +********************************************************************** + + output->next_section( `8) Field Symbols in the Context of Processing a Structure` ). + + "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. + + 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. + output->display( |sy-index: { sy-index }, component content:| ). + output->display( ). + ENDIF. + + ENDDO. + +********************************************************************** + + output->next_section( `Data references` ). + output->display( `9) Declaring Data References` ). + + "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 + + output->display( `No output for this section. See the code.` ). + +********************************************************************** + + output->next_section( `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 ). + + output->display( `No output for this section. See the code.` ). + +********************************************************************** + + output->next_section( `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 with + "suitable type 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 + + output->display( input = dref_c6->* name = `dref_c6->*` ). + output->display( input = dref_c7->* name = `dref_c7->*` ). + output->display( input = dref_c8->* name = `dref_c8->*` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = ref_d2->* name = `ref_d2->*` ). + output->display( input = ref_data_d1->* name = `ref_data_d1->*` ). + output->display( input = ref_d5->* name = `ref_d5->*` ). + +********************************************************************** + + output->next_section( `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->*. + + output->display( input = ref_e1->* name = `ref_e1->*` ). + + "Content of referenced data object is changed + ref_e1->* = 10. + + output->display( input = ref_e1->* name = `ref_e1->*` ). + + "Data reference used in a logical expression + IF ref_e1->* > 5. + output->display( `The value of ref_e1 is greater than 5.` ). + ELSE. + output->display( `The value of ref_e1 is lower than 5.` ). + ENDIF. + + "Dereferenced generic type + DATA(calc) = 1 + ref_data_e->*. + + output->display( input = calc name = `calc` ). + + "Complete structure + DATA(struc) = ref_e2->*. + + output->display( input = ref_e2->* name = `ref_e2->*` ). + + "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'. + + output->display( input = ref_e2->carrid name = `ref_e2->carrid` ). + + "The following syntax also works (dereferencing operator and the component selector). + ref_e2->*-carrname = 'United Airlines'. + + output->display( input = ref_e2->*-carrname name = `ref_e2->*-carrname` ). + +********************************************************************** + + output->next_section( `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. + + IF ref_f1 IS BOUND. + output->display( `ref_f1 is bound.` ). + ELSE. + output->display( `ref_f1 is not bound.` ). + ENDIF. + + IF ref_f2 IS BOUND. + output->display( `ref_f2 is bound.` ). + ELSE. + output->display( `ref_f2 is not bound.` ). + ENDIF. + +********************************************************************** + + output->next_section( `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. + output->display( `Before CLEAR: ref_g1 is initial.` ). + ELSE. + output->display( `Before CLEAR: ref_g1 is not intial.` ). + ENDIF. + + IF ref_g1 IS BOUND. + output->display( `Before CLEAR: ref_g1 is bound.` ). + ELSE. + output->display( `Before CLEAR: ref_g1 is not bound.` ). + ENDIF. + + CLEAR ref_g1. + + IF ref_g1 IS INITIAL. + output->display( `After CLEAR: ref_g1 is initial.` ). + ELSE. + output->display( `After CLEAR: ref_g1 is not initial.` ). + ENDIF. + + IF ref_g1 IS BOUND. + output->display( `After CLEAR: ref_g1 is bound.` ). + ELSE. + output->display( `After CLEAR: ref_g1 is not bound.` ). + ENDIF. + +********************************************************************** + + output->next_section( `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 ). + + output->display( input = ref_h1->* name = `ref_h1->*` ). + + ref_h1 = NEW string( `ref_h1 overwritten.` ). + + output->display( input = ref_h1->* name = `ref_h1->*` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = itab_i name = `itab_i` ). + output->display( input = `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 ]->* && `.` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = flsch_tab name = `flsch_tab` ). + +********************************************************************** + + output->next_section( `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 ) ). + + output->display( input = struc_l ). + + "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. + + output->display( input = itab_l name = `itab_l` ). + +********************************************************************** + + output->next_section( `Dynamic ABAP Statements` ). + output->display( `20) Dynamic Specifications in ASSIGN Statements (1) - Attributes of Classes/Interfaces` ). + + "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(). + + output->display( |Data object name determined at runtime: { dobj_name } | ). + output->display( |The content of the data object is: { } | ). + + dobj_name = lcl_det_at_runtime=>get_dyn_dobj( ). + + "Completely dynamic assign + ASSIGN ('lcl_det_at_runtime')=>(dobj_name) TO FIELD-SYMBOL(). + output->display( input = name = `` ). + + ASSIGN ('zdemo_abap_objects_interface')=>('const_intf') TO FIELD-SYMBOL(). + output->display( input = name = `` ). + + "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(). + + output->display( input = name = `` ). + + ASSIGN cl_ref->('another_string') TO FIELD-SYMBOL(). + + output->display( input = name = `` ). + +********************************************************************** + + output->next_section( `21) Dynamic Specifications in ASSIGN Statements (2) - Setting sy-subrc/ELSE UNASSIGN` ). + + "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. + output->display( |Successful assignment for attribute "{ attribute }". sy-subrc = { sy-subrc }. | ). + output->display( input = name = `` ). + ELSE. + output->display( |Assignment not successful for attribute "{ attribute }". sy-subrc = { sy-subrc }. | ). + ENDIF. + + IF IS ASSIGNED. + output->display( `The field symbol is assigned.` ). + output->display( `--------------------` ). + ELSE. + output->display( `The field symbol is not assigned.` ). + ENDIF. + + ENDLOOP. + +********************************************************************** + + output->next_section( `22) Dynamic Specifications in ASSIGN Statements (3) - Structure Components` ). + + "Dynamic specification of structure components that are assigned to a field symbol. + + 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(). + + output->display( input = name = `` ). + output->display( input = name = `` ). + output->display( input = subrc1 name = `subrc1` ). + output->display( input = subrc2 name = `subrc2` ). + output->display( input = name = `` ). + output->display( input = name = `` ). + output->display( input = name = `` ). + output->display( input = name = `` ). + output->display( input = name = `` ). + +********************************************************************** + + output->next_section( `23) Dynamic Specifications in ASSIGN Statements (4) - 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'). + + output->display( input = name = `` ). + +********************************************************************** + + output->next_section( `Dynamically Creating Data Objects at Runtime Using Dynamic Type Definitions` ). + output->display( `24) Miscellaneous Data Objects (1)` ). + + "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). + output->display( |***** Loop iteration { sy-tabix }. Type: { refwa->* } *****| ). + + CASE refwa->*. + WHEN `I`. + CREATE DATA dataref TYPE (refwa->*). + dataref->* = 123. + + output->display( input = dataref->* name = `dataref->*` ). + + CREATE DATA dataref TYPE TABLE OF (refwa->*). + + INSERT 1 INTO TABLE dataref->*. + INSERT 2 INTO TABLE dataref->*. + + output->display( input = dataref->* name = `dataref->*` ). + + CREATE DATA dataref TYPE REF TO (refwa->*). + dataref->* = REF i( 456 ). + + output->display( input = dataref->* name = `dataref->*` ). + WHEN `STRING`. + CREATE DATA dataref TYPE (refwa->*). + dataref->* = `hello`. + + output->display( input = dataref->* name = `dataref->*` ). + + CREATE DATA dataref TYPE TABLE OF (refwa->*). + INSERT `hello` INTO TABLE dataref->*. + INSERT `abap` INTO TABLE dataref->*. + + output->display( input = dataref->* name = `dataref->*` ). + + CREATE DATA dataref TYPE REF TO (refwa->*). + dataref->* = REF string( `hi` ). + + output->display( input = dataref->* name = `dataref->*` ). + WHEN `ZDEMO_ABAP_CARR`. + CREATE DATA dataref TYPE (refwa->*). + SELECT SINGLE * FROM zdemo_abap_carr INTO @dataref->*. + + output->display( input = dataref->* name = `dataref->*` ). + + CREATE DATA dataref TYPE TABLE OF (refwa->*). + SELECT * FROM zdemo_abap_carr INTO TABLE @dataref->* UP TO 3 ROWS. + + output->display( input = dataref->* name = `dataref->*` ). + + CREATE DATA dataref TYPE REF TO (refwa->*). + SELECT SINGLE * FROM zdemo_abap_carr INTO NEW @dataref->*. + + output->display( input = dataref->* name = `dataref->*` ). + ENDCASE. + ENDLOOP. + +********************************************************************** + + output->next_section( `25) 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. + output->display( `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->* ) ). + + output->display( |Built-in type determined at runtime: { b_type-builtin_type } | ). + output->display( `Created data object at runtime:` ). + output->display( input = descr_builtin_type name = `descr_builtin_type` ). + + CATCH cx_root. + output->display( `Something went wrong.` ). + ENDTRY. + +********************************************************************** + + output->next_section( `26) 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->*. + + output->display( |Structured data type/database table name determined at runtime: { type4struc } | ). + output->display( input = ref_dynstruc->* name = `ref_dynstruc->*` ). + +********************************************************************** + + output->next_section( `27) 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. + + output->display( |Table/type name determined at runtime: { type_name } | ). + output->display( |At most, { random_upto } lines should have been read from the database table.| ). + output->display( input = ref_n->* name = `ref_n->*` ). + +********************************************************************** + + output->next_section( `28) Excursion: 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'). + + output->display( `No output for this section. See the code.` ). + +********************************************************************** + + output->next_section( `Dynamically Specifying Components/Clauses in Statements for Processing Internal Tables with ...` ). + output->display( `29) SORT` ). + + "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). + + output->display( |Field name determined at runtime | && + |by which the sorting was done: { field_name } | ). + output->display( input = carr_itab name = `carr_itab` ). + +********************************************************************** + + output->next_section( `30) READ TABLE` ). + + "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. + output->display( input = wa_comps-comp name = `wa_comps-comp` ). + output->display( input = wa_comps-value name = `wa_comps-value` ). + output->display( input = read_line name = `read_line` ). + CLEAR read_line. + ELSE. + output->display( input = wa_comps-comp name = `wa_comps-comp` ). + output->display( input = wa_comps-value name = `wa_comps-value` ). + output->display( `Line not found.` ). + ENDIF. + + ENDLOOP. + +********************************************************************** + + output->next_section( `31) MODIFY` ). + + "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 ) ). + + output->display( `Internal table content before modifications:` ). + output->display( input = itab_mod_tab_dyn name = `itab_mod_tab_dyn` ). + + "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. + output->display( |Invalid WHERE condition "{ condition }".| ). + ENDTRY. + ENDLOOP. + + output->display( `Internal table content after modifications:` ). + output->display( input = itab_mod_tab_dyn name = `itab_mod_tab_dyn` ). + +********************************************************************** + + output->next_section( `32) DELETE` ). + + "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 ) ). + + output->display( `Internal table content before modifications:` ). + output->display( input = itab_del_tab_dyn name = `itab_del_tab_dyn` ). + + 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). + + output->display( |Condition: { condition }| ). + output->display( input = itab_del_tab_dyn name = `itab_del_tab_dyn` ). + + CATCH cx_sy_itab_dyn_loop. + output->display( |Invalid WHERE condition "{ condition }".| ). + ENDTRY. + ENDDO. + +********************************************************************** + + output->next_section( `33) LOOP` ). + + "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. + + output->display( |Loop over internal table using key "{ k }".| ). + output->display( input = itab_dyn_key name = `itab_dyn_key` ). + CLEAR itab_dyn_key. + + ENDLOOP. + +********************************************************************** + + output->next_section( `Dynamically Specifying Clauses in ABAP SQL SELECT Statements` ). + output->display( `34) SELECT List` ). + + "In the example, the SELECT list that is used in a SELECT statement is + "determined at runtime. + + 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. + + output->display( |SELECT list determined at runtime: { select_list } | ). + output->display( input = sel_table name = `sel_table` ). + +********************************************************************** + + output->next_section( `35) 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). + + output->display( |Table name determined at runtime: { tab_name } | ). + output->display( |The table { tab_name } has { count } entries.| ). + +********************************************************************** + + output->next_section( `36) 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. + + output->display( |WHERE clause determined at runtime:| ). + output->display( input = where_clause name = `where_clause` ). + output->display( input = where_tab name = `where_tab` ). + +********************************************************************** + + output->next_section( `37) Excursion: 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. + + output->display( `Dynamically determined syntax elements:` ). + output->display( input = dyn_syntax_elem name = `dyn_syntax_elem` ). + + 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. + + output->display( input = dyn_syntax_elem-target->* name = `dyn_syntax_elem-target->*` ). + + ELSE. + output->display( `There's an issue with syntax elements.` ). + ENDIF. + +********************************************************************** + + output->next_section( `38) Dynamic Invoke` ). + + "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. + + output->display( |Class name determined at runtime: { cl_name } | ). + output->display( |Method name determined at runtime: { meth_name } | ). + + output->display( `Result of method call (text stored in a variable):` ). + output->display( input = lcl_det_at_runtime=>dyn_meth_call_result name = `lcl_det_at_runtime=>dyn_meth_call_result` ). + + "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). + + output->display( input = lcl_det_at_runtime=>dyn_meth_call_result name = `lcl_det_at_runtime=>dyn_meth_call_result` ). + + DATA class TYPE string VALUE `LCL_DET_AT_RUNTIME`. + + CALL METHOD (class)=>fill_string. + + output->display( input = lcl_det_at_runtime=>dyn_meth_call_result name = `lcl_det_at_runtime=>dyn_meth_call_result` ). + +********************************************************************** + + output->next_section( `39) 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. + output->display( input = 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. + + output->display( input = kind_elem name = `kind_elem` ). + output->display( input = type_kind_elem name = `type_kind_elem` ). + output->display( input = output_length_elem name = `output_length_elem` ). + + "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' ). + + output->display( input = type_descr_obj_elem_inl_2->kind name = `type_descr_obj_elem_inl_2->kind` ). + output->display( input = type_descr_obj_elem_inl_2->type_kind name = `type_descr_obj_elem_inl_2->type_kind` ). + + "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. + + output->display( input = struc_kind name = `struc_kind` ). + output->display( input = comps_struc name = `comps_struc` ). + output->display( input = comps_struc2 name = `comps_struc2` ). + output->display( input = struct_kind name = `struct_kind` ). + + "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( ). + + output->display( input = tab_kind name = `tab_kind` ). + output->display( input = tab_keys name = `tab_keys` ). + output->display( input = tab_keys2 name = `tab_keys2` ). + output->display( input = tab_comps name = `tab_comps` ). + + "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. + + output->display( input = ref_kind name = `ref_kind` ). + output->display( input = ref_type name = `ref_type` ). + output->display( input = ref_type_abs_name name = `ref_type_abs_name` ). + output->display( input = 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 ) ). + +********************************************************************** + + output->next_section( `40) 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( ). + + output->display( |Type name determined at runtime: { get_type }| ). + + 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. + output->display( `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 ). + output->display( input = el name = `el` ). + "Various attributes and methods possible + output->display( input = el->type_kind name = `el->type_kind` ). + output->display( input = el->absolute_name name = `el->absolute_name` ). + output->display( input = el->get_relative_name( ) name = `el->get_relative_name( )` ). + + "Structure + ELSEIF some_type->kind = cl_abap_typedescr=>kind_struct. + DATA(stru) = CAST cl_abap_structdescr( some_type ). + output->display( input = stru->absolute_name name = `stru->absolute_name` ). + output->display( input = stru->components name = `stru->components` ). + output->display( input = stru->struct_kind name = `stru->struct_kind` ). + output->display( input = stru->get_components( ) name = `stru->get_components( )` ). + + "Internal table + ELSEIF some_type->kind = cl_abap_typedescr=>kind_table. + DATA(tab) = CAST cl_abap_tabledescr( some_type ). + output->display( input = tab->absolute_name name = `tab->absolute_name` ). + output->display( input = tab->table_kind name = `tab->table_kind` ). + output->display( input = tab->get_keys( ) name = `tab->get_keys` ). + output->display( input = tab->get_table_line_type( ) name = `tab->get_table_line_type( )` ). + + "Reference + ELSEIF some_type->kind = cl_abap_typedescr=>kind_ref. + DATA(ref_descr) = CAST cl_abap_refdescr( some_type ). + output->display( input = ref_descr->absolute_name name = `ref_descr->absolute_name` ). + output->display( input = ref_descr->get_referenced_type( ) name = `ref_descr->get_referenced_type( )` ). + ELSE. + output->display( `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 ). + output->display( input = class_desc->absolute_name name = `class_desc->absolute_name` ). + output->display( input = class_desc->attributes name = `class_desc->attributes` ). + output->display( input = class_desc->methods name = `class_desc->methods` ). + + "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 ). + output->display( input = descr_ref->absolute_name name = `descr_ref->absolute_name` ). + output->display( input = descr_ref->kind name = `descr_ref->kind` ). + CATCH cx_root. + output->display( `Error` ). + ENDTRY. + + "Interface + ELSEIF some_type->kind = cl_abap_typedescr=>kind_intf. + DATA(if_descr) = CAST cl_abap_intfdescr( some_type ). + output->display( input = if_descr->absolute_name name = `if_descr->absolute_name` ). + output->display( input = if_descr->methods name = `class_desc->methods` ). + ELSE. + output->display( `Others ...` ). + ENDIF. + ENDIF. + +********************************************************************** + + output->next_section( `41) 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' ). + + output->display( `No output for this section. See the code.` ). + +********************************************************************** + + output->next_section( `42) 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. + + output->display( input = dref_typ_obj->* name = `dref_typ_obj->*` ). + + "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'. + + output->display( input = dref_typ_obj->* name = `dref_typ_obj->*` ). + + "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. + + output->display( input = dref_typ_obj->* name = `dref_typ_obj->*` ). + + "Reference + CREATE DATA dref_typ_obj TYPE HANDLE tdo_ref_3. + dref_typ_obj->* = NEW t( '120000' ). + + output->display( input = dref_typ_obj->* name = `dref_typ_obj->*` ). + +********************************************************************** + + output->next_section( `43) 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'. + + output->display( input = dref_struc->* name = `dref_struc->*` ). + +********************************************************************** + + output->next_section( `44) 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. + + output->display( |Type/Database table name determined at runtime: { table_name }| ). + output->display( |Internal table entries (ordered by { dyn_order_by }):| ). + output->display( input = ref_tab->* name = `ref_tab->*` ). + + ENDMETHOD. +ENDCLASS. 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_flight_tables.clas.abap b/src/zcl_demo_abap_flight_tables.clas.abap new file mode 100644 index 0000000..b656210 --- /dev/null +++ b/src/zcl_demo_abap_flight_tables.clas.abap @@ -0,0 +1,711 @@ +*********************************************************************** +* +* 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_flight_tables DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + CLASS-METHODS: clear_dbtabs, + fill_dbtabs. + +protected section. +private section. +ENDCLASS. + + + +CLASS ZCL_DEMO_ABAP_FLIGHT_TABLES 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. +ENDCLASS. diff --git a/src/zcl_demo_abap_flight_tables.clas.xml b/src/zcl_demo_abap_flight_tables.clas.xml new file mode 100644 index 0000000..3164a2a --- /dev/null +++ b/src/zcl_demo_abap_flight_tables.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_FLIGHT_TABLES + E + Class supporting ABAP cheat sheet examples + 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..babc887 --- /dev/null +++ b/src/zcl_demo_abap_internal_tables.clas.abap @@ -0,0 +1,1790 @@ +*********************************************************************** +* +* ABAP cheat sheet: Internal tables +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntactical 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. + +protected section. + 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. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: Internal Tables` ). + output->display( `Filling and Copying Internal Table Content` ). + output->display( `1) Adding single lines using APPEND/INSERT` ). + + "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. + + output->display( input = it_st name = `it_st` ). + output->display( input = it_so name = `it_so` ). + +********************************************************************** + + output->next_section( `2) Adding initial line` ). + + APPEND INITIAL LINE TO it_st. + + INSERT INITIAL LINE INTO TABLE it_so. + + output->display( input = it_st name = `it_st` ). + output->display( input = it_so name = `it_so` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = it_st name = `it_st` ). + output->display( input = it_so name = `it_so` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = it_st name = `it_st` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = it_st name = `it_st` ). + +********************************************************************** + + output->next_section( `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' ) ). + + output->display( input = it_st name = `it_st` ). + +********************************************************************** + + output->next_section( `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' ) ). + + output->display( input = it_st2 name = `it_st2` ). + +********************************************************************** + + output->next_section( `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' ) + ). + + output->display( input = it_st name = `it_st` ). + +********************************************************************** + + output->next_section( `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' ) + ). + + output->display( input = it_st name = `it_st` ). + +********************************************************************** + + output->next_section( `10) Copying table content (without constructor ` && + `expression)` ). + + "Assignment of a table to another one having a matching line type + it_st = it_st2. + + output->display( input = it_st name = `it_st` ). + +********************************************************************** + + output->next_section( `11) CORRESPONDING Operator and MOVE-CORRESPONDING` ). + output->display( `Internal table content before assignments` ). + + "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( ). + + output->display( input = tab1 name = `tab1` ). + output->display( input = tab2 name = `tab2` ). + output->display( input = tab3 name = `tab3` ). + output->display( input = tab4 name = `tab4` ). + +********************************************************************** + + output->next_section( `Copying content from another table that has ` && + `a different line type ...` ). + output->display( `11a) ... and deleting existing table content ` && + `using the CORRESPONDING operator` ). + + tab1 = CORRESPONDING #( tab2 ). + + output->display( input = tab1 name = `tab1` ). + + fill_itabs_for_corresponding( ). + +********************************************************************** + + output->next_section( `11b) ... and deleting existing table content ` && + `using MOVE-CORRESPONDING` ). + + MOVE-CORRESPONDING tab2 TO tab1. + + output->display( input = tab1 name = `tab1` ). + + fill_itabs_for_corresponding( ). + +********************************************************************** + + output->next_section( `11c) ... and keeping existing table ` && + `content using the CORRESPONDING operator` ). + + tab1 = CORRESPONDING #( BASE ( tab1 ) tab2 ). + + output->display( input = tab1 name = `tab1` ). + + fill_itabs_for_corresponding( ). + +********************************************************************** + + output->next_section( `11d) ... and keeping existing table ` && + `content using MOVE-CORRESPONDING` ). + + MOVE-CORRESPONDING tab2 TO tab1 KEEPING TARGET LINES. + + output->display( input = tab1 name = `tab1` ). + + fill_itabs_for_corresponding( ). + +********************************************************************** + + output->next_section( `11e) ... 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 ). + + output->display( input = tab1 name = `tab1` ). + + fill_itabs_for_corresponding( ). + +********************************************************************** + + output->next_section( `11f) ... excluding components` ). + + "Excluding components from the assignment + tab1 = CORRESPONDING #( tab2 EXCEPT b ). + + output->display( input = tab1 name = `tab1` ). + +********************************************************************** + + output->next_section( `11g) ... excluding components and using MAPPING` ). + + "EXCEPT * means that all components remain initial not specified + "for mapping + tab1 = CORRESPONDING #( tab2 MAPPING d = f EXCEPT * ). + + output->display( input = tab1 name = `tab1` ). + + fill_itabs_for_corresponding( ). + +********************************************************************** + + output->next_section( `11h) ... 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 ). + + output->display( input = tab3 name = `tab3` ). + + fill_itabs_for_corresponding( ). + + tab3 = CORRESPONDING #( BASE ( tab3 ) tab4 DISCARDING DUPLICATES + MAPPING d = f EXCEPT b ). + + output->display( input = tab3 name = `tab3` ). + +********************************************************************** + + output->next_section( `11i) Copying data from a deep ` && + `internal table to another deep internal table` ). + output->display( `Original table content` ). + + output->display( input = itab_nested1 name = `itab_nested1` ). + output->display( input = itab_nested2 name = `itab_nested2` ). + +********************************************************************** + + output->next_section( `11j) ... deleting ` && + `existing content (CORRESPONDING operator)` ). + + itab_nested2 = CORRESPONDING #( DEEP itab_nested1 ). + + output->display( input = itab_nested2 name = `itab_nested2` ). + + fill_itabs_for_corresponding( ). + +********************************************************************** + + output->next_section( `11k) ... deleting ` && + `existing content (MOVE-CORRESPONDING)` ). + + MOVE-CORRESPONDING itab_nested1 TO itab_nested2 + EXPANDING NESTED TABLES. + + output->display( input = itab_nested2 name = `itab_nested2` ). + + fill_itabs_for_corresponding( ). + +********************************************************************** + + output->next_section( `11l) ... keeping ` && + `existing content (CORRESPONDING operator)` ). + + itab_nested2 = CORRESPONDING #( DEEP BASE ( itab_nested2 ) + itab_nested1 ). + + output->display( input = itab_nested2 name = `itab_nested2` ). + + fill_itabs_for_corresponding( ). + +********************************************************************** + + output->next_section( `11m) ... keeping ` && + `existing content (MOVE-CORRESPONDING)` ). + + MOVE-CORRESPONDING itab_nested1 TO itab_nested2 + EXPANDING NESTED TABLES KEEPING TARGET LINES. + + output->display( input = itab_nested2 name = `itab_nested2` ). + +********************************************************************** + + output->next_section( `Filling internal tables: Excursions` ). + output->display( `12) Selecting multiple rows from a database ` && + `table into an internal table` ). + + SELECT FROM zdemo_abap_tab1 + FIELDS key_field, char1, char2, num1, num2 + WHERE num1 > 3 + INTO TABLE @DATA(itab_select1). + + output->display( input = itab_select1 name = `itab_select1` ). + +********************************************************************** + + output->next_section( `13) 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. + + output->display( input = itab name = `itab` ). + +********************************************************************** + + output->next_section( `14) 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. + + output->display( input = itab name = `itab` ). + +********************************************************************** + + output->next_section( `15) 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. + + output->display( input = itab name = `itab` ). + +********************************************************************** + + output->next_section( `16) 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). + + output->display( input = itab_clone name = `itab_clone` ). + +********************************************************************** + + output->next_section( `17) 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). + + output->display( input = join_result name = `join_result` ). + +********************************************************************** + + output->next_section( `18) 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). + + output->display( input = subquery_result1 name = `subquery_result1` ). + +********************************************************************** + + output->next_section( `19) 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). + + output->display( input = subquery_result2 name = `subquery_result2` ). + +********************************************************************** + + output->next_section( `20) 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. + + output->display( input = select_result name = `select_result` ). + +********************************************************************** + + output->next_section( `21) 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. + + output->display( input = itab2 name = `itab2` ). + +********************************************************************** + + output->next_section( `22) 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 ). + + output->display( input = f1 name = `f1` ). + + "USING KEY primary_key explicitly specified; same as above + DATA(f2) = FILTER #( fi_tab1 USING KEY primary_key WHERE a >= 3 ). + + output->display( input = f2 name = `f2` ). + + "EXCEPT addition + DATA(f3) = FILTER #( fi_tab1 EXCEPT WHERE a >= 3 ). + + output->display( input = f3 name = `f3` ). + + DATA(f4) = FILTER #( fi_tab1 EXCEPT USING KEY primary_key WHERE a >= 3 ). + + output->display( input = f4 name = `f4` ). + + "Secondary table key specified after USING KEY + DATA(f5) = FILTER #( fi_tab2 USING KEY sec_key WHERE a >= 4 ). + + output->display( input = f5 name = `f5` ). + + DATA(f6) = FILTER #( fi_tab2 EXCEPT USING KEY sec_key WHERE a >= 3 ). + + output->display( input = f6 name = `f6` ). + + "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 ). + + output->display( input = f7 name = `f7` ). + + "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 ). + + output->display( input = f8 name = `f8` ). + + "EXCEPT addition + DATA(f9) = FILTER #( fi_tab1 EXCEPT IN filter_tab1 WHERE a = table_line ). + + output->display( input = f9 name = `f9` ). + + "USING KEY is specified for the filter table + DATA(f10) = FILTER #( fi_tab2 IN filter_tab2 USING KEY line WHERE a = table_line ). + + output->display( input = f10 name = `f10` ). + + "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 ). + + output->display( input = f11 name = `f11` ). + +********************************************************************** + + output->next_section( `25) 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. + + output->display( input = itab_num name = `itab_num` ). + +********************************************************************** + + output->next_section( `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. + + output->display( `Original table content` ). + + output->display( input = it_so_sec name = `it_so_sec` ). + output->display( input = it_ha_sec name = `it_ha_sec` ). + +********************************************************************** + + output->next_section( `26) 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. + + output->display( input = wa1 name = `wa1` ). + output->display( input = wa2 name = `wa2` ). + output->display( input = name = `` ). + output->display( input = name = `` ). + output->display( input = dref->* name = `dref->*` ). + output->display( input = dref2->* name = `dref2->*` ). + +********************************************************************** + + output->next_section( `Reading a single line via index ...` ). + output->display( `27) ... using READ TABLE` ). + + "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. + + output->display( input = wa3 name = `wa3` ). + output->display( input = wa4 name = `wa4` ). + output->display( input = wa5 name = `wa5` ). + output->display( input = wa6 name = `wa6` ). + output->display( input = wa7 name = `wa7` ). + output->display( input = wa8 name = `wa8` ). + +********************************************************************** + + output->next_section( `28) ... 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 ]. + + output->display( input = lv1 name = `lv1` ). + + IF lv2 IS NOT INITIAL. + output->display( input = lv2 name = `lv2` ). + ENDIF. + + IF error IS NOT INITIAL. + output->display( input = error name = `error` ). + ENDIF. + + output->display( input = lv3 name = `lv3` ). + output->display( input = lv4 name = `lv4` ). + output->display( input = lv5 name = `lv5` ). + +********************************************************************** + + output->next_section( `29) ... 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 ] ). + + output->display( input = lv6 name = `lv6` ). + output->display( input = dref3->* name = `dref3->*` ). + output->display( input = lv7 name = `lv7` ). + output->display( input = lv8 name = `lv8` ). + +********************************************************************** + + output->next_section( `Reading a single line via table keys ...` ). + output->display( `30) ... using READ TABLE (1)` ). + + "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'. + + output->display( input = wa9 name = `wa9` ). + output->display( input = wa10 name = `wa10` ). + output->display( input = wa11 name = `wa11` ). + output->display( input = wa12 name = `wa12` ). + output->display( input = wa13 name = `wa13` ). + +********************************************************************** + + output->next_section( `31) ... 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). + + output->display( input = wa14 name = `wa14` ). + output->display( input = wa15 name = `wa15` ). + output->display( input = wa16 name = `wa16` ). + +********************************************************************** + + output->next_section( `32) ... 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 + + output->display( input = lv9 name = `lv9` ). + output->display( input = lv10 name = `lv10` ). + output->display( input = lv11 name = `lv11` ). + output->display( input = lv12 name = `lv12` ). + output->display( input = lv13 name = `lv13` ). + +********************************************************************** + + output->next_section( `33) 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' ]. + + output->display( input = wa17 name = `wa17` ). + output->display( input = lv14 name = `lv14` ). + +********************************************************************** + + output->next_section( `34) 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. + + output->display( input = comp1 name = `comp1` ). + output->display( input = comp2 name = `comp2` ). + output->display( input = comp3 name = `comp3` ). + output->display( input = comp4 name = `comp4` ). + +********************************************************************** + + output->next_section( `35) 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 ] ). + output->display( |Line { key1 } exists in internal table.| ). + ELSE. + output->display( |Line { key1 } does not exist in internal table.| ). + ENDIF. + + "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. + output->display( |Line { key1 } exists in internal table.| ). + ELSE. + output->display( |Line { key1 } does not exist in internal table.| ). + ENDIF. + +********************************************************************** + + output->next_section( `36) 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. + output->display( |The index of the line with key = { key2 } | && + |is { idx_of_line1 } in the internal table.| ). + ELSE. + output->display( |The line with key = { key2 } does not exist | && + |in the internal table.| ). + ENDIF. + + IF idx_of_line2 <> 0. + output->display( |The index of the line with key = { key3 } | && + |is { idx_of_line2 } in the internal table.| ). + ELSE. + output->display( |The line with key = { key3 } does not exist | && + |in the internal table.| ). + ENDIF. + + IF tab_idx1 <> 0. + output->display( |The index of the line with key = { key2 } | && + |is { tab_idx1 } in the internal table.| ). + ELSE. + output->display( |The line with key = { key2 } does not exist | && + |in the internal table.| ). + ENDIF. + + IF tab_idx2 <> 0. + output->display( |The index of the line with key = { key3 } | && + |is { tab_idx2 } in the internal table.| ). + ELSE. + output->display( |The line with key = { key3 } does not exist | && + |in the internal table.| ). + ENDIF. + + output->next_section( `37) Checking how many lines are in an` && + ` internal table` ). + DATA(itab_lines) = lines( it_so_sec ). + + output->display( |The internal table consists of { itab_lines } | && + |lines.| ). + +********************************************************************** + + output->next_section( `Processing multiple internal table lines ` && + `sequentially` ). + output->display( `38a) Reading a complete table by sequentially ` && + `reading all lines` ). + + "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. + + output->display( input = it_so_sec name = `it_so_sec` ). + +********************************************************************** + + output->display( `38b) 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 ). + output->display( |There should be { lines_in_table } iterations per loop.| ). + + "Target: Existing work area + output->display( `---- Loop target: Existing work area ----` ). + DATA wa_lo LIKE LINE OF it_so_sec. + + LOOP AT it_so_sec INTO wa_lo. + IF sy-tabix = 1. + output->display( |This text is displayed when reaching line { sy-tabix }.| ). + ELSEIF sy-tabix = lines_in_table. + output->display( |This text is displayed when reaching line { sy-tabix }.| ). + ENDIF. + ENDLOOP. + + output->display( `---- Loop target: Work area created inline ----` ). + LOOP AT it_so_sec INTO DATA(wa_inl). + IF sy-tabix = 1. + output->display( |This text is displayed when reaching line { sy-tabix }.| ). + ELSEIF sy-tabix = lines_in_table. + output->display( |This text is displayed when reaching line { sy-tabix }.| ). + ENDIF. + ENDLOOP. + + output->display( `---- Loop target: Existing field symbol ----` ). + FIELD-SYMBOLS LIKE LINE OF it_so_sec. + + LOOP AT it_so_sec ASSIGNING . + IF sy-tabix = 1. + output->display( |This text is displayed when reaching line { sy-tabix }.| ). + ELSEIF sy-tabix = lines_in_table. + output->display( |This text is displayed when reaching line { sy-tabix }.| ). + ENDIF. + + ENDLOOP. + + output->display( `---- Loop target: Field symbol created inline ----` ). + LOOP AT it_so_sec ASSIGNING FIELD-SYMBOL(). + IF sy-tabix = 1. + output->display( |This text is displayed when reaching line { sy-tabix }.| ). + ELSEIF sy-tabix = lines_in_table. + output->display( |This text is displayed when reaching line { sy-tabix }.| ). + ENDIF. + ENDLOOP. + + output->display( `---- Loop target: Existing data reference variable ----` ). + DATA dref_lo TYPE REF TO struc1 . + + LOOP AT it_so_sec REFERENCE INTO dref_lo. + IF sy-tabix = 1. + output->display( |This text is displayed when reaching line { sy-tabix }.| ). + ELSEIF sy-tabix = lines_in_table. + output->display( |This text is displayed when reaching line { sy-tabix }.| ). + ENDIF. + ENDLOOP. + + output->display( `Loop target: Data reference variable created inline` ). + LOOP AT it_so_sec REFERENCE INTO DATA(dref_inl). + IF sy-tabix = 1. + output->display( |This text is displayed when reaching line { sy-tabix }.| ). + ELSEIF sy-tabix = lines_in_table. + output->display( |This text is displayed when reaching line { sy-tabix }.| ). + ENDIF. + ENDLOOP. + +********************************************************************** + + output->next_section( `39) 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. + + output->display( input = it_so_sec name = `it_so_sec` ). + +********************************************************************** + + output->next_section( `40) 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. + + output->display( input = it_so_sec name = `it_so_sec` ). + +********************************************************************** + + output->next_section( `41) 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. + + output->display( |There are { num } lines in the table | && + |fulfilling the condition.| ). + +********************************************************************** + + output->next_section( `42) 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. + + output->display( input = it_st_em name = `it_st_em` ). + +********************************************************************** + + output->next_section( `Creating and filling tables using table ` && + `iterations with FOR and VALUE` ). + output->display( `43) Retrieving values of one column in ` && + `an internal table.` ). + + "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 ) ). + + output->display( input = lv_num_a name = `lv_num_a` ). + +********************************************************************** + + output->next_section( `44) 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 ) ). + + output->display( input = lv_num_b name = `lv_num_b` ). + +********************************************************************** + + output->next_section( `45) 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 ) ). + + output->display( input = itab_for_2tab name = `itab_for_2tab` ). + +********************************************************************** + + output->next_section( `46) 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' ) ). + + output->display( input = it_changed name = `it_changed` ). + + + output->next_section( `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. + + output->display( `Original internal table content ` && + `(it1 and it2 have the same content)` ). + + output->display( input = it1 name = `it1` ). + output->display( input = it2 name = `it2` ). + +********************************************************************** + + output->next_section( `47) Sorting by primary table key` ). + + "Primary key: component a + SORT it1. + + output->display( input = it1 name = `it1` ). + +********************************************************************** + + output->next_section( `48) 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. + + output->display( input = it1 name = `it1` ). + +********************************************************************** + + output->next_section( `49) Sorting by primary table key respecting all ` && + `non-numeric fields` ). + + "Primary key: standard table key (all non-numeric fields) + SORT it2. + + output->display( input = 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. + "output->display( input = it3 name = `it3` ). + +********************************************************************** + + output->next_section( `50) Sorting by primary table key in ` && + `descending order` ). + + "Sorting in descending order and by primary table key + SORT it1 DESCENDING. + + output->display( input = it1 name = `it1` ). + +********************************************************************** + + output->next_section( `51) 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. + + output->display( input = it1 name = `it1` ). + +********************************************************************** + + output->next_section( `52) Sorting by explicitly specified component (2)` ). + + "Sorting by arbitrary, non-key field + SORT it1 BY d DESCENDING. + + output->display( input = it1 name = `it1` ). + +********************************************************************** + + output->next_section( `53) Sorting by multiple explicitly specified` && + ` components` ). + + "Sorting by multiple components and specifying the sort order + SORT it1 BY b ASCENDING c DESCENDING. + + output->display( input = it1 name = `it1` ). + +********************************************************************** + + output->next_section( `54) 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. + + output->display( input = it1 name = `it1` ). + +********************************************************************** + + output->next_section( `Modifying internal table content` ). + output->display( `Internal table content before modifications` ). + + "Standard table + output->display( input = it_st name = `it_st` ). + + "Sorted table + output->display( input = it_so_sec name = `it_so_sec` ). + + "Hashed table + output->display( input = it_ha_sec name = `it_ha_sec` ). + +********************************************************************** + + output->next_section( `55) 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' ). + + output->display( input = it_so_sec[ 1 ] name = `it_so_sec[ 1 ]` ). + output->display( input = it_st[ 1 ] name = `it_st[ 1 ]` ). + +********************************************************************** + + output->next_section( `56) 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. + + output->display( input = it_st name = `it_st` ). + output->display( input = it_so_sec name = `it_so_sec` ). + output->display( input = it_ha_sec name = `it_ha_sec` ). + +********************************************************************** + + output->next_section( `57) 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. + + output->display( input = it_st name = `it_st` ). + output->display( input = it_so_sec name = `it_so_sec` ). + output->display( input = it_ha_sec name = `it_ha_sec` ). + +********************************************************************** + + output->next_section( `Deleting adjacent duplicate entries` ). + output->display( `Original table content (restored before` && + ` each of the following examples)` ). + + 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. + + output->display( input = it_st2 name = `it_st2` ). + +********************************************************************** + + output->display( `58) 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. + + output->display( input = it_st2 name = `it_st2` ). + + it_st2 = it_st. + +********************************************************************** + + output->next_section( `59) Deleting adjacent duplicates by comparing ` && + `all field values` ). + + DELETE ADJACENT DUPLICATES FROM it_st2 COMPARING ALL FIELDS. + + output->display( input = it_st2 name = `it_st2` ). + + it_st2 = it_st. + +********************************************************************** + + output->next_section( `60) Deleting adjacent duplicates by comparing ` && + `specific field values` ). + + DELETE ADJACENT DUPLICATES FROM it_st2 COMPARING a c. + + output->display( input = it_st2 name = `it_st2` ). + + it_st2 = it_st. + +********************************************************************** + + output->next_section( `61) 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. + + output->display( input = it_st2 name = `it_st2` ). + +********************************************************************** + + output->next_section( `62) 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 #( ). + + output->display( input = it_st name = `it_st` ). + output->display( input = it_st2 name = `it_st2` ). + output->display( input = it_str name = `it_str` ). + +********************************************************************** + + output->next_section( `Excursions` ). + output->display( `63) Secondary table keys and hashed tables` ). + + "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. + + output->display( input = int_itab name = `int_itab` ). + + 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. + + output->display( input = int_itab name = `int_itab` ). + + "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 ]. + + output->display( input = line_of_ht name = `line_of_ht` ). + +********************************************************************** + + output->next_section( `64) 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( ). + + + output->display( input = k1 name = `k1` ). + + DATA(k2) = CAST cl_abap_tabledescr( + cl_abap_typedescr=>describe_by_data( + it_inline ) + )->get_keys( ). + + output->display( input = 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..4a5f636 --- /dev/null +++ b/src/zcl_demo_abap_objects.clas.abap @@ -0,0 +1,1055 @@ +*********************************************************************** +* +* ABAP cheat sheet: ABAP object orientation +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntactical 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 +* - 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. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: ABAP Object Orientation` ). + output->display( `Working with objects and components` ). + output->display( `1) Declaring reference variables` ). + + "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. + output->display( `The declared reference variables are initial.` ). + ELSE. + output->display( `One or more of the declared reference ` && + `variables are not initial.` ). + ENDIF. + +********************************************************************** + + output->next_section( `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. + output->display( `ref2a and ref2b point to instances ` && + `of the class local_class.` ). + ELSE. + output->display( `One or more of the reference variables ` && + `do not point to instances of the class local_class.` ). + ENDIF. + +********************************************************************** + + output->next_section( `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. + output->display( `ref3b has been assigned to ref3a.` ). + ELSE. + output->display( `ref3b has not been assigned to ref3a.` ). + ENDIF. + +********************************************************************** + + output->next_section( `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 #( ). + + output->display( input = ref4->no_of_instances name = `ref4->no_of_instances` ). + + ref4 = NEW #( ). + + output->display( input = ref4->no_of_instances name = `ref4->no_of_instances` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = itab5 name = `itab5` ). + +********************************************************************** + + output->next_section( `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. + output->display( `ref6 is initial.` ). + ELSE. + output->display( `ref6 is not initial.` ). + ENDIF. + +********************************************************************** + + output->next_section( `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. + + output->display( input = obj_instance_attr name = `obj_instance_attr` ). + output->display( input = obj_static_attr_obj name = `obj_static_attr_obj` ). + output->display( input = class_static_attr name = `class_static_attr` ). + +********************************************************************** + + output->next_section( `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( ). + + output->display( input = string name = `string` ). + + "Static methods + lcl_demo=>hallo_static_ext( ). + + output->display( input = lcl_demo=>string name = `lcl_demo=>string` ). + + "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( ). + + output->display( input = string name = `string` ). + +********************************************************************** + + output->next_section( `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 ). + + output->display( input = lcl_demo=>calc_result name = `lcl_demo=>calc_result` ). + + lcl_demo=>powers_of_two( i_pow = 5 ). + + output->display( input = lcl_demo=>calc_result name = `lcl_demo=>calc_result` ). + + lcl_demo=>powers_of_two( EXPORTING i_pow = 6 ). + + output->display( input = lcl_demo=>calc_result name = `lcl_demo=>calc_result` ). + + "Method with two importing parameters + lcl_demo=>addition( i_add1 = 1 i_add2 = 4 ). + + output->display( input = lcl_demo=>calc_result name = `lcl_demo=>calc_result` ). + + lcl_demo=>addition_optional( i_add_mand = 1 ). + + output->display( input = lcl_demo=>calc_result name = `lcl_demo=>calc_result` ). + +********************************************************************** + + output->next_section( `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) ). + + output->display( input = hallo name = `hallo` ). + output->display( input = subtraction_result name = `subtraction_result` ). + +********************************************************************** + + output->next_section( `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 ). + + output->display( input = num name = `num` ). + +********************************************************************** + + output->next_section( `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 ). + + output->display( input = mult_result name = `mult_result` ). + + "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) ). + + output->display( input = mult_res_exp name = `mult_res_exp` ). + + "Example with a logical expression + IF lcl_demo=>multiplication( i_mult1 = 5 i_mult2 = 3 ) < 20. + output->display( |The value is lower than 20.| ). + ELSE. + output->display( |The value is greater than 20.| ). + ENDIF. + + "Receiving parameter + lcl_demo=>multiplication( EXPORTING i_mult1 = 10 + i_mult2 = 11 + RECEIVING r_mult_result = DATA(res_received) ). + + output->display( input = res_received name = `res_received` ). + + "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( ). + + output->display( input = random_no1 name = `random_no1` ). + output->display( input = random_no2 name = `random_no2` ). + output->display( input = random_no3 name = `random_no3` ). + +********************************************************************** + + output->next_section( `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. + output->display( input = div_result1 name = `div_result1` ). + ELSE. + output->display( |Calculation error: { lcl_demo=>string }| ). + ENDIF. + + DATA(div_result2) = lcl_demo=>division( i_div1 = 1 i_div2 = 0 ). + IF lcl_demo=>string IS INITIAL. + output->display( input = div_result2 name = `div_result2` ). + ELSE. + output->display( |Calculation error: { lcl_demo=>string }| ). + ENDIF. + + "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. + + output->display( input = greets name = `greets` ). + +********************************************************************** + + output->next_section( `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). + output->display( input = error->get_text( ) name = `error->get_text( )` ). + ENDTRY. + + output->display( input = ref14a name = `ref14a` ). + + TRY. + DATA(ref14b) = NEW lcl_constructors( num1 = 18 num2 = 6 ). + CATCH cx_sy_zerodivide INTO error. + output->display( input = error->get_text( ) name = `error->get_text( )` ). + ENDTRY. + + output->display( input = ref14b name = `ref14b` ). + + TRY. + DATA(ref14c) = NEW lcl_constructors( num1 = 1 num2 = 0 ). + CATCH cx_sy_zerodivide INTO error. + output->display( |Error with ref14c: { error->get_text( ) }| ). + ENDTRY. + + output->display( input = ref14c name = `ref14c` ). + + "Static constructor + lcl_constructors=>add_1( ). + + output->display( input = lcl_constructors=>stat_text name = `lcl_constructors=>stat_text` ). + output->display( input = lcl_constructors=>stat_number name = `lcl_constructors=>stat_number` ). + +********************************************************************** + + output->next_section( `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 ). + + output->display( input = lcl_demo=>some_data->* name = `lcl_demo=>some_data->*` ). + + DATA strtab TYPE TABLE OF string. + + strtab = VALUE #( ( `I'm a ` ) ( `string table.` ) ). + + lcl_demo=>generic_data( EXPORTING i_data = strtab ). + + output->display( input = lcl_demo=>some_data->* name = `lcl_demo=>some_data->*` ). + + 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 ). + + output->display( input = lcl_demo=>some_data->* name = `lcl_demo=>some_data->*` ). + + lcl_demo=>generic_tab( EXPORTING i_anytab = c_tab ). + + output->display( input = lcl_demo=>some_data->* name = `lcl_demo=>some_data->*` ). + +********************************************************************** + + output->next_section( `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( ). + + output->display( input = first_string name = `first_string` ). + output->display( input = second_string name = `second_string` ). + output->display( input = third_string name = `third_string` ). + +********************************************************************** + + output->next_section( `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( ). + + output->display( input = str1 name = `str1` ). + output->display( input = str2 name = `str2` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = dc_check name = `dc_check` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = rtti_c name = `rtti_c` ). + + DATA(rtti_d) = CAST cl_abap_structdescr( + cl_abap_typedescr=>describe_by_data( struct4cast ) + )->components. + + output->display( input = rtti_d name = `rtti_d` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = inst_intf_attr_via_iref name = `inst_intf_attr_via_iref` ). + output->display( input = inst_intf_meth_via_iref1 name = `inst_intf_meth_via_iref1` ). + output->display( input = inst_intf_meth_via_iref2 name = `inst_intf_meth_via_iref2` ). + output->display( input = inst_intf_attr_via_cref name = `inst_intf_attr_via_cref` ). + output->display( input = inst_intf_meth_via_cref name = `inst_intf_meth_via_cref` ). + + output->display( input = stat_intf_attr1 name = `stat_intf_attr1` ). + output->display( input = stat_intf_meth1 name = `stat_intf_meth1` ). + output->display( input = stat_intf_meth2 name = `stat_intf_meth2` ). + output->display( input = stat_intf_attr2 name = `stat_intf_attr2` ). + output->display( input = stat_intf_meth3 name = `stat_intf_meth3` ). + output->display( input = stat_intf_meth4 name = `stat_intf_meth4` ). + + output->display( input = intf_const1 name = `intf_const1` ). + output->display( input = intf_const2 name = `intf_const2` ). + output->display( input = intf_const3 name = `intf_const3` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = timestamp name = `timestamp` ). + output->display( input = no_of_instances name = `no_of_instances` ). + + "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. + + output->display( input = timestamp name = `timestamp` ). + output->display( input = no_of_instances name = `no_of_instances` ). + + "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. + + output->display( input = timestamp name = `timestamp` ). + output->display( input = no_of_instances name = `no_of_instances` ). + +********************************************************************** + + output->next_section( `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. + + output->display( `First try: inst_1` ). + + TRY. + DATA(inst_1) = lcl_abstract=>factory_method( 1 ). + DATA(str_1) = inst_1->return_string( `inst_1` ). + output->display( input = str_1 name = `str_1` ). + CATCH cx_sy_ref_is_initial INTO DATA(error1). + output->display( |Error message: { error1->get_text( ) }| ). + ENDTRY. + + output->display( input = lcl_abstract=>message name = `lcl_abstract=>message` ). + + output->display( `Second try: inst_2` ). + + TRY. + DATA(inst_2) = lcl_abstract=>factory_method( 2 ). + DATA(str_2) = inst_2->return_string( `inst_2` ). + output->display( input = str_2 name = `str_2` ). + CATCH cx_sy_ref_is_initial INTO DATA(error2). + output->display( |Error message: { error2->get_text( ) }| ). + ENDTRY. + + output->display( input = lcl_abstract=>message name = `lcl_abstract=>message` ). + +********************************************************************** + + output->next_section( `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( ). + + output->display( input = string_table name = `string_table` ). + +********************************************************************** + + output->next_section( `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) ). + + output->display( input = string_without_me name = `string_without_me` ). + output->display( input = string_with_me name = `string_with_me` ). + +********************************************************************** + + output->next_section( `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( ). + + output->display( input = 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..81e42b7 --- /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 '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..1602692 --- /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..c8538b5 --- /dev/null +++ b/src/zcl_demo_abap_objects_friend.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_DEMO_ABAP_OBJECTS_FRIEND + E + Class supporting 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..29be1a7 --- /dev/null +++ b/src/zcl_demo_abap_prog_flow_logic.clas.abap @@ -0,0 +1,1403 @@ +*********************************************************************** +* +* 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. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: Program Flow Logic` ). + +********************************************************************** + + output->display( `1) Control Structure with IF` ). + + "Simple control structure realized by an IF ... ELSEIF ... ELSE ... ENDIF. + "statement. Multiple statement blocks can be included, of which only 1 is + "executed at most and depending on conditions. + + "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 = `+`. + + output->display( |The result of { num1 } { operator } { num2 } is { num1 + num2 }. | ). + + ELSEIF operator = `-`. + + output->display( |The result of { num1 } { operator } { num2 } is { num1 - num2 }. | ). + + ELSE. + + output->display( |The operator { operator } is not possible.| ). + + ENDIF. + +********************************************************************** + + output->next_section( `2) IF: Checking sy-subrc` ). + + "A prominent use case for IF statements: Checking sy-subrc. + "In the case below, a FIND statement is used. If there is a finding, + "sy-subrc has the value 0. + + DATA(to_be_found) = `AB`. + DATA(string_to_search) = `ABAP is great!`. + + FIND to_be_found IN string_to_search. + + IF sy-subrc = 0. + + output->display( |'{ to_be_found }' was found in the string '{ string_to_search }'.| ). + + ELSE. + + output->display( |'{ to_be_found }' was not found in the string '{ string_to_search }'.| ). + + ENDIF. + +********************************************************************** + + output->next_section( `3) Excursion: COND Operator` ). + + "The conditional operator COND can also be used to implement branches in operand positions + "that are based on logical expressions. Such conditional expressions have a result that + "is dependent on the logical expressions. + "The example provides an output based on the time stamp. + + DATA(syst_time) = cl_abap_context_info=>get_system_time( ). + + DATA(greetings) = COND #( WHEN syst_time BETWEEN '050001' AND '120000' + THEN |It's { syst_time TIME = ISO }. Good morning, { sy-uname }.| + WHEN syst_time BETWEEN '170001' AND '210000' + THEN |It's { syst_time TIME = ISO }. Good evening, { sy-uname }.| + WHEN syst_time BETWEEN '210001' AND '050000' + THEN |It's { syst_time TIME = ISO }. Good night, { sy-uname }.| + ELSE |It's { syst_time TIME = ISO }. Hallo, { sy-uname }.| ). + + output->display( input = greetings name = `greetings` ). + +********************************************************************** + + output->next_section( `4) Expressions and Functions for Conditions` ). + + "Control structures are generally controlled by logical expressions that + "define conditions for operands. The result of such an expression is either true or false. + "The example demonstrates a selection of possible expressions and operands of such expressions + "using a big IF statement. It includes multiple expressions combined by AND to demonstrate + "different options. Here, it is just meant to touch on many syntax options in one go + "for demonstration purposes. + + "Data declarations to be used in the IF statement below + DATA(num) = 2. "integer + DATA(empty_string) = ``. "empty string + DATA(flag) = 'x'. + DATA(dref) = NEW string( `ref` ). "data reference variable + + "Object reference variable + DATA oref TYPE REF TO object. + "Creating an object and assigning it to the reference variable + oref = NEW zcl_demo_abap_prog_flow_logic( ). + + "Declaration of and assignment to a field symbol + FIELD-SYMBOLS TYPE string. + ASSIGN `hallo` TO . + + "Creating an internal table of type string inline + DATA(str_table) = VALUE string_table( ( `a` ) ( `b` ) ( `c` ) ). + + "Comparisons + IF 2 = num "equal, alternative EQ + AND 1 <> num "not equal, alternative NE + AND 1 < num "less than, alternative LT + AND 3 > num "greater than, alternative GT + AND 2 >= num "greater equal, alternative GE + AND 2 <= num "less equal, alternative LE + + "Checks whether the content of an operand operand is within a closed interval + AND num BETWEEN 1 AND 3 + AND NOT num BETWEEN 5 AND 7 "NOT negates a logical expression + AND ( num >= 1 AND num <= 3 ) "Equivalent to 'num BETWEEN 1 AND 3'; + "here, demonstrating the use of parentheses + + "CO, CN ,CA, NA, CS, NS, CP, NP "Comparison operators for character-like data types; + "see the cheat sheet on string processing + + "Predicate Expressions + AND empty_string IS INITIAL "Checks whether the operand operand is initial. The expression + "is true, if the operand contains its type-dependent initial value + AND num IS NOT INITIAL "NOT negates + + AND dref IS BOUND "Checks whether a data reference variable contains a valid reference and + "can be dereferenced; + "IS NOT BOUND is possible which is valid for the following examples, too + AND oref IS BOUND "Checks whether an object reference variable contains a valid reference + + "IS INSTANCE OF checks whether for a + "a) non-initial object reference variable the dynamic type + "b) for an initial object reference variable the static type + "is more specific or equal to a comparison type. + AND oref IS INSTANCE OF zcl_demo_abap_prog_flow_logic + AND oref IS INSTANCE OF if_oo_adt_classrun + + AND IS ASSIGNED "Checks whether a memory area is assigned to a field symbol + + "The predicate expression IS SUPPLIED is dealt with further down. + + "Predicate Functions + AND contains( val = pcre = `\D` ) "Checks whether a certain value is contained; + "the example uses the pcre parameter for regular expressions; + "it checks whether there is any non-digit character contained + AND matches( val = pcre = `ha.+` ) "Compares a search range of the argument for the val parameter + "the example uses the pcre parameter for regular expressions; + "it checks whether the value matches the pattern + "'ha' and a sequence of any characters + + "Predicate functions for table-like arguments + "Checks whether a line of an internal table specified in the table expression + "exists and returns the corresponding truth value. + AND line_exists( str_table[ 2 ] ) + + "Predicative method call + "The result of the relational expression is true if the result of the functional method call + "is not initial and false if it is initial. The data type of the result of the functional method call, + "i. e. the return value of the called function method, is arbitrary. + "A check is made for the type-dependent initial value. + AND check_is_supplied( ) + "It is basically the short form of such a predicate expression: + AND check_is_supplied( ) IS NOT INITIAL + + "Boolean Functions + "Determine the truth value of a logical expression specified as an argument; + "the return value has a data type dependent on the function and expresses + "the truth value of the logical expression with a value of this type. + + "Function boolc: Returns a single-character character string of the type string. + "If the logical expression is true, X is returned. False: A blank is returned. + "Not to be compared with the constants abap_true and abap_false in relational expressions, + "since the latter converts from c to string and ignores any blanks. Note: If the logical + "expression is false, the result of boolc does not meet the condition IS INITIAL, since + "a blank and no empty string is returned. If this is desired, the function xsdbool + "can be used instead of boolc. + AND boolc( check_is_supplied( ) ) = abap_true + + "Result has the same ABAP type as abap_bool. + AND xsdbool( check_is_supplied( ) ) = abap_true + + "Examples for possible operands + + "Data objects as shown in the examples above + AND 2 = 2 + AND num = 2 + + "Built-in functions + AND to_upper( flag ) = 'X' + AND NOT to_lower( flag ) = 'X' + + "Numeric functions + AND ipow( base = num exp = 2 ) = 4 + + "Functional methods + "Method with exactly one return value + AND addition( num1 = 1 num2 = 1 ) = 2 + + "Calculation expressions + AND 4 - 3 + 1 = num + + "String expressions + AND `ha` && `llo` = + + "Constructor expression + AND CONV i( '2.03' ) = num + AND VALUE string_table( ( `a` ) ( `b` ) ( `c` ) ) = str_table + + "Table expression + AND str_table[ 2 ] = `b`. + + output->display( `All of the logical expressions are true.` ). + + ELSE. + output->display( `At least one of the logical expressions is false.` ). + ENDIF. + +********************************************************************** + + output->next_section( `5) Predicate Expression with IS SUPPLIED` ). + + "The predicate expression IS SUPPLIED is available in method implementations + "and checks whether a formal parameter of a procedure is filled or requested. + "In this example, a method includes two importing parameters that are both + "declared as non-mandatory. The method implementation includes a check with + "IS SUPPLIED and stores the result. The result is returned and displayed. + + DATA(is_supplied) = check_is_supplied( num1 = 123 ). + + output->display( input = is_supplied name = `is_supplied` ). + + is_supplied = check_is_supplied( num2 = 456 ). + + output->display( input = is_supplied name = `is_supplied` ). + +********************************************************************** + + output->next_section( `6) Control Structure with CASE` ). + + "CASE statements are used for case distinctions. If the content of an operand + "specified after WHEN matches the content specified after CASE, the statement + "block is executed. Constant values should be specified as operands. + "The example is basically the same as 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 '+'. + output->display( |The result of { n1 } { op } { n2 } is { n1 + n2 }. | ). + WHEN '-'. + output->display( |The result of { n1 } { op } { n2 } is { n1 - n2 }. | ). + WHEN OTHERS. + output->display( |The operator { op } is not possible.| ). + ENDCASE. + +********************************************************************** + + output->next_section( `7) CASE TYPE OF` ). + + "CASE TYPE OF: Checks the type of object reference variables + + "Creating an object reference variable and an object + DATA oref_check TYPE REF TO object. + oref_check = NEW zcl_demo_abap_prog_flow_logic( ). + + CASE TYPE OF oref_check. + WHEN TYPE zcl_demo_abap_prog_flow_logic. + output->display( `Type zcl_demo_abap_prog_flow_logic? True!` ). + WHEN TYPE if_oo_adt_classrun. + output->display( `Type if_oo_adt_classrun? True!` ). + WHEN TYPE zcl_demo_abap_sql. + output->display( `Type zcl_demo_abap_sql? True!` ). + WHEN OTHERS. + output->display( `Other type.` ). + ENDCASE. + + "The same logic as above is realized in the following IF statements + "using IS INSTANCE OF. + "In the example, the type check for if_oo_adt_classrun 'comes first'. + "This type is also true for the object reference variable. This class + "implements the interface. + IF oref_check IS INSTANCE OF if_oo_adt_classrun. + output->display( `Type if_oo_adt_classrun? True!` ). + ELSEIF oref_check IS INSTANCE OF zcl_demo_abap_prog_flow_logic. + output->display( `Type zcl_demo_abap_prog_flow_logic? True!` ). + ELSEIF oref_check IS INSTANCE OF zcl_demo_abap_sql. + output->display( `Type zcl_demo_abap_sql? True!` ). + ELSE. + output->display( `Other type.` ). + ENDIF. + +********************************************************************** + + output->next_section( `8) Excursion: SWITCH Operator` ). + + "The conditional operator SWITCH can also be used to make case + "distinctions in operand positions. Such conditional expressions have + "a result that is dependent on the logical expressions. + "The example provides an output based on the date. + + DATA(syst_date) = cl_abap_context_info=>get_system_date( ). + + DATA(switch_res) = SWITCH #( syst_date+4(2) "Extracting the month + WHEN '01' THEN `January` + WHEN '02' THEN `February` + WHEN '03' THEN `March` + WHEN '04' THEN `April` + WHEN '05' THEN `May` + WHEN '06' THEN `June` + WHEN '07' THEN `July` + WHEN '08' THEN `August` + WHEN '09' THEN `September` + WHEN '10' THEN `October` + WHEN '11' THEN `November` + WHEN '12' THEN `December` + ELSE `Oops ...` ). + + output->display( input = switch_res name = `switch_res` ). + +********************************************************************** + + output->next_section( `Loops (Iterations)` ). + + output->display( `9) Unconditional Loops with DO` ). + + "The example demonstrate the restriction of loop passes by specifying + "a number (of maximum loop passes) and the TIMES addition in a DO loop. + "The value of sy-index containing the number of loop passes is output, too. + + DATA do_counter TYPE i. + DATA do_sy_index TYPE string. + + DO 10 TIMES. + do_counter += 1. + + do_sy_index = do_sy_index && sy-index && ` `. + ENDDO. + + output->display( input = do_counter name = `do_counter` ). + output->display( input = do_sy_index name = `do_sy_index` ). + +********************************************************************** + + output->next_section( `10) Terminating Loops Completely Using EXIT` ). + + "Using the EXIT statement, you can terminate a loop completely. + "The program flow resumes after the closing statement of the loop. + + CLEAR: do_counter, do_sy_index. + + DO. + do_counter += 1. + + do_sy_index = do_sy_index && sy-index && ` `. + IF sy-index = 5. + EXIT. + ENDIF. + ENDDO. + + output->display( input = do_counter name = `do_counter` ). + output->display( input = do_sy_index name = `do_sy_index` ). + +********************************************************************** + + output->next_section( `11) Terminating Loop Passes` ). + + "CONTINUE: The current loop pass is terminated immediately and the + " program flow is continued with the next loop pass. + "CHECK: Conditional termination. If the specified logical expression + " is false, the current loop pass is terminated immediately and + " the program flow is continued with the next loop pass. + + CLEAR: do_counter, do_sy_index. + + DO. + + IF sy-index = 2. "skipped + CONTINUE. + ENDIF. + + CHECK sy-index <> 5. "skipped + + IF sy-index = 8. "terminates the loop completely + EXIT. + ENDIF. + + do_counter += 1. + + do_sy_index = do_sy_index && sy-index && ` `. + + ENDDO. + + output->display( input = do_counter name = `do_counter` ). + output->display( input = do_sy_index name = `do_sy_index` ). + +********************************************************************** + + output->next_section( `12) Excursion: Terminating Procedures Using RETURN` ). + + "RETURN statements immediately terminate the current processing block. + "However, according to the guidelines, RETURN should only be used to exit + "procedures like methods. + "The method implementation includes a check if the passed number is positive. + "If so, the square root is calculated. Otherwise, the method call is terminated. + "That means, the returned value for the second method call is initial. + + DATA(return1) = meth_with_return( 81 ). + + output->display( input = return1 name = `return1` ). + + DATA(return2) = meth_with_return( -9 ). + + output->display( input = return2 name = `return2` ). + +********************************************************************** + + output->next_section( `Conditional Loops with WHILE` ). + + output->display( `13) WHILE Example 1` ). + + "The following example highlights the setting of sy-index within loop passes. + "The value is added to an internal table. The loop iteration stops when + "the internal table has 10 lines. + + DATA int_itab TYPE TABLE OF i. + + WHILE lines( int_itab ) < 10. + + int_itab = VALUE #( BASE int_itab ( sy-index ) ). + + ENDWHILE. + + output->display( input = int_itab name = `int_itab` ). + +********************************************************************** + + output->display( `14) WHILE Example 2` ). + + "In the following example, all occurrences of a certain substring + "should be replaced by another string. Instead of, for example, using + "a REPLACE ALL OCCURRENCES statement, a WHILE loop is used which + "contains a FIND statement that stores offset and length of the found + "substring. Then, a REPLACE statement is used and that only replaces + "the substring according to the values. The condition for WHILE is + "tailored in a way that the loop is terminated if there are no more + "findings. The number of findings 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. + + output->display( input = count_occ_via_sy_index name = `count_occ_via_sy_index` ). + output->display( input = count_occ name = `count_occ` ). + output->display( input = while_string name = `while_string` ). + +********************************************************************** + + output->display( `15) WHILE Example 3` ). + + "The example demonstrates the 3 options for loop terminations + "in the context of a WHILE ... ENDWHILE statement. + + DATA while_cnt TYPE i. + DATA while_sy_index TYPE string. + + DATA(index) = 0. + + WHILE index <> 10. + + index = sy-index. + + IF sy-index = 2. "Skips loop pass + CONTINUE. + ENDIF. + + CHECK sy-index <> 5. "Skips loop pass + + IF sy-index = 8. "Terminates loop + EXIT. + ENDIF. + + while_cnt += 1. + + while_sy_index = while_sy_index && sy-index && ` `. + + ENDWHILE. + + output->display( input = while_cnt name = `while_cnt` ). + output->display( input = while_sy_index name = `while_sy_index` ). + + +********************************************************************** + + output->next_section( `Loops across Tables` ). + + output->display( `16) Loop across Internal Table Using LOOP ... ENDLOOP` ). + + "LOOP ... ENDLOOP statements are meant for loops across internal tables. + "For more examples, see the cheat sheet example on internal tables. + "Note: In contrast to the loops above, the system field sy-index is not set. + "Instead, the system field sy-tabix is set and which contains the table index + "of the current table line in the loop pass. + "The example demonstrates a loop across an internal table. It combines loops + "and control structures mentioned before: + "- An internal table includes two numbers and an operator based on which a + " a calculation is carried out. + "- The calculation is done in a method. It is implemented in a way that + " IF and CASE statements as well as COND and SWITCH operators are used + " for the calculation. + "- The calculation result is stored in an internal table which is output. + "- All four results - which are all the same, of course, just demonstrating + " the variety of options to achieve the same regarding control structures - + " are available in the table. Plus, the value in sy-tabix is also added to + " the internal table representing the table index. + + TYPES: BEGIN OF calc_struc, + num1 TYPE i, + operator TYPE c LENGTH 1, + num2 TYPE i, + END OF calc_struc. + + DATA calc_itab TYPE TABLE OF calc_struc WITH EMPTY KEY. + + "Internal table containing numbers and operators on whose basis + "calculations are to be carried out. + calc_itab = VALUE #( ( num1 = 123 operator = '+' num2 = 456 ) + ( num1 = -10 operator = '+' num2 = 9 ) + ( num1 = 12 operator = '-' num2 = 89 ) + ( num1 = -5 operator = '-' num2 = -12 ) + ( num1 = 11 operator = '*' num2 = 10 ) + ( num1 = -3 operator = '*' num2 = 3 ) + ( num1 = 1 operator = '/' num2 = 5 ) + ( num1 = -40 operator = '/' num2 = 2 ) + ( num1 = 5 operator = '/' num2 = 0 ) + ( num1 = 7 operator = '#' num2 = 4 ) ). + + DATA calc_results TYPE TABLE OF calc_results_struc WITH EMPTY KEY. + + LOOP AT calc_itab ASSIGNING FIELD-SYMBOL(). + + "Method call to calculate and return the result + DATA(res) = calc( num1 = -num1 + operator = -operator + num2 = -num2 ). + + "Adding the sy-tabix value to the table, too. + res-sy_tabix = sy-tabix. + + APPEND res TO calc_results. + + ENDLOOP. + + output->display( input = calc_results name = `calc_results` ). + +********************************************************************** + + output->display( `17) SELECT Loop` ). + + "SELECT ... ENDSELECT statements loop across the result set of a database access. + "For more examples, see the cheat sheet example on ABAP SQL. + "The example is meant to give an idea. The SELECT loop is purposely done across + "an internal table which is also possible - just in the interest of not dealing + "with a database table and not ensuring that a demo database table is filled. + "As above, the example includes simple calculations. + + TYPES: BEGIN OF struc4loop, + num TYPE i, + calc_result TYPE i, + END OF struc4loop. + + TYPES ty_itab_select_loop TYPE TABLE OF struc4loop WITH EMPTY KEY. + + DATA(loop_pass) = 0. + + DATA(itab_select_loop) = VALUE ty_itab_select_loop( ( num = 1 ) + ( num = 2 ) + ( num = 3 ) + ( num = 4 ) + ( num = 5 ) + ( num = 6 ) ). + + SELECT * + FROM @itab_select_loop AS itab + INTO @DATA(wa). + + IF sy-subrc = 0. + + "Loop pass stored and representing the table index value for the + "ABAP SQL statement further down. + loop_pass += 1. + + IF loop_pass <= 3. + + wa-calc_result = wa-num * 5. + + ELSEIF loop_pass = 6. + + "No calculation for this loop pass. Loop is terminated. + EXIT. + + ELSE. + + wa-calc_result = wa-num * 100. + + ENDIF. + + "Inserting calculation result in table + MODIFY itab_select_loop FROM wa INDEX loop_pass TRANSPORTING calc_result. + + ENDIF. + + ENDSELECT. + + output->display( input = itab_select_loop name = `itab_select_loop` ). + +********************************************************************** + + output->next_section( `Exception Handling` ). + + output->display( `18) TRY Control Structures` ). + + "TRY control structures are meant for handling catchable exceptions locally + "The example shows divisions. The predefined exception class cx_sy_zerodivide + "as suitable exception class is used. + "If the exception is not handled, the program is terminated and the runtime + "error COMPUTE_INT_ZERODIVIDE occurs. + "The third calculation is not carried out because the statement block is + "left due to the previous erroneous 0 division. + + TRY. + + DATA(div1) = 4 / 2. + output->display( input = div1 name = `div1` ). + + DATA(div2) = 4 / 0. + output->display( input = div2 name = `div2` ). + + DATA(div3) = 9 / 3. + output->display( input = div3 name = `div3` ). + + CATCH cx_sy_zerodivide. + + output->display( `0 division. The exception was caught.` ). + + ENDTRY. + + "The following example shows a catchable exception that is + "raised if a line is not found when using table expressions. + TRY. + + DATA(line) = str_table[ 12345 ]. + + "The predefined exception class cx_sy_itab_line_not_found + "as suitable exception class is used here. + "If the exception is not handled, the program is terminated + "and the runtime error ITAB_LINE_NOT_FOUND occurs. + CATCH cx_sy_itab_line_not_found. + + output->display( `The line was not found. The exception was caught.` ). + + ENDTRY. + +********************************************************************** + + output->next_section( `19) Multiple CATCH Blocks` ). + + "It is possible to specify multiple exception classes in a list and + "multiple CATCH blocks. + "Note: If there are multiple CATCH blocks for exceptions that are in an inheritance + "relationship, you must pay attention that the more special exceptions are specified + "before the more general ones. + "The calculation example shows multiple CATCH blocks that themselves have more than + "one exception class specified. Here, local exception classes are specified just for + "demonstration purposes. They are not relevant in this TRY control structure. + + "Filling internal table of type i as basis for calculations + int_itab = VALUE #( ( 5 ) ( 0 ) ( 987654321 ) ). + + LOOP AT int_itab ASSIGNING FIELD-SYMBOL(). + + TRY. + + output->display( |--- Calculations with { } ---| ). + + DATA(calc1) = CONV decfloat34( 1 / ). + + output->display( input = calc1 name = `calc1` ). + + DATA(calc2) = ipow( base = exp = 2 ). + + output->display( input = calc2 name = `calc2` ). + + + CATCH cx_sy_arithmetic_overflow lcx_calc_error. + + output->display( `Arithmetic overflow. The exception was caught.` ). + + CATCH cx_sy_zerodivide lcx_some_error. + + output->display( `0 division. The exception was caught.` ). + + ENDTRY. + + ENDLOOP. + +********************************************************************** + + output->next_section( `20) Using an Exception Class Higher Up in the Inheritance Tree` ). + + "In the following CATCH block, the predefined exception class cx_sy_arithmetic_error + "is specified. Both cx_sy_zerodivide and cx_sy_arithmetic_overflow are derived from + "cx_sy_arithmetic_error which is an exception class higher up in the inheritance + "tree. Hence, cx_sy_arithmetic_error can be specified and handle both exceptions, too. + "The following example is basically the same as above. However, only one exception + "class is specified. + + LOOP AT int_itab ASSIGNING FIELD-SYMBOL(). + + TRY. + + output->display( |--- Calculations with { } ---| ). + + calc1 = 1 / . + + output->display( input = calc1 name = `calc1` ). + + calc2 = ipow( base = exp = 2 ). + + output->display( input = calc2 name = `calc2` ). + + + CATCH cx_sy_arithmetic_error. + + output->display( `Arithmetic error. The exception was caught.` ). + + ENDTRY. + + ENDLOOP. + +********************************************************************** + + output->next_section( `21) Storing a Reference to the Exception Object` ). + + "You can use the addition INTO plus an object reference variable to store + "a reference to an exception object. It is, for example, relevant to + "determine the exact exception. + "The following example is the same as above using the more general exception + "class cx_sy_arithmetic_error. You can carry out certain tasks, for + "example, retrieving and displaying the exception text. To retrieve exception + "texts, you can call, for example, the method get_text. + + LOOP AT int_itab ASSIGNING FIELD-SYMBOL(). + + TRY. + + output->display( |--- Calculations with { } ---| ). + + calc1 = 1 / . + + output->display( input = calc1 name = `calc1` ). + + calc2 = ipow( base = exp = 2 ). + + output->display( input = calc2 name = `calc2` ). + + + CATCH cx_sy_arithmetic_error INTO exception. + "Note: + "- The object reference variable is of type cx_root. + "- You could also create the variable inline, e. g. ... INTO DATA(exc). + + "Retrieving and displaying exception text + exception_text = exception->get_text( ). + + output->display( input = exception_text name = `exception_text` ). + + ENDTRY. + + ENDLOOP. + +********************************************************************** + + output->next_section( `22) Raising Exceptions Programmatically` ). + + "The following examples demonstrate the ABAP statement RAISE EXCEPTION + "using the addition TYPE. Note there are more additions available that + "are not dealt with here. + + "ABAP 'allows' zero division if the first operand is also 0 as shown + "here. + DATA(division) = 0 / 0. + + output->display( input = division name = `division` ). + + "In this example, the appropriate exception - the predefined exception + "class cx_sy_zerodivide - is raised. + TRY. + division = 0 / 0. + + "raise predefined exception for 0 division + RAISE EXCEPTION TYPE cx_sy_zerodivide. + + CATCH cx_sy_zerodivide INTO DATA(error_oref). + + exception_text = error_oref->get_text( ). + + output->display( input = exception_text name = `exception_text` ). + + ENDTRY. + + "The following example just demonstrates a locally defined + "exception class that must be raised programmatically. + "In ADT, see the definition of the class in the include, i. e. + "the tab 'Class-relevant Local Types'. + TRY. + RAISE EXCEPTION TYPE lcx_some_error. + + CATCH lcx_some_error INTO exception. + + exception_text = exception->get_text( ). + + output->display( `Default exception text for a local exception class:` ). + output->display( input = exception_text name = `exception_text` ). + + ENDTRY. + +********************************************************************** + + output->next_section( `23) Nested TRY Control Structure` ). + + TRY. + TRY. + RAISE EXCEPTION TYPE lcx_some_error. + CATCH lcx_some_error INTO DATA(error_inner_catch). + output->display( `Inner CATCH` ). + RAISE EXCEPTION error_inner_catch. + ENDTRY. + CATCH lcx_some_error. + output->display( `Outer CATCH` ). + ENDTRY. + +********************************************************************** + + output->next_section( `24) Raising an Exception in the Context of Conditional Expressions` ). + + "In this example, the optional addition THROW is used in a conditional + "expression. A self-defined local exception class is used. + + DATA(number) = 3. + + TRY. + DATA(cond_raise) = COND #( WHEN number = 1 THEN `one` + WHEN number = 2 THEN `two` + ELSE THROW lcx_some_error( ) ). + + output->display( input = cond_raise name = `cond_raise` ). + + CATCH lcx_some_error INTO exception. + + exception_text = exception->get_text( ). + + output->display( input = exception_text name = `exception_text` ). + + ENDTRY. + +********************************************************************** + + output->next_section( `25) RAISING Parameter in Method Delcarations` ). + + "In the following example, a string table is looped across. The table + "includes valid and invalid email addresses. The validity is checked + "in a method. In the method, a local exception class is raised if the + "email address is invalid. + + str_table = VALUE string_table( ( `john.doe@email.com` ) "valid + ( `john.doe@#email.com` ) "invalid + ( `jane.doe@email.com` ) "valid + ( `jane#doe@email.com` ) "invalid + ( `max.mustermann@email12.com` ) "valid + ( `max.mustermann@email12.c#m` ) "invalid + ( `some_name@email.com` ) "valid + ( `some_name@email.c` ) "invalid + ). + + TYPES: BEGIN OF struc_email_check, + email TYPE string, + is_email_valid TYPE abap_bool, + exception_raised TYPE abap_bool, + END OF struc_email_check. + + DATA itab_email_check TYPE TABLE OF struc_email_check WITH EMPTY KEY. + + LOOP AT str_table ASSIGNING FIELD-SYMBOL(). + + TRY. + DATA(email_valid) = validate_email( email = ). + DATA(exc_raised) = abap_false. + + CATCH lcx_invalid_email. + + email_valid = abap_false. + exc_raised = abap_true. + + ENDTRY. + + APPEND VALUE #( email = + is_email_valid = email_valid + exception_raised = exc_raised ) TO itab_email_check. + + ENDLOOP. + + output->display( input = itab_email_check name = `itab_email_check` ). + +********************************************************************** + + output->next_section( `Exception Classes Derived from CX_STATIC_CHECK` ). + + "Exception Classes of type cx_static_check force users to handle exceptions. + "Exceptions that are declared in the method signature make users aware of which + "potential exception might occur. And the exceptions should be respected when using + "such a method. + "In this simplified example, a method has a self-defined local exception class + "specified for the RAISING addition. If an actual parameter has a certain + "value, the method raises an exception. + "You can comment in the following line of code to see the enforcement. + "A syntax warning is displayed since there is no proper exception handling. + + "DATA(my_user_a) = whats_my_user( get_name = abap_false ). + + "Method call with a proper exception handling + TRY. + + DATA(my_user_b) = whats_my_user( get_name = abap_true ). + + output->display( input = my_user_b name = `my_user_b` ). + + DATA(my_user_c) = whats_my_user( get_name = abap_false ). + + output->display( input = my_user_c name = `my_user_c` ). + + CATCH lcx_static_exc_class INTO exception. + + exception_text = exception->get_text( ). + + output->display( input = exception_text name = `exception_texts` ). + + ENDTRY. + +********************************************************************** + + output->next_section( `Exception Classes Derived from CX_DYNAMIC_CHECK` ). + + "Exception Classes derived from cx_dynamic_check are for exceptions that + "can be checked and avoided by preconditions. + "The checking if a local handling or an explicit declaration in procedure + "interfaces is available is carried out at runtime only ("dynamic check") + "and only in case the exception is indeed raised. + "The following example includes two TRY control structures in which a + "method is called that carries out calculations. In this case, the + "method uses the imported value and carries out two calculations (the + "value powered by 2 and the square root). + "The method signature purposely only includes the exception class + "cx_sy_arithmetic_overflow, i. e. calculation errors regarding the + "square root calculation are 'ignored' here. + "The two exception classes cx_sy_arithmetic_overflow and cx_sy_arg_out_of_domain + "are both derived from cx_dynamic_check. + + "This TRY control structure only catches exception for + "cx_sy_arithmetic_overflow. The example does purposely not + "include an error for the square root calculation. If you + "implemented the code outside of the TRY control structure, + "there would not be a syntax warning. Actually, you could + "prevent a calculation failure by setting appropriate values. + TRY. + + DATA(res1) = power2_and_sqrt( num = 4 ). + + output->display( input = res1 name = `res1` ). + + DATA(res2) = power2_and_sqrt( num = 123456789 ). + + output->display( input = res2 name = `res2` ). + + CATCH cx_sy_arithmetic_overflow INTO exception. + + exception_text = exception->get_text( ). + + output->display( input = exception_text name = `exception_text` ). + + ENDTRY. + + "This TRY control structure demonstrates the following: + "If it is determined at runtime that an exception derived from cx_dynamic_check + "is neither locally handled nor an interface is declared appropriately - and the + "exception is raised - a new exception of type cx_sy_no_handler is raised. In this + "case, the attribute 'previous' contains a reference to the original exception. + TRY. + + DATA(res3) = power2_and_sqrt( num = 16 ). + + output->display( input = res3 name = `res3` ). + + DATA(res4) = power2_and_sqrt( num = -1 ). + + output->display( input = res4 name = `res4` ). + + "The specification of the suitable exception class does not help here. + "The error is raised while processing the method. + CATCH cx_sy_arg_out_of_domain INTO exception. + + exception_text = exception->get_text( ). + + output->display( input = exception_text name = `exception_text` ). + + CATCH cx_sy_no_handler INTO exception. + + exception_text = exception->get_text( ). + + output->display( input = exception_text name = `exception_text` ). + + "Attribute 'previous' + "In this case, the information of the actual runtime error is provided: + "Here, it is COMPUTE_SQRT_DOMAIN. + output->display( input = exception->previous name = `exception->previous` ). + + "For demo purposes, RTTI is used here to retrieve the 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( ). + + output->display( input = relative_name name = `relative_name` ). + + ENDTRY. + +********************************************************************** + + "Excursion: Runtime Errors and Terminating Programs + + output->next_section( `Excursion: Runtime Errors and Terminating Programs` ). + + "ASSERT statements are followed by a logical expression. If the expression is false, + "the program is terminated and an uncatchable exception is raised resulting in the + "runtime error ASSERTION_FAILED. + "You can comment 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##` ). + + output->display( `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 = |Calculation result:\n{ num } powered by 2 = { ipow( base = num exp = 2 ) }\nSquare 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..832208b --- /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..f9f13d5 --- /dev/null +++ b/src/zcl_demo_abap_rap_draft_ln_m.clas.abap @@ -0,0 +1,507 @@ +*********************************************************************** +* +* 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 V2 - 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. +* +* ----------------------------- 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. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: RAP Calculator Using Managed, ` && + `Draft-Enabled RAP BO (Late Numbering)` ). + output->display( `1) Creating Instances and ` && + `Saving to the database` ). + + "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. + output->display( `Responses after MODIFY operation` ). + + IF m IS NOT INITIAL. + output->display( input = m name = `m` ). + ENDIF. + + IF f IS NOT INITIAL. + output->display( input = f name = `f` ).. + ENDIF. + + IF r IS NOT INITIAL. + output->display( input = r name = `r` ). + ENDIF. + + ENDIF. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `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. + output->display( `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 + output->display( `1a) Draft and database tables before ` && + `ACTIVATE action` ). + output->display( `Draft table before activation` ). + output->display( input = draft_parent_before_act name = `draft_parent_before_act` ). + + + output->display( `Database table before activation` ). + output->display( input = db_tab_root_before_act name = `db_tab_root_before_act` ). + + + output->next_section( `1b) Draft and database tables after ` && + `ACTIVATE action` ). + output->display( `Draft table after activation` ). + output->display( input = draft_parent_afer_act name = `draft_parent_afer_act` ). + + + output->display( `Database table after activation` ). + output->display( input = db_tab_root_after_act name = `db_tab_root_after_act` ). + + +********************************************************************** + + output->next_section( `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. + output->display( input = `Responses after MODIFY operation` ). + + IF m IS NOT INITIAL. + output->display( input = m name = `m` ). + ENDIF. + + IF f IS NOT INITIAL. + output->display( input = f name = `f` ).. + ENDIF. + + IF r IS NOT INITIAL. + output->display( input = r name = `r` ). + ENDIF. + + ENDIF. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `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. + output->display( input = `Responses after MODIFY operation` ). + + IF m IS NOT INITIAL. + output->display( input = m name = `m` ). + ENDIF. + + IF f IS NOT INITIAL. + output->display( input = f name = `f` ).. + ENDIF. + + IF r IS NOT INITIAL. + output->display( input = r name = `r` ). + ENDIF. + + ENDIF. + + COMMIT ENTITIES. + + IF sy-subrc <> 0. + output->display( `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 + output->next_section( `2a) Draft and database tables before ` && + `ACTIVATE action` ). + output->display( `Draft table before activation` ). + output->display( input = draft_parent_before_act name = `draft_parent_before_act` ). + + + output->display( `Database table before activation` ). + output->display( input = db_tab_root_before_act name = `db_tab_root_before_act` ). + + + output->next_section( `2b) Draft and database tables after ` && + `ACTIVATE action` ). + output->display( `Draft table after activation` ). + output->display( input = draft_parent_afer_act name = `draft_parent_afer_act` ). + + + output->display( `Database table after activation` ). + output->display( input = db_tab_root_after_act name = `db_tab_root_after_act` ). + + +********************************************************************** + + output->next_section( `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. + output->display( `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. + output->display( `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 + output->display( input = `Draft and database tables after ` && + `ACTIVATE action` ). + + output->display( `Draft table after activation` ). + output->display( input = draft_parent_afer_act name = `draft_parent_afer_act` ). + + + output->display( `Database table after activation` ). + output->display( input = 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_ext_num_m.clas.abap b/src/zcl_demo_abap_rap_ext_num_m.clas.abap new file mode 100644 index 0000000..70b7835 --- /dev/null +++ b/src/zcl_demo_abap_rap_ext_num_m.clas.abap @@ -0,0 +1,973 @@ +*********************************************************************** +* +* 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. + 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(). + + DATA op TYPE string. + + 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. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: RAP BO Operations Using a Managed ` && + `RAP BO` ). + +********************************************************************** +* +* Create operation +* +********************************************************************** + + output->display( `1) Create operation` ). + + "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. + output->display( `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). + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Update operations +* +********************************************************************** + + output->next_section( `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. + output->display( `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. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Delete operation +* +********************************************************************** + + output->next_section( `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. + output->display( `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. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Action multiply_by_2 +* +********************************************************************** + + output->next_section( `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. + output->display( `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. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Create-by-association operation (from root to child entity) +* +********************************************************************** + + output->next_section( `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. + output->display( `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). + + output->display( input = tab_root name = `tab_root` ). + output->display( input = tab_child name = `tab_child` ). + + "Displaying response information + IF mapped IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root and child entity)` ). + + output->display( input = mapped name = `mapped` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Validation val +* +********************************************************************** + + output->next_section( `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. + output->display( `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. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root and child entity)` ). + + output->display( input = mapped name = `mapped` ). + ENDIF. + + IF failed_late IS NOT INITIAL. + output->display( `Entries in FAILED LATE response parameter` ). + + output->display( input = failed_late name = `failed_late` ). + ENDIF. + + IF reported_late IS NOT INITIAL. + output->display( `Entries in REPORTED LATE response parameter` ). + + output->display( input = reported_late name = `reported_late` ). + ENDIF. + +********************************************************************** +* +* Read operation (root entity) +* +********************************************************************** + + output->next_section( `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 + output->display( input = result name = `result` ). + + "Displaying response information + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************* +* +* Read operation (child entity) +* +********************************************************************** + + output->next_section( `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 + output->display( input = read_ch name = `read_ch` ). + + + "Displaying response information + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Read-by-association operation (from parent to child) +* +********************************************************************** + + output->next_section( `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 + output->display( input = rba_result name = `rba_result` ). + output->display( input = association_links name = `association_links` ). + + "Displaying response information + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Read-by-association operation (from child to parent) +* +********************************************************************** + + output->next_section( `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 + output->display( input = rba_parent name = `rba_parent` ). + output->display( input = association_links_parent name = `association_links_parent` ). + + "Displaying response information + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Excursion: Read and read-by-association operation using a dynamic +* EML statement +* +********************************************************************** + + output->next_section( `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. + + output->display( `Read result (root)` ). + output->display( input = read_dyn_result name = `read_dyn_result` ). + output->display( `Read result (read-by-association)` ). + output->display( input = rba_dyn_result name = `rba_dyn_result` ). + output->display( `Links` ). + output->display( input = 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..c277626 --- /dev/null +++ b/src/zcl_demo_abap_rap_ext_num_u.clas.abap @@ -0,0 +1,1085 @@ +*********************************************************************** +* +* 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. + 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(). + + DATA op TYPE string. + + 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. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: RAP BO Operations Using an ` && + `Unmanaged RAP BO (External Numbering)` ). + +********************************************************************** +* +* Create operation +* +********************************************************************** + + output->display( `1) Create Operation` ). + + "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. + output->display( `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). + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Update operations +* +********************************************************************** + + output->next_section( `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. + output->display( `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. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Delete operation +* +********************************************************************** + + output->next_section( `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. + output->display( `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. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Action multiply_by_2 +* +********************************************************************** + + output->next_section( `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. + output->display( `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. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Action multiply_by_3 +* +********************************************************************** + + output->next_section( `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. + output->display( `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. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + + ENDIF. + +********************************************************************** +* +* Action set_z +* +********************************************************************** + + output->next_section( `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. + output->display( `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. + + output->display( input = tab_root name = `tab_root` ). + + "Displaying response information + IF mapped-root IS NOT INITIAL. + + output->display( `Entries in MAPPED response parameter ` && + `(root entity)` ). + + output->display( input = mapped-root name = `mapped-root` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + + ENDIF. + +********************************************************************** +* +* Create-by-Association Operation (from Root to Child Entity) +* +********************************************************************** + + output->next_section( `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. + output->display( `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). + + output->display( input = tab_root name = `tab_root` ). + output->display( input = tab_child name = `tab_child` ). + + "Displaying response information + IF mapped IS NOT INITIAL. + output->display( `Entries in MAPPED response parameter ` && + `(root and child entity)` ). + + output->display( input = mapped name = `mapped` ). + ENDIF. + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Read operation +* +********************************************************************** + + output->next_section( `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 + output->display( input = result name = `result` ). + + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************* +* +* Read operation (child entity) +* +********************************************************************** + + output->next_section( `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 + output->display( input = read_ch name = `read_ch` ). + + "Displaying response information + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Read-by-association operation (from parent to child) +* +********************************************************************** + + output->next_section( `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 + output->display( input = rba_result name = `rba_result` ). + output->display( input = association_links name = `association_links` ). + + "Displaying response information + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + ENDIF. + +********************************************************************** +* +* Read-by-association operation (from child to parent) +* +********************************************************************** + + output->next_section( `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 + output->display( input = rba_parent name = `rba_parent` ). + output->display( input = association_links_parent name = `association_links_parent` ). + + "Displaying response information + IF failed IS NOT INITIAL. + output->display( `Entries in FAILED response parameter` ). + + output->display( input = extract_from_failed( ) name = `extract_from_failed( )` ). + ENDIF. + + IF reported IS NOT INITIAL. + output->display( `Entries in REPORTED response parameter` ). + + output->display( input = extract_from_reported( ) name = `extract_from_reported( )` ). + 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. +********************************************************************** + + output->next_section( `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 ) ) ). + + output->display( `Result if FULL parameter is ` && + `not flagged for RBA` ). + + 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. + + output->display( input = read_dyn_result name = `read_dyn_result` ). + output->display( input = rba_dyn_result name = `rba_dyn_result` ). + output->display( input = rba_dyn_link name = `rba_dyn_link` ). + + output->display( `Result if FULL parameter is ` && + `flagged for RBA` ). + + 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. + + output->display( input = read_dyn_result name = `read_dyn_result` ). + output->display( input = rba_dyn_result name = `rba_dyn_result` ). + output->display( input = 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_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_sql.clas.abap b/src/zcl_demo_abap_sql.clas.abap new file mode 100644 index 0000000..d613e61 --- /dev/null +++ b/src/zcl_demo_abap_sql.clas.abap @@ -0,0 +1,1600 @@ +*********************************************************************** +* +* ABAP cheat sheet: ABAP SQL +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntactical 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_flight_tables=>fill_dbtabs( ). + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: ABAP SQL` ). + output->display( `Using SELECT for multiple purposes` ). + output->display( `1) Reading a single row from database table ` && + `into a structure` ). + + "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). + + output->display( input = struct name = `struct` ). + output->display( input = struct_1a name = `struct_1a` ). + + "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. + + output->display( input = struct_1b name = `struct_1b` ). + output->display( input = struct_1c name = `struct_1c` ). + output->display( input = struct_1d name = `struct_1d` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = itab name = `itab` ). + output->display( input = itab_2a name = `itab_2a` ). + output->display( input = itab_2b name = `itab_2b` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = itab3 name = `itab3` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = struc4 name = `struc4` ). + output->display( input = itab4 name = `itab4` ). + +********************************************************************** + + output->next_section( `Clause variations and additions in SELECT statements` ). + + "SELECT/FROM clause variants + output->display( 'SELECT/FROM clause variants' ). + + output->display( `5) Checking the existence of a row in ` && + `a database table` ). + + "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. + output->display( `A line was found.` ). + ELSE. + output->display( `Nothing found.` ). + ENDIF. + +********************************************************************** + + output->next_section( `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). + + output->display( input = itab_6a name = `itab_6a` ). + output->display( input = itab_6b name = `itab_6b` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = itab_7a name = `itab_7a` ). + output->display( input = itab_7b name = `itab_7b` ). + output->display( input = itab_7c name = `itab_7c` ). + output->display( input = itab_7d name = `itab_7d` ). + output->display( input = itab_7e name = `itab_7e` ). + output->display( input = itab_7f name = `itab_7f` ). + output->display( input = itab_7g name = `itab_7g` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = itab_read2 name = `itab_read2` ). + +********************************************************************** + + output->next_section( 'INTO clause variants' ). + output->display( `9) UP TO: Limiting the number of returned table rows` ). + + "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. + + output->display( input = itab_up name = `itab_up` ). + +********************************************************************** + + output->display( `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). + + output->display( input = itab_no_off name = `itab_no_off` ). + + SELECT * + FROM zdemo_abap_flsch + WHERE carrid = 'LH' + ORDER BY fltime ASCENDING + INTO TABLE @DATA(itab_w_off) + OFFSET 2. + + output->display( input = itab_w_off name = `itab_w_off` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = itab_ind name = `itab_ind` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = itab_up name = `itab_up` ). + + "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. + + output->display( input = itab_corr name = `itab_corr` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = pack_table name = `pack_table` ). + output->display( input = itab_pack name = `itab_pack` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = dref->* name = `dref->*` ). + +********************************************************************** + + output->next_section( `Excursion: ABAP SQL - Operands and Expressions` ). + output->display( `15) SQL operands` ). + + "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 + + output->display( input = sql_operands name = `sql_operands` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = numeric_functions name = `numeric_functions` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = string_functions name = `string_functions` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = components name = `components` ). + output->display( input = special_functions name = `special_functions` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = agg_exp name = `agg_exp` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = more_sql_expr name = `more_sql_expr` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = win name = `win` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = win_order name = `win_order` ). + +********************************************************************** + + output->next_section( `SQL conditions` ). + output->display( `23) SQL conditions (1)` ). + "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). + + output->display( input = itab_comp_op name = `itab_comp_op` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = it_sql_cond name = `it_sql_cond` ). + +********************************************************************** + + output->next_section( `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). + + + output->display( input = itab_like_in name = `itab_like_in` ). + +********************************************************************** + + output->next_section( `Further clauses in SELECT statements` ). + + output->display( `26) GROUP BY: Combining groups of table rows ` && + `in the result set` ). + "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). + + output->display( input = itab_gr name = `itab_gr` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = itab_hav name = `itab_hav` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = itab_ord1 name = `itab_ord1` ). + output->display( input = itab_ord2 name = `itab_ord2` ). + +********************************************************************** + + output->next_section( `WHERE clause variants: Selecting data by evaluating the content of other tables` ). + output->display( `29) FOR ALL ENTRIES addition` ). + + "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. + + output->display( input = itab_forall name = `itab_forall` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = itab_sub name = `itab_sub` ). + +********************************************************************** + + output->next_section( `Combining Data of Multiple Database Tables` ). + output->display( `31) Inner join` ). + "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). + + output->display( input = itab_in1 name = `itab_in1` ). + output->display( input = itab_in2 name = `itab_in2` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = itab_lo name = `itab_lo` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = itab_union name = `itab_union` ). + +********************************************************************** + + output->next_section( `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). + + output->display( input = itab_cte name = `itab_cte` ). + +********************************************************************** + + output->next_section( `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. + output->display( input = wa_cte_loop name = `wa_cte_loop` ). + ENDWITH. + +********************************************************************** + + output->next_section( `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' ). + +********************************************************************** + + output->display( `36) INSERT: Inserting individual line into a database table` ). + + "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( ). + output->display( input = itab_res name = `itab_res` ). + +********************************************************************** + + output->next_section( `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( ). + output->display( input = itab_res name = `itab_res` ). + +********************************************************************** + + output->next_section( `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( ). + output->display( input = itab_res name = `itab_res` ). + output->display( input = subrc name = `subrc` ). + +********************************************************************** + + output->next_section( `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( ). + output->display( input = itab_res name = `itab_res` ). + +********************************************************************** + + output->next_section( `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( ). + output->display( input = itab_res name = `itab_res` ). + +********************************************************************** + + output->next_section( `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( ). + output->display( input = itab_res name = `itab_res` ). + +********************************************************************** + + output->next_section( `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( ). + output->display( input = itab_res name = `itab_res` ). + +********************************************************************** + + output->next_section( `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( ). + output->display( input = itab_res name = `itab_res` ). + output->display( |{ dbcnt } table rows were modified.| ). + +********************************************************************** + + output->next_section( `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( ). + output->display( input = itab_res name = `itab_res` ). + +********************************************************************** + + output->next_section( `45) DELETE: Deleting table rows based on a condition` ). + + DELETE FROM zdemo_abap_carr WHERE currcode <> 'EUR'. + + select_from_dbtab( ). + output->display( input = itab_res name = `itab_res` ). + +********************************************************************** + + output->next_section( `46) DELETE: Delete complete table` ). + + DELETE FROM zdemo_abap_carr. + + select_from_dbtab( ). + output->display( input = 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..e92cd5f --- /dev/null +++ b/src/zcl_demo_abap_sql_group_by.clas.abap @@ -0,0 +1,175 @@ +*********************************************************************** +* +* 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_flight_tables=>fill_dbtabs( ). + ENDMETHOD. + + + METHOD if_oo_adt_classrun~main. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: Grouping Internal Tables` ). + + SELECT * + FROM zdemo_abap_flsch + INTO TABLE @DATA(fli_tab). + + output->display( `1) Representative Binding` ). + output->display( `1a) Grouping by one column` ). + + LOOP AT fli_tab INTO wa + GROUP BY wa-carrid. + output->display( wa-carrid ). + ENDLOOP. + + output->next_section( `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. + + output->display( members ). + ENDLOOP. + + output->next_section( `1c) Grouping by two columns` ). + + LOOP AT fli_tab INTO wa + GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom ). + + output->display( |{ wa-carrid } { wa-airpfrom }| ). + ENDLOOP. + + output->next_section( `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. + + output->display( members ). + ENDLOOP. + + output->next_section( `2) Group Key Binding` ). + output->display( `2a) Grouping by one column` ). + + LOOP AT fli_tab INTO wa + GROUP BY wa-carrid + INTO DATA(key). + + output->display( key ). + ENDLOOP. + + output->next_section( `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. + + output->display( members ). + ENDLOOP. + + output->next_section( `2c) Grouping by two columns` ). + + LOOP AT fli_tab INTO wa + GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom ) + INTO DATA(keys). + + output->display( keys ). + ENDLOOP. + + output->next_section( `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. + + output->display( members ). + ENDLOOP. + + output->next_section( `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). + + output->display( keysplus ). + 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..c560db6 --- /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..d18fe54 --- /dev/null +++ b/src/zcl_demo_abap_string_proc.clas.abap @@ -0,0 +1,1759 @@ +*********************************************************************** +* +* ABAP cheat sheet: String processing +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntactical 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. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: String Processing` ). + output->display( `1) Creating Strings and Assigning Values` ). + + "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. + char_a2 = 'ijklmnopq'. + + "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 && `!`. + + output->display( input = str_a3 name = `str_a3` ). + output->display( input = char_a2 name = `char_a2` ). + output->display( input = str_a7 name = `str_a7` ). + output->display( input = str_a8 name = `str_a8` ). + +********************************************************************** + + output->next_section( `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 `. + + output->display( input = str_b4 name = `str_b4` ). + output->display( input = char_b1 name = `char_b1` ). + +********************************************************************** + + output->next_section( `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?|. + + output->display( input = str_c4 name = `str_c4` ). + +********************************************************************** + + output->next_section( `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?|. + + output->display( input = str_c5 name = `str_c5` ). + + "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. + + output->display( input = str_c6 name = `str_c6` ). + output->display( input = str_c7 name = `str_c7` ). + output->display( input = str_c8 name = `str_c8` ). + output->display( input = str_c9 name = `str_c9` ). + +********************************************************************** + + output->next_section( `4) String Templates (3): Formatting Options` ). + "Time, date + DATA(str_d1) = + |Date: { cl_abap_context_info=>get_system_date( ) DATE = USER }\n| && + |Time: { cl_abap_context_info=>get_system_time( ) TIME = ISO }\n| && + |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) = |\\ \| \{ \}|. + + output->display( input = str_d1 name = `str_d1` ). + output->display( input = str_d3 name = `str_d3` ). + output->display( input = str_d4 name = `str_d4` ). + output->display( input = str_d5 name = `str_d5` ). + output->display( input = str_d6 name = `str_d6` ). + output->display( input = str_d7 name = `str_d7` ). + output->display( input = str_d8 name = `str_d8` ). + output->display( input = str_d9 name = `str_d9` ). + output->display( input = str_d10 name = `str_d10` ). + output->display( input = str_d11 name = `str_d11` ). + output->display( input = str_d14 name = `str_d14` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = length_e1 name = `length_e1` ). + output->display( input = length_e2 name = `length_e2` ). + output->display( input = length_e3 name = `length_e3` ). + output->display( input = length_e4 name = `length_e4` ). + output->display( input = int_e1 name = `int_e1` ). + output->display( input = int_e2 name = `int_e2` ). + +********************************************************************** + + output->next_section( `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 = `,` ). + + output->display( input = str_f3 name = `str_f3` ). + output->display( input = str_f4 name = `str_f4` ). + output->display( input = str_f5 name = `str_f5` ). + output->display( input = str_f6 name = `str_f6` ). + output->display( input = str_f7 name = `str_f7` ). + output->display( input = str_f8 name = `str_f8` ). + output->display( input = char_f4 name = `char_f4` ). + output->display( input = char_f5 name = `char_f5` ). + output->display( input = alphabet1 name = `alphabet1` ). + output->display( input = alphabet2 name = `alphabet2` ). + output->display( input = alphabet3 name = `alphabet3` ). + output->display( input = alphabet4 name = `alphabet4` ). + output->display( input = alphabet5 name = `alphabet5` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = str_g2 name = `str_g2` ). + output->display( input = str_g3 name = `str_g3` ). + output->display( input = str_g4 name = `str_g4` ). + output->display( input = str_g5 name = `str_g5` ). + output->display( input = str_g6 name = `str_g6` ). + output->display( input = itab_g1 name = `itab_g1` ). + output->display( input = str_g7 name = `str_g7` ). + output->display( input = str_g8 name = `str_g8` ). + output->display( input = itab_g2 name = `itab_g2` ). + output->display( input = seg_nom name = `seg_nom` ). + +********************************************************************** + + output->next_section( `Modifying Strings` ). + output->display( `8) Transforming to Lower and Upper Case` ). + + 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 ). + + output->display( input = str_h3 name = `str_h3` ). + output->display( input = str_h4 name = `str_h4` ). + output->display( input = str_h1 name = `str_h1` ). + output->display( input = str_h2 name = `str_h2` ). + output->display( input = str_h6 name = `str_h6` ). + output->display( input = str_h7 name = `str_h7` ). + output->display( input = str_h8 name = `str_h8` ). + output->display( input = str_h9 name = `str_h9` ). + +********************************************************************** + + output->next_section( `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 + + output->display( `SHIFT statements:` ). + output->display( input = str_i2 name = `str_i2` ). + output->display( input = str_i3 name = `str_i3` ). + output->display( input = str_i4 name = `str_i4` ). + output->display( input = char_i1 name = `char_i1` ). + output->display( input = str_i5 name = `str_i5` ). + output->display( input = char_i2 name = `char_i2` ). + output->display( input = char_i3 name = `char_i3` ). + output->display( input = str_i6 name = `str_i6` ). + output->display( input = str_i7 name = `str_i7` ). + + output->display( `String functions:` ). + output->display( input = str_i9 name = `str_i9` ). + output->display( input = str_i10 name = `str_i10` ). + output->display( input = str_i11 name = `str_i11` ). + output->display( input = str_i13 name = `str_i13` ). + output->display( input = str_i14 name = `str_i14` ). + output->display( input = str_i15 name = `str_i15` ). + +********************************************************************** + + output->next_section( `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` ). + + output->display( `CONDENSE statements:` ). + output->display( input = char_j1 name = `char_j1` ). + output->display( input = char_j2 name = `char_j2` ). + output->display( input = char_j3 name = `char_j3` ). + output->display( input = str_j name = `str_j` ). + output->next_section( `String function condense:` ). + output->display( input = str_j2 name = `str_j2` ). + output->display( input = str_j3 name = `str_j3` ). + output->display( input = str_j4 name = `str_j4` ). + output->display( input = str_j6 name = `str_j6` ). + output->display( input = str_j7 name = `str_j7` ). + +********************************************************************** + + output->next_section( `11) Reversing Strings` ). + + DATA(str_k) = reverse( `ollah` ). + + output->display( input = str_k name = `str_k` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = str_l2 name = `str_l2` ). + output->display( input = str_l3 name = `str_l3` ). + output->display( input = str_l4 name = `str_l4` ). + +********************************************************************** + + output->next_section( `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'. + + output->display( input = cl_name name = `cl_name` ). + output->display( input = t1 name = `t1` ). + +********************************************************************** + + output->next_section( `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` ). + + output->display( input = str_m2 name = `str_m2` ). + output->display( input = str_m3 name = `str_m3` ). + output->display( input = str_m4 name = `str_m4` ). + output->display( input = str_m5 name = `str_m5` ). + output->display( input = str_m6 name = `str_m6` ). + output->display( input = str_m7 name = `str_m7` ). + output->display( input = char_m2 name = `char_m2` ). + output->display( input = char_m3 name = `char_m3` ). + output->display( input = str_m9 name = `str_m9` ). + output->display( input = str_m10 name = `str_m10` ). + output->display( input = str_m11 name = `str_m11` ). + output->display( input = str_m12 name = `str_m12` ). + output->display( input = str_m13 name = `str_m13` ). + +********************************************************************** + + output->next_section( `Searching and Replacing in Strings` ). + output->display( `15) Searching Specific Characters in Strings ` && + `Using Comparison Operators and String Functions` ). + + 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`. + output->display( |CA: str_n1 contains any of the characters. | && + |The position of the first found character is { sy-fdpos }.| ). + ELSE. + output->display( |CA: str_n1 does not contain any of the characters. | && + |The length of str_n1 is { sy-fdpos }.| ). + ENDIF. + + "NA (contains not any) + IF str_n1 NA `xyz`. + output->display( |NA: str_n1 does not contain any of the characters.| && + |The length of str_n1 is { sy-fdpos }.| + ). + ELSE. + output->display( |NA: str_n1 contains any of the characters. | && + |The position of the first found character is { sy-fdpos }.| ). + ENDIF. + + "Determining if a string is exclusively composed of a certain + "character set + IF str_n1 CO `rs`. + output->display( |CO: str_n1 contains only the characters. | + && |The length of str_n1 is { sy-fdpos }.| ). + ELSE. + output->display( |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 }.| ). + ENDIF. + + "Negation of CO + IF str_n1 CN `chers`. + output->display( |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 }.| + ). + ELSE. + output->display( |CN: str_n1 contains only the characters. | + && |The length of str_n1 is { sy-fdpos }.| ). + 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.` ). + + output->display( input = res_n3 name = `res_n3` ). + output->display( input = res_n4 name = `res_n4` ). + output->display( input = res_n5 name = `res_n5` ). + output->display( input = res_n6 name = `res_n6` ). + output->display( input = res_n7 name = `res_n7` ). + output->display( input = res_n8 name = `res_n8` ). + output->display( input = res_n9 name = `res_n9` ). + output->display( input = res_n10 name = `res_n10` ). + output->display( input = res_n11 name = `res_n11` ). + output->display( input = res_n12 name = `res_n12` ). + output->display( input = res_n13 name = `res_n13` ). + +********************************************************************** + + output->next_section( `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`. + + output->display( input = str_o2 name = `str_o2` ). + output->display( input = str_o3 name = `str_o3` ). + output->display( input = str_o1 name = `str_o1` ). + +********************************************************************** + + output->next_section( `Searching for Substrings in Strings` ). + output->display( `17) Substring Search: Simple Search Using Comparison Operators` ). + + 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`. + output->display( |CS: The string contains the substring. | + && |The offset is { sy-fdpos }.| ). + ELSE. + output->display( |CS: The string does not contain the substring. | + && |The length of the string is { sy-fdpos }.| ). + ENDIF. + + "NS (contains no string) + IF str_p1 NS `abc`. + output->display( |NS: The string does not contain the substring. | + && |The length of the string is { sy-fdpos }.| ). + ELSE. + output->display( |NS: The string contains the substring. | + && |The offset is { sy-fdpos }.| ). + ENDIF. + +********************************************************************** + + output->next_section( `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. + output->display( `'se' found in the string` ). + ELSE. + output->display( `'se' not found in the string` ). + ENDIF. + + "Addition SUBSTRING is optional + FIND SUBSTRING `hi` IN str_qa. + + IF sy-subrc = 0. + output->display( `'hi' Found in the string` ). + ELSE. + output->display( `'hi' not found in the string` ). + ENDIF. + + "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. + + output->display( input = cnt_q1 name = `cnt_q1` ). + output->display( input = off_q2 name = `off_q2` ). + output->display( input = cnt_q3 name = `cnt_q3` ). + output->display( input = off_q4 name = `off_q4` ). + output->display( input = cnt_q5 name = `cnt_q5` ). + output->display( input = off_q6 name = `off_q6` ). + output->display( input = cnt_q7 name = `cnt_q7` ). + output->display( input = off_q8 name = `off_q8` ). + output->display( input = cnt_q9 name = `cnt_q9` ). + output->display( input = off_q10 name = `off_q10` ). + output->display( input = cnt_q11 name = `cnt_q11` ). + output->display( input = off_q12 name = `off_q12` ). + output->display( input = off_q13 name = `off_q13` ). + output->display( input = len_q14 name = `len_q14` ). + output->display( input = res_q15 name = `res_q15` ). + output->display( input = res_q16 name = `res_q16` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = res_r1 name = `res_r1` ). + output->display( input = res_r2 name = `res_r2` ). + output->display( input = line_r3 name = `line_r3` ). + output->display( input = off_r4 name = `off_r4` ). + output->display( input = len_r5 name = `len_r5` ). + +********************************************************************** + + output->next_section( `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 ). + + output->display( input = res_s1 name = `res_s1` ). + output->display( input = res_s2 name = `res_s2` ). + output->display( input = res_s3 name = `res_s3` ). + output->display( input = res_s4 name = `res_s4` ). + output->display( input = res_s5 name = `res_s5` ). + output->display( input = res_s6 name = `res_s6` ). + output->display( input = res_s7 name = `res_s7` ). + output->display( input = res_s8 name = `res_s8` ). + output->display( input = res_s9 name = `res_s9` ). + output->display( input = res_s10 name = `res_s10` ). + output->display( input = res_s11 name = `res_s11` ). + output->display( input = res_s12 name = `res_s12` ). + + "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. + output->display( `The exception cx_sy_range_out_of_bounds was raised.` ). + ENDTRY. + +*********************************************************************** + + output->next_section( `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 `##`. + + output->display( input = str_t1 name = `str_t1` ). + DATA(str_t2) = str_t. + + "Addition SUBSTRING is optional; same effect as the statement above + REPLACE SUBSTRING `ab` IN str_t2 WITH `##`. + + output->display( input = str_t2 name = `str_t2` ). + 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 `##`. + + output->display( input = str_t3 name = `str_t3` ). + 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 `##`. + + output->display( input = str_t4 name = `str_t4` ). + 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. + + output->display( input = str_t5 name = `str_t5` ). + DATA(str_t6) = str_t. + + "REPLACEMENT COUNT addition + REPLACE ALL OCCURRENCES OF `ab` + IN str_t6 WITH `##` + REPLACEMENT COUNT DATA(cnt_t7) + IGNORING CASE. + + output->display( input = str_t6 name = `str_t6` ). + output->display( input = cnt_t7 name = `cnt_t7` ). + 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. + + output->display( input = str_t8 name = `str_t8` ). + output->display( input = cnt_t9 name = `cnt_t9` ). + output->display( input = off_t10 name = `off_t10` ). + output->display( input = len_t11 name = `len_t11` ). + 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. + + output->display( input = str_t12 name = `str_t12` ). + output->display( input = cnt_t13 name = `cnt_t13` ). + output->display( input = off_t14 name = `off_t14` ). + output->display( input = len_t15 name = `len_t15` ). + 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. + + output->display( input = str_t16 name = `str_t16` ). + output->display( input = tab_t17 name = `tab_t17` ). + 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. + + output->display( input = str_t18 name = `str_t18` ). + output->display( input = struc_t19 name = `struc_t19` ). + +*********************************************************************** + + output->next_section( `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 `#`. + + output->display( input = str_u1 name = `str_u1` ). + DATA(str_u2) = str_u. + + "Only OFFSET specified, LENGTH: up to the end of the string + REPLACE SECTION OFFSET 5 OF str_u2 WITH `#`. + + output->display( input = str_u2 name = `str_u2` ). + DATA(str_u3) = str_u. + + "Only LENGTH specified, OFFSET: starting from the leftmost position + REPLACE SECTION LENGTH 6 OF str_u3 WITH `#`. + + output->display( input = str_u3 name = `str_u3` ). + +*********************************************************************** + + output->next_section( `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. + + output->display( input = tab_v1 name = `tab_v1` ). + output->display( input = res_v2 name = `res_v2` ). + 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. + + output->display( input = tab_v3 name = `tab_v3` ). + output->display( input = res_v4 name = `res_v4` ). + 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. + + output->display( input = tab_v5 name = `tab_v5` ). + 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. + + output->display( input = tab_v6 name = `tab_v6` ). + +*********************************************************************** + + output->next_section( `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 ) ). + + output->display( input = str_w_1 name = `str_w_1` ). + output->display( input = str_w_2 name = `str_w_2` ). + output->display( input = str_w_3 name = `str_w_3` ). + output->display( input = str_w_4 name = `str_w_4` ). + output->display( input = str_w_5 name = `str_w_5` ). + output->display( input = str_w_6 name = `str_w_6` ). + output->display( input = str_w_7 name = `str_w_7` ). + output->display( input = str_w_8 name = `str_w_8` ). + output->display( input = str_w_9 name = `str_w_9` ). + output->display( input = str_w_10 name = `str_w_10` ). + +*********************************************************************** + + output->next_section( `Pattern-Based Searching and Replacing in Strings` ). + output->display( `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#_*`. + output->display( |CP: The string covers the pattern. | + && |The offset is { sy-fdpos }.| ). + ELSE. + output->display( |CP: The string does not cover the pattern. | + && |The length of the string is { sy-fdpos }.| ). + ENDIF. + + "NP (does not conform to pattern) + IF str_x NP `i+`. + output->display( |NP: The string does not cover the pattern. | + && |The length of the string is { sy-fdpos }.| ). + ELSE. + output->display( |NP: The string covers the pattern. | + && |The offset is { sy-fdpos }.| ). + ENDIF. + +*********************************************************************** + + output->next_section( `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. + + output->display( input = off_y1 name = `off_y1` ). + output->display( input = cnt_y2 name = `cnt_y2` ). + output->display( input = cnt_y3 name = `cnt_y3` ). + output->display( input = str_y4 name = `str_y4` ). + output->display( input = str_y5 name = `str_y5` ). + output->display( input = str_y6 name = `str_y6` ). + output->display( input = subm_y7 name = `subm_y7` ). + output->display( input = subm_y8 name = `subm_y8` ). + output->display( input = cnt_y9 name = `cnt_y9` ). + output->display( input = tab_y10 name = `tab_y10` ). + output->display( input = line_y13 name = `line_y13` ). + output->display( input = off_y14 name = `off_y14` ). + output->display( input = len_y15 name = `len_y15` ). + output->display( input = res_y16 name = `res_y16` ). + output->display( input = tab_y17 name = `tab_y17` ). + +*********************************************************************** + + output->next_section( `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 . + + output->display( input = |Original str_z: { str_z }| ). + output->display( input = str_z1 name = `str_z1` ). + output->display( input = str_z2 name = `str_z2` ). + output->display( input = str_z3 name = `str_z3` ). + output->display( input = str_z4 name = `str_z4` ). + output->display( input = str_z5 name = `str_z5` ). + output->display( input = str_z6 name = `str_z6` ). + output->display( input = str_z7 name = `str_z7` ). + output->display( input = str_z8 name = `str_z8` ). + output->display( input = str_z9 name = `str_z9` ). + output->display( input = str_z10 name = `str_z10` ). + output->display( input = str_z11 name = `str_z11` ). + output->display( input = str_z12 name = `str_z12` ). + output->display( input = str_z3 name = `str_z13` ). + output->display( input = |Original str_zb: { str_zb }| ). + output->display( input = str_z15 name = `str_z15` ). + output->display( input = str_z16 name = `str_z16` ). + output->display( input = str_z17 name = `str_z17` ). + output->display( input = str_z18 name = `str_z18` ). + output->display( input = str_z19 name = `str_z19` ). + output->display( input = str_z20 name = `str_z20` ). + output->display( input = str_z21 name = `str_z21` ). + output->display( input = |Original str_zc: { str_zc }| ). + output->display( input = str_z23 name = `str_z23` ). + output->display( input = str_z24 name = `str_z24` ). + output->display( input = str_z25 name = `str_z25` ). + output->display( input = str_zc name = `str_zc` ). + output->display( input = itab_z name = `itab_z` ). + output->display( input = |Number of replacements in itab (cnt_z26): { cnt_z26 }| ). + +*********************************************************************** + + output->next_section( `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( ). + + output->display( input = result1 name = `result1` ). + + "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( ). + + output->display( input = result2 name = `result2` ). + + "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. + + output->display( input = str_tab_reg_find name = `str_tab_reg_find` ). + + ENDMETHOD. +ENDCLASS. 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..b95b611 --- /dev/null +++ b/src/zcl_demo_abap_structures.clas.abap @@ -0,0 +1,1168 @@ +*********************************************************************** +* +* ABAP cheat sheet: Structures +* +* -------------------------- PURPOSE ---------------------------------- +* - Example to demonstrate various syntactical 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_flight_tables=>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. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: Structures` ). + +********************************************************************** + + output->display( `1) Creating structures and structured types` ). + + "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 ). + + output->display( `No output for this section. See the code.` ). + +********************************************************************** + + output->next_section( `Variants of structures` ). + output->display( `2) Flat structure with default values` ). + + "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. + + output->display( input = ls_flat name = `ls_flat` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = ls_nested_address name = `ls_nested_address` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = ls_flat_address name = `ls_flat_address` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = ls_flights name = `ls_flights` ). + +********************************************************************** + + output->next_section( `Accessing and populating structures` ). + output->display( `6) Populating structure components` && + ` using the component selector` ). + + gs_struc-num1 = 1. + gs_struc-num2 = 2. + gs_struc-char1 = 'aaa'. + gs_struc-char2 = 'bbb'. + gs_struc-pnum = '333.33'. + + output->display( input = gs_struc name = `gs_struc` ). + +********************************************************************** + + output->next_section( `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' ) ) ). + + output->display( input = gs_struc name = `gs_struc` ). + output->display( input = ls_nested_address name = `ls_nested_address` ). + output->display( input = ls_flights name = `ls_flights` ). + +********************************************************************** + + output->next_section( `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' ). + + output->display( input = ls_copy name = `ls_copy` ). + +********************************************************************** + + output->next_section( `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'. + + output->display( input = lv_copy name = `lv_copy` ). + output->display( input = ls_nested_address-name-first_name name = `ls_nested_address-name-first_name` ). + output->display( input = ls_flights-lt_flights[ 1 ]-cityto name = `ls_flights-lt_flights[ 1 ]-cityto` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = ref_comp1 name = `ref_comp1` ). + output->display( input = ref_comp2 name = `ref_comp2` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = lv_num1 name = `lv_num1` ). + output->display( input = lv_num2 name = `lv_num2` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = gs_struc_2 name = `gs_struc_2` ). + +********************************************************************** + + output->next_section( `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. + + output->display( `Original content of structures:` ). + output->display( input = gs_struc name = `gs_struc` ). + output->display( input = gs_struc_diff name = `gs_struc_diff` ). + + "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 ). + + output->display( `Results of statements:` ). + output->display( input = gs_struc_diff name = `gs_struc_diff` ). + output->display( input = gs_struc_diff2 name = `gs_struc_diff2` ). + output->display( input = gs_struc_diff3 name = `gs_struc_diff3` ). + output->display( input = gs_struc_diff4 name = `gs_struc_diff4` ). + output->display( input = gs_struc_diff5 name = `gs_struc_diff5` ). + +********************************************************************** + + output->next_section( `14) Copying content of a deep ` && + `structure to another` ). + output->display( 'Original content of deep structures:' ). + + "Note: The example purposely uses non-fitting components + "to emphasize conversion and assignment rules. + + output->display( input = gs_deep1 name = `gs_deep1` ). + output->display( input = gs_deep2 name = `gs_deep2` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + output->next_section( `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 ). + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + output->next_section( `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 ). + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + output->next_section( `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 ). + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + output->next_section( `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 ). + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + output->next_section( `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 ). + + output->display( input = gs_deep2 name = `gs_deep2` ). + + fill_deep_structures( ). + +********************************************************************** + + output->next_section( `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 ). + + output->display( input = gs_deep2 name = `gs_deep2` ). + +********************************************************************** + + output->next_section( `25) Clearing individual components of a ` && + `structure and the complete structure` ). + + "Clearing individual component + CLEAR gs_struc-char1. + + output->display( input = gs_struc name = `gs_struc` ). + + "Clearing the whole structure + CLEAR gs_struc. + + output->display( input = gs_struc name = `gs_struc` ). + +********************************************************************** + + output->next_section( `Processing structures` ). + output->display( `Reading a row from a database table into a ` && + `structure ...` ). + output->display( `26) ... that has a compatible type` ). + + "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). + + output->display( input = ls_flsch1 name = `ls_flsch1` ). + output->display( input = ls_flsch2 name = `ls_flsch2` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = ls_fli_diff name = `ls_fli_diff` ). + +********************************************************************** + + output->next_section( `Reading a line from an internal table into a structure ...` ). + output->display( `28) ... using a SELECT statement` ). + + "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). + + output->display( input = ls_select_itab name = `ls_select_itab` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = ls_read_table name = `ls_read_table` ). + output->display( input = name = `` ). + output->display( input = dref->* name = `dref->*` ). + +********************************************************************** + + output->next_section( `30) ... using a table expression` ). + "The line number, that is, the index, is specified in square + "brackets. + + DATA(ls_table_exp) = itab[ 3 ]. + + output->display( input = ls_table_exp name = `ls_table_exp` ). + +********************************************************************** + + output->next_section( `Sequentially reading ...` ). + output->display( `31) ... a row from a database table into a structure` ). + + "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. + + output->display( input = itab name = `itab` ). + +********************************************************************** + + output->next_section( `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. + + output->display( input = itab name = `itab` ). + +********************************************************************** + + output->next_section( `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( ). + output->display( input = gt_tab name = `gt_tab` ). + +********************************************************************** + + output->next_section( `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( ). + output->display( input = gt_tab name = `gt_tab` ). + +********************************************************************** + + output->next_section( `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( ). + output->display( input = gt_tab name = `gt_tab` ). + +********************************************************************** + + output->next_section( `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( ). + output->display( input = gt_tab name = `gt_tab` ). + +********************************************************************** + + output->next_section( `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 ). + + output->display( input = gt_tab name = `gt_tab` ). + +********************************************************************** + + output->next_section( `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`. + + output->display( input = 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..543fb10 --- /dev/null +++ b/src/zcl_demo_abap_unit_test.clas.abap @@ -0,0 +1,686 @@ +*********************************************************************** +* +* 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_flight_tables=>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. + + DATA(output) = NEW zcl_demo_abap_display( out ). + + output->display( `ABAP Cheat Sheet Example: ABAP Unit Tests` ). + + output->display( `************************************************************************` ). + output->display( `---> Choose Ctrl/Cmd + Shift + F10 to launch all tests in the class <---` ). + output->display( `************************************************************************` ). + + output->display( `1) get_sum Method` ). + "This method demonstrates the use of the setup and teardown methods in the test class. + + DATA(sum) = get_sum( key = 1 char = 'aaa' ). + + output->display( input = sum name = `sum` ). + +********************************************************************** + + output->next_section( `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(). + + get_common_div_and_gcd( EXPORTING a = -num1 + b = -num2 + IMPORTING common_divisors = DATA(common_divs) gcd = DATA(gcd) ). + + output->display( |Common divisors of { -num1 } and { -num2 }| ). + + output->display( input = common_divs name = `common_divs` ). + + output->display( |Greatest common divisor of { -num1 } and { -num2 }: { gcd } | ). + + ENDLOOP. + + +********************************************************************** + + output->next_section( `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( ). + + output->display( |The digit sum of { } is { digit_sum }.| ). + + ENDLOOP. + +********************************************************************** + + output->next_section( `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 ). + + output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate }%.| ). + + ENDLOOP. + output->next_section( `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) ). + + output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate_test_seam }%.| ). + + output->display( |num1: { num1 }| ). + output->display( |num2: { num2 }| ). + + ENDLOOP. + +********************************************************************** + + output->next_section( `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 ). + + output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate_local_itf }%.| ). + + ENDLOOP. + +********************************************************************** + + output->next_section( `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 ). + + output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate_global_itf }%.| ). + + ENDLOOP. + +********************************************************************** + + output->next_section( `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 ). + + output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate_setter_inj }%.| ). + + ENDLOOP. + +********************************************************************** + + output->next_section( `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 ). + + output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate_param_inj }%.| ). + + 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/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_cds_ve_agg_exp.ddls.asddls b/src/zdemo_abap_cds_ve_agg_exp.ddls.asddls new file mode 100644 index 0000000..7d4d08a --- /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_abap_demo_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..b09ce4d --- /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_abap_demo_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..74596a5 --- /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_abap_demo_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..486afd1 --- /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_abap_demo_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..9d0644c --- /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_abap_demo_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, + + // In newer ABAP releases, 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_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_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..be85c2c --- /dev/null +++ b/src/zdemo_abap_rap_draft_m.bdef.asbdef @@ -0,0 +1,31 @@ +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; + 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; + 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..5482f5c --- /dev/null +++ b/src/zdemo_abap_rap_draft_m.bdef.xml @@ -0,0 +1,56 @@ + + + + + + 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 + + + /sap/bc/adt/repository/informationsystem/abaplanguageversions?uri=%2Fsap%2Fbc%2Fadt%2Fbo%2Fbehaviordefinitions%2Fzdemo_abap_rap_draft_m + http://www.sap.com/adt/relations/informationsystem/abaplanguageversions + application/vnd.sap.adt.nameditems.v1+xml + Allowed ABAP language 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) + + + ./zdemo_abap_rap_draft_m/objectstructure + http://www.sap.com/adt/relations/objectstructure + Object Structure + + + /sap/bc/adt/vit/wb/object_type/bdefbdo/object_name/ZDEMO_ABAP_RAP_DRAFT_M + self + application/vnd.sap.sapgui + Representation in SAP GUI + + + EN + X + ./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..af3a9a1 --- /dev/null +++ b/src/zdemo_abap_rap_draft_m.ddls.asddls @@ -0,0 +1,73 @@ + @AccessControl.authorizationCheck: #NOT_REQUIRED + +@ObjectModel.semanticKey: ['id'] +@UI: { headerInfo: { title: { value: 'id' }, + typeName: 'Calculation', typeNamePlural: 'Calculations' } } +define root view entity ZDEMO_ABAP_RAP_DRAFT_M + as select from zdemo_abap_tabca + { + //FACET SECTION + @UI.facet: [ + + // Header Facet (Object Page): + + { id: 'HeaderFacet', + purpose: #HEADER, + type: #FIELDGROUP_REFERENCE, + //label: 'Calculation ID', + targetQualifier: 'Fieldgroup:HeaderItems', + position: 10 }, + + // Body Facets (Object Page) + + { id: 'Calculation', + type: #IDENTIFICATION_REFERENCE, + label: 'Calculation', + position: 10 } + + ] + + // Element List + @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..9421fd8 --- /dev/null +++ b/src/zdemo_abap_rap_ro_m.bdef.xml @@ -0,0 +1,56 @@ + + + + + + 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 + + + /sap/bc/adt/repository/informationsystem/abaplanguageversions?uri=%2Fsap%2Fbc%2Fadt%2Fbo%2Fbehaviordefinitions%2Fzdemo_abap_rap_ro_m + http://www.sap.com/adt/relations/informationsystem/abaplanguageversions + application/vnd.sap.adt.nameditems.v1+xml + Allowed ABAP language 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) + + + ./zdemo_abap_rap_ro_m/objectstructure + http://www.sap.com/adt/relations/objectstructure + Object Structure + + + /sap/bc/adt/vit/wb/object_type/bdefbdo/object_name/ZDEMO_ABAP_RAP_RO_M + self + application/vnd.sap.sapgui + Representation in SAP GUI + + + EN + X + ./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_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..f803b22 --- /dev/null +++ b/src/zdemo_abap_rap_ro_u.bdef.xml @@ -0,0 +1,56 @@ + + + + + + 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 + + + /sap/bc/adt/repository/informationsystem/abaplanguageversions?uri=%2Fsap%2Fbc%2Fadt%2Fbo%2Fbehaviordefinitions%2Fzdemo_abap_rap_ro_u + http://www.sap.com/adt/relations/informationsystem/abaplanguageversions + application/vnd.sap.adt.nameditems.v1+xml + Allowed ABAP language 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) + + + ./zdemo_abap_rap_ro_u/objectstructure + http://www.sap.com/adt/relations/objectstructure + Object Structure + + + /sap/bc/adt/vit/wb/object_type/bdefbdo/object_name/ZDEMO_ABAP_RAP_RO_U + self + application/vnd.sap.sapgui + Representation in SAP GUI + + + EN + X + ./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_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..a5c88b7 --- /dev/null +++ b/src/zdemo_abap_table_function.ddls.asddls @@ -0,0 +1,13 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +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.dec( 10, 2 ); + avg_distance : abap.dec( 10, 2 ); + } + 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 + + + +