REPORT zabappm_standalone LINE-SIZE 100. * See http://www.abappm.com ******************************************************************************** * The MIT License (MIT) * * Copyright 2024 apm.to Inc. * Copyright 2014 abapGit Contributors [ where noted in code ] * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. ******************************************************************************** ****** INTERFACES DEFERRED ****** INTERFACE /apmg/if_apm_ajson DEFERRED. INTERFACE /apmg/if_apm_ajson_filter DEFERRED. INTERFACE /apmg/if_apm_ajson_iterator DEFERRED. INTERFACE /apmg/if_apm_ajson_mapping DEFERRED. INTERFACE /apmg/if_apm_ajson_ref_initial DEFERRED. INTERFACE /apmg/if_apm_ajson_refs_init DEFERRED. INTERFACE /apmg/if_apm_ajson_types DEFERRED. INTERFACE /apmg/if_apm_arborist DEFERRED. INTERFACE /apmg/if_apm_constants DEFERRED. INTERFACE /apmg/if_apm_file_importer DEFERRED. INTERFACE /apmg/if_apm_frontend_services DEFERRED. INTERFACE /apmg/if_apm_gui_asset_manager DEFERRED. INTERFACE /apmg/if_apm_gui_error_handler DEFERRED. INTERFACE /apmg/if_apm_gui_event DEFERRED. INTERFACE /apmg/if_apm_gui_event_handler DEFERRED. INTERFACE /apmg/if_apm_gui_hotkey_ctl DEFERRED. INTERFACE /apmg/if_apm_gui_hotkeys DEFERRED. INTERFACE /apmg/if_apm_gui_html_processo DEFERRED. INTERFACE /apmg/if_apm_gui_menu_provider DEFERRED. INTERFACE /apmg/if_apm_gui_modal DEFERRED. INTERFACE /apmg/if_apm_gui_page_title DEFERRED. INTERFACE /apmg/if_apm_gui_render_item DEFERRED. INTERFACE /apmg/if_apm_gui_renderable DEFERRED. INTERFACE /apmg/if_apm_gui_router DEFERRED. INTERFACE /apmg/if_apm_gui_services DEFERRED. INTERFACE /apmg/if_apm_html DEFERRED. INTERFACE /apmg/if_apm_html_form DEFERRED. INTERFACE /apmg/if_apm_html_table DEFERRED. INTERFACE /apmg/if_apm_html_viewer DEFERRED. INTERFACE /apmg/if_apm_http_agent DEFERRED. INTERFACE /apmg/if_apm_http_response DEFERRED. INTERFACE /apmg/if_apm_importer DEFERRED. INTERFACE /apmg/if_apm_object DEFERRED. INTERFACE /apmg/if_apm_package_json DEFERRED. INTERFACE /apmg/if_apm_pacote DEFERRED. INTERFACE /apmg/if_apm_persist_apm DEFERRED. INTERFACE /apmg/if_apm_popups DEFERRED. INTERFACE /apmg/if_apm_progress_bar DEFERRED. INTERFACE /apmg/if_apm_readme DEFERRED. INTERFACE /apmg/if_apm_semver_constants DEFERRED. INTERFACE /apmg/if_apm_semver_options DEFERRED. INTERFACE /apmg/if_apm_settings DEFERRED. INTERFACE /apmg/if_apm_types DEFERRED. INTERFACE /apmg/if_apm_version DEFERRED. INTERFACE zif_abapgit_aff_intf_v1 DEFERRED. INTERFACE zif_abapgit_aff_oo_types_v1 DEFERRED. INTERFACE zif_abapgit_aff_registry DEFERRED. INTERFACE zif_abapgit_aff_type_mapping DEFERRED. INTERFACE zif_abapgit_aff_types_v1 DEFERRED. INTERFACE zif_abapgit_apack_definitions DEFERRED. INTERFACE zif_abapgit_comparator DEFERRED. INTERFACE zif_abapgit_cts_api DEFERRED. INTERFACE zif_abapgit_data_config DEFERRED. INTERFACE zif_abapgit_data_persistence DEFERRED. INTERFACE zif_abapgit_data_supporter DEFERRED. INTERFACE zif_abapgit_default_transport DEFERRED. INTERFACE zif_abapgit_definitions DEFERRED. INTERFACE zif_abapgit_dot_abapgit DEFERRED. INTERFACE zif_abapgit_ecatt DEFERRED. INTERFACE zif_abapgit_ecatt_download DEFERRED. INTERFACE zif_abapgit_ecatt_upload DEFERRED. INTERFACE zif_abapgit_environment DEFERRED. INTERFACE zif_abapgit_exit DEFERRED. INTERFACE zif_abapgit_field_rules DEFERRED. INTERFACE zif_abapgit_function_module DEFERRED. INTERFACE zif_abapgit_git_definitions DEFERRED. INTERFACE zif_abapgit_gui_jumper DEFERRED. INTERFACE zif_abapgit_i18n_file DEFERRED. INTERFACE zif_abapgit_lang_definitions DEFERRED. INTERFACE zif_abapgit_log DEFERRED. INTERFACE zif_abapgit_longtexts DEFERRED. INTERFACE zif_abapgit_lxe_texts DEFERRED. INTERFACE zif_abapgit_object DEFERRED. INTERFACE zif_abapgit_object_enho DEFERRED. INTERFACE zif_abapgit_object_enhs DEFERRED. INTERFACE zif_abapgit_object_tabl DEFERRED. INTERFACE zif_abapgit_objects DEFERRED. INTERFACE zif_abapgit_oo_object_fnc DEFERRED. INTERFACE zif_abapgit_persistence DEFERRED. INTERFACE zif_abapgit_progress DEFERRED. INTERFACE zif_abapgit_sap_namespace DEFERRED. INTERFACE zif_abapgit_sap_package DEFERRED. INTERFACE zif_abapgit_sap_report DEFERRED. INTERFACE zif_abapgit_status_calc DEFERRED. INTERFACE zif_abapgit_tadir DEFERRED. INTERFACE zif_abapgit_user_record DEFERRED. INTERFACE zif_abapgit_version DEFERRED. INTERFACE zif_abapgit_xml_input DEFERRED. INTERFACE zif_abapgit_xml_output DEFERRED. ****** EXCEPTIONS ****** CLASS lcl_error_longtext DEFINITION. PUBLIC SECTION. CLASS-METHODS remove_newlines IMPORTING longtext TYPE string RETURNING VALUE(result) TYPE string. CLASS-METHODS to_string IMPORTING lines TYPE tline_tab RETURNING VALUE(result) TYPE string. PRIVATE SECTION. CLASS-METHODS remove_empty_section IMPORTING !tabix_from TYPE i !tabix_to TYPE i CHANGING !itf TYPE tline_tab. CLASS-METHODS replace_section_head_with_text CHANGING !itf TYPE tline. ENDCLASS. CLASS lcl_error_longtext IMPLEMENTATION. METHOD remove_newlines. result = longtext. REPLACE ALL OCCURRENCES OF ` ` && cl_abap_char_utilities=>cr_lf IN result WITH ` `. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf IN result WITH ` `. REPLACE ALL OCCURRENCES OF ` ` && cl_abap_char_utilities=>newline IN result WITH ` `. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>newline IN result WITH ` `. ENDMETHOD. METHOD to_string. CONSTANTS c_format_section TYPE string VALUE 'U1'. DATA: stream TYPE STANDARD TABLE OF tdline WITH KEY table_line, stream_lines TYPE string_table, itf TYPE tline_tab. itf = lines. " We replace the U1 format because that preserves the section header of longtexts LOOP AT itf ASSIGNING FIELD-SYMBOL(
) WHERE tdformat = c_format_section. DATA(has_content) = VALUE abap_bool( ). DATA(tabix_from) = sy-tabix. DATA(tabix_to) = 0. LOOP AT itf ASSIGNING FIELD-SYMBOL() FROM sy-tabix + 1. IF -tdformat = c_format_section. tabix_to = sy-tabix. EXIT. ELSEIF -tdline IS NOT INITIAL. has_content = abap_true. ENDIF. ENDLOOP. IF has_content = abap_false. remove_empty_section( EXPORTING tabix_from = tabix_from tabix_to = tabix_to CHANGING itf = itf ). CONTINUE. ENDIF. replace_section_head_with_text( CHANGING itf =
). ENDLOOP. CALL FUNCTION 'CONVERT_ITF_TO_STREAM_TEXT' EXPORTING lf = 'X' IMPORTING stream_lines = stream_lines TABLES itf_text = itf text_stream = stream. result = concat_lines_of( table = stream_lines sep = cl_abap_char_utilities=>newline ). ENDMETHOD. METHOD remove_empty_section. IF tabix_to BETWEEN tabix_from AND lines( itf ). DELETE itf FROM tabix_from TO tabix_to. ELSE. DELETE itf INDEX tabix_from. ENDIF. ENDMETHOD. METHOD replace_section_head_with_text. CONSTANTS: BEGIN OF c_section_text, cause TYPE string VALUE `Cause`, system_response TYPE string VALUE `System response`, what_to_do TYPE string VALUE `Procedure`, sys_admin TYPE string VALUE `System administration`, END OF c_section_text, BEGIN OF c_section_token, cause TYPE string VALUE `&CAUSE&`, system_response TYPE string VALUE `&SYSTEM_RESPONSE&`, what_to_do TYPE string VALUE `&WHAT_TO_DO&`, sys_admin TYPE string VALUE `&SYS_ADMIN&`, END OF c_section_token. CASE itf-tdline. WHEN c_section_token-cause. itf-tdline = c_section_text-cause. WHEN c_section_token-system_response. itf-tdline = c_section_text-system_response. WHEN c_section_token-what_to_do. itf-tdline = c_section_text-what_to_do. WHEN c_section_token-sys_admin. itf-tdline = c_section_text-sys_admin. ENDCASE. ENDMETHOD. ENDCLASS. CLASS /apmg/cx_apm_error DEFINITION INHERITING FROM cx_static_check CREATE PUBLIC. ************************************************************************ * General Error * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CONSTANTS c_version TYPE string VALUE '1.0.0' ##NEEDED. INTERFACES: if_t100_dyn_msg, if_t100_message. TYPES: BEGIN OF ty_scr_info, program TYPE progname, include TYPE progname, line TYPE i, END OF ty_scr_info. "! Black Hole "! Can be used for MESSAGE ... INTO null CLASS-DATA null TYPE string ##NEEDED. DATA longtext TYPE string READ-ONLY. DATA callstack TYPE abap_callstack READ-ONLY. DATA src_info TYPE ty_scr_info READ-ONLY. METHODS constructor IMPORTING !textid LIKE if_t100_message=>t100key OPTIONAL !previous LIKE previous OPTIONAL !msgv1 TYPE symsgv OPTIONAL !msgv2 TYPE symsgv OPTIONAL !msgv3 TYPE symsgv OPTIONAL !msgv4 TYPE symsgv OPTIONAL !longtext TYPE csequence OPTIONAL. "! Raise exception with text "! @parameter text | Text "! @parameter previous | Previous exception "! @parameter longtext | Longtext "! @raising /apmg/cx_apm_error | Exception CLASS-METHODS raise IMPORTING !text TYPE clike !previous TYPE REF TO cx_root OPTIONAL !longtext TYPE csequence OPTIONAL RAISING /apmg/cx_apm_error. "! Raise exception with T100 message "!

"! Will default to sy-msg* variables. These need to be set right before calling this method. "!

"! @parameter msgid | Message ID "! @parameter msgno | Message number "! @parameter msgv1 | Message variable 1 "! @parameter msgv2 | Message variable 2 "! @parameter msgv3 | Message variable 3 "! @parameter msgv4 | Message variable 4 "! @parameter previous | Previous exception "! @parameter longtext | Longtext "! @raising /apmg/cx_apm_error | Exception CLASS-METHODS raise_t100 IMPORTING msgid TYPE symsgid DEFAULT sy-msgid msgno TYPE symsgno DEFAULT sy-msgno msgv1 TYPE symsgv DEFAULT sy-msgv1 msgv2 TYPE symsgv DEFAULT sy-msgv2 msgv3 TYPE symsgv DEFAULT sy-msgv3 msgv4 TYPE symsgv DEFAULT sy-msgv4 !previous TYPE REF TO cx_root OPTIONAL !longtext TYPE csequence OPTIONAL RAISING /apmg/cx_apm_error. "! Raise with text from previous exception "! @parameter previous | Previous exception "! @parameter longtext | Longtext "! @raising /apmg/cx_apm_error | Exception CLASS-METHODS raise_with_text IMPORTING !previous TYPE REF TO cx_root !longtext TYPE csequence OPTIONAL RAISING /apmg/cx_apm_error. METHODS get_source_position REDEFINITION. METHODS if_message~get_longtext REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_generic_error_msg TYPE string VALUE `An error occured`. METHODS save_callstack. METHODS get_t100_longtext RETURNING VALUE(result) TYPE tline_tab. ENDCLASS. CLASS /apmg/cx_apm_error IMPLEMENTATION. METHOD constructor ##ADT_SUPPRESS_GENERATION. super->constructor( previous = previous ). if_t100_dyn_msg~msgv1 = msgv1. if_t100_dyn_msg~msgv2 = msgv2. if_t100_dyn_msg~msgv3 = msgv3. if_t100_dyn_msg~msgv4 = msgv4. CLEAR me->textid. IF textid IS INITIAL. if_t100_message~t100key = if_t100_message=>default_textid. ELSE. if_t100_message~t100key = textid. ENDIF. me->longtext = longtext. save_callstack( ). " Save for debugger get_source_position( IMPORTING program_name = src_info-program include_name = src_info-include source_line = src_info-line ). ENDMETHOD. METHOD get_source_position. READ TABLE callstack ASSIGNING FIELD-SYMBOL() INDEX 1. IF sy-subrc = 0. program_name = -mainprogram. include_name = -include. source_line = -line. ELSE. super->get_source_position( IMPORTING program_name = program_name include_name = include_name source_line = source_line ). ENDIF. ENDMETHOD. METHOD get_t100_longtext. DATA(docu_key) = CONV doku_obj( if_t100_message~t100key-msgid && if_t100_message~t100key-msgno ). CALL FUNCTION 'DOCU_GET' EXPORTING id = 'NA' langu = sy-langu object = docu_key typ = 'E' TABLES line = result EXCEPTIONS OTHERS = 1. IF sy-subrc = 0. ASSIGN me->(if_t100_message~t100key-attr1) TO FIELD-SYMBOL(). IF sy-subrc = 0. REPLACE ALL OCCURRENCES OF '&V1&' IN TABLE result WITH . ENDIF. ASSIGN me->(if_t100_message~t100key-attr2) TO . IF sy-subrc = 0. REPLACE ALL OCCURRENCES OF '&V2&' IN TABLE result WITH . ENDIF. ASSIGN me->(if_t100_message~t100key-attr3) TO . IF sy-subrc = 0. REPLACE ALL OCCURRENCES OF '&V3&' IN TABLE result WITH . ENDIF. ASSIGN me->(if_t100_message~t100key-attr4) TO . IF sy-subrc = 0. REPLACE ALL OCCURRENCES OF '&V4&' IN TABLE result WITH . ENDIF. ENDIF. ENDMETHOD. METHOD if_message~get_longtext. IF longtext IS NOT INITIAL. result = longtext. IF preserve_newlines = abap_false. result = lcl_error_longtext=>remove_newlines( result ). ENDIF. ELSEIF if_t100_message~t100key IS NOT INITIAL. result = lcl_error_longtext=>to_string( get_t100_longtext( ) ). IF preserve_newlines = abap_false. result = lcl_error_longtext=>remove_newlines( result ). ENDIF. ELSE. result = super->get_longtext( preserve_newlines ). ENDIF. ENDMETHOD. METHOD raise. IF text IS INITIAL. cl_message_helper=>set_msg_vars_for_clike( c_generic_error_msg ). ELSE. cl_message_helper=>set_msg_vars_for_clike( text ). ENDIF. raise_t100( previous = previous longtext = longtext ). ENDMETHOD. METHOD raise_t100. IF msgid IS NOT INITIAL. DATA(t100_key) = VALUE scx_t100key( msgid = msgid msgno = msgno attr1 = 'IF_T100_DYN_MSG~MSGV1' attr2 = 'IF_T100_DYN_MSG~MSGV2' attr3 = 'IF_T100_DYN_MSG~MSGV3' attr4 = 'IF_T100_DYN_MSG~MSGV4' ). ENDIF. RAISE EXCEPTION TYPE /apmg/cx_apm_error EXPORTING textid = t100_key msgv1 = msgv1 msgv2 = msgv2 msgv3 = msgv3 msgv4 = msgv4 previous = previous longtext = longtext. ENDMETHOD. METHOD raise_with_text. raise( text = previous->get_text( ) previous = previous longtext = longtext ). ENDMETHOD. METHOD save_callstack. CALL FUNCTION 'SYSTEM_CALLSTACK' IMPORTING callstack = callstack. DATA(main_pattern) = cl_abap_typedescr=>describe_by_object_ref( me )->get_relative_name( ) && '*'. " Remember that the first lines are from this exception class and are " removed so that highest level in the callstack is the position where " the exception is raised. " " For a merged report it's hard to do that, because the exception " isn't visible in the callstack. Therefore we have to check the events. LOOP AT callstack ASSIGNING FIELD-SYMBOL(). IF -mainprogram CP main_pattern " full OR -blockname = `SAVE_CALLSTACK` " merged OR -blockname = `CONSTRUCTOR` " merged OR -blockname CP `RAISE*`. "merged DELETE TABLE callstack FROM . ELSE. EXIT. ENDIF. ENDLOOP. ENDMETHOD. ENDCLASS. CLASS /apmg/cx_apm_error_t100 DEFINITION INHERITING FROM /apmg/cx_apm_error CREATE PUBLIC. ************************************************************************ * Message Error * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. METHODS constructor IMPORTING !msgid TYPE symsgid DEFAULT sy-msgid !msgno TYPE symsgno DEFAULT sy-msgno !msgv1 TYPE symsgv DEFAULT sy-msgv1 !msgv2 TYPE symsgv DEFAULT sy-msgv2 !msgv3 TYPE symsgv DEFAULT sy-msgv3 !msgv4 TYPE symsgv DEFAULT sy-msgv4 !previous TYPE REF TO cx_root OPTIONAL !longtext TYPE csequence OPTIONAL. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cx_apm_error_t100 IMPLEMENTATION. METHOD constructor ##ADT_SUPPRESS_GENERATION. DATA(textid) = VALUE scx_t100key( msgid = msgid msgno = msgno attr1 = 'IF_T100_DYN_MSG~MSGV1' attr2 = 'IF_T100_DYN_MSG~MSGV2' attr3 = 'IF_T100_DYN_MSG~MSGV3' attr4 = 'IF_T100_DYN_MSG~MSGV4' ). super->constructor( textid = textid previous = previous msgv1 = msgv1 msgv2 = msgv2 msgv3 = msgv3 msgv4 = msgv4 longtext = longtext ). ENDMETHOD. ENDCLASS. CLASS /apmg/cx_apm_error_text DEFINITION INHERITING FROM /apmg/cx_apm_error_t100 CREATE PUBLIC. ************************************************************************ * Text Error * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. METHODS constructor IMPORTING !text TYPE clike !previous LIKE previous OPTIONAL !longtext TYPE csequence OPTIONAL. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cx_apm_error_text IMPLEMENTATION. METHOD constructor ##ADT_SUPPRESS_GENERATION. cl_message_helper=>set_msg_vars_for_clike( text ). super->constructor( previous = previous longtext = longtext ). ENDMETHOD. ENDCLASS. CLASS /apmg/cx_apm_error_prev DEFINITION INHERITING FROM /apmg/cx_apm_error_text CREATE PUBLIC. ************************************************************************ * Previous Error * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. METHODS constructor IMPORTING !previous LIKE previous !longtext TYPE csequence OPTIONAL. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cx_apm_error_prev IMPLEMENTATION. METHOD constructor ##ADT_SUPPRESS_GENERATION. super->constructor( text = previous->get_text( ) previous = previous longtext = longtext ). ENDMETHOD. ENDCLASS. CLASS /apmg/cx_apm_cancel DEFINITION INHERITING FROM /apmg/cx_apm_error FINAL CREATE PUBLIC. PUBLIC SECTION. METHODS constructor IMPORTING !textid LIKE if_t100_message=>t100key OPTIONAL !previous LIKE previous OPTIONAL !msgv1 TYPE symsgv OPTIONAL !msgv2 TYPE symsgv OPTIONAL !msgv3 TYPE symsgv OPTIONAL !msgv4 TYPE symsgv OPTIONAL !longtext TYPE csequence OPTIONAL. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cx_apm_cancel IMPLEMENTATION. METHOD constructor ##ADT_SUPPRESS_GENERATION. CALL METHOD super->constructor EXPORTING previous = previous msgv1 = msgv1 msgv2 = msgv2 msgv3 = msgv3 msgv4 = msgv4 longtext = longtext. CLEAR me->textid. IF textid IS INITIAL. if_t100_message~t100key = if_t100_message=>default_textid. ELSE. if_t100_message~t100key = textid. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cx_apm_ajson_error DEFINITION INHERITING FROM cx_static_check FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES if_t100_message . TYPES: ty_rc TYPE c LENGTH 4 . CONSTANTS: BEGIN OF /apmg/cx_apm_ajson_error, msgid TYPE symsgid VALUE '00', msgno TYPE symsgno VALUE '001', attr1 TYPE scx_attrname VALUE 'A1', attr2 TYPE scx_attrname VALUE 'A2', attr3 TYPE scx_attrname VALUE 'A3', attr4 TYPE scx_attrname VALUE 'A4', END OF /apmg/cx_apm_ajson_error . DATA rc TYPE ty_rc READ-ONLY . DATA message TYPE string READ-ONLY . DATA location TYPE string READ-ONLY . DATA a1 TYPE symsgv READ-ONLY . DATA a2 TYPE symsgv READ-ONLY . DATA a3 TYPE symsgv READ-ONLY . DATA a4 TYPE symsgv READ-ONLY . METHODS constructor IMPORTING !textid LIKE if_t100_message=>t100key OPTIONAL !previous LIKE previous OPTIONAL !rc TYPE ty_rc OPTIONAL !message TYPE string OPTIONAL !location TYPE string OPTIONAL !a1 TYPE symsgv OPTIONAL !a2 TYPE symsgv OPTIONAL !a3 TYPE symsgv OPTIONAL !a4 TYPE symsgv OPTIONAL . CLASS-METHODS raise IMPORTING !iv_msg TYPE string !iv_location TYPE string OPTIONAL !is_node TYPE any OPTIONAL RAISING /apmg/cx_apm_ajson_error . METHODS set_location IMPORTING !iv_location TYPE string OPTIONAL !is_node TYPE any OPTIONAL PREFERRED PARAMETER iv_location . PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_message_parts, a1 LIKE a1, a2 LIKE a1, a3 LIKE a1, a4 LIKE a1, END OF ty_message_parts. ENDCLASS. CLASS /apmg/cx_apm_ajson_error IMPLEMENTATION. METHOD constructor. CALL METHOD super->constructor EXPORTING previous = previous. me->rc = rc . me->message = message . me->location = location . me->a1 = a1 . me->a2 = a2 . me->a3 = a3 . me->a4 = a4 . CLEAR me->textid. IF textid IS INITIAL. if_t100_message~t100key = /apmg/cx_apm_ajson_error . ELSE. if_t100_message~t100key = textid. ENDIF. ENDMETHOD. METHOD raise. DATA lx TYPE REF TO /apmg/cx_apm_ajson_error. CREATE OBJECT lx EXPORTING message = iv_msg. lx->set_location( iv_location = iv_location is_node = is_node ). RAISE EXCEPTION lx. ENDMETHOD. METHOD set_location. DATA ls_msg TYPE ty_message_parts. DATA lv_location TYPE string. DATA lv_tmp TYPE string. FIELD-SYMBOLS TYPE string. FIELD-SYMBOLS TYPE string. IF iv_location IS NOT INITIAL. lv_location = iv_location. ELSEIF is_node IS NOT INITIAL. ASSIGN COMPONENT 'PATH' OF STRUCTURE is_node TO . ASSIGN COMPONENT 'NAME' OF STRUCTURE is_node TO . IF IS ASSIGNED AND IS ASSIGNED. lv_location = && . ENDIF. ENDIF. IF lv_location IS NOT INITIAL. lv_tmp = message && | @{ lv_location }|. ELSE. lv_tmp = message. ENDIF. ls_msg = lv_tmp. location = lv_location. a1 = ls_msg-a1. a2 = ls_msg-a2. a3 = ls_msg-a3. a4 = ls_msg-a4. ENDMETHOD. ENDCLASS. "! abapGit general error CLASS zcx_abapgit_exception DEFINITION INHERITING FROM cx_static_check CREATE PUBLIC . PUBLIC SECTION. INTERFACES if_t100_message . TYPES: BEGIN OF ty_scr_info, program TYPE progname, include TYPE progname, line TYPE i, END OF ty_scr_info. CONSTANTS: BEGIN OF c_section_text, cause TYPE string VALUE `Cause`, system_response TYPE string VALUE `System response`, what_to_do TYPE string VALUE `Procedure`, sys_admin TYPE string VALUE `System administration`, END OF c_section_text . CONSTANTS: BEGIN OF c_section_token, cause TYPE string VALUE `&CAUSE&`, system_response TYPE string VALUE `&SYSTEM_RESPONSE&`, what_to_do TYPE string VALUE `&WHAT_TO_DO&`, sys_admin TYPE string VALUE `&SYS_ADMIN&`, END OF c_section_token . CLASS-DATA null TYPE string. DATA msgv1 TYPE symsgv READ-ONLY . DATA msgv2 TYPE symsgv READ-ONLY . DATA msgv3 TYPE symsgv READ-ONLY . DATA msgv4 TYPE symsgv READ-ONLY . DATA mv_longtext TYPE string READ-ONLY. DATA mt_callstack TYPE abap_callstack READ-ONLY. DATA mi_log TYPE REF TO zif_abapgit_log READ-ONLY. DATA ms_src_info TYPE ty_scr_info READ-ONLY. "! Raise exception with text "! @parameter iv_text | Text "! @parameter ix_previous | Previous exception "! @parameter ii_log | Log "! @parameter iv_longtext | Longtext "! @raising zcx_abapgit_exception | Exception CLASS-METHODS raise IMPORTING !iv_text TYPE clike !ix_previous TYPE REF TO cx_root OPTIONAL !ii_log TYPE REF TO zif_abapgit_log OPTIONAL !iv_longtext TYPE csequence OPTIONAL RAISING zcx_abapgit_exception . "! Raise exception with T100 message "!

"! Will default to sy-msg* variables. These need to be set right before calling this method. "!

"! @parameter iv_msgid | Message ID "! @parameter iv_msgno | Message number "! @parameter iv_msgv1 | Message variable 1 "! @parameter iv_msgv2 | Message variable 2 "! @parameter iv_msgv3 | Message variable 3 "! @parameter iv_msgv4 | Message variable 4 "! @parameter ii_log | Log "! @parameter ix_previous | Previous exception "! @parameter iv_longtext | Longtext "! @raising zcx_abapgit_exception | Exception CLASS-METHODS raise_t100 IMPORTING !iv_msgid TYPE symsgid DEFAULT sy-msgid !iv_msgno TYPE symsgno DEFAULT sy-msgno !iv_msgv1 TYPE symsgv DEFAULT sy-msgv1 !iv_msgv2 TYPE symsgv DEFAULT sy-msgv2 !iv_msgv3 TYPE symsgv DEFAULT sy-msgv3 !iv_msgv4 TYPE symsgv DEFAULT sy-msgv4 !ii_log TYPE REF TO zif_abapgit_log OPTIONAL !ix_previous TYPE REF TO cx_root OPTIONAL !iv_longtext TYPE csequence OPTIONAL RAISING zcx_abapgit_exception . "! Raise with text from previous exception "! @parameter ix_previous | Previous exception "! @parameter iv_longtext | Longtext "! @raising zcx_abapgit_exception | Exception CLASS-METHODS raise_with_text IMPORTING !ix_previous TYPE REF TO cx_root !iv_longtext TYPE csequence OPTIONAL RAISING zcx_abapgit_exception . METHODS constructor IMPORTING !textid LIKE if_t100_message=>t100key OPTIONAL !previous LIKE previous OPTIONAL !log TYPE REF TO zif_abapgit_log OPTIONAL !msgv1 TYPE symsgv OPTIONAL !msgv2 TYPE symsgv OPTIONAL !msgv3 TYPE symsgv OPTIONAL !msgv4 TYPE symsgv OPTIONAL !longtext TYPE csequence OPTIONAL . METHODS get_source_position REDEFINITION . METHODS if_message~get_longtext REDEFINITION . PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_generic_error_msg TYPE string VALUE `An error occurred (ZCX_ABAPGIT_EXCEPTION)`. METHODS save_callstack . METHODS itf_to_string IMPORTING !it_itf TYPE tline_tab RETURNING VALUE(rv_result) TYPE string . METHODS get_t100_longtext_itf RETURNING VALUE(rt_itf) TYPE tline_tab . METHODS remove_empty_section IMPORTING !iv_tabix_from TYPE i !iv_tabix_to TYPE i CHANGING !ct_itf TYPE tline_tab . METHODS replace_section_head_with_text CHANGING !cs_itf TYPE tline . CLASS-METHODS remove_newlines_from_string IMPORTING iv_string TYPE string RETURNING VALUE(rv_result) TYPE string. ENDCLASS. CLASS zcx_abapgit_exception IMPLEMENTATION. METHOD constructor ##ADT_SUPPRESS_GENERATION. super->constructor( previous = previous ). me->msgv1 = msgv1. me->msgv2 = msgv2. me->msgv3 = msgv3. me->msgv4 = msgv4. mi_log = log. mv_longtext = longtext. CLEAR me->textid. IF textid IS INITIAL. if_t100_message~t100key = if_t100_message=>default_textid. ELSE. if_t100_message~t100key = textid. ENDIF. save_callstack( ). " Save for debugger get_source_position( IMPORTING program_name = ms_src_info-program include_name = ms_src_info-include source_line = ms_src_info-line ). ENDMETHOD. METHOD get_source_position. FIELD-SYMBOLS: LIKE LINE OF mt_callstack. READ TABLE mt_callstack ASSIGNING INDEX 1. IF sy-subrc = 0. program_name = -mainprogram. include_name = -include. source_line = -line. ELSE. super->get_source_position( IMPORTING program_name = program_name include_name = include_name source_line = source_line ). ENDIF. ENDMETHOD. METHOD get_t100_longtext_itf. DATA: lv_docu_key TYPE doku_obj. FIELD-SYMBOLS TYPE any. lv_docu_key = if_t100_message~t100key-msgid && if_t100_message~t100key-msgno. CALL FUNCTION 'DOCU_GET' EXPORTING id = 'NA' langu = sy-langu object = lv_docu_key typ = 'E' TABLES line = rt_itf EXCEPTIONS OTHERS = 1. IF sy-subrc = 0. ASSIGN me->(if_t100_message~t100key-attr1) TO . IF sy-subrc = 0. REPLACE ALL OCCURRENCES OF '&V1&' IN TABLE rt_itf WITH . ENDIF. ASSIGN me->(if_t100_message~t100key-attr2) TO . IF sy-subrc = 0. REPLACE ALL OCCURRENCES OF '&V2&' IN TABLE rt_itf WITH . ENDIF. ASSIGN me->(if_t100_message~t100key-attr3) TO . IF sy-subrc = 0. REPLACE ALL OCCURRENCES OF '&V3&' IN TABLE rt_itf WITH . ENDIF. ASSIGN me->(if_t100_message~t100key-attr4) TO . IF sy-subrc = 0. REPLACE ALL OCCURRENCES OF '&V4&' IN TABLE rt_itf WITH . ENDIF. ENDIF. ENDMETHOD. METHOD if_message~get_longtext. DATA: lv_preserve_newlines_handled TYPE abap_bool VALUE abap_false. IF mv_longtext IS NOT INITIAL. result = mv_longtext. ELSEIF if_t100_message~t100key IS NOT INITIAL. result = itf_to_string( get_t100_longtext_itf( ) ). ELSE. result = super->get_longtext( preserve_newlines ). lv_preserve_newlines_handled = abap_true. ENDIF. IF lv_preserve_newlines_handled = abap_false AND preserve_newlines = abap_false. result = remove_newlines_from_string( result ). ENDIF. ENDMETHOD. METHOD itf_to_string. CONSTANTS: lc_format_section TYPE string VALUE 'U1'. DATA: lt_stream TYPE TABLE OF tdline, lt_string TYPE TABLE OF string, lt_itf TYPE tline_tab, lv_has_content TYPE abap_bool, lv_tabix_from TYPE syst-tabix, lv_tabix_to TYPE syst-tabix. FIELD-SYMBOLS: TYPE tline, TYPE tline. lt_itf = it_itf. " You should remember that we replace the U1 format because " that preserves the section header of longtexts. LOOP AT lt_itf ASSIGNING WHERE tdformat = lc_format_section. CLEAR: lv_has_content, lv_tabix_to. lv_tabix_from = sy-tabix. LOOP AT lt_itf ASSIGNING FROM sy-tabix + 1. IF -tdformat = lc_format_section. lv_tabix_to = sy-tabix. EXIT. ELSEIF -tdline IS NOT INITIAL. lv_has_content = abap_true. ENDIF. ENDLOOP. IF lv_has_content = abap_false. remove_empty_section( EXPORTING iv_tabix_from = lv_tabix_from iv_tabix_to = lv_tabix_to CHANGING ct_itf = lt_itf ). CONTINUE. ENDIF. replace_section_head_with_text( CHANGING cs_itf = ). ENDLOOP. CALL FUNCTION 'CONVERT_ITF_TO_STREAM_TEXT' EXPORTING lf = 'X' IMPORTING stream_lines = lt_string TABLES itf_text = lt_itf text_stream = lt_stream. rv_result = concat_lines_of( table = lt_string sep = cl_abap_char_utilities=>newline ). ENDMETHOD. METHOD raise. IF iv_text IS INITIAL. cl_message_helper=>set_msg_vars_for_clike( c_generic_error_msg ). ELSE. cl_message_helper=>set_msg_vars_for_clike( iv_text ). ENDIF. raise_t100( ii_log = ii_log ix_previous = ix_previous iv_longtext = iv_longtext ). ENDMETHOD. METHOD raise_t100. DATA: ls_t100_key TYPE scx_t100key. ls_t100_key-msgid = iv_msgid. ls_t100_key-msgno = iv_msgno. ls_t100_key-attr1 = 'MSGV1'. ls_t100_key-attr2 = 'MSGV2'. ls_t100_key-attr3 = 'MSGV3'. ls_t100_key-attr4 = 'MSGV4'. IF iv_msgid IS INITIAL. CLEAR ls_t100_key. ENDIF. RAISE EXCEPTION TYPE zcx_abapgit_exception EXPORTING textid = ls_t100_key log = ii_log msgv1 = iv_msgv1 msgv2 = iv_msgv2 msgv3 = iv_msgv3 msgv4 = iv_msgv4 previous = ix_previous longtext = iv_longtext. ENDMETHOD. METHOD raise_with_text. raise( iv_text = ix_previous->get_text( ) ix_previous = ix_previous iv_longtext = iv_longtext ). ENDMETHOD. METHOD remove_empty_section. IF iv_tabix_to BETWEEN iv_tabix_from AND lines( ct_itf ). DELETE ct_itf FROM iv_tabix_from TO iv_tabix_to. ELSE. DELETE ct_itf FROM iv_tabix_from. ENDIF. ENDMETHOD. METHOD remove_newlines_from_string. rv_result = iv_string. REPLACE ALL OCCURRENCES OF ` ` && cl_abap_char_utilities=>cr_lf IN rv_result WITH ` `. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf IN rv_result WITH ` `. REPLACE ALL OCCURRENCES OF ` ` && cl_abap_char_utilities=>newline IN rv_result WITH ` `. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>newline IN rv_result WITH ` `. ENDMETHOD. METHOD replace_section_head_with_text. CASE cs_itf-tdline. WHEN c_section_token-cause. cs_itf-tdline = c_section_text-cause. WHEN c_section_token-system_response. cs_itf-tdline = c_section_text-system_response. WHEN c_section_token-what_to_do. cs_itf-tdline = c_section_text-what_to_do. WHEN c_section_token-sys_admin. cs_itf-tdline = c_section_text-sys_admin. ENDCASE. ENDMETHOD. METHOD save_callstack. FIELD-SYMBOLS: LIKE LINE OF mt_callstack. CALL FUNCTION 'SYSTEM_CALLSTACK' IMPORTING callstack = mt_callstack. " You should remember that the first lines are from zcx_abapgit_exception " and are removed so that highest level in the callstack is the position where " the exception is raised. " " For the merged report it's hard to do that, because zcx_abapgit_exception " isn't visible in the callstack. Therefore we have to check the Events. LOOP AT mt_callstack ASSIGNING . IF -mainprogram CP |ZCX_ABAPGIT_EXCEPTION*| " full OR -blockname = `SAVE_CALLSTACK` " merged OR -blockname = `CONSTRUCTOR` " merged OR -blockname CP `RAISE*`. "merged DELETE TABLE mt_callstack FROM . ELSE. EXIT. ENDIF. ENDLOOP. ENDMETHOD. ENDCLASS. CLASS zcx_abapgit_type_not_supported DEFINITION INHERITING FROM zcx_abapgit_exception CREATE PUBLIC . PUBLIC SECTION. METHODS constructor IMPORTING textid LIKE if_t100_message=>t100key OPTIONAL previous LIKE previous OPTIONAL log TYPE REF TO zif_abapgit_log OPTIONAL msgv1 TYPE symsgv OPTIONAL msgv2 TYPE symsgv OPTIONAL msgv3 TYPE symsgv OPTIONAL msgv4 TYPE symsgv OPTIONAL longtext TYPE csequence OPTIONAL obj_type TYPE trobjtype. METHODS get_text REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. DATA mv_obj_type TYPE trobjtype. ENDCLASS. CLASS zcx_abapgit_type_not_supported IMPLEMENTATION. METHOD constructor ##ADT_SUPPRESS_GENERATION. super->constructor( textid = textid previous = previous log = log msgv1 = msgv1 msgv2 = msgv2 msgv3 = msgv3 msgv4 = msgv4 longtext = longtext ). mv_obj_type = obj_type. ENDMETHOD. METHOD get_text. result = |Object type { mv_obj_type } is not supported by this system|. ENDMETHOD. ENDCLASS. CLASS zcx_abapgit_not_found DEFINITION INHERITING FROM cx_static_check FINAL CREATE PUBLIC. PUBLIC SECTION. METHODS constructor IMPORTING !textid LIKE textid OPTIONAL !previous LIKE previous OPTIONAL. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcx_abapgit_not_found IMPLEMENTATION. METHOD constructor ##ADT_SUPPRESS_GENERATION. super->constructor( textid = textid previous = previous ). ENDMETHOD. ENDCLASS. ****** CLASSES DEFERRED ****** CLASS /apmg/cl_apm_abapgit_objects DEFINITION DEFERRED. CLASS /apmg/cl_apm_abapgit_serialize DEFINITION DEFERRED. CLASS /apmg/cl_apm_ajson DEFINITION DEFERRED. CLASS /apmg/cl_apm_ajson_extensions DEFINITION DEFERRED. CLASS /apmg/cl_apm_ajson_filter_lib DEFINITION DEFERRED. CLASS /apmg/cl_apm_ajson_mapping DEFINITION DEFERRED. CLASS /apmg/cl_apm_ajson_ref_initial DEFINITION DEFERRED. CLASS /apmg/cl_apm_ajson_refs_init_l DEFINITION DEFERRED. CLASS /apmg/cl_apm_ajson_utilities DEFINITION DEFERRED. CLASS /apmg/cl_apm_arborist DEFINITION DEFERRED. CLASS /apmg/cl_apm_arborist_edge DEFINITION DEFERRED. CLASS /apmg/cl_apm_arborist_node DEFINITION DEFERRED. CLASS /apmg/cl_apm_auth DEFINITION DEFERRED. CLASS /apmg/cl_apm_code_import_rules DEFINITION DEFERRED. CLASS /apmg/cl_apm_code_importer DEFINITION DEFERRED. CLASS /apmg/cl_apm_code_mapper DEFINITION DEFERRED. CLASS /apmg/cl_apm_command_deprecate DEFINITION DEFERRED. CLASS /apmg/cl_apm_command_init DEFINITION DEFERRED. CLASS /apmg/cl_apm_command_install DEFINITION DEFERRED. CLASS /apmg/cl_apm_command_installer DEFINITION DEFERRED. CLASS /apmg/cl_apm_command_integrity DEFINITION DEFERRED. CLASS /apmg/cl_apm_command_login DEFINITION DEFERRED. CLASS /apmg/cl_apm_command_publish DEFINITION DEFERRED. CLASS /apmg/cl_apm_command_uninstall DEFINITION DEFERRED. CLASS /apmg/cl_apm_command_unpublish DEFINITION DEFERRED. CLASS /apmg/cl_apm_command_update DEFINITION DEFERRED. CLASS /apmg/cl_apm_command_utils DEFINITION DEFERRED. CLASS /apmg/cl_apm_emoji DEFINITION DEFERRED. CLASS /apmg/cl_apm_exception_viewer DEFINITION DEFERRED. CLASS /apmg/cl_apm_file_importer DEFINITION DEFERRED. CLASS /apmg/cl_apm_frontend_services DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_asset_manager DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_buttons DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_chunk_lib DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_component DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_css_processor DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_dlg_deprecate DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_dlg_init DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_dlg_install DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_dlg_publish DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_dlg_undepreca DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_dlg_uninstall DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_dlg_unpublish DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_event DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_factory DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_hotkey_ctl DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_html_processo DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_html_viewer DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_menus DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_page DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_page_db DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_page_db_entry DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_page_debuginf DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_page_hoc DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_page_list DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_page_package DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_router DEFINITION DEFERRED. CLASS /apmg/cl_apm_gui_utils DEFINITION DEFERRED. CLASS /apmg/cl_apm_highlighter DEFINITION DEFERRED. CLASS /apmg/cl_apm_highlighter_abap DEFINITION DEFERRED. CLASS /apmg/cl_apm_highlighter_css DEFINITION DEFERRED. CLASS /apmg/cl_apm_highlighter_diff DEFINITION DEFERRED. CLASS /apmg/cl_apm_highlighter_facto DEFINITION DEFERRED. CLASS /apmg/cl_apm_highlighter_js DEFINITION DEFERRED. CLASS /apmg/cl_apm_highlighter_json DEFINITION DEFERRED. CLASS /apmg/cl_apm_highlighter_md DEFINITION DEFERRED. CLASS /apmg/cl_apm_highlighter_po DEFINITION DEFERRED. CLASS /apmg/cl_apm_highlighter_txt DEFINITION DEFERRED. CLASS /apmg/cl_apm_highlighter_xml DEFINITION DEFERRED. CLASS /apmg/cl_apm_highlighter_yaml DEFINITION DEFERRED. CLASS /apmg/cl_apm_html DEFINITION DEFERRED. CLASS /apmg/cl_apm_html_action_utils DEFINITION DEFERRED. CLASS /apmg/cl_apm_html_form DEFINITION DEFERRED. CLASS /apmg/cl_apm_html_form_utils DEFINITION DEFERRED. CLASS /apmg/cl_apm_html_parts DEFINITION DEFERRED. CLASS /apmg/cl_apm_html_table DEFINITION DEFERRED. CLASS /apmg/cl_apm_html_toolbar DEFINITION DEFERRED. CLASS /apmg/cl_apm_http_agent DEFINITION DEFERRED. CLASS /apmg/cl_apm_http_login_manage DEFINITION DEFERRED. CLASS /apmg/cl_apm_importer DEFINITION DEFERRED. CLASS /apmg/cl_apm_installer DEFINITION DEFERRED. CLASS /apmg/cl_apm_installer_files DEFINITION DEFERRED. CLASS /apmg/cl_apm_json DEFINITION DEFERRED. CLASS /apmg/cl_apm_logo DEFINITION DEFERRED. CLASS /apmg/cl_apm_markdown DEFINITION DEFERRED. CLASS /apmg/cl_apm_markdown_path DEFINITION DEFERRED. CLASS /apmg/cl_apm_markdown_syn DEFINITION DEFERRED. CLASS /apmg/cl_apm_object_clas DEFINITION DEFERRED. CLASS /apmg/cl_apm_object_intf DEFINITION DEFERRED. CLASS /apmg/cl_apm_object_prog DEFINITION DEFERRED. CLASS /apmg/cl_apm_package_json DEFINITION DEFERRED. CLASS /apmg/cl_apm_package_json_vali DEFINITION DEFERRED. CLASS /apmg/cl_apm_pacote DEFINITION DEFERRED. CLASS /apmg/cl_apm_persist_apm DEFINITION DEFERRED. CLASS /apmg/cl_apm_persist_apm_setup DEFINITION DEFERRED. CLASS /apmg/cl_apm_popup_utils DEFINITION DEFERRED. CLASS /apmg/cl_apm_popups DEFINITION DEFERRED. CLASS /apmg/cl_apm_progress_bar DEFINITION DEFERRED. CLASS /apmg/cl_apm_readme DEFINITION DEFERRED. CLASS /apmg/cl_apm_roadmap DEFINITION DEFERRED. CLASS /apmg/cl_apm_semver DEFINITION DEFERRED. CLASS /apmg/cl_apm_semver_cli DEFINITION DEFERRED. CLASS /apmg/cl_apm_semver_comparator DEFINITION DEFERRED. CLASS /apmg/cl_apm_semver_fixtures DEFINITION DEFERRED. CLASS /apmg/cl_apm_semver_functions DEFINITION DEFERRED. CLASS /apmg/cl_apm_semver_identifier DEFINITION DEFERRED. CLASS /apmg/cl_apm_semver_integratio DEFINITION DEFERRED. CLASS /apmg/cl_apm_semver_range DEFINITION DEFERRED. CLASS /apmg/cl_apm_semver_ranges DEFINITION DEFERRED. CLASS /apmg/cl_apm_semver_re DEFINITION DEFERRED. CLASS /apmg/cl_apm_semver_sap DEFINITION DEFERRED. CLASS /apmg/cl_apm_semver_utils DEFINITION DEFERRED. CLASS /apmg/cl_apm_settings DEFINITION DEFERRED. CLASS /apmg/cl_apm_string_map DEFINITION DEFERRED. CLASS /apmg/cl_apm_tar DEFINITION DEFERRED. CLASS /apmg/cl_apm_trace DEFINITION DEFERRED. CLASS /apmg/cl_apm_url DEFINITION DEFERRED. CLASS /apmg/cl_apm_url_params DEFINITION DEFERRED. CLASS zcl_abapgit_abap_language_vers DEFINITION DEFERRED. CLASS zcl_abapgit_adt_link DEFINITION DEFERRED. CLASS zcl_abapgit_aff_factory DEFINITION DEFERRED. CLASS zcl_abapgit_aff_injector DEFINITION DEFERRED. CLASS zcl_abapgit_aff_registry DEFINITION DEFERRED. CLASS zcl_abapgit_convert DEFINITION DEFERRED. CLASS zcl_abapgit_cts_api DEFINITION DEFERRED. CLASS zcl_abapgit_default_transport DEFINITION DEFERRED. CLASS zcl_abapgit_dependencies DEFINITION DEFERRED. CLASS zcl_abapgit_dot_abapgit DEFINITION DEFERRED. CLASS zcl_abapgit_ecatt_config_downl DEFINITION DEFERRED. CLASS zcl_abapgit_ecatt_config_upl DEFINITION DEFERRED. CLASS zcl_abapgit_ecatt_data_downl DEFINITION DEFERRED. CLASS zcl_abapgit_ecatt_data_upload DEFINITION DEFERRED. CLASS zcl_abapgit_ecatt_helper DEFINITION DEFERRED. CLASS zcl_abapgit_ecatt_script_downl DEFINITION DEFERRED. CLASS zcl_abapgit_ecatt_script_upl DEFINITION DEFERRED. CLASS zcl_abapgit_ecatt_sp_download DEFINITION DEFERRED. CLASS zcl_abapgit_ecatt_sp_upload DEFINITION DEFERRED. CLASS zcl_abapgit_ecatt_system_downl DEFINITION DEFERRED. CLASS zcl_abapgit_ecatt_system_upl DEFINITION DEFERRED. CLASS zcl_abapgit_ecatt_val_obj_down DEFINITION DEFERRED. CLASS zcl_abapgit_ecatt_val_obj_upl DEFINITION DEFERRED. CLASS zcl_abapgit_env_factory DEFINITION DEFERRED. CLASS zcl_abapgit_env_injector DEFINITION DEFERRED. CLASS zcl_abapgit_environment DEFINITION DEFERRED. CLASS zcl_abapgit_exit DEFINITION DEFERRED. CLASS zcl_abapgit_factory DEFINITION DEFERRED. CLASS zcl_abapgit_feature DEFINITION DEFERRED. CLASS zcl_abapgit_field_rules DEFINITION DEFERRED. CLASS zcl_abapgit_filename_logic DEFINITION DEFERRED. CLASS zcl_abapgit_folder_logic DEFINITION DEFERRED. CLASS zcl_abapgit_function_module DEFINITION DEFERRED. CLASS zcl_abapgit_gui_jumper DEFINITION DEFERRED. CLASS zcl_abapgit_hash DEFINITION DEFERRED. CLASS zcl_abapgit_i18n_params DEFINITION DEFERRED. CLASS zcl_abapgit_injector DEFINITION DEFERRED. CLASS zcl_abapgit_item_graph DEFINITION DEFERRED. CLASS zcl_abapgit_json_handler DEFINITION DEFERRED. CLASS zcl_abapgit_json_path DEFINITION DEFERRED. CLASS zcl_abapgit_language DEFINITION DEFERRED. CLASS zcl_abapgit_log DEFINITION DEFERRED. CLASS zcl_abapgit_longtexts DEFINITION DEFERRED. CLASS zcl_abapgit_lxe_texts DEFINITION DEFERRED. CLASS zcl_abapgit_object_acid DEFINITION DEFERRED. CLASS zcl_abapgit_object_aifc DEFINITION DEFERRED. CLASS zcl_abapgit_object_amsd DEFINITION DEFERRED. CLASS zcl_abapgit_object_apis DEFINITION DEFERRED. CLASS zcl_abapgit_object_aplo DEFINITION DEFERRED. CLASS zcl_abapgit_object_aqbg DEFINITION DEFERRED. CLASS zcl_abapgit_object_aqqu DEFINITION DEFERRED. CLASS zcl_abapgit_object_aqsg DEFINITION DEFERRED. CLASS zcl_abapgit_object_area DEFINITION DEFERRED. CLASS zcl_abapgit_object_asfc DEFINITION DEFERRED. CLASS zcl_abapgit_object_auth DEFINITION DEFERRED. CLASS zcl_abapgit_object_avar DEFINITION DEFERRED. CLASS zcl_abapgit_object_avas DEFINITION DEFERRED. CLASS zcl_abapgit_object_bdef DEFINITION DEFERRED. CLASS zcl_abapgit_object_bgqc DEFINITION DEFERRED. CLASS zcl_abapgit_object_cdbo DEFINITION DEFERRED. CLASS zcl_abapgit_object_char DEFINITION DEFERRED. CLASS zcl_abapgit_object_chdo DEFINITION DEFERRED. CLASS zcl_abapgit_object_chkc DEFINITION DEFERRED. CLASS zcl_abapgit_object_chko DEFINITION DEFERRED. CLASS zcl_abapgit_object_chkv DEFINITION DEFERRED. CLASS zcl_abapgit_object_clas DEFINITION DEFERRED. CLASS zcl_abapgit_object_cmod DEFINITION DEFERRED. CLASS zcl_abapgit_object_cmpt DEFINITION DEFERRED. CLASS zcl_abapgit_object_common_aff DEFINITION DEFERRED. CLASS zcl_abapgit_object_cota DEFINITION DEFERRED. CLASS zcl_abapgit_object_cus0 DEFINITION DEFERRED. CLASS zcl_abapgit_object_cus1 DEFINITION DEFERRED. CLASS zcl_abapgit_object_cus2 DEFINITION DEFERRED. CLASS zcl_abapgit_object_dcls DEFINITION DEFERRED. CLASS zcl_abapgit_object_ddls DEFINITION DEFERRED. CLASS zcl_abapgit_object_ddlx DEFINITION DEFERRED. CLASS zcl_abapgit_object_desd DEFINITION DEFERRED. CLASS zcl_abapgit_object_devc DEFINITION DEFERRED. CLASS zcl_abapgit_object_dial DEFINITION DEFERRED. CLASS zcl_abapgit_object_doct DEFINITION DEFERRED. CLASS zcl_abapgit_object_docv DEFINITION DEFERRED. CLASS zcl_abapgit_object_doma DEFINITION DEFERRED. CLASS zcl_abapgit_object_dras DEFINITION DEFERRED. CLASS zcl_abapgit_object_drty DEFINITION DEFERRED. CLASS zcl_abapgit_object_drul DEFINITION DEFERRED. CLASS zcl_abapgit_object_dsfd DEFINITION DEFERRED. CLASS zcl_abapgit_object_dsfi DEFINITION DEFERRED. CLASS zcl_abapgit_object_dsys DEFINITION DEFERRED. CLASS zcl_abapgit_object_dtdc DEFINITION DEFERRED. CLASS zcl_abapgit_object_dteb DEFINITION DEFERRED. CLASS zcl_abapgit_object_dtel DEFINITION DEFERRED. CLASS zcl_abapgit_object_ecat DEFINITION DEFERRED. CLASS zcl_abapgit_object_ecatt_super DEFINITION DEFERRED. CLASS zcl_abapgit_object_ecsd DEFINITION DEFERRED. CLASS zcl_abapgit_object_ecsp DEFINITION DEFERRED. CLASS zcl_abapgit_object_ectc DEFINITION DEFERRED. CLASS zcl_abapgit_object_ectd DEFINITION DEFERRED. CLASS zcl_abapgit_object_ecvo DEFINITION DEFERRED. CLASS zcl_abapgit_object_eeec DEFINITION DEFERRED. CLASS zcl_abapgit_object_enhc DEFINITION DEFERRED. CLASS zcl_abapgit_object_enho DEFINITION DEFERRED. CLASS zcl_abapgit_object_enho_badi DEFINITION DEFERRED. CLASS zcl_abapgit_object_enho_class DEFINITION DEFERRED. CLASS zcl_abapgit_object_enho_clif DEFINITION DEFERRED. CLASS zcl_abapgit_object_enho_fugr DEFINITION DEFERRED. CLASS zcl_abapgit_object_enho_hook DEFINITION DEFERRED. CLASS zcl_abapgit_object_enho_intf DEFINITION DEFERRED. CLASS zcl_abapgit_object_enho_wdyc DEFINITION DEFERRED. CLASS zcl_abapgit_object_enho_wdyn DEFINITION DEFERRED. CLASS zcl_abapgit_object_enhs DEFINITION DEFERRED. CLASS zcl_abapgit_object_enhs_badi_d DEFINITION DEFERRED. CLASS zcl_abapgit_object_enhs_hook_d DEFINITION DEFERRED. CLASS zcl_abapgit_object_enqu DEFINITION DEFERRED. CLASS zcl_abapgit_object_ensc DEFINITION DEFERRED. CLASS zcl_abapgit_object_evtb DEFINITION DEFERRED. CLASS zcl_abapgit_object_fdt0 DEFINITION DEFERRED. CLASS zcl_abapgit_object_form DEFINITION DEFERRED. CLASS zcl_abapgit_object_ftgl DEFINITION DEFERRED. CLASS zcl_abapgit_object_fugr DEFINITION DEFERRED. CLASS zcl_abapgit_object_fugs DEFINITION DEFERRED. CLASS zcl_abapgit_object_g4ba DEFINITION DEFERRED. CLASS zcl_abapgit_object_g4bs DEFINITION DEFERRED. CLASS zcl_abapgit_object_gsmp DEFINITION DEFERRED. CLASS zcl_abapgit_object_http DEFINITION DEFERRED. CLASS zcl_abapgit_object_iamu DEFINITION DEFERRED. CLASS zcl_abapgit_object_iarp DEFINITION DEFERRED. CLASS zcl_abapgit_object_iasp DEFINITION DEFERRED. CLASS zcl_abapgit_object_iatu DEFINITION DEFERRED. CLASS zcl_abapgit_object_iaxu DEFINITION DEFERRED. CLASS zcl_abapgit_object_idoc DEFINITION DEFERRED. CLASS zcl_abapgit_object_iext DEFINITION DEFERRED. CLASS zcl_abapgit_object_intf DEFINITION DEFERRED. CLASS zcl_abapgit_object_iobj DEFINITION DEFERRED. CLASS zcl_abapgit_object_iwmo DEFINITION DEFERRED. CLASS zcl_abapgit_object_iwom DEFINITION DEFERRED. CLASS zcl_abapgit_object_iwpr DEFINITION DEFERRED. CLASS zcl_abapgit_object_iwsg DEFINITION DEFERRED. CLASS zcl_abapgit_object_iwsv DEFINITION DEFERRED. CLASS zcl_abapgit_object_iwvb DEFINITION DEFERRED. CLASS zcl_abapgit_object_jobd DEFINITION DEFERRED. CLASS zcl_abapgit_object_msag DEFINITION DEFERRED. CLASS zcl_abapgit_object_nont DEFINITION DEFERRED. CLASS zcl_abapgit_object_nrob DEFINITION DEFERRED. CLASS zcl_abapgit_object_nspc DEFINITION DEFERRED. CLASS zcl_abapgit_object_oa2p DEFINITION DEFERRED. CLASS zcl_abapgit_object_odso DEFINITION DEFERRED. CLASS zcl_abapgit_object_otgr DEFINITION DEFERRED. CLASS zcl_abapgit_object_para DEFINITION DEFERRED. CLASS zcl_abapgit_object_pdxx_super DEFINITION DEFERRED. CLASS zcl_abapgit_object_pers DEFINITION DEFERRED. CLASS zcl_abapgit_object_pinf DEFINITION DEFERRED. CLASS zcl_abapgit_object_prag DEFINITION DEFERRED. CLASS zcl_abapgit_object_prog DEFINITION DEFERRED. CLASS zcl_abapgit_object_ront DEFINITION DEFERRED. CLASS zcl_abapgit_object_sajc DEFINITION DEFERRED. CLASS zcl_abapgit_object_sajt DEFINITION DEFERRED. CLASS zcl_abapgit_object_samc DEFINITION DEFERRED. CLASS zcl_abapgit_object_sapc DEFINITION DEFERRED. CLASS zcl_abapgit_object_saxx_super DEFINITION DEFERRED. CLASS zcl_abapgit_object_scp1 DEFINITION DEFERRED. CLASS zcl_abapgit_object_scvi DEFINITION DEFERRED. CLASS zcl_abapgit_object_sfbf DEFINITION DEFERRED. CLASS zcl_abapgit_object_sfbs DEFINITION DEFERRED. CLASS zcl_abapgit_object_sfpf DEFINITION DEFERRED. CLASS zcl_abapgit_object_sfpi DEFINITION DEFERRED. CLASS zcl_abapgit_object_sfsw DEFINITION DEFERRED. CLASS zcl_abapgit_object_shi3 DEFINITION DEFERRED. CLASS zcl_abapgit_object_shi5 DEFINITION DEFERRED. CLASS zcl_abapgit_object_shi8 DEFINITION DEFERRED. CLASS zcl_abapgit_object_shlp DEFINITION DEFERRED. CLASS zcl_abapgit_object_shma DEFINITION DEFERRED. CLASS zcl_abapgit_object_sicf DEFINITION DEFERRED. CLASS zcl_abapgit_object_sktd DEFINITION DEFERRED. CLASS zcl_abapgit_object_smbc DEFINITION DEFERRED. CLASS zcl_abapgit_object_smim DEFINITION DEFERRED. CLASS zcl_abapgit_object_smtg DEFINITION DEFERRED. CLASS zcl_abapgit_object_sobj DEFINITION DEFERRED. CLASS zcl_abapgit_object_sod1 DEFINITION DEFERRED. CLASS zcl_abapgit_object_sod2 DEFINITION DEFERRED. CLASS zcl_abapgit_object_sots DEFINITION DEFERRED. CLASS zcl_abapgit_object_splo DEFINITION DEFERRED. CLASS zcl_abapgit_object_sppf DEFINITION DEFERRED. CLASS zcl_abapgit_object_sprx DEFINITION DEFERRED. CLASS zcl_abapgit_object_sqsc DEFINITION DEFERRED. CLASS zcl_abapgit_object_srfc DEFINITION DEFERRED. CLASS zcl_abapgit_object_srvb DEFINITION DEFERRED. CLASS zcl_abapgit_object_srvd DEFINITION DEFERRED. CLASS zcl_abapgit_object_ssfo DEFINITION DEFERRED. CLASS zcl_abapgit_object_ssst DEFINITION DEFERRED. CLASS zcl_abapgit_object_stvi DEFINITION DEFERRED. CLASS zcl_abapgit_object_styl DEFINITION DEFERRED. CLASS zcl_abapgit_object_sucu DEFINITION DEFERRED. CLASS zcl_abapgit_object_susc DEFINITION DEFERRED. CLASS zcl_abapgit_object_sush DEFINITION DEFERRED. CLASS zcl_abapgit_object_suso DEFINITION DEFERRED. CLASS zcl_abapgit_object_swcr DEFINITION DEFERRED. CLASS zcl_abapgit_object_sxci DEFINITION DEFERRED. CLASS zcl_abapgit_object_sxsd DEFINITION DEFERRED. CLASS zcl_abapgit_object_tabl DEFINITION DEFERRED. CLASS zcl_abapgit_object_tabl_compar DEFINITION DEFERRED. CLASS zcl_abapgit_object_tabl_ddl DEFINITION DEFERRED. CLASS zcl_abapgit_object_tobj DEFINITION DEFERRED. CLASS zcl_abapgit_object_tran DEFINITION DEFERRED. CLASS zcl_abapgit_object_ttyp DEFINITION DEFERRED. CLASS zcl_abapgit_object_type DEFINITION DEFERRED. CLASS zcl_abapgit_object_ucsa DEFINITION DEFERRED. CLASS zcl_abapgit_object_udmo DEFINITION DEFERRED. CLASS zcl_abapgit_object_ueno DEFINITION DEFERRED. CLASS zcl_abapgit_object_uiad DEFINITION DEFERRED. CLASS zcl_abapgit_object_uipg DEFINITION DEFERRED. CLASS zcl_abapgit_object_uist DEFINITION DEFERRED. CLASS zcl_abapgit_object_vcls DEFINITION DEFERRED. CLASS zcl_abapgit_object_view DEFINITION DEFERRED. CLASS zcl_abapgit_object_w3ht DEFINITION DEFERRED. CLASS zcl_abapgit_object_w3mi DEFINITION DEFERRED. CLASS zcl_abapgit_object_w3xx_super DEFINITION DEFERRED. CLASS zcl_abapgit_object_wapa DEFINITION DEFERRED. CLASS zcl_abapgit_object_wdca DEFINITION DEFERRED. CLASS zcl_abapgit_object_wdcc DEFINITION DEFERRED. CLASS zcl_abapgit_object_wdya DEFINITION DEFERRED. CLASS zcl_abapgit_object_wdyn DEFINITION DEFERRED. CLASS zcl_abapgit_object_webi DEFINITION DEFERRED. CLASS zcl_abapgit_object_xinx DEFINITION DEFERRED. CLASS zcl_abapgit_object_xslt DEFINITION DEFERRED. CLASS zcl_abapgit_objects_activation DEFINITION DEFERRED. CLASS zcl_abapgit_objects_compare DEFINITION DEFERRED. CLASS zcl_abapgit_objects_factory DEFINITION DEFERRED. CLASS zcl_abapgit_objects_files DEFINITION DEFERRED. CLASS zcl_abapgit_objects_generic DEFINITION DEFERRED. CLASS zcl_abapgit_objects_injector DEFINITION DEFERRED. CLASS zcl_abapgit_objects_program DEFINITION DEFERRED. CLASS zcl_abapgit_objects_super DEFINITION DEFERRED. CLASS zcl_abapgit_oo_base DEFINITION DEFERRED. CLASS zcl_abapgit_oo_class DEFINITION DEFERRED. CLASS zcl_abapgit_oo_factory DEFINITION DEFERRED. CLASS zcl_abapgit_oo_interface DEFINITION DEFERRED. CLASS zcl_abapgit_oo_serializer DEFINITION DEFERRED. CLASS zcl_abapgit_path DEFINITION DEFERRED. CLASS zcl_abapgit_po_file DEFINITION DEFERRED. CLASS zcl_abapgit_progress DEFINITION DEFERRED. CLASS zcl_abapgit_properties_file DEFINITION DEFERRED. CLASS zcl_abapgit_sap_namespace DEFINITION DEFERRED. CLASS zcl_abapgit_sap_package DEFINITION DEFERRED. CLASS zcl_abapgit_sap_report DEFINITION DEFERRED. CLASS zcl_abapgit_sotr_handler DEFINITION DEFERRED. CLASS zcl_abapgit_sots_handler DEFINITION DEFERRED. CLASS zcl_abapgit_status_calc DEFINITION DEFERRED. CLASS zcl_abapgit_string_buffer DEFINITION DEFERRED. CLASS zcl_abapgit_tadir DEFINITION DEFERRED. CLASS zcl_abapgit_timer DEFINITION DEFERRED. CLASS zcl_abapgit_url DEFINITION DEFERRED. CLASS zcl_abapgit_user_record DEFINITION DEFERRED. CLASS zcl_abapgit_utils DEFINITION DEFERRED. CLASS zcl_abapgit_version DEFINITION DEFERRED. CLASS zcl_abapgit_xml DEFINITION DEFERRED. CLASS zcl_abapgit_xml_input DEFINITION DEFERRED. CLASS zcl_abapgit_xml_output DEFINITION DEFERRED. CLASS zcl_abapgit_xml_pretty DEFINITION DEFERRED. ****** INTERFACES ****** INTERFACE zif_abapgit_aff_types_v1 . TYPES ty_format_version TYPE string. TYPES ty_abap_language_version TYPE c LENGTH 1. TYPES ty_abap_language_version_cloud TYPE c LENGTH 1. TYPES ty_abap_language_version_src TYPE c LENGTH 1. CONSTANTS: BEGIN OF co_abap_language_version_src, standard TYPE ty_abap_language_version_src VALUE 'X', key_user TYPE ty_abap_language_version_src VALUE '2', cloud_development TYPE ty_abap_language_version_src VALUE '5', END OF co_abap_language_version_src. CONSTANTS: BEGIN OF co_abap_language_version, standard TYPE ty_abap_language_version VALUE space, key_user TYPE ty_abap_language_version VALUE '2', cloud_development TYPE ty_abap_language_version VALUE '5', END OF co_abap_language_version. CONSTANTS: BEGIN OF co_abap_language_version_cloud, standard TYPE ty_abap_language_version_cloud VALUE space, cloud_development TYPE ty_abap_language_version_cloud VALUE '5', END OF co_abap_language_version_cloud. TYPES ty_description_60 TYPE c LENGTH 60. TYPES ty_description_100 TYPE c LENGTH 100. TYPES ty_object_name_30 TYPE c LENGTH 30. TYPES ty_original_language TYPE sy-langu. TYPES: BEGIN OF ty_header_60_src, description TYPE ty_description_60, original_language TYPE ty_original_language, abap_language_version TYPE ty_abap_language_version_src, END OF ty_header_60_src. TYPES: BEGIN OF ty_header_60_cloud, description TYPE ty_description_60, original_language TYPE ty_original_language, abap_language_version TYPE ty_abap_language_version_cloud, END OF ty_header_60_cloud. TYPES: BEGIN OF ty_header_60, description TYPE ty_description_60, original_language TYPE ty_original_language, abap_language_version TYPE ty_abap_language_version, END OF ty_header_60. TYPES: BEGIN OF ty_header_60_no_abap_lv, description TYPE ty_description_60, original_language TYPE ty_original_language, END OF ty_header_60_no_abap_lv. TYPES: BEGIN OF ty_header_100, description TYPE ty_description_100, original_language TYPE ty_original_language, abap_language_version TYPE ty_abap_language_version, END OF ty_header_100. TYPES: BEGIN OF ty_header_only_description, description TYPE ty_description_60, END OF ty_header_only_description. TYPES ty_option TYPE c LENGTH 2. CONSTANTS: BEGIN OF co_option, equals TYPE ty_option VALUE 'EQ', between TYPE ty_option VALUE 'BT', greater_than TYPE ty_option VALUE 'GT', contains_pattern TYPE ty_option VALUE 'CP', not_equal TYPE ty_option VALUE 'NE', not_between TYPE ty_option VALUE 'NB', not_contains_pattern TYPE ty_option VALUE 'NP', greater_equal TYPE ty_option VALUE 'GE', less_than TYPE ty_option VALUE 'LT', less_equal TYPE ty_option VALUE 'LE', END OF co_option. TYPES ty_sign TYPE c LENGTH 1. CONSTANTS: BEGIN OF co_sign, include TYPE ty_sign VALUE 'I', exclude TYPE ty_sign VALUE 'E', END OF co_sign. ENDINTERFACE. INTERFACE zif_abapgit_git_definitions . * this interface is self contained * only references to built in types * git does not know the concept of TADIR objects, only knows files TYPES: ty_type TYPE c LENGTH 6 . TYPES: ty_sha1 TYPE c LENGTH 40 . TYPES: ty_sha1_tt TYPE STANDARD TABLE OF ty_sha1 WITH DEFAULT KEY . TYPES: ty_adler32 TYPE x LENGTH 4 . TYPES ty_item_state TYPE c LENGTH 1. TYPES: BEGIN OF ty_file_signature, path TYPE string, filename TYPE string, sha1 TYPE ty_sha1, END OF ty_file_signature . TYPES: ty_file_signatures_tt TYPE STANDARD TABLE OF ty_file_signature WITH DEFAULT KEY . TYPES: ty_file_signatures_ts TYPE SORTED TABLE OF ty_file_signature WITH UNIQUE KEY path filename . TYPES: BEGIN OF ty_file. INCLUDE TYPE ty_file_signature. TYPES: data TYPE xstring, END OF ty_file . TYPES: ty_files_tt TYPE STANDARD TABLE OF ty_file WITH DEFAULT KEY WITH UNIQUE SORTED KEY file_path COMPONENTS path filename WITH NON-UNIQUE SORTED KEY file COMPONENTS filename. TYPES ty_git_branch_type TYPE c LENGTH 2 . TYPES: BEGIN OF ty_git_branch, sha1 TYPE ty_sha1, name TYPE string, type TYPE ty_git_branch_type, is_head TYPE abap_bool, display_name TYPE string, END OF ty_git_branch . TYPES: ty_git_branch_list_tt TYPE STANDARD TABLE OF ty_git_branch WITH DEFAULT KEY WITH NON-UNIQUE SORTED KEY name_key COMPONENTS name. TYPES: BEGIN OF ty_git_tag, sha1 TYPE ty_sha1, object TYPE ty_sha1, name TYPE string, type TYPE ty_git_branch_type, display_name TYPE string, tagger_name TYPE string, tagger_email TYPE string, message TYPE string, body TYPE string, END OF ty_git_tag . TYPES: BEGIN OF ty_git_user, name TYPE string, email TYPE string, END OF ty_git_user . TYPES: BEGIN OF ty_comment, committer TYPE ty_git_user, author TYPE ty_git_user, comment TYPE string, END OF ty_comment . TYPES: ty_chmod TYPE c LENGTH 6 . CONSTANTS: BEGIN OF c_chmod, file TYPE ty_chmod VALUE '100644', executable TYPE ty_chmod VALUE '100755', dir TYPE ty_chmod VALUE '40000 ', submodule TYPE ty_chmod VALUE '160000', symbolic_link TYPE ty_chmod VALUE '120000', END OF c_chmod . TYPES: BEGIN OF ty_expanded, path TYPE string, name TYPE string, sha1 TYPE ty_sha1, chmod TYPE ty_chmod, END OF ty_expanded . TYPES: ty_expanded_tt TYPE STANDARD TABLE OF ty_expanded WITH DEFAULT KEY WITH NON-UNIQUE SORTED KEY path_name COMPONENTS path name. TYPES: BEGIN OF ty_create, name TYPE string, parent TYPE string, END OF ty_create . TYPES: BEGIN OF ty_commit, sha1 TYPE ty_sha1, parent1 TYPE ty_sha1, parent2 TYPE ty_sha1, author TYPE string, email TYPE string, time TYPE string, message TYPE string, body TYPE STANDARD TABLE OF string WITH DEFAULT KEY, branch TYPE string, merge TYPE string, tags TYPE STANDARD TABLE OF string WITH DEFAULT KEY, create TYPE STANDARD TABLE OF ty_create WITH DEFAULT KEY, compressed TYPE abap_bool, END OF ty_commit . TYPES: ty_commit_tt TYPE STANDARD TABLE OF ty_commit WITH DEFAULT KEY . CONSTANTS: BEGIN OF c_type, commit TYPE ty_type VALUE 'commit', "#EC NOTEXT tree TYPE ty_type VALUE 'tree', "#EC NOTEXT ref_d TYPE ty_type VALUE 'ref_d', "#EC NOTEXT tag TYPE ty_type VALUE 'tag', "#EC NOTEXT blob TYPE ty_type VALUE 'blob', "#EC NOTEXT END OF c_type . CONSTANTS: BEGIN OF c_git_branch_type, branch TYPE ty_git_branch_type VALUE 'HD', lightweight_tag TYPE ty_git_branch_type VALUE 'TG', annotated_tag TYPE ty_git_branch_type VALUE 'AT', other TYPE ty_git_branch_type VALUE 'ZZ', END OF c_git_branch_type . CONSTANTS c_head_name TYPE string VALUE 'HEAD' ##NO_TEXT. CONSTANTS: BEGIN OF c_git_branch, main TYPE string VALUE 'refs/heads/main', prefix TYPE string VALUE 'refs/', heads_prefix TYPE string VALUE 'refs/heads/', heads TYPE string VALUE 'refs/heads/*', tags_prefix TYPE string VALUE 'refs/tags/', tags TYPE string VALUE 'refs/tags/*', peel TYPE string VALUE '^{}', END OF c_git_branch. TYPES ty_head_type TYPE c LENGTH 1. CONSTANTS: BEGIN OF c_head_types, all TYPE ty_head_type VALUE 'A', branch TYPE ty_head_type VALUE 'B', tag TYPE ty_head_type VALUE 'T', commit TYPE ty_head_type VALUE 'C', pull_request TYPE ty_head_type VALUE 'P', END OF c_head_types. ENDINTERFACE. INTERFACE zif_abapgit_definitions . TYPES: ty_string_tt TYPE STANDARD TABLE OF string WITH DEFAULT KEY . TYPES: BEGIN OF ty_item_signature, obj_type TYPE tadir-object, obj_name TYPE tadir-obj_name, devclass TYPE devclass, END OF ty_item_signature . TYPES: BEGIN OF ty_obj_namespace, namespace TYPE trnspace-namespace, obj_without_namespace TYPE tadir-obj_name, END OF ty_obj_namespace. TYPES: BEGIN OF ty_item. INCLUDE TYPE ty_item_signature. TYPES: srcsystem TYPE tadir-srcsystem, origlang TYPE tadir-masterlang, inactive TYPE abap_bool, abap_language_version TYPE zif_abapgit_aff_types_v1=>ty_abap_language_version, END OF ty_item . TYPES: ty_items_tt TYPE STANDARD TABLE OF ty_item WITH DEFAULT KEY . TYPES: ty_items_ts TYPE SORTED TABLE OF ty_item WITH UNIQUE KEY obj_type obj_name . TYPES: BEGIN OF ty_file_item, file TYPE zif_abapgit_git_definitions=>ty_file, item TYPE ty_item, END OF ty_file_item . TYPES: ty_files_item_tt TYPE STANDARD TABLE OF ty_file_item WITH DEFAULT KEY . TYPES: ty_files_item_by_file_tt TYPE SORTED TABLE OF ty_file_item WITH UNIQUE KEY file-path file-filename. TYPES: ty_yes_no TYPE c LENGTH 1, ty_yes_no_partial TYPE c LENGTH 1. TYPES: BEGIN OF ty_overwrite. INCLUDE TYPE ty_item. TYPES: state TYPE c LENGTH 2, action TYPE i, icon TYPE icon_d, text TYPE string, decision TYPE ty_yes_no, END OF ty_overwrite . TYPES: ty_overwrite_tt TYPE STANDARD TABLE OF ty_overwrite WITH DEFAULT KEY WITH UNIQUE HASHED KEY object_type_and_name COMPONENTS obj_type obj_name . TYPES: BEGIN OF ty_requirements, met TYPE ty_yes_no, decision TYPE ty_yes_no, END OF ty_requirements . TYPES: BEGIN OF ty_dependencies, met TYPE ty_yes_no, decision TYPE ty_yes_no, END OF ty_dependencies . TYPES: BEGIN OF ty_transport_type, request TYPE trfunction, task TYPE trfunction, END OF ty_transport_type . TYPES: BEGIN OF ty_transport, required TYPE abap_bool, transport TYPE trkorr, type TYPE ty_transport_type, END OF ty_transport . TYPES: BEGIN OF ty_deserialize_checks, overwrite TYPE ty_overwrite_tt, warning_package TYPE ty_overwrite_tt, data_loss TYPE ty_overwrite_tt, delete_tabl_with_data TYPE ty_overwrite_tt, requirements TYPE ty_requirements, dependencies TYPE ty_dependencies, transport TYPE ty_transport, customizing TYPE ty_transport, END OF ty_deserialize_checks . TYPES: BEGIN OF ty_delete_checks, transport TYPE ty_transport, END OF ty_delete_checks . TYPES: BEGIN OF ty_metadata, class TYPE string, version TYPE string, END OF ty_metadata . TYPES: BEGIN OF ty_repo_file, path TYPE string, filename TYPE string, is_changed TYPE abap_bool, rstate TYPE zif_abapgit_git_definitions=>ty_item_state, lstate TYPE zif_abapgit_git_definitions=>ty_item_state, END OF ty_repo_file . TYPES: ty_repo_file_tt TYPE STANDARD TABLE OF ty_repo_file WITH DEFAULT KEY . TYPES: BEGIN OF ty_object, sha1 TYPE zif_abapgit_git_definitions=>ty_sha1, type TYPE zif_abapgit_git_definitions=>ty_type, data TYPE xstring, adler32 TYPE zif_abapgit_git_definitions=>ty_adler32, index TYPE i, END OF ty_object . TYPES: ty_objects_tt TYPE STANDARD TABLE OF ty_object WITH DEFAULT KEY WITH NON-UNIQUE SORTED KEY sha COMPONENTS sha1 WITH NON-UNIQUE SORTED KEY type COMPONENTS type sha1 . TYPES: BEGIN OF ty_tadir, pgmid TYPE tadir-pgmid, object TYPE tadir-object, obj_name TYPE tadir-obj_name, devclass TYPE tadir-devclass, korrnum TYPE tadir-korrnum, " used by ZCL_ABAPGIT_DEPENDENCIES->RESOLVE delflag TYPE tadir-delflag, genflag TYPE tadir-genflag, path TYPE string, srcsystem TYPE tadir-srcsystem, masterlang TYPE tadir-masterlang, END OF ty_tadir . TYPES: ty_tadir_tt TYPE STANDARD TABLE OF ty_tadir WITH DEFAULT KEY . TYPES: BEGIN OF ty_result, obj_type TYPE tadir-object, obj_name TYPE tadir-obj_name, inactive TYPE abap_bool, path TYPE string, filename TYPE string, package TYPE devclass, match TYPE abap_bool, lstate TYPE zif_abapgit_git_definitions=>ty_item_state, rstate TYPE zif_abapgit_git_definitions=>ty_item_state, packmove TYPE abap_bool, srcsystem TYPE tadir-srcsystem, origlang TYPE tadir-masterlang, END OF ty_result . TYPES: ty_results_tt TYPE STANDARD TABLE OF ty_result WITH DEFAULT KEY WITH NON-UNIQUE SORTED KEY sec_key COMPONENTS obj_type obj_name. TYPES: ty_results_ts_path TYPE HASHED TABLE OF ty_result WITH UNIQUE KEY path filename . TYPES: BEGIN OF ty_stage_files, local TYPE ty_files_item_tt, remote TYPE zif_abapgit_git_definitions=>ty_files_tt, status TYPE ty_results_ts_path, END OF ty_stage_files . TYPES: BEGIN OF ty_transport_to_branch, branch_name TYPE string, commit_text TYPE string, END OF ty_transport_to_branch . TYPES: BEGIN OF ty_diff, patch_flag TYPE abap_bool, new_num TYPE c LENGTH 6, new TYPE string, result TYPE c LENGTH 1, old_num TYPE c LENGTH 6, old TYPE string, short TYPE abap_bool, beacon TYPE i, END OF ty_diff . TYPES: ty_diffs_tt TYPE STANDARD TABLE OF ty_diff WITH DEFAULT KEY WITH NON-UNIQUE SORTED KEY new_num COMPONENTS new_num WITH NON-UNIQUE SORTED KEY old_num COMPONENTS old_num. TYPES: BEGIN OF ty_count, insert TYPE i, delete TYPE i, update TYPE i, END OF ty_count . TYPES: BEGIN OF ty_ancestor, commit TYPE zif_abapgit_git_definitions=>ty_sha1, tree TYPE zif_abapgit_git_definitions=>ty_sha1, time TYPE string, body TYPE string, END OF ty_ancestor . TYPES: BEGIN OF ty_repo_item, obj_type TYPE tadir-object, obj_name TYPE tadir-obj_name, inactive TYPE abap_bool, sortkey TYPE i, path TYPE string, is_dir TYPE abap_bool, changes TYPE i, lstate TYPE zif_abapgit_git_definitions=>ty_item_state, rstate TYPE zif_abapgit_git_definitions=>ty_item_state, files TYPE ty_repo_file_tt, changed_by TYPE syuname, transport TYPE trkorr, packmove TYPE abap_bool, srcsystem TYPE tadir-srcsystem, origlang TYPE tadir-masterlang, END OF ty_repo_item . TYPES: ty_repo_item_tt TYPE STANDARD TABLE OF ty_repo_item WITH DEFAULT KEY . TYPES: ty_dokil_tt TYPE STANDARD TABLE OF dokil WITH NON-UNIQUE DEFAULT KEY . TYPES: ty_proxy_bypass_url TYPE c LENGTH 255, ty_range_proxy_bypass_url TYPE RANGE OF ty_proxy_bypass_url. TYPES: BEGIN OF ty_version, major TYPE i, minor TYPE i, patch TYPE i, prerelase TYPE string, prerelase_patch TYPE i, END OF ty_version. TYPES ty_sci_result TYPE c LENGTH 1. CONSTANTS: BEGIN OF c_sci_result, no_run TYPE ty_sci_result VALUE '', failed TYPE ty_sci_result VALUE 'F', warning TYPE ty_sci_result VALUE 'W', passed TYPE ty_sci_result VALUE 'P', END OF c_sci_result. CONSTANTS: BEGIN OF c_diff, unchanged TYPE c LENGTH 1 VALUE ' ', insert TYPE c LENGTH 1 VALUE 'I', delete TYPE c LENGTH 1 VALUE 'D', update TYPE c LENGTH 1 VALUE 'U', END OF c_diff . CONSTANTS: BEGIN OF c_state, " https://git-scm.com/docs/git-status unchanged TYPE zif_abapgit_git_definitions=>ty_item_state VALUE '', added TYPE zif_abapgit_git_definitions=>ty_item_state VALUE 'A', modified TYPE zif_abapgit_git_definitions=>ty_item_state VALUE 'M', deleted TYPE zif_abapgit_git_definitions=>ty_item_state VALUE 'D', mixed TYPE zif_abapgit_git_definitions=>ty_item_state VALUE '*', END OF c_state . CONSTANTS c_english TYPE spras VALUE 'E' ##NO_TEXT. CONSTANTS c_root_dir TYPE string VALUE '/' ##NO_TEXT. CONSTANTS c_dot_abapgit TYPE string VALUE '.abapgit.xml' ##NO_TEXT. CONSTANTS c_author_regex TYPE string VALUE '^(.+) <(.*)> (\d{10})\s?.\d{4}$' ##NO_TEXT. CONSTANTS: BEGIN OF c_action, abapgit_home TYPE string VALUE 'abapgit_home', bg_update TYPE string VALUE 'bg_update', change_order_by TYPE string VALUE 'change_order_by', changelog TYPE string VALUE 'changelog', clipboard TYPE string VALUE 'clipboard', db_display TYPE string VALUE 'db_display', db_edit TYPE string VALUE 'db_edit', direction TYPE string VALUE 'direction', documentation TYPE string VALUE 'documentation', flow TYPE string VALUE 'flow', git_branch_create TYPE string VALUE 'git_branch_create', git_branch_delete TYPE string VALUE 'git_branch_delete', git_branch_merge TYPE string VALUE 'git_branch_merge', git_branch_switch TYPE string VALUE 'git_branch_switch', git_commit TYPE string VALUE 'git_commit', git_pull TYPE string VALUE 'git_pull', git_tag_create TYPE string VALUE 'git_tag_create', git_tag_delete TYPE string VALUE 'git_tag_delete', git_tag_switch TYPE string VALUE 'git_tag_switch', go_back TYPE string VALUE 'go_back', go_background TYPE string VALUE 'go_background', go_background_run TYPE string VALUE 'go_background_run', go_commit TYPE string VALUE 'go_commit', go_db TYPE string VALUE 'go_db', go_debuginfo TYPE string VALUE 'go_debuginfo', go_explore TYPE string VALUE 'go_explore', go_file_diff TYPE string VALUE 'go_file_diff', go_home TYPE string VALUE 'go_home', go_patch TYPE string VALUE 'go_patch', go_repo TYPE string VALUE 'go_repo', go_repo_diff TYPE string VALUE 'go_repo_diff', go_settings TYPE string VALUE 'go_settings', go_settings_personal TYPE string VALUE 'go_settings_personal', go_stage TYPE string VALUE 'go_stage', go_stage_transport TYPE string VALUE 'go_stage_transport', go_tutorial TYPE string VALUE 'go_tutorial', goto_message TYPE string VALUE 'goto_message', goto_source TYPE string VALUE 'goto_source', homepage TYPE string VALUE 'homepage', ie_devtools TYPE string VALUE 'ie_devtools', jump TYPE string VALUE 'jump', jump_transaction TYPE string VALUE 'jump_transaction', jump_transport TYPE string VALUE 'jump_transport', jump_user TYPE string VALUE 'jump_user', performance_test TYPE string VALUE 'performance_test', repo_activate_objects TYPE string VALUE 'repo_activate_objects', repo_add_all_obj_to_trans_req TYPE string VALUE 'repo_add_all_obj_to_trans_req', repo_background TYPE string VALUE 'repo_background', repo_change_package TYPE string VALUE 'repo_change_package', repo_code_inspector TYPE string VALUE 'repo_code_inspector', repo_delete_objects TYPE string VALUE 'repo_delete_objects', repo_infos TYPE string VALUE 'repo_infos', repo_local_settings TYPE string VALUE 'repo_local_settings', repo_log TYPE string VALUE 'repo_log', repo_newoffline TYPE string VALUE 'repo_newoffline', repo_newonline TYPE string VALUE 'repo_newonline', repo_open_in_master_lang TYPE string VALUE 'repo_open_in_master_lang', repo_purge TYPE string VALUE 'repo_purge', repo_refresh TYPE string VALUE 'repo_refresh', repo_refresh_checksums TYPE string VALUE 'repo_refresh_checksums', repo_remote_settings TYPE string VALUE 'repo_remote_settings', repo_remove TYPE string VALUE 'repo_remove', repo_settings TYPE string VALUE 'repo_settings', repo_syntax_check TYPE string VALUE 'repo_syntax_check', repo_toggle_fav TYPE string VALUE 'repo_toggle_fav', repo_transport_to_branch TYPE string VALUE 'repo_transport_to_branch', rfc_compare TYPE string VALUE 'rfc_compare', show_callstack TYPE string VALUE 'show_callstack', show_hotkeys TYPE string VALUE 'show_hotkeys', sponsor TYPE string VALUE 'sponsor', toggle_favorites TYPE string VALUE 'toggle_favorites', url TYPE string VALUE 'url', where_used TYPE string VALUE 'where_used', zip_export TYPE string VALUE 'zip_export', zip_export_transport TYPE string VALUE 'zip_export_transport', zip_import TYPE string VALUE 'zip_import', zip_object TYPE string VALUE 'zip_object', zip_package TYPE string VALUE 'zip_package', zip_transport TYPE string VALUE 'zip_transport', END OF c_action. CONSTANTS c_spagpa_param_repo_key TYPE c LENGTH 20 VALUE 'REPO_KEY' ##NO_TEXT. CONSTANTS c_spagpa_param_package TYPE c LENGTH 20 VALUE 'PACKAGE' ##NO_TEXT. CONSTANTS c_yes TYPE ty_yes_no VALUE 'Y'. CONSTANTS c_no TYPE ty_yes_no VALUE 'N'. CONSTANTS c_partial TYPE ty_yes_no_partial VALUE 'P'. TYPES: ty_method TYPE c LENGTH 1 . TYPES: BEGIN OF ty_stage, file TYPE zif_abapgit_git_definitions=>ty_file, method TYPE ty_method, status TYPE ty_result, END OF ty_stage . TYPES: ty_stage_tt TYPE SORTED TABLE OF ty_stage WITH UNIQUE KEY file-path file-filename . CONSTANTS: BEGIN OF c_method, add TYPE ty_method VALUE 'A', rm TYPE ty_method VALUE 'R', ignore TYPE ty_method VALUE 'I', skip TYPE ty_method VALUE '?', END OF c_method . TYPES: ty_sap_langu_tab TYPE STANDARD TABLE OF langu WITH DEFAULT KEY. TYPES: ty_languages TYPE STANDARD TABLE OF laiso WITH DEFAULT KEY. TYPES: BEGIN OF ty_i18n_params, main_language TYPE sy-langu, main_language_only TYPE abap_bool, translation_languages TYPE ty_languages, use_lxe TYPE abap_bool, suppress_po_comments TYPE abap_bool, END OF ty_i18n_params . TYPES ty_trrngtrkor_tt TYPE RANGE OF trkorr. CONSTANTS c_multiple_transports TYPE trkorr VALUE 'MULTIPLE'. ENDINTERFACE. INTERFACE zif_abapgit_objects . TYPES: BEGIN OF ty_serialization, files TYPE zif_abapgit_git_definitions=>ty_files_tt, item TYPE zif_abapgit_definitions=>ty_item, END OF ty_serialization . TYPES: BEGIN OF ty_deserialization, obj TYPE REF TO zif_abapgit_object, xml TYPE REF TO zif_abapgit_xml_input, package TYPE devclass, item TYPE zif_abapgit_definitions=>ty_item, files TYPE REF TO zcl_abapgit_objects_files, END OF ty_deserialization . TYPES: ty_deserialization_tt TYPE STANDARD TABLE OF ty_deserialization WITH DEFAULT KEY . TYPES: ty_types_tt TYPE SORTED TABLE OF tadir-object WITH UNIQUE KEY table_line. TYPES: ty_deserialization_step TYPE string. TYPES: ty_deserialization_step_tt TYPE STANDARD TABLE OF ty_deserialization_step WITH DEFAULT KEY. TYPES: BEGIN OF ty_step_data, step_id TYPE ty_deserialization_step, order TYPE i, descr TYPE string, syntax_check TYPE abap_bool, objects TYPE ty_deserialization_tt, END OF ty_step_data . TYPES: ty_step_data_tt TYPE STANDARD TABLE OF ty_step_data WITH DEFAULT KEY . CONSTANTS: BEGIN OF c_deserialize_action, " also used to determine priority if object has multiple changes, so don't change order no_support TYPE i VALUE -1, none TYPE i VALUE 0, add TYPE i VALUE 1, update TYPE i VALUE 2, overwrite TYPE i VALUE 3, delete TYPE i VALUE 4, delete_add TYPE i VALUE 5, packmove TYPE i VALUE 6, data_loss TYPE i VALUE 7, delete_tabl_with_data TYPE i VALUE 8, END OF c_deserialize_action. ENDINTERFACE. INTERFACE zif_abapgit_xml_output . METHODS add IMPORTING !iv_name TYPE clike !ig_data TYPE any RAISING zcx_abapgit_exception . METHODS set_raw IMPORTING !ii_raw TYPE REF TO if_ixml_element . METHODS add_xml IMPORTING !iv_name TYPE clike !ii_xml TYPE REF TO if_ixml_element . METHODS render IMPORTING !iv_normalize TYPE abap_bool DEFAULT abap_true !is_metadata TYPE zif_abapgit_definitions=>ty_metadata OPTIONAL RETURNING VALUE(rv_xml) TYPE string . ENDINTERFACE. INTERFACE zif_abapgit_dot_abapgit . TYPES: BEGIN OF ty_requirement, component TYPE tdevc-dlvunit, min_release TYPE saprelease, min_patch TYPE sappatchlv, END OF ty_requirement . TYPES: ty_requirement_tt TYPE STANDARD TABLE OF ty_requirement WITH DEFAULT KEY . TYPES: BEGIN OF ty_dot_abapgit, name TYPE string, master_language TYPE spras, i18n_languages TYPE zif_abapgit_definitions=>ty_languages, use_lxe TYPE abap_bool, without_translation TYPE STANDARD TABLE OF string WITH DEFAULT KEY, starting_folder TYPE string, folder_logic TYPE string, ignore TYPE STANDARD TABLE OF string WITH DEFAULT KEY, requirements TYPE ty_requirement_tt, version_constant TYPE string, abap_language_version TYPE string, original_system TYPE tadir-srcsystem, END OF ty_dot_abapgit . CONSTANTS: BEGIN OF c_folder_logic, prefix TYPE string VALUE 'PREFIX', full TYPE string VALUE 'FULL', mixed TYPE string VALUE 'MIXED', END OF c_folder_logic, BEGIN OF c_abap_language_version, standard TYPE string VALUE 'standard', key_user TYPE string VALUE 'keyUser', cloud_development TYPE string VALUE 'cloudDevelopment', ignore TYPE string VALUE 'ignore', undefined TYPE string VALUE 'undefined', " any END OF c_abap_language_version. ENDINTERFACE. INTERFACE zif_abapgit_persistence . TYPES: ty_type TYPE c LENGTH 12 . TYPES: ty_value TYPE c LENGTH 12 . TYPES: BEGIN OF ty_content, type TYPE ty_type, value TYPE ty_value, data_str TYPE string, END OF ty_content . TYPES: ty_contents TYPE SORTED TABLE OF ty_content WITH UNIQUE KEY type value . TYPES: BEGIN OF ty_local_checksum, item TYPE zif_abapgit_definitions=>ty_item_signature, files TYPE zif_abapgit_git_definitions=>ty_file_signatures_tt, END OF ty_local_checksum. TYPES: BEGIN OF ty_local_settings, display_name TYPE string, ignore_subpackages TYPE abap_bool, write_protected TYPE abap_bool, only_local_objects TYPE abap_bool, code_inspector_check_variant TYPE sci_chkv, block_commit TYPE abap_bool, main_language_only TYPE abap_bool, suppress_lxe_po_comments TYPE abap_bool, labels TYPE string, transport_request TYPE trkorr, customizing_request TYPE trkorr, flow TYPE abap_bool, exclude_remote_paths TYPE string_table, END OF ty_local_settings. TYPES: ty_local_checksum_tt TYPE STANDARD TABLE OF ty_local_checksum WITH DEFAULT KEY. TYPES: ty_local_checksum_by_item_tt TYPE SORTED TABLE OF ty_local_checksum WITH NON-UNIQUE KEY item-obj_type item-obj_name. TYPES: BEGIN OF ty_repo_xml, url TYPE string, branch_name TYPE string, selected_commit TYPE zif_abapgit_git_definitions=>ty_sha1, package TYPE devclass, created_by TYPE syuname, created_at TYPE timestampl, deserialized_by TYPE syuname, deserialized_at TYPE timestampl, offline TYPE abap_bool, switched_origin TYPE string, dot_abapgit TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit, head_branch TYPE string, " HEAD symref of the repo, master branch local_settings TYPE ty_local_settings, END OF ty_repo_xml. TYPES: BEGIN OF ty_repo_meta_mask, url TYPE abap_bool, branch_name TYPE abap_bool, selected_commit TYPE abap_bool, package TYPE abap_bool, created_by TYPE abap_bool, created_at TYPE abap_bool, deserialized_by TYPE abap_bool, deserialized_at TYPE abap_bool, offline TYPE abap_bool, switched_origin TYPE abap_bool, dot_abapgit TYPE abap_bool, head_branch TYPE abap_bool, local_settings TYPE abap_bool, END OF ty_repo_meta_mask. TYPES: BEGIN OF ty_repo, key TYPE ty_value. INCLUDE TYPE ty_repo_xml. TYPES: END OF ty_repo. TYPES: ty_repos TYPE STANDARD TABLE OF ty_repo WITH DEFAULT KEY. TYPES: ty_repo_keys TYPE STANDARD TABLE OF ty_repo-key WITH DEFAULT KEY. TYPES: BEGIN OF ty_remote_settings, offline TYPE ty_repo-offline, url TYPE ty_repo-url, branch TYPE zif_abapgit_git_definitions=>ty_git_branch-name, tag TYPE zif_abapgit_git_definitions=>ty_git_tag-name, commit TYPE zif_abapgit_git_definitions=>ty_commit-sha1, pull_request TYPE string, head_type TYPE zif_abapgit_git_definitions=>ty_head_type, switched_origin TYPE ty_repo-switched_origin, END OF ty_remote_settings. ENDINTERFACE. INTERFACE zif_abapgit_data_persistence . METHODS save_config IMPORTING !iv_repo_key TYPE zif_abapgit_persistence=>ty_repo-key RAISING zcx_abapgit_exception. METHODS load_config IMPORTING !iv_repo_key TYPE zif_abapgit_persistence=>ty_repo-key RAISING zcx_abapgit_exception. ENDINTERFACE. INTERFACE zif_abapgit_data_config . INTERFACES zif_abapgit_data_persistence. TYPES: ty_data_type TYPE c LENGTH 4 . TYPES: BEGIN OF ty_config, type TYPE ty_data_type, name TYPE tadir-obj_name, skip_initial TYPE abap_bool, where TYPE string_table, END OF ty_config . TYPES: ty_config_tt TYPE SORTED TABLE OF ty_config WITH UNIQUE KEY type name . CONSTANTS c_default_path TYPE string VALUE '/data/' ##NO_TEXT. CONSTANTS c_default_format TYPE string VALUE 'json' ##NO_TEXT. CONSTANTS c_config TYPE string VALUE 'conf' ##NO_TEXT. CONSTANTS: BEGIN OF c_data_type, tabu TYPE ty_data_type VALUE 'TABU', vdat TYPE ty_data_type VALUE 'VDAT', cdat TYPE ty_data_type VALUE 'CDAT', tdat TYPE ty_data_type VALUE 'TDAT', END OF c_data_type . METHODS add_config IMPORTING !is_config TYPE ty_config RAISING zcx_abapgit_exception . METHODS from_json IMPORTING !it_files TYPE zif_abapgit_git_definitions=>ty_files_tt RAISING zcx_abapgit_exception . METHODS get_configs RETURNING VALUE(rt_configs) TYPE ty_config_tt . METHODS remove_config IMPORTING !is_config TYPE ty_config RAISING zcx_abapgit_exception . METHODS to_json RETURNING VALUE(rt_files) TYPE zif_abapgit_git_definitions=>ty_files_tt RAISING zcx_abapgit_exception . METHODS update_config IMPORTING !is_config TYPE ty_config RAISING zcx_abapgit_exception . ENDINTERFACE. INTERFACE zif_abapgit_data_supporter . TYPES: BEGIN OF ty_object, type TYPE zif_abapgit_data_config=>ty_config-type, name TYPE zif_abapgit_data_config=>ty_config-name, END OF ty_object. TYPES: ty_objects TYPE SORTED TABLE OF ty_object WITH UNIQUE KEY type name. METHODS is_object_supported IMPORTING !iv_type TYPE ty_object-type !iv_name TYPE ty_object-name RETURNING VALUE(rv_supported) TYPE abap_bool. ENDINTERFACE. INTERFACE zif_abapgit_exit . TYPES: BEGIN OF ty_ci_repo, name TYPE string, clone_url TYPE string, END OF ty_ci_repo. TYPES ty_ci_repos TYPE STANDARD TABLE OF ty_ci_repo WITH DEFAULT KEY. TYPES ty_object_types TYPE HASHED TABLE OF tadir-object WITH UNIQUE KEY table_line. TYPES: BEGIN OF ty_class_key, clsname TYPE abap_classname, END OF ty_class_key. METHODS adjust_display_commit_url IMPORTING !iv_repo_url TYPE csequence !iv_repo_name TYPE csequence !iv_repo_key TYPE csequence !iv_commit_hash TYPE zif_abapgit_git_definitions=>ty_sha1 CHANGING !cv_display_url TYPE csequence RAISING zcx_abapgit_exception. METHODS adjust_display_filename IMPORTING !is_repo_meta TYPE zif_abapgit_persistence=>ty_repo !iv_filename TYPE string RETURNING VALUE(rv_filename) TYPE string. METHODS allow_sap_objects RETURNING VALUE(rv_allowed) TYPE abap_bool. METHODS change_committer_info IMPORTING iv_repo_url TYPE csequence CHANGING cv_name TYPE csequence cv_email TYPE csequence. METHODS change_local_host CHANGING !ct_hosts TYPE zif_abapgit_definitions=>ty_string_tt. METHODS change_max_parallel_processes IMPORTING !iv_package TYPE devclass CHANGING !cv_max_processes TYPE i. METHODS change_proxy_authentication IMPORTING !iv_repo_url TYPE csequence CHANGING !cv_proxy_authentication TYPE abap_bool. METHODS change_proxy_port IMPORTING !iv_repo_url TYPE csequence CHANGING !cv_proxy_port TYPE string. METHODS change_proxy_url IMPORTING !iv_repo_url TYPE csequence CHANGING !cv_proxy_url TYPE string. METHODS change_rfc_server_group CHANGING !cv_group TYPE rzlli_apcl. METHODS change_supported_data_objects CHANGING !ct_objects TYPE zif_abapgit_data_supporter=>ty_objects. METHODS change_supported_object_types CHANGING !ct_types TYPE ty_object_types. METHODS change_tadir IMPORTING !iv_package TYPE devclass !ii_log TYPE REF TO zif_abapgit_log !is_dot_abapgit TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit !iv_ignore_subpackages TYPE abap_bool DEFAULT abap_false !iv_only_local_objects TYPE abap_bool DEFAULT abap_false CHANGING !ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt. METHODS create_http_client IMPORTING !iv_url TYPE string RETURNING VALUE(ri_client) TYPE REF TO if_http_client RAISING zcx_abapgit_exception. METHODS custom_serialize_abap_clif IMPORTING !is_class_key TYPE ty_class_key !it_source TYPE zif_abapgit_definitions=>ty_string_tt OPTIONAL RETURNING VALUE(rt_source) TYPE zif_abapgit_definitions=>ty_string_tt RAISING zcx_abapgit_exception. METHODS deserialize_postprocess IMPORTING !iv_package TYPE devclass OPTIONAL !is_step TYPE zif_abapgit_objects=>ty_step_data OPTIONAL !ii_log TYPE REF TO zif_abapgit_log OPTIONAL !it_remote TYPE zif_abapgit_git_definitions=>ty_files_tt OPTIONAL CHANGING !ct_updated_files TYPE zif_abapgit_git_definitions=>ty_file_signatures_tt OPTIONAL. METHODS get_ci_tests IMPORTING !iv_object TYPE tadir-object CHANGING !ct_ci_repos TYPE ty_ci_repos. METHODS get_ssl_id RETURNING VALUE(rv_ssl_id) TYPE ssfapplssl. METHODS http_client IMPORTING !iv_url TYPE string !ii_client TYPE REF TO if_http_client. METHODS pre_calculate_repo_status IMPORTING !is_repo_meta TYPE zif_abapgit_persistence=>ty_repo CHANGING !ct_local TYPE zif_abapgit_definitions=>ty_files_item_tt !ct_remote TYPE zif_abapgit_git_definitions=>ty_files_tt RAISING zcx_abapgit_exception. METHODS serialize_postprocess IMPORTING !iv_package TYPE devclass !ii_log TYPE REF TO zif_abapgit_log CHANGING !ct_files TYPE zif_abapgit_definitions=>ty_files_item_tt. METHODS validate_after_push IMPORTING ii_repo_online TYPE REF TO zif_abapgit_repo_online RAISING zcx_abapgit_exception. ENDINTERFACE. INTERFACE zif_abapgit_sap_package . TYPES: ty_devclass_tt TYPE STANDARD TABLE OF devclass WITH DEFAULT KEY . TYPES: BEGIN OF ty_create, devclass TYPE devclass, korrflag TYPE abap_bool, dlvunit TYPE tdevc-dlvunit, component TYPE c LENGTH 20, ctext TYPE c LENGTH 60, parentcl TYPE devclass, pdevclass TYPE c LENGTH 4, as4user TYPE usnam, END OF ty_create. METHODS get RETURNING VALUE(rs_package) TYPE ty_create RAISING zcx_abapgit_exception . METHODS validate_name RAISING zcx_abapgit_exception . METHODS create IMPORTING !is_package TYPE ty_create RAISING zcx_abapgit_exception . METHODS create_local RAISING zcx_abapgit_exception . METHODS list_subpackages RETURNING VALUE(rt_list) TYPE ty_devclass_tt . METHODS list_superpackages RETURNING VALUE(rt_list) TYPE ty_devclass_tt RAISING zcx_abapgit_exception . METHODS read_parent RETURNING VALUE(rv_parentcl) TYPE devclass RAISING zcx_abapgit_exception . METHODS read_description RETURNING VALUE(rv_description) TYPE string. METHODS read_responsible RETURNING VALUE(rv_responsible) TYPE usnam. METHODS create_child IMPORTING !iv_child TYPE devclass RAISING zcx_abapgit_exception . METHODS exists RETURNING VALUE(rv_bool) TYPE abap_bool . METHODS are_changes_recorded_in_tr_req RETURNING VALUE(rv_are_changes_rec_in_tr_req) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS get_transport_type RETURNING VALUE(rs_transport_type) TYPE zif_abapgit_definitions=>ty_transport_type RAISING zcx_abapgit_exception . METHODS get_default_transport_layer RETURNING VALUE(rv_transport_layer) TYPE devlayer RAISING zcx_abapgit_exception. METHODS check_object_type IMPORTING iv_obj_type TYPE tadir-object RAISING zcx_abapgit_exception. ENDINTERFACE. INTERFACE /apmg/if_apm_ajson_types . TYPES: ty_node_type TYPE string. CONSTANTS: BEGIN OF node_type, boolean TYPE ty_node_type VALUE 'bool', string TYPE ty_node_type VALUE 'str', number TYPE ty_node_type VALUE 'num', null TYPE ty_node_type VALUE 'null', array TYPE ty_node_type VALUE 'array', object TYPE ty_node_type VALUE 'object', END OF node_type. TYPES: BEGIN OF ty_node, path TYPE string, name TYPE string, type TYPE ty_node_type, value TYPE string, index TYPE i, order TYPE i, children TYPE i, END OF ty_node. TYPES: ty_nodes_tt TYPE STANDARD TABLE OF ty_node WITH KEY path name. TYPES: ty_nodes_ts TYPE SORTED TABLE OF ty_node WITH UNIQUE KEY path name WITH NON-UNIQUE SORTED KEY array_index COMPONENTS path index WITH NON-UNIQUE SORTED KEY item_order COMPONENTS path order. TYPES: BEGIN OF ty_path_name, path TYPE string, name TYPE string, END OF ty_path_name. ENDINTERFACE. INTERFACE /apmg/if_apm_ajson . CONSTANTS version TYPE string VALUE 'v1.1.13'. "#EC NOTEXT CONSTANTS origin TYPE string VALUE 'https://github.com/sbcgua/ajson'. "#EC NOTEXT CONSTANTS license TYPE string VALUE 'MIT'. "#EC NOTEXT TYPES: BEGIN OF ty_opts, read_only TYPE abap_bool, keep_item_order TYPE abap_bool, format_datetime TYPE abap_bool, to_abap_corresponding_only TYPE abap_bool, END OF ty_opts. " DATA DATA mt_json_tree TYPE /apmg/if_apm_ajson_types=>ty_nodes_ts READ-ONLY. " CLONING METHODS clone RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS filter IMPORTING ii_filter TYPE REF TO /apmg/if_apm_ajson_filter RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS map IMPORTING ii_mapper TYPE REF TO /apmg/if_apm_ajson_mapping RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. " METHODS METHODS freeze. METHODS keep_item_order RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson. METHODS format_datetime IMPORTING iv_use_iso TYPE abap_bool DEFAULT abap_true RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson. METHODS to_abap_corresponding_only IMPORTING iv_enable TYPE abap_bool DEFAULT abap_true RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson. METHODS opts RETURNING VALUE(rs_opts) TYPE ty_opts. " METHODS ex.reader METHODS is_empty RETURNING VALUE(rv_yes) TYPE abap_bool. METHODS exists IMPORTING iv_path TYPE string RETURNING VALUE(rv_exists) TYPE abap_bool. METHODS members IMPORTING iv_path TYPE string RETURNING VALUE(rt_members) TYPE string_table. METHODS get IMPORTING iv_path TYPE string RETURNING VALUE(rv_value) TYPE string. METHODS get_node_type IMPORTING iv_path TYPE string RETURNING VALUE(rv_node_type) TYPE /apmg/if_apm_ajson_types=>ty_node_type. METHODS get_boolean IMPORTING iv_path TYPE string RETURNING VALUE(rv_value) TYPE abap_bool. METHODS get_integer IMPORTING iv_path TYPE string RETURNING VALUE(rv_value) TYPE i. METHODS get_number IMPORTING iv_path TYPE string RETURNING VALUE(rv_value) TYPE f. METHODS get_date IMPORTING iv_path TYPE string RETURNING VALUE(rv_value) TYPE d. METHODS get_timestamp IMPORTING iv_path TYPE string RETURNING VALUE(rv_value) TYPE timestamp. METHODS get_timestampl IMPORTING iv_path TYPE string RETURNING VALUE(rv_value) TYPE timestampl. METHODS get_string IMPORTING iv_path TYPE string RETURNING VALUE(rv_value) TYPE string. METHODS slice IMPORTING iv_path TYPE string RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson. METHODS to_abap IMPORTING iv_corresponding TYPE abap_bool DEFAULT abap_false ii_refs_initiator TYPE REF TO /apmg/if_apm_ajson_ref_initial OPTIONAL EXPORTING ev_container TYPE any RAISING /apmg/cx_apm_ajson_error. METHODS array_to_string_table IMPORTING iv_path TYPE string RETURNING VALUE(rt_string_table) TYPE string_table RAISING /apmg/cx_apm_ajson_error. " METHODS ex.writer METHODS clear RAISING /apmg/cx_apm_ajson_error. METHODS set IMPORTING iv_path TYPE string iv_val TYPE any iv_ignore_empty TYPE abap_bool DEFAULT abap_true iv_node_type TYPE /apmg/if_apm_ajson_types=>ty_node_type OPTIONAL RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS setx IMPORTING iv_param TYPE string RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS set_boolean IMPORTING iv_path TYPE string iv_val TYPE any RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS set_string IMPORTING iv_path TYPE string iv_val TYPE clike RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS set_integer IMPORTING iv_path TYPE string iv_val TYPE i RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS set_date IMPORTING iv_path TYPE string iv_val TYPE d RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS set_timestamp IMPORTING iv_path TYPE string iv_val TYPE timestamp RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS set_timestampl IMPORTING iv_path TYPE string iv_val TYPE timestampl RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS set_null IMPORTING iv_path TYPE string RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS delete IMPORTING iv_path TYPE string RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS touch_array IMPORTING iv_path TYPE string iv_clear TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS push IMPORTING iv_path TYPE string iv_val TYPE any RETURNING VALUE(ri_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS stringify IMPORTING iv_indent TYPE i DEFAULT 0 RETURNING VALUE(rv_json) TYPE string RAISING /apmg/cx_apm_ajson_error. ENDINTERFACE. INTERFACE /apmg/if_apm_ajson_filter . TYPES ty_filter_tab TYPE STANDARD TABLE OF REF TO /apmg/if_apm_ajson_filter WITH KEY table_line. TYPES ty_visit_type TYPE i. CONSTANTS: BEGIN OF visit_type, value TYPE ty_visit_type VALUE 0, open TYPE ty_visit_type VALUE 1, close TYPE ty_visit_type VALUE 2, END OF visit_type. METHODS keep_node IMPORTING is_node TYPE /apmg/if_apm_ajson_types=>ty_node iv_visit TYPE ty_visit_type DEFAULT visit_type-value RETURNING VALUE(rv_keep) TYPE abap_bool RAISING /apmg/cx_apm_ajson_error. ENDINTERFACE. INTERFACE /apmg/if_apm_ajson_mapping . TYPES: BEGIN OF ty_mapping_field, " deprecated, will be removed abap TYPE string, json TYPE string, END OF ty_mapping_field, ty_mapping_fields TYPE STANDARD TABLE OF ty_mapping_field WITH UNIQUE SORTED KEY abap COMPONENTS abap WITH UNIQUE SORTED KEY json COMPONENTS json. TYPES: BEGIN OF ty_rename, from TYPE string, to TYPE string, END OF ty_rename, tty_rename_map TYPE STANDARD TABLE OF ty_rename WITH UNIQUE SORTED KEY by_name COMPONENTS from. TYPES: ty_table_of TYPE STANDARD TABLE OF REF TO /apmg/if_apm_ajson_mapping. METHODS to_abap " deprecated, will be removed IMPORTING !iv_path TYPE string !iv_name TYPE string RETURNING VALUE(rv_result) TYPE string. METHODS to_json " deprecated, will be removed IMPORTING !iv_path TYPE string !iv_name TYPE string RETURNING VALUE(rv_result) TYPE string. METHODS rename_node IMPORTING !is_node TYPE /apmg/if_apm_ajson_types=>ty_node CHANGING !cv_name TYPE /apmg/if_apm_ajson_types=>ty_node-name. ENDINTERFACE. INTERFACE /apmg/if_apm_ajson_refs_init . TYPES: BEGIN OF ty_data_ref, path TYPE string, name TYPE string, dref TYPE REF TO data, END OF ty_data_ref, tty_data_refs TYPE STANDARD TABLE OF ty_data_ref WITH UNIQUE SORTED KEY by_path COMPONENTS path name. METHODS get_data_ref IMPORTING !is_node TYPE /apmg/if_apm_ajson_types=>ty_node RETURNING VALUE(ro_ref) TYPE REF TO data. ENDINTERFACE. INTERFACE /apmg/if_apm_ajson_ref_initial . TYPES: BEGIN OF ty_data_ref, path TYPE string, name TYPE string, dref TYPE REF TO data, END OF ty_data_ref. TYPES: tty_data_refs TYPE STANDARD TABLE OF ty_data_ref WITH UNIQUE SORTED KEY by_path COMPONENTS path name. METHODS get_data_ref IMPORTING !is_node TYPE /apmg/if_apm_ajson_types=>ty_node RETURNING VALUE(ro_ref) TYPE REF TO data. ENDINTERFACE. INTERFACE /apmg/if_apm_arborist . ************************************************************************ * Arborist * * Copyright 2025 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * Similar to @npmcli/arborist * * https://www.npmjs.com/package/@npmcli/arborist ************************************************************************ CONSTANTS c_version TYPE string VALUE '1.0.0' ##NEEDED. TYPES: ty_dependency_type TYPE string, ty_error_type TYPE string. CONSTANTS: BEGIN OF c_dependency_type, prod TYPE ty_dependency_type VALUE 'prod', dev TYPE ty_dependency_type VALUE 'dev', optional TYPE ty_dependency_type VALUE 'optional', peer TYPE ty_dependency_type VALUE 'peer', END OF c_dependency_type. CONSTANTS: BEGIN OF c_error_type, detached TYPE ty_error_type VALUE 'DETACHED', missing TYPE ty_error_type VALUE 'MISSING', peer_local TYPE ty_error_type VALUE 'PEER LOCAL', invalid TYPE ty_error_type VALUE 'INVALID', END OF c_error_type. TYPES: ty_node_ref TYPE REF TO /apmg/cl_apm_arborist_node, ty_node_refs TYPE STANDARD TABLE OF ty_node_ref WITH KEY table_line. TYPES: "! Log entry for tree issues BEGIN OF ty_log_entry, type TYPE string, message TYPE string, name TYPE string, version TYPE string, spec TYPE string, END OF ty_log_entry, ty_log TYPE STANDARD TABLE OF ty_log_entry WITH EMPTY KEY. CONSTANTS: BEGIN OF c_log_type, info TYPE string VALUE 'INFO', warning TYPE string VALUE 'WARNING', error TYPE string VALUE 'ERROR', circular TYPE string VALUE 'CIRCULAR', depth TYPE string VALUE 'DEPTH', END OF c_log_type. " READING "! Reads the installed packages and builds the actual tree METHODS load_actual_tree RETURNING VALUE(result) TYPE ty_node_refs. "! Read just what the package-lock.abap.json says (FUTURE) METHODS load_virtual_tree. " OPTIMIZING AND DESIGNING "! Build an ideal tree from package.abap.json and various lockfiles METHODS build_ideal_tree. " WRITING "! Make the idealTree be the thing that's persisted METHODS reify_tree. "! Get the log of issues found during tree building METHODS get_log RETURNING VALUE(result) TYPE ty_log. "! Get all nodes in the tree METHODS get_tree RETURNING VALUE(result) TYPE ty_node_refs. ENDINTERFACE. INTERFACE /apmg/if_apm_types . ************************************************************************ * apm Types * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * Schema for package.abap.json * * The definition of package.json for apm is closely aligned with npm * but there are differences! Several fields are not included while * others have been added. * * https://docs.npmjs.com/cli/v10/configuring-npm/package-json ************************************************************************ CONSTANTS c_version TYPE string VALUE '1.0.0' ##NO_TEXT. " Maximum key length to allow transporting entries CONSTANTS c_max_key_len TYPE i VALUE 120. TYPES: "! Key for DB persistence ty_key TYPE c LENGTH c_max_key_len ##NEEDED, "! SAP package (always upper case) ty_devclass TYPE devclass, "! Name of package in registry (always lower case) ty_name TYPE string, "! Semantic version of package ty_version TYPE string, "! Semantic versions of package ty_versions TYPE STANDARD TABLE OF ty_version WITH KEY table_line ##NEEDED, "! Package specification (version, range, tag name, git url, or tarball URL) ty_spec TYPE string, "! Email ty_email TYPE string, "! URI ty_uri TYPE string, "! Person BEGIN OF ty_person, name TYPE string, url TYPE ty_uri, email TYPE ty_email, avatar TYPE ty_uri, END OF ty_person, "! List of persons ty_persons TYPE STANDARD TABLE OF ty_person WITH KEY name url email, "! Dependency with semver range BEGIN OF ty_dependency, key TYPE string, range TYPE ty_spec, END OF ty_dependency, "! List of dependencies ty_dependencies TYPE STANDARD TABLE OF ty_dependency WITH KEY key, "! Bundle dependencies (version is defined in prod/dev dependencies) ty_bundle_dependencies TYPE string_table, "! Generic key value pair BEGIN OF ty_generic, key TYPE string, value TYPE string, END OF ty_generic, ty_dist_tags TYPE STANDARD TABLE OF ty_generic WITH KEY key, "! Timestamp BEGIN OF ty_time, key TYPE string, timestamp TYPE timestampl, END OF ty_time, ty_times TYPE STANDARD TABLE OF ty_time WITH KEY key, "! Signature BEGIN OF ty_signature, keyid TYPE string, sig TYPE string, END OF ty_signature, "! User Rating BEGIN OF ty_user, name TYPE string, stars TYPE i, END OF ty_user, ty_users TYPE STANDARD TABLE OF ty_user WITH KEY name, "! Bugs (Issues) BEGIN OF ty_bugs, url TYPE ty_uri, email TYPE ty_email, END OF ty_bugs, "! Repository BEGIN OF ty_repository, type TYPE string, url TYPE ty_uri, directory TYPE string, END OF ty_repository, "! Funding BEGIN OF ty_funding, type TYPE string, url TYPE ty_uri, END OF ty_funding, "! Dist details BEGIN OF ty_dist, file_count TYPE i, shasum TYPE string, tarball TYPE string, unpacked_size TYPE i, integrity TYPE string, signatures TYPE STANDARD TABLE OF ty_signature WITH KEY keyid, END OF ty_dist, "! SAP package BEGIN OF ty_sap_package, default TYPE ty_devclass, software_component TYPE dlvunit, abap_language_version TYPE string, END OF ty_sap_package. " *** PACKAGE.ABAP.JSON *** TYPES: "! Schema for package.abap.json "! Everything but "icon" and "devclass" is also in regular npm package.json BEGIN OF ty_package_json, name TYPE ty_name, version TYPE ty_version, description TYPE string, type TYPE string, keywords TYPE string_table, homepage TYPE string, icon TYPE string, bugs TYPE ty_bugs, license TYPE string, author TYPE ty_person, contributors TYPE ty_persons, maintainers TYPE ty_persons, main TYPE string, man TYPE string_table, repository TYPE ty_repository, funding TYPE ty_funding, dependencies TYPE ty_dependencies, dev_dependencies TYPE ty_dependencies, optional_dependencies TYPE ty_dependencies, peer_dependencies TYPE ty_dependencies, bundle_dependencies TYPE ty_bundle_dependencies, engines TYPE ty_dependencies, os TYPE string_table, cpu TYPE string_table, db TYPE string_table, private TYPE abap_bool, readme TYPE string, sap_package TYPE ty_sap_package, END OF ty_package_json. " *** MANIFEST *** "! Full manifest "! "! fetched with "accept: application/json" in HTTP headers TYPES BEGIN OF ty_manifest. INCLUDE TYPE ty_package_json. TYPES: dist TYPE ty_dist, deprecated TYPE string, _id TYPE string, _abap_version TYPE string, _apm_version TYPE string, END OF ty_manifest. TYPES: "! Abbreviated manifest "! "! fetched with "accept: application/vnd.npm.install-v1+json" in the HTTP headers BEGIN OF ty_manifest_abbreviated ##NEEDED, name TYPE ty_name, version TYPE ty_version, dependencies TYPE ty_dependencies, dev_dependencies TYPE ty_dependencies, optional_dependencies TYPE ty_dependencies, peer_dependencies TYPE ty_dependencies, bundle_dependencies TYPE ty_bundle_dependencies, engines TYPE ty_dependencies, os TYPE string_table, cpu TYPE string_table, db TYPE string_table, dist TYPE ty_dist, deprecated TYPE string, END OF ty_manifest_abbreviated. " *** PACKUMENT *** TYPES: "! Version Manifest BEGIN OF ty_version_manifest, key TYPE string, manifest TYPE ty_manifest, END OF ty_version_manifest, ty_version_manifests TYPE STANDARD TABLE OF ty_version_manifest WITH KEY key. TYPES: "! Tarball Attachment BEGIN OF ty_attachment, key TYPE string, BEGIN OF tarball, content_type TYPE string, data TYPE string, length TYPE i, END OF tarball, END OF ty_attachment, ty_attachments TYPE STANDARD TABLE OF ty_attachment WITH KEY key. TYPES: "! Full packument (as fetched from registry) "! Some fields are hoisted from latest version to root BEGIN OF ty_packument ##NEEDED, name TYPE ty_name, description TYPE string, dist_tags TYPE ty_dist_tags, time TYPE ty_times, versions TYPE ty_version_manifests, maintainers TYPE ty_persons, readme TYPE string, users TYPE ty_users, homepage TYPE string, icon TYPE string, bugs TYPE ty_bugs, license TYPE string, keywords TYPE string_table, author TYPE ty_person, repository TYPE ty_repository, _id TYPE string, _rev TYPE string, _attachments TYPE ty_attachments, _objects TYPE string_table, access TYPE string, END OF ty_packument. CONSTANTS: "! Scope Specs "! apm is a bit more strict than npm BEGIN OF c_scope, min_length TYPE i VALUE 4, max_length TYPE i VALUE 214, regex TYPE string VALUE '^@[a-z0-9][a-z0-9\-]*$', END OF c_scope. CONSTANTS: "! Package Name Specs "! apm is a bit more strict than npm BEGIN OF c_package_name, min_length TYPE i VALUE 3, max_length TYPE i VALUE 214, regex TYPE string VALUE '^[a-z0-9][a-z0-9\-]*$', regex_with_scope TYPE string VALUE '^(?:@[a-z0-9][a-z0-9\-]*/)?[a-z0-9][a-z0-9\-]*$', END OF c_package_name. CONSTANTS: "! Package Manifest File BEGIN OF c_package_json_file, obj_name TYPE c LENGTH 7 VALUE 'package', sep1 TYPE c LENGTH 1 VALUE '.', obj_type TYPE c LENGTH 4 VALUE 'abap', sep2 TYPE c LENGTH 1 VALUE '.', extension TYPE c LENGTH 4 VALUE 'json', END OF c_package_json_file. "! Package Readme File CONSTANTS c_readme_file TYPE string VALUE 'README.md'. CONSTANTS: "! Package Types BEGIN OF c_package_type, common_abap TYPE string VALUE 'commonabap', module TYPE string VALUE 'module', END OF c_package_type. CONSTANTS: "! Supported Engines BEGIN OF c_engine, abap TYPE string VALUE 'abap', apm TYPE string VALUE 'apm', btp TYPE string VALUE 'btp', END OF c_engine. CONSTANTS: "! Most Common Licenses (https://spdx.org/licenses/) BEGIN OF c_license, agpl_3_0_only TYPE string VALUE 'AGPL-3.0-only', apache_2_0 TYPE string VALUE 'Apache-2.0', bsd_2_clause TYPE string VALUE 'BSD-2-Clause', bsd_3_clause TYPE string VALUE 'BSD-3-Clause', bsl_1_0 TYPE string VALUE 'BSL-1.0', cc0_1_0 TYPE string VALUE 'CC0-1.0', cddl_1_0 TYPE string VALUE 'CDDL-1.0', cddl_1_1 TYPE string VALUE 'CDDL-1.1', epl_1_0 TYPE string VALUE 'EPL-1.0', epl_2_0 TYPE string VALUE 'EPL-2.0', gpl_2_0_only TYPE string VALUE 'GPL-2.0-only', gpl_3_0_only TYPE string VALUE 'GPL-3.0-only', isc TYPE string VALUE 'ISC', lgpl_2_0_only TYPE string VALUE 'LGPL-2.0-only', lgpl_2_1_only TYPE string VALUE 'LGPL-2.1-only', lgpl_2_1_or_later TYPE string VALUE 'LGPL-2.1-or-later', lgpl_3_0_only TYPE string VALUE 'LGPL-3.0-only', lgpl_3_0_or_later TYPE string VALUE 'LGPL-3.0-or-later', mit TYPE string VALUE 'MIT', mpl_2_0 TYPE string VALUE 'MPL-2.0', ms_pl TYPE string VALUE 'MS-PL', fsl_1_1_alv2 TYPE string VALUE 'FSL-1.1-ALv2', fsl_1_1_mit TYPE string VALUE 'FSL-1.1-MIT', unlicensed TYPE string VALUE 'UNLICENSED', END OF c_license. CONSTANTS: "! Operating System Platforms BEGIN OF c_os, aix TYPE string VALUE 'aix', hp_ux TYPE string VALUE 'hp-ux', linux TYPE string VALUE 'linux', ms_windows TYPE string VALUE 'ms-windows', solaris TYPE string VALUE 'solaris', os_390 TYPE string VALUE 'os/390', os_400 TYPE string VALUE 'os/400', END OF c_os. CONSTANTS: "! Hardware Platforms BEGIN OF c_cpu, x86_64 TYPE string VALUE 'x86-64', power_pc TYPE string VALUE 'power-pc', sparc TYPE string VALUE 'sparc', END OF c_cpu. CONSTANTS: "! Database Platforms BEGIN OF c_db, db2 TYPE string VALUE 'db2', db400 TYPE string VALUE 'db400', db6 TYPE string VALUE 'db6', hdb TYPE string VALUE 'hdb', informix TYPE string VALUE 'informix', mssql TYPE string VALUE 'mssql', oracle TYPE string VALUE 'oracle', sap_db TYPE string VALUE 'sap-db', sybase TYPE string VALUE 'sybase', END OF c_db. CONSTANTS: "! ABAP Language Version (same as zif_abapgit_dot_abapgit) BEGIN OF c_abap_language_version, standard TYPE string VALUE 'standard', key_user TYPE string VALUE 'keyUser', cloud_development TYPE string VALUE 'cloudDevelopment', ignore TYPE string VALUE 'ignore', undefined TYPE string VALUE 'undefined', " any END OF c_abap_language_version. ENDINTERFACE. INTERFACE /apmg/if_apm_package_json . ************************************************************************ * Package JSON * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * Similar to @npmcli/package-json but with its own persistence * https://www.npmjs.com/package/@npmcli/package-json ************************************************************************ CONSTANTS c_version TYPE string VALUE '1.0.0' ##NEEDED. INTERFACES /apmg/if_apm_types. TYPES: ty_package_id TYPE n LENGTH 40, " numeric hash BEGIN OF ty_package, key TYPE /apmg/if_apm_types=>ty_key, package TYPE /apmg/if_apm_types=>ty_devclass, name TYPE /apmg/if_apm_types=>ty_package_json-name, version TYPE /apmg/if_apm_types=>ty_package_json-version, description TYPE /apmg/if_apm_types=>ty_package_json-description, type TYPE /apmg/if_apm_types=>ty_package_json-type, private TYPE /apmg/if_apm_types=>ty_package_json-private, changed_by TYPE as4user, changed_at TYPE string, changed_at_raw TYPE timestampl, bundle TYPE abap_bool, parent TYPE /apmg/if_apm_types=>ty_devclass, abap_language_version TYPE string, favorite TYPE abap_bool, " settings write_protected TYPE abap_bool, " settings labels TYPE string_table, " settings instance TYPE REF TO /apmg/if_apm_package_json, id TYPE ty_package_id, END OF ty_package, ty_packages TYPE STANDARD TABLE OF ty_package WITH NON-UNIQUE KEY primary_key COMPONENTS key WITH UNIQUE HASHED KEY package COMPONENTS package WITH NON-UNIQUE SORTED KEY name COMPONENTS name. METHODS get RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_package_json. METHODS get_json IMPORTING !is_complete TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. METHODS set IMPORTING !package_json TYPE /apmg/if_apm_types=>ty_package_json RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_package_json RAISING /apmg/cx_apm_error. METHODS set_json IMPORTING !json TYPE string RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_package_json RAISING /apmg/cx_apm_error. METHODS exists RETURNING VALUE(result) TYPE abap_bool. METHODS load RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_package_json RAISING /apmg/cx_apm_error. METHODS save RAISING /apmg/cx_apm_error. METHODS delete RAISING /apmg/cx_apm_error. METHODS is_valid RETURNING VALUE(result) TYPE abap_bool. " TODO: normalize " Intended for normalizing package.json in a modules tree. " https://www.npmjs.com/package/normalize-package-data " TODO: prepare " Like normalize but intended for preparing package.json for publish. " TODO: fix " Like normalize but intended for the apm pkg fix command. " TODO: update " Updates the contents of a package.json with the content provided. ENDINTERFACE. INTERFACE /apmg/if_apm_importer . ************************************************************************ * apm Importer * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ " Examples for class and interface regex matching " YCL_TEST -> $1 = CL, $2 = TEST " ZIF_TEST -> $1 = IF, $2 = TEST " /APMG/CL_TEST, $1 = CL, $2 = TEST " /APMG/IF_TEST, $1 = IF, $2 = TEST CONSTANTS c_default_import_rule TYPE string VALUE '(?:\/.+\/|Y|Z)(..)(.*)'. CONSTANTS: BEGIN OF c_action, none TYPE string VALUE 'none', add TYPE string VALUE 'add', remove TYPE string VALUE 'remove', update TYPE string VALUE 'update', END OF c_action. TYPES: BEGIN OF ty_dependency, name TYPE string, version TYPE string, " installed version package TYPE devclass, " location of installed version range TYPE string, " from dependencies{ } action TYPE string, END OF ty_dependency, ty_dependencies TYPE STANDARD TABLE OF ty_dependency WITH KEY name. TYPES: BEGIN OF ty_program, program TYPE progname, package TYPE devclass, END OF ty_program, ty_programs TYPE STANDARD TABLE OF ty_program WITH KEY program. TYPES: BEGIN OF ty_rule, old_object TYPE string, new_object TYPE string, target_package TYPE devclass, parent_package TYPE devclass, name TYPE string, version TYPE string, END OF ty_rule, ty_rules TYPE STANDARD TABLE OF ty_rule WITH KEY old_object new_object target_package. TYPES: BEGIN OF ty_package, name TYPE string, version TYPE string, source_package TYPE devclass, target_package TYPE devclass, parent_package TYPE devclass, END OF ty_package, ty_packages TYPE STANDARD TABLE OF ty_package WITH KEY name. TYPES: BEGIN OF ty_item, obj_type TYPE tadir-object, obj_name TYPE tadir-obj_name, package TYPE devclass, language TYPE sy-langu, END OF ty_item. TYPES: BEGIN OF ty_map_item, object_type TYPE tadir-object, old_object TYPE tadir-obj_name, new_object TYPE tadir-obj_name, source_package TYPE devclass, target_package TYPE devclass, name TYPE string, version TYPE string, END OF ty_map_item, ty_map TYPE STANDARD TABLE OF ty_map_item WITH KEY old_object. TYPES: ty_object_types TYPE RANGE OF tadir-object, ty_object_names TYPE RANGE OF tadir-obj_name, ty_code TYPE rswsourcet. ENDINTERFACE. INTERFACE /apmg/if_apm_popups . TYPES ty_char1 TYPE c LENGTH 1. TYPES ty_icon TYPE c LENGTH 30. TYPES: BEGIN OF ty_popup_position, start_column LIKE sy-cucol, start_row LIKE sy-curow, end_column LIKE sy-cucol, end_row LIKE sy-curow, END OF ty_popup_position. METHODS popup_search_help IMPORTING !iv_tab_field TYPE string RETURNING VALUE(rv_value) TYPE ddshretval-fieldval RAISING /apmg/cx_apm_error. METHODS popup_folder_logic RETURNING VALUE(rv_folder_logic) TYPE string RAISING /apmg/cx_apm_error. METHODS popup_to_confirm IMPORTING !iv_titlebar TYPE clike !iv_text_question TYPE clike !iv_text_button_1 TYPE clike DEFAULT 'Yes' !iv_icon_button_1 TYPE ty_icon DEFAULT space !iv_text_button_2 TYPE clike DEFAULT 'No' !iv_icon_button_2 TYPE ty_icon DEFAULT space !iv_default_button TYPE ty_char1 DEFAULT '1' !iv_display_cancel_button TYPE ty_char1 DEFAULT abap_true !iv_popup_type TYPE clike DEFAULT 'ICON_MESSAGE_QUESTION' RETURNING VALUE(rv_answer) TYPE ty_char1 RAISING /apmg/cx_apm_error. METHODS popup_to_create_package IMPORTING is_package_data TYPE zif_abapgit_sap_package=>ty_create OPTIONAL EXPORTING !es_package_data TYPE zif_abapgit_sap_package=>ty_create !ev_create TYPE abap_bool RAISING /apmg/cx_apm_error. METHODS popup_to_select_transport RETURNING VALUE(rv_trkorr) TYPE trkorr. METHODS popup_to_select_labels IMPORTING iv_labels TYPE string OPTIONAL RETURNING VALUE(rv_labels) TYPE string RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_file_importer . ************************************************************************ * apm Files for Importer * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ METHODS get_abap IMPORTING !extra TYPE string OPTIONAL RETURNING VALUE(result) TYPE string_table RAISING /apmg/cx_apm_error. METHODS get_xml RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. METHODS get_xml_parsed RETURNING VALUE(result) TYPE REF TO zif_abapgit_xml_input RAISING /apmg/cx_apm_error. METHODS get_json RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_object . ************************************************************************ * apm Import Interface for Objects * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ TYPES ty_item TYPE /apmg/if_apm_importer=>ty_item. METHODS import IMPORTING !new_package TYPE ty_item-package !new_object TYPE ty_item-obj_name !language TYPE ty_item-language !map TYPE /apmg/if_apm_importer=>ty_map !files TYPE REF TO /apmg/if_apm_file_importer OPTIONAL !is_dry_run TYPE abap_bool DEFAULT abap_true !is_production TYPE abap_bool DEFAULT abap_true RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_frontend_services . TYPES: ty_char1 TYPE c LENGTH 1, ty_gui_release TYPE n LENGTH 4, ty_gui_sp TYPE n LENGTH 2, ty_gui_patch TYPE n LENGTH 2. METHODS file_upload IMPORTING !iv_path TYPE string RETURNING VALUE(rv_xstr) TYPE xstring RAISING /apmg/cx_apm_error. METHODS file_download IMPORTING !iv_path TYPE string !iv_xstr TYPE xstring RAISING /apmg/cx_apm_error . METHODS show_file_save_dialog IMPORTING !iv_title TYPE string !iv_extension TYPE string !iv_default_filename TYPE string RETURNING VALUE(rv_path) TYPE string RAISING /apmg/cx_apm_error. METHODS show_file_open_dialog IMPORTING !iv_title TYPE string !iv_extension TYPE string !iv_default_filename TYPE string RETURNING VALUE(rv_path) TYPE string RAISING /apmg/cx_apm_error. METHODS clipboard_export IMPORTING iv_no_auth_check TYPE abap_bool DEFAULT abap_false VALUE(it_data) TYPE STANDARD TABLE RAISING /apmg/cx_apm_error. "#EC CI_VALPAR METHODS execute IMPORTING !iv_document TYPE string OPTIONAL !iv_application TYPE string OPTIONAL !iv_parameter TYPE string OPTIONAL !iv_default_directory TYPE string OPTIONAL !iv_maximized TYPE string OPTIONAL !iv_minimized TYPE string OPTIONAL !iv_synchronous TYPE string OPTIONAL !iv_operation TYPE string DEFAULT 'OPEN' RAISING /apmg/cx_apm_error. METHODS get_system_directory CHANGING !cv_system_directory TYPE string RAISING /apmg/cx_apm_error. METHODS directory_browse IMPORTING iv_window_title TYPE string OPTIONAL iv_initial_folder TYPE string OPTIONAL CHANGING cv_selected_folder TYPE string RAISING /apmg/cx_apm_error. METHODS get_file_separator CHANGING cv_file_separator TYPE ty_char1 RAISING /apmg/cx_apm_error. METHODS get_gui_version EXPORTING ev_gui_release TYPE ty_gui_release ev_gui_sp TYPE ty_gui_sp ev_gui_patch TYPE ty_gui_patch ev_gui_version_string TYPE string RAISING /apmg/cx_apm_error. METHODS directory_exist IMPORTING iv_directory TYPE string RETURNING VALUE(rv_exists) TYPE abap_bool RAISING /apmg/cx_apm_error. METHODS directory_create IMPORTING iv_directory TYPE string CHANGING cv_rc TYPE i RAISING /apmg/cx_apm_error. METHODS gui_is_available RETURNING VALUE(rv_gui_is_available) TYPE abap_bool. METHODS is_sapgui_for_java RETURNING VALUE(rv_result) TYPE abap_bool. METHODS is_sapgui_for_windows RETURNING VALUE(rv_result) TYPE abap_bool. METHODS is_webgui RETURNING VALUE(rv_is_webgui) TYPE abap_bool. METHODS open_ie_devtools RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_gui_services . METHODS cache_asset IMPORTING !iv_text TYPE string OPTIONAL !iv_xdata TYPE xstring OPTIONAL !iv_url TYPE string OPTIONAL !iv_type TYPE c !iv_subtype TYPE c RETURNING VALUE(rv_url) TYPE string RAISING /apmg/cx_apm_error. " Notes: " - page_asset is supposed to be not cacheable " - add mime64 if needed (supposedly won't be needed) METHODS register_page_asset IMPORTING !iv_url TYPE string !iv_type TYPE string !iv_mime_name TYPE wwwdatatab-objid OPTIONAL !iv_inline TYPE string OPTIONAL RAISING /apmg/cx_apm_error. METHODS register_event_handler IMPORTING !ii_event_handler TYPE REF TO /apmg/if_apm_gui_event_handler. METHODS get_current_page_name RETURNING VALUE(rv_page_name) TYPE string. METHODS get_hotkeys_ctl RETURNING VALUE(ri_hotkey_ctl) TYPE REF TO /apmg/if_apm_gui_hotkey_ctl. METHODS get_html_parts RETURNING VALUE(ro_parts) TYPE REF TO /apmg/cl_apm_html_parts. METHODS get_log IMPORTING !iv_create_new TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ri_log) TYPE REF TO zif_abapgit_log. ENDINTERFACE. INTERFACE /apmg/if_apm_html_viewer . TYPES: ty_char256 TYPE c LENGTH 256, ty_post_data TYPE STANDARD TABLE OF ty_char256 WITH DEFAULT KEY, BEGIN OF ty_name_value, name TYPE c LENGTH 30, value TYPE c LENGTH 250, END OF ty_name_value, ty_query_table TYPE STANDARD TABLE OF ty_name_value WITH DEFAULT KEY. CONSTANTS c_id_sapevent TYPE i VALUE 1 ##NO_TEXT. EVENTS sapevent EXPORTING VALUE(action) TYPE c OPTIONAL VALUE(frame) TYPE c OPTIONAL VALUE(getdata) TYPE c OPTIONAL VALUE(postdata) TYPE ty_post_data OPTIONAL VALUE(query_table) TYPE ty_query_table OPTIONAL. METHODS load_data IMPORTING !iv_url TYPE string OPTIONAL !iv_type TYPE c DEFAULT 'text' !iv_subtype TYPE c DEFAULT 'html' !iv_size TYPE i DEFAULT 0 EXPORTING !ev_assigned_url TYPE string CHANGING !ct_data_table TYPE STANDARD TABLE RAISING /apmg/cx_apm_error. METHODS set_registered_events IMPORTING !it_events TYPE cntl_simple_events RAISING /apmg/cx_apm_error. METHODS show_url IMPORTING !iv_url TYPE string RAISING /apmg/cx_apm_error. METHODS free. METHODS close_document. METHODS get_url RETURNING VALUE(rv_url) TYPE string. METHODS back. METHODS set_visiblity IMPORTING !iv_visible TYPE abap_bool. METHODS set_focus RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE zif_abapgit_log . CONSTANTS: BEGIN OF c_status, ok TYPE sy-msgty VALUE 'S', error TYPE sy-msgty VALUE 'E', warning TYPE sy-msgty VALUE 'W', END OF c_status. CONSTANTS: BEGIN OF c_log_level, empty TYPE i VALUE 0, info TYPE i VALUE 1, warning TYPE i VALUE 2, error TYPE i VALUE 3, END OF c_log_level. TYPES: BEGIN OF ty_log_out, type TYPE sy-msgty, id TYPE sy-msgid, number TYPE sy-msgno, text TYPE string, obj_type TYPE tadir-object, obj_name TYPE tadir-obj_name, exception TYPE REF TO cx_root, END OF ty_log_out . TYPES: ty_log_outs TYPE STANDARD TABLE OF ty_log_out WITH NON-UNIQUE DEFAULT KEY . TYPES: BEGIN OF ty_msg, text TYPE string, type TYPE sy-msgty, id TYPE sy-msgid, number TYPE sy-msgno, level TYPE i, END OF ty_msg . TYPES: ty_msgs TYPE STANDARD TABLE OF ty_msg WITH NON-UNIQUE DEFAULT KEY . TYPES: BEGIN OF ty_item_status_out, item TYPE zif_abapgit_definitions=>ty_item, status TYPE sy-msgty, messages TYPE ty_msgs, END OF ty_item_status_out . TYPES: ty_item_status_outs TYPE SORTED TABLE OF ty_item_status_out WITH UNIQUE KEY item-obj_type item-obj_name . METHODS add IMPORTING !iv_msg TYPE csequence !iv_type TYPE sy-msgty DEFAULT 'E' !iv_class TYPE sy-msgid OPTIONAL !iv_number TYPE sy-msgno OPTIONAL !is_item TYPE zif_abapgit_definitions=>ty_item OPTIONAL !ix_exc TYPE REF TO cx_root OPTIONAL . METHODS add_error IMPORTING !iv_msg TYPE csequence !is_item TYPE zif_abapgit_definitions=>ty_item OPTIONAL . METHODS add_info IMPORTING !iv_msg TYPE csequence !is_item TYPE zif_abapgit_definitions=>ty_item OPTIONAL . METHODS add_warning IMPORTING !iv_msg TYPE csequence !is_item TYPE zif_abapgit_definitions=>ty_item OPTIONAL . METHODS add_success IMPORTING !iv_msg TYPE csequence !is_item TYPE zif_abapgit_definitions=>ty_item OPTIONAL . METHODS add_exception IMPORTING !ix_exc TYPE REF TO cx_root !is_item TYPE zif_abapgit_definitions=>ty_item OPTIONAL . METHODS clear . METHODS count RETURNING VALUE(rv_count) TYPE i . METHODS get_messages RETURNING VALUE(rt_msg) TYPE ty_log_outs . METHODS get_item_status RETURNING VALUE(rt_item_status) TYPE ty_item_status_outs . METHODS get_status RETURNING VALUE(rv_status) TYPE sy-msgty . METHODS get_log_level RETURNING VALUE(rv_level) TYPE i . METHODS get_title RETURNING VALUE(rv_title) TYPE string . METHODS set_title IMPORTING !iv_title TYPE csequence RETURNING VALUE(ri_log) TYPE REF TO zif_abapgit_log. METHODS merge_with IMPORTING ii_log TYPE REF TO zif_abapgit_log iv_min_level TYPE i DEFAULT 0 RETURNING VALUE(ri_log) TYPE REF TO zif_abapgit_log. METHODS clone RETURNING VALUE(ri_log) TYPE REF TO zif_abapgit_log. ENDINTERFACE. INTERFACE /apmg/if_apm_gui_event_handler . TYPES: BEGIN OF ty_handling_result, page TYPE REF TO /apmg/if_apm_gui_renderable, state TYPE i, END OF ty_handling_result. METHODS on_event IMPORTING !ii_event TYPE REF TO /apmg/if_apm_gui_event RETURNING VALUE(rs_handled) TYPE ty_handling_result RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_gui_event . DATA mv_action TYPE string READ-ONLY. DATA mv_getdata TYPE string READ-ONLY. DATA mt_postdata TYPE /apmg/if_apm_html_viewer=>ty_post_data READ-ONLY. DATA mi_gui_services TYPE REF TO /apmg/if_apm_gui_services READ-ONLY. DATA mv_current_page_name TYPE string. METHODS query RETURNING VALUE(ro_string_map) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS form_data RETURNING VALUE(ro_string_map) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_gui_asset_manager . TYPES: BEGIN OF ty_web_asset, url TYPE string, type TYPE c LENGTH 50, subtype TYPE c LENGTH 50, content TYPE xstring, is_cacheable TYPE abap_bool, END OF ty_web_asset, ty_web_assets TYPE STANDARD TABLE OF ty_web_asset WITH DEFAULT KEY. METHODS get_all_assets RETURNING VALUE(rt_assets) TYPE ty_web_assets RAISING /apmg/cx_apm_error. METHODS get_asset IMPORTING !iv_url TYPE string RETURNING VALUE(rs_asset) TYPE ty_web_asset RAISING /apmg/cx_apm_error. METHODS get_text_asset IMPORTING !iv_url TYPE string !iv_assert_subtype TYPE string OPTIONAL RETURNING VALUE(rv_asset) TYPE string RAISING /apmg/cx_apm_error. METHODS register_asset IMPORTING !iv_url TYPE string !iv_type TYPE string !iv_cacheable TYPE abap_bool DEFAULT abap_true !iv_mime_name TYPE wwwdatatab-objid OPTIONAL !iv_base64 TYPE string OPTIONAL !iv_inline TYPE string OPTIONAL RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_gui_hotkeys . TYPES: BEGIN OF ty_hotkey_with_descr, ui_component TYPE string, action TYPE string, hotkey TYPE string, description TYPE string, END OF ty_hotkey_with_descr, ty_hotkeys_with_descr TYPE STANDARD TABLE OF ty_hotkey_with_descr WITH DEFAULT KEY WITH UNIQUE SORTED KEY action COMPONENTS ui_component action. METHODS get_hotkey_actions RETURNING VALUE(rt_hotkey_actions) TYPE ty_hotkeys_with_descr RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_gui_hotkey_ctl . METHODS register_hotkeys IMPORTING !it_hotkeys TYPE /apmg/if_apm_gui_hotkeys=>ty_hotkeys_with_descr. METHODS reset. METHODS get_registered_hotkeys RETURNING VALUE(rt_registered_hotkeys) TYPE /apmg/if_apm_gui_hotkeys=>ty_hotkeys_with_descr RAISING /apmg/cx_apm_error. METHODS set_visible IMPORTING !iv_visible TYPE abap_bool. ENDINTERFACE. INTERFACE /apmg/if_apm_gui_renderable . METHODS render RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_persist_apm . ************************************************************************ * apm Persistence * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ CONSTANTS c_version TYPE string VALUE '1.0.0' ##NEEDED. " Maximum key length to allow transporting entries CONSTANTS c_max_key_len TYPE i VALUE 120. TYPES: ty_key TYPE c LENGTH c_max_key_len, ty_value TYPE string, BEGIN OF ty_zabappm, keys TYPE ty_key, value TYPE ty_value, luser TYPE as4user, timestamp TYPE timestampl, END OF ty_zabappm, BEGIN OF ty_list_item, keys TYPE ty_key, key_type TYPE string, key_name TYPE string, key_extra TYPE string, value TYPE ty_value, user TYPE as4user, timestamp TYPE timestampl, END OF ty_list_item, ty_list TYPE SORTED TABLE OF ty_list_item WITH UNIQUE KEY keys. TYPES: BEGIN OF ty_explained, key_type TYPE string, description TYPE string, extra TYPE string, content_type TYPE string, END OF ty_explained. CONSTANTS: c_zapm TYPE tadir-object VALUE 'ZAPM', c_devclass TYPE c LENGTH 30 VALUE '$TMP', c_transaction TYPE c LENGTH 30 VALUE 'ZAPM', c_tabname TYPE c LENGTH 30 VALUE 'ZABAPPM', c_lock TYPE c LENGTH 30 VALUE 'EZABAPPM', c_english TYPE c LENGTH 1 VALUE 'E'. CONSTANTS: BEGIN OF c_key_type, package TYPE ty_key VALUE 'PACKAGE', settings TYPE ty_key VALUE 'SETTINGS', packument TYPE ty_key VALUE 'PACKUMENT', END OF c_key_type, BEGIN OF c_key_name, global_settings TYPE ty_key VALUE '$GLOBAL$', user_settings TYPE ty_key VALUE '$USER$', END OF c_key_name, BEGIN OF c_key_extra, package_json TYPE ty_key VALUE 'PACKAGE_JSON', package_readme TYPE ty_key VALUE 'README', package_bundles TYPE ty_key VALUE 'BUNDLES', END OF c_key_extra. CONSTANTS: BEGIN OF c_content_type, json TYPE string VALUE 'json', markdown TYPE string VALUE 'markdown', text TYPE string VALUE 'text', END OF c_content_type. METHODS list IMPORTING !filter TYPE ty_key OPTIONAL !from TYPE timestampl DEFAULT 0 !to TYPE timestampl DEFAULT 99991231000000 PREFERRED PARAMETER filter RETURNING VALUE(result) TYPE ty_list. METHODS load IMPORTING !key TYPE ty_key RETURNING VALUE(result) TYPE ty_zabappm RAISING /apmg/cx_apm_error. METHODS save IMPORTING !key TYPE ty_key !value TYPE ty_value RAISING /apmg/cx_apm_error. METHODS delete IMPORTING !key TYPE ty_key RAISING /apmg/cx_apm_error. METHODS lock IMPORTING !key TYPE ty_key !mode TYPE enqmode DEFAULT 'E' RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_gui_html_processo . METHODS process IMPORTING !iv_html TYPE string !ii_gui_services TYPE REF TO /apmg/if_apm_gui_services RETURNING VALUE(rv_html) TYPE string RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_settings . ************************************************************************ * apm Settings * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ CONSTANTS c_version TYPE string VALUE '1.0.0' ##NO_TEXT. TYPES: BEGIN OF ty_gui_settings, adt_jump_enabled TYPE abap_bool, activate_wo_popup TYPE abap_bool, max_lines TYPE i, icon_scaling TYPE c LENGTH 1, ui_theme TYPE string, label_colors TYPE string, END OF ty_gui_settings, BEGIN OF ty_keyboard_settings, link_hints_enabled TYPE abap_bool, link_hint_key TYPE c LENGTH 1, END OF ty_keyboard_settings, BEGIN OF ty_list_settings, filter TYPE string, only_favorites TYPE abap_bool, " show_details TYPE abap_bool, " uses JS windows.localStorage order_by TYPE string, order_descending TYPE abap_bool, END OF ty_list_settings, BEGIN OF ty_package_settings, package TYPE devclass, favorite TYPE abap_bool, write_protected TYPE abap_bool, labels TYPE string_table, END OF ty_package_settings. TYPES: BEGIN OF ty_settings, registry TYPE string, last_package TYPE devclass, show_last_package TYPE abap_bool, experimental_features TYPE string, gui_settings TYPE ty_gui_settings, keyboard_settings TYPE ty_keyboard_settings, list_settings TYPE ty_list_settings, package_settings TYPE SORTED TABLE OF ty_package_settings WITH UNIQUE KEY package, END OF ty_settings. TYPES ty_name TYPE syst_uname. CONSTANTS: c_registry TYPE string VALUE 'https://registry.abappm.com', c_playground TYPE string VALUE 'https://playground.abappm.com', c_global TYPE ty_name VALUE /apmg/if_apm_persist_apm=>c_key_name-global_settings, c_user TYPE ty_name VALUE /apmg/if_apm_persist_apm=>c_key_name-user_settings. METHODS get RETURNING VALUE(result) TYPE ty_settings. METHODS get_json IMPORTING !is_complete TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. METHODS set IMPORTING !settings TYPE ty_settings RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_settings RAISING /apmg/cx_apm_error. METHODS set_json IMPORTING !json TYPE string RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_settings RAISING /apmg/cx_apm_error. METHODS load RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_settings RAISING /apmg/cx_apm_error. METHODS save RAISING /apmg/cx_apm_error. METHODS delete RAISING /apmg/cx_apm_error. METHODS is_valid RETURNING VALUE(result) TYPE abap_bool. ENDINTERFACE. INTERFACE /apmg/if_apm_html . TYPES: BEGIN OF ty_data_attr, name TYPE string, value TYPE string, END OF ty_data_attr, ty_data_attrs TYPE STANDARD TABLE OF ty_data_attr WITH KEY name, ty_table_of TYPE STANDARD TABLE OF REF TO /apmg/if_apm_html WITH DEFAULT KEY. CONSTANTS: BEGIN OF c_action_type, sapevent TYPE c VALUE 'E', url TYPE c VALUE 'U', onclick TYPE c VALUE 'C', separator TYPE c VALUE 'S', dummy TYPE c VALUE '_', END OF c_action_type. CONSTANTS: BEGIN OF c_html_opt, strong TYPE c VALUE 'E', cancel TYPE c VALUE 'C', crossout TYPE c VALUE 'X', END OF c_html_opt. DATA mv_chunk_title TYPE string READ-ONLY. " Primarily for debug of postponed html parts METHODS set_title IMPORTING !iv_title TYPE string RETURNING VALUE(ri_self) TYPE REF TO /apmg/if_apm_html. METHODS add IMPORTING !ig_chunk TYPE any RETURNING VALUE(ri_self) TYPE REF TO /apmg/if_apm_html. METHODS render IMPORTING !iv_no_indent_jscss TYPE abap_bool DEFAULT abap_false !iv_no_line_breaks TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rv_html) TYPE string. METHODS is_empty RETURNING VALUE(rv_yes) TYPE abap_bool. METHODS add_a IMPORTING !iv_txt TYPE string !iv_act TYPE string !iv_query TYPE string OPTIONAL !iv_typ TYPE c DEFAULT c_action_type-sapevent !iv_opt TYPE clike OPTIONAL !iv_class TYPE string OPTIONAL !iv_id TYPE string OPTIONAL !iv_style TYPE string OPTIONAL !iv_title TYPE string OPTIONAL RETURNING VALUE(ri_self) TYPE REF TO /apmg/if_apm_html. METHODS add_checkbox IMPORTING !iv_id TYPE string !iv_checked TYPE abap_bool OPTIONAL RETURNING VALUE(ri_self) TYPE REF TO /apmg/if_apm_html. METHODS a IMPORTING !iv_txt TYPE string !iv_act TYPE string !iv_query TYPE string OPTIONAL !iv_typ TYPE c DEFAULT /apmg/if_apm_html=>c_action_type-sapevent !iv_opt TYPE clike OPTIONAL !iv_class TYPE string OPTIONAL !iv_id TYPE string OPTIONAL !iv_style TYPE string OPTIONAL !iv_title TYPE string OPTIONAL RETURNING VALUE(rv_str) TYPE string. METHODS icon IMPORTING !iv_name TYPE string !iv_hint TYPE string OPTIONAL !iv_class TYPE string OPTIONAL !iv_onclick TYPE string OPTIONAL RETURNING VALUE(rv_str) TYPE string. METHODS add_icon IMPORTING !iv_name TYPE string !iv_hint TYPE string OPTIONAL !iv_class TYPE string OPTIONAL !iv_onclick TYPE string OPTIONAL RETURNING VALUE(ri_self) TYPE REF TO /apmg/if_apm_html. METHODS wrap IMPORTING !iv_tag TYPE string !iv_content TYPE string OPTIONAL !ii_content TYPE REF TO /apmg/if_apm_html OPTIONAL !iv_id TYPE string OPTIONAL !iv_class TYPE string OPTIONAL !iv_hint TYPE string OPTIONAL !iv_format_single_line TYPE abap_bool DEFAULT abap_false !is_data_attr TYPE ty_data_attr OPTIONAL !it_data_attrs TYPE ty_data_attrs OPTIONAL RETURNING VALUE(ri_self) TYPE REF TO /apmg/if_apm_html. METHODS td IMPORTING !iv_content TYPE string OPTIONAL !ii_content TYPE REF TO /apmg/if_apm_html OPTIONAL !iv_id TYPE string OPTIONAL !iv_class TYPE string OPTIONAL !iv_hint TYPE string OPTIONAL !iv_format_single_line TYPE abap_bool DEFAULT abap_true !is_data_attr TYPE ty_data_attr OPTIONAL !it_data_attrs TYPE ty_data_attrs OPTIONAL PREFERRED PARAMETER iv_content RETURNING VALUE(ri_self) TYPE REF TO /apmg/if_apm_html. METHODS th IMPORTING !iv_content TYPE string OPTIONAL !ii_content TYPE REF TO /apmg/if_apm_html OPTIONAL !iv_id TYPE string OPTIONAL !iv_class TYPE string OPTIONAL !iv_hint TYPE string OPTIONAL !iv_format_single_line TYPE abap_bool DEFAULT abap_true !is_data_attr TYPE ty_data_attr OPTIONAL !it_data_attrs TYPE ty_data_attrs OPTIONAL PREFERRED PARAMETER iv_content RETURNING VALUE(ri_self) TYPE REF TO /apmg/if_apm_html. METHODS div IMPORTING !iv_content TYPE string OPTIONAL !ii_content TYPE REF TO /apmg/if_apm_html OPTIONAL !iv_id TYPE string OPTIONAL !iv_class TYPE string OPTIONAL !is_data_attr TYPE ty_data_attr OPTIONAL !it_data_attrs TYPE ty_data_attrs OPTIONAL PREFERRED PARAMETER iv_content RETURNING VALUE(ri_self) TYPE REF TO /apmg/if_apm_html. ENDINTERFACE. INTERFACE /apmg/if_apm_html_form . TYPES: BEGIN OF ty_subitem, label TYPE string, value TYPE string, readonly TYPE abap_bool, END OF ty_subitem, ty_subitems TYPE STANDARD TABLE OF ty_subitem WITH DEFAULT KEY. TYPES: BEGIN OF ty_field, type TYPE i, name TYPE string, label TYPE string, hint TYPE string, dblclick TYPE string, click TYPE string, placeholder TYPE string, required TYPE string, upper_case TYPE abap_bool, item_class TYPE string, error TYPE string, default_value TYPE string, side_action TYPE string, subitems TYPE ty_subitems, readonly TYPE abap_bool, password TYPE abap_bool, condense TYPE abap_bool, min TYPE i, max TYPE i, rows TYPE i, cols TYPE i, END OF ty_field, ty_fields TYPE STANDARD TABLE OF ty_field WITH DEFAULT KEY WITH UNIQUE SORTED KEY by_name COMPONENTS name. TYPES: BEGIN OF ty_command, label TYPE string, action TYPE string, cmd_type TYPE i, END OF ty_command. CONSTANTS c_rows TYPE string VALUE 'rows' ##NO_TEXT. CONSTANTS: BEGIN OF c_cmd_type, input TYPE i VALUE 1, input_main TYPE i VALUE 2, link TYPE i VALUE 3, button TYPE i VALUE 4, END OF c_cmd_type. CONSTANTS: BEGIN OF c_field_type, text TYPE i VALUE 1, radio TYPE i VALUE 2, checkbox TYPE i VALUE 3, field_group TYPE i VALUE 4, number TYPE i VALUE 5, textarea TYPE i VALUE 6, table TYPE i VALUE 7, hidden TYPE i VALUE 8, END OF c_field_type. ENDINTERFACE. INTERFACE /apmg/if_apm_html_table . TYPES: BEGIN OF ty_row_attrs, css_class TYPE string, data TYPE /apmg/if_apm_html=>ty_data_attr, END OF ty_row_attrs. TYPES: BEGIN OF ty_cell_render, css_class TYPE string, content TYPE string, html TYPE REF TO /apmg/if_apm_html, END OF ty_cell_render. TYPES: BEGIN OF ty_sorting_state, column_id TYPE string, descending TYPE abap_bool, END OF ty_sorting_state. METHODS get_row_attrs IMPORTING !iv_table_id TYPE string !iv_row_index TYPE i !is_row TYPE any RETURNING VALUE(rs_attrs) TYPE ty_row_attrs RAISING /apmg/cx_apm_error. METHODS render_cell IMPORTING !iv_table_id TYPE string !iv_row_index TYPE i !is_row TYPE any !iv_column_id TYPE string !iv_value TYPE any RETURNING VALUE(rs_render) TYPE ty_cell_render RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_http_agent . ************************************************************************ * HTTP Agent * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ CONSTANTS c_version TYPE string VALUE '1.0.1' ##NEEDED. CONSTANTS: BEGIN OF c_method, get TYPE string VALUE 'GET', post TYPE string VALUE 'POST', put TYPE string VALUE 'PUT', delete TYPE string VALUE 'DELETE', patch TYPE string VALUE 'PATCH', END OF c_method, BEGIN OF c_header, accept TYPE string VALUE 'accept', authorization TYPE string VALUE 'authorization', content_type TYPE string VALUE 'content-type', user_agent TYPE string VALUE 'user-agent', cookie TYPE string VALUE 'cookie', set_cookie TYPE string VALUE 'set-cookie', x_csrf_token TYPE string VALUE 'x-csrf-token', END OF c_header, BEGIN OF c_content_type, json TYPE string VALUE 'application/json', text TYPE string VALUE 'application/text', xml TYPE string VALUE 'application/xml', bin TYPE string VALUE 'application/octet-stream', END OF c_content_type. METHODS global_headers RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_string_map. METHODS request IMPORTING !url TYPE string !ssl_id TYPE ssfapplssl DEFAULT 'ANONYM' !method TYPE string DEFAULT c_method-get !query TYPE REF TO /apmg/cl_apm_string_map OPTIONAL !headers TYPE REF TO /apmg/cl_apm_string_map OPTIONAL !payload TYPE any OPTIONAL " can be char, string, xstring RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_http_response RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE zif_abapgit_oo_object_fnc . CONSTANTS: BEGIN OF c_parts, locals_def TYPE string VALUE 'locals_def', locals_imp TYPE string VALUE 'locals_imp', macros TYPE string VALUE 'macros', testclasses TYPE string VALUE 'testclasses', END OF c_parts. TYPES: BEGIN OF ty_includes, programm TYPE syrepid, END OF ty_includes, ty_includes_tt TYPE STANDARD TABLE OF ty_includes WITH DEFAULT KEY. TYPES: ty_seoclasstx_tt TYPE STANDARD TABLE OF seoclasstx WITH DEFAULT KEY . TYPES: ty_seocompotx_tt TYPE STANDARD TABLE OF seocompotx WITH DEFAULT KEY . TYPES: ty_seosubcotx_tt TYPE STANDARD TABLE OF seosubcotx WITH DEFAULT KEY . TYPES: BEGIN OF ty_obj_attribute, cmpname TYPE seocmpname, attkeyfld TYPE seokeyfld, attbusobj TYPE seobusobj, exposure TYPE seoexpose, END OF ty_obj_attribute . TYPES: ty_obj_attribute_tt TYPE STANDARD TABLE OF ty_obj_attribute WITH DEFAULT KEY WITH NON-UNIQUE SORTED KEY cmpname COMPONENTS cmpname . METHODS: create IMPORTING iv_check TYPE abap_bool iv_package TYPE devclass it_attributes TYPE ty_obj_attribute_tt OPTIONAL CHANGING cg_properties TYPE any RAISING zcx_abapgit_exception, generate_locals IMPORTING is_key TYPE seoclskey it_local_definitions TYPE seop_source_string OPTIONAL it_local_implementations TYPE seop_source_string OPTIONAL it_local_macros TYPE seop_source_string OPTIONAL it_local_test_classes TYPE seop_source_string OPTIONAL iv_package TYPE devclass iv_version TYPE uccheck RAISING zcx_abapgit_exception, deserialize_source IMPORTING is_key TYPE seoclskey it_source TYPE zif_abapgit_definitions=>ty_string_tt iv_package TYPE devclass iv_version TYPE uccheck RAISING zcx_abapgit_exception cx_sy_dyn_call_error, insert_text_pool IMPORTING iv_class_name TYPE seoclsname it_text_pool TYPE textpool_table iv_language TYPE spras iv_state TYPE c DEFAULT 'I' RAISING zcx_abapgit_exception, update_descriptions_class IMPORTING is_key TYPE seoclskey iv_language TYPE spras it_descriptions TYPE ty_seoclasstx_tt, update_descriptions_compo IMPORTING is_key TYPE seoclskey it_descriptions TYPE ty_seocompotx_tt, update_descriptions_subco IMPORTING is_key TYPE seoclskey it_descriptions TYPE ty_seosubcotx_tt, add_to_activation_list IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item RAISING zcx_abapgit_exception, create_sotr IMPORTING iv_object_name TYPE sobj_name iv_package TYPE devclass ii_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception, create_documentation IMPORTING it_lines TYPE tlinetab iv_id TYPE dokhl-id iv_object_name TYPE dokhl-object iv_language TYPE spras iv_no_masterlang TYPE abap_bool OPTIONAL RAISING zcx_abapgit_exception, delete_documentation IMPORTING iv_id TYPE dokhl-id iv_object_name TYPE dokhl-object iv_language TYPE spras RAISING zcx_abapgit_exception, get_includes IMPORTING iv_object_name TYPE sobj_name RETURNING VALUE(rt_includes) TYPE ty_includes_tt RAISING zcx_abapgit_exception, exists IMPORTING iv_object_name TYPE seoclsname RETURNING VALUE(rv_exists) TYPE abap_bool, serialize_abap IMPORTING is_class_key TYPE seoclskey iv_type TYPE seop_include_ext_app OPTIONAL RETURNING VALUE(rt_source) TYPE zif_abapgit_definitions=>ty_string_tt RAISING zcx_abapgit_exception cx_sy_dyn_call_error, get_skip_test_classes RETURNING VALUE(rv_skip) TYPE abap_bool, get_class_properties IMPORTING is_class_key TYPE seoclskey RETURNING VALUE(rs_class_properties) TYPE vseoclass RAISING zcx_abapgit_exception, get_interface_properties IMPORTING is_interface_key TYPE seoclskey RETURNING VALUE(rs_interface_properties) TYPE vseointerf RAISING zcx_abapgit_exception, read_text_pool IMPORTING iv_class_name TYPE seoclsname iv_language TYPE spras RETURNING VALUE(rt_text_pool) TYPE textpool_table, read_documentation IMPORTING iv_id TYPE dokhl-id iv_object_name TYPE dokhl-object iv_language TYPE spras RETURNING VALUE(rt_lines) TYPE tlinetab, read_sotr IMPORTING iv_object_name TYPE sobj_name ii_xml TYPE REF TO zif_abapgit_xml_output io_i18n_params TYPE REF TO zcl_abapgit_i18n_params RAISING zcx_abapgit_exception, read_descriptions_class IMPORTING iv_object_name TYPE seoclsname iv_language TYPE spras OPTIONAL RETURNING VALUE(rt_descriptions) TYPE ty_seoclasstx_tt, read_descriptions_compo IMPORTING iv_object_name TYPE seoclsname iv_language TYPE spras OPTIONAL RETURNING VALUE(rt_descriptions) TYPE ty_seocompotx_tt, read_descriptions_subco IMPORTING iv_object_name TYPE seoclsname iv_language TYPE spras OPTIONAL RETURNING VALUE(rt_descriptions) TYPE ty_seosubcotx_tt, delete IMPORTING is_deletion_key TYPE seoclskey RAISING zcx_abapgit_exception, read_superclass IMPORTING iv_classname TYPE seoclsname RETURNING VALUE(rv_superclass) TYPE seoclsname, read_attributes IMPORTING iv_object_name TYPE seoclsname RETURNING VALUE(rt_attributes) TYPE ty_obj_attribute_tt, syntax_check IMPORTING iv_object_name TYPE seoclsname RAISING zcx_abapgit_exception. ENDINTERFACE. INTERFACE zif_abapgit_sap_report . TYPES: BEGIN OF ty_progdir, name TYPE progdir-name, state TYPE progdir-state, sqlx TYPE progdir-sqlx, edtx TYPE progdir-edtx, varcl TYPE progdir-varcl, dbapl TYPE progdir-dbapl, dbna TYPE progdir-dbna, clas TYPE progdir-clas, type TYPE progdir-type, occurs TYPE progdir-occurs, subc TYPE progdir-subc, appl TYPE progdir-appl, secu TYPE progdir-secu, cnam TYPE progdir-cnam, cdat TYPE progdir-cdat, unam TYPE progdir-unam, udat TYPE progdir-udat, vern TYPE progdir-vern, levl TYPE progdir-levl, rstat TYPE progdir-rstat, rmand TYPE progdir-rmand, rload TYPE progdir-rload, fixpt TYPE progdir-fixpt, sset TYPE progdir-sset, sdate TYPE progdir-sdate, stime TYPE progdir-stime, idate TYPE progdir-idate, itime TYPE progdir-itime, ldbname TYPE progdir-ldbname, uccheck TYPE progdir-uccheck, END OF ty_progdir. METHODS read_report IMPORTING iv_name TYPE syrepid iv_state TYPE r3state OPTIONAL is_item TYPE zif_abapgit_definitions=>ty_item OPTIONAL RETURNING VALUE(rt_source) TYPE abaptxt255_tab RAISING zcx_abapgit_exception. METHODS insert_report IMPORTING iv_name TYPE syrepid it_source TYPE STANDARD TABLE iv_state TYPE r3state OPTIONAL iv_program_type TYPE c OPTIONAL iv_extension_type TYPE c OPTIONAL iv_package TYPE devclass iv_version TYPE uccheck is_item TYPE zif_abapgit_definitions=>ty_item OPTIONAL RAISING zcx_abapgit_exception. METHODS update_report IMPORTING iv_name TYPE syrepid it_source TYPE STANDARD TABLE iv_state TYPE r3state OPTIONAL iv_program_type TYPE c OPTIONAL iv_extension_type TYPE c OPTIONAL iv_package TYPE devclass iv_version TYPE uccheck is_item TYPE zif_abapgit_definitions=>ty_item OPTIONAL RETURNING VALUE(rv_updated) TYPE abap_bool RAISING zcx_abapgit_exception. METHODS delete_report IMPORTING iv_name TYPE syrepid iv_raise_error TYPE abap_bool DEFAULT abap_false is_item TYPE zif_abapgit_definitions=>ty_item OPTIONAL RAISING zcx_abapgit_exception. METHODS read_progdir IMPORTING iv_name TYPE syrepid iv_state TYPE r3state DEFAULT 'A' RETURNING VALUE(rs_progdir) TYPE ty_progdir RAISING zcx_abapgit_exception. METHODS update_progdir IMPORTING is_progdir TYPE ty_progdir iv_package TYPE devclass iv_state TYPE r3state DEFAULT 'I' RAISING zcx_abapgit_exception. ENDINTERFACE. INTERFACE /apmg/if_apm_pacote . ************************************************************************ * Pacote * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ CONSTANTS c_version TYPE string VALUE '1.0.0' ##NEEDED. TYPES: BEGIN OF ty_pacote, key TYPE /apmg/if_apm_persist_apm=>ty_key, name TYPE string, json TYPE string, packument TYPE /apmg/if_apm_types=>ty_packument, instance TYPE REF TO /apmg/if_apm_pacote, END OF ty_pacote, ty_pacotes TYPE STANDARD TABLE OF ty_pacote WITH KEY key ##NEEDED. METHODS get RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_packument. METHODS get_json RETURNING VALUE(result) TYPE string. METHODS get_version IMPORTING !version TYPE string RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_manifest. METHODS get_versions IMPORTING !with_deprecated TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_versions. METHODS set IMPORTING !packument TYPE /apmg/if_apm_types=>ty_packument RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_pacote RAISING /apmg/cx_apm_error. METHODS set_json IMPORTING !json TYPE string RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_pacote RAISING /apmg/cx_apm_error. METHODS exists RETURNING VALUE(result) TYPE abap_bool. METHODS load RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_pacote RAISING /apmg/cx_apm_error. METHODS save RAISING /apmg/cx_apm_error. METHODS delete RAISING /apmg/cx_apm_error. METHODS manifest IMPORTING version TYPE string abbreviated TYPE abap_bool DEFAULT abap_false write TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. METHODS packument IMPORTING write TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. METHODS tarball IMPORTING filename TYPE string RETURNING VALUE(result) TYPE xstring RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_progress_bar . METHODS show IMPORTING !current TYPE i !text TYPE csequence RAISING /apmg/cx_apm_error. METHODS set_total IMPORTING !total TYPE i. METHODS off RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_readme . ************************************************************************ * Readme * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ CONSTANTS c_version TYPE string VALUE '1.0.0' ##NEEDED. TYPES: BEGIN OF ty_readme, key TYPE /apmg/if_apm_persist_apm=>ty_key, markdown TYPE string, instance TYPE REF TO /apmg/if_apm_readme, END OF ty_readme, ty_readmes TYPE STANDARD TABLE OF ty_readme WITH KEY key ##NEEDED. METHODS get RETURNING VALUE(result) TYPE string. METHODS set IMPORTING !markdown TYPE string RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_readme RAISING /apmg/cx_apm_error. METHODS exists RETURNING VALUE(result) TYPE abap_bool. METHODS load RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_readme RAISING /apmg/cx_apm_error. METHODS save RAISING /apmg/cx_apm_error. METHODS delete RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_semver_options . ************************************************************************ * SemVer Options * * Copyright (c) Isaac Z. Schlueter and Contributors * Ported to ABAP by apm.to Inc. * SPDX-License-Identifier: ISC ************************************************************************ TYPES: BEGIN OF ty_options, loose TYPE abap_bool, incpre TYPE abap_bool, rtl TYPE abap_bool, END OF ty_options. ENDINTERFACE. INTERFACE zif_abapgit_cts_api . TYPES: BEGIN OF ty_transport, obj_type TYPE tadir-object, obj_name TYPE tadir-obj_name, trkorr TYPE trkorr, END OF ty_transport . TYPES: ty_transport_list TYPE SORTED TABLE OF ty_transport WITH NON-UNIQUE KEY obj_type obj_name . TYPES: ty_trkorr_tt TYPE STANDARD TABLE OF trkorr WITH DEFAULT KEY . TYPES: BEGIN OF ty_transport_key, object TYPE e071k-object, objname TYPE e071k-objname, tabkey TYPE e071k-tabkey, END OF ty_transport_key . TYPES: BEGIN OF ty_transport_data, trstatus TYPE e070-trstatus, as4date TYPE d, keys TYPE STANDARD TABLE OF ty_transport_key WITH DEFAULT KEY, as4user TYPE sy-uname, END OF ty_transport_data . TYPES: BEGIN OF ty_transport_obj, object TYPE e071-object, obj_name TYPE e071-obj_name, END OF ty_transport_obj . TYPES: ty_transport_obj_tt TYPE STANDARD TABLE OF ty_transport_obj WITH DEFAULT KEY . CONSTANTS: BEGIN OF c_transport_type, wb_request TYPE c LENGTH 1 VALUE 'K', "workbench request wb_repair TYPE c LENGTH 1 VALUE 'R', "workbench repair wb_task TYPE c LENGTH 1 VALUE 'S', "workbench task cust_request TYPE c LENGTH 1 VALUE 'W', "customizing request cust_task TYPE c LENGTH 1 VALUE 'Q', "customizing task END OF c_transport_type . CONSTANTS: BEGIN OF c_transport_category, workbench TYPE c LENGTH 4 VALUE 'SYST', customizing TYPE c LENGTH 4 VALUE 'CUST', END OF c_transport_category . CONSTANTS: BEGIN OF c_transport_mode, insert TYPE c LENGTH 1 VALUE 'I', delete TYPE c LENGTH 1 VALUE 'D', END OF c_transport_mode . CONSTANTS: BEGIN OF c_transport_status, modifiable TYPE c LENGTH 1 VALUE 'D', END OF c_transport_status . METHODS confirm_transport_messages RETURNING VALUE(rv_messages_confirmed) TYPE abap_bool . METHODS create_transport_entries IMPORTING !iv_transport TYPE trkorr !it_table_ins TYPE ANY TABLE OPTIONAL !it_table_upd TYPE ANY TABLE OPTIONAL !it_table_del TYPE ANY TABLE OPTIONAL !iv_tabname TYPE tabname RAISING zcx_abapgit_exception . METHODS get_r3tr_obj_for_limu_obj IMPORTING !iv_object TYPE tadir-object !iv_obj_name TYPE trobj_name EXPORTING !ev_object TYPE tadir-object !ev_obj_name TYPE trobj_name RAISING zcx_abapgit_exception . METHODS get_transports_for_list IMPORTING !it_items TYPE zif_abapgit_definitions=>ty_items_tt RETURNING VALUE(rt_transports) TYPE ty_transport_list RAISING zcx_abapgit_exception . "! Returns the transport request / task the object is currently in "! @parameter is_item | Object "! @parameter rv_transport | Transport request / task "! @raising zcx_abapgit_exception | Object is not in a transport METHODS get_transport_for_object IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item RETURNING VALUE(rv_transport) TYPE trkorr RAISING zcx_abapgit_exception . METHODS insert_transport_object IMPORTING !iv_pgmid TYPE tadir-pgmid DEFAULT 'R3TR' !iv_object TYPE tadir-object !iv_obj_name TYPE csequence !iv_package TYPE devclass !iv_transport TYPE trkorr OPTIONAL !iv_language TYPE sy-langu DEFAULT sy-langu !iv_mode TYPE c DEFAULT 'I' EXPORTING !ev_object TYPE tadir-object !ev_obj_name TYPE trobj_name RAISING zcx_abapgit_exception . "! Check if change recording is possible for the given package "! @parameter iv_package | Package "! @parameter rv_possible | Change recording is possible "! @raising zcx_abapgit_exception | Package could not be loaded METHODS is_chrec_possible_for_package IMPORTING !iv_package TYPE devclass RETURNING VALUE(rv_possible) TYPE abap_bool RAISING zcx_abapgit_exception . TYPES ty_date_range TYPE RANGE OF sy-datum. METHODS list_open_requests IMPORTING !it_date TYPE ty_date_range OPTIONAL RETURNING VALUE(rt_trkorr) TYPE ty_trkorr_tt RAISING zcx_abapgit_exception . METHODS list_r3tr_by_request IMPORTING !iv_request TYPE trkorr RETURNING VALUE(rt_list) TYPE ty_transport_obj_tt RAISING zcx_abapgit_exception . METHODS read IMPORTING !iv_trkorr TYPE trkorr RETURNING VALUE(rs_request) TYPE ty_transport_data RAISING zcx_abapgit_exception . METHODS read_description IMPORTING !iv_trkorr TYPE trkorr RETURNING VALUE(rv_description) TYPE string . METHODS read_user IMPORTING !iv_trkorr TYPE trkorr RETURNING VALUE(rv_uname) TYPE uname . METHODS validate_transport_request IMPORTING !iv_transport_request TYPE trkorr RAISING zcx_abapgit_exception . METHODS change_transport_type IMPORTING !iv_transport_request TYPE trkorr !iv_transport_type_from TYPE trfunction !iv_transport_type_to TYPE trfunction RAISING zcx_abapgit_exception. TYPES: BEGIN OF ty_request_and_tasks, trkorr TYPE trkorr, as4user TYPE sy-uname, END OF ty_request_and_tasks. TYPES: ty_request_and_tasks_tt TYPE STANDARD TABLE OF ty_request_and_tasks WITH DEFAULT KEY. METHODS read_request_and_tasks IMPORTING iv_request TYPE trkorr RETURNING VALUE(rt_tasks) TYPE ty_request_and_tasks_tt RAISING zcx_abapgit_exception. ENDINTERFACE. INTERFACE zif_abapgit_default_transport . TYPES: BEGIN OF ty_get, trfunction TYPE c LENGTH 1, ordernum TYPE trkorr, END OF ty_get. METHODS set IMPORTING iv_transport TYPE trkorr RAISING zcx_abapgit_exception. METHODS reset RAISING zcx_abapgit_exception. METHODS get RETURNING VALUE(rs_default_task) TYPE ty_get RAISING zcx_abapgit_exception . ENDINTERFACE. INTERFACE zif_abapgit_ecatt . " downport missing types TYPES: ty_invert_validation TYPE c LENGTH 1, ty_error_prio TYPE n LENGTH 1, ty_impl_name TYPE c LENGTH 30, ty_impl_type TYPE c LENGTH 1, ty_impl_subtype TYPE c LENGTH 4, ty_package TYPE c LENGTH 255, BEGIN OF ty_impl_det, impl_name TYPE ty_impl_name, impl_type TYPE ty_impl_type, impl_subtype TYPE ty_impl_subtype, impl_package TYPE ty_package, END OF ty_impl_det. TYPES: BEGIN OF ty_bus_msg. INCLUDE TYPE etobj_key. TYPES: bus_msg_no TYPE c LENGTH 1, " ty_msg_no arbgb TYPE arbgb, msgnr TYPE msgnr, bus_msg_text TYPE string, "ty_bus_msg_text otr_key TYPE sotr_conc, msg_type TYPE c LENGTH 4, "ty_msg_type END OF ty_bus_msg, ty_bus_msgs TYPE STANDARD TABLE OF ty_bus_msg. ENDINTERFACE. INTERFACE zif_abapgit_environment . TYPES: BEGIN OF ty_release_sp, release TYPE c LENGTH 10, sp TYPE c LENGTH 10, END OF ty_release_sp, ty_system_language_filter TYPE RANGE OF spras. METHODS is_sap_cloud_platform RETURNING VALUE(rv_result) TYPE abap_bool. METHODS is_merged RETURNING VALUE(rv_result) TYPE abap_bool. METHODS is_repo_object_changes_allowed RETURNING VALUE(rv_result) TYPE abap_bool. METHODS compare_with_inactive RETURNING VALUE(rv_result) TYPE abap_bool. METHODS is_restart_required RETURNING VALUE(rv_result) TYPE abap_bool. METHODS is_sap_object_allowed RETURNING VALUE(rv_allowed) TYPE abap_bool. METHODS get_basis_release RETURNING VALUE(rs_result) TYPE ty_release_sp. METHODS get_system_language_filter RETURNING VALUE(rt_system_language_filter) TYPE ty_system_language_filter. METHODS is_variant_maintenance RETURNING VALUE(rv_is_variant_maintenance) TYPE abap_bool. METHODS init_parallel_processing IMPORTING iv_group TYPE clike RETURNING VALUE(rv_free_work_processes) TYPE i. METHODS check_parallel_processing IMPORTING iv_group TYPE clike RETURNING VALUE(rv_checked) TYPE abap_bool. METHODS get_available_user_sessions RETURNING VALUE(rv_sessions) TYPE i. ENDINTERFACE. INTERFACE zif_abapgit_user_record . METHODS get_name IMPORTING iv_username TYPE sy-uname RETURNING VALUE(rv_name) TYPE string. METHODS get_email IMPORTING iv_username TYPE sy-uname RETURNING VALUE(rv_email) TYPE string. METHODS get_title IMPORTING iv_username TYPE sy-uname RETURNING VALUE(rv_title) TYPE string. ENDINTERFACE. INTERFACE zif_abapgit_longtexts . TYPES: BEGIN OF ty_longtext, dokil TYPE dokil, head TYPE thead, lines TYPE tline_tab, END OF ty_longtext . TYPES: ty_longtexts TYPE STANDARD TABLE OF ty_longtext WITH NON-UNIQUE DEFAULT KEY . METHODS changed_by IMPORTING !iv_object_name TYPE tadir-obj_name !iv_longtext_id TYPE dokil-id !it_dokil TYPE zif_abapgit_definitions=>ty_dokil_tt OPTIONAL RETURNING VALUE(rv_user) TYPE syuname RAISING zcx_abapgit_exception . METHODS serialize IMPORTING !iv_longtext_name TYPE string DEFAULT 'LONGTEXTS' !iv_object_name TYPE clike !iv_longtext_id TYPE dokil-id !it_dokil TYPE zif_abapgit_definitions=>ty_dokil_tt OPTIONAL !ii_xml TYPE REF TO zif_abapgit_xml_output !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params RETURNING VALUE(rt_longtexts) TYPE ty_longtexts RAISING zcx_abapgit_exception . METHODS deserialize IMPORTING !iv_longtext_name TYPE string DEFAULT 'LONGTEXTS' !iv_object_name TYPE clike !iv_longtext_id TYPE dokil-id !ii_xml TYPE REF TO zif_abapgit_xml_input !iv_main_language TYPE sy-langu RAISING zcx_abapgit_exception . METHODS delete IMPORTING !iv_object_name TYPE tadir-obj_name !iv_longtext_id TYPE dokil-id RAISING zcx_abapgit_exception . ENDINTERFACE. INTERFACE zif_abapgit_lxe_texts . * type LXE_PCX_S1 inlined to be compatible with open-abap and ABAP Cloud TYPES: BEGIN OF ty_text_pair, textkey TYPE c LENGTH 32, s_text TYPE c LENGTH 255, t_text TYPE c LENGTH 255, unitmlt TYPE i, uppcase TYPE c LENGTH 4, texttype TYPE c LENGTH 1, END OF ty_text_pair. TYPES ty_text_pairs TYPE STANDARD TABLE OF ty_text_pair WITH DEFAULT KEY. METHODS serialize IMPORTING !iv_object_type TYPE tadir-object !iv_object_name TYPE tadir-obj_name !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params !ii_xml TYPE REF TO zif_abapgit_xml_output !io_files TYPE REF TO zcl_abapgit_objects_files RAISING zcx_abapgit_exception . METHODS deserialize IMPORTING !iv_object_type TYPE tadir-object !iv_object_name TYPE tadir-obj_name !iv_package TYPE tadir-devclass !ii_xml TYPE REF TO zif_abapgit_xml_input !io_files TYPE REF TO zcl_abapgit_objects_files !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception . ENDINTERFACE. INTERFACE zif_abapgit_i18n_file . TYPES ty_table_of TYPE STANDARD TABLE OF REF TO zif_abapgit_i18n_file WITH DEFAULT KEY. METHODS render RETURNING VALUE(rv_data) TYPE xstring RAISING zcx_abapgit_exception. METHODS translate CHANGING cv_changed TYPE abap_bool ct_text_pairs TYPE zif_abapgit_lxe_texts=>ty_text_pairs RAISING zcx_abapgit_exception. METHODS ext RETURNING VALUE(rv_ext) TYPE string. METHODS lang RETURNING VALUE(rv_lang) TYPE laiso. METHODS lang_suffix RETURNING VALUE(rv_lang_suffix) TYPE string. ENDINTERFACE. INTERFACE zif_abapgit_sap_namespace . METHODS exists IMPORTING iv_namespace TYPE trnspace-namespace RETURNING VALUE(rv_yes) TYPE abap_bool. METHODS is_editable IMPORTING iv_namespace TYPE trnspace-namespace RETURNING VALUE(rv_yes) TYPE abap_bool. METHODS split_by_name IMPORTING iv_obj_with_namespace TYPE csequence iv_allow_slash_in_name TYPE abap_bool DEFAULT abap_true RETURNING VALUE(rs_obj_namespace) TYPE zif_abapgit_definitions=>ty_obj_namespace RAISING zcx_abapgit_exception. ENDINTERFACE. INTERFACE zif_abapgit_tadir . METHODS get_object_package IMPORTING !iv_pgmid TYPE tadir-pgmid DEFAULT 'R3TR' !iv_object TYPE tadir-object !iv_obj_name TYPE tadir-obj_name RETURNING VALUE(rv_devclass) TYPE tadir-devclass RAISING zcx_abapgit_exception . METHODS read IMPORTING !iv_package TYPE tadir-devclass !iv_ignore_subpackages TYPE abap_bool DEFAULT abap_false !iv_only_local_objects TYPE abap_bool DEFAULT abap_false !io_dot TYPE REF TO zcl_abapgit_dot_abapgit OPTIONAL !ii_log TYPE REF TO zif_abapgit_log OPTIONAL !it_filter TYPE zif_abapgit_definitions=>ty_tadir_tt OPTIONAL !iv_check_exists TYPE abap_bool DEFAULT abap_true iv_ignore_delflag TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rt_tadir) TYPE zif_abapgit_definitions=>ty_tadir_tt RAISING zcx_abapgit_exception . METHODS read_single IMPORTING !iv_pgmid TYPE tadir-pgmid DEFAULT 'R3TR' !iv_object TYPE tadir-object !iv_obj_name TYPE tadir-obj_name RETURNING VALUE(rs_tadir) TYPE zif_abapgit_definitions=>ty_tadir. METHODS insert_single IMPORTING !iv_pgmid TYPE csequence DEFAULT 'R3TR' !iv_object TYPE csequence !iv_obj_name TYPE csequence !iv_package TYPE csequence OPTIONAL !iv_language TYPE tadir-masterlang OPTIONAL !iv_srcsystem TYPE tadir-srcsystem OPTIONAL !iv_set_genflag TYPE abap_bool DEFAULT abap_false !iv_set_edtflag TYPE abap_bool DEFAULT abap_false RAISING zcx_abapgit_exception. METHODS delete_single IMPORTING !iv_pgmid TYPE csequence DEFAULT 'R3TR' !iv_object TYPE csequence !iv_obj_name TYPE csequence RAISING zcx_abapgit_exception. ENDINTERFACE. INTERFACE zif_abapgit_field_rules . TYPES ty_fill_rule TYPE c LENGTH 2. CONSTANTS: BEGIN OF c_fill_rule, date TYPE ty_fill_rule VALUE 'DT', time TYPE ty_fill_rule VALUE 'TM', timestamp TYPE ty_fill_rule VALUE 'TS', user TYPE ty_fill_rule VALUE 'UR', client TYPE ty_fill_rule VALUE 'CT', package TYPE ty_fill_rule VALUE 'PK', abap_language_version TYPE ty_fill_rule VALUE 'AL', END OF c_fill_rule. METHODS add IMPORTING iv_table TYPE tabname iv_field TYPE fieldname iv_fill_rule TYPE ty_fill_rule RETURNING VALUE(ro_self) TYPE REF TO zif_abapgit_field_rules. METHODS apply_clear_logic IMPORTING iv_table TYPE tabname CHANGING ct_data TYPE STANDARD TABLE. METHODS apply_fill_logic IMPORTING iv_table TYPE tabname iv_package TYPE devclass iv_abap_language_version TYPE uccheck OPTIONAL CHANGING ct_data TYPE STANDARD TABLE. ENDINTERFACE. INTERFACE zif_abapgit_gui_jumper . TYPES: ty_bdcdata_tt TYPE STANDARD TABLE OF bdcdata WITH DEFAULT KEY. METHODS jump IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !is_sub_item TYPE zif_abapgit_definitions=>ty_item OPTIONAL !iv_line_number TYPE i OPTIONAL !iv_new_window TYPE abap_bool DEFAULT abap_true RETURNING VALUE(rv_exit) TYPE abap_bool RAISING zcx_abapgit_exception. METHODS jump_adt IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_sub_obj_name TYPE zif_abapgit_definitions=>ty_item-obj_name !iv_line_number TYPE i RETURNING VALUE(rv_exit) TYPE abap_bool RAISING zcx_abapgit_exception. METHODS jump_batch_input IMPORTING !iv_tcode TYPE sy-tcode !it_bdcdata TYPE ty_bdcdata_tt !iv_new_window TYPE abap_bool DEFAULT abap_true RAISING zcx_abapgit_exception. ENDINTERFACE. INTERFACE zif_abapgit_comparator . TYPES: BEGIN OF ty_result, text TYPE string, END OF ty_result . METHODS compare IMPORTING !ii_local TYPE REF TO zif_abapgit_xml_input !ii_remote TYPE REF TO zif_abapgit_xml_input !ii_log TYPE REF TO zif_abapgit_log RETURNING VALUE(rs_result) TYPE ty_result RAISING zcx_abapgit_exception . ENDINTERFACE. INTERFACE zif_abapgit_xml_input . METHODS read IMPORTING !iv_name TYPE clike CHANGING !cg_data TYPE any RAISING zcx_abapgit_exception . METHODS get_raw RETURNING VALUE(ri_raw) TYPE REF TO if_ixml_document . * todo, add read_xml to match add_xml in lcl_xml_output METHODS get_metadata RETURNING VALUE(rs_metadata) TYPE zif_abapgit_definitions=>ty_metadata . ENDINTERFACE. INTERFACE zif_abapgit_lang_definitions . TYPES: BEGIN OF ty_tpool. INCLUDE TYPE textpool. TYPES: split TYPE c LENGTH 8, END OF ty_tpool, ty_tpool_tt TYPE STANDARD TABLE OF ty_tpool WITH DEFAULT KEY. TYPES: BEGIN OF ty_i18n_tpool, language TYPE langu, textpool TYPE ty_tpool_tt, END OF ty_i18n_tpool, ty_i18n_tpools TYPE STANDARD TABLE OF ty_i18n_tpool. TYPES: BEGIN OF ty_i18n_line, language TYPE langu, lines TYPE tlinetab, END OF ty_i18n_line, ty_i18n_lines TYPE STANDARD TABLE OF ty_i18n_line WITH KEY language. TYPES: ty_langus TYPE STANDARD TABLE OF langu. ENDINTERFACE. INTERFACE zif_abapgit_object . CONSTANTS: BEGIN OF gc_step_id, early TYPE zif_abapgit_objects=>ty_deserialization_step VALUE 'EARLY', abap TYPE zif_abapgit_objects=>ty_deserialization_step VALUE 'ABAP', ddic TYPE zif_abapgit_objects=>ty_deserialization_step VALUE 'DDIC', late TYPE zif_abapgit_objects=>ty_deserialization_step VALUE 'LATE', lxe TYPE zif_abapgit_objects=>ty_deserialization_step VALUE 'LXE', END OF gc_step_id. METHODS serialize IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS deserialize IMPORTING !iv_package TYPE devclass !io_xml TYPE REF TO zif_abapgit_xml_input !iv_step TYPE zif_abapgit_objects=>ty_deserialization_step !ii_log TYPE REF TO zif_abapgit_log !iv_transport TYPE trkorr RAISING zcx_abapgit_exception . METHODS delete IMPORTING !iv_package TYPE devclass !iv_transport TYPE trkorr !ii_log TYPE REF TO zif_abapgit_log RAISING zcx_abapgit_exception . METHODS exists RETURNING VALUE(rv_bool) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS is_locked RETURNING VALUE(rv_is_locked) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS is_active RETURNING VALUE(rv_active) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS changed_by IMPORTING !iv_extra TYPE string OPTIONAL RETURNING VALUE(rv_user) TYPE syuname RAISING zcx_abapgit_exception . METHODS jump IMPORTING !iv_extra TYPE string OPTIONAL RETURNING VALUE(rv_exit) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS get_metadata RETURNING VALUE(rs_metadata) TYPE zif_abapgit_definitions=>ty_metadata . METHODS get_comparator RETURNING VALUE(ri_comparator) TYPE REF TO zif_abapgit_comparator RAISING zcx_abapgit_exception . METHODS get_deserialize_steps RETURNING VALUE(rt_steps) TYPE zif_abapgit_objects=>ty_deserialization_step_tt. METHODS get_deserialize_order IMPORTING !it_all_objects TYPE zif_abapgit_definitions=>ty_items_tt RETURNING VALUE(rt_objects_before) TYPE zif_abapgit_definitions=>ty_items_tt. CLASS-METHODS map_filename_to_object IMPORTING !iv_item_part_of_filename TYPE string !iv_path TYPE string OPTIONAL !io_dot TYPE REF TO zcl_abapgit_dot_abapgit OPTIONAL !iv_package TYPE devclass OPTIONAL CHANGING cs_item TYPE zif_abapgit_definitions=>ty_item RAISING zcx_abapgit_exception. CLASS-METHODS map_object_to_filename IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_ext TYPE string !iv_extra TYPE clike CHANGING !cv_item_part_of_filename TYPE string RAISING zcx_abapgit_exception. ENDINTERFACE. INTERFACE zif_abapgit_object_enho . METHODS: deserialize IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_input iv_package TYPE devclass RAISING zcx_abapgit_exception, serialize IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_output ii_enh_tool TYPE REF TO if_enh_tool RAISING zcx_abapgit_exception. ENDINTERFACE. INTERFACE zif_abapgit_aff_oo_types_v1 . TYPES: BEGIN OF ty_component_description, name TYPE zif_abapgit_aff_types_v1=>ty_object_name_30, description TYPE zif_abapgit_aff_types_v1=>ty_description_60, END OF ty_component_description, ty_component_descriptions TYPE SORTED TABLE OF ty_component_description WITH UNIQUE KEY name. TYPES: BEGIN OF ty_method, name TYPE zif_abapgit_aff_types_v1=>ty_object_name_30, description TYPE zif_abapgit_aff_types_v1=>ty_description_60, parameters TYPE ty_component_descriptions, exceptions TYPE ty_component_descriptions, END OF ty_method, ty_methods TYPE SORTED TABLE OF ty_method WITH UNIQUE KEY name. TYPES: BEGIN OF ty_event, name TYPE zif_abapgit_aff_types_v1=>ty_object_name_30, description TYPE zif_abapgit_aff_types_v1=>ty_description_60, parameters TYPE ty_component_descriptions, END OF ty_event, ty_events TYPE SORTED TABLE OF ty_event WITH UNIQUE KEY name. TYPES: BEGIN OF ty_descriptions, types TYPE ty_component_descriptions, attributes TYPE ty_component_descriptions, events TYPE ty_events, methods TYPE ty_methods, END OF ty_descriptions. ENDINTERFACE. INTERFACE zif_abapgit_aff_intf_v1 . TYPES ty_category TYPE n LENGTH 2. CONSTANTS: BEGIN OF co_category, general TYPE ty_category VALUE '00', classic_badi TYPE ty_category VALUE '01', business_static_components TYPE ty_category VALUE '51', business_instance_components TYPE ty_category VALUE '52', db_procedure_proxy TYPE ty_category VALUE '65', web_dynpro_runtime TYPE ty_category VALUE '80', enterprise_service TYPE ty_category VALUE '90', END OF co_category. TYPES: BEGIN OF ty_main, format_version TYPE zif_abapgit_aff_types_v1=>ty_format_version, header TYPE zif_abapgit_aff_types_v1=>ty_header_60_src, category TYPE ty_category, proxy TYPE abap_bool, descriptions TYPE zif_abapgit_aff_oo_types_v1=>ty_descriptions, END OF ty_main. ENDINTERFACE. INTERFACE zif_abapgit_object_tabl . CONSTANTS: BEGIN OF c_s_dataname, segment_definition TYPE string VALUE 'SEGMENT_DEFINITION', tabl_extras TYPE string VALUE 'TABL_EXTRAS', END OF c_s_dataname. TYPES: BEGIN OF ty_dd02_text, ddlanguage TYPE dd02t-ddlanguage, ddtext TYPE dd02t-ddtext, END OF ty_dd02_text. TYPES ty_dd02_texts TYPE STANDARD TABLE OF ty_dd02_text WITH DEFAULT KEY. TYPES ty_dd03p_tt TYPE STANDARD TABLE OF dd03p WITH DEFAULT KEY. TYPES: BEGIN OF ty_segment_definition, segmentheader TYPE edisegmhd, segmentdefinition TYPE edisegmdef, segmentstructures TYPE STANDARD TABLE OF edisegstru WITH DEFAULT KEY, END OF ty_segment_definition. TYPES: ty_segment_definitions TYPE STANDARD TABLE OF ty_segment_definition WITH DEFAULT KEY. TYPES: BEGIN OF ty_tabl_extras, tddat TYPE tddat, abap_language_version TYPE uccheck, END OF ty_tabl_extras. TYPES: BEGIN OF ty_internal, dd02v TYPE dd02v, dd09l TYPE dd09l, dd03p TYPE ty_dd03p_tt, dd05m TYPE STANDARD TABLE OF dd05m WITH DEFAULT KEY, dd08v TYPE STANDARD TABLE OF dd08v WITH DEFAULT KEY, dd12v TYPE STANDARD TABLE OF dd12v WITH DEFAULT KEY, dd17v TYPE STANDARD TABLE OF dd17v WITH DEFAULT KEY, dd35v TYPE STANDARD TABLE OF dd35v WITH DEFAULT KEY, dd36m TYPE STANDARD TABLE OF dd36m WITH DEFAULT KEY, dd02_texts TYPE ty_dd02_texts, i18n_langs TYPE STANDARD TABLE OF langu WITH DEFAULT KEY, longtexts TYPE zif_abapgit_longtexts=>ty_longtexts, segment_definitions TYPE ty_segment_definitions, extras TYPE ty_tabl_extras, END OF ty_internal. ENDINTERFACE. INTERFACE zif_abapgit_progress . METHODS show IMPORTING !iv_current TYPE i !iv_text TYPE csequence RAISING zcx_abapgit_exception. METHODS set_total IMPORTING !iv_total TYPE i . METHODS off RAISING zcx_abapgit_exception. ENDINTERFACE. INTERFACE zif_abapgit_status_calc . METHODS calculate_status IMPORTING !it_local TYPE zif_abapgit_definitions=>ty_files_item_tt !it_remote TYPE zif_abapgit_git_definitions=>ty_files_tt !it_cur_state TYPE zif_abapgit_git_definitions=>ty_file_signatures_tt RETURNING VALUE(rt_results) TYPE zif_abapgit_definitions=>ty_results_tt RAISING zcx_abapgit_exception. ENDINTERFACE. INTERFACE /apmg/if_apm_ajson_iterator . METHODS has_next RETURNING VALUE(rv_yes) TYPE abap_bool. METHODS next RETURNING VALUE(ri_item) TYPE REF TO /apmg/if_apm_ajson. ENDINTERFACE. INTERFACE /apmg/if_apm_constants . ************************************************************************ * apm Constants * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ CONSTANTS: c_website TYPE string VALUE 'https://abappm.com', c_documentation TYPE string VALUE 'https://docs.abappm.com', c_registry TYPE string VALUE 'https://registry.abappm.com', c_repository TYPE string VALUE 'https://github.com/abapPM/abapPM', c_new_issue TYPE string VALUE 'https://github.com/abapPM/abapPM/issues/new/choose', c_sponsor TYPE string VALUE 'https://github.com/sponsors/abapPM', c_changelog TYPE string VALUE 'https://github.com/abapPM/abapPM/blob/main/CHANGELOG.md'. ENDINTERFACE. INTERFACE /apmg/if_apm_gui_error_handler . METHODS handle_error IMPORTING !ix_error TYPE REF TO /apmg/cx_apm_error RETURNING VALUE(rv_handled) TYPE abap_bool. ENDINTERFACE. INTERFACE /apmg/if_apm_gui_menu_provider . METHODS get_menu RETURNING VALUE(ro_toolbar) TYPE REF TO /apmg/cl_apm_html_toolbar RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_gui_modal . METHODS is_modal RETURNING VALUE(rv_yes) TYPE abap_bool. ENDINTERFACE. INTERFACE /apmg/if_apm_gui_page_title . METHODS get_page_title RETURNING VALUE(rv_title) TYPE string. ENDINTERFACE. INTERFACE /apmg/if_apm_gui_render_item . METHODS render IMPORTING !iv_item TYPE any !iv_index TYPE i RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. ENDINTERFACE. INTERFACE /apmg/if_apm_gui_router . ************************************************************************ * apm GUI Router * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ CONSTANTS: BEGIN OF c_action, apm_deprecate TYPE string VALUE 'apm_deprecate', apm_home TYPE string VALUE 'apm_home', apm_init TYPE string VALUE 'apm_init', apm_install TYPE string VALUE 'apm_install', apm_uninstall TYPE string VALUE 'apm_uninstall', apm_publish TYPE string VALUE 'apm_publish', apm_undeprecate TYPE string VALUE 'apm_undeprecate', apm_unpublish TYPE string VALUE 'apm_unpublish', apm_update TYPE string VALUE 'apm_update', change_order_by TYPE string VALUE 'change_order_by', changelog TYPE string VALUE 'changelog', clipboard TYPE string VALUE 'clipboard', direction TYPE string VALUE 'direction', documentation TYPE string VALUE 'documentation', favorite_package TYPE string VALUE 'favorite_package', feedback TYPE string VALUE 'feedback', go_back TYPE string VALUE 'go_back', go_db TYPE string VALUE 'go_db', go_debuginfo TYPE string VALUE 'go_debuginfo', go_home TYPE string VALUE 'go_home', go_settings TYPE string VALUE 'go_settings', go_settings_personal TYPE string VALUE 'go_settings_personal', go_tutorial TYPE string VALUE 'go_tutorial', goto_message TYPE string VALUE 'goto_message', goto_source TYPE string VALUE 'goto_source', homepage TYPE string VALUE 'homepage', ie_devtools TYPE string VALUE 'ie_devtools', jump TYPE string VALUE 'jump', jump_transaction TYPE string VALUE 'jump_transaction', jump_transport TYPE string VALUE 'jump_transport', jump_user TYPE string VALUE 'jump_user', registry TYPE string VALUE 'registry', show_callstack TYPE string VALUE 'show_callstack', show_hotkeys TYPE string VALUE 'show_hotkeys', sponsor TYPE string VALUE 'sponsor', url TYPE string VALUE 'url', END OF c_action. ENDINTERFACE. INTERFACE /apmg/if_apm_http_response . ************************************************************************ * HTTP Response * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ METHODS data RETURNING VALUE(result) TYPE xstring. METHODS cdata RETURNING VALUE(result) TYPE string. METHODS json RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_error. METHODS is_ok RETURNING VALUE(result) TYPE abap_bool. METHODS code RETURNING VALUE(result) TYPE i. METHODS error RETURNING VALUE(result) TYPE string. METHODS headers RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS close. ENDINTERFACE. INTERFACE /apmg/if_apm_semver_constants . ************************************************************************ * SemVer Constants * * Copyright (c) Isaac Z. Schlueter and Contributors * Ported to ABAP by apm.to Inc. * SPDX-License-Identifier: ISC ************************************************************************ * Based on node semver package v7.7.3 (October 2025) * https://github.com/npm/node-semver/releases/tag/v7.7.3 * not included yet: * https://github.com/npm/node-semver/compare/v7.7.3...main ************************************************************************ " Package version CONSTANTS version TYPE string VALUE '7.7.3' ##NEEDED. " Note: this is the semver.org version of the spec that it implements " Not necessarily the package version of this code. CONSTANTS semver_spec_version TYPE string VALUE '2.0.0'. CONSTANTS max_length TYPE i VALUE 256. CONSTANTS max_safe_integer TYPE i VALUE 999999998. " JS: int8 " Max safe segment length for coercion. CONSTANTS max_safe_component_length TYPE i VALUE 9. " JS: 16 for int8 " Max safe length for a build identifier. The max length minus 6 characters for " the shortest version with a build 0.0.0+BUILD. CONSTANTS max_safe_build_length TYPE i VALUE 250. CONSTANTS: BEGIN OF release_types, major TYPE string VALUE 'major', premajor TYPE string VALUE 'premajor', minor TYPE string VALUE 'minor', preminor TYPE string VALUE 'preminor', patch TYPE string VALUE 'patch', prepatch TYPE string VALUE 'prepatch', prerelease TYPE string VALUE 'prerelease', release TYPE string VALUE 'release', END OF release_types. ENDINTERFACE. INTERFACE /apmg/if_apm_version . ************************************************************************ * apm Version * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ CONSTANTS c_version TYPE string VALUE '1.0.0'. ENDINTERFACE. INTERFACE zif_abapgit_aff_registry . METHODS: "! Returns TRUE if the object type is supported by ABAP file formats (AFF) in abapGit.
"! Either there is a (standalone AFF capable) object handler, "! or object handler calls the AFF framework in newer ABAP systems. is_supported_object_type IMPORTING iv_obj_type TYPE tadir-object RETURNING VALUE(rv_result) TYPE abap_bool. ENDINTERFACE. INTERFACE zif_abapgit_aff_type_mapping . METHODS: "! Convert from AFF to abapGit data "! "! @parameter iv_data | ABAP data as AFF type "! @parameter iv_object_name | Name of object "! @parameter es_data | ABAP data as abapGit type to_abapgit IMPORTING iv_data TYPE data iv_object_name TYPE sobj_name EXPORTING es_data TYPE data, "! Converts to AFF specific meta data "! "! @parameter iv_data | (meta-)data of the object "! @parameter es_data | aff data of the object, e.g. zif_abapgit_aff_intf_v1=>ty_main to_aff IMPORTING iv_data TYPE data EXPORTING es_data TYPE data. ENDINTERFACE. INTERFACE zif_abapgit_apack_definitions . TYPES: BEGIN OF ty_dependency, group_id TYPE string, artifact_id TYPE string, version TYPE string, sem_version TYPE zif_abapgit_definitions=>ty_version, git_url TYPE string, target_package TYPE devclass, END OF ty_dependency, ty_dependencies TYPE STANDARD TABLE OF ty_dependency WITH NON-UNIQUE DEFAULT KEY, ty_repository_type TYPE string, BEGIN OF ty_descriptor_wo_dependencies, group_id TYPE string, artifact_id TYPE string, version TYPE string, sem_version TYPE zif_abapgit_definitions=>ty_version, repository_type TYPE ty_repository_type, git_url TYPE string, END OF ty_descriptor_wo_dependencies, BEGIN OF ty_descriptor. INCLUDE TYPE ty_descriptor_wo_dependencies. TYPES: dependencies TYPE ty_dependencies, END OF ty_descriptor, ty_descriptors TYPE STANDARD TABLE OF ty_descriptor WITH NON-UNIQUE DEFAULT KEY. TYPES: BEGIN OF ty_manifest_declaration, clsname TYPE seoclsname, devclass TYPE devclass, END OF ty_manifest_declaration, ty_manifest_declarations TYPE STANDARD TABLE OF ty_manifest_declaration WITH DEFAULT KEY. CONSTANTS c_dot_apack_manifest TYPE string VALUE '.apack-manifest.xml' ##NO_TEXT. CONSTANTS c_repository_type_abapgit TYPE ty_repository_type VALUE 'abapGit' ##NO_TEXT. CONSTANTS c_apack_interface_sap TYPE seoclsname VALUE 'IF_APACK_MANIFEST' ##NO_TEXT. CONSTANTS c_apack_interface_cust TYPE seoclsname VALUE 'ZIF_APACK_MANIFEST' ##NO_TEXT. CONSTANTS c_apack_interface_nspc TYPE seoclsname VALUE '/*/IF_APACK_MANIFEST' ##NO_TEXT. ENDINTERFACE. INTERFACE zif_abapgit_ecatt_download . METHODS: get_xml_stream RETURNING VALUE(rv_xml_stream) TYPE xstring. ENDINTERFACE. INTERFACE zif_abapgit_ecatt_upload . METHODS: set_stream_for_upload IMPORTING iv_xml TYPE xstring. ENDINTERFACE. INTERFACE zif_abapgit_function_module . METHODS: function_exists IMPORTING iv_function_module_name TYPE clike RETURNING VALUE(rv_exists) TYPE abap_bool. ENDINTERFACE. INTERFACE zif_abapgit_object_enhs . METHODS: deserialize IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_input iv_package TYPE devclass ii_enh_spot_tool TYPE REF TO if_enh_spot_tool RAISING zcx_abapgit_exception, serialize IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_output ii_enh_spot_tool TYPE REF TO if_enh_spot_tool RAISING zcx_abapgit_exception. ENDINTERFACE. INTERFACE zif_abapgit_version . CONSTANTS c_xml_version TYPE string VALUE 'v1.0.0' ##NO_TEXT. CONSTANTS c_abap_version TYPE string VALUE '1.132.0' ##NO_TEXT. ENDINTERFACE. ****** CLASSES ****** CLASS zcl_abapgit_xml DEFINITION ABSTRACT CREATE PUBLIC . PUBLIC SECTION. METHODS: constructor IMPORTING iv_filename TYPE string OPTIONAL. PROTECTED SECTION. DATA: mi_ixml TYPE REF TO if_ixml, mi_xml_doc TYPE REF TO if_ixml_document, ms_metadata TYPE zif_abapgit_definitions=>ty_metadata, mv_filename TYPE string. CONSTANTS: c_abapgit_tag TYPE string VALUE 'abapGit' ##NO_TEXT, c_attr_version TYPE string VALUE 'version' ##NO_TEXT, c_attr_serializer TYPE string VALUE 'serializer' ##NO_TEXT, c_attr_serializer_version TYPE string VALUE 'serializer_version' ##NO_TEXT. METHODS to_xml IMPORTING iv_normalize TYPE abap_bool DEFAULT abap_true RETURNING VALUE(rv_xml) TYPE string. METHODS parse IMPORTING iv_xml TYPE string RAISING zcx_abapgit_exception. PRIVATE SECTION. METHODS error IMPORTING !ii_parser TYPE REF TO if_ixml_parser RAISING zcx_abapgit_exception . METHODS raise_version_mismatch IMPORTING !iv_vers TYPE string RAISING zcx_abapgit_exception . METHODS raise_exception_for IMPORTING !ii_error TYPE REF TO if_ixml_parse_error RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_xml_output DEFINITION INHERITING FROM zcl_abapgit_xml CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_xml_output. PROTECTED SECTION. PRIVATE SECTION. DATA mi_raw TYPE REF TO if_ixml_element . METHODS build_asx_node RETURNING VALUE(ri_element) TYPE REF TO if_ixml_element . ENDCLASS. CLASS /apmg/cl_apm_abapgit_objects DEFINITION CREATE PUBLIC. ************************************************************************ * apm abapGit Objects * * Copyright 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ * This is a replacement for ZCL_ABAPGIT_OBJECTS * * Using ZCL_ABAPGIT_OBJECTS would drag in many other dependencies * which are unnecessary for apm (like the abapGit repo layer). ************************************************************************ PUBLIC SECTION. CLASS-METHODS serialize IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params RETURNING VALUE(rs_files_and_item) TYPE zif_abapgit_objects=>ty_serialization RAISING zcx_abapgit_exception. CLASS-METHODS deserialize IMPORTING !iv_package TYPE devclass !iv_language TYPE spras !iv_transport TYPE trkorr OPTIONAL !it_local TYPE zif_abapgit_definitions=>ty_files_item_tt OPTIONAL !it_local_checksums TYPE zif_abapgit_git_definitions=>ty_file_signatures_tt OPTIONAL !it_remote TYPE zif_abapgit_git_definitions=>ty_files_tt !io_dot TYPE REF TO zcl_abapgit_dot_abapgit !ii_log TYPE REF TO zif_abapgit_log RETURNING VALUE(rt_accessed_files) TYPE zif_abapgit_git_definitions=>ty_file_signatures_tt RAISING zcx_abapgit_exception. CLASS-METHODS delete IMPORTING !it_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt !iv_transport TYPE trkorr OPTIONAL !ii_log TYPE REF TO zif_abapgit_log RAISING zcx_abapgit_exception. CLASS-METHODS jump IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !is_sub_item TYPE zif_abapgit_definitions=>ty_item OPTIONAL !iv_filename TYPE string OPTIONAL !iv_line_number TYPE i OPTIONAL !iv_new_window TYPE abap_bool DEFAULT abap_true RAISING zcx_abapgit_exception. CLASS-METHODS is_supported IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_native_only TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rv_bool) TYPE abap_bool. CLASS-METHODS is_type_supported IMPORTING !iv_obj_type TYPE zif_abapgit_definitions=>ty_item-obj_type RETURNING VALUE(rv_bool) TYPE abap_bool. CLASS-METHODS exists IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item RETURNING VALUE(rv_bool) TYPE abap_bool. CLASS-METHODS supported_list RETURNING VALUE(rt_types) TYPE zif_abapgit_objects=>ty_types_tt. CLASS-METHODS is_active IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item RETURNING VALUE(rv_active) TYPE abap_bool RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_supported_types, obj_type TYPE tadir-object, supported TYPE abap_bool, END OF ty_supported_types. TYPES: ty_supported_types_tt TYPE SORTED TABLE OF ty_supported_types WITH UNIQUE KEY obj_type. TYPES: BEGIN OF ty_obj_serializer_item, item TYPE zif_abapgit_definitions=>ty_item, metadata TYPE zif_abapgit_definitions=>ty_metadata, END OF ty_obj_serializer_item. TYPES: ty_obj_serializer_map TYPE SORTED TABLE OF ty_obj_serializer_item WITH UNIQUE KEY item. CLASS-DATA gt_obj_serializer_map TYPE ty_obj_serializer_map. CLASS-DATA gt_supported_obj_types TYPE ty_supported_types_tt. CLASS-DATA gv_supported_obj_types_loaded TYPE abap_bool. CLASS-METHODS files_to_deserialize IMPORTING !iv_package TYPE devclass !it_local TYPE zif_abapgit_definitions=>ty_files_item_tt !it_local_checksums TYPE zif_abapgit_git_definitions=>ty_file_signatures_tt !it_remote TYPE zif_abapgit_git_definitions=>ty_files_tt !io_dot TYPE REF TO zcl_abapgit_dot_abapgit !ii_log TYPE REF TO zif_abapgit_log OPTIONAL RETURNING VALUE(rt_results) TYPE zif_abapgit_definitions=>ty_results_tt RAISING zcx_abapgit_exception. CLASS-METHODS prioritize_deser IMPORTING !ii_log TYPE REF TO zif_abapgit_log OPTIONAL !it_results TYPE zif_abapgit_definitions=>ty_results_tt RETURNING VALUE(rt_results) TYPE zif_abapgit_definitions=>ty_results_tt. CLASS-METHODS class_name IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item RETURNING VALUE(rv_class_name) TYPE string. CLASS-METHODS update_package_tree IMPORTING !iv_package TYPE devclass. CLASS-METHODS delete_object IMPORTING !iv_package TYPE devclass !is_item TYPE zif_abapgit_definitions=>ty_item !iv_transport TYPE trkorr !ii_log TYPE REF TO zif_abapgit_log RAISING zcx_abapgit_exception. CLASS-METHODS deserialize_steps IMPORTING !it_steps TYPE zif_abapgit_objects=>ty_step_data_tt !ii_log TYPE REF TO zif_abapgit_log !iv_transport TYPE trkorr !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params CHANGING !ct_files TYPE zif_abapgit_git_definitions=>ty_file_signatures_tt RAISING zcx_abapgit_exception. CLASS-METHODS deserialize_step IMPORTING !is_step TYPE zif_abapgit_objects=>ty_step_data !ii_log TYPE REF TO zif_abapgit_log !iv_transport TYPE trkorr CHANGING !ct_files TYPE zif_abapgit_git_definitions=>ty_file_signatures_tt RAISING zcx_abapgit_exception. CLASS-METHODS deserialize_lxe IMPORTING !is_step TYPE zif_abapgit_objects=>ty_step_data !ii_log TYPE REF TO zif_abapgit_log !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params CHANGING !ct_files TYPE zif_abapgit_git_definitions=>ty_file_signatures_tt RAISING zcx_abapgit_exception. CLASS-METHODS update_original_system IMPORTING !it_items TYPE zif_abapgit_definitions=>ty_items_tt !ii_log TYPE REF TO zif_abapgit_log !io_dot TYPE REF TO zcl_abapgit_dot_abapgit !iv_transport TYPE trkorr RAISING zcx_abapgit_exception . CLASS-METHODS check_objects_locked IMPORTING !it_items TYPE zif_abapgit_definitions=>ty_items_tt RAISING zcx_abapgit_exception. CLASS-METHODS create_object IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL !is_metadata TYPE zif_abapgit_definitions=>ty_metadata OPTIONAL !iv_native_only TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ri_obj) TYPE REF TO zif_abapgit_object RAISING zcx_abapgit_exception. CLASS-METHODS map_tadir_to_items IMPORTING !it_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt RETURNING VALUE(rt_items) TYPE zif_abapgit_definitions=>ty_items_tt. CLASS-METHODS map_results_to_items IMPORTING !it_results TYPE zif_abapgit_definitions=>ty_results_tt RETURNING VALUE(rt_items) TYPE zif_abapgit_definitions=>ty_items_tt. CLASS-METHODS filter_files_to_deserialize IMPORTING !it_results TYPE zif_abapgit_definitions=>ty_results_tt !ii_log TYPE REF TO zif_abapgit_log OPTIONAL RETURNING VALUE(rt_results) TYPE zif_abapgit_definitions=>ty_results_tt. CLASS-METHODS get_deserialize_steps RETURNING VALUE(rt_steps) TYPE zif_abapgit_objects=>ty_step_data_tt. CLASS-METHODS check_main_package IMPORTING !iv_package TYPE devclass !iv_obj_type TYPE tadir-object RAISING zcx_abapgit_exception. CLASS-METHODS change_package_assignments IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !ii_log TYPE REF TO zif_abapgit_log. CLASS-METHODS determine_i18n_params IMPORTING !io_dot TYPE REF TO zcl_abapgit_dot_abapgit !iv_main_language_only TYPE abap_bool RETURNING VALUE(rs_i18n_params) TYPE zif_abapgit_definitions=>ty_i18n_params RAISING zcx_abapgit_exception. CLASS-METHODS get_extra_from_filename IMPORTING !iv_filename TYPE string RETURNING VALUE(rv_extra) TYPE string. ENDCLASS. CLASS /apmg/cl_apm_abapgit_serialize DEFINITION CREATE PUBLIC. ************************************************************************ * apm abapGit Serializer * * Copyright 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ * This is a replacement for ZCL_ABAPGIT_SERIALIZE * * Using ZCL_ABAPGIT_SERIALIZE would include APACK and TABUs which are * not supported in apm ************************************************************************ PUBLIC SECTION. METHODS constructor IMPORTING !io_dot_abapgit TYPE REF TO zcl_abapgit_dot_abapgit OPTIONAL !is_local_settings TYPE zif_abapgit_persistence=>ty_repo-local_settings OPTIONAL RAISING zcx_abapgit_exception. METHODS serialize IMPORTING !iv_package TYPE devclass OPTIONAL !it_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt !ii_log TYPE REF TO zif_abapgit_log OPTIONAL !iv_force_sequential TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rt_files) TYPE zif_abapgit_definitions=>ty_files_item_tt RAISING zcx_abapgit_exception. METHODS files_local IMPORTING !iv_package TYPE devclass !ii_log TYPE REF TO zif_abapgit_log !it_filter TYPE zif_abapgit_definitions=>ty_tadir_tt OPTIONAL RETURNING VALUE(rt_files) TYPE zif_abapgit_definitions=>ty_files_item_tt RAISING zcx_abapgit_exception. PROTECTED SECTION. TYPES: BEGIN OF ty_unsupported_count, obj_type TYPE tadir-object, obj_name TYPE tadir-obj_name, count TYPE i, END OF ty_unsupported_count. TYPES: ty_unsupported_count_tt TYPE HASHED TABLE OF ty_unsupported_count WITH UNIQUE KEY obj_type. TYPES: ty_char32 TYPE c LENGTH 32. DATA mt_files TYPE zif_abapgit_definitions=>ty_files_item_tt. DATA mi_log TYPE REF TO zif_abapgit_log. DATA mo_dot_abapgit TYPE REF TO zcl_abapgit_dot_abapgit. DATA ms_local_settings TYPE zif_abapgit_persistence=>ty_repo-local_settings. DATA ms_i18n_params TYPE zif_abapgit_definitions=>ty_i18n_params. DATA mo_abap_language_version TYPE REF TO zcl_abapgit_abap_language_vers. METHODS add_dot_abapgit CHANGING !ct_files TYPE zif_abapgit_definitions=>ty_files_item_tt RAISING zcx_abapgit_exception. METHODS add_to_return IMPORTING iv_path TYPE string is_file_item TYPE zif_abapgit_objects=>ty_serialization. METHODS run_sequential IMPORTING !is_tadir TYPE zif_abapgit_definitions=>ty_tadir RAISING zcx_abapgit_exception. METHODS add_objects IMPORTING !iv_package TYPE devclass !ii_log TYPE REF TO zif_abapgit_log !it_filter TYPE zif_abapgit_definitions=>ty_tadir_tt OPTIONAL CHANGING !ct_files TYPE zif_abapgit_definitions=>ty_files_item_tt RAISING zcx_abapgit_exception. METHODS filter_unsupported_objects CHANGING !ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt. METHODS filter_ignored_objects IMPORTING !iv_package TYPE devclass CHANGING !ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt RAISING zcx_abapgit_exception. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_ajson DEFINITION CREATE PUBLIC. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson. ALIASES: is_empty FOR /apmg/if_apm_ajson~is_empty, exists FOR /apmg/if_apm_ajson~exists, members FOR /apmg/if_apm_ajson~members, get FOR /apmg/if_apm_ajson~get, get_boolean FOR /apmg/if_apm_ajson~get_boolean, get_integer FOR /apmg/if_apm_ajson~get_integer, get_number FOR /apmg/if_apm_ajson~get_number, get_date FOR /apmg/if_apm_ajson~get_date, get_timestamp FOR /apmg/if_apm_ajson~get_timestamp, get_string FOR /apmg/if_apm_ajson~get_string, slice FOR /apmg/if_apm_ajson~slice, to_abap FOR /apmg/if_apm_ajson~to_abap, array_to_string_table FOR /apmg/if_apm_ajson~array_to_string_table. ALIASES: clear FOR /apmg/if_apm_ajson~clear, set FOR /apmg/if_apm_ajson~set, setx FOR /apmg/if_apm_ajson~setx, set_boolean FOR /apmg/if_apm_ajson~set_boolean, set_string FOR /apmg/if_apm_ajson~set_string, set_integer FOR /apmg/if_apm_ajson~set_integer, set_date FOR /apmg/if_apm_ajson~set_date, set_timestamp FOR /apmg/if_apm_ajson~set_timestamp, set_null FOR /apmg/if_apm_ajson~set_null, delete FOR /apmg/if_apm_ajson~delete, touch_array FOR /apmg/if_apm_ajson~touch_array, push FOR /apmg/if_apm_ajson~push, stringify FOR /apmg/if_apm_ajson~stringify. ALIASES: clone FOR /apmg/if_apm_ajson~clone, filter FOR /apmg/if_apm_ajson~filter, map FOR /apmg/if_apm_ajson~map. ALIASES: mt_json_tree FOR /apmg/if_apm_ajson~mt_json_tree, keep_item_order FOR /apmg/if_apm_ajson~keep_item_order, format_datetime FOR /apmg/if_apm_ajson~format_datetime, to_abap_corresponding_only FOR /apmg/if_apm_ajson~to_abap_corresponding_only, freeze FOR /apmg/if_apm_ajson~freeze. CLASS-METHODS parse IMPORTING !iv_json TYPE any !iv_freeze TYPE abap_bool DEFAULT abap_false !ii_custom_mapping TYPE REF TO /apmg/if_apm_ajson_mapping OPTIONAL !iv_keep_item_order TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ro_instance) TYPE REF TO /apmg/cl_apm_ajson RAISING /apmg/cx_apm_ajson_error. CLASS-METHODS create_empty " Might be deprecated, prefer using new( ) or create object IMPORTING !ii_custom_mapping TYPE REF TO /apmg/if_apm_ajson_mapping OPTIONAL iv_keep_item_order TYPE abap_bool DEFAULT abap_false iv_format_datetime TYPE abap_bool DEFAULT abap_true iv_to_abap_corresponding_only TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ro_instance) TYPE REF TO /apmg/cl_apm_ajson. " Experimental ! May change CLASS-METHODS create_from " TODO, rename to 'from' ? IMPORTING !ii_source_json TYPE REF TO /apmg/if_apm_ajson !ii_filter TYPE REF TO /apmg/if_apm_ajson_filter OPTIONAL " Might be deprecated, use filter() instead !ii_mapper TYPE REF TO /apmg/if_apm_ajson_mapping OPTIONAL " Might be deprecated, use map() instead RETURNING VALUE(ro_instance) TYPE REF TO /apmg/cl_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS constructor IMPORTING iv_keep_item_order TYPE abap_bool DEFAULT abap_false iv_format_datetime TYPE abap_bool DEFAULT abap_true iv_to_abap_corresponding_only TYPE abap_bool DEFAULT abap_false. CLASS-METHODS new IMPORTING iv_keep_item_order TYPE abap_bool DEFAULT abap_false iv_format_datetime TYPE abap_bool DEFAULT abap_true iv_to_abap_corresponding_only TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ro_instance) TYPE REF TO /apmg/cl_apm_ajson. CLASS-METHODS normalize_path IMPORTING iv_path TYPE string RETURNING VALUE(rv_path) TYPE string. PROTECTED SECTION. PRIVATE SECTION. CLASS-DATA go_float_regex TYPE REF TO cl_abap_regex. DATA ms_opts TYPE /apmg/if_apm_ajson=>ty_opts. DATA mi_custom_mapping TYPE REF TO /apmg/if_apm_ajson_mapping. " DEPRECATED, will be removed METHODS get_item IMPORTING iv_path TYPE string RETURNING VALUE(rv_item) TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. METHODS prove_path_exists IMPORTING iv_path TYPE string RETURNING VALUE(rr_end_node) TYPE REF TO /apmg/if_apm_ajson_types=>ty_node RAISING /apmg/cx_apm_ajson_error. METHODS delete_subtree IMPORTING iv_path TYPE string iv_name TYPE string ir_parent TYPE REF TO /apmg/if_apm_ajson_types=>ty_node OPTIONAL RETURNING VALUE(rs_top_node) TYPE /apmg/if_apm_ajson_types=>ty_node. METHODS read_only_watchdog RAISING /apmg/cx_apm_ajson_error. ENDCLASS. CLASS /apmg/cl_apm_ajson_extensions DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * Extensions for AJSON (Filter, Mappings, etc.) * * Copyright 2025 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CONSTANTS c_version TYPE string VALUE '1.0.0' ##NEEDED. "! Like zcl_ajson_mapping=>create_to_camel_case( ) but keep any leading underscore "! and first characters after it in lower case "! "! Examples: "! _ID -> _id "! _ABAP_VERSION -> _abapVersion "! DEV_DEPENDENCIES -> devDependencies CLASS-METHODS to_camel_case_underscore RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_ajson_mapping. "! Like zcl_ajson_mapping=>create_camel_case( ) but keep any leading underscore "! "! Examples: "! _id -> _ID "! _abapVersion -> _ABAP_VERSION "! devDependencies -> DEV_DEPENDENCIES CLASS-METHODS from_camel_case_underscore RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_ajson_mapping. "! Like zcl_ajson_filter_lib=>create_empty_filter( ) but also remove initial numbers and null CLASS-METHODS filter_empty_zero_null RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_ajson_filter. "! Like filter_empty_zero_null but keep "deprecated" value CLASS-METHODS filter_deprecated RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_ajson_filter. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_ajson_filter_lib DEFINITION FINAL CREATE PUBLIC . PUBLIC SECTION. CLASS-METHODS create_empty_filter RETURNING VALUE(ri_filter) TYPE REF TO /apmg/if_apm_ajson_filter RAISING /apmg/cx_apm_ajson_error . CLASS-METHODS create_path_filter IMPORTING !it_skip_paths TYPE string_table OPTIONAL !iv_skip_paths TYPE string OPTIONAL !iv_pattern_search TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ri_filter) TYPE REF TO /apmg/if_apm_ajson_filter RAISING /apmg/cx_apm_ajson_error . CLASS-METHODS create_and_filter IMPORTING !it_filters TYPE /apmg/if_apm_ajson_filter=>ty_filter_tab RETURNING VALUE(ri_filter) TYPE REF TO /apmg/if_apm_ajson_filter RAISING /apmg/cx_apm_ajson_error . PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS lcl_mapping_fields DEFINITION. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_mapping. METHODS constructor IMPORTING it_mapping_fields TYPE /apmg/if_apm_ajson_mapping~ty_mapping_fields OPTIONAL. PROTECTED SECTION. PRIVATE SECTION. DATA mt_mapping_fields TYPE /apmg/if_apm_ajson_mapping~ty_mapping_fields. ENDCLASS. CLASS lcl_rename DEFINITION. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_mapping. METHODS constructor IMPORTING it_rename_map TYPE /apmg/if_apm_ajson_mapping~tty_rename_map iv_rename_by TYPE i. PROTECTED SECTION. PRIVATE SECTION. DATA mt_rename_map TYPE /apmg/if_apm_ajson_mapping~tty_rename_map. DATA mv_rename_by TYPE i. ENDCLASS. CLASS lcl_mapping_to_upper DEFINITION. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_mapping. METHODS constructor IMPORTING it_mapping_fields TYPE /apmg/if_apm_ajson_mapping~ty_mapping_fields OPTIONAL. PROTECTED SECTION. PRIVATE SECTION. DATA mi_mapping_fields TYPE REF TO /apmg/if_apm_ajson_mapping. ENDCLASS. CLASS lcl_mapping_to_lower DEFINITION. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_mapping. METHODS constructor IMPORTING it_mapping_fields TYPE /apmg/if_apm_ajson_mapping~ty_mapping_fields OPTIONAL. PROTECTED SECTION. PRIVATE SECTION. DATA mi_mapping_fields TYPE REF TO /apmg/if_apm_ajson_mapping. ENDCLASS. CLASS lcl_mapping_camel DEFINITION. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_mapping. METHODS constructor IMPORTING it_mapping_fields TYPE /apmg/if_apm_ajson_mapping~ty_mapping_fields OPTIONAL iv_first_json_upper TYPE abap_bool DEFAULT abap_true. PROTECTED SECTION. PRIVATE SECTION. DATA mv_first_json_upper TYPE abap_bool. DATA mi_mapping_fields TYPE REF TO /apmg/if_apm_ajson_mapping. ENDCLASS. CLASS lcl_compound_mapper DEFINITION. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_mapping. METHODS constructor IMPORTING it_queue TYPE /apmg/if_apm_ajson_mapping=>ty_table_of. PROTECTED SECTION. PRIVATE SECTION. DATA mt_queue TYPE /apmg/if_apm_ajson_mapping=>ty_table_of. ENDCLASS. CLASS lcl_to_snake DEFINITION. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_mapping. ENDCLASS. CLASS lcl_to_camel DEFINITION. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_mapping. METHODS constructor IMPORTING iv_first_json_upper TYPE abap_bool. PRIVATE SECTION. DATA mv_first_json_upper TYPE abap_bool. ENDCLASS. CLASS /apmg/cl_apm_ajson_mapping DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. CONSTANTS: BEGIN OF rename_by, attr_name TYPE i VALUE 0, full_path TYPE i VALUE 1, pattern TYPE i VALUE 2, " regex type i value 3, " TODO add if needed in future END OF rename_by. CLASS-METHODS create_camel_case " DEPRECATED IMPORTING it_mapping_fields TYPE /apmg/if_apm_ajson_mapping=>ty_mapping_fields OPTIONAL iv_first_json_upper TYPE abap_bool DEFAULT abap_true RETURNING VALUE(ri_mapping) TYPE REF TO /apmg/if_apm_ajson_mapping. CLASS-METHODS create_upper_case IMPORTING it_mapping_fields TYPE /apmg/if_apm_ajson_mapping=>ty_mapping_fields OPTIONAL RETURNING VALUE(ri_mapping) TYPE REF TO /apmg/if_apm_ajson_mapping. CLASS-METHODS create_lower_case IMPORTING it_mapping_fields TYPE /apmg/if_apm_ajson_mapping=>ty_mapping_fields OPTIONAL RETURNING VALUE(ri_mapping) TYPE REF TO /apmg/if_apm_ajson_mapping. CLASS-METHODS create_field_mapping " DEPRECATED IMPORTING it_mapping_fields TYPE /apmg/if_apm_ajson_mapping=>ty_mapping_fields RETURNING VALUE(ri_mapping) TYPE REF TO /apmg/if_apm_ajson_mapping. CLASS-METHODS create_rename IMPORTING it_rename_map TYPE /apmg/if_apm_ajson_mapping=>tty_rename_map iv_rename_by TYPE i DEFAULT rename_by-attr_name RETURNING VALUE(ri_mapping) TYPE REF TO /apmg/if_apm_ajson_mapping. CLASS-METHODS create_compound_mapper IMPORTING ii_mapper1 TYPE REF TO /apmg/if_apm_ajson_mapping OPTIONAL ii_mapper2 TYPE REF TO /apmg/if_apm_ajson_mapping OPTIONAL ii_mapper3 TYPE REF TO /apmg/if_apm_ajson_mapping OPTIONAL it_more TYPE /apmg/if_apm_ajson_mapping=>ty_table_of OPTIONAL RETURNING VALUE(ri_mapping) TYPE REF TO /apmg/if_apm_ajson_mapping. CLASS-METHODS create_to_snake_case RETURNING VALUE(ri_mapping) TYPE REF TO /apmg/if_apm_ajson_mapping. CLASS-METHODS create_to_camel_case IMPORTING iv_first_json_upper TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ri_mapping) TYPE REF TO /apmg/if_apm_ajson_mapping. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_ajson_refs_init_l DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. CLASS-METHODS create_path_refs_init IMPORTING !it_data_refs TYPE /apmg/if_apm_ajson_refs_init=>tty_data_refs RETURNING VALUE(ri_refs_init) TYPE REF TO /apmg/if_apm_ajson_refs_init RAISING /apmg/cx_apm_ajson_error. ENDCLASS. CLASS /apmg/cl_apm_ajson_ref_initial DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. CLASS-METHODS create_path_refs_init IMPORTING !it_data_refs TYPE /apmg/if_apm_ajson_ref_initial=>tty_data_refs RETURNING VALUE(ri_refs_init) TYPE REF TO /apmg/if_apm_ajson_ref_initial RAISING /apmg/cx_apm_ajson_error. ENDCLASS. CLASS /apmg/cl_apm_ajson_utilities DEFINITION CREATE PUBLIC. PUBLIC SECTION. CLASS-METHODS new RETURNING VALUE(ro_instance) TYPE REF TO /apmg/cl_apm_ajson_utilities. METHODS diff IMPORTING !iv_json_a TYPE string OPTIONAL !iv_json_b TYPE string OPTIONAL !io_json_a TYPE REF TO /apmg/if_apm_ajson OPTIONAL !io_json_b TYPE REF TO /apmg/if_apm_ajson OPTIONAL !iv_keep_empty_arrays TYPE abap_bool DEFAULT abap_false EXPORTING !eo_insert TYPE REF TO /apmg/if_apm_ajson !eo_delete TYPE REF TO /apmg/if_apm_ajson !eo_change TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS merge IMPORTING !iv_json_a TYPE string OPTIONAL !iv_json_b TYPE string OPTIONAL !io_json_a TYPE REF TO /apmg/if_apm_ajson OPTIONAL !io_json_b TYPE REF TO /apmg/if_apm_ajson OPTIONAL !iv_keep_empty_arrays TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ro_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS sort IMPORTING !iv_json TYPE string OPTIONAL !io_json TYPE REF TO /apmg/if_apm_ajson OPTIONAL RETURNING VALUE(rv_sorted) TYPE string RAISING /apmg/cx_apm_ajson_error. METHODS is_equal IMPORTING !iv_json_a TYPE string OPTIONAL !iv_json_b TYPE string OPTIONAL !ii_json_a TYPE REF TO /apmg/if_apm_ajson OPTIONAL !ii_json_b TYPE REF TO /apmg/if_apm_ajson OPTIONAL RETURNING VALUE(rv_yes) TYPE abap_bool RAISING /apmg/cx_apm_ajson_error. CLASS-METHODS iterate_array IMPORTING ii_json TYPE REF TO /apmg/if_apm_ajson iv_path TYPE string RETURNING VALUE(ri_iterator) TYPE REF TO /apmg/if_apm_ajson_iterator RAISING /apmg/cx_apm_ajson_error. CLASS-METHODS iterate_object IMPORTING ii_json TYPE REF TO /apmg/if_apm_ajson iv_path TYPE string RETURNING VALUE(ri_iterator) TYPE REF TO /apmg/if_apm_ajson_iterator RAISING /apmg/cx_apm_ajson_error. PROTECTED SECTION. PRIVATE SECTION. DATA mo_json_a TYPE REF TO /apmg/if_apm_ajson. DATA mo_json_b TYPE REF TO /apmg/if_apm_ajson. DATA mo_insert TYPE REF TO /apmg/if_apm_ajson. DATA mo_delete TYPE REF TO /apmg/if_apm_ajson. DATA mo_change TYPE REF TO /apmg/if_apm_ajson. METHODS normalize_input IMPORTING !iv_json TYPE string OPTIONAL !io_json TYPE REF TO /apmg/if_apm_ajson OPTIONAL RETURNING VALUE(ro_json) TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. METHODS diff_a_b IMPORTING !iv_path TYPE string RAISING /apmg/cx_apm_ajson_error. METHODS diff_b_a IMPORTING !iv_path TYPE string !iv_array TYPE abap_bool DEFAULT abap_false RAISING /apmg/cx_apm_ajson_error. METHODS delete_empty_nodes IMPORTING !io_json TYPE REF TO /apmg/if_apm_ajson !iv_keep_empty_arrays TYPE abap_bool RAISING /apmg/cx_apm_ajson_error. ENDCLASS. CLASS /apmg/cl_apm_arborist DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * Arborist * * Inspect and manage package trees. In ABAP, there's only one * tree containing all packages managed by apm. * * Copyright 2025 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * https://www.npmjs.com/package/@npmcli/arborist * https://github.com/npm/cli/tree/latest/workspaces/arborist ************************************************************************ PUBLIC SECTION. INTERFACES /apmg/if_apm_arborist. CLASS-METHODS factory IMPORTING !registry TYPE string !with_bundle_dependencies TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_arborist. CLASS-METHODS injector IMPORTING !mock TYPE REF TO /apmg/if_apm_arborist. METHODS constructor IMPORTING !registry TYPE string !with_bundle_dependencies TYPE abap_bool DEFAULT abap_false. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_max_depth TYPE i VALUE 10. CONSTANTS c_max_iterations TYPE i VALUE 5. TYPES: BEGIN OF ty_visited, name TYPE /apmg/if_apm_types=>ty_name, END OF ty_visited, ty_visited_set TYPE HASHED TABLE OF ty_visited WITH UNIQUE KEY name. CLASS-DATA instance TYPE REF TO /apmg/if_apm_arborist. DATA registry TYPE string. DATA with_bundle_dependencies TYPE abap_bool. DATA log TYPE /apmg/if_apm_arborist=>ty_log. DATA visited TYPE ty_visited_set. DATA processing_stack TYPE string_table. "! Add a log entry METHODS add_log IMPORTING !type TYPE string !message TYPE string !name TYPE string OPTIONAL !version TYPE string OPTIONAL !spec TYPE string OPTIONAL. "! Process a single package and its dependencies METHODS process_package IMPORTING !package_info TYPE /apmg/if_apm_package_json=>ty_package !depth TYPE i DEFAULT 0. "! Process dependencies of a node METHODS process_dependencies IMPORTING !node TYPE REF TO /apmg/cl_apm_arborist_node !depth TYPE i. "! Process uninstalled dependencies METHODS process_uninstalled. METHODS resolve RETURNING VALUE(result) TYPE /apmg/if_apm_arborist=>ty_node_refs. "! Create edges for a dependency list METHODS create_edges IMPORTING !type TYPE /apmg/if_apm_arborist=>ty_dependency_type !node TYPE REF TO /apmg/cl_apm_arborist_node !dependencies TYPE /apmg/if_apm_types=>ty_dependencies !bundle_dependencies TYPE /apmg/if_apm_types=>ty_bundle_dependencies OPTIONAL. "! Check for circular dependency METHODS is_circular IMPORTING !name TYPE /apmg/if_apm_types=>ty_name RETURNING VALUE(result) TYPE abap_bool. "! Get manifest from pacote (cached locally if possible) METHODS get_manifest IMPORTING !name TYPE /apmg/if_apm_types=>ty_name !version TYPE /apmg/if_apm_types=>ty_version OPTIONAL RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_package_json. "! Get list of available versions from manifest METHODS get_versions IMPORTING !name TYPE /apmg/if_apm_types=>ty_name RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_versions. ENDCLASS. CLASS /apmg/cl_apm_arborist_edge DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * Arborist - Edge * * An Edge represents a dependency relationship. Each node has an * edgesIn set and an edgesOut set. Each edge has a type which specifies * what kind of dependency it represents. * * Copyright 2025 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * An Edge represents a dependency relationship. Each node has an edgesIn * set, and an edgesOut map. Each edge has a type which specifies what * kind of dependency it represents. edge.from is a reference to the node * that has the dependency, and edge.to is a reference to the node that * requires the dependency. ************************************************************************ PUBLIC SECTION. "! Source node (the package that has the dependency) DATA from TYPE REF TO /apmg/cl_apm_arborist_node READ-ONLY. "! Dependency type (prod, dev, optional, peer) DATA type TYPE /apmg/if_apm_arborist=>ty_dependency_type READ-ONLY. "! Name of the required package DATA name TYPE /apmg/if_apm_types=>ty_name READ-ONLY. "! Version spec/range required DATA spec TYPE /apmg/if_apm_types=>ty_spec READ-ONLY. "! Target node (the package that satisfies the dependency) DATA to TYPE REF TO /apmg/cl_apm_arborist_node READ-ONLY. "! Is the dependency valid (satisfies spec) DATA valid TYPE abap_bool READ-ONLY. "! Error type if not valid DATA error TYPE /apmg/if_apm_arborist=>ty_error_type READ-ONLY. "! Factory method to create an edge CLASS-METHODS create IMPORTING !from TYPE REF TO /apmg/cl_apm_arborist_node !type TYPE /apmg/if_apm_arborist=>ty_dependency_type !name TYPE /apmg/if_apm_types=>ty_name !spec TYPE /apmg/if_apm_types=>ty_spec RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_arborist_edge. "! Constructor METHODS constructor IMPORTING !from TYPE REF TO /apmg/cl_apm_arborist_node !type TYPE /apmg/if_apm_arborist=>ty_dependency_type !name TYPE /apmg/if_apm_types=>ty_name !spec TYPE /apmg/if_apm_types=>ty_spec. "! Resolve the target node and validate METHODS resolve. "! Check if the dependency is missing METHODS is_missing RETURNING VALUE(result) TYPE abap_bool. "! Check if the dependency is invalid (wrong version) METHODS is_invalid RETURNING VALUE(result) TYPE abap_bool. "! Get error description METHODS get_error_description RETURNING VALUE(result) TYPE string. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_arborist_node DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * Arborist - Node * * A node represents a package that is installed on this system, either * as a package, or as a bundle of another package. * * Copyright 2025 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * A node represents a package that is installed on this system, either * as a global package, or as a modules of another package (bundle). * * https://www.npmjs.com/package/@npmcli/arborist * https://github.com/npm/cli/tree/latest/workspaces/arborist ************************************************************************ PUBLIC SECTION. TYPES: ty_edge TYPE REF TO /apmg/cl_apm_arborist_edge, ty_edges TYPE STANDARD TABLE OF ty_edge WITH KEY table_line. TYPES: ty_node_ref TYPE REF TO /apmg/cl_apm_arborist_node, ty_node_refs TYPE STANDARD TABLE OF ty_node_ref WITH KEY table_line. "! Package (SAP devclass) DATA package TYPE /apmg/if_apm_types=>ty_devclass READ-ONLY. "! Package name in registry DATA name TYPE /apmg/if_apm_types=>ty_name READ-ONLY. "! Installed version DATA version TYPE /apmg/if_apm_types=>ty_version READ-ONLY. "! Maximum version that satisfies the list of version specs (of all in edges) DATA max_satisfying_version TYPE /apmg/if_apm_types=>ty_version READ-ONLY. "! Production dependencies DATA dependencies TYPE /apmg/if_apm_types=>ty_dependencies READ-ONLY. "! Development dependencies DATA dev_dependencies TYPE /apmg/if_apm_types=>ty_dependencies READ-ONLY. "! Peer dependencies DATA peer_dependencies TYPE /apmg/if_apm_types=>ty_dependencies READ-ONLY. "! Optional dependencies DATA optional_dependencies TYPE /apmg/if_apm_types=>ty_dependencies READ-ONLY. "! bundle dependencies DATA bundle_dependencies TYPE /apmg/if_apm_types=>ty_bundle_dependencies READ-ONLY. "! Is this package installed DATA installed TYPE abap_bool READ-ONLY. "! Outgoing edges (dependencies of this package) DATA edges_out TYPE ty_edges READ-ONLY. "! Incoming edges (packages that depend on this) DATA edges_in TYPE ty_edges READ-ONLY. "! Errors during tree building DATA errors TYPE string_table READ-ONLY. "! Factory method to create a node from manifest CLASS-METHODS create IMPORTING !package TYPE /apmg/if_apm_types=>ty_devclass OPTIONAL !manifest TYPE /apmg/if_apm_types=>ty_package_json !installed TYPE abap_bool DEFAULT abap_true RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_arborist_node. "! Get a node by name from the global tree CLASS-METHODS get_by_name IMPORTING !name TYPE /apmg/if_apm_types=>ty_name RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_arborist_node. "! Get a node by package from the global tree CLASS-METHODS get_by_package IMPORTING !package TYPE /apmg/if_apm_types=>ty_devclass RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_arborist_node. "! Get all nodes in the global tree CLASS-METHODS get_all RETURNING VALUE(result) TYPE ty_node_refs. "! Clear the global tree CLASS-METHODS clear. "! Check if a node exists in the tree by name CLASS-METHODS exists IMPORTING !name TYPE /apmg/if_apm_types=>ty_name RETURNING VALUE(result) TYPE abap_bool. "! Constructor METHODS constructor IMPORTING !package TYPE /apmg/if_apm_types=>ty_devclass OPTIONAL !manifest TYPE /apmg/if_apm_types=>ty_package_json !installed TYPE abap_bool DEFAULT abap_true. "! Add an outgoing edge (dependency) METHODS add_edge_out IMPORTING !edge TYPE REF TO /apmg/cl_apm_arborist_edge. "! Add an incoming edge (depended by) METHODS add_edge_in IMPORTING !edge TYPE REF TO /apmg/cl_apm_arborist_edge. "! Check if this node satisfies a version spec METHODS satisfies IMPORTING !range TYPE /apmg/if_apm_types=>ty_spec RETURNING VALUE(result) TYPE abap_bool. "! Get the maximum version that satisfies a list of version specs METHODS max_satisfying IMPORTING !versions TYPE /apmg/if_apm_types=>ty_versions !specs TYPE string_table RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_version. "! Set the maximum version that satisfies the version specs METHODS set_max_satisfying IMPORTING !max_satisfying TYPE /apmg/if_apm_types=>ty_version. "! Add an error message METHODS add_error IMPORTING !message TYPE string. "! Get all dependencies as a flat list METHODS get_all_dependencies RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_dependencies. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_node_entry, name TYPE /apmg/if_apm_types=>ty_name, package TYPE /apmg/if_apm_types=>ty_devclass, instance TYPE REF TO /apmg/cl_apm_arborist_node, END OF ty_node_entry, ty_node_entries TYPE HASHED TABLE OF ty_node_entry WITH UNIQUE KEY name. "! Global tree storage (singleton pattern) CLASS-DATA tree TYPE ty_node_entries. ENDCLASS. CLASS /apmg/cl_apm_auth DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm Auth * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS is_package_allowed IMPORTING package TYPE devclass RETURNING VALUE(result) TYPE abap_bool. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_code_importer DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm Code Importer * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS scan IMPORTING !program_name TYPE progname !program_source TYPE /apmg/if_apm_importer=>ty_code OPTIONAL RETURNING VALUE(result) TYPE stokesx_tab RAISING /apmg/cx_apm_error. CLASS-METHODS import IMPORTING !program_name TYPE progname !map TYPE /apmg/if_apm_importer=>ty_map !is_pretty TYPE abap_bool DEFAULT abap_false !program_source TYPE /apmg/if_apm_importer=>ty_code OPTIONAL RETURNING VALUE(result) TYPE /apmg/if_apm_importer=>ty_code RAISING /apmg/cx_apm_error. CLASS-METHODS read IMPORTING !program_name TYPE progname !program_source TYPE /apmg/if_apm_importer=>ty_code OPTIONAL RETURNING VALUE(result) TYPE /apmg/if_apm_importer=>ty_code RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CLASS-METHODS prepare IMPORTING !program_source TYPE /apmg/if_apm_importer=>ty_code RETURNING VALUE(result) TYPE /apmg/if_apm_importer=>ty_code. CLASS-METHODS get_class_name_from_token IMPORTING !token TYPE string RETURNING VALUE(result) TYPE string. CLASS-METHODS get_interface_name_from_token IMPORTING !token TYPE string RETURNING VALUE(result) TYPE string. CLASS-METHODS get_object_name_from_abapdoc IMPORTING !token TYPE string RETURNING VALUE(result) TYPE string. ENDCLASS. CLASS /apmg/cl_apm_code_import_rules DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm Code Import Rules * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * TODO: replace logging with ABAP Logger (wait for v2 of it) ************************************************************************ PUBLIC SECTION. CLASS-METHODS get IMPORTING !programs TYPE /apmg/if_apm_importer=>ty_programs !is_logging TYPE abap_bool !default_rule TYPE string RETURNING VALUE(result) TYPE /apmg/if_apm_importer=>ty_rules RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_width TYPE i VALUE 150. CLASS-METHODS get_import_rules IMPORTING !program TYPE /apmg/if_apm_importer=>ty_program !is_logging TYPE abap_bool RETURNING VALUE(result) TYPE /apmg/if_apm_importer=>ty_rules RAISING /apmg/cx_apm_error. CLASS-METHODS get_old_object IMPORTING !token TYPE stokesx !pos TYPE string RETURNING VALUE(result) TYPE /apmg/if_apm_importer=>ty_rule-old_object RAISING /apmg/cx_apm_error. CLASS-METHODS get_new_object IMPORTING !token TYPE stokesx !pos TYPE string RETURNING VALUE(result) TYPE /apmg/if_apm_importer=>ty_rule-new_object RAISING /apmg/cx_apm_error. CLASS-METHODS get_module_name IMPORTING !token TYPE stokesx !pos TYPE string RETURNING VALUE(result) TYPE /apmg/if_apm_importer=>ty_rule-name RAISING /apmg/cx_apm_error. CLASS-METHODS check_result IMPORTING !result TYPE /apmg/if_apm_importer=>ty_rules !is_logging TYPE abap_bool RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_code_mapper DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm Code Mapper * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * TODO: replace logging with ABAP Logger (wait for v2 of it) ************************************************************************ PUBLIC SECTION. CLASS-METHODS get IMPORTING !source_package TYPE devclass !target_package TYPE devclass !rules TYPE /apmg/if_apm_importer=>ty_rules !object_types TYPE /apmg/if_apm_importer=>ty_object_types !object_names TYPE /apmg/if_apm_importer=>ty_object_names !is_logging TYPE abap_bool DEFAULT abap_false !is_production TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE /apmg/if_apm_importer=>ty_map RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_width TYPE i VALUE 150. CLASS-METHODS get_tadir_objects IMPORTING !source_package TYPE devclass !object_types TYPE /apmg/if_apm_importer=>ty_object_types !object_names TYPE /apmg/if_apm_importer=>ty_object_names !is_logging TYPE abap_bool RETURNING VALUE(result) TYPE zif_abapgit_definitions=>ty_tadir_tt RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_command_deprecate DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * apm Deprecate * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * Note: Use empty message to undeprecate package or version ************************************************************************ PUBLIC SECTION. CLASS-METHODS run IMPORTING !registry TYPE string !name TYPE string !range TYPE string OPTIONAL !message_text TYPE string OPTIONAL RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. METHODS execute IMPORTING !registry TYPE string !name TYPE string !range TYPE string !message_text TYPE string RAISING /apmg/cx_apm_error. METHODS update_deprecate_message IMPORTING !packument TYPE /apmg/if_apm_types=>ty_packument !range TYPE string !message_text TYPE string RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_packument RAISING /apmg/cx_apm_error. METHODS deprecate_package_version IMPORTING !registry TYPE string !packument TYPE /apmg/if_apm_types=>ty_packument RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_command_init DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * apm Init Command * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS run IMPORTING !package TYPE devclass !package_json TYPE /apmg/if_apm_types=>ty_package_json RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. METHODS execute IMPORTING !package TYPE devclass !package_json TYPE /apmg/if_apm_types=>ty_package_json RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_command_install DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * apm Install Command * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS run IMPORTING !registry TYPE string !package TYPE devclass !package_json TYPE /apmg/if_apm_types=>ty_package_json !is_production TYPE abap_bool DEFAULT abap_false !is_force TYPE abap_bool DEFAULT abap_false !is_dry_run TYPE abap_bool DEFAULT abap_false RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_action, missing TYPE /apmg/if_apm_types=>ty_dependency, invalid TYPE /apmg/if_apm_types=>ty_dependency, error TYPE string, warning TYPE string, END OF ty_action, BEGIN OF ty_actions, missing TYPE /apmg/if_apm_types=>ty_dependencies, invalid TYPE /apmg/if_apm_types=>ty_dependencies, errors TYPE string_table, warnings TYPE string_table, END OF ty_actions. METHODS execute IMPORTING !registry TYPE string !package TYPE devclass !package_json TYPE /apmg/if_apm_types=>ty_package_json !is_production TYPE abap_bool !is_force TYPE abap_bool !is_dry_run TYPE abap_bool RAISING /apmg/cx_apm_error ##NEEDED. METHODS check_package IMPORTING !package TYPE devclass !name TYPE string RAISING /apmg/cx_apm_error. METHODS check_prerequisites IMPORTING !manifest TYPE /apmg/if_apm_types=>ty_manifest !is_force TYPE abap_bool RAISING /apmg/cx_apm_error. METHODS collect_actions IMPORTING !action TYPE ty_action CHANGING result TYPE ty_actions. METHODS check_actions IMPORTING !actions TYPE ty_actions RAISING /apmg/cx_apm_error. METHODS check_dependencies IMPORTING !manifest TYPE /apmg/if_apm_types=>ty_manifest !is_force TYPE abap_bool DEFAULT abap_false !is_production TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE ty_actions RAISING /apmg/cx_apm_error. METHODS check_dependency IMPORTING !list TYPE /apmg/if_apm_package_json=>ty_packages !dependency TYPE /apmg/if_apm_types=>ty_dependency !category TYPE string !is_force TYPE abap_bool DEFAULT abap_false !is_optional TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE ty_action RAISING /apmg/cx_apm_error. METHODS check_semver IMPORTING !name TYPE string !version TYPE string !range TYPE string !category TYPE string !is_force TYPE abap_bool DEFAULT abap_false !is_optional TYPE abap_bool DEFAULT abap_false RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_command_installer DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm Command Installer * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * Note: This is a stateless class. Do not add any attributes! ************************************************************************ PUBLIC SECTION. CLASS-METHODS install_package IMPORTING !registry TYPE string !manifest TYPE /apmg/if_apm_types=>ty_manifest !package TYPE devclass !name TYPE string !version TYPE string !is_production TYPE abap_bool RAISING /apmg/cx_apm_error. CLASS-METHODS uninstall_package IMPORTING !name TYPE string !version TYPE string !package TYPE devclass RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_command_integrity DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm Command Integrity Checks * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * Note: This is a stateless class. Do not add any attributes! ************************************************************************ PUBLIC SECTION. CLASS-METHODS check_integrity IMPORTING !tarball TYPE xstring !dist TYPE /apmg/if_apm_types=>ty_dist RAISING /apmg/cx_apm_error. CLASS-METHODS get_integrity IMPORTING !tarball TYPE xstring RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_dist RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_initial_key TYPE xstring VALUE ''. CLASS-METHODS calc_sha1 IMPORTING !data TYPE xstring RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS calc_sha512 IMPORTING !data TYPE xstring RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_command_login DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * apm Install Login * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * FUTURE: Enable web login (with optional 2fa) ************************************************************************ PUBLIC SECTION. CLASS-METHODS run IMPORTING !registry TYPE string !username TYPE string !password TYPE string !auth_type TYPE string DEFAULT 'legacy' RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_request, name TYPE string, password TYPE string, END OF ty_request, BEGIN OF ty_response, ok TYPE string, token TYPE string, END OF ty_response. METHODS execute IMPORTING !registry TYPE string !username TYPE string !password TYPE string !auth_type TYPE string RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_command_publish DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * apm Publish Command * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * TODO: Support dist-tags ************************************************************************ PUBLIC SECTION. CLASS-METHODS run IMPORTING !registry TYPE string !package TYPE devclass !is_dry_run TYPE abap_bool DEFAULT abap_false RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. METHODS execute IMPORTING !registry TYPE string !package TYPE devclass !is_dry_run TYPE abap_bool RAISING /apmg/cx_apm_error ##NEEDED. METHODS check_package IMPORTING !package TYPE devclass RAISING /apmg/cx_apm_error. METHODS check_packument IMPORTING !packument TYPE /apmg/if_apm_types=>ty_packument !package_json TYPE /apmg/if_apm_types=>ty_package_json RAISING /apmg/cx_apm_error. METHODS get_package_json IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_package_json RAISING /apmg/cx_apm_error. METHODS serialize_package IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE zif_abapgit_definitions=>ty_files_item_tt RAISING /apmg/cx_apm_error. METHODS attach_object_list IMPORTING !files TYPE zif_abapgit_definitions=>ty_files_item_tt CHANGING !packument TYPE /apmg/if_apm_types=>ty_packument RAISING /apmg/cx_apm_error. METHODS get_tar IMPORTING !package_json TYPE /apmg/if_apm_types=>ty_package_json !files TYPE zif_abapgit_definitions=>ty_files_item_tt RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_tar RAISING /apmg/cx_apm_error. METHODS init_package IMPORTING !packument TYPE /apmg/if_apm_types=>ty_packument !package_json TYPE /apmg/if_apm_types=>ty_package_json RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_packument RAISING /apmg/cx_apm_error. METHODS attach_tarball IMPORTING !registry TYPE string !version TYPE string !tar TYPE REF TO /apmg/cl_apm_tar CHANGING !packument TYPE /apmg/if_apm_types=>ty_packument RAISING /apmg/cx_apm_error. METHODS publish_package IMPORTING !registry TYPE string !packument TYPE /apmg/if_apm_types=>ty_packument RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_command_uninstall DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * apm Uninstall Command * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS run IMPORTING !package TYPE devclass RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. METHODS execute IMPORTING !package TYPE devclass RAISING /apmg/cx_apm_error. METHODS check_package IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_package_json RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_command_unpublish DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * apm Unpublish Command * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS run IMPORTING !registry TYPE string !name TYPE string !version TYPE string OPTIONAL RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. METHODS execute IMPORTING !registry TYPE string !name TYPE string !version TYPE string RAISING /apmg/cx_apm_error. METHODS get_tarball IMPORTING !packument TYPE /apmg/if_apm_types=>ty_packument !version TYPE string RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. METHODS remove_version IMPORTING !packument TYPE /apmg/if_apm_types=>ty_packument !version TYPE string !tarball TYPE string RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_packument RAISING /apmg/cx_apm_error. METHODS update_dist_tags IMPORTING !packument TYPE /apmg/if_apm_types=>ty_packument !version TYPE string RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_packument RAISING /apmg/cx_apm_error. METHODS delete_tarball IMPORTING !registry TYPE string !packument TYPE /apmg/if_apm_types=>ty_packument !tarball TYPE string RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. METHODS unpublish_complete_package IMPORTING !registry TYPE string !packument TYPE /apmg/if_apm_types=>ty_packument RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. METHODS unpublish_package_version IMPORTING !registry TYPE string !packument TYPE /apmg/if_apm_types=>ty_packument RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_command_update DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * apm Update Command * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS run IMPORTING !registry TYPE string !package TYPE devclass !is_dry_run TYPE abap_bool DEFAULT abap_false !is_production TYPE abap_bool DEFAULT abap_false RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. METHODS execute IMPORTING !registry TYPE string !package TYPE devclass !is_dry_run TYPE abap_bool !is_production TYPE abap_bool RAISING /apmg/cx_apm_error. METHODS get_package IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_package_json RAISING /apmg/cx_apm_error. METHODS get_bundle_dependencies IMPORTING !package TYPE devclass !manifest TYPE /apmg/if_apm_types=>ty_manifest RETURNING VALUE(result) TYPE /apmg/if_apm_importer=>ty_dependencies RAISING /apmg/cx_apm_error. METHODS get_max_satisfying_version IMPORTING !registry TYPE string !name TYPE string !range TYPE string RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. METHODS process_dependencies IMPORTING !registry TYPE string !dependencies TYPE /apmg/if_apm_importer=>ty_dependencies RETURNING VALUE(result) TYPE /apmg/if_apm_importer=>ty_dependencies RAISING /apmg/cx_apm_error. METHODS is_newer IMPORTING !version_installed TYPE string !version_next TYPE string RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. METHODS save_package IMPORTING !package TYPE devclass !manifest TYPE /apmg/if_apm_types=>ty_manifest RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_command_utils DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm Command Utilities * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * Note: This is a stateless class. Do not add any attributes! ************************************************************************ PUBLIC SECTION. CLASS-METHODS fetch_registry IMPORTING !registry TYPE string !url TYPE string !command TYPE string OPTIONAL !auth_type TYPE string OPTIONAL !username TYPE string OPTIONAL !password TYPE string OPTIONAL !method TYPE string DEFAULT /apmg/if_apm_http_agent=>c_method-get !payload TYPE any OPTIONAL RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_http_response RAISING /apmg/cx_apm_error. CLASS-METHODS get_packument_from_registry IMPORTING !registry TYPE string !name TYPE string !write TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_packument RAISING /apmg/cx_apm_error. CLASS-METHODS get_manifest_from_registry IMPORTING !registry TYPE string !name TYPE string !version TYPE string !write TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_manifest RAISING /apmg/cx_apm_error. CLASS-METHODS get_tarball_from_registry IMPORTING !registry TYPE string !name TYPE string !tarball TYPE string RETURNING VALUE(result) TYPE xstring RAISING /apmg/cx_apm_error. CLASS-METHODS get_versions_from_packument IMPORTING packument TYPE /apmg/if_apm_types=>ty_packument RETURNING VALUE(result) TYPE string_table RAISING /apmg/cx_apm_error. CLASS-METHODS get_package_from_name IMPORTING name TYPE string RETURNING VALUE(result) TYPE string. CLASS-METHODS get_scope_from_name IMPORTING name TYPE string RETURNING VALUE(result) TYPE string. CLASS-METHODS get_name_from_scope_package IMPORTING scope TYPE string OPTIONAL package TYPE string RETURNING VALUE(result) TYPE string. CLASS-METHODS check_response IMPORTING !response TYPE REF TO /apmg/if_apm_http_response !text TYPE string DEFAULT 'Error' RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS get_agent IMPORTING !host TYPE string RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_http_agent RAISING /apmg/cx_apm_error. CLASS-METHODS get_abap_version RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: BEGIN OF c_header, apm_command TYPE string VALUE 'apm-command', apm_auth_type TYPE string VALUE 'apm-auth-type', END OF c_header. CLASS-METHODS get_error IMPORTING !response TYPE string RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_emoji DEFINITION CREATE PRIVATE. ************************************************************************ * ABAP Emoji * * Support for GitHub Emoji * https://docs.github.com/en/rest/emojis/emojis * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CONSTANTS c_version TYPE string VALUE '3.0.0' ##NEEDED. TYPES: ty_code TYPE string_table, ty_results TYPE SORTED TABLE OF string WITH UNIQUE KEY table_line, BEGIN OF ty_emoji, name TYPE string, img TYPE string, code TYPE string, hex TYPE string, END OF ty_emoji, ty_emojis TYPE HASHED TABLE OF ty_emoji WITH UNIQUE KEY name WITH NON-UNIQUE SORTED KEY code COMPONENTS code. CLASS-METHODS create RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_emoji. METHODS constructor. METHODS get_emoji_css IMPORTING size_in_px TYPE i DEFAULT 20 RETURNING VALUE(result) TYPE ty_code. METHODS get_emoji_list RETURNING VALUE(result) TYPE ty_code. METHODS find_emoji IMPORTING !regex TYPE string RETURNING VALUE(result) TYPE ty_results. METHODS format_emoji IMPORTING !line TYPE string !base_url TYPE string OPTIONAL RETURNING VALUE(result) TYPE string. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_base_url TYPE string VALUE 'https://github.githubassets.com/images/icons/emoji'. CLASS-DATA emoji TYPE REF TO /apmg/cl_apm_emoji. DATA emojis TYPE ty_emojis. METHODS init_emoji_list. ENDCLASS. CLASS /apmg/cl_apm_exception_viewer DEFINITION CREATE PUBLIC. PUBLIC SECTION. METHODS constructor IMPORTING !ix_error TYPE REF TO /apmg/cx_apm_error. METHODS goto_source RAISING /apmg/cx_apm_error. METHODS goto_message RAISING /apmg/cx_apm_error. METHODS show_callstack RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. DATA: mx_error TYPE REF TO /apmg/cx_apm_error, mt_callstack TYPE abap_callstack. METHODS: build_top_of_list IMPORTING is_top_of_stack TYPE abap_callstack_line RETURNING VALUE(ro_form) TYPE REF TO cl_salv_form_element, add_row IMPORTING io_grid TYPE REF TO cl_salv_form_layout_grid iv_col_1 TYPE csequence iv_col_2 TYPE csequence, on_double_click FOR EVENT double_click OF cl_salv_events_table IMPORTING row column, set_text IMPORTING io_columns TYPE REF TO cl_salv_columns_table iv_column TYPE lvc_fname iv_text TYPE string RAISING cx_static_check, goto_source_code IMPORTING is_callstack TYPE abap_callstack_line RAISING /apmg/cx_apm_error, extract_classname IMPORTING iv_mainprogram TYPE abap_callstack_line-mainprogram RETURNING VALUE(rv_classname) TYPE tadir-obj_name, get_top_of_callstack RETURNING VALUE(rs_top_of_callstack) TYPE abap_callstack_line RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_file_importer DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm Files for Importer * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES /apmg/if_apm_file_importer. METHODS constructor IMPORTING !item TYPE /apmg/if_apm_importer=>ty_item !files TYPE zif_abapgit_git_definitions=>ty_files_tt. PROTECTED SECTION. PRIVATE SECTION. DATA: item TYPE /apmg/if_apm_object=>ty_item, files TYPE zif_abapgit_git_definitions=>ty_files_tt. METHODS get_file_name IMPORTING !extension TYPE string !extra TYPE string OPTIONAL RETURNING VALUE(result) TYPE string. METHODS get_file_content IMPORTING !extension TYPE string !extra TYPE string OPTIONAL RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_frontend_services DEFINITION CREATE PRIVATE FRIENDS /apmg/cl_apm_gui_factory. PUBLIC SECTION. INTERFACES /apmg/if_apm_frontend_services. PROTECTED SECTION. PRIVATE SECTION. CLASS-DATA gv_initial_folder TYPE string. METHODS get_path_from_fullname IMPORTING iv_fullname TYPE string RETURNING VALUE(rv_path) TYPE string. ENDCLASS. CLASS zcl_abapgit_log DEFINITION CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_log . METHODS constructor IMPORTING iv_title TYPE string OPTIONAL. CLASS-METHODS from_exception IMPORTING io_x TYPE REF TO cx_root RETURNING VALUE(ro_log) TYPE REF TO zcl_abapgit_log. PROTECTED SECTION. TYPES: BEGIN OF ty_log, "in order of occurrence msg TYPE zif_abapgit_log=>ty_msg, item TYPE zif_abapgit_definitions=>ty_item, exception TYPE REF TO cx_root, END OF ty_log . DATA: mt_log TYPE STANDARD TABLE OF ty_log WITH DEFAULT KEY . DATA mv_title TYPE string . METHODS get_messages_status IMPORTING !it_msg TYPE zif_abapgit_log=>ty_msgs RETURNING VALUE(rv_status) TYPE sy-msgty . PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_gui_event DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES /apmg/if_apm_gui_event. CLASS-METHODS class_constructor. CLASS-METHODS new IMPORTING !ii_gui_services TYPE REF TO /apmg/if_apm_gui_services OPTIONAL !iv_action TYPE clike !iv_getdata TYPE clike OPTIONAL !it_postdata TYPE /apmg/if_apm_html_viewer=>ty_post_data OPTIONAL RETURNING VALUE(ro_instance) TYPE REF TO /apmg/cl_apm_gui_event. METHODS constructor IMPORTING !ii_gui_services TYPE REF TO /apmg/if_apm_gui_services OPTIONAL !iv_action TYPE clike !iv_getdata TYPE clike OPTIONAL !it_postdata TYPE /apmg/if_apm_html_viewer=>ty_post_data OPTIONAL. PROTECTED SECTION. PRIVATE SECTION. DATA mo_query TYPE REF TO /apmg/cl_apm_string_map. DATA mo_form_data TYPE REF TO /apmg/cl_apm_string_map. CLASS-DATA gv_non_breaking_space TYPE string . TYPES: BEGIN OF ty_name_value, name TYPE string, value TYPE string, END OF ty_name_value. TYPES ty_name_value_tt TYPE STANDARD TABLE OF ty_name_value WITH DEFAULT KEY. METHODS fields_to_map IMPORTING it_fields TYPE ty_name_value_tt RETURNING VALUE(ro_string_map) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. CLASS-METHODS parse_post_form_data IMPORTING !it_post_data TYPE /apmg/if_apm_html_viewer=>ty_post_data !iv_upper_cased TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rt_fields) TYPE ty_name_value_tt . CLASS-METHODS parse_fields IMPORTING !iv_string TYPE clike !iv_upper_cased TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rt_fields) TYPE ty_name_value_tt . CLASS-METHODS parse_fields_upper_case_name IMPORTING !iv_string TYPE clike RETURNING VALUE(rt_fields) TYPE ty_name_value_tt . CLASS-METHODS translate_postdata IMPORTING !it_postdata TYPE /apmg/if_apm_html_viewer=>ty_post_data RETURNING VALUE(rv_string) TYPE string . CLASS-METHODS field_keys_to_upper CHANGING !ct_fields TYPE ty_name_value_tt . CLASS-METHODS unescape IMPORTING !iv_string TYPE string RETURNING VALUE(rv_string) TYPE string . ENDCLASS. CLASS /apmg/cl_apm_gui DEFINITION CREATE PUBLIC. PUBLIC SECTION. INTERFACES /apmg/if_apm_gui_services. CONSTANTS: BEGIN OF c_event_state, not_handled TYPE i VALUE 0, re_render TYPE i VALUE 1, new_page TYPE i VALUE 2, go_back TYPE i VALUE 3, no_more_act TYPE i VALUE 4, new_page_w_bookmark TYPE i VALUE 5, go_back_to_bookmark TYPE i VALUE 6, new_page_replacing TYPE i VALUE 7, END OF c_event_state. METHODS go_home IMPORTING !iv_action TYPE string RAISING /apmg/cx_apm_error. METHODS back IMPORTING !iv_to_bookmark TYPE abap_bool DEFAULT abap_false !iv_graceful TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rv_exit) TYPE abap_bool RAISING /apmg/cx_apm_error. METHODS back_graceful RETURNING VALUE(rv_handled) TYPE abap_bool RAISING /apmg/cx_apm_error. METHODS on_event FOR EVENT sapevent OF /apmg/if_apm_html_viewer IMPORTING !action !frame !getdata !postdata !query_table. METHODS constructor IMPORTING !io_component TYPE REF TO object OPTIONAL !ii_asset_man TYPE REF TO /apmg/if_apm_gui_asset_manager OPTIONAL !ii_hotkey_ctl TYPE REF TO /apmg/if_apm_gui_hotkey_ctl OPTIONAL !ii_html_processor TYPE REF TO /apmg/if_apm_gui_html_processo OPTIONAL !iv_rollback_on_error TYPE abap_bool DEFAULT abap_true RAISING /apmg/cx_apm_error. METHODS free. METHODS set_focus RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_page_stack, page TYPE REF TO /apmg/if_apm_gui_renderable, bookmark TYPE abap_bool, END OF ty_page_stack. DATA: mv_rollback_on_error TYPE abap_bool, mi_cur_page TYPE REF TO /apmg/if_apm_gui_renderable, mt_stack TYPE STANDARD TABLE OF ty_page_stack, mt_event_handlers TYPE STANDARD TABLE OF REF TO /apmg/if_apm_gui_event_handler, mi_router TYPE REF TO /apmg/if_apm_gui_event_handler, mi_asset_man TYPE REF TO /apmg/if_apm_gui_asset_manager, mi_hotkey_ctl TYPE REF TO /apmg/if_apm_gui_hotkey_ctl, mi_html_processor TYPE REF TO /apmg/if_apm_gui_html_processo, mi_html_viewer TYPE REF TO /apmg/if_apm_html_viewer, mo_html_parts TYPE REF TO /apmg/cl_apm_html_parts, mi_common_log TYPE REF TO zif_abapgit_log. METHODS cache_html IMPORTING !iv_text TYPE string RETURNING VALUE(rv_url) TYPE string RAISING /apmg/cx_apm_error. METHODS startup RAISING /apmg/cx_apm_error. METHODS render RAISING /apmg/cx_apm_error. METHODS call_page IMPORTING !ii_page TYPE REF TO /apmg/if_apm_gui_renderable !iv_with_bookmark TYPE abap_bool DEFAULT abap_false !iv_replacing TYPE abap_bool DEFAULT abap_false RAISING /apmg/cx_apm_error. METHODS handle_action IMPORTING !iv_action TYPE c !iv_getdata TYPE c OPTIONAL !it_postdata TYPE /apmg/if_apm_html_viewer=>ty_post_data OPTIONAL. METHODS handle_error IMPORTING !ix_exception TYPE REF TO /apmg/cx_apm_error. METHODS is_page_modal IMPORTING !ii_page TYPE REF TO /apmg/if_apm_gui_renderable RETURNING VALUE(rv_yes) TYPE abap_bool. ENDCLASS. CLASS /apmg/cl_apm_gui_asset_manager DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES /apmg/if_apm_gui_asset_manager. CLASS-METHODS create RETURNING VALUE(ri_asset_manager) TYPE REF TO /apmg/if_apm_gui_asset_manager. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_asset_entry. INCLUDE TYPE /apmg/if_apm_gui_asset_manager~ty_web_asset. TYPES: mime_name TYPE wwwdatatab-objid, END OF ty_asset_entry, ty_asset_register TYPE STANDARD TABLE OF ty_asset_entry WITH KEY url. DATA mt_asset_register TYPE ty_asset_register. METHODS get_mime_asset IMPORTING iv_mime_name TYPE c RETURNING VALUE(rv_xdata) TYPE xstring RAISING /apmg/cx_apm_error. METHODS load_asset IMPORTING is_asset_entry TYPE ty_asset_entry RETURNING VALUE(rs_asset) TYPE /apmg/if_apm_gui_asset_manager~ty_web_asset RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_buttons DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm GUI Buttons * * Copyright 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS advanced RETURNING VALUE(result) TYPE string. CLASS-METHODS help RETURNING VALUE(result) TYPE string. CLASS-METHODS package_list RETURNING VALUE(result) TYPE string. CLASS-METHODS settings RETURNING VALUE(result) TYPE string. CLASS-METHODS experimental RETURNING VALUE(result) TYPE string. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_gui_chunk_lib DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. TYPES: BEGIN OF ty_col_spec, tech_name TYPE string, display_name TYPE string, css_class TYPE string, add_tz TYPE abap_bool, title TYPE string, allow_order_by TYPE abap_bool, END OF ty_col_spec, ty_col_spec_tt TYPE STANDARD TABLE OF ty_col_spec WITH NON-UNIQUE KEY tech_name. CLASS-METHODS class_constructor. CLASS-METHODS render_error IMPORTING !ix_error TYPE REF TO /apmg/cx_apm_error OPTIONAL !iv_error TYPE string OPTIONAL !iv_extra_style TYPE string OPTIONAL RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html. CLASS-METHODS render_success IMPORTING !iv_message TYPE string RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html. CLASS-METHODS render_js_error_banner RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. CLASS-METHODS render_error_message_box IMPORTING !ix_error TYPE REF TO /apmg/cx_apm_error RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html. CLASS-METHODS render_table_header IMPORTING !it_col_spec TYPE ty_col_spec_tt !iv_order_by TYPE string !iv_order_descending TYPE abap_bool RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html. CLASS-METHODS render_table_footer IMPORTING !iv_message TYPE string RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html. CLASS-METHODS render_warning_banner IMPORTING !iv_text TYPE string RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html. CLASS-METHODS render_infopanel IMPORTING !iv_div_id TYPE string !iv_title TYPE string !iv_hide TYPE abap_bool DEFAULT abap_true !iv_hint TYPE string OPTIONAL !iv_scrollable TYPE abap_bool DEFAULT abap_true !io_content TYPE REF TO /apmg/if_apm_html RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. CLASS-METHODS render_registry_link IMPORTING !iv_name TYPE string !iv_version TYPE string OPTIONAL RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. CLASS-METHODS render_package_name IMPORTING !iv_package TYPE devclass !iv_interactive TYPE abap_bool DEFAULT abap_true !iv_suppress_title TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. CLASS-METHODS render_user_name IMPORTING !iv_username TYPE syuname !iv_interactive TYPE abap_bool DEFAULT abap_true !iv_icon_only TYPE abap_bool DEFAULT abap_false !iv_suppress_title TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. CLASS-METHODS render_transport IMPORTING !iv_transport TYPE trkorr !iv_interactive TYPE abap_bool DEFAULT abap_true !iv_icon_only TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. CLASS-METHODS render_timestamp IMPORTING !iv_timestamp TYPE timestampl RETURNING VALUE(rv_rendered) TYPE string. CLASS-METHODS render_text_input IMPORTING !iv_name TYPE string !iv_label TYPE string !iv_value TYPE string OPTIONAL !iv_max_length TYPE string OPTIONAL !iv_autofocus TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html. CLASS-METHODS render_label_list IMPORTING !it_labels TYPE string_table !io_label_colors TYPE REF TO /apmg/cl_apm_string_map !iv_clickable_action TYPE string OPTIONAL !iv_unlisted TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rv_html) TYPE string. CLASS-METHODS render_help_hint IMPORTING !iv_text_to_wrap TYPE string !iv_add_class TYPE string OPTIONAL RETURNING VALUE(rv_html) TYPE string. PROTECTED SECTION. PRIVATE SECTION. CLASS-DATA gv_time_zone TYPE timezone . CLASS-METHODS get_t100_text IMPORTING !iv_msgid TYPE scx_t100key-msgid !iv_msgno TYPE scx_t100key-msgno RETURNING VALUE(rv_text) TYPE string . CLASS-METHODS normalize_program_name IMPORTING !iv_program_name TYPE sy-repid RETURNING VALUE(rv_normalized_program_name) TYPE string . ENDCLASS. CLASS /apmg/cl_apm_gui_component DEFINITION ABSTRACT CREATE PUBLIC. ************************************************************************ * apm GUI Component * * Copyright 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ * adapted: gui_services PUBLIC SECTION. CONSTANTS: BEGIN OF c_html_parts, scripts TYPE string VALUE 'scripts', hidden_forms TYPE string VALUE 'hidden_forms', END OF c_html_parts. PROTECTED SECTION. METHODS register_deferred_script IMPORTING part TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS gui_services RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_services RAISING /apmg/cx_apm_error. METHODS register_handlers RAISING /apmg/cx_apm_error. PRIVATE SECTION. DATA gui_service TYPE REF TO /apmg/if_apm_gui_services. METHODS register_event_handler IMPORTING event_handler TYPE REF TO /apmg/if_apm_gui_event_handler OPTIONAL RAISING /apmg/cx_apm_error. METHODS register_hotkeys IMPORTING hotkey_provider TYPE REF TO /apmg/if_apm_gui_hotkeys OPTIONAL RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_css_processor DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. METHODS constructor IMPORTING !ii_asset_manager TYPE REF TO /apmg/if_apm_gui_asset_manager. METHODS add_file IMPORTING !iv_url TYPE string. METHODS process RETURNING VALUE(rv_result) TYPE string RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_css_var, name TYPE string, value TYPE string, END OF ty_css_var, ty_css_vars TYPE SORTED TABLE OF ty_css_var WITH UNIQUE KEY name. DATA: mi_asset_manager TYPE REF TO /apmg/if_apm_gui_asset_manager, mt_files TYPE string_table. METHODS get_css_vars_in_string IMPORTING iv_string TYPE string RETURNING VALUE(rt_variables) TYPE ty_css_vars. METHODS resolve_var_recursively IMPORTING iv_variable_name TYPE string CHANGING ct_variables TYPE ty_css_vars RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_dlg_deprecate DEFINITION INHERITING FROM /apmg/cl_apm_gui_component FINAL CREATE PRIVATE. ************************************************************************ * apm GUI Dialog for Deprecate Command * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES: /apmg/if_apm_gui_event_handler, /apmg/if_apm_gui_menu_provider, /apmg/if_apm_gui_renderable. CLASS-METHODS create IMPORTING !package TYPE devclass OPTIONAL RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_renderable RAISING /apmg/cx_apm_error. METHODS constructor IMPORTING !package TYPE devclass RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_params, package TYPE devclass, name TYPE string, version TYPE string, message TYPE string, END OF ty_params. CONSTANTS: BEGIN OF c_id, package TYPE string VALUE 'package', name TYPE string VALUE 'name', version TYPE string VALUE 'version', message TYPE string VALUE 'message', END OF c_id. CONSTANTS: BEGIN OF c_action, choose_package TYPE string VALUE 'choose-package', deprecate TYPE string VALUE 'deprecate', refresh TYPE string VALUE 'refresh', END OF c_action. DATA: registry TYPE string, deprecate_package TYPE devclass, form TYPE REF TO /apmg/cl_apm_html_form, form_data TYPE REF TO /apmg/cl_apm_string_map, form_util TYPE REF TO /apmg/cl_apm_html_form_utils, validation_log TYPE REF TO /apmg/cl_apm_string_map. METHODS get_form_schema RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_html_form. METHODS get_parameters IMPORTING !form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(result) TYPE ty_params. METHODS validate_form IMPORTING !form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS read_package IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS confirm_popup_version IMPORTING !params TYPE ty_params RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_dlg_init DEFINITION INHERITING FROM /apmg/cl_apm_gui_component FINAL CREATE PRIVATE. ************************************************************************ * apm GUI Dialog for Init Command * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES: /apmg/if_apm_gui_event_handler, /apmg/if_apm_gui_renderable. CLASS-METHODS create RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_renderable RAISING /apmg/cx_apm_error. METHODS constructor RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_params, package TYPE devclass, name TYPE string, version TYPE string, description TYPE string, private TYPE abap_bool, labels TYPE string, package_json TYPE /apmg/if_apm_types=>ty_package_json, END OF ty_params. CONSTANTS: BEGIN OF c_id, package TYPE string VALUE 'package', name TYPE string VALUE 'name', version TYPE string VALUE 'version', description TYPE string VALUE 'description', private TYPE string VALUE 'private', labels TYPE string VALUE 'labels', END OF c_id. CONSTANTS: BEGIN OF c_action, choose_package TYPE string VALUE 'choose-package', choose_labels TYPE string VALUE 'choose-labels', create_package TYPE string VALUE 'create-package', init_package TYPE string VALUE 'init-package', END OF c_action . DATA: form TYPE REF TO /apmg/cl_apm_html_form, form_data TYPE REF TO /apmg/cl_apm_string_map, form_util TYPE REF TO /apmg/cl_apm_html_form_utils, validation_log TYPE REF TO /apmg/cl_apm_string_map. METHODS get_form_schema RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_html_form. METHODS get_parameters IMPORTING !form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(result) TYPE ty_params. METHODS validate_form IMPORTING !form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS choose_labels RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_dlg_install DEFINITION INHERITING FROM /apmg/cl_apm_gui_component FINAL CREATE PRIVATE. ************************************************************************ * apm GUI Dialog for Install Command * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES: /apmg/if_apm_gui_event_handler, /apmg/if_apm_gui_menu_provider, /apmg/if_apm_gui_renderable. CLASS-METHODS create RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_renderable RAISING /apmg/cx_apm_error. METHODS constructor RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_params, package TYPE devclass, name TYPE string, version TYPE string, package_json TYPE /apmg/if_apm_types=>ty_package_json, END OF ty_params. CONSTANTS: BEGIN OF c_id, package TYPE string VALUE 'package', name TYPE string VALUE 'name', version TYPE string VALUE 'version', END OF c_id. CONSTANTS: BEGIN OF c_action, choose_package TYPE string VALUE 'choose-package', create_package TYPE string VALUE 'create-package', install_package TYPE string VALUE 'install-package', END OF c_action . DATA: registry TYPE string, form TYPE REF TO /apmg/cl_apm_html_form, form_data TYPE REF TO /apmg/cl_apm_string_map, form_util TYPE REF TO /apmg/cl_apm_html_form_utils, validation_log TYPE REF TO /apmg/cl_apm_string_map. METHODS get_form_schema RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_html_form. METHODS get_parameters IMPORTING !form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(result) TYPE ty_params. METHODS validate_form IMPORTING !form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_dlg_publish DEFINITION INHERITING FROM /apmg/cl_apm_gui_component FINAL CREATE PRIVATE. ************************************************************************ * apm GUI Dialog for Publish Command * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES: /apmg/if_apm_gui_event_handler, /apmg/if_apm_gui_menu_provider, /apmg/if_apm_gui_renderable. CLASS-METHODS create IMPORTING !package TYPE devclass OPTIONAL RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_renderable RAISING /apmg/cx_apm_error. METHODS constructor IMPORTING !package TYPE devclass RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_params, package TYPE devclass, name TYPE string, version TYPE string, END OF ty_params. CONSTANTS: BEGIN OF c_id, package TYPE string VALUE 'package', name TYPE string VALUE 'name', version TYPE string VALUE 'version', END OF c_id. CONSTANTS: BEGIN OF c_action, choose_package TYPE string VALUE 'choose-package', publish_package TYPE string VALUE 'publish-package', refresh TYPE string VALUE 'refresh', END OF c_action. DATA: registry TYPE string, pubish_package TYPE devclass, form TYPE REF TO /apmg/cl_apm_html_form, form_data TYPE REF TO /apmg/cl_apm_string_map, form_util TYPE REF TO /apmg/cl_apm_html_form_utils, validation_log TYPE REF TO /apmg/cl_apm_string_map. METHODS get_form_schema RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_html_form. METHODS get_parameters IMPORTING !form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(result) TYPE ty_params. METHODS validate_form IMPORTING !form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS read_package IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS confirm_popup IMPORTING !params TYPE ty_params RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_dlg_undepreca DEFINITION INHERITING FROM /apmg/cl_apm_gui_component FINAL CREATE PRIVATE. ************************************************************************ * apm GUI Dialog for Undeprecate Command * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES: /apmg/if_apm_gui_event_handler, /apmg/if_apm_gui_menu_provider, /apmg/if_apm_gui_renderable. CLASS-METHODS create IMPORTING !package TYPE devclass OPTIONAL RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_renderable RAISING /apmg/cx_apm_error. METHODS constructor IMPORTING !package TYPE devclass RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_params, package TYPE devclass, name TYPE string, version TYPE string, END OF ty_params. CONSTANTS: BEGIN OF c_id, package TYPE string VALUE 'package', name TYPE string VALUE 'name', version TYPE string VALUE 'version', END OF c_id. CONSTANTS: BEGIN OF c_action, choose_package TYPE string VALUE 'choose-package', undeprecate TYPE string VALUE 'undeprecate', END OF c_action. DATA: registry TYPE string, undeprecate_package TYPE devclass, form TYPE REF TO /apmg/cl_apm_html_form, form_data TYPE REF TO /apmg/cl_apm_string_map, form_util TYPE REF TO /apmg/cl_apm_html_form_utils, validation_log TYPE REF TO /apmg/cl_apm_string_map. METHODS get_form_schema RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_html_form. METHODS get_parameters IMPORTING !form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(result) TYPE ty_params. METHODS validate_form IMPORTING !form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS read_package IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS confirm_popup_version IMPORTING !params TYPE ty_params RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_dlg_uninstall DEFINITION INHERITING FROM /apmg/cl_apm_gui_component FINAL CREATE PRIVATE. ************************************************************************ * apm GUI Dialog for Uninstall Command * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES: /apmg/if_apm_gui_event_handler, /apmg/if_apm_gui_renderable. CLASS-METHODS create IMPORTING !package TYPE devclass OPTIONAL RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_renderable RAISING /apmg/cx_apm_error. METHODS constructor IMPORTING !package TYPE devclass RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_params, package TYPE devclass, name TYPE string, version TYPE string, END OF ty_params. CONSTANTS: BEGIN OF c_id, package TYPE string VALUE 'package', name TYPE string VALUE 'name', version TYPE string VALUE 'version', END OF c_id. CONSTANTS: BEGIN OF c_action, choose_package TYPE string VALUE 'choose-package', uninstall_package TYPE string VALUE 'uninstall-package', refresh TYPE string VALUE 'refresh', END OF c_action. DATA: registry TYPE string, unpubish_package TYPE devclass, form TYPE REF TO /apmg/cl_apm_html_form, form_data TYPE REF TO /apmg/cl_apm_string_map, form_util TYPE REF TO /apmg/cl_apm_html_form_utils, validation_log TYPE REF TO /apmg/cl_apm_string_map. METHODS get_form_schema RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_html_form. METHODS get_parameters IMPORTING !form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(result) TYPE ty_params. METHODS validate_form IMPORTING !io_form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS read_package IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS confirm_popup IMPORTING !params TYPE ty_params RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_dlg_unpublish DEFINITION INHERITING FROM /apmg/cl_apm_gui_component FINAL CREATE PRIVATE. ************************************************************************ * apm GUI Dialog for Unpublish Command * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES: /apmg/if_apm_gui_event_handler, /apmg/if_apm_gui_menu_provider, /apmg/if_apm_gui_renderable. CLASS-METHODS create IMPORTING !package TYPE devclass OPTIONAL RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_renderable RAISING /apmg/cx_apm_error. METHODS constructor IMPORTING !package TYPE devclass RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_params, package TYPE devclass, name TYPE string, version TYPE string, END OF ty_params. CONSTANTS: BEGIN OF c_id, package TYPE string VALUE 'package', name TYPE string VALUE 'name', version TYPE string VALUE 'version', END OF c_id. CONSTANTS: BEGIN OF c_action, choose_package TYPE string VALUE 'choose-package', unpublish_package TYPE string VALUE 'unpublish-package', unpublish_version TYPE string VALUE 'unpublish-version', refresh TYPE string VALUE 'refresh', END OF c_action. DATA: registry TYPE string, unpublish_package TYPE devclass, form TYPE REF TO /apmg/cl_apm_html_form, form_data TYPE REF TO /apmg/cl_apm_string_map, form_util TYPE REF TO /apmg/cl_apm_html_form_utils, validation_log TYPE REF TO /apmg/cl_apm_string_map. METHODS get_form_schema RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_html_form. METHODS get_parameters IMPORTING !form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(result) TYPE ty_params. METHODS validate_form IMPORTING !form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS read_package IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS confirm_popup_version IMPORTING !params TYPE ty_params RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. METHODS confirm_popup_package IMPORTING !params TYPE ty_params RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_router DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm GUI Router * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES: /apmg/if_apm_gui_event_handler, /apmg/if_apm_gui_router. PROTECTED SECTION. PRIVATE SECTION. METHODS general_page_routing IMPORTING !event TYPE REF TO /apmg/if_apm_gui_event RETURNING VALUE(result) TYPE /apmg/if_apm_gui_event_handler=>ty_handling_result RAISING /apmg/cx_apm_error. METHODS command_dialogs IMPORTING !event TYPE REF TO /apmg/if_apm_gui_event RETURNING VALUE(result) TYPE /apmg/if_apm_gui_event_handler=>ty_handling_result RAISING /apmg/cx_apm_error. METHODS utility_actions IMPORTING !event TYPE REF TO /apmg/if_apm_gui_event RETURNING VALUE(result) TYPE /apmg/if_apm_gui_event_handler=>ty_handling_result RAISING /apmg/cx_apm_error. METHODS sap_gui_actions IMPORTING !event TYPE REF TO /apmg/if_apm_gui_event RETURNING VALUE(result) TYPE /apmg/if_apm_gui_event_handler=>ty_handling_result RAISING /apmg/cx_apm_error. METHODS browser_actions IMPORTING !event TYPE REF TO /apmg/if_apm_gui_event RETURNING VALUE(result) TYPE /apmg/if_apm_gui_event_handler=>ty_handling_result RAISING /apmg/cx_apm_error. METHODS other_utilities IMPORTING !event TYPE REF TO /apmg/if_apm_gui_event RETURNING VALUE(result) TYPE /apmg/if_apm_gui_event_handler=>ty_handling_result RAISING /apmg/cx_apm_error. CLASS-METHODS jump_object IMPORTING !obj_type TYPE string !obj_name TYPE string !filename TYPE string !sub_type TYPE string OPTIONAL !sub_name TYPE string OPTIONAL !line TYPE string OPTIONAL !new_window TYPE string DEFAULT 'X' RAISING /apmg/cx_apm_error. CLASS-METHODS jump_display_transport IMPORTING !transport TYPE trkorr RAISING /apmg/cx_apm_error. CLASS-METHODS jump_display_user IMPORTING !username TYPE syuname RAISING /apmg/cx_apm_error. METHODS call_browser IMPORTING !url TYPE csequence RAISING /apmg/cx_apm_error. METHODS main_page RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_renderable RAISING /apmg/cx_apm_error ##CALLED. METHODS toggle_favorite IMPORTING !package TYPE csequence RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_hotkey_ctl DEFINITION INHERITING FROM /apmg/cl_apm_gui_component FINAL CREATE PUBLIC. ************************************************************************ * apm GUI Hotkey Controller * * Copyright 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ * adapted: gui_component and settings PUBLIC SECTION. INTERFACES /apmg/if_apm_gui_hotkeys. INTERFACES /apmg/if_apm_gui_hotkey_ctl. INTERFACES /apmg/if_apm_gui_renderable. CONSTANTS c_showhotkeys_action TYPE string VALUE `showHotkeys` ##NO_TEXT. CLASS-METHODS should_show_hint RETURNING VALUE(result) TYPE abap_bool. METHODS constructor RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. DATA: hotkeys TYPE /apmg/if_apm_gui_hotkeys=>ty_hotkeys_with_descr, keyboard_settings TYPE /apmg/if_apm_settings=>ty_keyboard_settings, is_visible TYPE abap_bool. CLASS-DATA was_hint_shown TYPE abap_bool. METHODS render_scripts IMPORTING !hotkeys TYPE /apmg/if_apm_gui_hotkeys=>ty_hotkeys_with_descr RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html. ENDCLASS. CLASS /apmg/cl_apm_gui_html_viewer DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES /apmg/if_apm_html_viewer. METHODS constructor IMPORTING !io_container TYPE REF TO cl_gui_container DEFAULT cl_gui_container=>screen0 !iv_disable_query_table TYPE abap_bool DEFAULT abap_true. PROTECTED SECTION. DATA mo_html_viewer TYPE REF TO cl_gui_html_viewer. METHODS on_event FOR EVENT sapevent OF cl_gui_html_viewer IMPORTING !action !frame !getdata !postdata !query_table. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_popups DEFINITION FINAL CREATE PRIVATE FRIENDS /apmg/cl_apm_gui_factory. ************************************************************************ * apm Popups * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * apm: remove irrelevant method implementations PUBLIC SECTION. INTERFACES /apmg/if_apm_popups. CLASS-METHODS center IMPORTING !iv_width TYPE i !iv_height TYPE i RETURNING VALUE(rs_position) TYPE /apmg/if_apm_popups=>ty_popup_position. PROTECTED SECTION. PRIVATE SECTION. DATA ms_position TYPE /apmg/if_apm_popups=>ty_popup_position. ENDCLASS. CLASS /apmg/cl_apm_gui_factory DEFINITION CREATE PRIVATE. ************************************************************************ * apm GUI Factory * * Copyright 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ * adapted: router, hotkey_controller PUBLIC SECTION. CLASS-METHODS get_gui RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_gui RAISING /apmg/cx_apm_error. CLASS-METHODS get_gui_services RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_services RAISING /apmg/cx_apm_error. CLASS-METHODS get_frontend_services RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_frontend_services. CLASS-METHODS get_html_viewer IMPORTING !container TYPE REF TO cl_gui_container DEFAULT cl_gui_container=>screen0 !disable_query_table TYPE abap_bool DEFAULT abap_true RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html_viewer. CLASS-METHODS get_popups RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_popups. PROTECTED SECTION. PRIVATE SECTION. CLASS-DATA: gui TYPE REF TO /apmg/cl_apm_gui, gui_service TYPE REF TO /apmg/if_apm_gui_services, frontend_services TYPE REF TO /apmg/if_apm_frontend_services, html_viewer TYPE REF TO /apmg/if_apm_html_viewer, popups TYPE REF TO /apmg/if_apm_popups. CLASS-METHODS get_asset_manager RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_asset_manager RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_html_processo DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES /apmg/if_apm_gui_html_processo. CONSTANTS c_css_build_name TYPE string VALUE 'css/bundle.css' ##NO_TEXT. CONSTANTS c_preprocess_marker TYPE string VALUE `` ##NO_TEXT. CONSTANTS c_comment_start TYPE string VALUE `` ##NO_TEXT. METHODS constructor IMPORTING !ii_asset_man TYPE REF TO /apmg/if_apm_gui_asset_manager. METHODS preserve_css IMPORTING !iv_css_url TYPE string. PROTECTED SECTION. PRIVATE SECTION. DATA mt_preserve_css TYPE string_table. DATA mi_asset_man TYPE REF TO /apmg/if_apm_gui_asset_manager. METHODS patch_html IMPORTING iv_html TYPE string EXPORTING ev_html TYPE string et_css_urls TYPE string_table RAISING /apmg/cx_apm_error. METHODS is_preserved IMPORTING !iv_css_url TYPE string RETURNING VALUE(rv_yes) TYPE abap_bool. METHODS find_head_offset IMPORTING iv_html TYPE string RETURNING VALUE(rv_head_end) TYPE i RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_menus DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm GUI Menus * * Copyright 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS advanced RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_html_toolbar. CLASS-METHODS help RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_html_toolbar. CLASS-METHODS back RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_html_toolbar. CLASS-METHODS settings RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_html_toolbar. CLASS-METHODS registry IMPORTING registry TYPE string RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_html_toolbar. CLASS-METHODS experimental IMPORTING !menu TYPE REF TO /apmg/cl_apm_html_toolbar. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_gui_page DEFINITION INHERITING FROM /apmg/cl_apm_gui_component ABSTRACT CREATE PUBLIC. ************************************************************************ * apm GUI Page * * Copyright 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ * adapted: gui_component, settings, title, and footer PUBLIC SECTION. INTERFACES: /apmg/if_apm_gui_modal, /apmg/if_apm_gui_renderable, /apmg/if_apm_gui_event_handler, /apmg/if_apm_gui_error_handler. TYPES: BEGIN OF ty_control, page_layout TYPE string, page_title TYPE string, page_menu TYPE REF TO /apmg/cl_apm_html_toolbar, page_menu_provider TYPE REF TO /apmg/if_apm_gui_menu_provider, page_title_provider TYPE REF TO /apmg/if_apm_gui_page_title, extra_css_url TYPE string, extra_js_url TYPE string, show_as_modal TYPE abap_bool, END OF ty_control. CONSTANTS: BEGIN OF c_page_layout, centered TYPE string VALUE `centered`, full_width TYPE string VALUE `full_width`, END OF c_page_layout. METHODS constructor RAISING /apmg/cx_apm_error. PROTECTED SECTION. DATA page_control TYPE ty_control. METHODS render_content " TODO refactor, render child directly ABSTRACT RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. PRIVATE SECTION. CONSTANTS: BEGIN OF c_ui_theme, default TYPE string VALUE 'default', dark TYPE string VALUE 'dark', belize TYPE string VALUE 'belize', synced_with_gui TYPE string VALUE 'synced_with_gui', END OF c_ui_theme. DATA settings TYPE /apmg/if_apm_settings=>ty_settings. DATA error TYPE REF TO /apmg/cx_apm_error. DATA exception_viewer TYPE REF TO /apmg/cl_apm_exception_viewer. METHODS render_deferred_parts IMPORTING !part_category TYPE string RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS html_head RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html. METHODS header_stylesheet_links IMPORTING html TYPE REF TO /apmg/if_apm_html. METHODS header_script_links IMPORTING html TYPE REF TO /apmg/if_apm_html. METHODS title RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS footer IMPORTING !time TYPE string RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_link_hints RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_browser_control_warning RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_command_palettes RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_hotkey_overview RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_error_message_box RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS scripts RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS get_version_details RETURNING VALUE(result) TYPE string. METHODS is_edge_control_warning_needed RETURNING VALUE(result) TYPE abap_bool. ENDCLASS. CLASS /apmg/cl_apm_gui_page_db DEFINITION INHERITING FROM /apmg/cl_apm_gui_component FINAL CREATE PUBLIC. ************************************************************************ * apm GUI Database Utility * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES: /apmg/if_apm_gui_event_handler, /apmg/if_apm_gui_menu_provider, /apmg/if_apm_gui_renderable, /apmg/if_apm_html_table. CLASS-METHODS class_constructor. CLASS-METHODS create RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_renderable RAISING /apmg/cx_apm_error. METHODS constructor RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: BEGIN OF c_action, delete TYPE string VALUE 'delete', backup TYPE string VALUE 'backup', restore TYPE string VALUE 'restore', db_display TYPE string VALUE 'db_display', db_edit TYPE string VALUE 'db_edit', END OF c_action. CONSTANTS: c_css_url TYPE string VALUE 'css/page_db.css', c_toc_filename TYPE string VALUE '#_Table_of_Content_#.txt'. TYPES: BEGIN OF ty_list_item, key_type TYPE string, key_name TYPE string, key_extra TYPE string, show_key TYPE string, keys TYPE /apmg/if_apm_persist_apm=>ty_key, value TYPE /apmg/if_apm_persist_apm=>ty_value, user TYPE as4user, timestamp TYPE timestampl, END OF ty_list_item, ty_list TYPE SORTED TABLE OF ty_list_item WITH UNIQUE KEY key_type key_name key_extra. CLASS-DATA db_persist TYPE REF TO /apmg/if_apm_persist_apm. DATA db_entries TYPE /apmg/if_apm_persist_apm=>ty_list. DATA list TYPE ty_list. METHODS prepare_list. METHODS register_stylesheet RAISING /apmg/cx_apm_error. METHODS render_stats IMPORTING html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_table IMPORTING !html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS do_backup_db RAISING /apmg/cx_apm_error. CLASS-METHODS do_delete_entry IMPORTING !key TYPE /apmg/if_apm_persist_apm=>ty_key RAISING /apmg/cx_apm_error. CLASS-METHODS do_restore_db RAISING /apmg/cx_apm_error. CLASS-METHODS explain_key IMPORTING !key TYPE /apmg/if_apm_persist_apm=>ty_key RETURNING VALUE(result) TYPE string. ENDCLASS. CLASS /apmg/cl_apm_gui_page_db_entry DEFINITION INHERITING FROM /apmg/cl_apm_gui_component FINAL CREATE PUBLIC. ************************************************************************ * apm GUI Database Entry * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES: /apmg/if_apm_gui_event_handler, /apmg/if_apm_gui_menu_provider, /apmg/if_apm_gui_renderable, /apmg/if_apm_gui_page_title. CLASS-METHODS class_constructor. CLASS-METHODS create IMPORTING !key TYPE /apmg/if_apm_persist_apm=>ty_key !edit_mode TYPE abap_bool DEFAULT abap_false !back_on_save TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_renderable RAISING /apmg/cx_apm_error. METHODS constructor IMPORTING !key TYPE /apmg/if_apm_persist_apm=>ty_key !edit_mode TYPE abap_bool !back_on_save TYPE abap_bool RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: BEGIN OF c_action, update TYPE string VALUE 'update', switch_mode TYPE string VALUE 'switch_mode', END OF c_action. CONSTANTS: c_edit_form_id TYPE string VALUE 'db_form', c_css_url TYPE string VALUE 'css/page_db_entry.css'. CLASS-DATA db_persist TYPE REF TO /apmg/if_apm_persist_apm. DATA: db_entry TYPE /apmg/if_apm_persist_apm=>ty_zabappm, content_type TYPE string, edit_mode TYPE abap_bool, back_on_save TYPE abap_bool. METHODS load_entry IMPORTING !key TYPE /apmg/if_apm_persist_apm=>ty_key RETURNING VALUE(result) TYPE /apmg/if_apm_persist_apm=>ty_zabappm RAISING /apmg/cx_apm_error. METHODS register_stylesheet RAISING /apmg/cx_apm_error. METHODS get_scripts RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_view IMPORTING !html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_edit IMPORTING !html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_header IMPORTING !html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS get_entry_tag RETURNING VALUE(result) TYPE string. METHODS do_update IMPORTING !key TYPE csequence !value TYPE csequence RAISING /apmg/cx_apm_error. METHODS escape_percent_sign IMPORTING !value TYPE csequence RETURNING VALUE(result) TYPE string. ENDCLASS. CLASS /apmg/cl_apm_gui_page_debuginf DEFINITION INHERITING FROM /apmg/cl_apm_gui_component FINAL CREATE PRIVATE. ************************************************************************ * apm GUI Debug Info * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES: /apmg/if_apm_gui_event_handler, /apmg/if_apm_gui_menu_provider, /apmg/if_apm_gui_renderable. CLASS-METHODS create RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_renderable RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: BEGIN OF c_action, save TYPE string VALUE 'save', END OF c_action. DATA html_for_download TYPE string. METHODS render_debug_info IMPORTING !html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS get_scripts RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_page_hoc DEFINITION INHERITING FROM /apmg/cl_apm_gui_page FINAL CREATE PRIVATE. ************************************************************************ * apm GUI Page HOC * * Copyright 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ * adapted: gui_page PUBLIC SECTION. CLASS-METHODS create IMPORTING !child_component TYPE REF TO /apmg/if_apm_gui_renderable !page_title TYPE string OPTIONAL !page_layout TYPE string DEFAULT /apmg/cl_apm_gui_page=>c_page_layout-centered !page_menu TYPE REF TO /apmg/cl_apm_html_toolbar OPTIONAL !page_menu_provider TYPE REF TO /apmg/if_apm_gui_menu_provider OPTIONAL !page_title_provider TYPE REF TO /apmg/if_apm_gui_page_title OPTIONAL !extra_css_url TYPE string OPTIONAL !extra_js_url TYPE string OPTIONAL !show_as_modal TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_renderable RAISING /apmg/cx_apm_error. METHODS get_child RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_renderable. METHODS constructor IMPORTING !child_component TYPE REF TO /apmg/if_apm_gui_renderable !page_controller TYPE /apmg/cl_apm_gui_page=>ty_control RAISING /apmg/cx_apm_error. PROTECTED SECTION. METHODS render_content REDEFINITION. PRIVATE SECTION. DATA child_component TYPE REF TO /apmg/if_apm_gui_renderable. METHODS detect_modal RETURNING VALUE(result) TYPE abap_bool. METHODS detect_menu_provider RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_menu_provider. METHODS detect_title_provider RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_page_title. ENDCLASS. CLASS /apmg/cl_apm_gui_page_list DEFINITION INHERITING FROM /apmg/cl_apm_gui_component FINAL CREATE PRIVATE. ************************************************************************ * apm GUI Package List * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * CSS classes have to match repo-overview to allow JS to work properly ************************************************************************ PUBLIC SECTION. INTERFACES: /apmg/if_apm_gui_event_handler, /apmg/if_apm_gui_hotkeys, /apmg/if_apm_gui_menu_provider, /apmg/if_apm_gui_renderable. CLASS-METHODS create IMPORTING !only_favorites TYPE abap_bool OPTIONAL RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_renderable RAISING /apmg/cx_apm_error. METHODS constructor IMPORTING !only_favorites TYPE abap_bool OPTIONAL RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: BEGIN OF c_action, select TYPE string VALUE 'select', apply_filter TYPE string VALUE 'apply_filter', label_filter TYPE string VALUE 'label_filter', toggle_favorites TYPE string VALUE 'toggle_favorites', change_order_by TYPE string VALUE 'change_order_by', direction TYPE string VALUE 'direction', refresh TYPE string VALUE 'refresh', END OF c_action, c_label_filter_prefix TYPE string VALUE 'label:', c_raw_field_suffix TYPE string VALUE '_RAW' ##NO_TEXT. DATA: packages TYPE /apmg/if_apm_package_json=>ty_packages, all_labels TYPE string_table, label_colors TYPE REF TO /apmg/cl_apm_string_map, settings TYPE /apmg/if_apm_settings=>ty_settings. METHODS set_order_by IMPORTING !order_by TYPE string RAISING /apmg/cx_apm_error. METHODS set_order_direction IMPORTING !order_descending TYPE abap_bool RAISING /apmg/cx_apm_error. METHODS set_filter IMPORTING !postdata TYPE /apmg/if_apm_html_viewer=>ty_post_data RAISING /apmg/cx_apm_error. METHODS apply_filter CHANGING !packages TYPE /apmg/if_apm_package_json=>ty_packages. METHODS render_package_list IMPORTING !html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS get_palette IMPORTING !action TYPE string RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_styles IMPORTING !html TYPE REF TO /apmg/if_apm_html. METHODS render_table_header IMPORTING !html TYPE REF TO /apmg/if_apm_html. METHODS render_table_footer IMPORTING !html TYPE REF TO /apmg/if_apm_html. METHODS render_table_body IMPORTING !html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_table_item IMPORTING !html TYPE REF TO /apmg/if_apm_html !package TYPE /apmg/if_apm_package_json=>ty_package RAISING /apmg/cx_apm_error. METHODS render_header_bar IMPORTING !html TYPE REF TO /apmg/if_apm_html. METHODS render_header_label_list IMPORTING !html TYPE REF TO /apmg/if_apm_html. METHODS apply_order_by CHANGING packages TYPE /apmg/if_apm_package_json=>ty_packages. METHODS prepare_packages RETURNING VALUE(result) TYPE /apmg/if_apm_package_json=>ty_packages RAISING /apmg/cx_apm_error. METHODS get_scripts RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_action_toolbar IMPORTING !html TYPE REF TO /apmg/if_apm_html. METHODS render_filter_bar IMPORTING !html TYPE REF TO /apmg/if_apm_html. METHODS render_registry IMPORTING !html TYPE REF TO /apmg/if_apm_html. METHODS build_table_scheme RETURNING VALUE(result) TYPE /apmg/cl_apm_gui_chunk_lib=>ty_col_spec_tt. METHODS collect_all_labels IMPORTING !packages TYPE /apmg/if_apm_package_json=>ty_packages RETURNING VALUE(result) TYPE string_table. METHODS render_filter_help_hint RETURNING VALUE(result) TYPE string. METHODS load_package_list RAISING /apmg/cx_apm_error. METHODS load_settings RAISING /apmg/cx_apm_error. METHODS save_settings RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_page_package DEFINITION INHERITING FROM /apmg/cl_apm_gui_component FINAL CREATE PRIVATE. ************************************************************************ * apm GUI Package View * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES: /apmg/if_apm_gui_event_handler, /apmg/if_apm_gui_hotkeys, /apmg/if_apm_gui_renderable, /apmg/if_apm_gui_menu_provider. CONSTANTS: BEGIN OF c_default, path TYPE string VALUE '/', filename TYPE string VALUE 'README.md', view TYPE string VALUE 'view_readme', END OF c_default. CLASS-METHODS create IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_gui_renderable RAISING /apmg/cx_apm_error. METHODS constructor IMPORTING !package TYPE devclass RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: BEGIN OF c_action, view_readme TYPE string VALUE 'view_readme', view_readme_code TYPE string VALUE 'view_readme_code', view_readme_raw TYPE string VALUE 'view_readme_raw', edit_readme TYPE string VALUE 'edit_readme', copy_readme TYPE string VALUE 'copy_readme', download_readme TYPE string VALUE 'download_readme', view_json TYPE string VALUE 'view_json', edit_json TYPE string VALUE 'edit_json', copy_json TYPE string VALUE 'copy_json', download_json TYPE string VALUE 'download_json', view_dependencies TYPE string VALUE 'view_dependencies', add_dependency TYPE string VALUE 'add_dependency', remove_dependency TYPE string VALUE 'remove_dependency', update_dependencies TYPE string VALUE 'update_dependencies', END OF c_action. CONSTANTS: BEGIN OF c_markdown, logo TYPE string VALUE 'markdown-logo.png', mime TYPE string VALUE 'ZMARKDOWN_LOGO', END OF c_markdown. TYPES: BEGIN OF ty_markdown, path TYPE string, filename TYPE string, data TYPE string, END OF ty_markdown. DATA: package TYPE devclass, view TYPE string, markdown TYPE ty_markdown, package_json TYPE /apmg/if_apm_types=>ty_package_json. METHODS get_toolbar RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_html_toolbar RAISING /apmg/cx_apm_error. METHODS get_markdown RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. METHODS get_package_json RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_package_json RAISING /apmg/cx_apm_error. METHODS get_json_data RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. METHODS get_mime IMPORTING !mime_name TYPE csequence RETURNING VALUE(result) TYPE xstring. METHODS get_root_href IMPORTING !url TYPE string !branch TYPE string RETURNING VALUE(result) TYPE string. METHODS get_root_img IMPORTING !url TYPE string !branch TYPE string RETURNING VALUE(result) TYPE string. METHODS get_package_boxed IMPORTING !name TYPE string !value TYPE string OPTIONAL RETURNING VALUE(result) TYPE string. METHODS render_styles IMPORTING !html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_top IMPORTING !html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_header IMPORTING !html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_header_content IMPORTING !html TYPE REF TO /apmg/if_apm_html !image TYPE string !text TYPE string RAISING /apmg/cx_apm_error. METHODS render_content IMPORTING !html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_markdown IMPORTING !html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_markdown_source IMPORTING !html TYPE REF TO /apmg/if_apm_html !raw TYPE abap_bool DEFAULT abap_false RAISING /apmg/cx_apm_error. METHODS render_dependencies IMPORTING !html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_dependencies_table IMPORTING !html TYPE REF TO /apmg/if_apm_html !dependencies TYPE /apmg/if_apm_types=>ty_dependencies !bundle_dependencies TYPE string_table OPTIONAL RAISING /apmg/cx_apm_error. METHODS render_json IMPORTING !html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS render_footer IMPORTING !html TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS copy_to_clipboard IMPORTING !data TYPE string RAISING /apmg/cx_apm_error. METHODS download_to_file IMPORTING !filename TYPE string !extension TYPE string !data TYPE string RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_gui_utils DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. CLASS-METHODS is_renderable IMPORTING !io_obj TYPE REF TO object RETURNING VALUE(rv_yes) TYPE abap_bool. CLASS-METHODS is_event_handler IMPORTING !io_obj TYPE REF TO object RETURNING VALUE(rv_yes) TYPE abap_bool. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_highlighter DEFINITION ABSTRACT CREATE PUBLIC. ************************************************************************ * Syntax Highlighter * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CONSTANTS c_version TYPE string VALUE '1.0.0' ##NEEDED. METHODS process_line IMPORTING !line TYPE string RETURNING VALUE(result) TYPE string. METHODS set_hidden_chars IMPORTING !hidden_chars TYPE abap_bool. PROTECTED SECTION. TYPES: BEGIN OF ty_match, token TYPE c LENGTH 1, " Type of matches offset TYPE i, " Beginning position of the string that should be formatted length TYPE i, " Length of the string that should be formatted text_tag TYPE string, " Type of text tag END OF ty_match, ty_match_tt TYPE STANDARD TABLE OF ty_match WITH KEY token offset length, BEGIN OF ty_rule, regex TYPE REF TO cl_abap_regex, token TYPE c LENGTH 1, style TYPE string, relevant_submatch TYPE i, END OF ty_rule. CONSTANTS c_token_none TYPE c VALUE '.'. DATA rules TYPE STANDARD TABLE OF ty_rule WITH KEY regex token style. DATA hidden_chars TYPE abap_bool. METHODS add_rule IMPORTING !regex TYPE string !token TYPE c !style TYPE string !submatch TYPE i OPTIONAL. METHODS parse_line IMPORTING !line TYPE string RETURNING VALUE(result) TYPE ty_match_tt. METHODS order_matches IMPORTING !line TYPE string CHANGING !matches TYPE ty_match_tt. METHODS extend_matches IMPORTING !line TYPE string CHANGING !matches TYPE ty_match_tt. METHODS format_line IMPORTING !line TYPE string !matches TYPE ty_match_tt RETURNING VALUE(result) TYPE string. METHODS apply_style IMPORTING !line TYPE string !class TYPE string OPTIONAL RETURNING VALUE(result) TYPE string. METHODS is_whitespace IMPORTING !string TYPE string RETURNING VALUE(rv_result) TYPE abap_bool. METHODS show_hidden_chars IMPORTING !line TYPE string RETURNING VALUE(result) TYPE string. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_highlighter_abap DEFINITION INHERITING FROM /apmg/cl_apm_highlighter CREATE PUBLIC. ************************************************************************ * Syntax Highlighter * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CONSTANTS: BEGIN OF c_css, keyword TYPE string VALUE 'keyword', text TYPE string VALUE 'text', comment TYPE string VALUE 'comment', END OF c_css, BEGIN OF c_token, keyword TYPE c VALUE 'K', text TYPE c VALUE 'T', comment TYPE c VALUE 'C', END OF c_token, BEGIN OF c_regex, comment TYPE string VALUE '##|"|^\*', text TYPE string VALUE '`|''|\||\{|\}', keyword TYPE string VALUE '&&|\b[-_a-z0-9]+\b', END OF c_regex. CLASS-METHODS class_constructor. METHODS constructor. PROTECTED SECTION. CLASS-DATA keywords TYPE HASHED TABLE OF string WITH UNIQUE KEY table_line. CLASS-METHODS init_keywords. CLASS-METHODS is_keyword IMPORTING chunk TYPE string RETURNING VALUE(result) TYPE abap_bool. METHODS order_matches REDEFINITION. METHODS parse_line REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_highlighter_css DEFINITION INHERITING FROM /apmg/cl_apm_highlighter CREATE PUBLIC. ************************************************************************ * Syntax Highlighter * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CONSTANTS: " CSS Standard https://www.w3.org/TR/css-2018/ " CSS Reference https://www.w3schools.com/cssref/default.asp " We used a mixture of above as reference for the keyword list " 1) CSS Properties https://www.w3schools.com/cssref/default.asp " 2) CSS Values & Units https://www.w3schools.com/cssref/css_units.asp " 3) CSS Selectors https://www.w3.org/TR/css-2018/#selectors " 4) CSS Functions https://www.w3schools.com/cssref/css_functions.asp " 5) CSS Colors https://www.w3schools.com/colors/colors_names.asp " 6) CSS Extensions " 7) CSS At-Rules https://www.w3.org/TR/css-2018/#at-rules " 8) HTML Tags BEGIN OF c_css, keyword TYPE string VALUE 'keyword', text TYPE string VALUE 'text', comment TYPE string VALUE 'comment', selectors TYPE string VALUE 'selectors', units TYPE string VALUE 'units', properties TYPE string VALUE 'properties', values TYPE string VALUE 'values', functions TYPE string VALUE 'functions', colors TYPE string VALUE 'colors', extensions TYPE string VALUE 'extensions', at_rules TYPE string VALUE 'at_rules', html TYPE string VALUE 'html', END OF c_css, BEGIN OF c_token, keyword TYPE c VALUE 'K', text TYPE c VALUE 'T', comment TYPE c VALUE 'C', selectors TYPE c VALUE 'S', units TYPE c VALUE 'U', properties TYPE c VALUE 'P', values TYPE c VALUE 'V', functions TYPE c VALUE 'F', colors TYPE c VALUE 'Z', extensions TYPE c VALUE 'E', at_rules TYPE c VALUE 'A', html TYPE c VALUE 'H', END OF c_token, BEGIN OF c_regex, " comments /* ... */ comment TYPE string VALUE '\/\*.*\*\/|\/\*|\*\/', " single or double quoted strings text TYPE string VALUE '("[^"]*")|(''[^'']*'')|(`[^`]*`)', " in general keywords don't contain numbers (except -ms-scrollbar-3dlight-color) keyword TYPE string VALUE '\b[a-z3@\-]+\b', " selectors begin with : selectors TYPE string VALUE ':[:a-z]+\b', " units units TYPE string VALUE '\b[0-9\. ]+(ch|cm|em|ex|in|mm|pc|pt|px|vh|vmax|vmin|vw)\b|\b[0-9\. ]+%', END OF c_regex. CLASS-METHODS class_constructor. METHODS constructor. PROTECTED SECTION. TYPES: ty_token TYPE c LENGTH 1, BEGIN OF ty_keyword, keyword TYPE string, token TYPE ty_token, END OF ty_keyword. CLASS-DATA keywords TYPE HASHED TABLE OF ty_keyword WITH UNIQUE KEY keyword. CLASS-DATA comment TYPE abap_bool. CLASS-METHODS init_keywords. CLASS-METHODS insert_keywords IMPORTING list TYPE string token TYPE ty_token. CLASS-METHODS is_keyword IMPORTING chunk TYPE string RETURNING VALUE(result) TYPE abap_bool. METHODS order_matches REDEFINITION. METHODS parse_line REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_highlighter_diff DEFINITION INHERITING FROM /apmg/cl_apm_highlighter CREATE PUBLIC. ************************************************************************ * Syntax Highlighter * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CONSTANTS: BEGIN OF c_css, ins TYPE string VALUE 'diff_ins', del TYPE string VALUE 'diff_del', test TYPE string VALUE 'diff_upd', comment TYPE string VALUE 'comment', END OF c_css, BEGIN OF c_token, ins TYPE c VALUE 'I', del TYPE c VALUE 'D', test TYPE c VALUE 'T', comment TYPE c VALUE 'C', END OF c_token, BEGIN OF c_regex, ins TYPE string VALUE '^\+.*', del TYPE string VALUE '^-.*', test TYPE string VALUE '^!.*', comment TYPE string VALUE '^#.*', END OF c_regex. METHODS constructor. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_highlighter_facto DEFINITION ABSTRACT CREATE PUBLIC. ************************************************************************ * Syntax Highlighter * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS create IMPORTING !filename TYPE string !hidden_chars TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_highlighter. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_highlighter_js DEFINITION INHERITING FROM /apmg/cl_apm_highlighter CREATE PUBLIC. ************************************************************************ * Syntax Highlighter * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CONSTANTS: " JavaScript " 1) General keywords " 2) Variable types " 3) HTML Tags BEGIN OF c_css, keyword TYPE string VALUE 'keyword', text TYPE string VALUE 'text', comment TYPE string VALUE 'comment', variables TYPE string VALUE 'variables', END OF c_css, BEGIN OF c_token, keyword TYPE c VALUE 'K', text TYPE c VALUE 'T', comment TYPE c VALUE 'C', variables TYPE c VALUE 'V', END OF c_token, BEGIN OF c_regex, " comments /* ... */ or // comment TYPE string VALUE '\/\*.*\*\/|\/\*|\*\/|\/\/', " single or double quoted strings text TYPE string VALUE '"|''|`', " in general keywords don't contain numbers (except -ms-scrollbar-3dlight-color) keyword TYPE string VALUE '\b[a-z-]+\b', END OF c_regex. CLASS-METHODS class_constructor. METHODS constructor. PROTECTED SECTION. TYPES ty_token TYPE c LENGTH 1. TYPES: BEGIN OF ty_keyword, keyword TYPE string, token TYPE ty_token, END OF ty_keyword. CLASS-DATA keywords TYPE HASHED TABLE OF ty_keyword WITH UNIQUE KEY keyword. CLASS-DATA comment TYPE abap_bool. CLASS-METHODS init_keywords. CLASS-METHODS insert_keywords IMPORTING list TYPE string token TYPE ty_token. CLASS-METHODS is_keyword IMPORTING chunk TYPE string RETURNING VALUE(result) TYPE abap_bool. METHODS order_matches REDEFINITION. METHODS parse_line REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_highlighter_json DEFINITION INHERITING FROM /apmg/cl_apm_highlighter CREATE PUBLIC. ************************************************************************ * Syntax Highlighter * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CONSTANTS: " JSON... This was easy :-) " JSONC... With comments BEGIN OF c_css, keyword TYPE string VALUE 'selectors', text TYPE string VALUE 'text', values TYPE string VALUE 'properties', comment TYPE string VALUE 'comment', END OF c_css, BEGIN OF c_token, keyword TYPE c VALUE 'K', text TYPE c VALUE 'T', values TYPE c VALUE 'V', comment TYPE c VALUE 'C', END OF c_token, BEGIN OF c_regex, " comments /* ... */ or // comment TYPE string VALUE '\/\*.*\*\/|\/\*|\*\/|\/\/', " not much here keyword TYPE string VALUE 'true|false|null', " double quoted strings text TYPE string VALUE '"', END OF c_regex. METHODS constructor. PROTECTED SECTION. METHODS order_matches REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_highlighter_md DEFINITION INHERITING FROM /apmg/cl_apm_highlighter CREATE PUBLIC. ************************************************************************ * Syntax Highlighter * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CONSTANTS: BEGIN OF c_css, xml_tag TYPE string VALUE 'xml_tag', attr TYPE string VALUE 'attr', attr_val TYPE string VALUE 'attr_val', heading TYPE string VALUE 'heading', link TYPE string VALUE 'link', url TYPE string VALUE 'url', strong TYPE string VALUE 'strong', emphasis TYPE string VALUE 'emphasis', comment TYPE string VALUE 'comment', END OF c_css, BEGIN OF c_token, xml_tag TYPE c VALUE 'X', attr TYPE c VALUE 'A', attr_val TYPE c VALUE 'V', heading TYPE c VALUE 'H', link TYPE c VALUE 'L', url TYPE c VALUE 'U', strong TYPE c VALUE 'S', emphasis TYPE c VALUE 'E', comment TYPE c VALUE 'C', END OF c_token, BEGIN OF c_regex, xml_tag TYPE string VALUE '(?:"[^"]*")|(?:''[^'']*'')|(?:`[^`]*`)|([<>])', attr TYPE string VALUE '(?:^|\s)[-a-z:_0-9]+\s*(?==\s*["|''|`])', attr_val TYPE string VALUE '("[^"]*")|(''[^'']*'')|(`[^`]*`)', heading TYPE string VALUE '^#\s*(.*)', link TYPE string VALUE '\[[^]]+\]', url TYPE string VALUE `http[s]*://[^>"'\)\s]+`, strong TYPE string VALUE '\*\*[^*]+\*\*', emphasis TYPE string VALUE '__[^_]+__', " comments comment TYPE string VALUE '[\<]!--.*--[\>]|[\<]!--|--[\>]', END OF c_regex. METHODS constructor. PROTECTED SECTION. CLASS-DATA comment TYPE abap_bool. METHODS order_matches REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_highlighter_po DEFINITION INHERITING FROM /apmg/cl_apm_highlighter FINAL CREATE PUBLIC. ************************************************************************ * Syntax Highlighter * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. METHODS constructor. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: BEGIN OF c_style, msgid TYPE string VALUE 'keyword', msgstr TYPE string VALUE 'xml_tag', comment TYPE string VALUE 'comment', END OF c_style. CONSTANTS: BEGIN OF c_token, msgid TYPE c VALUE 'I', msgstr TYPE c VALUE 'S', comment TYPE c VALUE 'C', END OF c_token. CONSTANTS: BEGIN OF c_regex, msgid TYPE string VALUE '^msgid\b', msgstr TYPE string VALUE '^msgstr\b', comment TYPE string VALUE '^#.*', END OF c_regex. ENDCLASS. CLASS /apmg/cl_apm_highlighter_txt DEFINITION INHERITING FROM /apmg/cl_apm_highlighter CREATE PUBLIC. ************************************************************************ * Syntax Highlighter * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. METHODS process_line REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_highlighter_xml DEFINITION INHERITING FROM /apmg/cl_apm_highlighter CREATE PUBLIC. ************************************************************************ * Syntax Highlighter * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CONSTANTS: BEGIN OF c_css, xml_tag TYPE string VALUE 'xml_tag', attr TYPE string VALUE 'attr', attr_val TYPE string VALUE 'attr_val', comment TYPE string VALUE 'comment', END OF c_css, BEGIN OF c_token, xml_tag TYPE c VALUE 'X', attr TYPE c VALUE 'A', attr_val TYPE c VALUE 'V', comment TYPE c VALUE 'C', END OF c_token, BEGIN OF c_regex, " For XML tags, we will use a submatch " main pattern includes quoted strings so we can ignore < and > in attr values xml_tag TYPE string VALUE '(?:"[^"]*")|(?:''[^'']*'')|(?:`[^`]*`)|([<>])', attr TYPE string VALUE '(?:^|\s)[-a-z:_0-9]+\s*(?==\s*["|''|`])', attr_val TYPE string VALUE '("[^"]*")|(''[^'']*'')|(`[^`]*`)', " comments comment TYPE string VALUE '[\<]!--.*--[\>]|[\<]!--|--[\>]', END OF c_regex. METHODS constructor. PROTECTED SECTION. CLASS-DATA comment TYPE abap_bool. METHODS order_matches REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_highlighter_yaml DEFINITION INHERITING FROM /apmg/cl_apm_highlighter CREATE PUBLIC. ************************************************************************ * Syntax Highlighter * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ * Basic YAML formatting * https://yaml.org/ ************************************************************************ PUBLIC SECTION. CONSTANTS: BEGIN OF c_css, keyword TYPE string VALUE 'selectors', text TYPE string VALUE 'text', values TYPE string VALUE 'properties', comment TYPE string VALUE 'comment', attr TYPE string VALUE 'attr', END OF c_css, BEGIN OF c_token, keyword TYPE c VALUE 'K', text TYPE c VALUE 'T', values TYPE c VALUE 'V', comment TYPE c VALUE 'C', attr TYPE c VALUE 'A', END OF c_token, BEGIN OF c_regex, " comments # comment TYPE string VALUE '#.+', " keywords keyword TYPE string VALUE '[-_a-zA-Z0-9]+', " not much here values TYPE string VALUE 'true|false|null', " double quoted strings text TYPE string VALUE '"', " YAML collections, structures, scalars, tags attr TYPE string VALUE '- |: |---|\.\.\.|\[|\]|\{|\}|&|\*|\? |>|!|\|', END OF c_regex. METHODS constructor. PROTECTED SECTION. METHODS order_matches REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_html DEFINITION CREATE PUBLIC. PUBLIC SECTION. INTERFACES /apmg/if_apm_html. CONSTANTS c_indent_size TYPE i VALUE 2 ##NO_TEXT. CLASS-METHODS class_constructor. CLASS-METHODS create IMPORTING !iv_initial_chunk TYPE any OPTIONAL RETURNING VALUE(ri_instance) TYPE REF TO /apmg/if_apm_html. CLASS-METHODS icon IMPORTING !iv_name TYPE string !iv_hint TYPE string OPTIONAL !iv_class TYPE string OPTIONAL !iv_onclick TYPE string OPTIONAL RETURNING VALUE(rv_str) TYPE string. CLASS-METHODS checkbox IMPORTING !iv_id TYPE string OPTIONAL !iv_checked TYPE abap_bool OPTIONAL RETURNING VALUE(rv_html) TYPE string. CLASS-METHODS parse_data_attr IMPORTING !iv_str TYPE string OPTIONAL RETURNING VALUE(rs_data_attr) TYPE /apmg/if_apm_html=>ty_data_attr. CLASS-METHODS set_debug_mode IMPORTING !iv_mode TYPE abap_bool. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_max_indent TYPE i VALUE 200. TYPES: BEGIN OF ty_indent_context, no_indent_jscss TYPE abap_bool, within_style TYPE abap_bool, within_js TYPE abap_bool, within_textarea TYPE abap_bool, within_pre TYPE abap_bool, indent TYPE i, indent_str TYPE string, END OF ty_indent_context, BEGIN OF ty_study_result, style_open TYPE abap_bool, style_close TYPE abap_bool, script_open TYPE abap_bool, script_close TYPE abap_bool, textarea_open TYPE abap_bool, textarea_close TYPE abap_bool, pre_open TYPE abap_bool, pre_close TYPE abap_bool, tag_close TYPE abap_bool, curly_close TYPE abap_bool, openings TYPE i, closings TYPE i, singles TYPE i, END OF ty_study_result. CLASS-DATA go_single_tags_re TYPE REF TO cl_abap_regex. CLASS-DATA gv_spaces TYPE string. CLASS-DATA gv_debug_mode TYPE abap_bool. DATA mt_buffer TYPE string_table. METHODS indent_line CHANGING !cs_context TYPE ty_indent_context !cv_line TYPE string. METHODS study_line IMPORTING !iv_line TYPE string !is_context TYPE ty_indent_context RETURNING VALUE(rs_result) TYPE ty_study_result. ENDCLASS. CLASS /apmg/cl_apm_html_action_utils DEFINITION CREATE PUBLIC. PUBLIC SECTION. CLASS-METHODS jump_encode IMPORTING !iv_obj_type TYPE tadir-object !iv_obj_name TYPE tadir-obj_name !iv_filename TYPE string OPTIONAL RETURNING VALUE(rv_string) TYPE string. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_name_value, name TYPE string, value TYPE string, END OF ty_name_value, ty_name_value_tt TYPE STANDARD TABLE OF ty_name_value WITH DEFAULT KEY. CLASS-METHODS add_field IMPORTING !iv_name TYPE string !ig_field TYPE any CHANGING !ct_field TYPE ty_name_value_tt. CLASS-METHODS fields_to_string IMPORTING !it_fields TYPE ty_name_value_tt RETURNING VALUE(rv_string) TYPE string. ENDCLASS. CLASS /apmg/cl_apm_html_form DEFINITION INHERITING FROM /apmg/cl_apm_gui_component FINAL CREATE PRIVATE. ************************************************************************ * apm GUI HTML Form * * Copyright 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ * adapted: gui_services PUBLIC SECTION. INTERFACES /apmg/if_apm_html_form. INTERFACES /apmg/if_apm_gui_hotkeys. CLASS-METHODS create IMPORTING !iv_form_id TYPE csequence OPTIONAL !iv_help_page TYPE csequence OPTIONAL RETURNING VALUE(ro_form) TYPE REF TO /apmg/cl_apm_html_form. METHODS render IMPORTING !iv_form_class TYPE csequence DEFAULT 'dialog-form' !io_values TYPE REF TO /apmg/cl_apm_string_map !io_validation_log TYPE REF TO /apmg/cl_apm_string_map OPTIONAL RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. METHODS command IMPORTING !iv_label TYPE csequence !iv_action TYPE csequence !iv_cmd_type TYPE i DEFAULT /apmg/if_apm_html_form=>c_cmd_type-input RETURNING VALUE(ro_self) TYPE REF TO /apmg/cl_apm_html_form. METHODS text IMPORTING !iv_label TYPE csequence !iv_name TYPE csequence !iv_hint TYPE csequence OPTIONAL !iv_required TYPE abap_bool DEFAULT abap_false !iv_upper_case TYPE abap_bool DEFAULT abap_false !iv_readonly TYPE abap_bool DEFAULT abap_false !iv_password TYPE abap_bool DEFAULT abap_false !iv_condense TYPE abap_bool OPTIONAL !iv_placeholder TYPE csequence OPTIONAL !iv_side_action TYPE csequence OPTIONAL !iv_min TYPE i DEFAULT cl_abap_math=>min_int4 !iv_max TYPE i DEFAULT cl_abap_math=>max_int4 RETURNING VALUE(ro_self) TYPE REF TO /apmg/cl_apm_html_form. METHODS textarea IMPORTING !iv_label TYPE csequence !iv_name TYPE csequence !iv_hint TYPE csequence OPTIONAL !iv_required TYPE abap_bool DEFAULT abap_false !iv_readonly TYPE abap_bool DEFAULT abap_false !iv_placeholder TYPE csequence OPTIONAL !iv_rows TYPE i OPTIONAL !iv_cols TYPE i OPTIONAL !iv_upper_case TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ro_self) TYPE REF TO /apmg/cl_apm_html_form. METHODS number IMPORTING !iv_label TYPE csequence !iv_name TYPE csequence !iv_hint TYPE csequence OPTIONAL !iv_required TYPE abap_bool DEFAULT abap_false !iv_readonly TYPE abap_bool DEFAULT abap_false !iv_min TYPE i DEFAULT cl_abap_math=>min_int4 !iv_max TYPE i DEFAULT cl_abap_math=>max_int4 RETURNING VALUE(ro_self) TYPE REF TO /apmg/cl_apm_html_form. METHODS checkbox IMPORTING !iv_label TYPE csequence !iv_name TYPE csequence !iv_hint TYPE csequence OPTIONAL !iv_readonly TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ro_self) TYPE REF TO /apmg/cl_apm_html_form. METHODS radio IMPORTING !iv_label TYPE csequence !iv_name TYPE csequence !iv_default_value TYPE csequence OPTIONAL !iv_hint TYPE csequence OPTIONAL !iv_condense TYPE abap_bool DEFAULT abap_false !iv_action TYPE csequence OPTIONAL RETURNING VALUE(ro_self) TYPE REF TO /apmg/cl_apm_html_form. METHODS option IMPORTING !iv_label TYPE csequence !iv_value TYPE csequence RETURNING VALUE(ro_self) TYPE REF TO /apmg/cl_apm_html_form. METHODS table IMPORTING !iv_label TYPE csequence !iv_name TYPE csequence !iv_hint TYPE csequence OPTIONAL RETURNING VALUE(ro_self) TYPE REF TO /apmg/cl_apm_html_form. METHODS column IMPORTING !iv_label TYPE csequence !iv_width TYPE csequence OPTIONAL !iv_readonly TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ro_self) TYPE REF TO /apmg/cl_apm_html_form. METHODS start_group IMPORTING !iv_label TYPE csequence !iv_name TYPE csequence !iv_hint TYPE csequence OPTIONAL RETURNING VALUE(ro_self) TYPE REF TO /apmg/cl_apm_html_form. METHODS hidden IMPORTING !iv_name TYPE csequence RETURNING VALUE(ro_self) TYPE REF TO /apmg/cl_apm_html_form. METHODS get_fields RETURNING VALUE(rt_fields) TYPE /apmg/if_apm_html_form=>ty_fields. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_attr, value TYPE string, error TYPE string, hint TYPE string, readonly TYPE string, placeholder TYPE string, required TYPE string, autofocus TYPE string, END OF ty_attr. DATA: mv_webgui TYPE abap_bool, mt_fields TYPE /apmg/if_apm_html_form=>ty_fields, mt_commands TYPE STANDARD TABLE OF /apmg/if_apm_html_form=>ty_command, mv_form_id TYPE string, mv_help_page TYPE string. METHODS render_field IMPORTING !ii_html TYPE REF TO /apmg/if_apm_html !io_values TYPE REF TO /apmg/cl_apm_string_map !io_validation_log TYPE REF TO /apmg/cl_apm_string_map !is_field TYPE /apmg/if_apm_html_form=>ty_field !iv_autofocus TYPE abap_bool. METHODS render_field_text IMPORTING !ii_html TYPE REF TO /apmg/if_apm_html !is_field TYPE /apmg/if_apm_html_form=>ty_field !is_attr TYPE ty_attr. METHODS render_field_textarea IMPORTING !ii_html TYPE REF TO /apmg/if_apm_html !is_field TYPE /apmg/if_apm_html_form=>ty_field !is_attr TYPE ty_attr. METHODS render_field_checkbox IMPORTING !ii_html TYPE REF TO /apmg/if_apm_html !is_field TYPE /apmg/if_apm_html_form=>ty_field !is_attr TYPE ty_attr. METHODS render_field_radio IMPORTING !ii_html TYPE REF TO /apmg/if_apm_html !is_field TYPE /apmg/if_apm_html_form=>ty_field !is_attr TYPE ty_attr. METHODS render_field_table IMPORTING !ii_html TYPE REF TO /apmg/if_apm_html !is_field TYPE /apmg/if_apm_html_form=>ty_field !is_attr TYPE ty_attr !io_values TYPE REF TO /apmg/cl_apm_string_map. METHODS render_command IMPORTING !ii_html TYPE REF TO /apmg/if_apm_html !is_cmd TYPE /apmg/if_apm_html_form=>ty_command. METHODS render_command_link IMPORTING !ii_html TYPE REF TO /apmg/if_apm_html !is_cmd TYPE /apmg/if_apm_html_form=>ty_command. METHODS render_field_hidden IMPORTING !ii_html TYPE REF TO /apmg/if_apm_html !is_field TYPE /apmg/if_apm_html_form=>ty_field !is_attr TYPE ty_attr. ENDCLASS. CLASS /apmg/cl_apm_html_form_utils DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm GUI HTML Form Utilities * * Copyright 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ * adapted: html_form PUBLIC SECTION. CLASS-METHODS create IMPORTING !io_form TYPE REF TO /apmg/cl_apm_html_form RETURNING VALUE(ro_form_util) TYPE REF TO /apmg/cl_apm_html_form_utils. CLASS-METHODS is_dirty IMPORTING !io_form_data TYPE REF TO /apmg/cl_apm_string_map !io_compare_with TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(rv_dirty) TYPE abap_bool. METHODS constructor IMPORTING !io_form TYPE REF TO /apmg/cl_apm_html_form. METHODS normalize IMPORTING !io_form_data TYPE REF TO /apmg/cl_apm_string_map OPTIONAL RETURNING VALUE(ro_form_data) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS normalize_abapgit IMPORTING !io_form_data TYPE REF TO /apmg/cl_apm_string_map OPTIONAL RETURNING VALUE(ro_form_data) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS validate IMPORTING !io_form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(ro_validation_log) TYPE REF TO /apmg/cl_apm_string_map RAISING /apmg/cx_apm_error. METHODS is_empty IMPORTING !io_form_data TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(rv_empty) TYPE abap_bool RAISING /apmg/cx_apm_error. METHODS set_data IMPORTING !io_form_data TYPE REF TO /apmg/cl_apm_string_map. METHODS exit IMPORTING !io_form_data TYPE REF TO /apmg/cl_apm_string_map !io_compare_with TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(rv_state) TYPE i RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. DATA mo_form TYPE REF TO /apmg/cl_apm_html_form. DATA mo_form_data TYPE REF TO /apmg/cl_apm_string_map. ENDCLASS. CLASS /apmg/cl_apm_html_parts DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. METHODS add_part IMPORTING !iv_collection TYPE string !ii_part TYPE REF TO /apmg/if_apm_html. METHODS get_parts IMPORTING !iv_collection TYPE string RETURNING VALUE(rt_parts) TYPE /apmg/if_apm_html=>ty_table_of. METHODS get_collection_names RETURNING VALUE(rt_list) TYPE string_table. METHODS get_collection_size IMPORTING !iv_collection TYPE string RETURNING VALUE(rv_size) TYPE i. METHODS clear. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_named_collection, name TYPE string, pile TYPE /apmg/if_apm_html=>ty_table_of, END OF ty_named_collection, ty_named_collections TYPE STANDARD TABLE OF ty_named_collection WITH KEY name. DATA mt_part_collections TYPE ty_named_collections. METHODS get_collection IMPORTING !iv_collection TYPE string !iv_create_if_missing TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rr_collection) TYPE REF TO ty_named_collection . ENDCLASS. CLASS /apmg/cl_apm_html_table DEFINITION INHERITING FROM /apmg/cl_apm_gui_component FINAL CREATE PUBLIC. PUBLIC SECTION. CLASS-METHODS create IMPORTING !ii_renderer TYPE REF TO /apmg/if_apm_html_table OPTIONAL " Can be passed to renderer !is_initial_sorting_state TYPE /apmg/if_apm_html_table=>ty_sorting_state OPTIONAL PREFERRED PARAMETER ii_renderer RETURNING VALUE(ro_instance) TYPE REF TO /apmg/cl_apm_html_table. " maybe also th css_class METHODS define_column IMPORTING !iv_column_id TYPE string !iv_column_title TYPE string OPTIONAL !iv_from_field TYPE abap_compname OPTIONAL !iv_sortable TYPE abap_bool DEFAULT abap_true RETURNING VALUE(ro_self) TYPE REF TO /apmg/cl_apm_html_table. METHODS define_column_group IMPORTING !iv_group_id TYPE string OPTIONAL " not mandatory, but can be used for CSS (TODO data-gid) !iv_group_title TYPE string OPTIONAL " can be empty ! PREFERRED PARAMETER iv_group_title RETURNING VALUE(ro_self) TYPE REF TO /apmg/cl_apm_html_table RAISING /apmg/cx_apm_error. " Maybe also data_provider " TODO record Limiter METHODS render IMPORTING !ii_renderer TYPE REF TO /apmg/if_apm_html_table OPTIONAL !it_data TYPE ANY TABLE !iv_id TYPE csequence OPTIONAL !iv_css_class TYPE csequence OPTIONAL !iv_with_cids TYPE abap_bool DEFAULT abap_false !is_sorting_state TYPE /apmg/if_apm_html_table=>ty_sorting_state OPTIONAL !iv_wrap_in_div TYPE string OPTIONAL " div class name RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html RAISING /apmg/cx_apm_error. " Sorting utils CLASS-METHODS detect_sorting_request IMPORTING !iv_event TYPE string RETURNING VALUE(rs_sorting_request) TYPE /apmg/if_apm_html_table=>ty_sorting_state. METHODS process_sorting_request IMPORTING !iv_event TYPE string RETURNING VALUE(rv_processed) TYPE abap_bool. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_sort_by_event_prefix TYPE string VALUE `sort_by:`. CONSTANTS c_sort_by_event_regex TYPE string VALUE `^sort_by:\w+:(asc|dsc)$`. TYPES: BEGIN OF ty_column, column_id TYPE string, column_title TYPE string, from_field TYPE abap_compname, sortable TYPE abap_bool, is_group TYPE abap_bool, group_span TYPE i, END OF ty_column, ty_columns TYPE STANDARD TABLE OF ty_column WITH KEY column_id. DATA mi_renderer TYPE REF TO /apmg/if_apm_html_table. DATA mt_columns TYPE ty_columns. DATA mi_html TYPE REF TO /apmg/if_apm_html. DATA mv_with_cids TYPE abap_bool. DATA mv_table_id TYPE string. DATA ms_sorting_state TYPE /apmg/if_apm_html_table=>ty_sorting_state. DATA mr_last_grp TYPE REF TO ty_column. " potentially receive from outside DATA mv_sort_span_class TYPE string VALUE `sort-arrow`. DATA mv_sort_active_class TYPE string VALUE `sort-active`. METHODS render_thead RAISING /apmg/cx_apm_error. METHODS render_tbody IMPORTING it_data TYPE ANY TABLE RAISING /apmg/cx_apm_error. METHODS render_row IMPORTING iv_row_index TYPE i is_row TYPE any RAISING /apmg/cx_apm_error. METHODS render_column_title IMPORTING is_col TYPE ty_column RETURNING VALUE(rv_text) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS cid_attr IMPORTING iv_column_id TYPE string RETURNING VALUE(rs_data_attr) TYPE /apmg/if_apm_html=>ty_data_attr. CLASS-METHODS gid_attr IMPORTING iv_column_id TYPE string RETURNING VALUE(rs_data_attr) TYPE /apmg/if_apm_html=>ty_data_attr. METHODS apply_sorting CHANGING ct_data TYPE STANDARD TABLE. ENDCLASS. CLASS /apmg/cl_apm_html_toolbar DEFINITION CREATE PUBLIC. PUBLIC SECTION. CLASS-METHODS create IMPORTING !iv_id TYPE string OPTIONAL RETURNING VALUE(ro_instance) TYPE REF TO /apmg/cl_apm_html_toolbar. METHODS constructor IMPORTING !iv_id TYPE string OPTIONAL. METHODS add IMPORTING !iv_txt TYPE string !io_sub TYPE REF TO /apmg/cl_apm_html_toolbar OPTIONAL !iv_typ TYPE c DEFAULT /apmg/if_apm_html=>c_action_type-sapevent !iv_act TYPE string OPTIONAL !iv_ico TYPE string OPTIONAL !iv_cur TYPE abap_bool OPTIONAL !iv_opt TYPE c OPTIONAL !iv_chk TYPE abap_bool DEFAULT abap_undefined !iv_aux TYPE string OPTIONAL !iv_id TYPE string OPTIONAL !iv_title TYPE string OPTIONAL !iv_class TYPE string OPTIONAL !iv_hotkey TYPE string OPTIONAL !iv_li_class TYPE string OPTIONAL RETURNING VALUE(ro_self) TYPE REF TO /apmg/cl_apm_html_toolbar. METHODS count_items RETURNING VALUE(rv_count) TYPE i. METHODS render IMPORTING !iv_right TYPE abap_bool OPTIONAL !iv_sort TYPE abap_bool OPTIONAL RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html. METHODS render_as_droplist IMPORTING !iv_label TYPE string !iv_right TYPE abap_bool OPTIONAL !iv_sort TYPE abap_bool OPTIONAL !iv_corner TYPE abap_bool OPTIONAL !iv_action TYPE string OPTIONAL RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html. METHODS get_hotkeys IMPORTING !iv_component_name TYPE string OPTIONAL RETURNING VALUE(rt_hotkeys) TYPE /apmg/if_apm_gui_hotkeys=>ty_hotkeys_with_descr. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_item, txt TYPE string, act TYPE string, ico TYPE string, sub TYPE REF TO /apmg/cl_apm_html_toolbar, opt TYPE c LENGTH 1, typ TYPE c LENGTH 1, cur TYPE abap_bool, chk TYPE abap_bool, aux TYPE string, id TYPE string, title TYPE string, class TYPE string, li_class TYPE string, hotkey TYPE string, END OF ty_item, ty_items TYPE STANDARD TABLE OF ty_item. DATA mt_items TYPE ty_items. DATA mv_id TYPE string. METHODS render_items IMPORTING !iv_sort TYPE abap_bool OPTIONAL RETURNING VALUE(ri_html) TYPE REF TO /apmg/if_apm_html. ENDCLASS. CLASS /apmg/cl_apm_http_agent DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * HTTP Agent * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES /apmg/if_apm_http_agent. CLASS-METHODS create IMPORTING !proxy_host TYPE string OPTIONAL !proxy_service TYPE string OPTIONAL !proxy_user TYPE string OPTIONAL !proxy_passwd TYPE string OPTIONAL RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_http_agent. METHODS constructor IMPORTING !proxy_host TYPE string OPTIONAL !proxy_service TYPE string OPTIONAL !proxy_user TYPE string OPTIONAL !proxy_passwd TYPE string OPTIONAL. PROTECTED SECTION. PRIVATE SECTION. DATA: proxy_host TYPE string, proxy_service TYPE string, proxy_user TYPE string, proxy_passwd TYPE string, global_headers TYPE REF TO /apmg/cl_apm_string_map. CLASS-METHODS attach_payload IMPORTING request TYPE REF TO if_http_request payload TYPE any RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_http_login_manage DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * HTTP Login Manager * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS get IMPORTING !host TYPE csequence RETURNING VALUE(result) TYPE string. CLASS-METHODS set IMPORTING !host TYPE csequence !username TYPE csequence !password TYPE csequence !is_basic TYPE abap_bool DEFAULT abap_true RETURNING VALUE(result) TYPE string. CLASS-METHODS save IMPORTING !host TYPE csequence !auth TYPE csequence. CLASS-METHODS clear. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_auth, host TYPE string, auth TYPE string, END OF ty_auth. CLASS-DATA auths TYPE TABLE OF ty_auth WITH DEFAULT KEY. CLASS-METHODS get_host IMPORTING !host TYPE string RETURNING VALUE(result) TYPE string. CLASS-METHODS append IMPORTING !host TYPE string !auth TYPE string. ENDCLASS. CLASS /apmg/cl_apm_importer DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm Importer * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * TODO!: use registry as package source, instead of installed packages * TODO: change to factory * TODO: replace logging with ABAP Logger (wait for v2 of logger) ************************************************************************ PUBLIC SECTION. CLASS-METHODS run IMPORTING !package TYPE devclass !dependencies TYPE /apmg/if_apm_importer=>ty_dependencies OPTIONAL !object_types TYPE /apmg/if_apm_importer=>ty_object_types OPTIONAL !object_names TYPE /apmg/if_apm_importer=>ty_object_names OPTIONAL !transport TYPE trkorr OPTIONAL !default_rule TYPE string DEFAULT /apmg/if_apm_importer=>c_default_import_rule !is_dry_run TYPE abap_bool DEFAULT abap_true !is_production TYPE abap_bool DEFAULT abap_true !is_logging TYPE abap_bool DEFAULT abap_true RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_width TYPE i VALUE 150. CLASS-DATA is_log TYPE abap_bool. CLASS-METHODS get_programs IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE /apmg/if_apm_importer=>ty_programs RAISING /apmg/cx_apm_error. CLASS-METHODS get_rules IMPORTING !programs TYPE /apmg/if_apm_importer=>ty_programs !default_rule TYPE string RETURNING VALUE(result) TYPE /apmg/if_apm_importer=>ty_rules RAISING /apmg/cx_apm_error. CLASS-METHODS get_packages IMPORTING !rules TYPE /apmg/if_apm_importer=>ty_rules !dependencies TYPE /apmg/if_apm_importer=>ty_dependencies RETURNING VALUE(result) TYPE /apmg/if_apm_importer=>ty_packages RAISING /apmg/cx_apm_error. CLASS-METHODS get_map IMPORTING !rules TYPE /apmg/if_apm_importer=>ty_rules !packages TYPE /apmg/if_apm_importer=>ty_packages !object_types TYPE /apmg/if_apm_importer=>ty_object_types !object_names TYPE /apmg/if_apm_importer=>ty_object_names !is_production TYPE abap_bool DEFAULT abap_true RETURNING VALUE(result) TYPE /apmg/if_apm_importer=>ty_map RAISING /apmg/cx_apm_error. CLASS-METHODS create_packages IMPORTING !packages TYPE /apmg/if_apm_importer=>ty_packages !is_dry_run TYPE abap_bool DEFAULT abap_true RAISING /apmg/cx_apm_error. CLASS-METHODS import_objects IMPORTING !map TYPE /apmg/if_apm_importer=>ty_map !transport TYPE trkorr !is_dry_run TYPE abap_bool DEFAULT abap_true !is_production TYPE abap_bool DEFAULT abap_true RAISING /apmg/cx_apm_error. CLASS-METHODS save_packages IMPORTING !packages TYPE /apmg/if_apm_importer=>ty_packages !dependencies TYPE /apmg/if_apm_importer=>ty_dependencies RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_installer DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm Installer * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * TODO: This installer is a copy from Marc Bernard Tools * Some of the features are not relevant for apm and can be removed ************************************************************************ PUBLIC SECTION. CONSTANTS: BEGIN OF c_enum_source, local TYPE i VALUE 0, internet TYPE i VALUE 1, server TYPE i VALUE 2, data TYPE i VALUE 3, registry TYPE i VALUE 4, END OF c_enum_source. CONSTANTS: BEGIN OF c_enum_folder_logic, default TYPE i VALUE 0, prefix TYPE i VALUE 1, mixed TYPE i VALUE 2, full TYPE i VALUE 3, END OF c_enum_folder_logic. CLASS-METHODS install IMPORTING !name TYPE string !version TYPE string !data TYPE xstring !package TYPE devclass !transport TYPE trkorr OPTIONAL !enum_source TYPE i " FUTURE !enum_folder_logic TYPE i " FUTURE !is_production TYPE abap_bool RAISING /apmg/cx_apm_error ##NEEDED. CLASS-METHODS uninstall IMPORTING !name TYPE string !version TYPE string !package TYPE devclass !transport TYPE trkorr OPTIONAL RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CLASS-DATA: remote_files TYPE zif_abapgit_git_definitions=>ty_files_tt, dot_abapgit TYPE REF TO zcl_abapgit_dot_abapgit, main_language TYPE sy-langu, folder_logic TYPE string, log TYPE REF TO zif_abapgit_log, clmcus_backup TYPE STANDARD TABLE OF clmcus WITH DEFAULT KEY. CLASS-METHODS _system_check RAISING /apmg/cx_apm_error. CLASS-METHODS _files IMPORTING !enum_source TYPE i !name TYPE string OPTIONAL !data TYPE xstring OPTIONAL RAISING /apmg/cx_apm_error. CLASS-METHODS _packaging RAISING /apmg/cx_apm_error. CLASS-METHODS _folder_logic IMPORTING !enum_folder_logic TYPE i RAISING /apmg/cx_apm_error. CLASS-METHODS _transport_check IMPORTING !package TYPE devclass !transport TYPE trkorr RAISING /apmg/cx_apm_error. CLASS-METHODS _transport_reset. CLASS-METHODS _namespaces IMPORTING !package TYPE devclass !transport TYPE trkorr !main_language TYPE sy-langu RAISING /apmg/cx_apm_error. CLASS-METHODS _confirm_messages. CLASS-METHODS _restore_messages. CLASS-METHODS _deserialize_objects IMPORTING !package TYPE devclass !transport TYPE trkorr !main_language TYPE sy-langu RAISING /apmg/cx_apm_error. CLASS-METHODS _log_start IMPORTING !title TYPE string !name TYPE string !version TYPE string. CLASS-METHODS _log_end RETURNING VALUE(result) TYPE sy-msgty RAISING /apmg/cx_apm_error. CLASS-METHODS _find_remote_dot_abapgit IMPORTING !remote TYPE zif_abapgit_git_definitions=>ty_files_tt RETURNING VALUE(result) TYPE REF TO zcl_abapgit_dot_abapgit RAISING /apmg/cx_apm_error. CLASS-METHODS _find_remote_namespaces RETURNING VALUE(result) TYPE zif_abapgit_git_definitions=>ty_files_tt. CLASS-METHODS _check_uninstalled IMPORTING !package TYPE devclass !tadir TYPE zif_abapgit_definitions=>ty_tadir_tt. CLASS-METHODS _uninstall_sotr IMPORTING !transport TYPE trkorr !tadir TYPE zif_abapgit_definitions=>ty_tadir_tt. CLASS-METHODS _uninstall_sots IMPORTING !transport TYPE trkorr !tadir TYPE zif_abapgit_definitions=>ty_tadir_tt. ENDCLASS. CLASS /apmg/cl_apm_installer_files DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm Installer Files * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS load_internet IMPORTING !url TYPE string !user TYPE string OPTIONAL !password TYPE string OPTIONAL !proxy_host TYPE string OPTIONAL !proxy_port TYPE string OPTIONAL !proxy_user TYPE string OPTIONAL !proxy_password TYPE string OPTIONAL RETURNING VALUE(result) TYPE xstring RAISING /apmg/cx_apm_error. CLASS-METHODS load_local IMPORTING !filename TYPE csequence RETURNING VALUE(result) TYPE xstring RAISING /apmg/cx_apm_error. CLASS-METHODS load_server IMPORTING !filename TYPE csequence RETURNING VALUE(result) TYPE xstring RAISING /apmg/cx_apm_error. CLASS-METHODS virus_scan IMPORTING !data TYPE xstring RAISING /apmg/cx_apm_error. CLASS-METHODS unzip IMPORTING !xstr TYPE xstring RETURNING VALUE(result) TYPE zif_abapgit_git_definitions=>ty_files_tt RAISING /apmg/cx_apm_error. CLASS-METHODS untar IMPORTING !xstr TYPE xstring RETURNING VALUE(result) TYPE zif_abapgit_git_definitions=>ty_files_tt RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. TYPES ty_hex TYPE x LENGTH 1024. CLASS-METHODS _filename IMPORTING !str TYPE string EXPORTING !path TYPE string !filename TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS _normalize_path CHANGING !files TYPE zif_abapgit_git_definitions=>ty_files_tt RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_json DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm JSON Helpers * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS get IMPORTING !json TYPE string !path TYPE string RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS to_string IMPORTING !value TYPE any RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS to_abap IMPORTING !json TYPE string CHANGING !result TYPE any RAISING /apmg/cx_apm_error. CLASS-METHODS validate_and_prettify IMPORTING !json TYPE string RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_logo DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm Logo * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS xml IMPORTING svg TYPE string RETURNING VALUE(result) TYPE string. CLASS-METHODS svg_logo IMPORTING height TYPE i DEFAULT 25 RETURNING VALUE(result) TYPE string. CLASS-METHODS svg_logo_with_text IMPORTING height TYPE i DEFAULT 25 RETURNING VALUE(result) TYPE string. CLASS-METHODS svg_cube IMPORTING height TYPE i DEFAULT 25 RETURNING VALUE(result) TYPE string. CLASS-METHODS svg_text IMPORTING height TYPE i DEFAULT 25 RETURNING VALUE(result) TYPE string. PROTECTED SECTION. PRIVATE SECTION. TYPES ty_ratio TYPE p LENGTH 10 DECIMALS 2. CLASS-METHODS replace_width_height IMPORTING svg TYPE string ratio TYPE ty_ratio height TYPE i RETURNING VALUE(result) TYPE string. ENDCLASS. *"* use this source file for any type of declarations (class *"* definitions, interfaces or type declarations) you need for *"* components in the private section "! "! Value type interface "! INTERFACE lif_value_type. METHODS copy IMPORTING source TYPE REF TO lif_value_type. ENDINTERFACE. "! "! String class for use in template objects "! CLASS lcl_string DEFINITION FINAL. PUBLIC SECTION. INTERFACES lif_value_type. METHODS: get_data RETURNING VALUE(result) TYPE string, set_data IMPORTING data TYPE string. PRIVATE SECTION. DATA data TYPE string. ENDCLASS. "! "! String array class for use in template objects "! CLASS lcl_string_array DEFINITION FINAL. PUBLIC SECTION. INTERFACES lif_value_type. METHODS: append IMPORTING value TYPE clike, append_array IMPORTING array TYPE REF TO lcl_string_array, delete IMPORTING value TYPE clike, find_val IMPORTING value TYPE clike RETURNING VALUE(result) TYPE i, get_data RETURNING VALUE(result) TYPE string_table, set_data IMPORTING data TYPE string_table. PRIVATE SECTION. DATA data TYPE string_table. ENDCLASS. "! "! Hashmap template class "! The key type is `string`, and the value type must be an object. "! "! A compound value type may be used, separating the basic type and its subsequent "! value type by a colon. "! Ex: 'lcl_hashmap:lcl_string_array' => The value type will be lcl_hashmap, "! and the value hashmaps' values type will be of type lcl_string_array. "! Ex: 'lcl_hashmap:lcl_hashmap:lcl_hashmap:lcl_string' => Recursive composition, "! for use of a 4-dimensional hashmap. "! CLASS lcl_hashmap DEFINITION FINAL. PUBLIC SECTION. INTERFACES lif_value_type. TYPES: BEGIN OF ty_item, key TYPE string, value TYPE REF TO lif_value_type, END OF ty_item, ty_hashmap TYPE HASHED TABLE OF ty_item WITH UNIQUE KEY key. METHODS: constructor IMPORTING value_type TYPE clike DEFAULT 'lcl_string', new IMPORTING key TYPE clike RETURNING VALUE(result) TYPE REF TO lif_value_type, exists IMPORTING key TYPE clike RETURNING VALUE(result) TYPE abap_bool, get IMPORTING key TYPE clike RETURNING VALUE(result) TYPE REF TO lif_value_type, set IMPORTING key TYPE clike value TYPE REF TO lif_value_type, delete IMPORTING key TYPE string, get_data RETURNING VALUE(result) TYPE ty_hashmap, set_data IMPORTING data TYPE ty_hashmap. PRIVATE SECTION. DATA data TYPE ty_hashmap. DATA: value_type TYPE string, subsequent_hashmap_value_type TYPE string. ENDCLASS. "! "! GitHub Alerts "! CLASS lcl_alerts DEFINITION FINAL. PUBLIC SECTION. TYPES: BEGIN OF ty_alert, tag TYPE string, class TYPE string, color TYPE string, icon TYPE string, text TYPE string, END OF ty_alert. CLASS-METHODS: get IMPORTING line TYPE string RETURNING VALUE(result) TYPE ty_alert, note RETURNING VALUE(result) TYPE string, tip RETURNING VALUE(result) TYPE string, important RETURNING VALUE(result) TYPE string, warning RETURNING VALUE(result) TYPE string, caution RETURNING VALUE(result) TYPE string. ENDCLASS. CLASS /apmg/cl_apm_markdown DEFINITION CREATE PUBLIC. ************************************************************************ * Markdown Renderer * * Original from https://github.com/koemaeda/abap-markdown * * Copyright (c) 2015 Guilherme Maeda * SPDX-License-Identifier: MIT ************************************************************************ * Added by apm: * - Option to render href and img src links with different root * - Option to use sapevent for launching links in external browser * - Option to set root path for internal links * - Normalizing of link paths * - Support for sapevent as protocol * - Syntax highlighting (based on abapGit + diff + markdown) * - Support for internal links (# Heading {#custom-id}) * - Support for strikethrough, subscript, superscript, highlight * - Support for task list ([ ] or [x] task) * - Fix a few regular expressions * - Support for GitHub alerts * - Fix for escaped | in tables * - CSS * - Remove variable prefixes, strict abaplint rules ************************************************************************ * TODO: Add "copy-to-clipboard" for code blocks ************************************************************************ PUBLIC SECTION. CONSTANTS c_version TYPE string VALUE '1.0.0' ##NEEDED. CLASS-METHODS styles RETURNING VALUE(result) TYPE string. METHODS text IMPORTING VALUE(text) TYPE clike RETURNING VALUE(markup) TYPE string. METHODS set_breaks_enabled IMPORTING VALUE(breaks_enabled) TYPE clike RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_markdown. METHODS set_markup_escaped IMPORTING VALUE(markup_escaped) TYPE clike RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_markdown. METHODS set_urls_linked IMPORTING VALUE(urls_linked) TYPE clike RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_markdown. METHODS set_safe_mode IMPORTING !iv_safe_mode TYPE clike RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_markdown. METHODS constructor IMPORTING !root_href TYPE string OPTIONAL !root_img TYPE string OPTIONAL !path TYPE string OPTIONAL !sapevent TYPE abap_bool DEFAULT abap_false. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_element_attribute, name TYPE string, value TYPE string, END OF ty_element_attribute, ty_t_element_attribute TYPE STANDARD TABLE OF ty_element_attribute WITH KEY name. TYPES: BEGIN OF ty_element0, name TYPE string, handler TYPE string, attributes TYPE ty_t_element_attribute, text TYPE string, lines TYPE string_table, END OF ty_element0, ty_t_element0 TYPE STANDARD TABLE OF ty_element0 WITH KEY name. TYPES: BEGIN OF ty_element1, name TYPE string, handler TYPE string, attributes TYPE ty_t_element_attribute, text TYPE string, texts TYPE ty_t_element0, lines TYPE string_table, END OF ty_element1, ty_t_element1 TYPE STANDARD TABLE OF ty_element1 WITH KEY name. TYPES: BEGIN OF ty_element2, name TYPE string, handler TYPE string, attributes TYPE ty_t_element_attribute, text TYPE string, texts TYPE ty_t_element1, lines TYPE string_table, END OF ty_element2, ty_t_element2 TYPE STANDARD TABLE OF ty_element2 WITH KEY name. TYPES: BEGIN OF ty_element3, name TYPE string, handler TYPE string, attributes TYPE ty_t_element_attribute, text TYPE string, texts TYPE ty_t_element2, lines TYPE string_table, END OF ty_element3, ty_t_element3 TYPE STANDARD TABLE OF ty_element3 WITH KEY name. TYPES: BEGIN OF ty_element4, name TYPE string, handler TYPE string, attributes TYPE ty_t_element_attribute, text TYPE string, texts TYPE ty_t_element3, lines TYPE string_table, END OF ty_element4, ty_t_element4 TYPE STANDARD TABLE OF ty_element4 WITH KEY name. TYPES: BEGIN OF ty_element5, name TYPE string, handler TYPE string, attributes TYPE ty_t_element_attribute, text TYPE ty_element4, texts TYPE ty_t_element4, lines TYPE string_table, END OF ty_element5. TYPES ty_element TYPE ty_element5. TYPES: BEGIN OF ty_block, "// general block fields continuable TYPE abap_bool, identified TYPE abap_bool, interrupted TYPE abap_bool, hidden TYPE abap_bool, closed TYPE abap_bool, type TYPE string, markup TYPE string, element TYPE ty_element, "// specific block fields char TYPE c LENGTH 1, complete TYPE abap_bool, indent TYPE i, pattern TYPE string, li TYPE ty_element4, loose TYPE abap_bool, name TYPE string, depth TYPE i, void TYPE abap_bool, alignments TYPE string_table, END OF ty_block. TYPES: BEGIN OF ty_line, body TYPE string, indent TYPE i, text TYPE string, END OF ty_line. TYPES: BEGIN OF ty_excerpt, text TYPE string, context TYPE string, END OF ty_excerpt. TYPES: BEGIN OF ty_inline, position TYPE i, markup TYPE string, extent TYPE string, element TYPE ty_element, END OF ty_inline. ">>> apm DATA: BEGIN OF config, root_href TYPE string, root_img TYPE string, sapevent TYPE abap_bool, path TYPE string, path_util TYPE REF TO /apmg/cl_apm_markdown_path, END OF config. "<<< apm DATA breaks_enabled TYPE abap_bool. DATA markup_escaped TYPE abap_bool. DATA urls_linked TYPE abap_bool VALUE abap_true. DATA safe_mode TYPE abap_bool. DATA block_types TYPE REF TO lcl_hashmap. DATA unmarked_block_types TYPE REF TO lcl_string_array. DATA inline_types TYPE REF TO lcl_hashmap. "DATA inline_marker_list TYPE string VALUE '!"*_&[:<>`~\\' ##NO_TEXT DATA inline_marker_list TYPE string VALUE '!"*_&[:<>`~\\=^' ##NO_TEXT. " apm DATA definition_data TYPE REF TO lcl_hashmap. DATA special_characters TYPE REF TO lcl_string_array. DATA strong_regex TYPE REF TO lcl_hashmap. DATA em_regex TYPE REF TO lcl_hashmap. DATA regex_html_attribute TYPE string VALUE '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"''=<>`\s]+|"[^"]*"|''[^'']*''))?' ##NO_TEXT. DATA void_elements TYPE REF TO lcl_string_array. DATA text_level_elements TYPE REF TO lcl_string_array. DATA safe_links_whitelist TYPE REF TO lcl_string_array. DATA methods TYPE string_table. CLASS-METHODS htmlspecialchars IMPORTING !input TYPE string !ent_html401 TYPE abap_bool DEFAULT abap_true !ent_noquotes TYPE abap_bool OPTIONAL !ent_quotes TYPE abap_bool OPTIONAL RETURNING VALUE(result) TYPE string. CLASS-METHODS trim IMPORTING !str TYPE string !mask TYPE string DEFAULT ' \t\n\r' RETURNING VALUE(result) TYPE string. CLASS-METHODS chop IMPORTING !str TYPE string !mask TYPE string DEFAULT ' \t\n\r' RETURNING VALUE(result) TYPE string ##CALLED. CLASS-METHODS magic_move IMPORTING !from TYPE any !name TYPE clike OPTIONAL CHANGING !to TYPE any. CLASS-METHODS match_marked_string IMPORTING !marker TYPE string !subject TYPE string EXPORTING !m0 TYPE string !m1 TYPE string !not_found TYPE abap_bool. CLASS-METHODS _escape IMPORTING !text TYPE string !allow_quotes TYPE abap_bool OPTIONAL RETURNING VALUE(result) TYPE string. CLASS-METHODS string_at_start IMPORTING !haystack TYPE string !needle TYPE string RETURNING VALUE(result) TYPE abap_bool. METHODS _lines IMPORTING !lines TYPE STANDARD TABLE RETURNING VALUE(result) TYPE string. " Dynamically called methods METHODS block_code IMPORTING !line TYPE ty_line !block TYPE ty_block OPTIONAL RETURNING VALUE(result) TYPE ty_block ##CALLED ##NEEDED. METHODS block_code_continue IMPORTING !line TYPE ty_line !block TYPE ty_block RETURNING VALUE(result) TYPE ty_block ##CALLED. METHODS block_code_complete IMPORTING !block TYPE ty_block RETURNING VALUE(result) TYPE ty_block ##CALLED. METHODS block_comment IMPORTING !line TYPE ty_line !block TYPE ty_block OPTIONAL RETURNING VALUE(result) TYPE ty_block ##CALLED ##NEEDED. METHODS block_comment_continue IMPORTING !line TYPE ty_line !block TYPE ty_block RETURNING VALUE(result) TYPE ty_block ##CALLED. METHODS block_fencedcode IMPORTING !line TYPE ty_line !block TYPE ty_block OPTIONAL RETURNING VALUE(result) TYPE ty_block ##CALLED ##NEEDED. METHODS block_fencedcode_continue IMPORTING !line TYPE ty_line !block TYPE ty_block RETURNING VALUE(result) TYPE ty_block ##CALLED. METHODS block_fencedcode_complete IMPORTING !block TYPE ty_block RETURNING VALUE(result) TYPE ty_block ##CALLED. METHODS block_header IMPORTING !line TYPE ty_line !block TYPE ty_block OPTIONAL RETURNING VALUE(result) TYPE ty_block ##CALLED ##NEEDED. METHODS block_list IMPORTING !line TYPE ty_line !block TYPE ty_block OPTIONAL RETURNING VALUE(result) TYPE ty_block ##CALLED ##NEEDED. METHODS block_list_continue IMPORTING !line TYPE ty_line !block TYPE ty_block RETURNING VALUE(result) TYPE ty_block ##CALLED. METHODS block_list_complete IMPORTING !block TYPE ty_block RETURNING VALUE(result) TYPE ty_block ##CALLED. METHODS block_quote IMPORTING !line TYPE ty_line !block TYPE ty_block OPTIONAL RETURNING VALUE(result) TYPE ty_block ##CALLED ##NEEDED. METHODS block_quote_complete IMPORTING !block TYPE ty_block RETURNING VALUE(result) TYPE ty_block ##CALLED. METHODS block_quote_continue IMPORTING !line TYPE ty_line !block TYPE ty_block RETURNING VALUE(result) TYPE ty_block ##CALLED. METHODS block_rule IMPORTING !line TYPE ty_line !block TYPE ty_block OPTIONAL RETURNING VALUE(result) TYPE ty_block ##CALLED ##NEEDED. METHODS block_setextheader IMPORTING !line TYPE ty_line !block TYPE ty_block OPTIONAL RETURNING VALUE(result) TYPE ty_block ##CALLED ##NEEDED. METHODS block_markup IMPORTING !line TYPE ty_line !block TYPE ty_block OPTIONAL RETURNING VALUE(result) TYPE ty_block ##CALLED ##NEEDED. METHODS block_markup_continue IMPORTING !line TYPE ty_line !block TYPE ty_block RETURNING VALUE(result) TYPE ty_block ##CALLED. METHODS block_reference IMPORTING !line TYPE ty_line !block TYPE ty_block OPTIONAL RETURNING VALUE(result) TYPE ty_block ##CALLED ##NEEDED. METHODS block_table IMPORTING !line TYPE ty_line !block TYPE ty_block OPTIONAL RETURNING VALUE(result) TYPE ty_block ##CALLED ##NEEDED. METHODS block_table_continue IMPORTING !line TYPE ty_line !block TYPE ty_block RETURNING VALUE(result) TYPE ty_block ##CALLED. METHODS paragraph IMPORTING !line TYPE ty_line RETURNING VALUE(result) TYPE ty_block ##CALLED. METHODS line IMPORTING !element TYPE ty_element4 RETURNING VALUE(result) TYPE string ##CALLED. METHODS inline_code IMPORTING !excerpt TYPE ty_excerpt RETURNING VALUE(result) TYPE ty_inline ##CALLED. METHODS inline_emailtag IMPORTING !excerpt TYPE ty_excerpt RETURNING VALUE(result) TYPE ty_inline ##CALLED. METHODS inline_emphasis IMPORTING !excerpt TYPE ty_excerpt RETURNING VALUE(result) TYPE ty_inline ##CALLED. METHODS inline_escapesequence IMPORTING !excerpt TYPE ty_excerpt RETURNING VALUE(result) TYPE ty_inline ##CALLED. METHODS inline_image IMPORTING !excerpt TYPE ty_excerpt RETURNING VALUE(result) TYPE ty_inline ##CALLED. METHODS inline_link IMPORTING !excerpt TYPE ty_excerpt RETURNING VALUE(result) TYPE ty_inline ##CALLED. METHODS inline_markup IMPORTING !excerpt TYPE ty_excerpt RETURNING VALUE(result) TYPE ty_inline ##CALLED. METHODS inline_specialcharacter IMPORTING !excerpt TYPE ty_excerpt RETURNING VALUE(result) TYPE ty_inline ##CALLED. METHODS inline_strikethrough IMPORTING !excerpt TYPE ty_excerpt RETURNING VALUE(result) TYPE ty_inline ##CALLED. METHODS inline_url IMPORTING !excerpt TYPE ty_excerpt RETURNING VALUE(result) TYPE ty_inline ##CALLED. METHODS inline_urltag IMPORTING !excerpt TYPE ty_excerpt RETURNING VALUE(result) TYPE ty_inline ##CALLED. ">>> apm METHODS inline_highlight IMPORTING !excerpt TYPE ty_excerpt RETURNING VALUE(result) TYPE ty_inline ##CALLED. "<<< apm METHODS unmarked_text IMPORTING !text TYPE string RETURNING VALUE(result) TYPE string. METHODS element IMPORTING !element TYPE any RETURNING VALUE(result) TYPE string. METHODS elements IMPORTING !elements TYPE STANDARD TABLE RETURNING VALUE(result) TYPE string ##CALLED. METHODS li IMPORTING !lines TYPE STANDARD TABLE RETURNING VALUE(result) TYPE string ##CALLED. METHODS filter_unsafe_url_in_attribute IMPORTING !element TYPE ty_element !attribute TYPE string RETURNING VALUE(result) TYPE ty_element. METHODS sanitise_element IMPORTING !element TYPE ty_element RETURNING VALUE(result) TYPE ty_element. ">>> apm METHODS _adjust_link IMPORTING !root TYPE string !source TYPE string RETURNING VALUE(result) TYPE string. METHODS _adjust_a_href IMPORTING !source TYPE string RETURNING VALUE(result) TYPE string. METHODS _adjust_img_src IMPORTING !source TYPE string RETURNING VALUE(result) TYPE string. METHODS _adjust_markup IMPORTING !source TYPE string RETURNING VALUE(result) TYPE string. METHODS syntax_highlighter IMPORTING !element TYPE any RETURNING VALUE(result) TYPE string ##CALLED. "<<< apm ENDCLASS. CLASS /apmg/cl_apm_markdown_path DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * Markdown Path * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. METHODS normalize IMPORTING !path TYPE string RETURNING VALUE(result) TYPE string. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_slash TYPE string VALUE '/'. CONSTANTS c_dot TYPE string VALUE '.'. METHODS posix_normalize IMPORTING path TYPE string allow_above_root TYPE abap_bool RETURNING VALUE(result) TYPE string. METHODS char_at IMPORTING val TYPE string off TYPE i RETURNING VALUE(result) TYPE string. METHODS slice IMPORTING val TYPE string start TYPE i end TYPE i RETURNING VALUE(result) TYPE string. METHODS last_index_of IMPORTING val TYPE string sub TYPE string RETURNING VALUE(result) TYPE i. ENDCLASS. CLASS /apmg/cl_apm_markdown_syn DEFINITION CREATE PUBLIC. ************************************************************************ * Markdown Syntax highlighter * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS process IMPORTING !source TYPE string !language TYPE string RETURNING VALUE(result) TYPE string. CLASS-METHODS process_line IMPORTING !line TYPE string !language TYPE string RETURNING VALUE(result) TYPE string. PROTECTED SECTION. PRIVATE SECTION. CLASS-DATA: current_language TYPE string, highlighter TYPE REF TO /apmg/cl_apm_highlighter. CLASS-METHODS create IMPORTING !language TYPE string RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_highlighter. ENDCLASS. CLASS zcl_abapgit_oo_base DEFINITION ABSTRACT CREATE PROTECTED. PUBLIC SECTION. INTERFACES zif_abapgit_oo_object_fnc . CONSTANTS c_cp_program_type TYPE c LENGTH 1 VALUE 'K'. CONSTANTS c_include_program_type TYPE c LENGTH 1 VALUE 'I'. CONSTANTS c_ip_program_type TYPE c LENGTH 1 VALUE 'J'. PROTECTED SECTION. CLASS-METHODS: convert_attrib_to_vseoattrib IMPORTING iv_clsname TYPE seoclsname it_attributes TYPE zif_abapgit_oo_object_fnc=>ty_obj_attribute_tt RETURNING VALUE(rt_vseoattrib) TYPE seoo_attributes_r. PRIVATE SECTION. CONSTANTS c_docu_state_active TYPE dokstate VALUE 'A'. " See include SDOC_CONSTANTS DATA mv_skip_test_classes TYPE abap_bool . ENDCLASS. CLASS zcl_abapgit_oo_class DEFINITION INHERITING FROM zcl_abapgit_oo_base CREATE PUBLIC FRIENDS zcl_abapgit_oo_factory. PUBLIC SECTION. METHODS zif_abapgit_oo_object_fnc~create REDEFINITION . METHODS zif_abapgit_oo_object_fnc~create_sotr REDEFINITION . METHODS zif_abapgit_oo_object_fnc~delete REDEFINITION . METHODS zif_abapgit_oo_object_fnc~deserialize_source REDEFINITION . METHODS zif_abapgit_oo_object_fnc~generate_locals REDEFINITION . METHODS zif_abapgit_oo_object_fnc~get_class_properties REDEFINITION . METHODS zif_abapgit_oo_object_fnc~get_includes REDEFINITION . METHODS zif_abapgit_oo_object_fnc~insert_text_pool REDEFINITION . METHODS zif_abapgit_oo_object_fnc~read_sotr REDEFINITION . METHODS zif_abapgit_oo_object_fnc~read_text_pool REDEFINITION . METHODS zif_abapgit_oo_object_fnc~exists REDEFINITION . METHODS zif_abapgit_oo_object_fnc~syntax_check REDEFINITION . PROTECTED SECTION. TYPES: ty_char1 TYPE c LENGTH 1 . TYPES: ty_char2 TYPE c LENGTH 2 . PRIVATE SECTION. CLASS-METHODS update_source_index IMPORTING !iv_clsname TYPE csequence !io_scanner TYPE REF TO cl_oo_source_scanner_class . CLASS-METHODS update_report IMPORTING !iv_program TYPE syrepid !it_source TYPE string_table !iv_package TYPE devclass !iv_version TYPE uccheck RETURNING VALUE(rv_updated) TYPE abap_bool RAISING zcx_abapgit_exception . CLASS-METHODS generate_classpool IMPORTING !iv_name TYPE seoclsname RAISING zcx_abapgit_exception . CLASS-METHODS update_meta IMPORTING !iv_name TYPE seoclsname !iv_exposure TYPE seoexpose !it_source TYPE rswsourcet RAISING zcx_abapgit_exception . CLASS-METHODS determine_method_include IMPORTING !iv_name TYPE seoclsname !iv_method TYPE seocpdname RETURNING VALUE(rv_program) TYPE syrepid RAISING zcx_abapgit_exception . CLASS-METHODS init_scanner IMPORTING !it_source TYPE zif_abapgit_definitions=>ty_string_tt !iv_name TYPE seoclsname RETURNING VALUE(ro_scanner) TYPE REF TO cl_oo_source_scanner_class RAISING zcx_abapgit_exception . CLASS-METHODS update_full_class_include IMPORTING !iv_classname TYPE seoclsname !it_source TYPE string_table !it_methods TYPE cl_oo_source_scanner_class=>type_method_implementations !iv_package TYPE devclass !iv_version TYPE uccheck RAISING zcx_abapgit_exception. CLASS-METHODS create_report IMPORTING !iv_program TYPE syrepid !it_source TYPE string_table !iv_extension TYPE ty_char2 !iv_program_type TYPE ty_char1 !iv_state TYPE r3state !iv_package TYPE devclass !iv_version TYPE uccheck RAISING zcx_abapgit_exception. CLASS-METHODS update_cs_number_of_methods IMPORTING !iv_classname TYPE seoclsname !iv_number_of_impl_methods TYPE i . CLASS-METHODS delete_report IMPORTING !iv_program TYPE syrepid RAISING zcx_abapgit_exception. CLASS-METHODS get_method_includes IMPORTING !iv_classname TYPE seoclsname RETURNING VALUE(rt_includes) TYPE seop_methods_w_include. CLASS-METHODS repair_classpool IMPORTING !is_key TYPE seoclskey RAISING zcx_abapgit_exception . CLASS-METHODS repair_redefinitions IMPORTING !is_key TYPE seoclskey RAISING zcx_abapgit_exception . ENDCLASS. CLASS /apmg/cl_apm_object_clas DEFINITION FINAL CREATE PUBLIC INHERITING FROM zcl_abapgit_oo_class. ************************************************************************ * apm CLAS Importer * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES /apmg/if_apm_object. METHODS constructor IMPORTING !item TYPE /apmg/if_apm_object=>ty_item. PROTECTED SECTION. PRIVATE SECTION. DATA class_name TYPE seoclsname. METHODS source RETURNING VALUE(result) TYPE seop_source_string RAISING /apmg/cx_apm_error. ENDCLASS. CLASS zcl_abapgit_oo_interface DEFINITION INHERITING FROM zcl_abapgit_oo_base CREATE PUBLIC FRIENDS zcl_abapgit_oo_factory. PUBLIC SECTION. METHODS zif_abapgit_oo_object_fnc~create REDEFINITION . METHODS zif_abapgit_oo_object_fnc~delete REDEFINITION . METHODS zif_abapgit_oo_object_fnc~get_includes REDEFINITION . METHODS zif_abapgit_oo_object_fnc~get_interface_properties REDEFINITION . METHODS zif_abapgit_oo_object_fnc~deserialize_source REDEFINITION . METHODS zif_abapgit_oo_object_fnc~exists REDEFINITION . METHODS zif_abapgit_oo_object_fnc~syntax_check REDEFINITION . PROTECTED SECTION. PRIVATE SECTION. CLASS-METHODS update_report IMPORTING !iv_program TYPE syrepid !it_source TYPE string_table !iv_package TYPE devclass !iv_version TYPE uccheck RETURNING VALUE(rv_updated) TYPE abap_bool RAISING zcx_abapgit_exception . CLASS-METHODS update_meta IMPORTING !iv_name TYPE seoclsname !it_source TYPE rswsourcet RAISING zcx_abapgit_exception . CLASS-METHODS init_scanner IMPORTING !it_source TYPE zif_abapgit_definitions=>ty_string_tt !iv_name TYPE seoclsname RETURNING VALUE(ro_scanner) TYPE REF TO cl_oo_source_scanner_interface RAISING zcx_abapgit_exception . ENDCLASS. CLASS /apmg/cl_apm_object_intf DEFINITION FINAL CREATE PUBLIC INHERITING FROM zcl_abapgit_oo_interface. ************************************************************************ * apm INTF Importer * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES /apmg/if_apm_object. METHODS constructor IMPORTING !item TYPE /apmg/if_apm_object=>ty_item. PROTECTED SECTION. PRIVATE SECTION. "! Original interface name DATA interface_name TYPE seoclsname. METHODS source RETURNING VALUE(result) TYPE seop_source_string RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_object_prog DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm PROG Importer (=WIP=) * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * TODO!: Activation of target program * TODO: Add support for dynpros, cua, longtexts ************************************************************************ PUBLIC SECTION. INTERFACES /apmg/if_apm_object. METHODS constructor IMPORTING !item TYPE /apmg/if_apm_object=>ty_item. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: BEGIN OF c_state, active TYPE r3state VALUE 'A', inactive TYPE r3state VALUE 'I', END OF c_state. DATA program_name TYPE program. METHODS deserialize_program IMPORTING !progdir TYPE zif_abapgit_sap_report=>ty_progdir !source TYPE /apmg/if_apm_importer=>ty_code !tpool TYPE textpool_table !package TYPE devclass RAISING /apmg/cx_apm_error. METHODS deserialize_textpool IMPORTING !program TYPE syrepid !tpool TYPE textpool_table !language TYPE sy-langu OPTIONAL !include TYPE abap_bool DEFAULT abap_false RAISING /apmg/cx_apm_error. METHODS get_program_title IMPORTING !tpool TYPE textpool_table RETURNING VALUE(result) TYPE repti. METHODS insert_program IMPORTING !progdir TYPE zif_abapgit_sap_report=>ty_progdir !source TYPE /apmg/if_apm_importer=>ty_code !title TYPE repti !package TYPE devclass !state TYPE progdir-state DEFAULT c_state-inactive RAISING /apmg/cx_apm_error. METHODS update_program IMPORTING !progdir TYPE zif_abapgit_sap_report=>ty_progdir !source TYPE /apmg/if_apm_importer=>ty_code !title TYPE repti !state TYPE progdir-state DEFAULT c_state-inactive RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_package_json DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * Package JSON * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES: /apmg/if_apm_types, /apmg/if_apm_package_json. CLASS-METHODS class_constructor. CLASS-METHODS factory IMPORTING !package TYPE devclass !name TYPE string OPTIONAL !version TYPE string OPTIONAL !private TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_package_json RAISING /apmg/cx_apm_error. CLASS-METHODS injector IMPORTING !package TYPE devclass !mock TYPE REF TO /apmg/if_apm_package_json. METHODS constructor IMPORTING !package TYPE devclass !name TYPE string OPTIONAL !version TYPE string OPTIONAL !private TYPE abap_bool DEFAULT abap_false RAISING /apmg/cx_apm_error. CLASS-METHODS list IMPORTING !filter TYPE string OPTIONAL !instanciate TYPE abap_bool DEFAULT abap_false !is_bundle TYPE abap_bool DEFAULT abap_undefined RETURNING VALUE(result) TYPE /apmg/if_apm_package_json=>ty_packages. CLASS-METHODS get_package_key IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE /apmg/if_apm_persist_apm=>ty_key. CLASS-METHODS get_package_from_key IMPORTING !key TYPE /apmg/if_apm_persist_apm=>ty_key RETURNING VALUE(result) TYPE devclass. CLASS-METHODS get_package_from_id IMPORTING !id TYPE /apmg/if_apm_package_json=>ty_package_id RETURNING VALUE(result) TYPE devclass. CLASS-METHODS get_id_from_package IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE /apmg/if_apm_package_json=>ty_package_id. CLASS-METHODS convert_json_to_manifest IMPORTING !json TYPE string RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_manifest RAISING /apmg/cx_apm_error. CLASS-METHODS convert_json_to_manifest_abbr IMPORTING !json TYPE string RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_manifest_abbreviated RAISING /apmg/cx_apm_error. CLASS-METHODS convert_manifest_to_json IMPORTING !manifest TYPE /apmg/if_apm_types=>ty_manifest !is_package_json TYPE abap_bool DEFAULT abap_false !is_complete TYPE abap_bool DEFAULT abap_false !is_deprecated TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_instance, package TYPE devclass, instance TYPE REF TO /apmg/if_apm_package_json, END OF ty_instance, ty_instances TYPE HASHED TABLE OF ty_instance WITH UNIQUE KEY package. TYPES ty_package_list TYPE STANDARD TABLE OF devclass WITH KEY table_line. CLASS-DATA: db_persist TYPE REF TO /apmg/if_apm_persist_apm, instances TYPE ty_instances. DATA: key TYPE /apmg/if_apm_persist_apm=>ty_key, package TYPE devclass, manifest TYPE /apmg/if_apm_types=>ty_manifest. CLASS-METHODS check_manifest IMPORTING !manifest TYPE /apmg/if_apm_types=>ty_manifest RAISING /apmg/cx_apm_error. CLASS-METHODS sort_manifest IMPORTING !manifest TYPE /apmg/if_apm_types=>ty_manifest RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_manifest. CLASS-METHODS replace_slash IMPORTING !value TYPE string RETURNING VALUE(result) TYPE string. CLASS-METHODS get_super_packages IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE ty_package_list. ENDCLASS. CLASS /apmg/cl_apm_package_json_vali DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * Package JSON Validator * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS check IMPORTING !manifest TYPE /apmg/if_apm_types=>ty_manifest RETURNING VALUE(result) TYPE string_table. CLASS-METHODS is_valid_package_type IMPORTING !type TYPE string RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_valid_sap_package IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_scoped IMPORTING !name TYPE string RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_valid_scope IMPORTING !scope TYPE string RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_valid_name IMPORTING !name TYPE string RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_valid_version IMPORTING !version TYPE string RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_valid_version_range IMPORTING !range TYPE string RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_valid_email IMPORTING !email TYPE string RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_valid_url IMPORTING !url TYPE string RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_valid_engine IMPORTING !engine TYPE string RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_valid_os IMPORTING !os TYPE string RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_valid_cpu IMPORTING !cpu TYPE string RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_valid_db IMPORTING !db TYPE string RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_valid_timestamp IMPORTING !timestamp TYPE string RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_valid_abap_language_version IMPORTING !vers TYPE string RETURNING VALUE(result) TYPE abap_bool. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_pacote DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * Pacote * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES /apmg/if_apm_pacote. CLASS-METHODS class_constructor. CLASS-METHODS factory IMPORTING !registry TYPE string !name TYPE string !packument TYPE string OPTIONAL RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_pacote RAISING /apmg/cx_apm_error. CLASS-METHODS injector IMPORTING !name TYPE string !mock TYPE REF TO /apmg/if_apm_pacote. METHODS constructor IMPORTING !registry TYPE string !name TYPE string !packument TYPE string OPTIONAL RAISING /apmg/cx_apm_error. CLASS-METHODS get_packument_key IMPORTING !name TYPE string RETURNING VALUE(result) TYPE /apmg/if_apm_persist_apm=>ty_key. CLASS-METHODS get_packument_from_key IMPORTING !key TYPE /apmg/if_apm_persist_apm=>ty_key RETURNING VALUE(result) TYPE string. CLASS-METHODS convert_json_to_packument IMPORTING !json TYPE string RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_packument RAISING /apmg/cx_apm_error. CLASS-METHODS convert_packument_to_json IMPORTING !packument TYPE /apmg/if_apm_types=>ty_packument !is_complete TYPE abap_bool DEFAULT abap_false !is_deprecated TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_abbreviated_json TYPE string VALUE 'application/vnd.npm.install-v1+json'. TYPES: BEGIN OF ty_instance, name TYPE string, instance TYPE REF TO /apmg/if_apm_pacote, END OF ty_instance, ty_instances TYPE HASHED TABLE OF ty_instance WITH UNIQUE KEY name. CLASS-DATA: db_persist TYPE REF TO /apmg/if_apm_persist_apm, instances TYPE ty_instances. DATA: registry TYPE string, pacote TYPE /apmg/if_apm_pacote=>ty_pacote. METHODS get_agent IMPORTING !url TYPE string !abbreviated TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_http_agent RAISING /apmg/cx_apm_error. METHODS request IMPORTING !url TYPE string !abbreviated TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_http_response RAISING /apmg/cx_apm_error. METHODS check_result IMPORTING !json TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS check_packument IMPORTING !packument TYPE /apmg/if_apm_types=>ty_packument RAISING /apmg/cx_apm_error. CLASS-METHODS sort_packument IMPORTING !packument TYPE /apmg/if_apm_types=>ty_packument RETURNING VALUE(result) TYPE /apmg/if_apm_types=>ty_packument RAISING /apmg/cx_apm_error. CLASS-METHODS write_request IMPORTING !write TYPE abap_bool RETURNING VALUE(result) TYPE string. ENDCLASS. CLASS /apmg/cl_apm_persist_apm DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * apm Persistence * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES /apmg/if_apm_persist_apm. CLASS-METHODS get_instance RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_persist_apm. CLASS-METHODS injector IMPORTING !mock TYPE REF TO /apmg/if_apm_persist_apm. CLASS-METHODS validate_key IMPORTING !key TYPE csequence RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS explain_key IMPORTING !key TYPE csequence RETURNING VALUE(result) TYPE /apmg/if_apm_persist_apm=>ty_explained. PROTECTED SECTION. PRIVATE SECTION. CLASS-DATA db_instance TYPE REF TO /apmg/if_apm_persist_apm. ENDCLASS. CLASS /apmg/cl_apm_persist_apm_setup DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm Persistence Setup * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS install RAISING /apmg/cx_apm_error. CLASS-METHODS uninstall RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CLASS-METHODS logo_create RAISING /apmg/cx_apm_error. CLASS-METHODS logo_delete RAISING /apmg/cx_apm_error. CLASS-METHODS logo_exists RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS table_create RAISING /apmg/cx_apm_error. CLASS-METHODS table_delete RAISING /apmg/cx_apm_error. CLASS-METHODS table_exists RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS lock_create RAISING /apmg/cx_apm_error. CLASS-METHODS lock_delete RAISING /apmg/cx_apm_error. CLASS-METHODS lock_exists RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS delete_ddic IMPORTING !objtype TYPE rsedd0-ddobjtype !objname TYPE rsedd0-ddobjname !no_ask TYPE abap_bool DEFAULT abap_true !no_ask_delete_append TYPE abap_bool DEFAULT abap_false RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_popup_utils DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. CLASS-METHODS create_package IMPORTING !package TYPE csequence OPTIONAL RETURNING VALUE(result) TYPE devclass RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_progress_bar DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * Progress Bar * * Copyright (c) 2014 abapGit Contributors * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CONSTANTS c_version TYPE string VALUE '1.0.0' ##NEEDED. INTERFACES /apmg/if_apm_progress_bar. CLASS-METHODS set_instance IMPORTING !instance TYPE REF TO /apmg/if_apm_progress_bar. CLASS-METHODS get_instance IMPORTING !total TYPE i RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_progress_bar. PROTECTED SECTION. CLASS-DATA global_instance TYPE REF TO /apmg/if_apm_progress_bar. DATA total TYPE i. METHODS calculate_percentage IMPORTING !current TYPE i RETURNING VALUE(result) TYPE i. PRIVATE SECTION. DATA time_next TYPE t. DATA date_next TYPE d. ENDCLASS. CLASS /apmg/cl_apm_readme DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * Readme * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES /apmg/if_apm_readme. CLASS-METHODS class_constructor. CLASS-METHODS factory IMPORTING !package TYPE devclass !markdown TYPE string OPTIONAL RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_readme RAISING /apmg/cx_apm_error. CLASS-METHODS injector IMPORTING !package TYPE devclass !mock TYPE REF TO /apmg/if_apm_readme. METHODS constructor IMPORTING !package TYPE devclass !markdown TYPE string OPTIONAL RAISING /apmg/cx_apm_error. CLASS-METHODS get_package_key IMPORTING !package TYPE devclass RETURNING VALUE(result) TYPE /apmg/if_apm_persist_apm=>ty_key. CLASS-METHODS get_package_from_key IMPORTING !key TYPE /apmg/if_apm_persist_apm=>ty_key RETURNING VALUE(result) TYPE devclass. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_instance, package TYPE devclass, instance TYPE REF TO /apmg/if_apm_readme, END OF ty_instance, ty_instances TYPE HASHED TABLE OF ty_instance WITH UNIQUE KEY package. CLASS-DATA: db_persist TYPE REF TO /apmg/if_apm_persist_apm, instances TYPE ty_instances. DATA: package TYPE devclass, readme TYPE /apmg/if_apm_readme=>ty_readme. ENDCLASS. CLASS /apmg/cl_apm_roadmap DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm Roadmap Placeholders * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS not_implemented RAISING /apmg/cx_apm_error. CLASS-METHODS planned IMPORTING !message TYPE string OPTIONAL RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_semver DEFINITION CREATE PRIVATE. ************************************************************************ * SemVer * * Copyright (c) Isaac Z. Schlueter and Contributors * Ported to ABAP by apm.to Inc. * SPDX-License-Identifier: ISC ************************************************************************ PUBLIC SECTION. DATA: version TYPE string READ-ONLY, major TYPE i READ-ONLY, minor TYPE i READ-ONLY, patch TYPE i READ-ONLY, prerelease TYPE string_table READ-ONLY, build TYPE string_table READ-ONLY. METHODS constructor IMPORTING !version TYPE string !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RAISING /apmg/cx_apm_error. CLASS-METHODS create IMPORTING !version TYPE any !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_semver RAISING /apmg/cx_apm_error. METHODS format RETURNING VALUE(result) TYPE string. METHODS to_string RETURNING VALUE(result) TYPE string. METHODS compare IMPORTING !other TYPE any RETURNING VALUE(result) TYPE i RAISING /apmg/cx_apm_error. METHODS compare_main IMPORTING !other TYPE any RETURNING VALUE(result) TYPE i RAISING /apmg/cx_apm_error. METHODS compare_pre IMPORTING !other TYPE any RETURNING VALUE(result) TYPE i RAISING /apmg/cx_apm_error. METHODS compare_build IMPORTING !other TYPE any RETURNING VALUE(result) TYPE i RAISING /apmg/cx_apm_error. METHODS inc IMPORTING !release_type TYPE string !identifier TYPE string OPTIONAL !identifier_base TYPE string OPTIONAL RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_semver RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS false TYPE string VALUE 'false'. DATA: raw TYPE string, options TYPE /apmg/if_apm_semver_options=>ty_options. METHODS _inc_check IMPORTING release_type TYPE string identifier TYPE string OPTIONAL identifier_base TYPE string OPTIONAL RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_semver_cli DEFINITION CREATE PUBLIC. ************************************************************************ * SemVer CLI * * Copyright (c) Isaac Z. Schlueter and Contributors * Ported to ABAP by apm.to Inc. * SPDX-License-Identifier: ISC ************************************************************************ PUBLIC SECTION. CLASS-METHODS main IMPORTING !args TYPE string RETURNING VALUE(result) TYPE string_table RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CLASS-DATA: argv TYPE string_table, versions TYPE string_table, ranges TYPE string_table, inc TYPE string, identifier TYPE string, identifier_base TYPE string, help TYPE abap_bool, loose TYPE abap_bool, incpre TYPE abap_bool, coerce TYPE abap_bool, rtl TYPE abap_bool, reverse TYPE abap_bool. CLASS-METHODS _argv IMPORTING args TYPE string. CLASS-METHODS _versions RAISING /apmg/cx_apm_error. CLASS-METHODS _help RETURNING VALUE(result) TYPE string_table. CLASS-METHODS _success RETURNING VALUE(result) TYPE string_table RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_semver_comparator DEFINITION CREATE PRIVATE. ************************************************************************ * SemVer Comparator * * Copyright (c) Isaac Z. Schlueter and Contributors * Ported to ABAP by apm.to Inc. * SPDX-License-Identifier: ISC ************************************************************************ PUBLIC SECTION. CLASS-DATA any_semver TYPE REF TO /apmg/cl_apm_semver READ-ONLY. DATA: operator TYPE string READ-ONLY, value TYPE string READ-ONLY, semver TYPE REF TO /apmg/cl_apm_semver READ-ONLY. CLASS-METHODS class_constructor. METHODS constructor IMPORTING !comp TYPE string !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RAISING /apmg/cx_apm_error. CLASS-METHODS create IMPORTING !comp TYPE any !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_semver_comparator RAISING /apmg/cx_apm_error. METHODS parse IMPORTING !comp TYPE string RAISING /apmg/cx_apm_error. METHODS to_string RETURNING VALUE(result) TYPE string. METHODS test IMPORTING !version TYPE any RETURNING VALUE(result) TYPE abap_bool. METHODS intersects IMPORTING !comp TYPE any !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. DATA options TYPE /apmg/if_apm_semver_options=>ty_options. ENDCLASS. CLASS /apmg/cl_apm_semver_fixtures DEFINITION CREATE PUBLIC. ************************************************************************ * SemVer Fixtures * * Copyright (c) Isaac Z. Schlueter and Contributors * Ported to ABAP by apm.to Inc. * SPDX-License-Identifier: ISC ************************************************************************ PUBLIC SECTION. TYPES: BEGIN OF ty_comparator_intersection, c0 TYPE string, c1 TYPE string, res TYPE abap_bool, incpre TYPE abap_bool, END OF ty_comparator_intersection, ty_comparator_intersections TYPE STANDARD TABLE OF ty_comparator_intersection WITH KEY c0 c1 res incpre. CLASS-METHODS comparator_intersection RETURNING VALUE(result) TYPE ty_comparator_intersections. TYPES: BEGIN OF ty_comparison, v0 TYPE string, v1 TYPE string, loose TYPE abap_bool, END OF ty_comparison, ty_comparisons TYPE STANDARD TABLE OF ty_comparison WITH KEY v0 v1 loose. CLASS-METHODS comparisons RETURNING VALUE(result) TYPE ty_comparisons. TYPES: BEGIN OF ty_equality, v0 TYPE string, v1 TYPE string, loose TYPE abap_bool, END OF ty_equality, ty_equalitys TYPE STANDARD TABLE OF ty_equality WITH KEY v0 v1 loose. CLASS-METHODS equality RETURNING VALUE(result) TYPE ty_equalitys. TYPES: BEGIN OF ty_increment, version TYPE string, release TYPE string, res TYPE string, loose TYPE abap_bool, incpre TYPE abap_bool, identifier TYPE string, identifier_base TYPE string, END OF ty_increment, ty_increments TYPE STANDARD TABLE OF ty_increment WITH KEY version release res loose incpre identifier identifier_base. CLASS-METHODS increments RETURNING VALUE(result) TYPE ty_increments. TYPES: BEGIN OF ty_invalid_version, value TYPE string, reason TYPE string, loose TYPE abap_bool, END OF ty_invalid_version, ty_invalid_versions TYPE STANDARD TABLE OF ty_invalid_version WITH KEY value reason loose. CLASS-METHODS invalid_versions RETURNING VALUE(result) TYPE ty_invalid_versions. TYPES: BEGIN OF ty_range, range TYPE string, version TYPE string, loose TYPE abap_bool, incpre TYPE abap_bool, END OF ty_range, ty_ranges TYPE STANDARD TABLE OF ty_range WITH KEY range version loose incpre. CLASS-METHODS range_exclude RETURNING VALUE(result) TYPE ty_ranges. CLASS-METHODS range_include RETURNING VALUE(result) TYPE ty_ranges. TYPES: BEGIN OF ty_range_intersection, r0 TYPE string, r1 TYPE string, res TYPE abap_bool, END OF ty_range_intersection, ty_range_intersections TYPE STANDARD TABLE OF ty_range_intersection WITH KEY r0 r1 res. CLASS-METHODS range_intersection RETURNING VALUE(result) TYPE ty_range_intersections. TYPES: BEGIN OF ty_range_parse, range TYPE string, res TYPE string, loose TYPE abap_bool, incpre TYPE abap_bool, END OF ty_range_parse, ty_range_parses TYPE STANDARD TABLE OF ty_range_parse WITH KEY range res loose incpre. CLASS-METHODS range_parse RETURNING VALUE(result) TYPE ty_range_parses. TYPES: BEGIN OF ty_version_range, range TYPE string, version TYPE string, loose TYPE abap_bool, incpre TYPE abap_bool, END OF ty_version_range, ty_version_ranges TYPE STANDARD TABLE OF ty_version_range WITH KEY range version loose incpre. CLASS-METHODS version_gt_range RETURNING VALUE(result) TYPE ty_version_ranges. CLASS-METHODS version_lt_range RETURNING VALUE(result) TYPE ty_version_ranges. CLASS-METHODS version_not_gt_range RETURNING VALUE(result) TYPE ty_version_ranges. CLASS-METHODS version_not_lt_range RETURNING VALUE(result) TYPE ty_version_ranges. TYPES: BEGIN OF ty_valid_version, version TYPE string, major TYPE i, minor TYPE i, patch TYPE i, prerelease TYPE string_table, build TYPE string_table, END OF ty_valid_version, ty_valid_versions TYPE STANDARD TABLE OF ty_valid_version WITH KEY version. CLASS-METHODS valid_versions RETURNING VALUE(result) TYPE ty_valid_versions. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_semver_functions DEFINITION CREATE PUBLIC. ************************************************************************ * SemVer Functions * * Copyright (c) Isaac Z. Schlueter and Contributors * Ported to ABAP by apm.to Inc. * SPDX-License-Identifier: ISC ************************************************************************ PUBLIC SECTION. CLASS-METHODS clean IMPORTING version TYPE string loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS cmp IMPORTING a TYPE any op TYPE string b TYPE any loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. CLASS-METHODS coerce IMPORTING version TYPE string rtl TYPE abap_bool DEFAULT abap_false loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_semver RAISING /apmg/cx_apm_error. CLASS-METHODS compare IMPORTING a TYPE any b TYPE any loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE i RAISING /apmg/cx_apm_error. CLASS-METHODS compare_build IMPORTING a TYPE any b TYPE any loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE i RAISING /apmg/cx_apm_error. CLASS-METHODS compare_loose IMPORTING a TYPE any b TYPE any incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE i RAISING /apmg/cx_apm_error. CLASS-METHODS diff IMPORTING version_1 TYPE any version_2 TYPE any RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS eq IMPORTING a TYPE any b TYPE any loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. CLASS-METHODS gt IMPORTING a TYPE any b TYPE any loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. CLASS-METHODS gte IMPORTING a TYPE any b TYPE any loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. CLASS-METHODS inc IMPORTING version TYPE any release_type TYPE string identifier TYPE string OPTIONAL identifier_base TYPE string OPTIONAL loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_semver. CLASS-METHODS lt IMPORTING a TYPE any b TYPE any loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. CLASS-METHODS lte IMPORTING a TYPE any b TYPE any loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. CLASS-METHODS major IMPORTING version TYPE any loose TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE i RAISING /apmg/cx_apm_error. CLASS-METHODS minor IMPORTING version TYPE any loose TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE i RAISING /apmg/cx_apm_error. CLASS-METHODS neq IMPORTING a TYPE any b TYPE any loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. CLASS-METHODS parse IMPORTING version TYPE any loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false throw_errors TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_semver RAISING /apmg/cx_apm_error. CLASS-METHODS patch IMPORTING version TYPE any loose TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE i RAISING /apmg/cx_apm_error. CLASS-METHODS prerelease IMPORTING version TYPE any loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE string_table RAISING /apmg/cx_apm_error. CLASS-METHODS rcompare IMPORTING a TYPE any b TYPE any loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE i RAISING /apmg/cx_apm_error. CLASS-METHODS rsort IMPORTING list TYPE string_table loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE string_table RAISING /apmg/cx_apm_error. CLASS-METHODS sort IMPORTING list TYPE string_table loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE string_table RAISING /apmg/cx_apm_error. CLASS-METHODS satisfies IMPORTING version TYPE any range TYPE any loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. CLASS-METHODS valid IMPORTING version TYPE any loose TYPE abap_bool DEFAULT abap_false incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE string. PROTECTED SECTION. PRIVATE SECTION. CLASS-METHODS equality IMPORTING a TYPE any b TYPE any RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_semver_identifier DEFINITION CREATE PUBLIC. ************************************************************************ * SemVer Identifiers * * Copyright (c) Isaac Z. Schlueter and Contributors * Ported to ABAP by apm.to Inc. * SPDX-License-Identifier: ISC ************************************************************************ PUBLIC SECTION. CLASS-METHODS compare_identifiers IMPORTING !a TYPE any !b TYPE any RETURNING VALUE(result) TYPE i. CLASS-METHODS rcompare_identifiers IMPORTING !a TYPE any !b TYPE any RETURNING VALUE(result) TYPE i. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_semver_integratio DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * SemVer Integration Tests * * Copyright (c) Isaac Z. Schlueter and Contributors * Ported to ABAP by apm.to Inc. * SPDX-License-Identifier: ISC ************************************************************************ * Note: The class has no methods but test classes ************************************************************************ PUBLIC SECTION. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_semver_range DEFINITION CREATE PUBLIC. ************************************************************************ * SemVer Range * * Copyright (c) Isaac Z. Schlueter and Contributors * Ported to ABAP by apm.to Inc. * SPDX-License-Identifier: ISC ************************************************************************ PUBLIC SECTION. TYPES: ty_comparators TYPE STANDARD TABLE OF REF TO /apmg/cl_apm_semver_comparator WITH KEY table_line, ty_set TYPE STANDARD TABLE OF ty_comparators WITH EMPTY KEY. DATA set TYPE ty_set READ-ONLY. METHODS constructor IMPORTING !range TYPE string !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RAISING /apmg/cx_apm_error. CLASS-METHODS create IMPORTING !range TYPE any !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_semver_range RAISING /apmg/cx_apm_error. METHODS range RETURNING VALUE(result) TYPE string. METHODS format RETURNING VALUE(result) TYPE string. METHODS to_string RETURNING VALUE(result) TYPE string. METHODS parse_range IMPORTING !range_string TYPE string RETURNING VALUE(result) TYPE ty_comparators RAISING /apmg/cx_apm_error. METHODS test IMPORTING !version TYPE any RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. METHODS intersects IMPORTING !range TYPE REF TO /apmg/cl_apm_semver_range !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS no_replace TYPE string VALUE '!replace'. TYPES: BEGIN OF ty_cache_entry, key TYPE string, value TYPE ty_comparators, END OF ty_cache_entry, ty_cache TYPE HASHED TABLE OF ty_cache_entry WITH UNIQUE KEY key. CLASS-DATA cache TYPE ty_cache. DATA: raw TYPE string, formatted TYPE string, options TYPE /apmg/if_apm_semver_options=>ty_options. CLASS-METHODS is_any IMPORTING !comp TYPE REF TO /apmg/cl_apm_semver_comparator RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_null_set IMPORTING !comp TYPE REF TO /apmg/cl_apm_semver_comparator RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_x IMPORTING !id TYPE string RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS is_satisfiable IMPORTING !comparators TYPE ty_comparators !loose TYPE abap_bool !incpre TYPE abap_bool RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. CLASS-METHODS parse_comparator IMPORTING !comp TYPE string !loose TYPE abap_bool !incpre TYPE abap_bool RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS replace_tildes IMPORTING !comp TYPE string !loose TYPE abap_bool !incpre TYPE abap_bool RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS replace_tilde IMPORTING !comp TYPE string !loose TYPE abap_bool !incpre TYPE abap_bool RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error ##NEEDED. CLASS-METHODS replace_carets IMPORTING !comp TYPE string !loose TYPE abap_bool !incpre TYPE abap_bool RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS replace_caret IMPORTING !comp TYPE string !loose TYPE abap_bool !incpre TYPE abap_bool RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS replace_xranges IMPORTING !comp TYPE string !loose TYPE abap_bool !incpre TYPE abap_bool RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS replace_xrange IMPORTING !comp TYPE string !loose TYPE abap_bool !incpre TYPE abap_bool RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS replace_stars IMPORTING !comp TYPE string !loose TYPE abap_bool !incpre TYPE abap_bool RETURNING VALUE(result) TYPE string ##NEEDED. CLASS-METHODS replace_gte0 IMPORTING !comp TYPE string !loose TYPE abap_bool !incpre TYPE abap_bool RETURNING VALUE(result) TYPE string ##NEEDED. CLASS-METHODS replace_hyphen IMPORTING !range TYPE string !loose TYPE abap_bool !incpre TYPE abap_bool RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS test_set IMPORTING !comparators TYPE ty_comparators !version TYPE any !loose TYPE abap_bool !incpre TYPE abap_bool RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS str IMPORTING !value TYPE i RETURNING VALUE(result) TYPE string. ENDCLASS. CLASS /apmg/cl_apm_semver_ranges DEFINITION CREATE PUBLIC. ************************************************************************ * SemVer Ranges * * Copyright (c) Isaac Z. Schlueter and Contributors * Ported to ABAP by apm.to Inc. * SPDX-License-Identifier: ISC ************************************************************************ PUBLIC SECTION. TYPES: ty_hilo TYPE c LENGTH 1, ty_comp_list TYPE string_table, ty_comp_lists TYPE STANDARD TABLE OF string_table WITH EMPTY KEY. CLASS-METHODS gtr IMPORTING !version TYPE any !range TYPE any !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. CLASS-METHODS intersects IMPORTING !r1 TYPE any !r2 TYPE any !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. CLASS-METHODS ltr IMPORTING !version TYPE any !range TYPE any !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. CLASS-METHODS max_satisfying IMPORTING !versions TYPE string_table !range TYPE any !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS min_satisfying IMPORTING !versions TYPE string_table !range TYPE any !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS min_version IMPORTING !range TYPE any !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_semver RAISING /apmg/cx_apm_error. CLASS-METHODS outside IMPORTING !version TYPE any !range TYPE any !hilo TYPE ty_hilo OPTIONAL !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error. CLASS-METHODS simplify IMPORTING !versions TYPE string_table !range TYPE any !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS subset IMPORTING !sub TYPE any !dom TYPE any !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE abap_bool RAISING /apmg/cx_apm_error ##NEEDED. CLASS-METHODS to_comparators IMPORTING !range TYPE any !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE ty_comp_lists RAISING /apmg/cx_apm_error. CLASS-METHODS valid_range IMPORTING !range TYPE any !loose TYPE abap_bool DEFAULT abap_false !incpre TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_semver_re DEFINITION CREATE PUBLIC. ************************************************************************ * SemVer Regex * * Copyright (c) Isaac Z. Schlueter and Contributors * Ported to ABAP by apm.to Inc. * SPDX-License-Identifier: ISC ************************************************************************ * TODO: Migrate POSIX to PCRE * https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenregex_posix_pcre_incompat.htm ************************************************************************ PUBLIC SECTION. TYPES: BEGIN OF ty_token, src TYPE string, regex TYPE REF TO cl_abap_regex, safe_src TYPE string, safe_regex TYPE REF TO cl_abap_regex, occ TYPE i, " 0 = global, 1 = once END OF ty_token. CONSTANTS: letter_dash_number TYPE string VALUE '[a-zA-Z0-9-]', caret_trim_replace TYPE string VALUE '$1^', tilde_trim_replace TYPE string VALUE '$1~', comparator_trim_replace TYPE string VALUE '$1$2$3', version_trim_replace TYPE string VALUE '$1$2'. " added for POSIX CLASS-DATA: BEGIN OF token, v TYPE ty_token, " added for POSIX vtrim TYPE ty_token, " added for POSIX build TYPE ty_token, buildidentifier TYPE ty_token, caret TYPE ty_token, caretloose TYPE ty_token, carettrim TYPE ty_token, coerce TYPE ty_token, coercefull TYPE ty_token, coerceplain TYPE ty_token, coercertl TYPE ty_token, coercertlfull TYPE ty_token, comparator TYPE ty_token, comparatorloose TYPE ty_token, comparatortrim TYPE ty_token, full TYPE ty_token, fullplain TYPE ty_token, gte0 TYPE ty_token, gte0pre TYPE ty_token, gtlt TYPE ty_token, hyphenrange TYPE ty_token, hyphenrangeloose TYPE ty_token, lonecaret TYPE ty_token, lonetilde TYPE ty_token, loose TYPE ty_token, looseplain TYPE ty_token, mainversion TYPE ty_token, mainversionloose TYPE ty_token, nonnumericidentifier TYPE ty_token, numericidentifier TYPE ty_token, numericidentifierloose TYPE ty_token, prerelease TYPE ty_token, prereleaseidentifier TYPE ty_token, prereleaseidentifierloose TYPE ty_token, prereleaseloose TYPE ty_token, star TYPE ty_token, tilde TYPE ty_token, tildeloose TYPE ty_token, tildetrim TYPE ty_token, xrange TYPE ty_token, xrangeidentifier TYPE ty_token, xrangeidentifierloose TYPE ty_token, xrangeloose TYPE ty_token, xrangeplain TYPE ty_token, xrangeplainloose TYPE ty_token, END OF token. CLASS-METHODS class_constructor. PROTECTED SECTION. PRIVATE SECTION. CLASS-METHODS create_token IMPORTING name TYPE string value TYPE string global TYPE abap_bool DEFAULT abap_false. CLASS-METHODS make_safe_regex IMPORTING value TYPE string RETURNING VALUE(result) TYPE string. ENDCLASS. CLASS /apmg/cl_apm_semver_sap DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * Semantic Version for SAP Release Mapping * * Copyright (c) apm.to * SPDX-License-Identifier: MIT ************************************************************************ * Note: Since SAP does not use semantic versioning, this class * implements a bi-directional mapping covering as many cases as possible ************************************************************************ PUBLIC SECTION. CONSTANTS c_version TYPE string VALUE '1.0.0' ##NEEDED. METHODS sap_release_to_semver IMPORTING !release TYPE cvers-release !support_pack TYPE cvers-extrelease OPTIONAL RETURNING VALUE(result) TYPE string RAISING cx_abap_invalid_value. METHODS sap_component_to_semver IMPORTING !component TYPE cvers-component RETURNING VALUE(result) TYPE string RAISING cx_abap_invalid_value. METHODS semver_to_sap_release IMPORTING !version TYPE string RETURNING VALUE(result) TYPE cvers-release RAISING cx_abap_invalid_value. METHODS semver_to_sap_release_sp IMPORTING !version TYPE string EXPORTING !release TYPE cvers-release !support_pack TYPE cvers-extrelease RAISING cx_abap_invalid_value. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_semver_utils DEFINITION CREATE PUBLIC. ************************************************************************ * SemVer Utilities * * Copyright (c) Isaac Z. Schlueter and Contributors * Ported to ABAP by apm.to Inc. * SPDX-License-Identifier: ISC ************************************************************************ PUBLIC SECTION. CLASS-METHODS is_numeric IMPORTING !data TYPE any RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS trim IMPORTING !data TYPE clike RETURNING VALUE(result) TYPE string. CLASS-METHODS version_trim IMPORTING !data TYPE clike RETURNING VALUE(result) TYPE string. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_settings DEFINITION FINAL CREATE PRIVATE. ************************************************************************ * apm Settings * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. INTERFACES /apmg/if_apm_settings. CLASS-METHODS class_constructor. CLASS-METHODS factory IMPORTING !name TYPE /apmg/if_apm_settings=>ty_name DEFAULT sy-uname RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_settings. CLASS-METHODS injector IMPORTING !name TYPE /apmg/if_apm_settings=>ty_name !mock TYPE REF TO /apmg/if_apm_settings. METHODS constructor IMPORTING !name TYPE /apmg/if_apm_settings=>ty_name. CLASS-METHODS initialize_global_settings RAISING /apmg/cx_apm_error. CLASS-METHODS get_setting_key IMPORTING !name TYPE /apmg/if_apm_settings=>ty_name RETURNING VALUE(result) TYPE /apmg/if_apm_persist_apm=>ty_key. CLASS-METHODS get_default RETURNING VALUE(result) TYPE /apmg/if_apm_settings=>ty_settings. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_instance, name TYPE /apmg/if_apm_settings=>ty_name, instance TYPE REF TO /apmg/if_apm_settings, END OF ty_instance, ty_instances TYPE HASHED TABLE OF ty_instance WITH UNIQUE KEY name. CLASS-DATA: db_persist TYPE REF TO /apmg/if_apm_persist_apm, instances TYPE ty_instances. DATA: key TYPE /apmg/if_apm_persist_apm=>ty_key, name TYPE /apmg/if_apm_settings=>ty_name, settings TYPE /apmg/if_apm_settings=>ty_settings. CLASS-METHODS check_settings IMPORTING !is_settings TYPE /apmg/if_apm_settings=>ty_settings RETURNING VALUE(result) TYPE string_table. CLASS-METHODS merge_settings CHANGING !cs_settings TYPE /apmg/if_apm_settings=>ty_settings. ENDCLASS. CLASS /apmg/cl_apm_string_map DEFINITION FINAL CREATE PUBLIC . PUBLIC SECTION. CONSTANTS version TYPE string VALUE 'v1.0.5'. CONSTANTS origin TYPE string VALUE 'https://github.com/sbcgua/abap-string-map'. CONSTANTS license TYPE string VALUE 'MIT'. TYPES: BEGIN OF ty_entry, k TYPE string, v TYPE string, END OF ty_entry. TYPES: tty_entries TYPE STANDARD TABLE OF ty_entry WITH KEY k. TYPES: tts_entries TYPE SORTED TABLE OF ty_entry WITH NON-UNIQUE KEY k. DATA mt_entries TYPE tts_entries READ-ONLY. CLASS-METHODS create IMPORTING !iv_case_insensitive TYPE abap_bool DEFAULT abap_false !iv_list_mode TYPE abap_bool DEFAULT abap_false " removes uniqueness requirement, " use with care: it is not the primary scenario !iv_from TYPE any OPTIONAL PREFERRED PARAMETER iv_from RETURNING VALUE(ro_instance) TYPE REF TO /apmg/cl_apm_string_map. METHODS constructor IMPORTING !iv_case_insensitive TYPE abap_bool DEFAULT abap_false !iv_list_mode TYPE abap_bool DEFAULT abap_false " removes uniqueness requirement, " use with care: it is not the primary scenario !iv_from TYPE any OPTIONAL. METHODS get IMPORTING !iv_key TYPE clike RETURNING VALUE(rv_val) TYPE string. METHODS has IMPORTING !iv_key TYPE clike RETURNING VALUE(rv_has) TYPE abap_bool. METHODS set IMPORTING !iv_key TYPE clike !iv_val TYPE clike RETURNING VALUE(ro_map) TYPE REF TO /apmg/cl_apm_string_map. METHODS setx IMPORTING !iv_str TYPE csequence RETURNING VALUE(ro_map) TYPE REF TO /apmg/cl_apm_string_map. METHODS size RETURNING VALUE(rv_size) TYPE i. METHODS is_empty RETURNING VALUE(rv_yes) TYPE abap_bool. METHODS delete IMPORTING !iv_key TYPE clike. METHODS keys RETURNING VALUE(rt_keys) TYPE string_table. METHODS values RETURNING VALUE(rt_values) TYPE string_table. METHODS clear. METHODS from_struc IMPORTING !is_container TYPE any RETURNING VALUE(ro_instance) TYPE REF TO /apmg/cl_apm_string_map. METHODS from_entries IMPORTING !it_entries TYPE ANY TABLE RETURNING VALUE(ro_instance) TYPE REF TO /apmg/cl_apm_string_map. METHODS from_string IMPORTING !iv_string_params TYPE csequence RETURNING VALUE(ro_instance) TYPE REF TO /apmg/cl_apm_string_map. METHODS from_map IMPORTING !io_string_map TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(ro_instance) TYPE REF TO /apmg/cl_apm_string_map. METHODS merge IMPORTING !io_string_map TYPE REF TO /apmg/cl_apm_string_map RETURNING VALUE(ro_instance) TYPE REF TO /apmg/cl_apm_string_map. METHODS to_struc CHANGING !cs_container TYPE any. METHODS to_string RETURNING VALUE(rv_string) TYPE string. METHODS to_entries CHANGING !ct_entries TYPE STANDARD TABLE. METHODS strict IMPORTING !iv_strict TYPE abap_bool DEFAULT abap_true RETURNING VALUE(ro_instance) TYPE REF TO /apmg/cl_apm_string_map. METHODS freeze. PROTECTED SECTION. PRIVATE SECTION. DATA mv_is_strict TYPE abap_bool. DATA mv_read_only TYPE abap_bool. DATA mv_case_insensitive TYPE abap_bool. DATA mv_list_mode TYPE abap_bool. ENDCLASS. CLASS /apmg/cl_apm_tar DEFINITION CREATE PRIVATE. ************************************************************************ * Tar * * Tar UStar and Pax Formats * * Based on * https://en.wikipedia.org/wiki/Tar_(computing) * https://en.wikipedia.org/wiki/Gzip * https://en.wikipedia.org/wiki/Pax_(command) * https://pubs.opengroup.org/onlinepubs/009695399/utilities/pax.html * * Note: Supports reading 7-zip tar files with long links but does not * support writing such files. * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * Limitation: Block size is hardcoded to 512 bytes ************************************************************************ * Performance note: Do not use && to concatenate xstring since it * converts to string implicitly. Use CONCATENATE ... IN BYTE MODE. ************************************************************************ PUBLIC SECTION. CONSTANTS c_version TYPE string VALUE '2.0.1' ##NEEDED. CONSTANTS c_blocksize TYPE i VALUE 512. TYPES: ty_typeflag TYPE c LENGTH 1, BEGIN OF ty_keyword, keyword TYPE string, value TYPE string, END OF ty_keyword, ty_keywords TYPE STANDARD TABLE OF ty_keyword WITH KEY keyword, BEGIN OF ty_file, name TYPE string, date TYPE d, time TYPE t, mode TYPE i, unixtime TYPE i, size TYPE i, typeflag TYPE ty_typeflag, content TYPE xstring, keywords TYPE ty_keywords, END OF ty_file, ty_tar_files TYPE STANDARD TABLE OF ty_file WITH KEY name. TYPES: "! Ustar header record (512 bytes) BEGIN OF ty_header, name TYPE c LENGTH 100, " Offset 0 mode TYPE c LENGTH 8, " 100 uid TYPE c LENGTH 8, " 108 gid TYPE c LENGTH 8, " 116 size TYPE c LENGTH 12, " 124 mtime TYPE c LENGTH 12, " 136 chksum TYPE c LENGTH 8, " 148 typeflag TYPE c LENGTH 1, " 156 linkname TYPE c LENGTH 100, " 157 magic TYPE c LENGTH 6, " 257 version TYPE c LENGTH 2, " 263 uname TYPE c LENGTH 32, " 265 gname TYPE c LENGTH 32, " 297 devmajor TYPE c LENGTH 8, " 329 devminor TYPE c LENGTH 8, " 337 prefix TYPE c LENGTH 155, " 345 padding TYPE c LENGTH 12, " 500 END OF ty_header. CONSTANTS: BEGIN OF c_typeflag, file TYPE ty_typeflag VALUE '0', hard_link TYPE ty_typeflag VALUE '1', symbolic_link TYPE ty_typeflag VALUE '2', character_special TYPE ty_typeflag VALUE '3', block_special TYPE ty_typeflag VALUE '4', directory TYPE ty_typeflag VALUE '5', fifo TYPE ty_typeflag VALUE '6', contiguous_file TYPE ty_typeflag VALUE '7', long_link TYPE ty_typeflag VALUE 'L', " 7-zip global_header TYPE ty_typeflag VALUE 'g', " pax extended_header TYPE ty_typeflag VALUE 'x', " pax END OF c_typeflag. CLASS-METHODS class_constructor. "! Create archive CLASS-METHODS new IMPORTING !force_ustar TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_tar. METHODS constructor IMPORTING !force_ustar TYPE abap_bool. "! Load archive METHODS load IMPORTING !tar TYPE xstring RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_tar RAISING /apmg/cx_apm_error. "! Create archive METHODS save RETURNING VALUE(result) TYPE xstring RAISING /apmg/cx_apm_error. "! Read file from archive METHODS get IMPORTING !name TYPE csequence RETURNING VALUE(result) TYPE xstring RAISING /apmg/cx_apm_error. "! List the table of contents of an archive (no data) METHODS list RETURNING VALUE(result) TYPE ty_tar_files RAISING /apmg/cx_apm_error. "! Number of files in archive METHODS file_count RETURNING VALUE(result) TYPE i RAISING /apmg/cx_apm_error. "! Total size of unpackage files in bytes METHODS unpacked_size RETURNING VALUE(result) TYPE i RAISING /apmg/cx_apm_error. "! Append file to archive METHODS append IMPORTING !name TYPE csequence !content TYPE xsequence !date TYPE d OPTIONAL !time TYPE t OPTIONAL !mode TYPE i OPTIONAL !typeflag TYPE c OPTIONAL !keywords TYPE ty_keywords OPTIONAL " pax RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_tar RAISING /apmg/cx_apm_error. "! Delete file from archive METHODS delete IMPORTING !name TYPE csequence RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_tar RAISING /apmg/cx_apm_error. "! Gzip archive METHODS gzip IMPORTING !tar TYPE xstring RETURNING VALUE(result) TYPE xstring RAISING /apmg/cx_apm_error. "! Gunzip archive METHODS gunzip IMPORTING !gzip TYPE xstring RETURNING VALUE(result) TYPE xstring RAISING /apmg/cx_apm_error. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: c_ustar_magic TYPE c LENGTH 5 VALUE 'ustar', c_ustar_version TYPE c LENGTH 2 VALUE '00', c_mode_default TYPE i VALUE 436, " octal 664 rw-rw-r-- c_path_sep TYPE c VALUE '/', " unix c_epoch TYPE timestamp VALUE '19700101000000'. TYPES: BEGIN OF ty_tar_item, name TYPE string, content TYPE xstring, END OF ty_tar_item, ty_tar_data TYPE HASHED TABLE OF ty_tar_item WITH UNIQUE KEY name. TYPES ty_block TYPE x LENGTH c_blocksize. CLASS-DATA null TYPE c LENGTH 256. DATA: force_ustar TYPE abap_bool, tar_files TYPE ty_tar_files, tar_data TYPE ty_tar_data. CLASS-METHODS _append_nulls CHANGING !data TYPE simple. CLASS-METHODS _remove_nulls CHANGING !data TYPE simple. CLASS-METHODS _pad IMPORTING !number TYPE numeric !length TYPE i RETURNING VALUE(result) TYPE string. CLASS-METHODS _unpad IMPORTING !data TYPE csequence RETURNING VALUE(result) TYPE i. CLASS-METHODS _from_octal IMPORTING !octal TYPE string RETURNING VALUE(result) TYPE i. CLASS-METHODS _to_octal IMPORTING !number TYPE numeric RETURNING VALUE(result) TYPE string. CLASS-METHODS _from_xstring IMPORTING !data TYPE xstring RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS _to_xstring IMPORTING !data TYPE simple RETURNING VALUE(result) TYPE xstring RAISING /apmg/cx_apm_error. CLASS-METHODS _from_filename IMPORTING !filename TYPE string EXPORTING !prefix TYPE ty_header-prefix !name TYPE ty_header-name RAISING /apmg/cx_apm_error. CLASS-METHODS _to_filename IMPORTING !prefix TYPE ty_header-prefix !name TYPE ty_header-name RETURNING VALUE(result) TYPE string. CLASS-METHODS _from_unixtime IMPORTING !unixtime TYPE i EXPORTING !date TYPE d !time TYPE t RAISING /apmg/cx_apm_error. CLASS-METHODS _to_unixtime IMPORTING !date TYPE d !time TYPE t RETURNING VALUE(result) TYPE i RAISING /apmg/cx_apm_error. CLASS-METHODS _checksum IMPORTING !data TYPE any RETURNING VALUE(result) TYPE i RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_trace DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * apm Trace * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ PUBLIC SECTION. CLASS-METHODS cdata IMPORTING !cdata TYPE csequence. CLASS-METHODS xdata IMPORTING !xdata TYPE xsequence. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS /apmg/cl_apm_url DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * URL Object * * Implementation of WHATWG-URL standard * https://url.spec.whatwg.org/ * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * TODO: Add support for International Domain Names for Application * (punycode) ************************************************************************ PUBLIC SECTION. CONSTANTS c_version TYPE string VALUE '1.0.0' ##NEEDED. TYPES: "! scheme://username:password@host:port/path?query#fragment BEGIN OF ty_url_components, scheme TYPE string, username TYPE string, password TYPE string, host TYPE string, port TYPE string, path TYPE string, query TYPE string, fragment TYPE string, is_special TYPE abap_bool, END OF ty_url_components. DATA components TYPE ty_url_components READ-ONLY. CLASS-METHODS parse IMPORTING url TYPE string RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_url RAISING /apmg/cx_apm_error. CLASS-METHODS default_port IMPORTING scheme TYPE string RETURNING VALUE(result) TYPE string. CLASS-METHODS serialize IMPORTING components TYPE ty_url_components RETURNING VALUE(result) TYPE string RAISING /apmg/cx_apm_error. METHODS constructor IMPORTING components TYPE ty_url_components. PROTECTED SECTION. PRIVATE SECTION. CLASS-METHODS is_special_scheme IMPORTING scheme TYPE string RETURNING VALUE(result) TYPE abap_bool. CLASS-METHODS validate_scheme IMPORTING scheme TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS parse_authority IMPORTING authority TYPE string scheme TYPE string EXPORTING username TYPE string password TYPE string host TYPE string port TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS normalize_path IMPORTING path TYPE string RETURNING VALUE(result) TYPE string. CLASS-METHODS percent_encode IMPORTING raw TYPE csequence RETURNING VALUE(result) TYPE string. CLASS-METHODS percent_decode IMPORTING raw TYPE csequence RETURNING VALUE(result) TYPE string. CLASS-METHODS validate_ipv6_address IMPORTING address TYPE string RAISING /apmg/cx_apm_error. CLASS-METHODS validate_ipv4_address IMPORTING address TYPE string RAISING /apmg/cx_apm_error. ENDCLASS. CLASS /apmg/cl_apm_url_params DEFINITION FINAL CREATE PUBLIC. ************************************************************************ * URL Query Parameters * * Implementation of WHATWG-URL standard * https://url.spec.whatwg.org/#interface-urlsearchparams * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * TODO: Tests ************************************************************************ PUBLIC SECTION. TYPES: BEGIN OF ty_param, key TYPE string, value TYPE string, END OF ty_param, ty_params TYPE STANDARD TABLE OF ty_param WITH KEY key. DATA params TYPE ty_params READ-ONLY. CLASS-METHODS parse IMPORTING !query TYPE string RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_url_params. CLASS-METHODS create IMPORTING !params TYPE ty_params RETURNING VALUE(result) TYPE REF TO /apmg/cl_apm_url_params. METHODS constructor IMPORTING !params TYPE ty_params. METHODS append IMPORTING !key TYPE string !value TYPE string. METHODS delete IMPORTING !key TYPE string !value TYPE string OPTIONAL. METHODS get IMPORTING !key TYPE string RETURNING VALUE(result) TYPE string. METHODS get_all IMPORTING !key TYPE string RETURNING VALUE(result) TYPE ty_params. METHODS has IMPORTING !key TYPE string !value TYPE string OPTIONAL RETURNING VALUE(result) TYPE abap_bool. METHODS set IMPORTING !key TYPE string !value TYPE string. METHODS sort. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_abap_language_vers DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. CONSTANTS: c_any_abap_language_version TYPE zif_abapgit_aff_types_v1=>ty_abap_language_version VALUE '*', c_no_abap_language_version TYPE zif_abapgit_aff_types_v1=>ty_abap_language_version VALUE '-', c_feature_flag TYPE string VALUE 'ALAV'. METHODS constructor IMPORTING !io_dot_abapgit TYPE REF TO zcl_abapgit_dot_abapgit. METHODS get_abap_language_vers_by_objt IMPORTING !iv_object_type TYPE trobjtype !iv_package TYPE devclass RETURNING VALUE(rv_allowed_abap_langu_version) TYPE zif_abapgit_aff_types_v1=>ty_abap_language_version. METHODS get_repo_abap_language_version RETURNING VALUE(rv_abap_language_version) TYPE zif_abapgit_aff_types_v1=>ty_abap_language_version. METHODS is_import_allowed IMPORTING !iv_package TYPE devclass RETURNING VALUE(rv_allowed) TYPE abap_bool. CLASS-METHODS check_abap_language_version IMPORTING !iv_abap_language_version TYPE zif_abapgit_aff_types_v1=>ty_abap_language_version !is_item TYPE zif_abapgit_definitions=>ty_item RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. DATA mo_dot_abapgit TYPE REF TO zcl_abapgit_dot_abapgit. " Depends on experimental feature flag and repo setting DATA mv_has_abap_language_vers TYPE abap_bool. METHODS get_default_abap_language_vers IMPORTING !iv_object_type TYPE trobjtype RETURNING VALUE(rv_abap_language_version) TYPE zif_abapgit_aff_types_v1=>ty_abap_language_version. METHODS get_abap_language_vers_by_devc IMPORTING !iv_package TYPE devclass RETURNING VALUE(rv_abap_language_version) TYPE string. METHODS get_abap_language_vers_by_repo RETURNING VALUE(rv_abap_language_version) TYPE string. CLASS-METHODS get_description IMPORTING !iv_abap_language_version TYPE zif_abapgit_aff_types_v1=>ty_abap_language_version RETURNING VALUE(rv_description) TYPE string. ENDCLASS. CLASS zcl_abapgit_adt_link DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. CLASS-METHODS jump IMPORTING !iv_obj_name TYPE zif_abapgit_definitions=>ty_item-obj_name !iv_obj_type TYPE zif_abapgit_definitions=>ty_item-obj_type !iv_sub_obj_name TYPE zif_abapgit_definitions=>ty_item-obj_name OPTIONAL !iv_line_number TYPE i OPTIONAL RAISING zcx_abapgit_exception. CLASS-METHODS link_transport IMPORTING iv_transport TYPE trkorr RETURNING VALUE(rv_link) TYPE string. PROTECTED SECTION. CLASS-METHODS generate IMPORTING !iv_obj_name TYPE zif_abapgit_definitions=>ty_item-obj_name !iv_obj_type TYPE zif_abapgit_definitions=>ty_item-obj_type !iv_sub_obj_name TYPE zif_abapgit_definitions=>ty_item-obj_name OPTIONAL !iv_line_number TYPE i OPTIONAL RETURNING VALUE(rv_result) TYPE string RAISING zcx_abapgit_exception. PRIVATE SECTION. CLASS-METHODS get_adt_objects_and_names IMPORTING iv_obj_name TYPE zif_abapgit_definitions=>ty_item-obj_name iv_obj_type TYPE zif_abapgit_definitions=>ty_item-obj_type EXPORTING eo_adt_uri_mapper TYPE REF TO object eo_adt_objectref TYPE REF TO object ev_program TYPE progname ev_include TYPE progname RAISING zcx_abapgit_exception. CLASS-METHODS is_adt_jump_possible IMPORTING io_object TYPE REF TO cl_wb_object io_adt TYPE REF TO object RETURNING VALUE(rv_is_adt_jump_possible) TYPE abap_bool RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_aff_registry DEFINITION FINAL CREATE PRIVATE FRIENDS zcl_abapgit_aff_factory . PUBLIC SECTION. INTERFACES: zif_abapgit_aff_registry. CONSTANTS c_aff_feature TYPE string VALUE 'AFF'. METHODS constructor. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_registry_entry, obj_type TYPE tadir-object, experimental TYPE abap_bool, END OF ty_registry_entry. CLASS-DATA: gt_registry TYPE HASHED TABLE OF ty_registry_entry WITH UNIQUE KEY obj_type. DATA mv_aff_enabled TYPE abap_bool. CLASS-METHODS initialize_registry_table. CLASS-METHODS: register IMPORTING iv_obj_type TYPE tadir-object iv_experimental TYPE abap_bool DEFAULT abap_false. ENDCLASS. CLASS zcl_abapgit_aff_factory DEFINITION FRIENDS zcl_abapgit_aff_injector. PUBLIC SECTION. CLASS-METHODS get_registry RETURNING VALUE(ri_registry) TYPE REF TO zif_abapgit_aff_registry. PRIVATE SECTION. CLASS-DATA gi_registry TYPE REF TO zif_abapgit_aff_registry. ENDCLASS. CLASS zcl_abapgit_aff_injector DEFINITION FINAL CREATE PRIVATE . PUBLIC SECTION. CLASS-METHODS set_registry IMPORTING ii_registry TYPE REF TO zif_abapgit_aff_registry. ENDCLASS. CLASS zcl_abapgit_convert DEFINITION CREATE PUBLIC . PUBLIC SECTION. CLASS-METHODS string_to_xstring_utf8 IMPORTING !iv_string TYPE string RETURNING VALUE(rv_xstring) TYPE xstring RAISING zcx_abapgit_exception . CLASS-METHODS xstring_to_string_utf8 IMPORTING !iv_data TYPE xsequence !iv_length TYPE i OPTIONAL RETURNING VALUE(rv_string) TYPE string RAISING zcx_abapgit_exception . CLASS-METHODS xstring_to_string_utf8_raw IMPORTING !iv_data TYPE xsequence !iv_length TYPE i OPTIONAL RETURNING VALUE(rv_string) TYPE string RAISING zcx_abapgit_exception . CLASS-METHODS string_to_xstring_utf8_bom IMPORTING !iv_string TYPE string RETURNING VALUE(rv_xstring) TYPE xstring RAISING zcx_abapgit_exception . CLASS-METHODS xstring_to_string_utf8_bom IMPORTING !iv_xstring TYPE xstring RETURNING VALUE(rv_string) TYPE string RAISING zcx_abapgit_exception . CLASS-METHODS xstring_to_int IMPORTING !iv_xstring TYPE xstring RETURNING VALUE(rv_i) TYPE i RAISING zcx_abapgit_exception . CLASS-METHODS int_to_xstring4 IMPORTING !iv_i TYPE i RETURNING VALUE(rv_xstring) TYPE xstring . CLASS-METHODS split_string IMPORTING !iv_string TYPE string RETURNING VALUE(rt_lines) TYPE string_table . CLASS-METHODS conversion_exit_isola_output IMPORTING !iv_spras TYPE spras RETURNING VALUE(rv_spras) TYPE laiso . CLASS-METHODS string_to_xstring IMPORTING !iv_str TYPE string RETURNING VALUE(rv_xstr) TYPE xstring RAISING zcx_abapgit_exception . CLASS-METHODS string_to_tab IMPORTING !iv_str TYPE string EXPORTING !ev_size TYPE i !et_tab TYPE STANDARD TABLE . CLASS-METHODS base64_to_xstring IMPORTING !iv_base64 TYPE string RETURNING VALUE(rv_xstr) TYPE xstring . CLASS-METHODS xstring_to_bintab IMPORTING !iv_xstr TYPE xsequence EXPORTING !ev_size TYPE i !et_bintab TYPE STANDARD TABLE . CLASS-METHODS language_sap1_to_sap2 IMPORTING im_lang_sap1 TYPE sy-langu RETURNING VALUE(re_lang_sap2) TYPE string EXCEPTIONS no_assignment. CLASS-METHODS language_sap1_to_text IMPORTING im_lang_sap1 TYPE sy-langu RETURNING VALUE(re_text) TYPE string. CLASS-METHODS language_sap2_to_sap1 IMPORTING im_lang_sap2 TYPE laiso RETURNING VALUE(re_lang_sap1) TYPE sy-langu EXCEPTIONS no_assignment. CLASS-METHODS language_sap1_to_bcp47 IMPORTING im_lang_sap1 TYPE sy-langu RETURNING VALUE(re_lang_bcp47) TYPE string EXCEPTIONS no_assignment. CLASS-METHODS language_bcp47_to_sap1 IMPORTING im_lang_bcp47 TYPE string RETURNING VALUE(re_lang_sap1) TYPE sy-langu EXCEPTIONS no_assignment. TYPES ty_char02 TYPE c LENGTH 2. CLASS-METHODS uccp IMPORTING iv_uccp TYPE string RETURNING VALUE(rv_char) TYPE ty_char02 EXCEPTIONS no_assignment. PROTECTED SECTION. PRIVATE SECTION. CLASS-METHODS xstring_remove_bom IMPORTING iv_xstr TYPE xsequence RETURNING VALUE(rv_xstr) TYPE xstring. ENDCLASS. "! Change transport system API CLASS zcl_abapgit_cts_api DEFINITION FINAL CREATE PRIVATE FRIENDS zcl_abapgit_factory. PUBLIC SECTION. INTERFACES: zif_abapgit_cts_api. PROTECTED SECTION. PRIVATE SECTION. DATA mv_confirm_transp_msgs_called TYPE abap_bool. "! Returns the transport request / task the object is currently locked in "! @parameter iv_program_id | Program ID "! @parameter iv_object_type | Object type "! @parameter iv_object_name | Object name "! @parameter rv_transport | Transport request / task "! @raising zcx_abapgit_exception | Object is not locked in a transport METHODS get_current_transport_for_obj IMPORTING !iv_program_id TYPE tadir-pgmid DEFAULT 'R3TR' !iv_object_type TYPE trobjtype !iv_object_name TYPE sobj_name RETURNING VALUE(rv_transport) TYPE trkorr RAISING zcx_abapgit_exception . "! Returns the transport request / task that includes the object (even if not locked) "! @parameter iv_program_id | Program ID "! @parameter iv_object_type | Object type "! @parameter iv_object_name | Object name "! @parameter rv_transport | Transport request / task "! @raising zcx_abapgit_exception | Object is not locked in a transport METHODS get_current_transport_from_db IMPORTING !iv_program_id TYPE tadir-pgmid DEFAULT 'R3TR' !iv_object_type TYPE trobjtype !iv_object_name TYPE sobj_name RETURNING VALUE(rv_transport) TYPE trkorr RAISING zcx_abapgit_exception . "! Check if the object is currently locked in a transport "! @parameter iv_program_id | Program ID "! @parameter iv_object_type | Object type "! @parameter iv_object_name | Object name "! @parameter rv_locked | Object is locked "! @raising zcx_abapgit_exception | Object type is not lockable METHODS is_object_locked_in_transport IMPORTING !iv_program_id TYPE tadir-pgmid DEFAULT 'R3TR' !iv_object_type TYPE trobjtype !iv_object_name TYPE sobj_name RETURNING VALUE(rv_locked) TYPE abap_bool RAISING zcx_abapgit_exception . "! Check if the object type is lockable "! @parameter iv_program_id | Program ID "! @parameter iv_object_type | Object type "! @parameter rv_lockable | Lockable METHODS is_object_type_lockable IMPORTING !iv_program_id TYPE tadir-pgmid DEFAULT 'R3TR' !iv_object_type TYPE trobjtype RETURNING VALUE(rv_lockable) TYPE abap_bool . "! Check if the object type can be transported "! @parameter iv_program_id | Program ID "! @parameter iv_object_type | Object type "! @parameter rv_transportable | Transportable METHODS is_object_type_transportable IMPORTING !iv_program_id TYPE tadir-pgmid DEFAULT 'R3TR' !iv_object_type TYPE trobjtype RETURNING VALUE(rv_transportable) TYPE abap_bool . ENDCLASS. CLASS zcl_abapgit_default_transport DEFINITION CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_default_transport. METHODS constructor. PROTECTED SECTION. PRIVATE SECTION. DATA mv_is_set_by_abapgit TYPE abap_bool . DATA ms_save TYPE e070use . METHODS store. METHODS restore RAISING zcx_abapgit_exception . METHODS set_internal IMPORTING !iv_transport TYPE trkorr RAISING zcx_abapgit_exception . METHODS clear IMPORTING !is_default_task TYPE e070use RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_dependencies DEFINITION FINAL CREATE PUBLIC . PUBLIC SECTION. CLASS-METHODS resolve IMPORTING !iv_skip_ddic TYPE abap_bool DEFAULT abap_false CHANGING !ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt RAISING zcx_abapgit_exception . PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_dependency, depname TYPE dd02l-tabname, deptyp TYPE c LENGTH 4, deplocal TYPE dd02l-as4local, refname TYPE dd02l-tabname, reftyp TYPE c LENGTH 4, kind TYPE c LENGTH 1, END OF ty_dependency . TYPES: ty_dedenpencies TYPE STANDARD TABLE OF ty_dependency WITH NON-UNIQUE DEFAULT KEY . TYPES: BEGIN OF ty_item, obj_type TYPE tadir-object, obj_name TYPE tadir-obj_name, devclass TYPE devclass, END OF ty_item . CLASS-METHODS resolve_ddic CHANGING !ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt RAISING zcx_abapgit_exception . CLASS-METHODS get_ddls_dependencies IMPORTING iv_ddls_name TYPE tadir-obj_name RETURNING VALUE(rt_dependency) TYPE ty_dedenpencies. CLASS-METHODS resolve_packages CHANGING ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt. ENDCLASS. CLASS zcl_abapgit_dot_abapgit DEFINITION CREATE PUBLIC . PUBLIC SECTION. CLASS-METHODS build_default RETURNING VALUE(ro_dot_abapgit) TYPE REF TO zcl_abapgit_dot_abapgit . CLASS-METHODS deserialize IMPORTING !iv_xstr TYPE xstring RETURNING VALUE(ro_dot_abapgit) TYPE REF TO zcl_abapgit_dot_abapgit RAISING zcx_abapgit_exception . METHODS constructor IMPORTING !is_data TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit . METHODS serialize RETURNING VALUE(rv_xstr) TYPE xstring RAISING zcx_abapgit_exception . METHODS to_file RETURNING VALUE(rs_file) TYPE zif_abapgit_git_definitions=>ty_file RAISING zcx_abapgit_exception . METHODS get_data RETURNING VALUE(rs_data) TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit . METHODS add_ignore IMPORTING !iv_path TYPE string !iv_filename TYPE string . METHODS is_ignored IMPORTING !iv_path TYPE string !iv_filename TYPE string RETURNING VALUE(rv_ignored) TYPE abap_bool . METHODS remove_ignore IMPORTING !iv_path TYPE string !iv_filename TYPE string . METHODS get_starting_folder RETURNING VALUE(rv_path) TYPE string . METHODS get_folder_logic RETURNING VALUE(rv_logic) TYPE string . METHODS set_folder_logic IMPORTING !iv_logic TYPE string . METHODS set_starting_folder IMPORTING !iv_path TYPE string . METHODS get_main_language RETURNING VALUE(rv_language) TYPE spras . METHODS get_i18n_languages RETURNING VALUE(rt_languages) TYPE zif_abapgit_definitions=>ty_languages RAISING zcx_abapgit_exception . METHODS set_i18n_languages IMPORTING it_languages TYPE zif_abapgit_definitions=>ty_languages RAISING zcx_abapgit_exception . METHODS determine_i18n_parameters IMPORTING !iv_main_language_only TYPE abap_bool RETURNING VALUE(rs_i18n_params) TYPE zif_abapgit_definitions=>ty_i18n_params RAISING zcx_abapgit_exception. METHODS get_signature RETURNING VALUE(rs_signature) TYPE zif_abapgit_git_definitions=>ty_file_signature RAISING zcx_abapgit_exception . METHODS use_lxe IMPORTING !iv_yes TYPE abap_bool DEFAULT abap_undefined RETURNING VALUE(rv_yes) TYPE abap_bool . METHODS get_requirements RETURNING VALUE(rt_requirements) TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt . METHODS set_requirements IMPORTING !it_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt . METHODS get_name RETURNING VALUE(rv_name) TYPE string. METHODS set_name IMPORTING !iv_name TYPE csequence. METHODS get_version_constant RETURNING VALUE(rv_version_constant) TYPE string . METHODS set_version_constant IMPORTING !iv_version_constant TYPE csequence . METHODS get_abap_language_version RETURNING VALUE(rv_abap_language_version) TYPE string . METHODS set_abap_language_version IMPORTING !iv_abap_language_version TYPE string . METHODS get_original_system RETURNING VALUE(rv_original_system) TYPE string . METHODS set_original_system IMPORTING !iv_original_system TYPE csequence . METHODS get_objs_without_translation RETURNING VALUE(rt_list) TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit-without_translation. METHODS set_objs_without_translation IMPORTING !it_list TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit-without_translation. PROTECTED SECTION. PRIVATE SECTION. DATA ms_data TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit . CLASS-METHODS to_xml IMPORTING !is_data TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit RETURNING VALUE(rv_xml) TYPE string RAISING zcx_abapgit_exception . CLASS-METHODS from_xml IMPORTING !iv_xml TYPE string RETURNING VALUE(rs_data) TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit . ENDCLASS. CLASS zcl_abapgit_ecatt_config_downl DEFINITION INHERITING FROM cl_apl_ecatt_config_download CREATE PUBLIC . PUBLIC SECTION. INTERFACES: zif_abapgit_ecatt_download. METHODS: download REDEFINITION. PROTECTED SECTION. METHODS: download_data REDEFINITION. PRIVATE SECTION. DATA: mv_xml_stream TYPE xstring. ENDCLASS. CLASS zcl_abapgit_ecatt_config_upl DEFINITION INHERITING FROM cl_apl_ecatt_config_upload FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES: zif_abapgit_ecatt_upload. PROTECTED SECTION. METHODS: upload_data_from_stream REDEFINITION. PRIVATE SECTION. DATA: mv_external_xml TYPE xstring. ENDCLASS. CLASS zcl_abapgit_ecatt_data_downl DEFINITION INHERITING FROM cl_apl_ecatt_data_download CREATE PUBLIC . PUBLIC SECTION. INTERFACES: zif_abapgit_ecatt_download. METHODS: download REDEFINITION. PROTECTED SECTION. METHODS: download_data REDEFINITION. PRIVATE SECTION. DATA: mv_xml_stream TYPE xstring. ENDCLASS. CLASS zcl_abapgit_ecatt_data_upload DEFINITION INHERITING FROM cl_apl_ecatt_data_upload FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES: zif_abapgit_ecatt_upload. METHODS upload REDEFINITION. PROTECTED SECTION. METHODS: upload_data_from_stream REDEFINITION. PRIVATE SECTION. DATA: mv_external_xml TYPE xstring, BEGIN OF ms_current_object, s_obj_type TYPE etobj_type, d_obj_name TYPE etobjdname, d_obj_ver TYPE etobjdver, END OF ms_current_object, mx_ecatt_apl TYPE REF TO cx_ecatt_apl. METHODS on_ev_object_saved FOR EVENT ev_object_saved OF cl_apl_ecatt_object IMPORTING ex_ecatt_object. ENDCLASS. CLASS zcl_abapgit_ecatt_helper DEFINITION CREATE PUBLIC . PUBLIC SECTION. CLASS-METHODS: build_xml_of_object IMPORTING iv_object_name TYPE etobj_name iv_object_version TYPE etobj_ver iv_object_type TYPE etobj_type io_download TYPE REF TO cl_apl_ecatt_download RETURNING VALUE(rv_xml_stream) TYPE xstring RAISING zcx_abapgit_exception, download_data IMPORTING ii_template_over_all TYPE REF TO if_ixml_document RETURNING VALUE(rv_xml_stream) TYPE xstring, upload_data_from_stream IMPORTING iv_xml_stream TYPE xstring RETURNING VALUE(ri_template_over_all) TYPE REF TO if_ixml_document RAISING cx_ecatt_apl. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: c_xml TYPE i VALUE 1. " downport of if_apl_ecatt_xml=>co_xml ENDCLASS. CLASS zcl_abapgit_ecatt_script_downl DEFINITION INHERITING FROM cl_apl_ecatt_script_download CREATE PUBLIC . PUBLIC SECTION. INTERFACES: zif_abapgit_ecatt_download. METHODS: download REDEFINITION. PROTECTED SECTION. METHODS: download_data REDEFINITION. PRIVATE SECTION. DATA: mv_xml_stream TYPE xstring, mi_script_node TYPE REF TO if_ixml_element. METHODS: set_script_to_template RAISING cx_ecatt_apl, set_control_data_for_tcd IMPORTING is_param TYPE etpar_gui io_params TYPE REF TO cl_apl_ecatt_params RAISING cx_ecatt_apl, escape_control_data IMPORTING ii_element TYPE REF TO if_ixml_element iv_tabname TYPE string iv_node TYPE string RAISING cx_ecatt_apl, set_blob_to_template RAISING cx_ecatt_apl, set_artmp_to_template RAISING cx_ecatt_apl. ENDCLASS. CLASS zcl_abapgit_ecatt_script_upl DEFINITION INHERITING FROM cl_apl_ecatt_script_upload FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES: zif_abapgit_ecatt_upload. PROTECTED SECTION. METHODS: upload_data_from_stream REDEFINITION. PRIVATE SECTION. DATA: mv_external_xml TYPE xstring. ENDCLASS. CLASS zcl_abapgit_ecatt_sp_download DEFINITION INHERITING FROM cl_apl_ecatt_download CREATE PUBLIC . PUBLIC SECTION. INTERFACES: zif_abapgit_ecatt_download. METHODS: download REDEFINITION. PROTECTED SECTION. METHODS: download_data REDEFINITION. PRIVATE SECTION. DATA: mv_xml_stream TYPE xstring. METHODS: set_sp_data_to_template. ENDCLASS. CLASS zcl_abapgit_ecatt_sp_upload DEFINITION INHERITING FROM cl_apl_ecatt_upload FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES: zif_abapgit_ecatt_upload. METHODS: upload REDEFINITION. PROTECTED SECTION. METHODS: upload_data_from_stream REDEFINITION, get_ecatt_sp RAISING cx_ecatt_apl . PRIVATE SECTION. DATA: mv_external_xml TYPE xstring. ENDCLASS. CLASS zcl_abapgit_ecatt_system_downl DEFINITION INHERITING FROM cl_apl_ecatt_systems_download CREATE PUBLIC . PUBLIC SECTION. INTERFACES: zif_abapgit_ecatt_download. METHODS: download REDEFINITION. PROTECTED SECTION. METHODS: download_data REDEFINITION. PRIVATE SECTION. DATA: mv_xml_stream TYPE xstring. METHODS: set_systems_data_to_template. ENDCLASS. CLASS zcl_abapgit_ecatt_system_upl DEFINITION INHERITING FROM cl_apl_ecatt_systems_upload FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES: zif_abapgit_ecatt_upload. PROTECTED SECTION. METHODS: upload_data_from_stream REDEFINITION. PRIVATE SECTION. DATA: mv_external_xml TYPE xstring. ENDCLASS. CLASS zcl_abapgit_ecatt_val_obj_down DEFINITION INHERITING FROM cl_apl_ecatt_download CREATE PUBLIC. PUBLIC SECTION. INTERFACES: zif_abapgit_ecatt_download. METHODS: download REDEFINITION. PROTECTED SECTION. DATA: mi_objects_node TYPE REF TO if_ixml_element. METHODS: download_data REDEFINITION. PRIVATE SECTION. DATA: mv_xml_stream TYPE xstring. METHODS: set_ecatt_impl_detail, set_ecatt_flags, set_business_msgs. ENDCLASS. CLASS zcl_abapgit_ecatt_val_obj_upl DEFINITION INHERITING FROM cl_apl_ecatt_upload FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES: zif_abapgit_ecatt_upload. METHODS: upload REDEFINITION. PROTECTED SECTION. METHODS: upload_data_from_stream REDEFINITION, get_business_msgs_from_dom RAISING cx_ecatt_apl, get_impl_detail_from_dom RAISING cx_ecatt_apl, get_vo_flags_from_dom RAISING cx_ecatt_apl. PRIVATE SECTION. DATA: mv_external_xml TYPE xstring. ENDCLASS. CLASS zcl_abapgit_environment DEFINITION FINAL CREATE PRIVATE FRIENDS zcl_abapgit_factory. PUBLIC SECTION. INTERFACES zif_abapgit_environment. PROTECTED SECTION. PRIVATE SECTION. DATA mv_cloud TYPE abap_bool VALUE abap_undefined ##NO_TEXT. DATA mv_is_merged TYPE abap_bool VALUE abap_undefined ##NO_TEXT. DATA mv_modifiable TYPE abap_bool VALUE abap_undefined ##NO_TEXT. METHODS is_system_changes_allowed RETURNING VALUE(rv_result) TYPE abap_bool. ENDCLASS. CLASS zcl_abapgit_user_record DEFINITION FINAL CREATE PRIVATE FRIENDS zcl_abapgit_env_factory. PUBLIC SECTION. CLASS-METHODS reset. INTERFACES zif_abapgit_user_record. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_user, user TYPE sy-uname, name TYPE string, email TYPE string, END OF ty_user. CLASS-DATA gt_user TYPE HASHED TABLE OF ty_user WITH UNIQUE KEY user. CLASS-METHODS check_user_exists IMPORTING iv_user TYPE sy-uname EXPORTING ev_fullname TYPE string ev_email TYPE string RAISING zcx_abapgit_exception. CLASS-METHODS get_user_dtls_from_other_clnt IMPORTING iv_user TYPE sy-uname RETURNING VALUE(rs_user) TYPE ty_user. CLASS-METHODS build_cache IMPORTING iv_user TYPE sy-uname RETURNING VALUE(rs_user) TYPE ty_user. CLASS-METHODS read_cache IMPORTING iv_user TYPE sy-uname RETURNING VALUE(rs_user) TYPE ty_user. ENDCLASS. CLASS zcl_abapgit_env_factory DEFINITION FRIENDS zcl_abapgit_env_injector. PUBLIC SECTION. CLASS-METHODS get_user_record RETURNING VALUE(ri_user_record) TYPE REF TO zif_abapgit_user_record. PRIVATE SECTION. CLASS-DATA gi_user_record TYPE REF TO zif_abapgit_user_record. ENDCLASS. CLASS zcl_abapgit_env_injector DEFINITION . PUBLIC SECTION. CLASS-METHODS set_user_record IMPORTING !ii_user_record TYPE REF TO zif_abapgit_user_record. ENDCLASS. CLASS zcl_abapgit_exit DEFINITION CREATE PUBLIC FRIENDS zcl_abapgit_injector. PUBLIC SECTION. INTERFACES zif_abapgit_exit. CLASS-METHODS get_instance RETURNING VALUE(ri_exit) TYPE REF TO zif_abapgit_exit. PROTECTED SECTION. PRIVATE SECTION. CLASS-DATA gi_global_exit TYPE REF TO zif_abapgit_exit. CLASS-DATA gi_exit TYPE REF TO zif_abapgit_exit. CLASS-METHODS is_running_in_test_context RETURNING VALUE(rv_running_in_test_context) TYPE abap_bool. ENDCLASS. CLASS zcl_abapgit_function_module DEFINITION FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES: zif_abapgit_function_module. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_longtexts DEFINITION CREATE PRIVATE FRIENDS zcl_abapgit_factory. PUBLIC SECTION. INTERFACES zif_abapgit_longtexts. PROTECTED SECTION. METHODS read IMPORTING !iv_object_name TYPE clike !iv_longtext_id TYPE dokil-id !it_dokil TYPE zif_abapgit_definitions=>ty_dokil_tt !iv_main_lang_only TYPE abap_bool DEFAULT abap_false !iv_clear_fields TYPE abap_bool DEFAULT abap_true RETURNING VALUE(rt_longtexts) TYPE zif_abapgit_longtexts=>ty_longtexts RAISING zcx_abapgit_exception . PRIVATE SECTION. CONSTANTS c_docu_state_active TYPE dokstate VALUE 'A' ##NO_TEXT. METHODS escape_name IMPORTING !iv_longtext_id TYPE dokil-id !iv_object_name TYPE clike RETURNING VALUE(rv_object) TYPE dokil-object. ENDCLASS. CLASS zcl_abapgit_lxe_texts DEFINITION FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_lxe_texts . CLASS-METHODS get_translation_languages IMPORTING !iv_main_language TYPE spras !it_i18n_languages TYPE zif_abapgit_definitions=>ty_languages RETURNING VALUE(rt_languages) TYPE zif_abapgit_definitions=>ty_languages RAISING zcx_abapgit_exception. CLASS-METHODS get_installed_languages RETURNING VALUE(rt_languages) TYPE zif_abapgit_definitions=>ty_languages RAISING zcx_abapgit_exception. CLASS-METHODS convert_lang_string_to_table IMPORTING !iv_langs TYPE string !iv_skip_main_language TYPE spras OPTIONAL RETURNING VALUE(rt_languages) TYPE zif_abapgit_definitions=>ty_languages RAISING zcx_abapgit_exception . CLASS-METHODS convert_table_to_lang_string IMPORTING !it_languages TYPE zif_abapgit_definitions=>ty_languages RETURNING VALUE(rv_langs) TYPE string RAISING zcx_abapgit_exception . CLASS-METHODS detect_unsupported_languages IMPORTING !it_languages TYPE zif_abapgit_definitions=>ty_languages RETURNING VALUE(rt_unsupported_languages) TYPE zif_abapgit_definitions=>ty_languages RAISING zcx_abapgit_exception . PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_custmnr TYPE lxecustmnr VALUE '999999' ##NEEDED. " The value for ABAP system translation is always 999999 (from lxecustmnr docs) TYPES: BEGIN OF ty_lxe_lang_map, " extract from LXE_T002X language TYPE lxeisolang, r3_lang TYPE spras, langshort TYPE c LENGTH 2, END OF ty_lxe_lang_map. CLASS-DATA gt_lxe_lang_cache TYPE SORTED TABLE OF ty_lxe_lang_map WITH UNIQUE KEY language WITH NON-UNIQUE SORTED KEY iso2 COMPONENTS langshort. " Controversial: we need uniq, but maybe it's better to keep it debuggable " TODO, add r3 key if needed in future TYPES: BEGIN OF ty_lxe_translation, source_lang TYPE lxeisolang, target_lang TYPE lxeisolang, custmnr TYPE lxecustmnr, objtype TYPE trobjtype, objname TYPE lxeobjname, text_pairs TYPE zif_abapgit_lxe_texts=>ty_text_pairs, END OF ty_lxe_translation. TYPES: ty_lxe_translations TYPE STANDARD TABLE OF ty_lxe_translation WITH DEFAULT KEY . CLASS-DATA gt_installed_languages_cache TYPE zif_abapgit_definitions=>ty_languages. DATA mo_i18n_params TYPE REF TO zcl_abapgit_i18n_params. DATA mi_xml_out TYPE REF TO zif_abapgit_xml_output. DATA mi_xml_in TYPE REF TO zif_abapgit_xml_input. DATA mo_files TYPE REF TO zcl_abapgit_objects_files. METHODS serialize_xml IMPORTING !iv_lxe_text_name TYPE string DEFAULT 'LXE_TEXTS' !iv_object_type TYPE tadir-object !iv_object_name TYPE tadir-obj_name RAISING zcx_abapgit_exception . METHODS serialize_as_po IMPORTING !iv_object_type TYPE tadir-object !iv_object_name TYPE tadir-obj_name RAISING zcx_abapgit_exception . " Implementation of deserialize_xml is not complete (but kept as future option) METHODS deserialize_xml IMPORTING !iv_lxe_text_name TYPE string DEFAULT 'LXE_TEXTS' !iv_object_type TYPE tadir-object OPTIONAL !iv_object_name TYPE tadir-obj_name OPTIONAL RAISING zcx_abapgit_exception ##NEEDED. METHODS deserialize_from_po IMPORTING !iv_object_type TYPE tadir-object !iv_object_name TYPE tadir-obj_name RETURNING VALUE(rv_changed) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS get_lang_iso4 IMPORTING iv_src TYPE laiso RETURNING VALUE(rv_iso4) TYPE lxeisolang RAISING zcx_abapgit_exception. METHODS get_lxe_object_list IMPORTING iv_object_type TYPE trobjtype iv_object_name TYPE sobj_name RETURNING VALUE(rt_obj_list) TYPE lxe_tt_colob . METHODS remove_irrelevant IMPORTING iv_objtype TYPE trobjtype CHANGING ct_text_pairs_tmp TYPE ty_lxe_translation-text_pairs. METHODS read_lxe_object_text_pair IMPORTING iv_s_lang TYPE lxeisolang iv_t_lang TYPE lxeisolang iv_custmnr TYPE lxecustmnr iv_objtype TYPE trobjtype iv_objname TYPE lxeobjname iv_read_only TYPE abap_bool DEFAULT abap_true RETURNING VALUE(rt_text_pairs_tmp) TYPE ty_lxe_translation-text_pairs RAISING zcx_abapgit_exception. METHODS write_lxe_object_text_pair IMPORTING iv_s_lang TYPE lxeisolang iv_t_lang TYPE lxeisolang iv_custmnr TYPE lxecustmnr iv_objtype TYPE trobjtype iv_objname TYPE lxeobjname it_pcx_s1 TYPE ty_lxe_translation-text_pairs RAISING zcx_abapgit_exception. METHODS read_text_items IMPORTING iv_object_type TYPE tadir-object iv_object_name TYPE tadir-obj_name RETURNING VALUE(rt_text_items) TYPE ty_lxe_translations RAISING zcx_abapgit_exception. CLASS-METHODS langu_to_laiso_safe IMPORTING iv_langu TYPE sy-langu RETURNING VALUE(rv_laiso) TYPE laiso RAISING zcx_abapgit_exception. CLASS-METHODS iso4_to_iso2 IMPORTING iv_lxe_lang TYPE lxeisolang RETURNING VALUE(rv_laiso) TYPE laiso RAISING zcx_abapgit_exception. CLASS-METHODS check_langs_versus_installed IMPORTING it_languages TYPE zif_abapgit_definitions=>ty_languages it_installed TYPE zif_abapgit_definitions=>ty_languages EXPORTING et_intersection TYPE zif_abapgit_definitions=>ty_languages et_missfits TYPE zif_abapgit_definitions=>ty_languages. ENDCLASS. CLASS zcl_abapgit_sap_namespace DEFINITION FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_sap_namespace. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_sap_package DEFINITION CREATE PRIVATE FRIENDS zcl_abapgit_factory . PUBLIC SECTION. INTERFACES zif_abapgit_sap_package . METHODS constructor IMPORTING !iv_package TYPE devclass . PROTECTED SECTION. PRIVATE SECTION. DATA mv_package TYPE devclass. ENDCLASS. CLASS zcl_abapgit_sap_report DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_abapgit_sap_report. PROTECTED SECTION. PRIVATE SECTION. METHODS authorization_check IMPORTING iv_mode TYPE csequence is_item TYPE zif_abapgit_definitions=>ty_item RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_tadir DEFINITION FINAL CREATE PRIVATE FRIENDS zcl_abapgit_factory . PUBLIC SECTION. INTERFACES zif_abapgit_tadir . PROTECTED SECTION. PRIVATE SECTION. METHODS check_exists IMPORTING !it_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt RETURNING VALUE(rt_tadir) TYPE zif_abapgit_definitions=>ty_tadir_tt RAISING zcx_abapgit_exception . METHODS build IMPORTING !iv_package TYPE tadir-devclass !io_dot TYPE REF TO zcl_abapgit_dot_abapgit !iv_ignore_subpackages TYPE abap_bool DEFAULT abap_false !iv_only_local_objects TYPE abap_bool DEFAULT abap_false iv_ignore_delflag TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rt_tadir) TYPE zif_abapgit_definitions=>ty_tadir_tt RAISING zcx_abapgit_exception . METHODS select_objects IMPORTING !iv_package TYPE tadir-devclass !iv_ignore_subpackages TYPE abap_bool DEFAULT abap_false !iv_only_local_objects TYPE abap_bool iv_ignore_delflag TYPE abap_bool DEFAULT abap_false EXPORTING !et_packages TYPE zif_abapgit_sap_package=>ty_devclass_tt !et_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt RAISING zcx_abapgit_exception . METHODS add_local_packages IMPORTING !it_packages TYPE zif_abapgit_sap_package=>ty_devclass_tt CHANGING !ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt RAISING zcx_abapgit_exception . METHODS add_namespaces IMPORTING !iv_package TYPE devclass CHANGING !ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt RAISING zcx_abapgit_exception . METHODS add_namespace IMPORTING !iv_package TYPE devclass !iv_object TYPE csequence CHANGING !ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt !ct_tadir_nspc TYPE zif_abapgit_definitions=>ty_tadir_tt RAISING zcx_abapgit_exception . METHODS determine_path IMPORTING !iv_package TYPE tadir-devclass !io_dot TYPE REF TO zcl_abapgit_dot_abapgit CHANGING !ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_factory DEFINITION CREATE PRIVATE FRIENDS zcl_abapgit_injector . PUBLIC SECTION. CLASS-METHODS get_tadir RETURNING VALUE(ri_tadir) TYPE REF TO zif_abapgit_tadir . CLASS-METHODS get_sap_package IMPORTING !iv_package TYPE devclass RETURNING VALUE(ri_sap_package) TYPE REF TO zif_abapgit_sap_package . CLASS-METHODS get_cts_api RETURNING VALUE(ri_cts_api) TYPE REF TO zif_abapgit_cts_api . CLASS-METHODS get_default_transport RETURNING VALUE(ri_default_transport) TYPE REF TO zif_abapgit_default_transport. CLASS-METHODS get_environment RETURNING VALUE(ri_environment) TYPE REF TO zif_abapgit_environment . CLASS-METHODS get_longtexts RETURNING VALUE(ri_longtexts) TYPE REF TO zif_abapgit_longtexts . CLASS-METHODS get_lxe_texts RETURNING VALUE(ri_lxe_texts) TYPE REF TO zif_abapgit_lxe_texts . CLASS-METHODS get_sap_namespace RETURNING VALUE(ri_namespace) TYPE REF TO zif_abapgit_sap_namespace . CLASS-METHODS get_sap_report RETURNING VALUE(ri_report) TYPE REF TO zif_abapgit_sap_report. CLASS-METHODS get_function_module RETURNING VALUE(ri_function_module) TYPE REF TO zif_abapgit_function_module. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_sap_package, package TYPE devclass, instance TYPE REF TO zif_abapgit_sap_package, END OF ty_sap_package . TYPES: ty_sap_packages TYPE HASHED TABLE OF ty_sap_package WITH UNIQUE KEY package . CLASS-DATA gi_tadir TYPE REF TO zif_abapgit_tadir . CLASS-DATA gt_sap_package TYPE ty_sap_packages . CLASS-DATA gi_cts_api TYPE REF TO zif_abapgit_cts_api . CLASS-DATA gi_environment TYPE REF TO zif_abapgit_environment . CLASS-DATA gi_longtext TYPE REF TO zif_abapgit_longtexts . CLASS-DATA gi_lxe_texts TYPE REF TO zif_abapgit_lxe_texts . CLASS-DATA gi_sap_namespace TYPE REF TO zif_abapgit_sap_namespace . CLASS-DATA gi_sap_report TYPE REF TO zif_abapgit_sap_report. CLASS-DATA gi_function_module TYPE REF TO zif_abapgit_function_module. CLASS-DATA gi_default_transport TYPE REF TO zif_abapgit_default_transport . ENDCLASS. CLASS zcl_abapgit_feature DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. " For dependency injection/testing, use the following " zcl_abapgit_persist_factory=>get_settings( )->read( )->set_experimental_features( ) CLASS-METHODS is_enabled IMPORTING !iv_feature TYPE string OPTIONAL RETURNING VALUE(rv_run) TYPE abap_bool. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_field_rules DEFINITION FINAL CREATE PRIVATE. PUBLIC SECTION. INTERFACES zif_abapgit_field_rules. CLASS-METHODS create RETURNING VALUE(ro_result) TYPE REF TO zif_abapgit_field_rules. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_item, tabname TYPE tabname, fieldname TYPE fieldname, fill_rule TYPE zif_abapgit_field_rules=>ty_fill_rule, END OF ty_item, ty_items TYPE SORTED TABLE OF ty_item WITH UNIQUE KEY tabname fieldname. DATA mt_item TYPE ty_items. METHODS fill_value IMPORTING iv_rule TYPE zif_abapgit_field_rules=>ty_fill_rule iv_package TYPE devclass iv_abap_language_version TYPE uccheck OPTIONAL CHANGING cv_value TYPE any. ENDCLASS. CLASS zcl_abapgit_filename_logic DEFINITION FINAL CREATE PUBLIC . PUBLIC SECTION. CONSTANTS: BEGIN OF c_package_file, obj_name TYPE c LENGTH 7 VALUE 'package', sep1 TYPE c LENGTH 1 VALUE '.', obj_type TYPE c LENGTH 4 VALUE 'devc', sep2 TYPE c LENGTH 1 VALUE '.', extension TYPE c LENGTH 3 VALUE 'xml', END OF c_package_file. CONSTANTS: BEGIN OF c_json_file, extension TYPE c LENGTH 4 VALUE 'json', END OF c_json_file. CLASS-METHODS detect_obj_definition IMPORTING !iv_type TYPE string !iv_ext TYPE string EXPORTING !ev_is_xml TYPE abap_bool !ev_is_json TYPE abap_bool. CLASS-METHODS is_obj_definition_file IMPORTING !iv_filename TYPE string RETURNING VALUE(rv_yes) TYPE abap_bool. CLASS-METHODS file_to_object IMPORTING !iv_filename TYPE string !iv_path TYPE string !iv_devclass TYPE devclass OPTIONAL !io_dot TYPE REF TO zcl_abapgit_dot_abapgit EXPORTING !es_item TYPE zif_abapgit_definitions=>ty_item !ev_is_xml TYPE abap_bool !ev_is_json TYPE abap_bool RAISING zcx_abapgit_exception . CLASS-METHODS i18n_file_to_object IMPORTING !iv_filename TYPE string !iv_path TYPE string EXPORTING !es_item TYPE zif_abapgit_definitions=>ty_item !ev_lang TYPE laiso !ev_ext TYPE string RAISING zcx_abapgit_exception . CLASS-METHODS object_to_file IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_ext TYPE string !iv_extra TYPE clike OPTIONAL RETURNING VALUE(rv_filename) TYPE string . CLASS-METHODS object_to_i18n_file IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_lang_suffix TYPE string !iv_ext TYPE string RETURNING VALUE(rv_filename) TYPE string. PROTECTED SECTION. PRIVATE SECTION. CLASS-METHODS name_escape IMPORTING !iv_name TYPE csequence RETURNING VALUE(rv_name) TYPE string. CLASS-METHODS name_unescape IMPORTING !iv_name TYPE csequence RETURNING VALUE(rv_name) TYPE string. CLASS-METHODS map_filename_to_object IMPORTING !iv_item_part_of_filename TYPE string !iv_path TYPE string !iv_package TYPE devclass !io_dot TYPE REF TO zcl_abapgit_dot_abapgit CHANGING cs_item TYPE zif_abapgit_definitions=>ty_item RAISING zcx_abapgit_exception. CLASS-METHODS map_object_to_filename IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_ext TYPE string !iv_extra TYPE clike CHANGING cv_item_part_of_filename TYPE string RAISING zcx_abapgit_exception. CLASS-METHODS get_lang_and_ext IMPORTING iv_filename TYPE string EXPORTING ev_lang TYPE laiso ev_ext TYPE string RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_folder_logic DEFINITION CREATE PUBLIC . PUBLIC SECTION. METHODS package_to_path IMPORTING !iv_top TYPE devclass !io_dot TYPE REF TO zcl_abapgit_dot_abapgit !iv_package TYPE devclass RETURNING VALUE(rv_path) TYPE string RAISING zcx_abapgit_exception . METHODS path_to_package IMPORTING !iv_top TYPE devclass !io_dot TYPE REF TO zcl_abapgit_dot_abapgit !iv_path TYPE string !iv_create_if_not_exists TYPE abap_bool DEFAULT abap_true RETURNING VALUE(rv_package) TYPE devclass RAISING zcx_abapgit_exception . CLASS-METHODS get_instance RETURNING VALUE(ro_instance) TYPE REF TO zcl_abapgit_folder_logic . PROTECTED SECTION. METHODS get_parent IMPORTING !iv_top TYPE devclass !iv_package TYPE devclass RETURNING VALUE(rv_parent) TYPE devclass RAISING zcx_abapgit_exception . PRIVATE SECTION. TYPES: BEGIN OF ty_devclass_info, devclass TYPE devclass, namespace TYPE namespace, parentcl TYPE parentcl, END OF ty_devclass_info . TYPES: ty_devclass_info_tt TYPE SORTED TABLE OF ty_devclass_info WITH UNIQUE KEY devclass . DATA mt_top_subpackages TYPE ty_devclass_info_tt . DATA mt_parent TYPE ty_devclass_info_tt . ENDCLASS. CLASS zcl_abapgit_gui_jumper DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_abapgit_gui_jumper. PROTECTED SECTION. PRIVATE SECTION. METHODS jump_tr IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item RETURNING VALUE(rv_exit) TYPE abap_bool. METHODS jump_wb IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_new_window TYPE abap_bool RETURNING VALUE(rv_exit) TYPE abap_bool. METHODS jump_wb_line IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_sub_obj_name TYPE zif_abapgit_definitions=>ty_item-obj_name !iv_sub_obj_type TYPE zif_abapgit_definitions=>ty_item-obj_type !iv_line_number TYPE i !iv_new_window TYPE abap_bool RETURNING VALUE(rv_exit) TYPE abap_bool. METHODS jump_bw IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_new_window TYPE abap_bool RETURNING VALUE(rv_exit) TYPE abap_bool. ENDCLASS. CLASS zcl_abapgit_hash DEFINITION CREATE PUBLIC . PUBLIC SECTION. CLASS-METHODS adler32 IMPORTING !iv_xstring TYPE xstring RETURNING VALUE(rv_checksum) TYPE zif_abapgit_git_definitions=>ty_adler32 . CLASS-METHODS sha1 IMPORTING !iv_type TYPE zif_abapgit_git_definitions=>ty_type !iv_data TYPE xstring RETURNING VALUE(rv_sha1) TYPE zif_abapgit_git_definitions=>ty_sha1 RAISING zcx_abapgit_exception . CLASS-METHODS sha1_commit IMPORTING !iv_data TYPE xstring RETURNING VALUE(rv_sha1) TYPE zif_abapgit_git_definitions=>ty_sha1 RAISING zcx_abapgit_exception . CLASS-METHODS sha1_tree IMPORTING !iv_data TYPE xstring RETURNING VALUE(rv_sha1) TYPE zif_abapgit_git_definitions=>ty_sha1 RAISING zcx_abapgit_exception . CLASS-METHODS sha1_tag IMPORTING !iv_data TYPE xstring RETURNING VALUE(rv_sha1) TYPE zif_abapgit_git_definitions=>ty_sha1 RAISING zcx_abapgit_exception . CLASS-METHODS sha1_blob IMPORTING !iv_data TYPE xstring RETURNING VALUE(rv_sha1) TYPE zif_abapgit_git_definitions=>ty_sha1 RAISING zcx_abapgit_exception . CLASS-METHODS sha1_raw IMPORTING !iv_data TYPE xstring RETURNING VALUE(rv_sha1) TYPE zif_abapgit_git_definitions=>ty_sha1 RAISING zcx_abapgit_exception . CLASS-METHODS sha1_string IMPORTING !iv_data TYPE string RETURNING VALUE(rv_sha1) TYPE zif_abapgit_git_definitions=>ty_sha1 RAISING zcx_abapgit_exception . PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_i18n_params DEFINITION FINAL CREATE PRIVATE. PUBLIC SECTION. DATA ms_params TYPE zif_abapgit_definitions=>ty_i18n_params READ-ONLY . CLASS-METHODS new IMPORTING !iv_main_language TYPE spras DEFAULT zif_abapgit_definitions=>c_english !iv_main_language_only TYPE abap_bool DEFAULT abap_false !it_translation_langs TYPE zif_abapgit_definitions=>ty_languages OPTIONAL !iv_use_lxe TYPE abap_bool DEFAULT abap_false !is_params TYPE zif_abapgit_definitions=>ty_i18n_params OPTIONAL RETURNING VALUE(ro_instance) TYPE REF TO zcl_abapgit_i18n_params. METHODS constructor IMPORTING !iv_main_language TYPE spras DEFAULT zif_abapgit_definitions=>c_english !iv_main_language_only TYPE abap_bool DEFAULT abap_false !it_translation_langs TYPE zif_abapgit_definitions=>ty_languages OPTIONAL !iv_use_lxe TYPE abap_bool DEFAULT abap_false !is_params TYPE zif_abapgit_definitions=>ty_i18n_params OPTIONAL. METHODS is_lxe_applicable RETURNING VALUE(rv_yes) TYPE abap_bool . METHODS build_language_filter RETURNING VALUE(rt_language_filter) TYPE zif_abapgit_environment=>ty_system_language_filter . METHODS trim_saplang_list CHANGING ct_sap_langs TYPE zif_abapgit_definitions=>ty_sap_langu_tab RAISING zcx_abapgit_exception. METHODS trim_saplang_keyed_table IMPORTING iv_lang_field_name TYPE abap_compname iv_keep_master_lang TYPE abap_bool DEFAULT abap_false "sy-langu OPTIONAL CHANGING ct_tab TYPE STANDARD TABLE RAISING zcx_abapgit_exception. CLASS-METHODS normalize_obj_patterns IMPORTING it_wo_translation_patterns TYPE string_table RETURNING VALUE(rt_wo_translation_clean) TYPE string_table RAISING zcx_abapgit_exception. CLASS-METHODS match_obj_patterns IMPORTING it_wo_translation_patterns TYPE string_table is_tadir TYPE zif_abapgit_definitions=>ty_tadir RETURNING VALUE(rv_yes) TYPE abap_bool RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. DATA mt_language_filter TYPE zif_abapgit_environment=>ty_system_language_filter. CLASS-METHODS iso_langs_to_lang_filter IMPORTING it_iso_filter TYPE zif_abapgit_definitions=>ty_languages RETURNING VALUE(rt_language_filter) TYPE zif_abapgit_environment=>ty_system_language_filter. ENDCLASS. CLASS zcl_abapgit_injector DEFINITION CREATE PRIVATE. PUBLIC SECTION. CLASS-METHODS set_tadir IMPORTING !ii_tadir TYPE REF TO zif_abapgit_tadir . CLASS-METHODS set_sap_package IMPORTING !iv_package TYPE devclass !ii_sap_package TYPE REF TO zif_abapgit_sap_package . CLASS-METHODS clear_sap_package. CLASS-METHODS set_cts_api IMPORTING !ii_cts_api TYPE REF TO zif_abapgit_cts_api . CLASS-METHODS set_environment IMPORTING !ii_environment TYPE REF TO zif_abapgit_environment . CLASS-METHODS set_longtexts IMPORTING !ii_longtexts TYPE REF TO zif_abapgit_longtexts . CLASS-METHODS set_lxe_texts IMPORTING !ii_lxe_texts TYPE REF TO zif_abapgit_lxe_texts . CLASS-METHODS set_sap_namespace IMPORTING !ii_namespace TYPE REF TO zif_abapgit_sap_namespace . CLASS-METHODS set_sap_report IMPORTING !ii_report TYPE REF TO zif_abapgit_sap_report. CLASS-METHODS set_function_module IMPORTING ii_function_module TYPE REF TO zif_abapgit_function_module. CLASS-METHODS set_exit IMPORTING ii_exit TYPE REF TO zif_abapgit_exit. CLASS-METHODS set_default_transport IMPORTING ii_default_transport TYPE REF TO zif_abapgit_default_transport. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_item_graph DEFINITION CREATE PUBLIC . PUBLIC SECTION. METHODS constructor IMPORTING !it_items TYPE zif_abapgit_definitions=>ty_items_tt . METHODS add_edge IMPORTING !is_from TYPE zif_abapgit_definitions=>ty_item !is_to TYPE zif_abapgit_definitions=>ty_item . METHODS has_vertices RETURNING VALUE(rv_bool) TYPE abap_bool . METHODS get_next IMPORTING !ii_log TYPE REF TO zif_abapgit_log RETURNING VALUE(rs_item) TYPE zif_abapgit_definitions=>ty_item . PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_edge, from TYPE zif_abapgit_definitions=>ty_item, to TYPE zif_abapgit_definitions=>ty_item, END OF ty_edge. DATA mt_vertices TYPE STANDARD TABLE OF zif_abapgit_definitions=>ty_item WITH DEFAULT KEY. DATA mt_edges TYPE STANDARD TABLE OF ty_edge WITH DEFAULT KEY WITH NON-UNIQUE SORTED KEY sec_to COMPONENTS to WITH NON-UNIQUE SORTED KEY sec_from COMPONENTS from. DATA mv_warning TYPE abap_bool. METHODS remove_vertex IMPORTING iv_index TYPE i. ENDCLASS. CLASS zcl_abapgit_json_handler DEFINITION FINAL CREATE PUBLIC . PUBLIC SECTION. TYPES: BEGIN OF ty_json_abap_mapping, json TYPE string, abap TYPE string, END OF ty_json_abap_mapping . TYPES: ty_json_abap_mappings TYPE STANDARD TABLE OF ty_json_abap_mapping WITH DEFAULT KEY . TYPES: BEGIN OF ty_enum_mapping, path TYPE string, mappings TYPE ty_json_abap_mappings, END OF ty_enum_mapping . TYPES: ty_enum_mappings TYPE STANDARD TABLE OF ty_enum_mapping WITH DEFAULT KEY . TYPES: BEGIN OF ty_path_value_pair, path TYPE string, value TYPE string, END OF ty_path_value_pair . TYPES: ty_skip_paths TYPE STANDARD TABLE OF ty_path_value_pair WITH KEY path . "! Serializes data to xstring. Type of data is specified in the "! implementing class. "! "! @parameter iv_data | data to be serialized "! @parameter iv_enum_mappings | ABAP/JSON value mappings "! @parameter iv_skip_paths | path/value pairs to be skipped during serialization "! @parameter rv_result | serialized data METHODS serialize IMPORTING !iv_data TYPE data !iv_enum_mappings TYPE ty_enum_mappings OPTIONAL !iv_skip_paths TYPE ty_skip_paths OPTIONAL RETURNING VALUE(rv_result) TYPE xstring RAISING cx_static_check . "! Deserializes xstring into data. The type of data is specified in "! the implementing class "! "! @parameter iv_content | xstring to be deserialized "! @parameter iv_defaults | path-value pairs that apply if value is initial "! @parameter ev_data | data of the xstring METHODS deserialize IMPORTING !iv_content TYPE string !iv_defaults TYPE ty_skip_paths OPTIONAL !iv_enum_mappings TYPE ty_enum_mappings OPTIONAL EXPORTING !ev_data TYPE data RAISING cx_static_check . PROTECTED SECTION. PRIVATE SECTION. METHODS: map2json_original_language CHANGING co_ajson TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error, map2json_custom_enum IMPORTING it_enum_mappings TYPE ty_enum_mappings CHANGING co_ajson TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error, map2json_abap_language_version CHANGING co_ajson TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error, "! Get the enum mapping from object handler, as other enums as well map2abap_abap_language_version CHANGING co_ajson TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error, "! For deserialization map2abap_original_language CHANGING co_ajson TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error, "! For deserialization set_defaults IMPORTING it_defaults TYPE ty_skip_paths CHANGING co_ajson TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error, map2abap_custom_enum IMPORTING it_enum_mappings TYPE ty_enum_mappings CHANGING co_ajson TYPE REF TO /apmg/if_apm_ajson RAISING /apmg/cx_apm_ajson_error. ENDCLASS. CLASS zcl_abapgit_json_path DEFINITION CREATE PUBLIC. PUBLIC SECTION. METHODS: serialize IMPORTING iv_json TYPE string RETURNING VALUE(rt_result) TYPE string_table RAISING zcx_abapgit_exception. METHODS: deserialize IMPORTING it_json_path TYPE string_table RETURNING VALUE(rv_result) TYPE string RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_language DEFINITION CREATE PUBLIC . *----------------------------------------------------------------------* * This helper class is used to set and restore the current language. * As some of the SAP functions used rely on SY-LANGU containing the * main language, this class is used to temporarily change and then * restore the value of SY-LANGU. *----------------------------------------------------------------------* PUBLIC SECTION. CLASS-METHODS class_constructor . CLASS-METHODS restore_login_language . CLASS-METHODS set_current_language IMPORTING !iv_language TYPE sy-langu . PROTECTED SECTION. PRIVATE SECTION. CLASS-DATA gv_login_language TYPE sy-langu . ENDCLASS. CLASS zcl_abapgit_objects_activation DEFINITION CREATE PUBLIC . PUBLIC SECTION. CLASS-METHODS add IMPORTING !iv_type TYPE trobjtype !iv_name TYPE clike !iv_delete TYPE abap_bool DEFAULT abap_false RAISING zcx_abapgit_exception . CLASS-METHODS add_item IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item RAISING zcx_abapgit_exception . CLASS-METHODS activate IMPORTING !iv_ddic TYPE abap_bool DEFAULT abap_false !ii_log TYPE REF TO zif_abapgit_log RAISING zcx_abapgit_exception . CLASS-METHODS clear . CLASS-METHODS is_ddic_type IMPORTING !iv_obj_type TYPE trobjtype RETURNING VALUE(rv_result) TYPE abap_bool . CLASS-METHODS is_active IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item RETURNING VALUE(rv_active) TYPE abap_bool RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_classes, object TYPE trobjtype, clsname TYPE seoclsname, END OF ty_classes. CONSTANTS: c_domain TYPE c LENGTH 9 VALUE 'DOMA DOMD', c_types TYPE c LENGTH 55 VALUE 'DTEL DTED TABL TABD SQLT SQLD TTYP TTYD VIEW VIED DRTY', c_technset TYPE c LENGTH 24 VALUE 'TABT VIET SQTT INDX XINX', c_f4_objects TYPE c LENGTH 35 VALUE 'SHLP SHLD MCOB MCOD MACO MACD MCID', c_enqueue TYPE c LENGTH 9 VALUE 'ENQU ENQD', c_sqsc TYPE c LENGTH 4 VALUE 'SQSC', c_stob TYPE c LENGTH 4 VALUE 'STOB', c_ntab TYPE c LENGTH 14 VALUE 'NTTT NTTB NTDT', c_cds TYPE c LENGTH 29 VALUE 'DDLS DRUL DTDC DTSC DTEB DESD', c_switches TYPE c LENGTH 24 VALUE 'SF01 SF02 SFSW SFBS SFBF', c_para TYPE c LENGTH 4 VALUE 'PARA', " can be referenced by DTEL c_enhd TYPE c LENGTH 4 VALUE 'ENHD', c_scalarfunc TYPE c LENGTH 9 VALUE 'DSFD DSFI', c_aspect TYPE c LENGTH 4 VALUE 'DRAS'. CLASS-DATA: gt_classes TYPE STANDARD TABLE OF ty_classes WITH DEFAULT KEY . CLASS-DATA: gt_objects TYPE TABLE OF dwinactiv . CLASS-METHODS update_where_used IMPORTING !ii_log TYPE REF TO zif_abapgit_log. CLASS-METHODS use_new_activation_logic RETURNING VALUE(rv_use_new_activation_logic) TYPE abap_bool . CLASS-METHODS activate_new IMPORTING !iv_ddic TYPE abap_bool DEFAULT abap_false !ii_log TYPE REF TO zif_abapgit_log RAISING zcx_abapgit_exception . CLASS-METHODS activate_old IMPORTING !iv_ddic TYPE abap_bool DEFAULT abap_false !ii_log TYPE REF TO zif_abapgit_log RAISING zcx_abapgit_exception . CLASS-METHODS activate_ddic IMPORTING !ii_log TYPE REF TO zif_abapgit_log RAISING zcx_abapgit_exception . CLASS-METHODS add_errors_and_warnings_to_log IMPORTING !iv_logname TYPE ddmass-logname !ii_log TYPE REF TO zif_abapgit_log RAISING zcx_abapgit_exception . CLASS-METHODS add_activation_errors_to_log IMPORTING !io_checklist TYPE REF TO cl_wb_checklist !ii_log TYPE REF TO zif_abapgit_log RETURNING VALUE(rv_try_again) TYPE abap_bool RAISING zcx_abapgit_exception . CLASS-METHODS is_non_ddic_active IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item RETURNING VALUE(rv_active) TYPE abap_bool RAISING zcx_abapgit_exception . CLASS-METHODS is_ddic_active IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item RETURNING VALUE(rv_active) TYPE abap_bool RAISING zcx_abapgit_exception . CLASS-METHODS get_ddic_type IMPORTING !iv_obj_type TYPE clike !iv_obj_name TYPE clike EXPORTING !ev_type TYPE ddobjtyp !ev_name TYPE ddobjname !ev_id TYPE ddobjectid RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_xml_input DEFINITION INHERITING FROM zcl_abapgit_xml CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_xml_input. METHODS constructor IMPORTING !iv_xml TYPE clike !iv_filename TYPE string OPTIONAL RAISING zcx_abapgit_exception . PROTECTED SECTION. PRIVATE SECTION. METHODS fix_xml. ENDCLASS. CLASS zcl_abapgit_objects_compare DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. CLASS-METHODS get_comparator IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item RETURNING VALUE(ri_comparator) TYPE REF TO zif_abapgit_comparator. CLASS-METHODS get_result IMPORTING ii_comparator TYPE REF TO zif_abapgit_comparator iv_filename TYPE string it_local TYPE zif_abapgit_definitions=>ty_files_item_tt it_remote TYPE zif_abapgit_git_definitions=>ty_files_tt RETURNING VALUE(rv_result) TYPE string RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_objects_factory DEFINITION CREATE PRIVATE FRIENDS zcl_abapgit_objects_injector . PUBLIC SECTION. CLASS-METHODS get_gui_jumper RETURNING VALUE(ri_gui_jumper) TYPE REF TO zif_abapgit_gui_jumper . PROTECTED SECTION. PRIVATE SECTION. CLASS-DATA gi_gui_jumper TYPE REF TO zif_abapgit_gui_jumper . ENDCLASS. CLASS zcl_abapgit_objects_files DEFINITION CREATE PRIVATE. PUBLIC SECTION. CLASS-METHODS new IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_path TYPE string OPTIONAL RETURNING VALUE(ro_files) TYPE REF TO zcl_abapgit_objects_files. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_path TYPE string OPTIONAL . METHODS add_string IMPORTING !iv_extra TYPE clike OPTIONAL !iv_ext TYPE string !iv_string TYPE string RAISING zcx_abapgit_exception . METHODS read_string IMPORTING !iv_extra TYPE clike OPTIONAL !iv_ext TYPE string RETURNING VALUE(rv_string) TYPE string RAISING zcx_abapgit_exception . METHODS add_xml IMPORTING !iv_extra TYPE clike OPTIONAL !ii_xml TYPE REF TO zif_abapgit_xml_output !iv_normalize TYPE abap_bool DEFAULT abap_true !is_metadata TYPE zif_abapgit_definitions=>ty_metadata OPTIONAL RAISING zcx_abapgit_exception . METHODS read_xml IMPORTING !iv_extra TYPE clike OPTIONAL RETURNING VALUE(ri_xml) TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception . METHODS read_abap IMPORTING !iv_extra TYPE clike OPTIONAL !iv_error TYPE abap_bool DEFAULT abap_true RETURNING VALUE(rt_abap) TYPE abaptxt255_tab RAISING zcx_abapgit_exception . METHODS add_abap IMPORTING !iv_extra TYPE clike OPTIONAL !it_abap TYPE STANDARD TABLE RAISING zcx_abapgit_exception . METHODS add IMPORTING !is_file TYPE zif_abapgit_git_definitions=>ty_file . METHODS add_raw IMPORTING !iv_extra TYPE clike OPTIONAL !iv_ext TYPE string !iv_data TYPE xstring. METHODS read_raw IMPORTING !iv_extra TYPE clike OPTIONAL !iv_ext TYPE string RETURNING VALUE(rv_data) TYPE xstring RAISING zcx_abapgit_exception . METHODS get_files RETURNING VALUE(rt_files) TYPE zif_abapgit_git_definitions=>ty_files_tt . METHODS set_files IMPORTING !it_files TYPE zif_abapgit_git_definitions=>ty_files_tt . METHODS get_accessed_files RETURNING VALUE(rt_files) TYPE zif_abapgit_git_definitions=>ty_file_signatures_tt . METHODS contains_file IMPORTING !iv_extra TYPE clike OPTIONAL !iv_ext TYPE string RETURNING VALUE(rv_present) TYPE abap_bool . METHODS get_file_pattern RETURNING VALUE(rv_pattern) TYPE string . METHODS is_json_metadata RETURNING VALUE(rv_result) TYPE abap_bool. METHODS add_i18n_file IMPORTING !ii_i18n_file TYPE REF TO zif_abapgit_i18n_file RAISING zcx_abapgit_exception . METHODS read_i18n_files RETURNING VALUE(rt_i18n_files) TYPE zif_abapgit_i18n_file=>ty_table_of RAISING zcx_abapgit_exception . PROTECTED SECTION. METHODS read_file IMPORTING !iv_filename TYPE string !iv_error TYPE abap_bool DEFAULT abap_true RETURNING VALUE(rv_data) TYPE xstring RAISING zcx_abapgit_exception . PRIVATE SECTION. DATA ms_item TYPE zif_abapgit_definitions=>ty_item . DATA mt_accessed_files TYPE zif_abapgit_git_definitions=>ty_file_signatures_tt . DATA mt_files TYPE zif_abapgit_git_definitions=>ty_files_tt . DATA mv_path TYPE string . METHODS mark_accessed IMPORTING !iv_path TYPE zif_abapgit_git_definitions=>ty_file-path !iv_file TYPE zif_abapgit_git_definitions=>ty_file-filename !iv_sha1 TYPE zif_abapgit_git_definitions=>ty_file-sha1. ENDCLASS. CLASS zcl_abapgit_objects_generic DEFINITION CREATE PUBLIC . PUBLIC SECTION. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras DEFAULT sy-langu io_field_rules TYPE REF TO zif_abapgit_field_rules OPTIONAL RAISING zcx_abapgit_exception . METHODS delete IMPORTING !iv_package TYPE devclass RAISING zcx_abapgit_exception . METHODS deserialize IMPORTING !iv_package TYPE devclass !io_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception . METHODS exists RETURNING VALUE(rv_bool) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS serialize IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . PROTECTED SECTION. TYPES: BEGIN OF ty_s_objkey, num TYPE n LENGTH 3, value TYPE c LENGTH 128, END OF ty_s_objkey . TYPES: ty_t_objkey TYPE SORTED TABLE OF ty_s_objkey WITH UNIQUE KEY num . DATA ms_object_header TYPE objh . DATA: mt_object_table TYPE STANDARD TABLE OF objsl WITH DEFAULT KEY . DATA: mt_object_method TYPE STANDARD TABLE OF objm WITH DEFAULT KEY . DATA ms_item TYPE zif_abapgit_definitions=>ty_item . DATA mv_language TYPE spras . METHODS after_import . METHODS before_export . METHODS corr_insert IMPORTING !iv_package TYPE devclass RAISING zcx_abapgit_exception . METHODS deserialize_data IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_input !iv_package TYPE devclass RAISING zcx_abapgit_exception . METHODS distribute_name_to_components IMPORTING !it_key_component TYPE ddfields CHANGING !ct_objkey TYPE ty_t_objkey !cs_objkey TYPE ty_s_objkey !cv_non_value_pos TYPE numc3 . METHODS get_key_fields IMPORTING !iv_table TYPE objsl-tobj_name RETURNING VALUE(rt_keys) TYPE ddfields RAISING zcx_abapgit_exception . METHODS get_primary_table RETURNING VALUE(rv_table) TYPE objsl-tobj_name RAISING zcx_abapgit_exception . METHODS get_where_clause IMPORTING !iv_tobj_name TYPE objsl-tobj_name RETURNING VALUE(rv_where) TYPE string RAISING zcx_abapgit_exception . METHODS serialize_data IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS split_value_to_keys IMPORTING !it_key_component TYPE ddfields CHANGING !ct_objkey TYPE ty_t_objkey !cs_objkey TYPE ty_s_objkey !cv_non_value_pos TYPE numc3 . METHODS validate IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception . PRIVATE SECTION. DATA mo_field_rules TYPE REF TO zif_abapgit_field_rules . METHODS apply_clear_logic IMPORTING !iv_table TYPE objsl-tobj_name CHANGING !ct_data TYPE STANDARD TABLE . METHODS apply_fill_logic IMPORTING !iv_table TYPE objsl-tobj_name !iv_package TYPE devclass CHANGING !ct_data TYPE STANDARD TABLE . ENDCLASS. CLASS zcl_abapgit_objects_injector DEFINITION CREATE PRIVATE . PUBLIC SECTION. CLASS-METHODS set_gui_jumper IMPORTING !ii_gui_jumper TYPE REF TO zif_abapgit_gui_jumper . PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_objects_super DEFINITION ABSTRACT CREATE PUBLIC. PUBLIC SECTION. CONSTANTS c_user_unknown TYPE syuname VALUE 'UNKNOWN'. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL. METHODS get_accessed_files RETURNING VALUE(rt_files) TYPE zif_abapgit_git_definitions=>ty_file_signatures_tt. PROTECTED SECTION. DATA: ms_item TYPE zif_abapgit_definitions=>ty_item, mv_language TYPE spras, mo_files TYPE REF TO zcl_abapgit_objects_files, mo_i18n_params TYPE REF TO zcl_abapgit_i18n_params. METHODS get_metadata RETURNING VALUE(rs_metadata) TYPE zif_abapgit_definitions=>ty_metadata . METHODS corr_insert IMPORTING !iv_package TYPE devclass !ig_object_class TYPE any OPTIONAL RAISING zcx_abapgit_exception . METHODS tadir_insert IMPORTING !iv_package TYPE devclass RAISING zcx_abapgit_exception . METHODS tadir_delete RAISING zcx_abapgit_exception . METHODS exists_a_lock_entry_for IMPORTING !iv_lock_object TYPE string !iv_argument TYPE csequence OPTIONAL !iv_prefix TYPE csequence OPTIONAL RETURNING VALUE(rv_exists_a_lock_entry) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS set_default_package IMPORTING !iv_package TYPE devclass . METHODS set_default_transport IMPORTING !iv_transport TYPE trkorr. METHODS serialize_longtexts IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output !iv_longtext_id TYPE dokil-id OPTIONAL !it_dokil TYPE zif_abapgit_definitions=>ty_dokil_tt OPTIONAL !iv_longtext_name TYPE string DEFAULT 'LONGTEXTS' RAISING zcx_abapgit_exception . METHODS deserialize_longtexts IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_input !iv_longtext_id TYPE dokil-id OPTIONAL !iv_longtext_name TYPE string DEFAULT 'LONGTEXTS' RAISING zcx_abapgit_exception . METHODS delete_longtexts IMPORTING !iv_longtext_id TYPE dokil-id RAISING zcx_abapgit_exception . METHODS is_active RETURNING VALUE(rv_active) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS delete_ddic IMPORTING !iv_objtype TYPE string !iv_no_ask TYPE abap_bool DEFAULT abap_true !iv_no_ask_delete_append TYPE abap_bool DEFAULT abap_false RAISING zcx_abapgit_exception . METHODS get_abap_language_version RETURNING VALUE(rv_abap_language_version) TYPE uccheck RAISING zcx_abapgit_exception . METHODS set_abap_language_version CHANGING !cv_abap_language_version TYPE uccheck RAISING zcx_abapgit_exception . METHODS clear_abap_language_version CHANGING !cv_abap_language_version TYPE uccheck RAISING zcx_abapgit_exception . PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_objects_program DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. TYPES: BEGIN OF ty_cua, adm TYPE rsmpe_adm, sta TYPE STANDARD TABLE OF rsmpe_stat WITH DEFAULT KEY, fun TYPE STANDARD TABLE OF rsmpe_funt WITH DEFAULT KEY, men TYPE STANDARD TABLE OF rsmpe_men WITH DEFAULT KEY, mtx TYPE STANDARD TABLE OF rsmpe_mnlt WITH DEFAULT KEY, act TYPE STANDARD TABLE OF rsmpe_act WITH DEFAULT KEY, but TYPE STANDARD TABLE OF rsmpe_but WITH DEFAULT KEY, pfk TYPE STANDARD TABLE OF rsmpe_pfk WITH DEFAULT KEY, set TYPE STANDARD TABLE OF rsmpe_staf WITH DEFAULT KEY, doc TYPE STANDARD TABLE OF rsmpe_atrt WITH DEFAULT KEY, tit TYPE STANDARD TABLE OF rsmpe_titt WITH DEFAULT KEY, biv TYPE STANDARD TABLE OF rsmpe_buts WITH DEFAULT KEY, END OF ty_cua. METHODS serialize_program IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_output OPTIONAL !is_item TYPE zif_abapgit_definitions=>ty_item !io_files TYPE REF TO zcl_abapgit_objects_files !iv_program TYPE syrepid OPTIONAL !iv_extra TYPE clike OPTIONAL RAISING zcx_abapgit_exception. METHODS deserialize_program IMPORTING !is_progdir TYPE zif_abapgit_sap_report=>ty_progdir !it_source TYPE abaptxt255_tab !it_tpool TYPE textpool_table !iv_package TYPE devclass RAISING zcx_abapgit_exception. PROTECTED SECTION. TYPES: ty_spaces_tt TYPE STANDARD TABLE OF i WITH DEFAULT KEY . TYPES: BEGIN OF ty_dynpro, header TYPE rpy_dyhead, containers TYPE dycatt_tab, fields TYPE dyfatc_tab, flow_logic TYPE swydyflow, spaces TYPE ty_spaces_tt, nat_header TYPE d020s, nat_fields TYPE STANDARD TABLE OF d021s WITH DEFAULT KEY, nat_texts TYPE STANDARD TABLE OF d021t WITH DEFAULT KEY, END OF ty_dynpro . TYPES: ty_dynpro_tt TYPE STANDARD TABLE OF ty_dynpro WITH DEFAULT KEY . METHODS strip_generation_comments CHANGING ct_source TYPE STANDARD TABLE. " tab of string or charX METHODS serialize_dynpros IMPORTING !iv_program_name TYPE syrepid RETURNING VALUE(rt_dynpro) TYPE ty_dynpro_tt RAISING zcx_abapgit_exception . METHODS serialize_cua IMPORTING !iv_program_name TYPE syrepid RETURNING VALUE(rs_cua) TYPE ty_cua RAISING zcx_abapgit_exception . METHODS deserialize_dynpros IMPORTING !it_dynpros TYPE ty_dynpro_tt RAISING zcx_abapgit_exception . METHODS deserialize_textpool IMPORTING !iv_program TYPE syrepid !it_tpool TYPE textpool_table !iv_language TYPE sy-langu OPTIONAL !iv_is_include TYPE abap_bool DEFAULT abap_false RAISING zcx_abapgit_exception . METHODS deserialize_cua IMPORTING !iv_program_name TYPE syrepid !is_cua TYPE ty_cua RAISING zcx_abapgit_exception . METHODS is_any_dynpro_locked IMPORTING !iv_program TYPE syrepid RETURNING VALUE(rv_is_any_dynpro_locked) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS is_cua_locked IMPORTING !iv_program TYPE syrepid RETURNING VALUE(rv_is_cua_locked) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS is_text_locked IMPORTING !iv_program TYPE syrepid RETURNING VALUE(rv_is_text_locked) TYPE abap_bool RAISING zcx_abapgit_exception . CLASS-METHODS add_tpool IMPORTING !it_tpool TYPE textpool_table RETURNING VALUE(rt_tpool) TYPE zif_abapgit_lang_definitions=>ty_tpool_tt. CLASS-METHODS read_tpool IMPORTING !it_tpool TYPE zif_abapgit_lang_definitions=>ty_tpool_tt RETURNING VALUE(rt_tpool) TYPE textpool_table. PRIVATE SECTION. CONSTANTS: BEGIN OF c_state, active TYPE r3state VALUE 'A', inactive TYPE r3state VALUE 'I', END OF c_state. CONSTANTS c_native_dynpro TYPE c LENGTH 2 VALUE 'IN'. METHODS: uncondense_flow IMPORTING it_flow TYPE swydyflow it_spaces TYPE ty_spaces_tt RETURNING VALUE(rt_flow) TYPE swydyflow. CLASS-METHODS auto_correct_cua_adm IMPORTING is_cua TYPE ty_cua CHANGING cs_adm TYPE rsmpe_adm. METHODS get_program_title IMPORTING !it_tpool TYPE textpool_table RETURNING VALUE(rv_title) TYPE repti . METHODS insert_program IMPORTING !is_progdir TYPE zif_abapgit_sap_report=>ty_progdir !it_source TYPE abaptxt255_tab !iv_title TYPE repti !iv_package TYPE devclass !iv_state TYPE progdir-state DEFAULT c_state-inactive RAISING zcx_abapgit_exception . METHODS update_program IMPORTING !is_progdir TYPE zif_abapgit_sap_report=>ty_progdir !it_source TYPE abaptxt255_tab !iv_title TYPE repti !iv_state TYPE progdir-state DEFAULT c_state-inactive RAISING zcx_abapgit_exception . METHODS is_exit_include IMPORTING !iv_program TYPE syrepid RETURNING VALUE(rv_is_exit_include) TYPE abap_bool. METHODS deserialize_exit_include IMPORTING !is_progdir TYPE zif_abapgit_sap_report=>ty_progdir !it_source TYPE abaptxt255_tab !it_tpool TYPE textpool_table !iv_package TYPE devclass RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_acid DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. METHODS: create_object RETURNING VALUE(ro_aab) TYPE REF TO cl_aab_id RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_aifc DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. TYPES: BEGIN OF ty_aif_key_s, ns TYPE c LENGTH 6, ifname TYPE c LENGTH 10, ifver TYPE c LENGTH 5, END OF ty_aif_key_s. TYPES: BEGIN OF ty_icd_data_key_s, depl_scenario TYPE c LENGTH 20, ns TYPE c LENGTH 6, ifname TYPE c LENGTH 10, ifver2 TYPE c LENGTH 4, END OF ty_icd_data_key_s. TYPES: BEGIN OF ty_icd_data_key, depl_scenario TYPE c LENGTH 20, ns TYPE c LENGTH 6, ifname TYPE c LENGTH 10, ifver2 TYPE c LENGTH 4, ifver TYPE c LENGTH 5, END OF ty_icd_data_key. TYPES: BEGIN OF ty_table_data_s, tabname TYPE tabname, table_data TYPE REF TO data, END OF ty_table_data_s. TYPES: ty_table_data_t TYPE SORTED TABLE OF ty_table_data_s WITH UNIQUE KEY tabname. DATA ms_icd_data_key TYPE ty_icd_data_key_s. METHODS handle_table_data IMPORTING !iv_tabname TYPE tabname !it_data TYPE STANDARD TABLE RAISING zcx_abapgit_exception. METHODS clear_client CHANGING !ct_data TYPE STANDARD TABLE RAISING zcx_abapgit_exception. METHODS authorization_check RETURNING VALUE(rv_success) TYPE abap_bool RAISING zcx_abapgit_exception. METHODS get_content_compress IMPORTING !io_log TYPE REF TO zif_abapgit_log !is_ifkeys TYPE ty_aif_key_s !iv_package TYPE devclass RAISING zcx_abapgit_exception. METHODS validate_interface IMPORTING !is_ifkeys TYPE ty_aif_key_s RETURNING VALUE(rv_success) TYPE abap_bool RAISING zcx_abapgit_exception. METHODS compress_interface IMPORTING !is_ifkeys TYPE ty_aif_key_s RETURNING VALUE(rv_success) TYPE abap_bool RAISING zcx_abapgit_exception. METHODS execute_checks IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_input RETURNING VALUE(rv_success) TYPE abap_bool RAISING zcx_abapgit_exception. PRIVATE SECTION. TYPES: BEGIN OF ty_content_s, tabname TYPE tabname, END OF ty_content_s. TYPES: ty_content_t TYPE STANDARD TABLE OF ty_content_s WITH NON-UNIQUE DEFAULT KEY. DATA mo_abapgit_util TYPE REF TO object. ENDCLASS. CLASS zcl_abapgit_object_amsd DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. METHODS: clear_fields CHANGING cs_logical_db_schema TYPE any, clear_field IMPORTING iv_fieldname TYPE csequence CHANGING cs_logical_db_schema TYPE any, fill_metadata_from_db CHANGING cs_logical_db_schema TYPE any RAISING zcx_abapgit_exception, get_wb_object_operator RETURNING VALUE(ri_wb_object_operator) TYPE REF TO object RAISING zcx_abapgit_exception. DATA: mr_logical_db_schema TYPE REF TO data, mv_logical_db_schema_key TYPE seu_objkey, mi_persistence TYPE REF TO if_wb_object_persist, mi_wb_object_operator TYPE REF TO object. ENDCLASS. CLASS zcl_abapgit_object_apis DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_model TYPE string VALUE 'ARS_S_API_ABAPGIT'. DATA mo_handler TYPE REF TO object. METHODS initialize. ENDCLASS. CLASS zcl_abapgit_object_common_aff DEFINITION INHERITING FROM zcl_abapgit_objects_super ABSTRACT CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object ABSTRACT METHODS changed_by . METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. TYPES: BEGIN OF ty_extension_mapper_pair, "! file extension extension TYPE string, "! instance of {@link CL_AFF_FILE_NAME_MAPPER} providing file names for file extensions file_name_mapper TYPE REF TO object, END OF ty_extension_mapper_pair, ty_extension_mapper_pairs TYPE STANDARD TABLE OF ty_extension_mapper_pair WITH DEFAULT KEY. "! Delivers other file extensions than json to be considered at serialize or deserialize of an object METHODS get_additional_extensions RETURNING VALUE(rv_additional_extensions) TYPE ty_extension_mapper_pairs ##NEEDED. "! Delivers an instance of AFF object handler ({@link IF_AFF_OBJECT_HANDLER}) METHODS get_object_handler RETURNING VALUE(ro_object_handler) TYPE REF TO object. METHODS create_aff_setting_deserialize FINAL RETURNING VALUE(ro_settings_deserialize) TYPE REF TO object RAISING zcx_abapgit_exception. PRIVATE SECTION. METHODS is_file_empty IMPORTING io_object_json_file TYPE REF TO object RETURNING VALUE(rv_is_empty) TYPE abap_bool. CLASS-METHODS remove_abap_language_version IMPORTING iv_json_as_xstring TYPE xstring RETURNING VALUE(rv_json_as_xstring_wo_alv) TYPE xstring RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_aplo DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION . PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_aqbg DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . METHODS get_field_rules RETURNING VALUE(ro_result) TYPE REF TO zif_abapgit_field_rules. ENDCLASS. CLASS zcl_abapgit_object_aqqu DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . METHODS get_field_rules RETURNING VALUE(ro_result) TYPE REF TO zif_abapgit_field_rules. ENDCLASS. CLASS zcl_abapgit_object_aqsg DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . METHODS get_field_rules RETURNING VALUE(ro_result) TYPE REF TO zif_abapgit_field_rules. ENDCLASS. CLASS zcl_abapgit_object_area DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_asfc DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_auth DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. DATA: mv_fieldname TYPE authx-fieldname. ENDCLASS. CLASS zcl_abapgit_object_avar DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. METHODS: create_object RETURNING VALUE(ro_aab_var) TYPE REF TO cl_aab_variant RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_avas DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. TYPES: BEGIN OF ty_header, guid TYPE guid_32, attribute TYPE cls_attribute_name, object TYPE pak_object_key, END OF ty_header . TYPES: BEGIN OF ty_avas, header TYPE ty_header, values TYPE cls_value_assignments, links TYPE cls_linked_objects, END OF ty_avas . METHODS insert_assignments IMPORTING !is_avas TYPE ty_avas RAISING zcx_abapgit_exception . METHODS instantiate RETURNING VALUE(ro_avas) TYPE REF TO cl_cls_attr_value_assignment RAISING zcx_abapgit_exception . PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_bdef DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. DATA mi_persistence TYPE REF TO if_wb_object_persist . DATA mi_wb_object_operator TYPE REF TO object . DATA mv_behaviour_definition_key TYPE seu_objkey . DATA mr_behaviour_definition TYPE REF TO data . METHODS clear_fields CHANGING !cs_metadata TYPE any . METHODS clear_field IMPORTING !iv_fieldname TYPE csequence CHANGING !cs_metadata TYPE any . METHODS get_wb_object_operator RETURNING VALUE(ri_wb_object_operator) TYPE REF TO object RAISING zcx_abapgit_exception . METHODS merge_object_data IMPORTING !io_object_data TYPE REF TO object RETURNING VALUE(ro_object_data_merged) TYPE REF TO if_wb_object_data_model RAISING zcx_abapgit_exception . METHODS get_object_data IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_input RETURNING VALUE(ro_object_data) TYPE REF TO if_wb_object_data_model RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_bgqc DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC. PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION. PRIVATE SECTION. CONSTANTS c_bgqc_name TYPE string VALUE 'BGQCNAME'. CONSTANTS c_bgqc_wbi_p TYPE string VALUE 'CL_BGQC_WBI_P'. CONSTANTS c_select_changed_by TYPE string VALUE 'IF_BGQC_WBI_P~SELECT_CHANGED_BY'. ENDCLASS. CLASS zcl_abapgit_object_cdbo DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. METHODS zif_abapgit_object~changed_by REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_table_name TYPE tabname VALUE 'CDB_OBJH' ##NO_TEXT. ENDCLASS. CLASS zcl_abapgit_object_char DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_char, cls_attribute TYPE cls_attribute, cls_attributet TYPE STANDARD TABLE OF cls_attributet WITH DEFAULT KEY, cls_attr_value TYPE STANDARD TABLE OF cls_attr_value WITH DEFAULT KEY, cls_attr_valuet TYPE STANDARD TABLE OF cls_attr_valuet WITH DEFAULT KEY, END OF ty_char . CONSTANTS c_longtext_id_char TYPE dokil-id VALUE 'CH'. METHODS instantiate_char_and_lock IMPORTING !iv_type_group TYPE cls_object_type_group !iv_activation_state TYPE pak_activation_state RETURNING VALUE(ro_char) TYPE REF TO cl_cls_attribute RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_chdo DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. METHODS after_import RAISING zcx_abapgit_exception . METHODS delete_tadir_cdnames IMPORTING !is_cdnames TYPE cdnames RAISING zcx_abapgit_exception . METHODS delete_tadir_tabl IMPORTING !is_tcdrs TYPE tcdrs RAISING zcx_abapgit_exception . PRIVATE SECTION. CONSTANTS c_class_gen_marker TYPE string VALUE '*CLASS_GEN*'. TYPES: BEGIN OF ty_change_document, reports_generated TYPE SORTED TABLE OF tcdrps WITH UNIQUE KEY object reportname, objects TYPE SORTED TABLE OF tcdobs WITH UNIQUE KEY object tabname, objects_text TYPE SORTED TABLE OF tcdobts WITH UNIQUE KEY spras object, END OF ty_change_document. DATA: mv_object TYPE cdobjectcl. ENDCLASS. CLASS zcl_abapgit_object_chkc DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_chko DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC. PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_chkv DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC. PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_clas DEFINITION INHERITING FROM zcl_abapgit_objects_program CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. DATA: mi_object_oriented_object_fct TYPE REF TO zif_abapgit_oo_object_fnc, mv_skip_testclass TYPE abap_bool, mv_classpool_name TYPE progname. METHODS: deserialize_abap IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_input iv_package TYPE devclass RAISING zcx_abapgit_exception, deserialize_descr IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception, deserialize_docu IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception, deserialize_tpool IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception, deserialize_tpool_i18n IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception, deserialize_sotr IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_input iv_package TYPE devclass RAISING zcx_abapgit_exception, deserialize_exceptions IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception, serialize_xml IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception, serialize_attr IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output !iv_clsname TYPE seoclsname RAISING zcx_abapgit_exception, serialize_descr_class IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output !iv_clsname TYPE seoclsname RAISING zcx_abapgit_exception, serialize_descr_compo IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output !iv_clsname TYPE seoclsname RAISING zcx_abapgit_exception, serialize_descr_subco IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output !iv_clsname TYPE seoclsname RAISING zcx_abapgit_exception, serialize_docu IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output !it_langu_additional TYPE zif_abapgit_lang_definitions=>ty_langus OPTIONAL !iv_clsname TYPE seoclsname RAISING zcx_abapgit_exception, serialize_tpool IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output !iv_clsname TYPE seoclsname RETURNING VALUE(rt_tpool) TYPE textpool_table RAISING zcx_abapgit_exception, serialize_tpool_i18n IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output !it_langu_additional TYPE zif_abapgit_lang_definitions=>ty_langus OPTIONAL !iv_clsname TYPE seoclsname !it_tpool_main TYPE textpool_table RAISING zcx_abapgit_exception, serialize_sotr IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception, source_apack_replacement CHANGING !ct_source TYPE seop_source_string RAISING zcx_abapgit_exception, repo_apack_replacement CHANGING !ct_source TYPE seop_source_string RAISING zcx_abapgit_exception. PRIVATE SECTION. CONSTANTS: BEGIN OF c_longtext_name, attributes TYPE string VALUE 'LONGTEXTS_CA', methods TYPE string VALUE 'LONGTEXTS_CO', events TYPE string VALUE 'LONGTEXTS_CE', types TYPE string VALUE 'LONGTEXTS_CT', END OF c_longtext_name. CONSTANTS: BEGIN OF c_longtext_id, class TYPE dokil-id VALUE 'CL', attributes TYPE dokil-id VALUE 'CA', methods TYPE dokil-id VALUE 'CO', events TYPE dokil-id VALUE 'CE', types TYPE dokil-id VALUE 'CT', END OF c_longtext_id. METHODS deserialize_pre_ddic IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_input !iv_package TYPE devclass RAISING zcx_abapgit_exception. METHODS is_class_locked RETURNING VALUE(rv_is_class_locked) TYPE abap_bool RAISING zcx_abapgit_exception. METHODS interface_replacement IMPORTING !iv_from_interface TYPE seoclsname !iv_to_interface TYPE seoclsname CHANGING !ct_source TYPE seop_source_string. ENDCLASS. CLASS zcl_abapgit_object_cmod DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_cmpt DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. DATA: mo_cmp_db TYPE REF TO object, mv_name TYPE c LENGTH 30. ENDCLASS. CLASS zcl_abapgit_object_cota DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION. METHODS zif_abapgit_object~delete REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_cus0 DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. TYPES: ty_img_activity_texts TYPE STANDARD TABLE OF cus_imgact WITH NON-UNIQUE DEFAULT KEY, BEGIN OF ty_img_activity, header TYPE cus_imgach, texts TYPE ty_img_activity_texts, END OF ty_img_activity. DATA: mv_img_activity TYPE cus_img_ac. ENDCLASS. CLASS zcl_abapgit_object_cus1 DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. TYPES: ty_activity_titles TYPE STANDARD TABLE OF cus_actt WITH NON-UNIQUE DEFAULT KEY, ty_objects TYPE STANDARD TABLE OF cus_actobj WITH NON-UNIQUE DEFAULT KEY, ty_objects_title TYPE STANDARD TABLE OF cus_actobt WITH NON-UNIQUE DEFAULT KEY, BEGIN OF ty_customzing_activity, activity_header TYPE cus_acth, activity_customer_exit TYPE cus_actext, activity_title TYPE ty_activity_titles, objects TYPE ty_objects, objects_title TYPE ty_objects_title, END OF ty_customzing_activity. DATA: mv_customizing_activity TYPE cus_img_ac. ENDCLASS. CLASS zcl_abapgit_object_cus2 DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. TYPES: ty_attribute_titles TYPE STANDARD TABLE OF cus_atrt WITH NON-UNIQUE DEFAULT KEY, ty_attribute_countries TYPE STANDARD TABLE OF cus_atrcou WITH NON-UNIQUE DEFAULT KEY, ty_attribute_components TYPE STANDARD TABLE OF tfm18 WITH NON-UNIQUE DEFAULT KEY, ty_attribute_comp_variants TYPE STANDARD TABLE OF cus_atrvco WITH NON-UNIQUE DEFAULT KEY. TYPES: BEGIN OF ty_customizing_attribute, header TYPE cus_atrh, titles TYPE ty_attribute_titles, countries TYPE ty_attribute_countries, components TYPE ty_attribute_components, components_variants TYPE ty_attribute_comp_variants, END OF ty_customizing_attribute. DATA: mv_img_attribute TYPE cus_atr. ENDCLASS. CLASS zcl_abapgit_object_dcls DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. DATA: mo_dcl_handler TYPE REF TO object. " CL_ACM_DCL_HANDLER METHODS clear_fields CHANGING !cg_data TYPE any RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_ddls DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. METHODS open_adt_stob IMPORTING iv_ddls_name TYPE tadir-obj_name RAISING zcx_abapgit_exception. PRIVATE SECTION. DATA mo_ddl_handler TYPE REF TO object. " CL_DD_DDL_HANDLER METHODS is_baseinfo_supported RETURNING VALUE(rv_supported) TYPE abap_bool . METHODS read_baseinfo RETURNING VALUE(rv_baseinfo_string) TYPE string. METHODS format_source_before_serialize CHANGING cv_string TYPE string. METHODS clear_fields CHANGING !cg_data TYPE any RAISING zcx_abapgit_exception. METHODS get_log_uuid RETURNING VALUE(rv_log_uuid) TYPE sysuuid_c32. ENDCLASS. CLASS zcl_abapgit_object_ddlx DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. DATA mi_persistence TYPE REF TO if_wb_object_persist. DATA mi_data_model TYPE REF TO if_wb_object_data_model. DATA mv_object_key TYPE seu_objkey. METHODS clear_fields CHANGING !cg_data TYPE any RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_desd DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC. PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION. METHODS zif_abapgit_object~get_deserialize_steps REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. METHODS _create_les_handler IMPORTING iv_desd_name TYPE sobj_name RETURNING VALUE(ro_handler) TYPE REF TO object. ENDCLASS. CLASS zcl_abapgit_object_devc DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES: zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. DATA mv_local_devclass TYPE devclass . METHODS get_package RETURNING VALUE(ri_package) TYPE REF TO if_package RAISING zcx_abapgit_exception . METHODS update_pinf_usages IMPORTING !ii_package TYPE REF TO if_package !it_usage_data TYPE scomppdata RAISING zcx_abapgit_exception . METHODS set_lock IMPORTING !ii_package TYPE REF TO if_package !iv_lock TYPE abap_bool RAISING zcx_abapgit_exception . METHODS unlock_and_raise_error IMPORTING !ii_package TYPE REF TO if_package RAISING zcx_abapgit_exception . METHODS is_empty IMPORTING !iv_package_name TYPE devclass RETURNING VALUE(rv_is_empty) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS load_package IMPORTING !iv_package_name TYPE devclass RETURNING VALUE(ri_package) TYPE REF TO if_package RAISING zcx_abapgit_exception . METHODS is_local IMPORTING !iv_package_name TYPE devclass RETURNING VALUE(rv_is_local) TYPE abap_bool . METHODS remove_obsolete_tadir IMPORTING !iv_package_name TYPE devclass RAISING zcx_abapgit_exception. METHODS adjust_sw_component CHANGING cv_dlvunit TYPE dlvunit. ENDCLASS. CLASS zcl_abapgit_object_dial DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_dialog_module, tdct TYPE tdct, dia_pars TYPE STANDARD TABLE OF diapar WITH NON-UNIQUE DEFAULT KEY, END OF ty_dialog_module. METHODS: _read_tdct RETURNING VALUE(rs_tdct) TYPE tdct. ENDCLASS. CLASS zcl_abapgit_object_doct DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_id TYPE dokhl-id VALUE 'TX' ##NO_TEXT. CONSTANTS c_name TYPE string VALUE 'DOC' ##NO_TEXT. DATA mi_longtexts TYPE REF TO zif_abapgit_longtexts . ENDCLASS. CLASS zcl_abapgit_object_docv DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_data, doctitle TYPE dsyst-doktitle, head TYPE thead, lines TYPE tline_tab, END OF ty_data. CONSTANTS c_typ TYPE dokhl-typ VALUE 'E' ##NO_TEXT. CONSTANTS c_version TYPE dokhl-dokversion VALUE '0001' ##NO_TEXT. CONSTANTS c_name TYPE string VALUE 'DOC' ##NO_TEXT. DATA mv_id TYPE dokhl-id. DATA mv_doc_object TYPE dokhl-object. METHODS read RETURNING VALUE(rs_data) TYPE ty_data. ENDCLASS. CLASS zcl_abapgit_object_doma DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_dd01_text, ddlanguage TYPE dd01v-ddlanguage, ddtext TYPE dd01v-ddtext, END OF ty_dd01_text . TYPES: BEGIN OF ty_dd07_text, valpos TYPE dd07v-valpos, ddlanguage TYPE dd07v-ddlanguage, domvalue_l TYPE dd07v-domvalue_l, domvalue_h TYPE dd07v-domvalue_h, ddtext TYPE dd07v-ddtext, domval_ld TYPE dd07v-domval_ld, domval_hd TYPE dd07v-domval_hd, END OF ty_dd07_text . TYPES: ty_dd01_texts TYPE STANDARD TABLE OF ty_dd01_text . TYPES: ty_dd07_texts TYPE STANDARD TABLE OF ty_dd07_text . " Fields that are not part of dd01v TYPES: BEGIN OF ty_extra, abap_language_version TYPE c LENGTH 1, END OF ty_extra. CONSTANTS c_longtext_id_doma TYPE dokil-id VALUE 'DO' ##NO_TEXT. METHODS serialize_texts IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output !it_dd07v TYPE dd07v_tab RAISING zcx_abapgit_exception . METHODS deserialize_texts IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_input !is_dd01v TYPE dd01v !it_dd07v TYPE dd07v_tab RAISING zcx_abapgit_exception . METHODS handle_dependencies IMPORTING !iv_step TYPE zif_abapgit_objects=>ty_deserialization_step CHANGING !cv_exit TYPE dd01v-convexit !cv_done TYPE abap_bool. METHODS adjust_exit CHANGING !cv_exit TYPE dd01v-convexit. METHODS check_exit IMPORTING !iv_exit TYPE dd01v-convexit RETURNING VALUE(rv_done) TYPE abap_bool. ENDCLASS. CLASS zcl_abapgit_object_dras DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION. METHODS zif_abapgit_object~get_deserialize_steps REDEFINITION. PROTECTED SECTION. METHODS get_additional_extensions REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_drty DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION. METHODS zif_abapgit_object~get_deserialize_steps REDEFINITION. PROTECTED SECTION. METHODS get_additional_extensions REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_drul DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. METHODS: clear_fields CHANGING cs_dependency_rule TYPE any, clear_field IMPORTING iv_fieldname TYPE csequence CHANGING cs_dependency_rule TYPE any, fill_metadata_from_db CHANGING cs_dependency_rule TYPE any RAISING zcx_abapgit_exception, get_wb_object_operator RETURNING VALUE(ri_wb_object_operator) TYPE REF TO object RAISING zcx_abapgit_exception. DATA: mr_dependency_rule TYPE REF TO data, mv_dependency_rule_key TYPE seu_objkey, mi_persistence TYPE REF TO if_wb_object_persist, mi_wb_object_operator TYPE REF TO object. ENDCLASS. CLASS zcl_abapgit_object_dsfd DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION. METHODS zif_abapgit_object~get_deserialize_steps REDEFINITION. PROTECTED SECTION. METHODS get_additional_extensions REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_dsfi DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION. METHODS zif_abapgit_object~get_deserialize_steps REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_dsys DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: c_typ TYPE dokhl-typ VALUE 'E', c_id TYPE dokhl-id VALUE 'HY'. DATA: mv_doc_object TYPE sobj_name. TYPES: BEGIN OF ty_data, doctitle TYPE dsyst-doktitle, head TYPE thead, lines TYPE tline_tab, END OF ty_data. METHODS deserialize_dsys IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception. METHODS get_main_lang RETURNING VALUE(rv_language) TYPE spras. ENDCLASS. CLASS zcl_abapgit_object_dtdc DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. METHODS: clear_fields CHANGING cs_dynamic_cache TYPE any, clear_field IMPORTING iv_fieldname TYPE csequence CHANGING cs_dynamic_cache TYPE any, fill_metadata_from_db CHANGING cs_dynamic_cache TYPE any RAISING zcx_abapgit_exception, get_wb_object_operator RETURNING VALUE(ri_wb_object_operator) TYPE REF TO object RAISING zcx_abapgit_exception, has_own_wb_data_class RETURNING VALUE(rv_supported) TYPE abap_bool. DATA: mr_dynamic_cache TYPE REF TO data, mv_dynamic_cache_key TYPE seu_objkey, mv_has_own_wb_data_class TYPE abap_bool, mi_persistence TYPE REF TO if_wb_object_persist, mi_wb_object_operator TYPE REF TO object. ENDCLASS. CLASS zcl_abapgit_object_dteb DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION . METHODS zif_abapgit_object~get_deserialize_steps REDEFINITION. PROTECTED SECTION. METHODS get_additional_extensions REDEFINITION . PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_dtel DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_dd04_text, ddlanguage TYPE dd04t-ddlanguage, ddtext TYPE dd04t-ddtext, reptext TYPE dd04t-reptext, scrtext_s TYPE dd04t-scrtext_s, scrtext_m TYPE dd04t-scrtext_m, scrtext_l TYPE dd04t-scrtext_l, END OF ty_dd04_text . TYPES: ty_dd04_texts TYPE STANDARD TABLE OF ty_dd04_text . " Fields that are not part of dd04v TYPES: BEGIN OF ty_extra, abap_language_version TYPE uccheck, END OF ty_extra. CONSTANTS c_longtext_id_dtel TYPE dokil-id VALUE 'DE' ##NO_TEXT. CONSTANTS c_longtext_id_dtel_suppl TYPE dokil-id VALUE 'DZ' ##NO_TEXT. METHODS serialize_texts IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS deserialize_texts IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_input !is_dd04v TYPE dd04v RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_ecatt_super DEFINITION INHERITING FROM zcl_abapgit_objects_super ABSTRACT CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. METHODS: get_object_type ABSTRACT RETURNING VALUE(rv_object_type) TYPE etobj_type, get_upload ABSTRACT RETURNING VALUE(ro_upload) TYPE REF TO cl_apl_ecatt_upload, get_download ABSTRACT RETURNING VALUE(ro_download) TYPE REF TO cl_apl_ecatt_download, get_lock_object ABSTRACT RETURNING VALUE(rv_lock_object) TYPE eqeobj. PRIVATE SECTION. TYPES: BEGIN OF ty_last_changed, luser TYPE syuname, ldate TYPE d, ltime TYPE t, END OF ty_last_changed. CONSTANTS: BEGIN OF c_name, version TYPE string VALUE 'VERSION' ##NO_TEXT, versions TYPE string VALUE 'VERSIONS' ##NO_TEXT, END OF c_name, c_default_version TYPE etobj_ver VALUE '1' ##NO_TEXT. CLASS-METHODS: is_change_more_recent_than IMPORTING is_currently_changed TYPE ty_last_changed is_last_changed TYPE ty_last_changed RETURNING VALUE(rv_is_change_more_recent) TYPE abap_bool. DATA: mv_object_name TYPE etobj_name. METHODS: get_changed_date IMPORTING ii_document TYPE REF TO if_ixml_document RETURNING VALUE(rv_changed_date) TYPE d, get_changed_time IMPORTING ii_document TYPE REF TO if_ixml_document RETURNING VALUE(rv_changed_time) TYPE t, get_changed_by_user IMPORTING ii_document TYPE REF TO if_ixml_document RETURNING VALUE(rv_changed_by_user) TYPE syuname, get_change_information IMPORTING is_version_info TYPE etversinfo RETURNING VALUE(rs_change_information) TYPE ty_last_changed RAISING cx_ecatt_apl zcx_abapgit_exception, clear_attributes CHANGING ci_document TYPE REF TO if_ixml_document, clear_elements CHANGING ci_document TYPE REF TO if_ixml_document, get_version_from_node IMPORTING ii_node TYPE REF TO if_ixml_node RETURNING VALUE(rv_version) TYPE string, deserialize_version IMPORTING ii_version_node TYPE REF TO if_ixml_node iv_package TYPE devclass RAISING zcx_abapgit_exception, serialize_version IMPORTING iv_version TYPE etversinfo-version CHANGING ci_node TYPE REF TO if_ixml_element RAISING cx_ecatt zcx_abapgit_exception, clear_element IMPORTING iv_name TYPE csequence CHANGING ci_document TYPE REF TO if_ixml_document, clear_element_collection IMPORTING iv_name TYPE csequence CHANGING ci_document TYPE REF TO if_ixml_document, serialize_versions IMPORTING it_version_info TYPE etversinfo_tabtype CHANGING ci_document TYPE REF TO if_ixml_document RAISING cx_ecatt zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_ecat DEFINITION INHERITING FROM zcl_abapgit_object_ecatt_super FINAL CREATE PUBLIC . PUBLIC SECTION. PROTECTED SECTION. METHODS: get_object_type REDEFINITION, get_upload REDEFINITION, get_download REDEFINITION, get_lock_object REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_ecsd DEFINITION INHERITING FROM zcl_abapgit_object_ecatt_super FINAL CREATE PUBLIC . PUBLIC SECTION. PROTECTED SECTION. METHODS: get_object_type REDEFINITION, get_upload REDEFINITION, get_download REDEFINITION, get_lock_object REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_ecsp DEFINITION INHERITING FROM zcl_abapgit_object_ecatt_super FINAL CREATE PUBLIC . PUBLIC SECTION. PROTECTED SECTION. METHODS: get_object_type REDEFINITION, get_upload REDEFINITION, get_download REDEFINITION, get_lock_object REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_ectc DEFINITION INHERITING FROM zcl_abapgit_object_ecatt_super FINAL CREATE PUBLIC . PUBLIC SECTION. PROTECTED SECTION. METHODS: get_object_type REDEFINITION, get_upload REDEFINITION, get_download REDEFINITION, get_lock_object REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_ectd DEFINITION INHERITING FROM zcl_abapgit_object_ecatt_super FINAL CREATE PUBLIC . PUBLIC SECTION. PROTECTED SECTION. METHODS: get_object_type REDEFINITION, get_upload REDEFINITION, get_download REDEFINITION, get_lock_object REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_ecvo DEFINITION INHERITING FROM zcl_abapgit_object_ecatt_super FINAL CREATE PUBLIC . PUBLIC SECTION. PROTECTED SECTION. METHODS: get_object_type REDEFINITION, get_upload REDEFINITION, get_download REDEFINITION, get_lock_object REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_eeec DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS: zif_abapgit_object~changed_by REDEFINITION . PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_enhc DEFINITION INHERITING FROM zcl_abapgit_objects_super. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. DATA: mv_composite_id TYPE enhcompositename. ENDCLASS. CLASS zcl_abapgit_object_enho_badi DEFINITION . PUBLIC SECTION. METHODS: constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_abap_language_version TYPE uccheck. INTERFACES: zif_abapgit_object_enho. PROTECTED SECTION. PRIVATE SECTION. DATA: ms_item TYPE zif_abapgit_definitions=>ty_item. DATA mv_abap_language_version TYPE uccheck. ENDCLASS. CLASS zcl_abapgit_object_enho_hook DEFINITION . PUBLIC SECTION. METHODS: constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item io_files TYPE REF TO zcl_abapgit_objects_files iv_abap_language_version TYPE uccheck. INTERFACES: zif_abapgit_object_enho. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_spaces, full_name TYPE string, spaces TYPE STANDARD TABLE OF i WITH DEFAULT KEY, END OF ty_spaces. TYPES: ty_spaces_tt TYPE STANDARD TABLE OF ty_spaces WITH DEFAULT KEY. TYPES: BEGIN OF ty_file, name TYPE string, file TYPE string, END OF ty_file. TYPES: ty_files TYPE HASHED TABLE OF ty_file WITH UNIQUE KEY name. CONSTANTS c_enhancement TYPE string VALUE 'ENHANCEMENT 0 *.' ##NO_TEXT. CONSTANTS c_endenhancement TYPE string VALUE 'ENDENHANCEMENT.' ##NO_TEXT. DATA: ms_item TYPE zif_abapgit_definitions=>ty_item. DATA: mo_files TYPE REF TO zcl_abapgit_objects_files. DATA mv_abap_language_version TYPE uccheck. METHODS add_sources CHANGING !ct_enhancements TYPE enh_hook_impl_it !ct_files TYPE ty_files RAISING zcx_abapgit_exception . METHODS read_sources CHANGING !ct_enhancements TYPE enh_hook_impl_it !ct_files TYPE ty_files RAISING zcx_abapgit_exception . METHODS hook_impl_deserialize IMPORTING !it_spaces TYPE ty_spaces_tt CHANGING !ct_impl TYPE enh_hook_impl_it RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_enho_class DEFINITION CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_abapgit_object_enho. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !io_files TYPE REF TO zcl_abapgit_objects_files iv_abap_language_version TYPE uccheck. PROTECTED SECTION. PRIVATE SECTION. CLASS-METHODS adjust_generated_comments CHANGING ct_source TYPE rswsourcet. METHODS: serialize_includes IMPORTING io_class TYPE REF TO cl_enh_tool_class RAISING zcx_abapgit_exception, deserialize_includes IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_input io_class TYPE REF TO cl_enh_tool_class RAISING zcx_abapgit_exception. DATA: ms_item TYPE zif_abapgit_definitions=>ty_item. DATA: mo_files TYPE REF TO zcl_abapgit_objects_files. DATA mv_abap_language_version TYPE uccheck. ENDCLASS. CLASS zcl_abapgit_object_enho_intf DEFINITION . PUBLIC SECTION. METHODS: constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item io_files TYPE REF TO zcl_abapgit_objects_files iv_abap_language_version TYPE uccheck. INTERFACES: zif_abapgit_object_enho. PROTECTED SECTION. PRIVATE SECTION. DATA: ms_item TYPE zif_abapgit_definitions=>ty_item, mo_files TYPE REF TO zcl_abapgit_objects_files. DATA mv_abap_language_version TYPE uccheck. ENDCLASS. CLASS zcl_abapgit_object_enho_wdyc DEFINITION . PUBLIC SECTION. METHODS: constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_abap_language_version TYPE uccheck. INTERFACES: zif_abapgit_object_enho. PROTECTED SECTION. PRIVATE SECTION. DATA: ms_item TYPE zif_abapgit_definitions=>ty_item. DATA mv_abap_language_version TYPE uccheck. ENDCLASS. CLASS zcl_abapgit_object_enho_fugr DEFINITION . PUBLIC SECTION. METHODS: constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item io_files TYPE REF TO zcl_abapgit_objects_files iv_abap_language_version TYPE uccheck. INTERFACES: zif_abapgit_object_enho. PROTECTED SECTION. PRIVATE SECTION. DATA: ms_item TYPE zif_abapgit_definitions=>ty_item, mo_files TYPE REF TO zcl_abapgit_objects_files. DATA mv_abap_language_version TYPE uccheck. ENDCLASS. CLASS zcl_abapgit_object_enho_wdyn DEFINITION . PUBLIC SECTION. METHODS: constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_abap_language_version TYPE uccheck. INTERFACES: zif_abapgit_object_enho. PROTECTED SECTION. PRIVATE SECTION. DATA: ms_item TYPE zif_abapgit_definitions=>ty_item. DATA mv_abap_language_version TYPE uccheck. ENDCLASS. CLASS zcl_abapgit_object_enho DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. METHODS: factory IMPORTING iv_tool TYPE enhtooltype iv_abap_language_version TYPE uccheck RETURNING VALUE(ri_enho) TYPE REF TO zif_abapgit_object_enho RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_enho_clif DEFINITION CREATE PUBLIC . PUBLIC SECTION. CLASS-METHODS deserialize IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_input !io_clif TYPE REF TO cl_enh_tool_clif RAISING zcx_abapgit_exception cx_enh_root . CLASS-METHODS serialize IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_output !io_clif TYPE REF TO cl_enh_tool_clif RAISING zcx_abapgit_exception . PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_enhs_badi_d DEFINITION . PUBLIC SECTION. INTERFACES: zif_abapgit_object_enhs. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_enhs_hook_d DEFINITION . PUBLIC SECTION. INTERFACES: zif_abapgit_object_enhs. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_hook_defifnition, pgmid TYPE tadir-pgmid, obj_name TYPE trobj_name, obj_type TYPE trobjtype, main_type TYPE trobjtype, main_name TYPE eu_aname, program TYPE progname, def_hooks TYPE enh_hook_def_ext_it, END OF ty_hook_defifnition. ENDCLASS. CLASS zcl_abapgit_object_enhs DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. METHODS: factory IMPORTING iv_tool TYPE enhtooltype RETURNING VALUE(ri_enho) TYPE REF TO zif_abapgit_object_enhs RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_enqu DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. TYPES: ty_dd27p TYPE STANDARD TABLE OF dd27p WITH DEFAULT KEY. " Fields that are not part of dd25v TYPES: BEGIN OF ty_extra, abap_language_version TYPE uccheck, END OF ty_extra. METHODS _clear_dd27p_fields CHANGING ct_dd27p TYPE ty_dd27p. ENDCLASS. CLASS zcl_abapgit_object_ensc DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_evtb DEFINITION INHERITING FROM zcl_abapgit_object_common_aff CREATE PUBLIC. PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION . PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: c_table_name TYPE tabname VALUE 'EVTB_HEADER'. ENDCLASS. CLASS zcl_abapgit_object_fdt0 DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. METHODS check_is_local RETURNING VALUE(rv_is_local) TYPE abap_bool . METHODS get_application_id RETURNING VALUE(rv_application_id) TYPE fdt_admn_0000s-application_id . METHODS before_xml_deserialize IMPORTING !iv_package TYPE devclass EXPORTING !ev_create TYPE abap_bool !ev_is_local TYPE abap_bool CHANGING !co_dom_tree TYPE REF TO if_ixml_document RAISING zcx_abapgit_exception . METHODS filter_xml_serialize CHANGING !co_ixml_element TYPE REF TO if_ixml_element RAISING zcx_abapgit_exception . METHODS set_field IMPORTING !iv_name TYPE string !iv_value TYPE string DEFAULT '' CHANGING !co_ixml_element TYPE REF TO if_ixml_element RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_form DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: c_objectname_form TYPE thead-tdobject VALUE 'FORM' ##NO_TEXT. CONSTANTS: c_objectname_tdlines TYPE thead-tdobject VALUE 'TDLINES' ##NO_TEXT. CONSTANTS: c_extension_xml TYPE string VALUE 'xml' ##NO_TEXT. DATA: mv_form_name TYPE itcta-tdform. TYPES: BEGIN OF ty_s_form_data, form_header TYPE itcta, text_header TYPE thead, orig_language TYPE sy-langu, pages TYPE STANDARD TABLE OF itctg WITH DEFAULT KEY, page_windows TYPE STANDARD TABLE OF itcth WITH DEFAULT KEY, paragraphs TYPE STANDARD TABLE OF itcdp WITH DEFAULT KEY, strings TYPE STANDARD TABLE OF itcds WITH DEFAULT KEY, tabs TYPE STANDARD TABLE OF itcdq WITH DEFAULT KEY, windows TYPE STANDARD TABLE OF itctw WITH DEFAULT KEY, END OF ty_s_form_data, ty_t_form_data TYPE STANDARD TABLE OF ty_s_form_data WITH DEFAULT KEY, ty_t_form_header TYPE STANDARD TABLE OF itcta WITH DEFAULT KEY, ty_s_form_header TYPE LINE OF ty_t_form_header, ty_t_text_header TYPE STANDARD TABLE OF thead WITH DEFAULT KEY, ty_s_text_header TYPE LINE OF ty_t_text_header, ty_t_lines TYPE tline_tab. METHODS get_last_changes IMPORTING iv_form_name TYPE zif_abapgit_definitions=>ty_item-obj_name RETURNING VALUE(rs_last_changed) TYPE ty_s_form_header. METHODS build_extra_from_header IMPORTING is_header TYPE ty_s_form_header RETURNING VALUE(rv_result) TYPE string. METHODS build_extra_from_header_old IMPORTING is_header TYPE ty_s_form_header RETURNING VALUE(rv_result) TYPE string. METHODS _save_form IMPORTING it_lines TYPE ty_t_lines CHANGING cs_form_data TYPE ty_s_form_data. METHODS extract_tdlines IMPORTING is_form_data TYPE ty_s_form_data RETURNING VALUE(rt_lines) TYPE ty_t_lines RAISING zcx_abapgit_exception. METHODS _clear_changed_fields CHANGING cs_form_data TYPE ty_s_form_data. METHODS compress_lines IMPORTING is_form_data TYPE ty_s_form_data it_lines TYPE ty_t_lines RAISING zcx_abapgit_exception. METHODS find_form IMPORTING iv_object_name TYPE zif_abapgit_definitions=>ty_item-obj_name RETURNING VALUE(rt_text_header) TYPE ty_t_text_header. METHODS _read_form IMPORTING is_text_header TYPE ty_s_text_header EXPORTING ev_form_found TYPE abap_bool es_form_data TYPE ty_s_form_data et_lines TYPE ty_t_lines. METHODS _sort_tdlines_by_windows CHANGING ct_form_windows TYPE ty_s_form_data-windows ct_lines TYPE ty_t_lines. METHODS order_check_and_insert RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_ftgl DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. DATA: mv_toggle_id TYPE c LENGTH 40, "sftgl_ft_id mr_toggle TYPE REF TO data. METHODS: clear_field IMPORTING iv_fieldname TYPE string CHANGING cg_header TYPE any. ENDCLASS. CLASS zcl_abapgit_object_fugr DEFINITION INHERITING FROM zcl_abapgit_objects_program CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: c_longtext_id_prog TYPE dokil-id VALUE 'RE', c_longtext_id_func TYPE dokil-id VALUE 'FU', c_longtext_id_func_exc TYPE dokil-id VALUE 'FX'. TYPES: ty_rs38l_incl_tt TYPE STANDARD TABLE OF rs38l_incl WITH DEFAULT KEY . TYPES: BEGIN OF ty_function, funcname TYPE rs38l_fnam, global_flag TYPE rs38l-global, remote_call TYPE rs38l-remote, update_task TYPE rs38l-utask, short_text TYPE tftit-stext, remote_basxml TYPE rs38l-basxml_enabled, rfcscope TYPE c LENGTH 1, " data element not on older releases rfcvers TYPE c LENGTH 10, " data element not on older releases import TYPE STANDARD TABLE OF rsimp WITH DEFAULT KEY, changing TYPE STANDARD TABLE OF rscha WITH DEFAULT KEY, export TYPE STANDARD TABLE OF rsexp WITH DEFAULT KEY, tables TYPE STANDARD TABLE OF rstbl WITH DEFAULT KEY, exception TYPE STANDARD TABLE OF rsexc WITH DEFAULT KEY, documentation TYPE STANDARD TABLE OF rsfdo WITH DEFAULT KEY, exception_classes TYPE abap_bool, END OF ty_function . TYPES: ty_function_tt TYPE STANDARD TABLE OF ty_function WITH DEFAULT KEY . TYPES: ty_sobj_name_tt TYPE STANDARD TABLE OF sobj_name WITH DEFAULT KEY . DATA mt_includes_cache TYPE ty_sobj_name_tt . DATA mt_includes_all TYPE ty_sobj_name_tt . METHODS check_rfc_parameters IMPORTING !is_function TYPE ty_function RAISING zcx_abapgit_exception . METHODS update_where_used IMPORTING !it_includes TYPE ty_sobj_name_tt . METHODS main_name RETURNING VALUE(rv_program) TYPE program RAISING zcx_abapgit_exception . METHODS functions RETURNING VALUE(rt_functab) TYPE ty_rs38l_incl_tt RAISING zcx_abapgit_exception . METHODS includes RETURNING VALUE(rt_includes) TYPE ty_sobj_name_tt RAISING zcx_abapgit_exception . METHODS serialize_functions RETURNING VALUE(rt_functions) TYPE ty_function_tt RAISING zcx_abapgit_exception . METHODS deserialize_functions IMPORTING !it_functions TYPE ty_function_tt !ii_log TYPE REF TO zif_abapgit_log !iv_version TYPE uccheck !iv_package TYPE devclass !iv_transport TYPE trkorr RAISING zcx_abapgit_exception . METHODS serialize_function_docs IMPORTING !iv_prog_name TYPE syrepid !it_functions TYPE ty_function_tt !ii_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS deserialize_function_docs IMPORTING !iv_prog_name TYPE syrepid !it_functions TYPE ty_function_tt !ii_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception . METHODS serialize_xml IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS deserialize_xml IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_input !iv_version TYPE uccheck !iv_package TYPE devclass !iv_transport TYPE trkorr RAISING zcx_abapgit_exception . METHODS serialize_includes RAISING zcx_abapgit_exception . METHODS deserialize_includes IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_input !iv_package TYPE devclass !ii_log TYPE REF TO zif_abapgit_log RAISING zcx_abapgit_exception . METHODS is_function_group_locked RETURNING VALUE(rv_is_functions_group_locked) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS is_any_include_locked RETURNING VALUE(rv_is_any_include_locked) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS is_any_function_module_locked RETURNING VALUE(rv_any_function_module_locked) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS get_abap_version IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_input RETURNING VALUE(rv_abap_version) TYPE progdir-uccheck RAISING zcx_abapgit_exception . METHODS update_func_group_short_text IMPORTING !iv_group TYPE rs38l-area !iv_short_text TYPE tftit-stext . METHODS serialize_texts IMPORTING !iv_prog_name TYPE syrepid !ii_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS deserialize_texts IMPORTING !iv_prog_name TYPE syrepid !ii_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception . METHODS is_part_of_other_fugr IMPORTING !iv_include TYPE sobj_name RETURNING VALUE(rv_belongs_to_other_fugr) TYPE abap_bool. ENDCLASS. CLASS zcl_abapgit_object_fugs DEFINITION INHERITING FROM zcl_abapgit_object_fugr FINAL CREATE PUBLIC . PUBLIC SECTION. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_g4ba DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . METHODS get_field_rules RETURNING VALUE(ro_result) TYPE REF TO zif_abapgit_field_rules. ENDCLASS. CLASS zcl_abapgit_object_g4bs DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . METHODS get_field_rules RETURNING VALUE(ro_result) TYPE REF TO zif_abapgit_field_rules. ENDCLASS. CLASS zcl_abapgit_object_gsmp DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_http DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_uconservhttphandler, id TYPE c LENGTH 30, version TYPE c LENGTH 1, serviceorder TYPE n LENGTH 2, servicehandler TYPE c LENGTH 30, END OF ty_uconservhttphandler. TYPES: BEGIN OF ty_uconhttpservtext, id TYPE c LENGTH 30, version TYPE c LENGTH 1, lang TYPE c LENGTH 1, shorttext TYPE c LENGTH 255, END OF ty_uconhttpservtext. TYPES: BEGIN OF ty_handler, id TYPE c LENGTH 30, version TYPE c LENGTH 1, serviceorder TYPE n LENGTH 2, servicehandler TYPE c LENGTH 30, END OF ty_handler. TYPES: BEGIN OF ty_gs_object_version, id TYPE c LENGTH 1, object_state TYPE c LENGTH 1, END OF ty_gs_object_version. TYPES: BEGIN OF ty_icf_node, icfname TYPE c LENGTH 15, icfparguid TYPE c LENGTH 25, END OF ty_icf_node. ENDCLASS. CLASS zcl_abapgit_object_iamu DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_internet_appl_comp_binary, attributes TYPE w3mimeattr, source TYPE w3mimetabtype, length TYPE i, extension TYPE string, END OF ty_internet_appl_comp_binary. DATA: mi_mime_api TYPE REF TO if_w3_api_mime. METHODS: get_extension IMPORTING iv_name TYPE csequence iv_data TYPE xstring RETURNING VALUE(rv_extension) TYPE string, load_mime_api RAISING zcx_abapgit_exception, read RETURNING VALUE(rs_internet_appl_comp_binary) TYPE ty_internet_appl_comp_binary RAISING zcx_abapgit_exception, save IMPORTING is_internet_appl_comp_binary TYPE ty_internet_appl_comp_binary RAISING zcx_abapgit_exception, lock IMPORTING iv_changable TYPE abap_bool RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_iarp DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. DATA: ms_name TYPE w3resokey. METHODS: read EXPORTING es_attributes TYPE w3resoattr et_parameters TYPE w3resopara_tabletype RAISING zcx_abapgit_exception, save IMPORTING is_attributes TYPE w3resoattr it_parameters TYPE w3resopara_tabletype RAISING zcx_abapgit_exception, w3_api_load RETURNING VALUE(ri_resource) TYPE REF TO if_w3_api_resource RAISING zcx_abapgit_exception, w3_api_get_attributes IMPORTING ii_resource TYPE REF TO if_w3_api_resource RETURNING VALUE(rs_attributes) TYPE w3resoattr RAISING zcx_abapgit_exception, w3_api_get_parameters IMPORTING ii_resource TYPE REF TO if_w3_api_resource RETURNING VALUE(rt_parameters) TYPE w3resopara_tabletype RAISING zcx_abapgit_exception, w3_api_create_new IMPORTING is_attributes TYPE w3resoattr RETURNING VALUE(ri_resource) TYPE REF TO if_w3_api_resource RAISING zcx_abapgit_exception, w3_api_set_attributes IMPORTING ii_resource TYPE REF TO if_w3_api_resource is_attributes TYPE w3resoattr RAISING zcx_abapgit_exception, w3_api_set_parameters IMPORTING ii_resource TYPE REF TO if_w3_api_resource it_parameters TYPE w3resopara_tabletype RAISING zcx_abapgit_exception, w3_api_save IMPORTING ii_resource TYPE REF TO if_w3_api_resource RAISING zcx_abapgit_exception, w3_api_set_changeable IMPORTING ii_resource TYPE REF TO if_w3_api_resource iv_changeable TYPE abap_bool DEFAULT abap_true RAISING zcx_abapgit_exception, w3_api_delete IMPORTING ii_resource TYPE REF TO if_w3_api_resource RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_iasp DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. DATA: mv_name TYPE itsappl. METHODS: read EXPORTING es_attr TYPE w3servattr et_parameters TYPE w3servpara_tabletype RAISING zcx_abapgit_exception, save IMPORTING is_attr TYPE w3servattr it_parameters TYPE w3servpara_tabletype RAISING zcx_abapgit_exception, w3_api_load RETURNING VALUE(ri_service) TYPE REF TO if_w3_api_service RAISING zcx_abapgit_exception, w3_api_get_attributes IMPORTING ii_service TYPE REF TO if_w3_api_service RETURNING VALUE(rs_attributes) TYPE w3servattr, w3_api_get_parameters IMPORTING ii_service TYPE REF TO if_w3_api_service RETURNING VALUE(rt_parameters) TYPE w3servpara_tabletype, w3_api_create_new IMPORTING is_attributes TYPE w3servattr RETURNING VALUE(ri_service) TYPE REF TO if_w3_api_service RAISING zcx_abapgit_exception, w3_api_set_attributes IMPORTING ii_service TYPE REF TO if_w3_api_service is_attributes TYPE w3servattr RAISING zcx_abapgit_exception, w3_api_set_parameters IMPORTING ii_service TYPE REF TO if_w3_api_service it_parameters TYPE w3servpara_tabletype RAISING zcx_abapgit_exception, w3_api_save IMPORTING ii_service TYPE REF TO if_w3_api_service RAISING zcx_abapgit_exception, w3_api_set_changeable IMPORTING ii_service TYPE REF TO if_w3_api_service iv_changeable TYPE abap_bool DEFAULT abap_true RAISING zcx_abapgit_exception, w3_api_delete IMPORTING ii_service TYPE REF TO if_w3_api_service RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_iatu DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. METHODS: read EXPORTING es_attr TYPE w3tempattr ev_source TYPE string RAISING zcx_abapgit_exception, save IMPORTING is_attr TYPE w3tempattr iv_source TYPE string RAISING zcx_abapgit_exception, w3_api_load IMPORTING is_name TYPE iacikeyt RETURNING VALUE(ri_template) TYPE REF TO if_w3_api_template RAISING zcx_abapgit_exception, w3_api_set_changeable IMPORTING iv_changeable TYPE abap_bool ii_template TYPE REF TO if_w3_api_template RAISING zcx_abapgit_exception, w3_api_delete IMPORTING ii_template TYPE REF TO if_w3_api_template RAISING zcx_abapgit_exception, w3_api_save IMPORTING ii_template TYPE REF TO if_w3_api_template RAISING zcx_abapgit_exception, w3_api_get_attributes IMPORTING ii_template TYPE REF TO if_w3_api_template RETURNING VALUE(rs_attributes) TYPE w3tempattr RAISING zcx_abapgit_exception, w3_api_get_source IMPORTING ii_template TYPE REF TO if_w3_api_template RETURNING VALUE(rt_source) TYPE w3htmltabtype RAISING zcx_abapgit_exception, w3_api_create_new IMPORTING is_template_data TYPE w3tempattr RETURNING VALUE(ri_template) TYPE REF TO if_w3_api_template RAISING zcx_abapgit_exception, w3_api_set_attributes IMPORTING ii_template TYPE REF TO if_w3_api_template is_attr TYPE w3tempattr RAISING zcx_abapgit_exception, w3_api_set_source IMPORTING ii_template TYPE REF TO if_w3_api_template it_source TYPE w3htmltabtype RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_iaxu DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. DATA: mv_source_style_2006 TYPE w3style VALUE 'XML', mv_generator_class TYPE w3styleclass VALUE 'CL_ITS_GENERATE_XML3'. METHODS: read RETURNING VALUE(rs_attr) TYPE w3tempattr RAISING zcx_abapgit_exception, save IMPORTING is_attr TYPE w3tempattr RAISING zcx_abapgit_exception, w3_api_load IMPORTING is_name TYPE iacikeyt EXPORTING eo_xml_api TYPE REF TO object es_attr TYPE w3tempattr RAISING zcx_abapgit_exception, w3_api_set_changeable IMPORTING io_xml_api TYPE REF TO object iv_changeable TYPE abap_bool RAISING zcx_abapgit_exception, w3_api_delete IMPORTING io_xml_api TYPE REF TO object RAISING zcx_abapgit_exception, w3_api_save IMPORTING io_xml_api TYPE REF TO object RAISING zcx_abapgit_exception, w3_api_create_new IMPORTING is_attr TYPE w3tempattr RETURNING VALUE(ro_xml_api) TYPE REF TO object RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_idoc DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. CLASS-METHODS clear_idoc_segement_fields CHANGING cg_structure TYPE any. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_idoc, attributes TYPE edi_iapi01, t_syntax TYPE STANDARD TABLE OF edi_iapi02 WITH NON-UNIQUE DEFAULT KEY, END OF ty_idoc. DATA: mv_idoctyp TYPE edi_iapi00-idoctyp. CLASS-METHODS clear_idoc_segement_field IMPORTING iv_fieldname TYPE csequence CHANGING cg_structure TYPE any. METHODS is_closed RETURNING VALUE(rv_closed) TYPE abap_bool. ENDCLASS. CLASS zcl_abapgit_object_iext DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_extention, attributes TYPE edi_iapi01, t_syntax TYPE STANDARD TABLE OF edi_iapi03 WITH NON-UNIQUE DEFAULT KEY, END OF ty_extention. CONSTANTS c_dataname_iext TYPE string VALUE 'IEXT' ##NO_TEXT. DATA: mv_extension TYPE edi_cimtyp. ENDCLASS. CLASS zcl_abapgit_object_intf DEFINITION FINAL INHERITING FROM zcl_abapgit_objects_program. PUBLIC SECTION. INTERFACES zif_abapgit_object. TYPES: BEGIN OF ty_docu, lines TYPE tlinetab, i18n_lines TYPE zif_abapgit_lang_definitions=>ty_i18n_lines, END OF ty_docu. TYPES: BEGIN OF ty_intf, vseointerf TYPE vseointerf, docu TYPE ty_docu, description_int TYPE zif_abapgit_oo_object_fnc=>ty_seoclasstx_tt, description TYPE zif_abapgit_oo_object_fnc=>ty_seocompotx_tt, description_sub TYPE zif_abapgit_oo_object_fnc=>ty_seosubcotx_tt, END OF ty_intf. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. METHODS deserialize_proxy IMPORTING iv_transport TYPE trkorr RAISING zcx_abapgit_exception . METHODS deserialize_docu IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_input !is_docu TYPE ty_docu RAISING zcx_abapgit_exception . METHODS serialize_docu IMPORTING !it_langu_additional TYPE zif_abapgit_lang_definitions=>ty_langus OPTIONAL !iv_clsname TYPE seoclsname RETURNING VALUE(rs_docu) TYPE ty_docu RAISING zcx_abapgit_exception. METHODS serialize_descr_class IMPORTING !iv_clsname TYPE seoclsname RETURNING VALUE(rs_description) TYPE ty_intf-description_int RAISING zcx_abapgit_exception. METHODS serialize_descr_compo IMPORTING !iv_clsname TYPE seoclsname RETURNING VALUE(rs_description) TYPE ty_intf-description RAISING zcx_abapgit_exception. METHODS serialize_descr_subco IMPORTING !iv_clsname TYPE seoclsname RETURNING VALUE(rs_description) TYPE ty_intf-description_sub RAISING zcx_abapgit_exception. METHODS serialize_xml IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . PRIVATE SECTION. CONSTANTS: BEGIN OF c_longtext_name, attributes TYPE string VALUE 'LONGTEXTS_IA', methods TYPE string VALUE 'LONGTEXTS_IO', events TYPE string VALUE 'LONGTEXTS_IE', END OF c_longtext_name. CONSTANTS: BEGIN OF c_longtext_id, interface TYPE dokil-id VALUE 'IF', attributes TYPE dokil-id VALUE 'IA', methods TYPE dokil-id VALUE 'IO', events TYPE dokil-id VALUE 'IE', END OF c_longtext_id. DATA mv_aff_enabled TYPE abap_bool. DATA mi_object_oriented_object_fct TYPE REF TO zif_abapgit_oo_object_fnc . METHODS deserialize_pre_ddic IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_input iv_package TYPE devclass RAISING zcx_abapgit_exception. METHODS deserialize_descr_class IMPORTING it_description TYPE zif_abapgit_oo_object_fnc=>ty_seoclasstx_tt OPTIONAL. METHODS deserialize_descr_compo IMPORTING it_description TYPE zif_abapgit_oo_object_fnc=>ty_seocompotx_tt OPTIONAL. METHODS deserialize_descr_subco IMPORTING it_description TYPE zif_abapgit_oo_object_fnc=>ty_seosubcotx_tt OPTIONAL. METHODS read_xml IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_input RETURNING VALUE(rs_intf) TYPE ty_intf RAISING zcx_abapgit_exception. METHODS read_json RETURNING VALUE(rs_intf) TYPE ty_intf RAISING zcx_abapgit_exception. METHODS extract_languages_for_transl IMPORTING is_intf TYPE ty_intf RETURNING VALUE(rs_result) TYPE zif_abapgit_definitions=>ty_languages. ENDCLASS. CLASS zcl_abapgit_object_iobj DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. METHODS: clear_field IMPORTING iv_fieldname TYPE string CHANGING cg_metadata TYPE any. ENDCLASS. CLASS zcl_abapgit_object_iwmo DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . PRIVATE SECTION. METHODS get_field_rules RETURNING VALUE(ro_result) TYPE REF TO zif_abapgit_field_rules. ENDCLASS. CLASS zcl_abapgit_object_iwom DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . PRIVATE SECTION. METHODS get_field_rules RETURNING VALUE(ro_result) TYPE REF TO zif_abapgit_field_rules. ENDCLASS. CLASS zcl_abapgit_object_iwpr DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . PRIVATE SECTION. METHODS get_field_rules RETURNING VALUE(ro_result) TYPE REF TO zif_abapgit_field_rules. ENDCLASS. CLASS zcl_abapgit_object_iwsg DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . PRIVATE SECTION. METHODS get_field_rules RETURNING VALUE(ro_result) TYPE REF TO zif_abapgit_field_rules. ENDCLASS. CLASS zcl_abapgit_object_iwsv DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . PRIVATE SECTION. METHODS get_field_rules RETURNING VALUE(ro_result) TYPE REF TO zif_abapgit_field_rules. ENDCLASS. CLASS zcl_abapgit_object_iwvb DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . PRIVATE SECTION. METHODS get_field_rules RETURNING VALUE(ro_result) TYPE REF TO zif_abapgit_field_rules. ENDCLASS. CLASS zcl_abapgit_object_jobd DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. TYPES: ty_jd_name TYPE c LENGTH 32. ENDCLASS. CLASS zcl_abapgit_object_msag DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_t100_text, sprsl TYPE t100-sprsl, msgnr TYPE t100-msgnr, text TYPE t100-text, END OF ty_t100_text . TYPES: ty_t100_texts TYPE STANDARD TABLE OF ty_t100_text . TYPES: ty_t100s TYPE STANDARD TABLE OF t100 WITH NON-UNIQUE DEFAULT KEY . CONSTANTS c_longtext_id_msag TYPE dokil-id VALUE 'NA'. METHODS serialize_texts IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS deserialize_texts IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception . METHODS serialize_longtexts_msag IMPORTING !it_t100 TYPE ty_t100s !ii_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS delete_msgid IMPORTING !iv_message_id TYPE arbgb RAISING zcx_abapgit_exception. METHODS free_access_permission IMPORTING !iv_message_id TYPE arbgb . METHODS delete_documentation IMPORTING !iv_message_id TYPE arbgb RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_nont DEFINITION INHERITING FROM zcl_abapgit_object_common_aff CREATE PUBLIC. PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_table_name TYPE tabname VALUE 'NONT_HEADER'. ENDCLASS. CLASS zcl_abapgit_object_nrob DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. METHODS: delete_intervals IMPORTING iv_object TYPE inri-object RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_nspc DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_nspc, namespace TYPE trnspacet-namespace, replicense TYPE trnspacet-replicense, sscrflag TYPE trnspacet-sscrflag, sapflag TYPE trnspacet-sapflag, gen_only TYPE trnspacet-gen_only, END OF ty_nspc . TYPES: BEGIN OF ty_nspc_text, spras TYPE trnspacett-spras, descriptn TYPE trnspacett-descriptn, owner TYPE trnspacett-owner, END OF ty_nspc_text . TYPES: ty_nspc_texts TYPE STANDARD TABLE OF ty_nspc_text . DATA mv_component TYPE cvers-component. METHODS serialize_texts IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS deserialize_texts IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_input !iv_namespace TYPE namespace RAISING zcx_abapgit_exception . METHODS add_to_transport IMPORTING !iv_package TYPE devclass RAISING zcx_abapgit_exception . METHODS serialize_sw_component IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS deserialize_sw_component IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_oa2p DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. DATA: mv_profile TYPE c LENGTH 30. ENDCLASS. CLASS zcl_abapgit_object_odso DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. METHODS: clear_field IMPORTING iv_fieldname TYPE string CHANGING cg_metadata TYPE any. ENDCLASS. CLASS zcl_abapgit_object_otgr DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_otgr, cls_type_group TYPE cls_type_group, texts TYPE STANDARD TABLE OF cls_type_groupt WITH DEFAULT KEY, elements TYPE STANDARD TABLE OF cls_tygr_element WITH DEFAULT KEY, END OF ty_otgr . METHODS instantiate_and_lock_otgr RETURNING VALUE(ro_otgr) TYPE REF TO cl_cls_object_type_group RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_para DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. METHODS unlock IMPORTING !iv_paramid TYPE memoryid . ENDCLASS. CLASS zcl_abapgit_object_pdxx_super DEFINITION INHERITING FROM zcl_abapgit_objects_super ABSTRACT. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. DATA ms_objkey TYPE hrsobject. METHODS check_subrc_for IMPORTING iv_call TYPE clike OPTIONAL RAISING zcx_abapgit_exception. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_pers DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_personalization_object, pers_reg TYPE spers_reg, pers_reg_text TYPE spers_regt, END OF ty_personalization_object. DATA: mv_pers_key TYPE spers_key. METHODS: get_personalization_object IMPORTING iv_create TYPE abap_bool OPTIONAL iv_view_only TYPE abap_bool OPTIONAL RETURNING VALUE(ro_personalization_object) TYPE REF TO cl_pers_reg RAISING zcx_abapgit_exception. ENDCLASS. INTERFACE lif_package_interface_facade. TYPES ty_tpak_package_interf_elem_tt TYPE STANDARD TABLE OF tpak_package_interf_elem_ref WITH DEFAULT KEY. METHODS: get_elements RETURNING VALUE(rt_elements) TYPE ty_tpak_package_interf_elem_tt RAISING zcx_abapgit_exception, set_elements_changeable IMPORTING iv_changeable TYPE abap_bool RAISING zcx_abapgit_exception, save_elements RAISING zcx_abapgit_exception, get_all_attributes RETURNING VALUE(rs_package_interface_data) TYPE scompidtln RAISING zcx_abapgit_exception, set_changeable IMPORTING iv_changeable TYPE abap_bool RAISING zcx_abapgit_exception, delete RAISING zcx_abapgit_exception, save RAISING zcx_abapgit_exception, remove_elements IMPORTING it_elements TYPE tpak_package_interf_elem_list RAISING zcx_abapgit_exception, add_elements IMPORTING it_elements_data TYPE scomeldata RAISING zcx_abapgit_exception, set_all_attributes IMPORTING is_package_interface_data TYPE scompidtln is_data_sign TYPE scompisign RAISING zcx_abapgit_exception, get_changeable RETURNING VALUE(rv_changeable) TYPE abap_bool RAISING zcx_abapgit_exception. ENDINTERFACE. CLASS lcl_package_interface_facade DEFINITION. PUBLIC SECTION. INTERFACES: lif_package_interface_facade. METHODS: constructor IMPORTING ii_interface TYPE REF TO if_package_interface. PRIVATE SECTION. DATA: mi_interface TYPE REF TO if_package_interface. ENDCLASS. CLASS zcl_abapgit_object_pinf DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_pinf, attributes TYPE scompidtln, elements TYPE STANDARD TABLE OF scomeldtln WITH DEFAULT KEY, END OF ty_pinf . TYPES: ty_elements TYPE STANDARD TABLE OF tpak_package_interf_elem_ref WITH DEFAULT KEY . METHODS create_or_load IMPORTING !is_pinf TYPE ty_pinf !iv_package TYPE devclass RETURNING VALUE(ri_interface) TYPE REF TO lif_package_interface_facade RAISING zcx_abapgit_exception . METHODS delete_elements IMPORTING !ii_interface TYPE REF TO lif_package_interface_facade RAISING zcx_abapgit_exception . METHODS update_attributes IMPORTING !iv_package TYPE devclass !is_pinf TYPE ty_pinf !ii_interface TYPE REF TO lif_package_interface_facade RAISING zcx_abapgit_exception . METHODS update_elements IMPORTING !iv_package TYPE devclass !is_pinf TYPE ty_pinf !ii_interface TYPE REF TO lif_package_interface_facade RAISING zcx_abapgit_exception . METHODS load IMPORTING iv_name TYPE scomifnam RETURNING VALUE(ri_interface) TYPE REF TO lif_package_interface_facade. METHODS create_facade IMPORTING ii_interface TYPE REF TO if_package_interface RETURNING VALUE(ri_facade) TYPE REF TO lif_package_interface_facade. ENDCLASS. CLASS zcl_abapgit_object_prag DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_pragma, pragma TYPE c LENGTH 40, extension TYPE c LENGTH 1, signature TYPE c LENGTH 10, description TYPE c LENGTH 255, END OF ty_pragma. ENDCLASS. CLASS zcl_abapgit_object_prog DEFINITION INHERITING FROM zcl_abapgit_objects_program FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_longtext_id_prog TYPE dokil-id VALUE 'RE' ##NO_TEXT. METHODS deserialize_with_ext IMPORTING !is_progdir TYPE zif_abapgit_sap_report=>ty_progdir !it_source TYPE abaptxt255_tab !iv_package TYPE devclass RAISING zcx_abapgit_exception . METHODS serialize_texts IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS deserialize_texts IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception . METHODS is_program_locked RETURNING VALUE(rv_is_program_locked) TYPE abap_bool RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_ront DEFINITION INHERITING FROM zcl_abapgit_object_common_aff CREATE PUBLIC. PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_table_name TYPE tabname VALUE 'RONT_HEADER'. ENDCLASS. CLASS zcl_abapgit_object_sajc DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION . METHODS zif_abapgit_object~get_deserialize_steps REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_sajt DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION . METHODS zif_abapgit_object~get_deserialize_steps REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_saxx_super DEFINITION INHERITING FROM zcl_abapgit_objects_super ABSTRACT CREATE PUBLIC . * common class for SAPC and SAMC objects PUBLIC SECTION. INTERFACES zif_abapgit_object . METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL. PROTECTED SECTION. METHODS get_persistence_class_name ABSTRACT RETURNING VALUE(rv_persistence_class_name) TYPE seoclsname . METHODS get_data_class_name ABSTRACT RETURNING VALUE(rv_data_class_name) TYPE seoclsname . METHODS get_data_structure_name ABSTRACT RETURNING VALUE(rv_data_structure_name) TYPE string . METHODS get_lock_object ABSTRACT RETURNING VALUE(rv_lock_object) TYPE string. METHODS create_channel_objects RAISING zcx_abapgit_type_not_supported . PRIVATE SECTION. DATA mi_persistence TYPE REF TO if_wb_object_persist . DATA mi_appl_obj_data TYPE REF TO if_wb_object_data_model . DATA mv_data_structure_name TYPE string . DATA mv_appl_obj_cls_name TYPE seoclsname . DATA mv_persistence_cls_name TYPE seoclsname . DATA mv_object_key TYPE seu_objkey . METHODS get_data EXPORTING !eg_data TYPE any RAISING zcx_abapgit_exception . METHODS lock RAISING zcx_abapgit_exception . METHODS unlock RAISING zcx_abapgit_exception . METHODS get_names . ENDCLASS. CLASS zcl_abapgit_object_samc DEFINITION INHERITING FROM zcl_abapgit_object_saxx_super FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. METHODS get_data_class_name REDEFINITION . METHODS get_data_structure_name REDEFINITION . METHODS get_persistence_class_name REDEFINITION . METHODS get_lock_object REDEFINITION . PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_sapc DEFINITION INHERITING FROM zcl_abapgit_object_saxx_super FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. METHODS get_data_class_name REDEFINITION . METHODS get_data_structure_name REDEFINITION . METHODS get_persistence_class_name REDEFINITION . METHODS get_lock_object REDEFINITION . PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_scp1 DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. TYPES: BEGIN OF ty_scp1, scprattr TYPE scprattr, scprtext TYPE STANDARD TABLE OF scprtext WITH DEFAULT KEY, scprvals TYPE STANDARD TABLE OF scprvals WITH DEFAULT KEY, scprvall TYPE STANDARD TABLE OF scprvall WITH DEFAULT KEY, scprreca TYPE STANDARD TABLE OF scprreca WITH DEFAULT KEY, scprfldv TYPE STANDARD TABLE OF scprfldv WITH DEFAULT KEY, subprofs TYPE STANDARD TABLE OF scprpprl WITH DEFAULT KEY, END OF ty_scp1 . METHODS dequeue . METHODS enqueue RAISING zcx_abapgit_exception . METHODS save IMPORTING !is_scp1 TYPE ty_scp1 RAISING zcx_abapgit_exception . METHODS save_hier IMPORTING !is_scp1 TYPE ty_scp1 RAISING zcx_abapgit_exception . METHODS adjust_inbound CHANGING !cs_scp1 TYPE ty_scp1 . METHODS adjust_outbound CHANGING !cs_scp1 TYPE ty_scp1 . METHODS load CHANGING !cs_scp1 TYPE ty_scp1 . METHODS load_hier CHANGING !cs_scp1 TYPE ty_scp1 . METHODS call_delete_fms IMPORTING !iv_profile_id TYPE scpr_id RAISING zcx_abapgit_exception . PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_scvi DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_screen_variant, shdsvci TYPE shdsvci, shdsvtxci TYPE STANDARD TABLE OF shdsvtxci WITH DEFAULT KEY, shdsvfvci TYPE STANDARD TABLE OF shdsvfvci WITH DEFAULT KEY, shdguixt TYPE STANDARD TABLE OF shdguixt WITH DEFAULT KEY, shdgxtcode TYPE STANDARD TABLE OF shdgxtcode WITH DEFAULT KEY, END OF ty_screen_variant . ENDCLASS. CLASS zcl_abapgit_object_sfbf DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_longtext_id_sfbf TYPE dokil-id VALUE 'BF'. DATA mv_bf TYPE sfw_bfunction. METHODS: unlock, activate RAISING zcx_abapgit_exception, create RETURNING VALUE(ro_bf) TYPE REF TO cl_sfw_bf RAISING zcx_abapgit_exception, get RETURNING VALUE(ro_bf) TYPE REF TO cl_sfw_bf RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_sfbs DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_longtext_id_sfbs TYPE dokil-id VALUE 'BS'. DATA mv_bfset TYPE sfw_bset. METHODS: unlock, activate RAISING zcx_abapgit_exception, create RETURNING VALUE(ro_bfs) TYPE REF TO cl_sfw_bfs RAISING zcx_abapgit_exception, get RETURNING VALUE(ro_bfs) TYPE REF TO cl_sfw_bfs RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_sfpf DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . CLASS-METHODS fix_oref IMPORTING !ii_document TYPE REF TO if_ixml_document RAISING zcx_abapgit_exception . PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_layout_file_ext TYPE string VALUE 'xdp'. METHODS: load RETURNING VALUE(ri_wb_form) TYPE REF TO if_fp_wb_form RAISING zcx_abapgit_exception, form_to_xstring RETURNING VALUE(rv_xstr) TYPE xstring RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_sfpi DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. METHODS: load RETURNING VALUE(ri_wb_interface) TYPE REF TO if_fp_wb_interface RAISING zcx_abapgit_exception, interface_to_xstring RETURNING VALUE(rv_xstr) TYPE xstring RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_sfsw DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_longtext_id_sfsw TYPE dokil-id VALUE 'SW'. DATA mv_switch TYPE sfw_switch_id. METHODS: unlock, activate RAISING zcx_abapgit_exception, create RETURNING VALUE(ro_switch) TYPE REF TO cl_sfw_sw RAISING zcx_abapgit_exception, get RETURNING VALUE(ro_switch) TYPE REF TO cl_sfw_sw RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_shi3 DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. METHODS has_authorization IMPORTING !iv_devclass TYPE devclass !iv_structure_id TYPE hier_guid !iv_activity TYPE activ_auth RAISING zcx_abapgit_exception . METHODS is_used IMPORTING !iv_structure_id TYPE hier_guid RAISING zcx_abapgit_exception . METHODS delete_tree_structure IMPORTING !iv_structure_id TYPE hier_guid . PRIVATE SECTION. DATA mv_tree_id TYPE ttree-id. METHODS insert_transport IMPORTING !iv_transport TYPE trkorr RAISING zcx_abapgit_exception. METHODS jump_se43 RAISING zcx_abapgit_exception. METHODS jump_sbach04 RAISING zcx_abapgit_exception. METHODS clear_fields CHANGING !cs_head TYPE ttree !ct_nodes TYPE hier_iface_t. ENDCLASS. CLASS zcl_abapgit_object_shi5 DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. TYPES: ty_ttree_extt TYPE STANDARD TABLE OF ttree_extt WITH NON-UNIQUE DEFAULT KEY, BEGIN OF ty_extension, header TYPE ttree_ext, texts TYPE ty_ttree_extt, sequences TYPE STANDARD TABLE OF ttrees WITH NON-UNIQUE DEFAULT KEY, END OF ty_extension. DATA: mv_extension TYPE hier_names. ENDCLASS. CLASS zcl_abapgit_object_shi8 DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. DATA: mv_assignment_id TYPE hier_sfw_id. ENDCLASS. CLASS zcl_abapgit_object_shlp DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. METHODS handle_dependencies IMPORTING !iv_step TYPE zif_abapgit_objects=>ty_deserialization_step CHANGING !cv_exit TYPE dd30v-selmexit !cv_done TYPE abap_bool. METHODS adjust_exit CHANGING !cv_exit TYPE dd30v-selmexit. METHODS check_exit IMPORTING !iv_exit TYPE dd30v-selmexit RETURNING VALUE(rv_done) TYPE abap_bool. ENDCLASS. CLASS zcl_abapgit_object_shma DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_sots_handler DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. TYPES: BEGIN OF ty_sots, header TYPE sotr_headu, entries TYPE sotr_textl_tt, END OF ty_sots. TYPES: ty_sots_tt TYPE STANDARD TABLE OF ty_sots WITH DEFAULT KEY. TYPES: ty_sots_use_tt TYPE STANDARD TABLE OF sotr_useu WITH DEFAULT KEY. CLASS-METHODS read_sots IMPORTING !iv_pgmid TYPE tadir-pgmid DEFAULT 'R3TR' !iv_object TYPE trobjtype !iv_obj_name TYPE csequence !io_xml TYPE REF TO zif_abapgit_xml_output OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params EXPORTING !et_sots TYPE ty_sots_tt !et_sots_use TYPE ty_sots_use_tt RAISING zcx_abapgit_exception. CLASS-METHODS create_sots IMPORTING !iv_package TYPE devclass !io_xml TYPE REF TO zif_abapgit_xml_input OPTIONAL RAISING zcx_abapgit_exception. CLASS-METHODS create_sots_from_data IMPORTING !iv_package TYPE devclass !it_sots TYPE ty_sots_tt OPTIONAL !it_sots_use TYPE ty_sots_use_tt OPTIONAL RAISING zcx_abapgit_exception. CLASS-METHODS delete_sots IMPORTING !iv_pgmid TYPE tadir-pgmid DEFAULT 'R3TR' !iv_object TYPE trobjtype !iv_obj_name TYPE csequence RAISING zcx_abapgit_exception. PROTECTED SECTION. CLASS-METHODS get_sots_usage IMPORTING !iv_pgmid TYPE tadir-pgmid !iv_object TYPE trobjtype !iv_obj_name TYPE csequence RETURNING VALUE(rt_sots_use) TYPE ty_sots_use_tt. CLASS-METHODS get_sots_4_concept IMPORTING !iv_concept TYPE sotr_conc RETURNING VALUE(rs_sots) TYPE ty_sots. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_sicf DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. TYPES ty_hash TYPE c LENGTH 25. TYPES: ty_icfhandler_tt TYPE STANDARD TABLE OF icfhandler WITH DEFAULT KEY . TYPES: BEGIN OF ty_sicf_key, icf_name TYPE icfservice-icf_name, icfparguid TYPE icfservice-icfparguid, END OF ty_sicf_key . METHODS serialize_otr IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS deserialize_otr IMPORTING !iv_package TYPE devclass !io_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception . METHODS read IMPORTING !iv_clear TYPE abap_bool DEFAULT abap_true EXPORTING !es_icfservice TYPE icfservice !es_icfdocu TYPE icfdocu !et_icfhandler TYPE ty_icfhandler_tt !ev_url TYPE string RAISING zcx_abapgit_exception . METHODS insert_sicf IMPORTING !is_icfservice TYPE icfservice !is_icfdocu TYPE icfdocu !it_icfhandler TYPE ty_icfhandler_tt !iv_package TYPE devclass !iv_url TYPE string RAISING zcx_abapgit_exception . METHODS change_sicf IMPORTING !is_icfservice TYPE icfservice !is_icfdocu TYPE icfdocu !it_icfhandler TYPE ty_icfhandler_tt !iv_package TYPE devclass !iv_parent TYPE icfparguid RAISING zcx_abapgit_exception . METHODS to_icfhndlist IMPORTING !it_list TYPE ty_icfhandler_tt RETURNING VALUE(rt_list) TYPE icfhndlist . METHODS find_parent IMPORTING !iv_url TYPE string RETURNING VALUE(rv_parent) TYPE icfparguid RAISING zcx_abapgit_exception . CLASS-METHODS get_hash_from_object IMPORTING !iv_obj_name TYPE tadir-obj_name RETURNING VALUE(rv_hash) TYPE ty_hash RAISING zcx_abapgit_exception. CLASS-METHODS get_icfaltname IMPORTING !is_icfservice TYPE icfservice RETURNING VALUE(rv_icfaltnme) TYPE icfservice-icfaltnme. ENDCLASS. CLASS zcl_abapgit_object_sktd DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. DATA mr_data TYPE REF TO data . DATA mv_object_key TYPE seu_objkey . DATA mi_persistence TYPE REF TO if_wb_object_persist . DATA mi_wb_object_operator TYPE REF TO object . METHODS clear_fields CHANGING !cs_data TYPE any . METHODS clear_field IMPORTING !iv_fieldname TYPE csequence CHANGING !cs_data TYPE any . METHODS get_wb_object_operator RETURNING VALUE(ri_wb_object_operator) TYPE REF TO object RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_smbc DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC. PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION. METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_smim DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: c_prop_abap_langu_vers TYPE string VALUE 'MIME_ABAP_LANGUAGE_VRS', c_prop_description TYPE string VALUE 'DESCRIPTION', c_prop_folder_id TYPE string VALUE 'KW_PARENT_FOLDER_ID'. TYPES: BEGIN OF ty_extra, file_name TYPE string, mimetype TYPE string, description TYPE string, abap_language_version TYPE uccheck, parent_folder_id TYPE skwf_io-objid, END OF ty_extra. METHODS get_filename IMPORTING iv_url TYPE skwf_url RETURNING VALUE(rv_filename) TYPE string. METHODS find_content IMPORTING iv_url TYPE skwf_url iv_filename TYPE string RETURNING VALUE(rv_content) TYPE xstring RAISING zcx_abapgit_exception. METHODS build_filename IMPORTING iv_filename TYPE string RETURNING VALUE(rv_filename) TYPE string. METHODS get_url_for_io EXPORTING ev_url TYPE skwf_url ev_is_folder TYPE abap_bool es_io TYPE skwf_io RAISING zcx_abapgit_not_found zcx_abapgit_exception. METHODS get_properties IMPORTING is_loio TYPE skwf_io CHANGING cs_extra TYPE ty_extra RAISING zcx_abapgit_exception. METHODS set_properties IMPORTING is_loio TYPE skwf_io is_extra TYPE ty_extra RAISING zcx_abapgit_exception. METHODS get_filename_and_mimetype IMPORTING is_loio TYPE skwf_io CHANGING cs_extra TYPE ty_extra RAISING zcx_abapgit_exception. METHODS set_filename_and_mimetype IMPORTING is_loio TYPE skwf_io is_extra TYPE ty_extra RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_smtg DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. DATA: mv_template_id TYPE c LENGTH 30, mo_structdescr TYPE REF TO cl_abap_structdescr. METHODS: clear_field IMPORTING iv_fieldname TYPE string CHANGING cg_header TYPE any, get_structure RETURNING VALUE(ro_structdescr) TYPE REF TO cl_abap_structdescr RAISING zcx_abapgit_exception, add_component IMPORTING iv_fielname TYPE string iv_structure_name TYPE string CHANGING ct_components TYPE abap_component_tab RAISING zcx_abapgit_exception, get_template EXPORTING es_template TYPE any RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_sobj DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . PRIVATE SECTION. METHODS get_field_rules RETURNING VALUE(ri_rules) TYPE REF TO zif_abapgit_field_rules. METHODS is_locked RETURNING VALUE(rv_is_locked) TYPE abap_bool. METHODS is_objtype_locked RETURNING VALUE(rv_is_locked) TYPE abap_bool. METHODS is_program_locked RETURNING VALUE(rv_is_locked) TYPE abap_bool. METHODS get_program RETURNING VALUE(rv_program) TYPE tojtb-progname. ENDCLASS. CLASS zcl_abapgit_object_sod1 DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: c_xml_transformation_name TYPE string VALUE 'SOD1', c_data_model_class_name TYPE string VALUE 'CL_APS_ODA_WBI_SOD1_DATA_MODEL'. METHODS create_wb_object_operator IMPORTING !is_object_type TYPE wbobjtype !iv_object_key TYPE seu_objkey !iv_transport_request TYPE trkorr OPTIONAL !iv_do_commits TYPE abap_bool DEFAULT abap_true !iv_run_in_test_mode TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ro_wb_object_operator) TYPE REF TO object RAISING zcx_abapgit_exception. METHODS get_wb_object_operator IMPORTING !is_object_type TYPE wbobjtype !iv_object_key TYPE seu_objkey !iv_transport_request TYPE trkorr OPTIONAL RETURNING VALUE(ro_wb_object_operator) TYPE REF TO object RAISING zcx_abapgit_exception. METHODS clear_metadata_fields CHANGING !cs_data TYPE any. METHODS clear_content_fields CHANGING !cs_data TYPE any. METHODS clear_field IMPORTING !iv_fieldname TYPE csequence CHANGING !cs_metadata TYPE any. ENDCLASS. CLASS zcl_abapgit_object_sod2 DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: c_xml_transformation_name TYPE string VALUE 'SOD2', c_data_model_class_name TYPE string VALUE 'CL_APS_ODA_WBI_SOD2_DATA_MODEL'. METHODS create_wb_object_operator IMPORTING !is_object_type TYPE wbobjtype !iv_object_key TYPE seu_objkey !iv_transport_request TYPE trkorr OPTIONAL !iv_do_commits TYPE abap_bool DEFAULT abap_true !iv_run_in_test_mode TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ro_wb_object_operator) TYPE REF TO object RAISING zcx_abapgit_exception. METHODS get_wb_object_operator IMPORTING !is_object_type TYPE wbobjtype !iv_object_key TYPE seu_objkey !iv_transport_request TYPE trkorr OPTIONAL RETURNING VALUE(ro_wb_object_operator) TYPE REF TO object RAISING zcx_abapgit_exception. METHODS clear_metadata_fields CHANGING !cs_data TYPE any. METHODS clear_content_fields CHANGING !cs_data TYPE any. METHODS clear_field IMPORTING !iv_fieldname TYPE csequence CHANGING !cs_metadata TYPE any. ENDCLASS. CLASS zcl_abapgit_object_sots DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES: zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_sots, header TYPE sotr_headu, entries TYPE sotr_textl_tt, END OF ty_sots, ty_sots_tt TYPE STANDARD TABLE OF ty_sots WITH NON-UNIQUE DEFAULT KEY. METHODS: read_sots RETURNING VALUE(rt_sots) TYPE ty_sots_tt RAISING zcx_abapgit_exception, create_sots IMPORTING is_sots TYPE ty_sots iv_package TYPE devclass iv_object TYPE trobjtype RAISING zcx_abapgit_exception, get_raw_text_filename IMPORTING is_entry TYPE sotr_textl RETURNING VALUE(rv_filename) TYPE string. ENDCLASS. CLASS zcl_abapgit_object_splo DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_sppf DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_sprx DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES: zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: BEGIN OF c_proxy, data TYPE string VALUE 'PROXY_DATA' ##NO_TEXT, header TYPE string VALUE 'PROXY_HEADER' ##NO_TEXT, END OF c_proxy . DATA mv_object TYPE sproxhdr-object . DATA mv_obj_name TYPE sproxhdr-obj_name . METHODS load_db RETURNING VALUE(rs_data) TYPE sprx_db_data . METHODS get_object_and_name EXPORTING !ev_object TYPE sproxhdr-object !ev_obj_name TYPE sproxhdr-obj_name . METHODS delta_handling IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_input EXPORTING !et_sproxhdr_new TYPE sprx_hdr_t !et_sproxdat_new TYPE sprx_dat_t RAISING zcx_abapgit_exception . METHODS check_sprx_tadir RAISING zcx_abapgit_exception . METHODS save IMPORTING !it_sproxhdr_new TYPE sprx_hdr_t !it_sproxdat_new TYPE sprx_dat_t . ENDCLASS. CLASS zcl_abapgit_object_sqsc DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES: zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. " Downport original structures from " - IF_DBPROC_PROXY_UI " - IF_DBPROC_PROXY_BASIC_TYPES TYPES: ty_db_name TYPE c LENGTH 256, ty_abap_name TYPE c LENGTH 30, ty_param_direction TYPE c LENGTH 10, ty_param_kind TYPE c LENGTH 10, ty_ddic_name TYPE ddobjname, BEGIN OF ty_db_simple_type_s, name TYPE ty_db_name, length TYPE i, decs TYPE i, END OF ty_db_simple_type_s, BEGIN OF ty_abap_simple_type_s, name TYPE ty_abap_name, length TYPE i, decs TYPE i, END OF ty_abap_simple_type_s, BEGIN OF ty_abap_simple_type_ui_s, typ TYPE ty_abap_simple_type_s, text TYPE string, END OF ty_abap_simple_type_ui_s, BEGIN OF ty_header_ui_s, db_repository_package TYPE ty_db_name, db_repository_proc_name TYPE ty_db_name, db_catalog_schema TYPE ty_db_name, db_catalog_proc_name TYPE ty_db_name, read_only TYPE abap_bool, interface_pool TYPE ty_abap_name, END OF ty_header_ui_s, BEGIN OF ty_param_ui_s, position TYPE i, db_name TYPE ty_db_name, direction TYPE ty_param_direction, kind TYPE ty_param_kind, db_table_type_schema TYPE ty_db_name, db_table_type_name TYPE ty_db_name, db_table_type_is_ddic TYPE abap_bool, transfer_table_schema TYPE ty_db_name, transfer_table_name TYPE ty_db_name, abap_name TYPE ty_abap_name, abap_name_is_ro TYPE abap_bool, ddic_table TYPE ty_ddic_name, ddic_table_is_ro TYPE abap_bool, END OF ty_param_ui_s, ty_param_ui_t TYPE STANDARD TABLE OF ty_param_ui_s WITH KEY position, ty_abap_simple_type_ui_t TYPE STANDARD TABLE OF ty_abap_simple_type_ui_s WITH DEFAULT KEY, BEGIN OF ty_param_type_ui_s, param_position TYPE i, comp_index TYPE i, db_comp_name TYPE ty_db_name, abap_comp_name TYPE ty_abap_name, abap_comp_name_is_ro TYPE abap_bool, db_type TYPE ty_db_simple_type_s, db_type_text TYPE string, abap_type TYPE ty_abap_simple_type_ui_s, abap_type_is_ro TYPE abap_bool, abap_type_selection TYPE ty_abap_simple_type_ui_t, ddic_type TYPE ty_ddic_name, ddic_type_is_ro TYPE abap_bool, END OF ty_param_type_ui_s , ty_param_type_ui_t TYPE STANDARD TABLE OF ty_param_type_ui_s WITH KEY param_position comp_index, BEGIN OF ty_proxy, description TYPE ddtext, header TYPE ty_header_ui_s, parameters TYPE ty_param_ui_t, parameter_types TYPE ty_param_type_ui_t, END OF ty_proxy. DATA: mo_proxy TYPE REF TO object. METHODS: delete_interface_if_it_exists IMPORTING iv_package TYPE devclass iv_transport TYPE trkorr iv_interface TYPE ty_abap_name ii_log TYPE REF TO zif_abapgit_log RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_srfc DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_srvb DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. METHODS: clear_fields CHANGING cs_service_binding TYPE any, clear_field IMPORTING iv_fieldname TYPE csequence CHANGING cs_service_binding TYPE any. METHODS get_wb_object_operator RETURNING VALUE(ro_object_operator) TYPE REF TO object RAISING zcx_abapgit_exception . METHODS merge_object_data IMPORTING !io_object_data TYPE REF TO object RETURNING VALUE(ro_object_data_merged) TYPE REF TO if_wb_object_data_model RAISING zcx_abapgit_exception . METHODS get_object_data IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_input RETURNING VALUE(ro_object_data) TYPE REF TO if_wb_object_data_model RAISING zcx_abapgit_exception . METHODS is_ai_supported RETURNING VALUE(rv_ai_supported) TYPE abap_bool. METHODS publish RAISING zcx_abapgit_exception. METHODS unpublish. DATA: mi_persistence TYPE REF TO if_wb_object_persist, mv_is_inactive_supported TYPE abap_bool, mv_service_binding_key TYPE seu_objkey, mr_service_binding TYPE REF TO data, mr_srvb_svrs_config TYPE REF TO object, mo_object_operator TYPE REF TO object. ENDCLASS. CLASS zcl_abapgit_object_srvd DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. DATA mv_service_definition_key TYPE seu_objkey . DATA mr_service_definition TYPE REF TO data . CONSTANTS c_source_file TYPE string VALUE 'srvdsrv' ##NO_TEXT. CONSTANTS c_xml_parent_name TYPE string VALUE 'SRVD' ##NO_TEXT. DATA mo_object_operator TYPE REF TO object . METHODS clear_fields CHANGING !cs_metadata TYPE any . METHODS clear_field IMPORTING !iv_fieldname TYPE csequence CHANGING !cs_metadata TYPE any . METHODS get_object_data IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_input RETURNING VALUE(ro_object_data) TYPE REF TO if_wb_object_data_model RAISING zcx_abapgit_exception . METHODS get_wb_object_operator RETURNING VALUE(ro_object_operator) TYPE REF TO object RAISING zcx_abapgit_exception . METHODS merge_object_data IMPORTING !io_object_data TYPE REF TO object RETURNING VALUE(ro_object_data_merged) TYPE REF TO if_wb_object_data_model RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_ssfo DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. TYPES: ty_string_range TYPE RANGE OF string . CLASS-DATA gt_range_node_codes TYPE ty_string_range . CONSTANTS c_prefix TYPE string VALUE 'File:'. METHODS fix_ids IMPORTING !ii_xml_doc TYPE REF TO if_ixml_document . CLASS-METHODS sort_texts IMPORTING !ii_xml_doc TYPE REF TO if_ixml_document RAISING zcx_abapgit_exception . METHODS get_range_node_codes RETURNING VALUE(rt_range_node_codes) TYPE ty_string_range . METHODS deserialize_sources IMPORTING !ii_node TYPE REF TO if_ixml_node RAISING zcx_abapgit_exception. METHODS serialize_sources IMPORTING !ii_node TYPE REF TO if_ixml_node RAISING zcx_abapgit_exception. METHODS get_hash_for_path IMPORTING !ii_node TYPE REF TO if_ixml_node RETURNING VALUE(rv_hash) TYPE string RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_ssst DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. CONSTANTS: c_style_active TYPE tdactivate VALUE 'A'. PROTECTED SECTION. PRIVATE SECTION. METHODS validate_font IMPORTING iv_tdfamily TYPE tdfamily RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_stvi DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_transaction_variant, shdtvciu TYPE shdtvciu, shdttciu TYPE STANDARD TABLE OF shdttciu WITH DEFAULT KEY, shdfvguicu TYPE STANDARD TABLE OF shdfvguicu WITH DEFAULT KEY, shdtvsvciu TYPE STANDARD TABLE OF shdtvsvciu WITH DEFAULT KEY, END OF ty_transaction_variant. ENDCLASS. CLASS zcl_abapgit_object_styl DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_style, header TYPE itcda, paragraphs TYPE STANDARD TABLE OF itcdp WITH DEFAULT KEY, strings TYPE STANDARD TABLE OF itcds WITH DEFAULT KEY, tabs TYPE STANDARD TABLE OF itcdq WITH DEFAULT KEY, END OF ty_style. ENDCLASS. CLASS zcl_abapgit_object_sucu DEFINITION INHERITING FROM zcl_abapgit_objects_super CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_susc DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. CONSTANTS c_transobjecttype_class TYPE c LENGTH 1 VALUE 'C' ##NO_TEXT. METHODS has_authorization IMPORTING !iv_class TYPE tobc-oclss !iv_activity TYPE activ_auth RAISING zcx_abapgit_exception . METHODS is_used IMPORTING !iv_auth_object_class TYPE tobc-oclss RAISING zcx_abapgit_exception . PRIVATE SECTION. METHODS delete_class IMPORTING !iv_auth_object_class TYPE tobc-oclss . METHODS put_delete_to_transport RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_sush DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. " Note: This serializer is re-used by zcl_abapgit_object_tran for SU22 data " because transaction don't generate a separate SUSH object INTERFACES zif_abapgit_object . METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. DATA ms_key TYPE usobkey. METHODS clear_metadata CHANGING cs_data_head TYPE any ct_usobx TYPE STANDARD TABLE ct_usobt TYPE STANDARD TABLE. ENDCLASS. CLASS zcl_abapgit_object_suso DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_longtext_id_suso TYPE dokil-id VALUE 'UO'. DATA: mv_objectname TYPE tobj-objct. METHODS: delete_documentation RAISING zcx_abapgit_exception, pre_check RAISING zcx_abapgit_exception, regenerate_sap_all. ENDCLASS. CLASS zcl_abapgit_object_swcr DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS: zif_abapgit_object~changed_by REDEFINITION. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_sxci DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_classic_badi_implementation, implementation_data TYPE impl_data, function_codes TYPE seex_fcode_table, control_composites TYPE seex_coco_table, customer_includes TYPE seex_table_table, screens TYPE seex_screen_table, filters TYPE seex_filter_table, END OF ty_classic_badi_implementation. ENDCLASS. CLASS zcl_abapgit_object_sxsd DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL. PROTECTED SECTION. PRIVATE SECTION. DATA ms_badi_attr TYPE sxc_attr. ENDCLASS. CLASS zcl_abapgit_object_tabl DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. "! get additional data like table authorization group "! @parameter iv_tabname | name of the table METHODS read_extras IMPORTING iv_tabname TYPE ddobjname RETURNING VALUE(rs_tabl_extras) TYPE zif_abapgit_object_tabl=>ty_tabl_extras RAISING zcx_abapgit_exception. "! Update additional data "! @parameter iv_tabname | name of the table "! @parameter is_tabl_extras | additional table data METHODS update_extras IMPORTING iv_tabname TYPE ddobjname is_tabl_extras TYPE zif_abapgit_object_tabl=>ty_tabl_extras RAISING zcx_abapgit_exception. "! Delete additional data "! @parameter iv_tabname | name of the table METHODS delete_extras IMPORTING iv_tabname TYPE ddobjname. "! Serialize IDoc Segment type/definition if exits "! @raising zcx_abapgit_exception | Exceptions METHODS serialize_idoc_segment CHANGING cs_internal TYPE zif_abapgit_object_tabl=>ty_internal RAISING zcx_abapgit_exception. "! Deserialize IDoc Segment type/definition if exits "! @parameter iv_package | Target package "! @parameter rv_deserialized | It's a segment and was deserialized "! @raising zcx_abapgit_exception | Exceptions METHODS deserialize_idoc_segment IMPORTING is_internal TYPE zif_abapgit_object_tabl=>ty_internal iv_transport TYPE trkorr iv_package TYPE devclass RETURNING VALUE(rv_deserialized) TYPE abap_bool RAISING zcx_abapgit_exception. "! Delete the IDoc Segment type if exists "! @parameter rv_deleted | It's a segment and was deleted "! @raising zcx_abapgit_exception | Exceptions METHODS delete_idoc_segment RETURNING VALUE(rv_deleted) TYPE abap_bool RAISING zcx_abapgit_exception. PRIVATE SECTION. CONSTANTS c_longtext_id_tabl TYPE dokil-id VALUE 'TB' ##NO_TEXT. METHODS deserialize_indexes IMPORTING !is_internal TYPE zif_abapgit_object_tabl=>ty_internal RAISING zcx_abapgit_exception . METHODS clear_dd03p_fields CHANGING !ct_dd03p TYPE zif_abapgit_object_tabl=>ty_dd03p_tt . "! Check if structure is an IDoc segment "! @parameter rv_is_idoc_segment | It's an IDoc segment or not METHODS is_idoc_segment RETURNING VALUE(rv_is_idoc_segment) TYPE abap_bool . METHODS clear_dd03p_fields_common CHANGING !cs_dd03p TYPE dd03p . METHODS clear_dd03p_fields_dataelement CHANGING !cs_dd03p TYPE dd03p . METHODS serialize_texts CHANGING !cs_internal TYPE zif_abapgit_object_tabl=>ty_internal RAISING zcx_abapgit_exception . METHODS deserialize_texts CHANGING !cs_internal TYPE zif_abapgit_object_tabl=>ty_internal RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_tabl_compar DEFINITION CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_abapgit_comparator. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item. PROTECTED SECTION. TYPES: ty_founds TYPE STANDARD TABLE OF rsfindlst WITH NON-UNIQUE DEFAULT KEY. TYPES: ty_seu_obj TYPE STANDARD TABLE OF seu_obj WITH NON-UNIQUE DEFAULT KEY. DATA ms_item TYPE zif_abapgit_definitions=>ty_item. METHODS get_where_used_recursive IMPORTING !iv_object_name TYPE csequence !iv_depth TYPE i !iv_object_type TYPE euobj-id !it_scope TYPE ty_seu_obj RETURNING VALUE(rt_founds_all) TYPE ty_founds RAISING zcx_abapgit_exception. METHODS is_structure_used_in_db_table IMPORTING !iv_object_name TYPE dd02v-tabname RETURNING VALUE(rv_is_structure_used_in_db_tab) TYPE abap_bool RAISING zcx_abapgit_exception. METHODS validate IMPORTING !ii_remote_version TYPE REF TO zif_abapgit_xml_input !ii_local_version TYPE REF TO zif_abapgit_xml_input !ii_log TYPE REF TO zif_abapgit_log RETURNING VALUE(rv_message) TYPE string RAISING zcx_abapgit_exception. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_tabl_ddl DEFINITION FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS read_data IMPORTING !iv_name TYPE tadir-obj_name RETURNING VALUE(rs_data) TYPE zif_abapgit_object_tabl=>ty_internal . METHODS serialize IMPORTING !is_data TYPE zif_abapgit_object_tabl=>ty_internal RETURNING VALUE(rv_ddl) TYPE string . METHODS deserialize IMPORTING !iv_ddl TYPE string RETURNING VALUE(rs_data) TYPE zif_abapgit_object_tabl=>ty_internal . METHODS serialize_adt IMPORTING !iv_name TYPE tadir-obj_name RETURNING VALUE(rv_ddl) TYPE string RAISING cx_static_check . PROTECTED SECTION. PRIVATE SECTION. METHODS parse_top_annotations CHANGING !cs_data TYPE zif_abapgit_object_tabl=>ty_internal !cv_ddl TYPE string . METHODS parse_field_annotations EXPORTING !es_dd08v TYPE dd08v CHANGING !cv_ddl TYPE string . METHODS parse_field IMPORTING !iv_field TYPE string CHANGING !cs_data TYPE zif_abapgit_object_tabl=>ty_internal . METHODS serialize_top IMPORTING !is_data TYPE zif_abapgit_object_tabl=>ty_internal RETURNING VALUE(rv_ddl) TYPE string . METHODS serialize_extend IMPORTING !is_dd03p TYPE dd03p !is_data TYPE zif_abapgit_object_tabl=>ty_internal RETURNING VALUE(rv_ddl) TYPE string . METHODS serialize_field_annotations IMPORTING !iv_fieldname TYPE clike !is_data TYPE zif_abapgit_object_tabl=>ty_internal RETURNING VALUE(rv_ddl) TYPE string . METHODS serialize_fkey_annotations IMPORTING !iv_fieldname TYPE clike !is_data TYPE zif_abapgit_object_tabl=>ty_internal RETURNING VALUE(rv_ddl) TYPE string . METHODS serialize_field_foreign_key IMPORTING !iv_fieldname TYPE clike !is_data TYPE zif_abapgit_object_tabl=>ty_internal RETURNING VALUE(rv_ddl) TYPE string . METHODS serialize_value_help IMPORTING !iv_fieldname TYPE clike !is_data TYPE zif_abapgit_object_tabl=>ty_internal RETURNING VALUE(rv_ddl) TYPE string . METHODS escape_string IMPORTING !iv_string TYPE clike RETURNING VALUE(rv_string) TYPE string . METHODS unescape_string IMPORTING !iv_string TYPE clike RETURNING VALUE(rv_string) TYPE string . METHODS serialize_type IMPORTING !is_dd03p TYPE dd03p RETURNING VALUE(rv_type) TYPE string . METHODS parse_type IMPORTING !iv_token TYPE string CHANGING !cs_dd03p TYPE dd03p . ENDCLASS. CLASS zcl_abapgit_object_tobj DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_tobj, tddat TYPE tddat, tvdir TYPE tvdir, tvimf TYPE STANDARD TABLE OF tvimf WITH DEFAULT KEY, END OF ty_tobj. METHODS: read_extra IMPORTING iv_tabname TYPE vim_name RETURNING VALUE(rs_tobj) TYPE ty_tobj, update_extra IMPORTING is_tobj TYPE ty_tobj, delete_extra IMPORTING iv_tabname TYPE vim_name. ENDCLASS. CLASS zcl_abapgit_object_tran DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. TYPES: ty_param_values TYPE STANDARD TABLE OF rsparam WITH NON-UNIQUE DEFAULT KEY , ty_tstca TYPE STANDARD TABLE OF tstca WITH DEFAULT KEY. CONSTANTS: c_oo_program TYPE c LENGTH 9 VALUE '\PROGRAM=' ##NO_TEXT, c_oo_class TYPE c LENGTH 7 VALUE '\CLASS=' ##NO_TEXT, c_oo_method TYPE c LENGTH 8 VALUE '\METHOD=' ##NO_TEXT, c_oo_tcode TYPE tcode VALUE 'OS_APPLICATION' ##NO_TEXT, c_oo_frclass TYPE c LENGTH 30 VALUE 'CLASS' ##NO_TEXT, c_oo_frmethod TYPE c LENGTH 30 VALUE 'METHOD' ##NO_TEXT, c_oo_frupdtask TYPE c LENGTH 30 VALUE 'UPDATE_MODE' ##NO_TEXT, c_oo_synchron TYPE c VALUE 'S' ##NO_TEXT, c_oo_asynchron TYPE c VALUE 'U' ##NO_TEXT, c_true TYPE c VALUE 'X' ##NO_TEXT, c_false TYPE c VALUE space ##NO_TEXT, BEGIN OF c_variant_type, dialog TYPE rglif-docutype VALUE 'D' ##NO_TEXT, report TYPE rglif-docutype VALUE 'R' ##NO_TEXT, variant TYPE rglif-docutype VALUE 'V' ##NO_TEXT, parameters TYPE rglif-docutype VALUE 'P' ##NO_TEXT, object TYPE rglif-docutype VALUE 'O' ##NO_TEXT, END OF c_variant_type. DATA: mt_bcdata TYPE STANDARD TABLE OF bdcdata . METHODS transaction_read IMPORTING iv_transaction TYPE tcode EXPORTING es_transaction TYPE tstc es_gui_attr TYPE tstcc RAISING zcx_abapgit_exception. METHODS shift_param CHANGING !ct_rsparam TYPE s_param !cs_tstcp TYPE tstcp . METHODS add_data IMPORTING !iv_fnam TYPE bdcdata-fnam !iv_fval TYPE clike . METHODS call_se93 RAISING zcx_abapgit_exception . METHODS set_oo_parameters IMPORTING !it_rsparam TYPE s_param CHANGING !cs_rsstcd TYPE rsstcd . METHODS split_parameters CHANGING !ct_rsparam TYPE s_param !cs_rsstcd TYPE rsstcd !cs_tstcp TYPE tstcp !cs_tstc TYPE tstc . METHODS split_parameters_comp IMPORTING !ig_type TYPE any !ig_param TYPE any CHANGING !cg_value TYPE any . METHODS serialize_texts IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS deserialize_texts IMPORTING !ii_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception . METHODS deserialize_oo_transaction IMPORTING !iv_package TYPE devclass !is_tstc TYPE tstc !is_tstcc TYPE tstcc !is_tstct TYPE tstct !is_rsstcd TYPE rsstcd RAISING zcx_abapgit_exception . METHODS save_authorizations IMPORTING iv_transaction TYPE tstc-tcode it_authorizations TYPE ty_tstca RAISING zcx_abapgit_exception. METHODS clear_functiongroup_globals. METHODS is_variant_transaction IMPORTING is_tstcp TYPE tstcp RETURNING VALUE(rv_variant_transaction) TYPE abap_bool. ENDCLASS. CLASS zcl_abapgit_object_ttyp DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. " Fields that are not part of dd40v TYPES: BEGIN OF ty_extra, abap_language_version TYPE uccheck, END OF ty_extra. CONSTANTS c_longtext_id_ttyp TYPE dokil-id VALUE 'TT'. ENDCLASS. CLASS zcl_abapgit_object_type DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: c_prefix TYPE c LENGTH 3 VALUE '%_C'. METHODS read EXPORTING ev_ddtext TYPE ddtypet-ddtext et_source TYPE abaptxt255_tab RAISING zcx_abapgit_exception. METHODS create IMPORTING iv_ddtext TYPE ddtypet-ddtext it_source TYPE abaptxt255_tab iv_devclass TYPE devclass RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_ucsa DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: BEGIN OF c_version, active TYPE r3state VALUE 'A', inactive TYPE r3state VALUE 'I', END OF c_version . TYPES: ty_id TYPE c LENGTH 30. METHODS: get_persistence IMPORTING iv_id TYPE ty_id RETURNING VALUE(ro_persistence) TYPE REF TO object, clear_dynamic_fields CHANGING cg_complete_comm_assembly TYPE any, clear_field IMPORTING iv_fieldname TYPE csequence CHANGING cg_header TYPE any. ENDCLASS. CLASS zcl_abapgit_object_udmo DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. METHODS corr_insert REDEFINITION . PRIVATE SECTION. TYPES: " You are reminded that the text serialisation / de-serialisation methods depend upon a common type. " To make the dependency explicit, there is one common definition. BEGIN OF ty_udmo_text_type. TYPES sprache TYPE dm40t-sprache. TYPES dmoid TYPE dm40t-dmoid. TYPES langbez TYPE dm40t-langbez. TYPES as4local TYPE dm40t-as4local. TYPES END OF ty_udmo_text_type . DATA mv_data_model TYPE uddmodl . DATA mv_text_object TYPE doku_obj . DATA mv_lxe_text_name TYPE lxeobjname . DATA mv_activation_state TYPE as4local . DATA ms_object_type TYPE rsdeo . CONSTANTS c_transport_object_class TYPE trobjtype VALUE 'SUDM' ##NO_TEXT. CONSTANTS c_lxe_text_type TYPE lxeobjtype VALUE 'IM' ##NO_TEXT. CONSTANTS c_correction_object_type TYPE rsdeo-objtype VALUE 'UDMO' ##NO_TEXT. CONSTANTS c_active_state TYPE as4local VALUE 'A' ##NO_TEXT. METHODS is_name_permitted RAISING zcx_abapgit_exception . METHODS update_tree . METHODS serialize_short_texts IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS deserialize_short_texts IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception . METHODS serialize_long_texts IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS deserialize_long_texts IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception . METHODS serialize_entities IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . METHODS deserialize_entities IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception . METHODS access_modify RETURNING VALUE(rv_result) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS access_free RETURNING VALUE(rv_result) TYPE abap_bool RAISING zcx_abapgit_exception . METHODS deserialize_model IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception . METHODS serialize_model IMPORTING !io_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_ueno DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. TYPES BEGIN OF ty_docu. TYPES language TYPE dm40t-sprache. TYPES header TYPE thead. TYPES content TYPE xstring. TYPES itf TYPE tsftext. TYPES END OF ty_docu. TYPES ty_docu_lines TYPE STANDARD TABLE OF ty_docu WITH DEFAULT KEY. DATA mv_entity_id TYPE udentity. CONSTANTS c_text_object_type TYPE lxeobjtype VALUE 'IM' ##NO_TEXT. CONSTANTS c_active_state TYPE as4local VALUE 'A' ##NO_TEXT. METHODS build_text_name IMPORTING iv_id TYPE tdid RETURNING VALUE(rv_result) TYPE doku_obj. METHODS is_name_permitted RAISING zcx_abapgit_exception. METHODS delete_docu_uen RAISING zcx_abapgit_exception. METHODS delete_docu_url RAISING zcx_abapgit_exception. METHODS delete_docu_usp RAISING zcx_abapgit_exception. METHODS deserialize_docu_uen IMPORTING io_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception. METHODS deserialize_docu_url IMPORTING io_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception. METHODS deserialize_docu_usp IMPORTING io_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception. METHODS serialize_docu_uen IMPORTING io_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception. METHODS serialize_docu_url IMPORTING io_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception. METHODS serialize_docu_xxxx IMPORTING iv_id TYPE tdid RETURNING VALUE(rt_result) TYPE ty_docu_lines. METHODS serialize_docu_usp IMPORTING io_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception. METHODS deserialize_docu_xxxx IMPORTING it_docu TYPE ty_docu_lines RAISING zcx_abapgit_exception. METHODS get_generic RETURNING VALUE(ro_generic) TYPE REF TO zcl_abapgit_objects_generic RAISING zcx_abapgit_exception . METHODS get_field_rules RETURNING VALUE(ro_result) TYPE REF TO zif_abapgit_field_rules. ENDCLASS. CLASS zcl_abapgit_object_uiad DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION . METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_uipg DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION . METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_uist DEFINITION INHERITING FROM zcl_abapgit_object_common_aff FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS zif_abapgit_object~changed_by REDEFINITION . METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_vcls DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. * See include MTOBJCON: CONSTANTS c_cluster_type TYPE c VALUE 'C' ##NO_TEXT. CONSTANTS c_mode_insert TYPE obj_para-maint_mode VALUE 'I' ##NO_TEXT. METHODS is_locked IMPORTING !iv_tabname TYPE tabname !iv_argument TYPE seqg3-garg RETURNING VALUE(rv_is_locked) TYPE abap_bool RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_view DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. "! get additional data like table authorization group "! @parameter iv_name | name of the view METHODS read_extras IMPORTING iv_name TYPE ddobjname RETURNING VALUE(rs_tabl_extras) TYPE zif_abapgit_object_tabl=>ty_tabl_extras RAISING zcx_abapgit_exception. "! Update additional data "! @parameter iv_name | name of the table "! @parameter iv_transport | transport request "! @parameter is_tabl_extras | additional view data METHODS update_extras IMPORTING iv_name TYPE ddobjname iv_transport TYPE trkorr is_tabl_extras TYPE zif_abapgit_object_tabl=>ty_tabl_extras RAISING zcx_abapgit_exception. "! Delete additional data "! @parameter iv_name | name of the view "! @parameter iv_transport | transport request METHODS delete_extras IMPORTING iv_name TYPE ddobjname iv_transport TYPE trkorr RAISING zcx_abapgit_exception. METHODS insert_transport IMPORTING iv_name TYPE ddobjname iv_transport TYPE trkorr RAISING zcx_abapgit_exception. PRIVATE SECTION. TYPES: ty_dd26v TYPE STANDARD TABLE OF dd26v WITH NON-UNIQUE DEFAULT KEY, ty_dd27p TYPE STANDARD TABLE OF dd27p WITH NON-UNIQUE DEFAULT KEY, ty_dd28j TYPE STANDARD TABLE OF dd28j WITH NON-UNIQUE DEFAULT KEY, ty_dd28v TYPE STANDARD TABLE OF dd28v WITH NON-UNIQUE DEFAULT KEY, BEGIN OF ty_dd25_text, ddlanguage TYPE dd25t-ddlanguage, ddtext TYPE dd25t-ddtext, END OF ty_dd25_text , ty_dd25_texts TYPE STANDARD TABLE OF ty_dd25_text. CONSTANTS c_longtext_id_view TYPE dokil-id VALUE 'VW'. METHODS: read_view IMPORTING iv_language TYPE sy-langu EXPORTING ev_state TYPE ddgotstate es_dd25v TYPE dd25v es_dd09l TYPE dd09l et_dd26v TYPE ty_dd26v et_dd27p TYPE ty_dd27p et_dd28j TYPE ty_dd28j et_dd28v TYPE ty_dd28v es_extras TYPE zif_abapgit_object_tabl=>ty_tabl_extras RAISING zcx_abapgit_exception, serialize_texts IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception, deserialize_texts IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_input is_dd25v TYPE dd25v RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_w3xx_super DEFINITION INHERITING FROM zcl_abapgit_objects_super ABSTRACT CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . TYPES: ty_wwwparams_tt TYPE STANDARD TABLE OF wwwparams WITH DEFAULT KEY . CONSTANTS: BEGIN OF c_param_names, version TYPE w3_name VALUE 'version', fileext TYPE w3_name VALUE 'fileextension', filesize TYPE w3_name VALUE 'filesize', filename TYPE w3_name VALUE 'filename', mimetype TYPE w3_name VALUE 'mimetype', END OF c_param_names . METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. TYPES ty_bdcdata TYPE STANDARD TABLE OF bdcdata WITH NON-UNIQUE DEFAULT KEY. METHODS change_bdc_jump_data ABSTRACT CHANGING ct_bdcdata TYPE ty_bdcdata. PRIVATE SECTION. DATA ms_key TYPE wwwdatatab. METHODS get_ext IMPORTING it_params TYPE ty_wwwparams_tt RETURNING VALUE(rv_ext) TYPE string RAISING zcx_abapgit_exception. METHODS normalize_params IMPORTING iv_size TYPE i CHANGING ct_params TYPE ty_wwwparams_tt " Param table to patch RAISING zcx_abapgit_exception. METHODS strip_params CHANGING ct_params TYPE ty_wwwparams_tt RAISING zcx_abapgit_exception. METHODS find_param IMPORTING it_params TYPE ty_wwwparams_tt iv_name TYPE w3_name RETURNING VALUE(rv_value) TYPE string RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_w3ht DEFINITION INHERITING FROM zcl_abapgit_object_w3xx_super FINAL. PROTECTED SECTION. METHODS: change_bdc_jump_data REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_w3mi DEFINITION INHERITING FROM zcl_abapgit_object_w3xx_super FINAL. PROTECTED SECTION. METHODS: change_bdc_jump_data REDEFINITION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_wapa DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_page, attributes TYPE o2pagattr, event_handlers TYPE o2pagevh_tabletype, parameters TYPE o2pagpar_tabletype, types TYPE rswsourcet, END OF ty_page. TYPES: ty_pages_tt TYPE STANDARD TABLE OF ty_page WITH DEFAULT KEY. CONSTANTS: c_active TYPE so2_version VALUE 'A'. METHODS: get_page_content IMPORTING io_page TYPE REF TO cl_o2_api_pages RETURNING VALUE(rv_content) TYPE xstring RAISING zcx_abapgit_exception, to_page_content IMPORTING iv_content TYPE xstring RETURNING VALUE(rt_content) TYPE o2pageline_table RAISING zcx_abapgit_exception, read_page IMPORTING is_page TYPE o2pagattr iv_no_files_add TYPE abap_bool OPTIONAL RETURNING VALUE(rs_page) TYPE ty_page RAISING zcx_abapgit_exception, create_new_application IMPORTING is_attributes TYPE o2applattr it_nodes TYPE o2applnode_table it_navgraph TYPE o2applgrap_table RETURNING VALUE(ro_bsp) TYPE REF TO cl_o2_api_application RAISING zcx_abapgit_exception, create_new_page IMPORTING is_page_attributes TYPE o2pagattr RETURNING VALUE(ro_page) TYPE REF TO cl_o2_api_pages RAISING zcx_abapgit_exception, delete_superfluous_pages IMPORTING it_local_pages TYPE o2pagelist it_remote_pages TYPE ty_pages_tt RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_wdca DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. METHODS read EXPORTING !es_outline TYPE wdy_cfg_outline_data !et_data TYPE wdy_cfg_persist_data_appl_tab RAISING zcx_abapgit_exception . METHODS save IMPORTING !is_outline TYPE wdy_cfg_outline_data !it_data TYPE wdy_cfg_persist_data_appl_tab !iv_package TYPE devclass !iv_transport TYPE trkorr RAISING zcx_abapgit_exception . METHODS delete IMPORTING !iv_package TYPE devclass !iv_transport TYPE trkorr RAISING zcx_abapgit_exception . METHODS check IMPORTING !it_messages TYPE cts_messages RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_object_wdcc DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . METHODS constructor IMPORTING is_item TYPE zif_abapgit_definitions=>ty_item iv_language TYPE spras io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_type_not_supported. PROTECTED SECTION. METHODS after_import RAISING zcx_abapgit_exception . PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_object_wdya DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_longtext_id_wdya TYPE dokil-id VALUE 'WA'. METHODS read EXPORTING es_app TYPE wdy_application et_properties TYPE wdy_app_property_table RAISING zcx_abapgit_exception. METHODS save IMPORTING is_app TYPE wdy_application it_properties TYPE wdy_app_property_table iv_package TYPE devclass RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_wdyn DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_object . PROTECTED SECTION. PRIVATE SECTION. CONSTANTS c_longtext_id_wc TYPE dokil-id VALUE 'WC' ##NO_TEXT. CONSTANTS c_longtext_id_wd TYPE dokil-id VALUE 'WD' ##NO_TEXT. CONSTANTS c_longtext_name_wc TYPE string VALUE 'LONGTEXTS_WC' ##NO_TEXT. DATA: mt_components TYPE TABLE OF wdy_ctlr_compo_vrs, mt_sources TYPE TABLE OF wdy_ctlr_compo_source_vrs. METHODS: get_limu_objects RETURNING VALUE(rt_objects) TYPE wdy_md_transport_keys, read RETURNING VALUE(rs_component) TYPE wdy_component_metadata RAISING zcx_abapgit_exception, read_controller IMPORTING is_key TYPE wdy_md_controller_key RETURNING VALUE(rs_controller) TYPE wdy_md_controller_meta_data RAISING zcx_abapgit_exception, read_definition IMPORTING is_key TYPE wdy_md_component_key RETURNING VALUE(rs_definition) TYPE wdy_md_component_meta_data RAISING zcx_abapgit_exception, read_view IMPORTING is_key TYPE wdy_md_view_key RETURNING VALUE(rs_view) TYPE wdy_md_view_meta_data RAISING zcx_abapgit_exception, recover_controller IMPORTING is_controller TYPE wdy_md_controller_meta_data RAISING zcx_abapgit_exception, recover_definition IMPORTING is_definition TYPE wdy_md_component_meta_data iv_package TYPE devclass RAISING zcx_abapgit_exception, recover_view IMPORTING is_view TYPE wdy_md_view_meta_data RAISING zcx_abapgit_exception, unlock_definition IMPORTING is_component_key TYPE wdy_md_component_key, unlock_controller IMPORTING is_controller_key TYPE wdy_md_controller_key, unlock_view IMPORTING is_view_key TYPE wdy_md_view_key, delta_controller IMPORTING is_controller TYPE wdy_md_controller_meta_data RETURNING VALUE(rs_delta) TYPE svrs2_xversionable_object RAISING zcx_abapgit_exception, delta_definition IMPORTING is_definition TYPE wdy_md_component_meta_data VALUE(iv_package) TYPE devclass RETURNING VALUE(rs_delta) TYPE svrs2_xversionable_object RAISING zcx_abapgit_exception, delta_view IMPORTING is_view TYPE wdy_md_view_meta_data RETURNING VALUE(rs_delta) TYPE svrs2_xversionable_object RAISING zcx_abapgit_exception, deserialize_sources IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception, serialize_sources IMPORTING ii_xml TYPE REF TO zif_abapgit_xml_output RAISING zcx_abapgit_exception, add_fm_param_exporting IMPORTING iv_name TYPE string ig_value TYPE any CHANGING ct_param TYPE abap_func_parmbind_tab, add_fm_param_tables IMPORTING iv_name TYPE string CHANGING ct_value TYPE ANY TABLE ct_param TYPE abap_func_parmbind_tab, add_fm_exception IMPORTING iv_name TYPE string iv_value TYPE i CHANGING ct_exception TYPE abap_func_excpbind_tab, add_with_inactive_parts RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_object_webi DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_webi, veptext TYPE veptext, pvepheader TYPE STANDARD TABLE OF vepheader WITH DEFAULT KEY, pvepfunction TYPE STANDARD TABLE OF vepfunction WITH DEFAULT KEY, pvepfault TYPE STANDARD TABLE OF vepfault WITH DEFAULT KEY, pvepparameter TYPE STANDARD TABLE OF vepparameter WITH DEFAULT KEY, pveptype TYPE STANDARD TABLE OF veptype WITH DEFAULT KEY, pvepelemtype TYPE STANDARD TABLE OF vepelemtype WITH DEFAULT KEY, pveptabletype TYPE STANDARD TABLE OF veptabletype WITH DEFAULT KEY, pvepstrutype TYPE STANDARD TABLE OF vepstrutype WITH DEFAULT KEY, pveptypesoapext TYPE STANDARD TABLE OF veptypesoapext WITH DEFAULT KEY, pvepeletypsoap TYPE STANDARD TABLE OF vepeletypsoap WITH DEFAULT KEY, pveptabtypsoap TYPE STANDARD TABLE OF veptabtypsoap WITH DEFAULT KEY, pvepfuncsoapext TYPE STANDARD TABLE OF vepfuncsoapext WITH DEFAULT KEY, pvepfieldref TYPE STANDARD TABLE OF vepfieldref WITH DEFAULT KEY, pvependpoint TYPE STANDARD TABLE OF vependpoint WITH DEFAULT KEY, pvepvisoapext TYPE STANDARD TABLE OF vepvisoapext WITH DEFAULT KEY, pvepparasoapext TYPE STANDARD TABLE OF vepparasoapext WITH DEFAULT KEY, pwsheader TYPE STANDARD TABLE OF wsheader WITH DEFAULT KEY, pwssoapprop TYPE STANDARD TABLE OF wssoapprop WITH DEFAULT KEY, END OF ty_webi. DATA: mi_vi TYPE REF TO if_ws_md_vif. METHODS: handle_endpoint IMPORTING is_webi TYPE ty_webi RAISING zcx_abapgit_exception cx_ws_md_exception, handle_types IMPORTING is_webi TYPE ty_webi RAISING zcx_abapgit_exception cx_ws_md_exception, handle_soap IMPORTING is_webi TYPE ty_webi RAISING zcx_abapgit_exception cx_ws_md_exception, handle_function IMPORTING is_webi TYPE ty_webi RAISING zcx_abapgit_exception cx_ws_md_exception. METHODS handle_single_parameter IMPORTING iv_parameter_type TYPE vepparamtype iv_name TYPE vepparameter-vepparam ii_function TYPE REF TO if_ws_md_vif_func RETURNING VALUE(ri_parameter) TYPE REF TO if_ws_md_vif_param RAISING zcx_abapgit_exception cx_ws_md_exception. METHODS sort CHANGING cs_webi TYPE ty_webi. ENDCLASS. CLASS zcl_abapgit_object_xinx DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. METHODS constructor IMPORTING !is_item TYPE zif_abapgit_definitions=>ty_item !iv_language TYPE spras !io_files TYPE REF TO zcl_abapgit_objects_files OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params OPTIONAL RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_extension_index, dd12v TYPE dd12v, t_dd17v TYPE STANDARD TABLE OF dd17v WITH NON-UNIQUE DEFAULT KEY, END OF ty_extension_index. CONSTANTS: c_objtype_extension_index TYPE trobjtype VALUE 'XINX'. CONSTANTS c_longtext_id_xinx TYPE dokil-id VALUE 'XI'. DATA: mv_name TYPE ddobjname, mv_id TYPE ddobjectid. METHODS: xinx_delete_docu IMPORTING iv_objname TYPE ddobjname iv_id TYPE ddobjectid. ENDCLASS. CLASS zcl_abapgit_object_xslt DEFINITION INHERITING FROM zcl_abapgit_objects_super FINAL. PUBLIC SECTION. INTERFACES zif_abapgit_object. PROTECTED SECTION. PRIVATE SECTION. TYPES: BEGIN OF ty_extra, abap_language_version TYPE uccheck, END OF ty_extra. METHODS: get RETURNING VALUE(ro_xslt) TYPE REF TO cl_o2_api_xsltdesc RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_oo_factory DEFINITION . PUBLIC SECTION. CLASS-METHODS: get_by_type IMPORTING iv_object_type TYPE tadir-object RETURNING VALUE(ri_object_oriented_object) TYPE REF TO zif_abapgit_oo_object_fnc, get_by_name IMPORTING iv_object_name TYPE seoclsname RETURNING VALUE(ri_object_oriented_object) TYPE REF TO zif_abapgit_oo_object_fnc RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_oo_serializer DEFINITION CREATE PUBLIC . PUBLIC SECTION. METHODS serialize_abap_clif_source IMPORTING !is_class_key TYPE seoclskey RETURNING VALUE(rt_source) TYPE zif_abapgit_definitions=>ty_string_tt RAISING zcx_abapgit_exception cx_sy_dyn_call_error . METHODS are_test_classes_skipped RETURNING VALUE(rv_return) TYPE abap_bool . METHODS serialize_locals_imp IMPORTING !is_clskey TYPE seoclskey RETURNING VALUE(rt_source) TYPE zif_abapgit_definitions=>ty_string_tt RAISING zcx_abapgit_exception . METHODS serialize_locals_def IMPORTING !is_clskey TYPE seoclskey RETURNING VALUE(rt_source) TYPE zif_abapgit_definitions=>ty_string_tt RAISING zcx_abapgit_exception . METHODS serialize_testclasses IMPORTING !is_clskey TYPE seoclskey RETURNING VALUE(rt_source) TYPE zif_abapgit_definitions=>ty_string_tt RAISING zcx_abapgit_exception . METHODS serialize_macros IMPORTING !is_clskey TYPE seoclskey RETURNING VALUE(rt_source) TYPE zif_abapgit_definitions=>ty_string_tt RAISING zcx_abapgit_exception . PROTECTED SECTION. PRIVATE SECTION. DATA mv_skip_testclass TYPE abap_bool . METHODS calculate_skip_testclass IMPORTING !it_source TYPE zif_abapgit_definitions=>ty_string_tt RETURNING VALUE(rv_skip_testclass) TYPE abap_bool . METHODS serialize_abap_old IMPORTING !is_clskey TYPE seoclskey RETURNING VALUE(rt_source) TYPE zif_abapgit_definitions=>ty_string_tt RAISING zcx_abapgit_exception . METHODS serialize_abap_new IMPORTING !is_clskey TYPE seoclskey RETURNING VALUE(rt_source) TYPE zif_abapgit_definitions=>ty_string_tt RAISING zcx_abapgit_exception cx_sy_dyn_call_error . METHODS remove_signatures CHANGING !ct_source TYPE zif_abapgit_definitions=>ty_string_tt . METHODS read_include IMPORTING !is_clskey TYPE seoclskey !iv_type TYPE seop_include_ext_app RETURNING VALUE(rt_source) TYPE seop_source_string RAISING zcx_abapgit_exception. METHODS reduce CHANGING !ct_source TYPE zif_abapgit_definitions=>ty_string_tt . ENDCLASS. CLASS zcl_abapgit_path DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. CLASS-METHODS split_file_location IMPORTING iv_fullpath TYPE string EXPORTING ev_path TYPE string ev_filename TYPE string. CLASS-METHODS is_root IMPORTING iv_path TYPE string RETURNING VALUE(rv_yes) TYPE abap_bool. CLASS-METHODS is_subdir IMPORTING iv_path TYPE string iv_parent TYPE string RETURNING VALUE(rv_yes) TYPE abap_bool. CLASS-METHODS change_dir IMPORTING iv_cur_dir TYPE string iv_cd TYPE string RETURNING VALUE(rv_path) TYPE string. CLASS-METHODS get_filename_from_syspath IMPORTING iv_path TYPE string RETURNING VALUE(rv_filename) TYPE string. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_po_file DEFINITION FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_i18n_file. METHODS constructor IMPORTING iv_lang TYPE laiso iv_suppress_comments TYPE abap_bool DEFAULT abap_false. METHODS parse IMPORTING iv_xdata TYPE xstring RAISING zcx_abapgit_exception. METHODS push_text_pairs IMPORTING iv_objtype TYPE trobjtype iv_objname TYPE lxeobjname it_text_pairs TYPE zif_abapgit_lxe_texts=>ty_text_pairs RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. CONSTANTS: BEGIN OF c_comment, translator TYPE i VALUE 1, extracted TYPE i VALUE 2, reference TYPE i VALUE 3, flag TYPE i VALUE 4, previous TYPE i VALUE 5, END OF c_comment. TYPES: BEGIN OF ty_comment, kind TYPE i, text TYPE string, END OF ty_comment. TYPES: BEGIN OF ty_msg_pair, source TYPE string, target TYPE string, comments TYPE STANDARD TABLE OF ty_comment WITH KEY kind text, END OF ty_msg_pair. DATA mv_lang TYPE laiso. DATA mv_suppress_comments TYPE abap_bool. DATA mt_pairs TYPE SORTED TABLE OF ty_msg_pair WITH UNIQUE KEY source. METHODS build_po_body RETURNING VALUE(ro_buf) TYPE REF TO zcl_abapgit_string_buffer. METHODS build_po_head RETURNING VALUE(ro_buf) TYPE REF TO zcl_abapgit_string_buffer. METHODS parse_po IMPORTING iv_data TYPE string RAISING zcx_abapgit_exception. CLASS-METHODS get_comment_marker IMPORTING iv_comment_kind TYPE i RETURNING VALUE(rv_marker) TYPE string. CLASS-METHODS quote IMPORTING iv_text TYPE string RETURNING VALUE(rv_text) TYPE string. CLASS-METHODS unquote IMPORTING iv_text TYPE string RETURNING VALUE(rv_text) TYPE string RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_progress DEFINITION FINAL CREATE PROTECTED . PUBLIC SECTION. INTERFACES zif_abapgit_progress . CLASS-METHODS set_instance IMPORTING !ii_progress TYPE REF TO zif_abapgit_progress . CLASS-METHODS get_instance IMPORTING !iv_total TYPE i RETURNING VALUE(ri_progress) TYPE REF TO zif_abapgit_progress . PROTECTED SECTION. DATA mv_total TYPE i . CLASS-DATA gi_progress TYPE REF TO zif_abapgit_progress . METHODS calc_pct IMPORTING !iv_current TYPE i RETURNING VALUE(rv_pct) TYPE i . PRIVATE SECTION. DATA mv_cv_time_next TYPE sy-uzeit . DATA mv_cv_datum_next TYPE sy-datum . ENDCLASS. CLASS zcl_abapgit_properties_file DEFINITION FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES zif_abapgit_i18n_file. METHODS constructor IMPORTING iv_lang TYPE laiso. METHODS parse IMPORTING iv_xdata TYPE xstring RAISING zcx_abapgit_exception. METHODS push_text_pairs IMPORTING it_translation TYPE string_table. METHODS get_translations EXPORTING ev_data TYPE data RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. DATA mv_lang TYPE laiso. DATA mt_translation TYPE string_table. ENDCLASS. CLASS zcl_abapgit_sotr_handler DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. TYPES: BEGIN OF ty_sotr, header TYPE sotr_head, entries TYPE sotr_text_tt, END OF ty_sotr. TYPES: ty_sotr_tt TYPE STANDARD TABLE OF ty_sotr WITH DEFAULT KEY. TYPES: ty_sotr_use_tt TYPE STANDARD TABLE OF sotr_use WITH DEFAULT KEY. CLASS-METHODS read_sotr IMPORTING !iv_pgmid TYPE tadir-pgmid DEFAULT 'R3TR' !iv_object TYPE trobjtype !iv_obj_name TYPE csequence !io_xml TYPE REF TO zif_abapgit_xml_output OPTIONAL !io_i18n_params TYPE REF TO zcl_abapgit_i18n_params EXPORTING !et_sotr TYPE ty_sotr_tt !et_sotr_use TYPE ty_sotr_use_tt RAISING zcx_abapgit_exception. CLASS-METHODS create_sotr IMPORTING !iv_package TYPE devclass !io_xml TYPE REF TO zif_abapgit_xml_input RAISING zcx_abapgit_exception. CLASS-METHODS create_sotr_from_data IMPORTING !iv_package TYPE devclass !it_sotr TYPE ty_sotr_tt !it_sotr_use TYPE ty_sotr_use_tt RAISING zcx_abapgit_exception. CLASS-METHODS delete_sotr IMPORTING !iv_pgmid TYPE tadir-pgmid DEFAULT 'R3TR' !iv_object TYPE trobjtype !iv_obj_name TYPE csequence RAISING zcx_abapgit_exception. CLASS-METHODS delete_sotr_package IMPORTING !iv_package TYPE devclass RAISING zcx_abapgit_exception. CLASS-METHODS change_sotr_package IMPORTING !iv_old_package TYPE devclass !iv_new_package TYPE devclass RAISING zcx_abapgit_exception. PROTECTED SECTION. CLASS-METHODS get_sotr_usage IMPORTING !iv_pgmid TYPE tadir-pgmid !iv_object TYPE trobjtype !iv_obj_name TYPE csequence RETURNING VALUE(rt_sotr_use) TYPE ty_sotr_use_tt. CLASS-METHODS get_sotr_4_concept IMPORTING !iv_concept TYPE sotr_conc RETURNING VALUE(rs_sotr) TYPE ty_sotr. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_status_calc DEFINITION FINAL CREATE PRIVATE. PUBLIC SECTION. INTERFACES zif_abapgit_status_calc. CLASS-METHODS get_instance IMPORTING !iv_root_package TYPE devclass !io_dot TYPE REF TO zcl_abapgit_dot_abapgit RETURNING VALUE(ri_instance) TYPE REF TO zif_abapgit_status_calc. METHODS constructor IMPORTING !iv_root_package TYPE devclass !io_dot TYPE REF TO zcl_abapgit_dot_abapgit. PROTECTED SECTION. PRIVATE SECTION. DATA mv_root_package TYPE devclass. DATA mo_dot TYPE REF TO zcl_abapgit_dot_abapgit. METHODS process_local IMPORTING !it_local TYPE zif_abapgit_definitions=>ty_files_item_tt !it_state_idx TYPE zif_abapgit_git_definitions=>ty_file_signatures_ts CHANGING !ct_remote TYPE zif_abapgit_git_definitions=>ty_files_tt !ct_items TYPE zif_abapgit_definitions=>ty_items_tt !ct_results TYPE zif_abapgit_definitions=>ty_results_tt RAISING zcx_abapgit_exception. METHODS process_items IMPORTING !it_unprocessed_remote TYPE zif_abapgit_git_definitions=>ty_files_tt CHANGING !ct_items TYPE zif_abapgit_definitions=>ty_items_tt RAISING zcx_abapgit_exception. METHODS process_remote IMPORTING !it_local TYPE zif_abapgit_definitions=>ty_files_item_tt !it_unprocessed_remote TYPE zif_abapgit_git_definitions=>ty_files_tt !it_state_idx TYPE zif_abapgit_git_definitions=>ty_file_signatures_ts !it_items_idx TYPE zif_abapgit_definitions=>ty_items_ts CHANGING !ct_results TYPE zif_abapgit_definitions=>ty_results_tt RAISING zcx_abapgit_exception. CLASS-METHODS build_existing IMPORTING !is_local TYPE zif_abapgit_definitions=>ty_file_item !is_remote TYPE zif_abapgit_git_definitions=>ty_file !it_state TYPE zif_abapgit_git_definitions=>ty_file_signatures_ts RETURNING VALUE(rs_result) TYPE zif_abapgit_definitions=>ty_result. CLASS-METHODS build_new_local IMPORTING !is_local TYPE zif_abapgit_definitions=>ty_file_item RETURNING VALUE(rs_result) TYPE zif_abapgit_definitions=>ty_result. METHODS build_new_remote IMPORTING !is_remote TYPE zif_abapgit_git_definitions=>ty_file !it_items_idx TYPE zif_abapgit_definitions=>ty_items_ts !it_state_idx TYPE zif_abapgit_git_definitions=>ty_file_signatures_ts RETURNING VALUE(rs_result) TYPE zif_abapgit_definitions=>ty_result RAISING zcx_abapgit_exception. CLASS-METHODS get_object_package IMPORTING !iv_object TYPE tadir-object !iv_obj_name TYPE tadir-obj_name RETURNING VALUE(rv_devclass) TYPE devclass RAISING zcx_abapgit_exception. CLASS-METHODS check_local_remote_consistency IMPORTING !is_local TYPE zif_abapgit_definitions=>ty_file_item !is_remote TYPE zif_abapgit_git_definitions=>ty_file RAISING zcx_abapgit_exception. CLASS-METHODS ensure_state IMPORTING !it_local TYPE zif_abapgit_definitions=>ty_files_item_tt !it_cur_state TYPE zif_abapgit_git_definitions=>ty_file_signatures_tt RETURNING VALUE(rt_state) TYPE zif_abapgit_git_definitions=>ty_file_signatures_tt. ENDCLASS. CLASS zcl_abapgit_string_buffer DEFINITION FINAL CREATE PUBLIC . PUBLIC SECTION. CLASS-METHODS new RETURNING VALUE(ro_me) TYPE REF TO zcl_abapgit_string_buffer. METHODS add IMPORTING !iv_str TYPE string RETURNING VALUE(ro_me) TYPE REF TO zcl_abapgit_string_buffer. METHODS join_and_flush RETURNING VALUE(rv_str) TYPE string. METHODS join_w_newline_and_flush RETURNING VALUE(rv_str) TYPE string. METHODS join_w_space_and_flush RETURNING VALUE(rv_str) TYPE string. PROTECTED SECTION. PRIVATE SECTION. DATA mt_buffer TYPE string_table. ENDCLASS. CLASS zcl_abapgit_timer DEFINITION FINAL CREATE PRIVATE. PUBLIC SECTION. CLASS-METHODS create IMPORTING !iv_text TYPE string OPTIONAL !iv_count TYPE i OPTIONAL PREFERRED PARAMETER iv_text RETURNING VALUE(ro_timer) TYPE REF TO zcl_abapgit_timer. METHODS constructor IMPORTING !iv_text TYPE string OPTIONAL !iv_count TYPE i OPTIONAL. METHODS start RETURNING VALUE(ro_timer) TYPE REF TO zcl_abapgit_timer. METHODS end IMPORTING !iv_output_as_status_message TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rv_result) TYPE string. PROTECTED SECTION. PRIVATE SECTION. DATA mv_text TYPE string. DATA mv_count TYPE i. DATA mv_timer TYPE timestampl. ENDCLASS. CLASS zcl_abapgit_url DEFINITION FINAL CREATE PUBLIC . PUBLIC SECTION. CLASS-METHODS validate IMPORTING !iv_url TYPE string RAISING zcx_abapgit_exception . CLASS-METHODS host IMPORTING !iv_url TYPE string RETURNING VALUE(rv_host) TYPE string RAISING zcx_abapgit_exception . CLASS-METHODS name IMPORTING !iv_url TYPE string !iv_validate TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rv_name) TYPE string RAISING zcx_abapgit_exception . CLASS-METHODS path_name IMPORTING !iv_url TYPE string RETURNING VALUE(rv_path_name) TYPE string RAISING zcx_abapgit_exception . CLASS-METHODS is_abapgit_repo IMPORTING !iv_url TYPE string RETURNING VALUE(rv_abapgit) TYPE abap_bool . CLASS-METHODS url_address IMPORTING !iv_url TYPE string RETURNING VALUE(rv_adress) TYPE string RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. CLASS-METHODS regex IMPORTING !iv_url TYPE string EXPORTING !ev_host TYPE string !ev_path TYPE string !ev_name TYPE string RAISING zcx_abapgit_exception . ENDCLASS. CLASS zcl_abapgit_utils DEFINITION FINAL CREATE PUBLIC . PUBLIC SECTION. CLASS-METHODS is_binary IMPORTING !iv_data TYPE xstring RETURNING VALUE(rv_is_binary) TYPE abap_bool. CLASS-METHODS is_valid_email IMPORTING iv_email TYPE string RETURNING VALUE(rv_valid) TYPE abap_bool. CLASS-METHODS check_eol IMPORTING !iv_data TYPE string RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_abapgit_version DEFINITION FINAL CREATE PUBLIC . PUBLIC SECTION. CLASS-METHODS normalize IMPORTING !iv_version TYPE string RETURNING VALUE(rv_version) TYPE string . CLASS-METHODS conv_str_to_version IMPORTING !iv_version TYPE csequence RETURNING VALUE(rs_version) TYPE zif_abapgit_definitions=>ty_version RAISING zcx_abapgit_exception . CLASS-METHODS check_dependant_version IMPORTING !is_current TYPE zif_abapgit_definitions=>ty_version !is_dependant TYPE zif_abapgit_definitions=>ty_version RAISING zcx_abapgit_exception . CLASS-METHODS compare IMPORTING !iv_a TYPE string OPTIONAL !iv_b TYPE string OPTIONAL !is_a TYPE zif_abapgit_definitions=>ty_version OPTIONAL !is_b TYPE zif_abapgit_definitions=>ty_version OPTIONAL RETURNING VALUE(rv_result) TYPE i . CLASS-METHODS get_version_constant_value IMPORTING iv_version_constant TYPE string RETURNING VALUE(rv_version) TYPE string RAISING zcx_abapgit_exception. PROTECTED SECTION. PRIVATE SECTION. CLASS-METHODS version_to_numeric IMPORTING !iv_version TYPE string RETURNING VALUE(rv_version) TYPE i. ENDCLASS. CLASS zcl_abapgit_xml_pretty DEFINITION CREATE PUBLIC . PUBLIC SECTION. CLASS-METHODS print IMPORTING !iv_xml TYPE string !iv_ignore_errors TYPE abap_bool DEFAULT abap_true !iv_unpretty TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rv_xml) TYPE string RAISING zcx_abapgit_exception . PROTECTED SECTION. PRIVATE SECTION. CLASS-METHODS raise_error IMPORTING ii_parser TYPE REF TO if_ixml_parser RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_xml IMPLEMENTATION. METHOD constructor. mi_ixml = cl_ixml=>create( ). mi_xml_doc = mi_ixml->create_document( ). mv_filename = iv_filename. ENDMETHOD. METHOD error. IF ii_parser->num_errors( ) <> 0. raise_exception_for( ii_parser->get_error( 0 ) ). ENDIF. IF mv_filename IS INITIAL. zcx_abapgit_exception=>raise( |Error while parsing XML| ). ELSE. zcx_abapgit_exception=>raise( |Error while parsing XML file { mv_filename }| ). ENDIF. ENDMETHOD. METHOD parse. DATA: li_stream_factory TYPE REF TO if_ixml_stream_factory, li_istream TYPE REF TO if_ixml_istream, li_element TYPE REF TO if_ixml_element, li_version TYPE REF TO if_ixml_node, li_parser TYPE REF TO if_ixml_parser. ASSERT NOT iv_xml IS INITIAL. li_stream_factory = mi_ixml->create_stream_factory( ). li_istream = li_stream_factory->create_istream_string( iv_xml ). li_parser = mi_ixml->create_parser( stream_factory = li_stream_factory istream = li_istream document = mi_xml_doc ). li_parser->add_strip_space_element( ). IF li_parser->parse( ) <> 0. error( li_parser ). ENDIF. li_istream->close( ). li_element = mi_xml_doc->find_from_name_ns( depth = 0 name = c_abapgit_tag ). li_version = li_element->if_ixml_node~get_attributes( )->get_named_item_ns( c_attr_version ). IF li_version->get_value( ) <> zif_abapgit_version=>c_xml_version. raise_version_mismatch( li_version->get_value( ) ). ENDIF. * buffer serializer metadata. Git node will be removed lateron ms_metadata-class = li_element->get_attribute_ns( c_attr_serializer ). ms_metadata-version = li_element->get_attribute_ns( c_attr_serializer_version ). ENDMETHOD. METHOD raise_exception_for. DATA lv_message TYPE string. lv_message = |XML parser error: { ii_error->get_reason( ) }, | && |Line { ii_error->get_line( ) } | && |Col. { ii_error->get_column( ) }|. IF mv_filename IS NOT INITIAL. lv_message = lv_message && | File { mv_filename }|. ENDIF. zcx_abapgit_exception=>raise( lv_message ). ENDMETHOD. METHOD raise_version_mismatch. DATA lv_text TYPE string. lv_text = |The XML versions do not match, expected: { zif_abapgit_version=>c_xml_version }, actual: { iv_vers }|. IF mv_filename IS NOT INITIAL. lv_text = lv_text && |, file: { mv_filename }|. ENDIF. lv_text = lv_text && | (see https://docs.abapgit.org/other-xml-mismatch.html)|. zcx_abapgit_exception=>raise( lv_text ). ENDMETHOD. METHOD to_xml. * will render to codepage UTF-16 DATA: li_ostream TYPE REF TO if_ixml_ostream, li_renderer TYPE REF TO if_ixml_renderer, li_streamfactory TYPE REF TO if_ixml_stream_factory. li_streamfactory = mi_ixml->create_stream_factory( ). li_ostream = li_streamfactory->create_ostream_cstring( rv_xml ). li_renderer = mi_ixml->create_renderer( ostream = li_ostream document = mi_xml_doc ). li_renderer->set_normalizing( iv_normalize ). li_renderer->render( ). " handling of BOM moved to zcl_abapgit_convert=>string_to_xstring_utf8_bom ENDMETHOD. ENDCLASS. CLASS ZCL_ABAPGIT_XML_OUTPUT IMPLEMENTATION. METHOD build_asx_node. DATA: li_attr TYPE REF TO if_ixml_attribute. ri_element = mi_xml_doc->create_element_ns( name = 'abap' prefix = 'asx' ). li_attr = mi_xml_doc->create_attribute_ns( 'version' ). li_attr->if_ixml_node~set_value( '1.0' ). ri_element->set_attribute_node_ns( li_attr ). li_attr = mi_xml_doc->create_attribute_ns( name = 'asx' prefix = 'xmlns' ). li_attr->if_ixml_node~set_value( 'http://www.sap.com/abapxml' ). ri_element->set_attribute_node_ns( li_attr ). ENDMETHOD. METHOD zif_abapgit_xml_output~add. DATA: li_node TYPE REF TO if_ixml_node, li_doc TYPE REF TO if_ixml_document, lt_stab TYPE abap_trans_srcbind_tab. FIELD-SYMBOLS: LIKE LINE OF lt_stab. ASSERT NOT iv_name IS INITIAL. IF ig_data IS INITIAL. RETURN. ENDIF. APPEND INITIAL LINE TO lt_stab ASSIGNING . -name = iv_name. GET REFERENCE OF ig_data INTO -value. li_doc = cl_ixml=>create( )->create_document( ). CALL TRANSFORMATION id OPTIONS initial_components = 'suppress' value_handling = 'move' SOURCE (lt_stab) RESULT XML li_doc. li_node = mi_xml_doc->get_root( )->get_first_child( ). IF li_node IS BOUND. mi_xml_doc->get_root( )->get_first_child( )->get_first_child( )->append_child( li_doc->get_root( )->get_first_child( )->get_first_child( )->get_first_child( ) ). ELSE. mi_xml_doc->get_root( )->append_child( li_doc->get_root( )->get_first_child( ) ). ENDIF. ENDMETHOD. METHOD zif_abapgit_xml_output~add_xml. DATA: li_element TYPE REF TO if_ixml_element. li_element = mi_xml_doc->create_element( iv_name ). li_element->append_child( ii_xml ). mi_xml_doc->get_root( )->get_first_child( )->get_first_child( )->append_child( li_element ). ENDMETHOD. METHOD zif_abapgit_xml_output~render. DATA: li_git TYPE REF TO if_ixml_element, li_abap TYPE REF TO if_ixml_element. IF mi_raw IS INITIAL. li_abap ?= mi_xml_doc->get_root( )->get_first_child( ). mi_xml_doc->get_root( )->remove_child( li_abap ). IF li_abap IS INITIAL. li_abap = build_asx_node( ). ENDIF. ELSE. li_abap = mi_raw. ENDIF. li_git = mi_xml_doc->create_element( c_abapgit_tag ). li_git->set_attribute( name = c_attr_version value = zif_abapgit_version=>c_xml_version ). IF NOT is_metadata IS INITIAL. li_git->set_attribute( name = c_attr_serializer value = is_metadata-class ). li_git->set_attribute( name = c_attr_serializer_version value = is_metadata-version ). ENDIF. li_git->append_child( li_abap ). mi_xml_doc->get_root( )->append_child( li_git ). rv_xml = to_xml( iv_normalize ). ENDMETHOD. METHOD zif_abapgit_xml_output~set_raw. mi_raw = ii_raw. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_abapgit_objects IMPLEMENTATION. METHOD change_package_assignments. CALL FUNCTION 'TR_TADIR_INTERFACE' EXPORTING wi_tadir_pgmid = 'R3TR' wi_tadir_object = is_item-obj_type wi_tadir_obj_name = is_item-obj_name wi_tadir_devclass = is_item-devclass wi_test_modus = abap_false EXCEPTIONS OTHERS = 1. IF sy-subrc = 0. ii_log->add_success( iv_msg = |Object { is_item-obj_name } assigned to package { is_item-devclass }| is_item = is_item ). ELSE. ii_log->add_error( iv_msg = |Package change of object { is_item-obj_name } failed| is_item = is_item ). ENDIF. ENDMETHOD. METHOD check_main_package. " check package restrictions, closed package, descriptive or " functional package cl_pak_object_types=>check_object_type( EXPORTING i_working_mode = 'I' i_package_name = iv_package i_pgmid = 'R3TR' i_object_type = iv_obj_type EXCEPTIONS wrong_object_type = 1 package_not_extensible = 2 package_not_loaded = 3 OTHERS = 4 ). CASE sy-subrc. WHEN 0. RETURN. WHEN 2. zcx_abapgit_exception=>raise( |Object type { iv_obj_type } not allowed for package { iv_package }| ). WHEN OTHERS. zcx_abapgit_exception=>raise_t100( ). ENDCASE. ENDMETHOD. METHOD check_objects_locked. DATA: li_obj TYPE REF TO zif_abapgit_object. FIELD-SYMBOLS: LIKE LINE OF it_items. LOOP AT it_items ASSIGNING . " You should remember that we ignore not supported objects here, " because otherwise the process aborts which is not desired IF is_type_supported( -obj_type ) = abap_false. CONTINUE. ENDIF. li_obj = create_object( ). IF li_obj->is_locked( ) = abap_true. zcx_abapgit_exception=>raise( |Object { -obj_type } { -obj_name } | && |is locked. Action not possible.| ). ENDIF. ENDLOOP. ENDMETHOD. METHOD class_name. CONCATENATE 'ZCL_ABAPGIT_OBJECT_' is_item-obj_type INTO rv_class_name. ENDMETHOD. METHOD create_object. DATA: lv_message TYPE string, lv_class_name TYPE string, ls_obj_serializer_map LIKE LINE OF gt_obj_serializer_map. " serialize & deserialize require files and i18n parameters, " other calls are good without them ASSERT io_files IS BOUND AND io_i18n_params IS BOUND OR io_files IS NOT BOUND AND io_i18n_params IS NOT BOUND. READ TABLE gt_obj_serializer_map INTO ls_obj_serializer_map WITH KEY item = is_item. IF sy-subrc = 0. lv_class_name = ls_obj_serializer_map-metadata-class. ELSEIF is_metadata IS NOT INITIAL. * Metadata is provided only on deserialization * Once this has been triggered, the same deserializer shall be used * for subsequent processes. * Thus, buffer the metadata afterwards ls_obj_serializer_map-item = is_item. ls_obj_serializer_map-metadata = is_metadata. INSERT ls_obj_serializer_map INTO TABLE gt_obj_serializer_map. lv_class_name = is_metadata-class. ELSE. lv_class_name = class_name( is_item ). ENDIF. REPLACE FIRST OCCURRENCE OF 'LCL' IN lv_class_name WITH 'ZCL_ABAPGIT'. IF zcl_abapgit_factory=>get_environment( )->is_merged( ) = abap_true. " Prevent accidental usage of object handlers in the developer version lv_class_name = |\\PROGRAM={ sy-repid }\\CLASS={ lv_class_name }|. ENDIF. TRY. IF io_files IS BOUND AND io_i18n_params IS BOUND. CREATE OBJECT ri_obj TYPE (lv_class_name) EXPORTING is_item = is_item iv_language = io_i18n_params->ms_params-main_language io_files = io_files io_i18n_params = io_i18n_params. ELSE. CREATE OBJECT ri_obj TYPE (lv_class_name) EXPORTING is_item = is_item iv_language = zif_abapgit_definitions=>c_english. ENDIF. CATCH cx_sy_create_object_error. lv_message = |Object type { is_item-obj_type } is not supported by this system|. " No support for bridge in apm zcx_abapgit_exception=>raise( lv_message ). ENDTRY. ENDMETHOD. METHOD delete. DATA: ls_item TYPE zif_abapgit_definitions=>ty_item, li_progress TYPE REF TO zif_abapgit_progress, lt_tadir LIKE it_tadir, lt_deleted LIKE it_tadir, lt_items TYPE zif_abapgit_definitions=>ty_items_tt, lx_error TYPE REF TO zcx_abapgit_exception, lv_count TYPE i. FIELD-SYMBOLS: LIKE LINE OF it_tadir. IF it_tadir IS INITIAL. RETURN. ENDIF. lt_tadir = it_tadir. IF ii_log IS BOUND. IF lines( lt_tadir ) = 1. ii_log->add_info( |>>> Deleting 1 object| ). ELSE. ii_log->add_info( |>>> Deleting { lines( lt_tadir ) } objects| ). ENDIF. ENDIF. IF iv_transport IS NOT INITIAL. zcl_abapgit_factory=>get_default_transport( )->set( iv_transport ). ENDIF. TRY. zcl_abapgit_dependencies=>resolve( CHANGING ct_tadir = lt_tadir ). li_progress = zcl_abapgit_progress=>get_instance( lines( lt_tadir ) ). lt_items = map_tadir_to_items( lt_tadir ). check_objects_locked( lt_items ). CATCH zcx_abapgit_exception INTO lx_error. zcl_abapgit_factory=>get_default_transport( )->reset( ). RAISE EXCEPTION lx_error. ENDTRY. lv_count = 1. DO. CLEAR lt_deleted. LOOP AT lt_tadir ASSIGNING . li_progress->show( iv_current = lv_count iv_text = |Delete { -obj_name }| ). CLEAR ls_item. ls_item-obj_type = -object. ls_item-obj_name = -obj_name. TRY. delete_object( iv_package = -devclass is_item = ls_item iv_transport = iv_transport ii_log = ii_log ). INSERT INTO TABLE lt_deleted. DELETE lt_tadir. lv_count = lv_count + 1. " make sure to save object deletions COMMIT WORK. IF ii_log IS BOUND. ii_log->add_info( iv_msg = |Object { ls_item-obj_type } { ls_item-obj_name } deleted| is_item = ls_item ). ENDIF. CATCH zcx_abapgit_exception INTO lx_error. IF ii_log IS BOUND. ii_log->add_exception( ix_exc = lx_error is_item = ls_item ). ii_log->add_error( iv_msg = |Deletion of object { ls_item-obj_name } failed| is_item = ls_item ). ENDIF. ENDTRY. ENDLOOP. " Exit if done or nothing else was deleted IF lines( lt_tadir ) = 0 OR lines( lt_deleted ) = 0. EXIT. ENDIF. ENDDO. zcl_abapgit_factory=>get_default_transport( )->reset( ). IF lx_error IS BOUND AND lines( lt_tadir ) > 0. zcx_abapgit_exception=>raise( 'Error during uninstall. Check the log.' ). ENDIF. li_progress->off( ). ENDMETHOD. METHOD delete_object. DATA: li_obj TYPE REF TO zif_abapgit_object. " Nothing to do for unsupported objects IF is_type_supported( is_item-obj_type ) = abap_false. RETURN. ENDIF. li_obj = create_object( is_item ). li_obj->delete( iv_package = iv_package iv_transport = iv_transport ii_log = ii_log ). ENDMETHOD. METHOD deserialize. DATA: ls_item TYPE zif_abapgit_definitions=>ty_item, li_obj TYPE REF TO zif_abapgit_object, lv_package TYPE devclass, lo_files TYPE REF TO zcl_abapgit_objects_files, ls_metadata TYPE zif_abapgit_definitions=>ty_metadata, lo_xml TYPE REF TO zif_abapgit_xml_input, lt_results TYPE zif_abapgit_definitions=>ty_results_tt, li_progress TYPE REF TO zif_abapgit_progress, lv_path TYPE string, lt_items TYPE zif_abapgit_definitions=>ty_items_tt, lt_steps_id TYPE zif_abapgit_objects=>ty_deserialization_step_tt, lt_steps TYPE zif_abapgit_objects=>ty_step_data_tt, lx_exc TYPE REF TO zcx_abapgit_exception. DATA lo_folder_logic TYPE REF TO zcl_abapgit_folder_logic. DATA lo_i18n_params TYPE REF TO zcl_abapgit_i18n_params. DATA lo_timer TYPE REF TO zcl_abapgit_timer. DATA lo_abap_language_vers TYPE REF TO zcl_abapgit_abap_language_vers. FIELD-SYMBOLS: TYPE zif_abapgit_definitions=>ty_result, TYPE LINE OF zif_abapgit_objects=>ty_deserialization_step_tt, TYPE LINE OF zif_abapgit_objects=>ty_step_data_tt, TYPE LINE OF zif_abapgit_objects=>ty_deserialization_tt. lt_steps = get_deserialize_steps( ). IF iv_transport IS NOT INITIAL. zcl_abapgit_factory=>get_default_transport( )->set( iv_transport ). ENDIF. zcl_abapgit_objects_activation=>clear( ). lt_results = files_to_deserialize( iv_package = iv_package it_local = it_local it_local_checksums = it_local_checksums it_remote = it_remote io_dot = io_dot ii_log = ii_log ). IF lt_results IS INITIAL. RETURN. ENDIF. li_progress = zcl_abapgit_progress=>get_instance( lines( lt_results ) ). lt_items = map_results_to_items( lt_results ). lo_timer = zcl_abapgit_timer=>create( iv_text = 'Deserialize:' iv_count = lines( lt_items ) )->start( ). zcl_abapgit_factory=>get_cts_api( )->confirm_transport_messages( ). check_objects_locked( lt_items ). lo_i18n_params = zcl_abapgit_i18n_params=>new( is_params = determine_i18n_params( io_dot = io_dot iv_main_language_only = abap_false ) ). "io_repo->get_local_settings( )-main_language_only IF lines( lt_items ) = 1. ii_log->add_info( |>>> Deserializing 1 object| ). ELSE. ii_log->add_info( |>>> Deserializing { lines( lt_items ) } objects| ). ENDIF. CREATE OBJECT lo_abap_language_vers EXPORTING io_dot_abapgit = io_dot. lo_folder_logic = zcl_abapgit_folder_logic=>get_instance( ). LOOP AT lt_results ASSIGNING . li_progress->show( iv_current = sy-tabix iv_text = |Prepare Deserialize: { -obj_type } { -obj_name }| ). CLEAR ls_item. CLEAR: lv_path, lv_package. ls_item-obj_type = -obj_type. ls_item-obj_name = -obj_name. "error handling & logging added TRY. IF ls_item-obj_type <> 'NSPC'. " If package does not exist yet, it will be created with this call lv_package = lo_folder_logic->path_to_package( iv_top = iv_package io_dot = io_dot iv_path = -path ). check_main_package( iv_package = lv_package iv_obj_type = ls_item-obj_type ). ENDIF. IF ls_item-obj_type = 'DEVC'. " Packages have the same filename across different folders. The path needs to be supplied " to find the correct file. lv_path = -path. ENDIF. ls_item-devclass = lv_package. ls_item-abap_language_version = lo_abap_language_vers->get_abap_language_vers_by_objt( iv_object_type = ls_item-obj_type iv_package = lv_package ). IF -packmove = abap_true. " Move object to new package change_package_assignments( is_item = ls_item ii_log = ii_log ). " No other changes required CONTINUE. ENDIF. " Create or update object lo_files = zcl_abapgit_objects_files=>new( is_item = ls_item iv_path = lv_path ). lo_files->set_files( it_remote ). IF lo_files->is_json_metadata( ) = abap_false. "analyze XML in order to instantiate the proper serializer lo_xml = lo_files->read_xml( ). ls_metadata = lo_xml->get_metadata( ). ELSE. " there's no XML and metadata for JSON format CLEAR: lo_xml, ls_metadata. ENDIF. li_obj = create_object( is_item = ls_item is_metadata = ls_metadata io_files = lo_files io_i18n_params = lo_i18n_params ). "get required steps for deserialize the object lt_steps_id = li_obj->get_deserialize_steps( ). LOOP AT lt_steps_id ASSIGNING . READ TABLE lt_steps WITH KEY step_id = ASSIGNING . ASSERT sy-subrc = 0. IF = zif_abapgit_object=>gc_step_id-ddic AND zcl_abapgit_objects_activation=>is_ddic_type( ls_item-obj_type ) = abap_false. " DDIC only for DDIC objects zcx_abapgit_exception=>raise( |Step { } is only for DDIC objects| ). ENDIF. APPEND INITIAL LINE TO -objects ASSIGNING . -item = ls_item. -files = lo_files. -obj = li_obj. -xml = lo_xml. -package = lv_package. ENDLOOP. CATCH zcx_abapgit_exception INTO lx_exc. ii_log->add_exception( ix_exc = lx_exc is_item = ls_item ). ii_log->add_error( iv_msg = |Import of object { ls_item-obj_name } failed| is_item = ls_item ). "object should not be part of any deserialization step CONTINUE. ENDTRY. ENDLOOP. li_progress->off( ). "run deserialize for all steps and its objects deserialize_steps( EXPORTING it_steps = lt_steps ii_log = ii_log io_i18n_params = lo_i18n_params iv_transport = iv_transport CHANGING ct_files = rt_accessed_files ). update_package_tree( iv_package ). " Set the original system for all updated objects to what's defined in repo settings update_original_system( it_items = lt_items ii_log = ii_log io_dot = io_dot iv_transport = iv_transport ). zcl_abapgit_factory=>get_default_transport( )->reset( ). lo_timer->end( abap_true ). ENDMETHOD. METHOD deserialize_lxe. DATA: lo_base TYPE REF TO zcl_abapgit_objects_super, lx_exc TYPE REF TO zcx_abapgit_exception. FIELD-SYMBOLS LIKE LINE OF is_step-objects. ii_log->add_success( |>> Step { is_step-order } - { is_step-descr }| ). LOOP AT is_step-objects ASSIGNING . TRY. zcl_abapgit_factory=>get_lxe_texts( )->deserialize( iv_object_type = -item-obj_type iv_object_name = -item-obj_name iv_package = -item-devclass ii_xml = -xml io_files = -files io_i18n_params = io_i18n_params ). lo_base ?= -obj. APPEND LINES OF lo_base->get_accessed_files( ) TO ct_files. ii_log->add_success( iv_msg = |Translations for { -item-obj_name } imported| is_item = -item ). CATCH zcx_abapgit_exception INTO lx_exc. ii_log->add_exception( ix_exc = lx_exc is_item = -item ). ii_log->add_error( iv_msg = |Import of translations for { -item-obj_name } failed| is_item = -item ). ENDTRY. ENDLOOP. ENDMETHOD. METHOD deserialize_step. DATA: li_progress TYPE REF TO zif_abapgit_progress, li_exit TYPE REF TO zif_abapgit_exit, lo_base TYPE REF TO zcl_abapgit_objects_super, lx_exc TYPE REF TO zcx_abapgit_exception. FIELD-SYMBOLS: LIKE LINE OF is_step-objects. zcl_abapgit_objects_activation=>clear( ). ii_log->add_success( |>> Step { is_step-order } - { is_step-descr }| ). li_progress = zcl_abapgit_progress=>get_instance( lines( is_step-objects ) ). LOOP AT is_step-objects ASSIGNING . li_progress->show( iv_current = sy-tabix iv_text = |Step { is_step-order } - { is_step-descr }:| && | { -item-obj_type } { -item-obj_name }| ). TRY. -obj->deserialize( iv_package = -package io_xml = -xml iv_step = is_step-step_id ii_log = ii_log iv_transport = iv_transport ). lo_base ?= -obj. APPEND LINES OF lo_base->get_accessed_files( ) TO ct_files. ii_log->add_success( iv_msg = |Object { -item-obj_name } imported| is_item = -item ). CATCH zcx_abapgit_exception INTO lx_exc. ii_log->add_exception( ix_exc = lx_exc is_item = -item ). ii_log->add_error( iv_msg = |Import of object { -item-obj_name } failed| is_item = -item ). ENDTRY. ENDLOOP. li_progress->show( iv_current = lines( is_step-objects ) iv_text = |Step { is_step-order } - Activating Objects| ). CASE is_step-step_id. WHEN zif_abapgit_object=>gc_step_id-ddic. zcl_abapgit_objects_activation=>activate( iv_ddic = abap_true ii_log = ii_log ). WHEN zif_abapgit_object=>gc_step_id-abap. zcl_abapgit_objects_activation=>activate( iv_ddic = abap_false ii_log = ii_log ). WHEN zif_abapgit_object=>gc_step_id-late. " late can have both DDIC (like TABL with REF TO) and non-DDIC objects zcl_abapgit_objects_activation=>activate( iv_ddic = abap_true ii_log = ii_log ). zcl_abapgit_objects_activation=>activate( iv_ddic = abap_false ii_log = ii_log ). ENDCASE. li_progress->off( ). ENDMETHOD. METHOD deserialize_steps. FIELD-SYMBOLS LIKE LINE OF it_steps. LOOP AT it_steps ASSIGNING . IF -step_id <> zif_abapgit_object=>gc_step_id-lxe. deserialize_step( EXPORTING is_step = ii_log = ii_log iv_transport = iv_transport CHANGING ct_files = ct_files ). ELSEIF io_i18n_params->is_lxe_applicable( ) = abap_true. deserialize_lxe( EXPORTING is_step = ii_log = ii_log io_i18n_params = io_i18n_params CHANGING ct_files = ct_files ). ENDIF. ENDLOOP. SORT ct_files BY path ASCENDING filename ASCENDING. DELETE ADJACENT DUPLICATES FROM ct_files. " Just in case ENDMETHOD. METHOD determine_i18n_params. " TODO: unify with ZCL_ABAPGIT_SERIALIZE=>DETERMINE_I18N_PARAMS, same code IF io_dot IS BOUND. rs_i18n_params-main_language = io_dot->get_main_language( ). rs_i18n_params-use_lxe = io_dot->use_lxe( ). rs_i18n_params-main_language_only = iv_main_language_only. rs_i18n_params-translation_languages = zcl_abapgit_lxe_texts=>get_translation_languages( iv_main_language = io_dot->get_main_language( ) it_i18n_languages = io_dot->get_i18n_languages( ) ). ENDIF. IF rs_i18n_params-main_language IS INITIAL. rs_i18n_params-main_language = sy-langu. ENDIF. ENDMETHOD. METHOD exists. DATA: li_obj TYPE REF TO zif_abapgit_object. " Might be called for objects without tadir entry IF is_item IS INITIAL. RETURN. ENDIF. " For unsupported objects, assume object exists IF is_type_supported( is_item-obj_type ) = abap_false. rv_bool = abap_true. RETURN. ENDIF. TRY. li_obj = create_object( is_item ). rv_bool = li_obj->exists( ). CATCH zcx_abapgit_exception. " Ignore errors and assume the object exists rv_bool = abap_true. ENDTRY. ENDMETHOD. METHOD files_to_deserialize. DATA: li_instance TYPE REF TO zif_abapgit_status_calc, lt_results TYPE zif_abapgit_definitions=>ty_results_tt. li_instance = zcl_abapgit_status_calc=>get_instance( iv_root_package = iv_package io_dot = io_dot ). lt_results = li_instance->calculate_status( it_local = it_local it_remote = it_remote it_cur_state = it_local_checksums ). rt_results = prioritize_deser( filter_files_to_deserialize( it_results = lt_results ii_log = ii_log ) ). ENDMETHOD. METHOD filter_files_to_deserialize. DATA lt_objects LIKE rt_results. DATA lr_object TYPE REF TO zif_abapgit_definitions=>ty_result. DATA ls_item TYPE zif_abapgit_definitions=>ty_item. DATA lv_tabix TYPE sy-tabix. rt_results = it_results. "preparation for object logging, sort all file entries by objects IF ii_log IS BOUND. lt_objects = rt_results. SORT lt_objects BY obj_type obj_name. DELETE ADJACENT DUPLICATES FROM lt_objects COMPARING obj_type obj_name. DELETE lt_objects WHERE obj_type IS INITIAL AND obj_name IS INITIAL. ENDIF. "ignore objects w/o changes DELETE rt_results WHERE match = abap_true. " Full match "log objects w/o changes IF sy-subrc = 0 AND ii_log IS BOUND. SORT rt_results BY obj_type obj_name. LOOP AT lt_objects REFERENCE INTO lr_object. lv_tabix = sy-tabix. READ TABLE rt_results WITH KEY obj_type = lr_object->obj_type obj_name = lr_object->obj_name BINARY SEARCH TRANSPORTING NO FIELDS. IF sy-subrc <> 0. "all parts of the objects have not changed ls_item-devclass = lr_object->package. ls_item-obj_type = lr_object->obj_type. ls_item-obj_name = lr_object->obj_name. ii_log->add_success( iv_msg = |Object { ls_item-obj_name } (type { ls_item-obj_type }) not changed; no import required| is_item = ls_item ). "ignore object for further messages DELETE lt_objects INDEX lv_tabix. ENDIF. ENDLOOP. ENDIF. "ignore objects w/o object type DELETE rt_results WHERE obj_type IS INITIAL. "log objects w/o object type IF sy-subrc = 0 AND ii_log IS BOUND. " Note: Moving the CHECK condition to the LOOP WHERE clause will lead to a " syntax warning in higher releases and syntax error in 702 LOOP AT lt_objects REFERENCE INTO lr_object. CHECK lr_object->obj_type IS INITIAL AND lr_object->obj_name IS NOT INITIAL. ls_item-devclass = lr_object->package. ls_item-obj_type = lr_object->obj_type. ls_item-obj_name = lr_object->obj_name. ii_log->add_warning( iv_msg = |Object type for { ls_item-obj_name } not defined - will be ignored by abapGit| is_item = ls_item ). ENDLOOP. DELETE lt_objects WHERE obj_type IS INITIAL. ENDIF. "ignore objects that exists only local DELETE rt_results WHERE lstate = zif_abapgit_definitions=>c_state-added AND rstate IS INITIAL. "ignore objects that where deleted remotely DELETE rt_results WHERE rstate = zif_abapgit_definitions=>c_state-deleted. "log objects that exists only local or where deleted remotely IF sy-subrc = 0 AND ii_log IS BOUND. SORT rt_results BY obj_type obj_name. LOOP AT lt_objects REFERENCE INTO lr_object. lv_tabix = sy-tabix. READ TABLE rt_results WITH KEY obj_type = lr_object->obj_type obj_name = lr_object->obj_name BINARY SEARCH TRANSPORTING NO FIELDS. IF sy-subrc <> 0. "all parts exists only local "no log message; ignore object for further messages DELETE lt_objects INDEX lv_tabix. ENDIF. ENDLOOP. ENDIF. "ignore table content " DELETE rt_results WHERE path = zif_abapgit_data_config=>c_default_path SORT rt_results BY obj_type ASCENDING obj_name ASCENDING rstate DESCENDING " ensures that non-empty rstate is kept lstate DESCENDING. " ensures that non-empty lstate is kept DELETE ADJACENT DUPLICATES FROM rt_results COMPARING obj_type obj_name. ENDMETHOD. METHOD get_deserialize_steps. FIELD-SYMBOLS: TYPE LINE OF zif_abapgit_objects=>ty_step_data_tt. APPEND INITIAL LINE TO rt_steps ASSIGNING . -step_id = zif_abapgit_object=>gc_step_id-early. -descr = 'Pre-process Objects'. -syntax_check = abap_false. -order = 1. APPEND INITIAL LINE TO rt_steps ASSIGNING . -step_id = zif_abapgit_object=>gc_step_id-ddic. -descr = 'Deserialize DDIC Objects'. -syntax_check = abap_false. -order = 2. APPEND INITIAL LINE TO rt_steps ASSIGNING . -step_id = zif_abapgit_object=>gc_step_id-abap. -descr = 'Deserialize non-DDIC Objects'. -syntax_check = abap_false. -order = 3. APPEND INITIAL LINE TO rt_steps ASSIGNING . -step_id = zif_abapgit_object=>gc_step_id-late. -descr = 'Post-process Objects'. -syntax_check = abap_true. -order = 4. SORT rt_steps BY order. " ensure correct processing order ENDMETHOD. METHOD get_extra_from_filename. IF iv_filename IS NOT INITIAL. FIND REGEX '\..*\.([\-a-z0-9_%]*)\.' IN iv_filename SUBMATCHES rv_extra ##REGEX_POSIX. IF sy-subrc = 0. rv_extra = cl_http_utility=>unescape_url( rv_extra ). ENDIF. ENDIF. ENDMETHOD. METHOD is_active. DATA: li_obj TYPE REF TO zif_abapgit_object. " For unsupported objects, assume active state IF is_type_supported( is_item-obj_type ) = abap_false. rv_active = abap_true. RETURN. ENDIF. TRY. li_obj = create_object( is_item ). rv_active = li_obj->is_active( ). CATCH cx_sy_dyn_call_illegal_method cx_sy_ref_is_initial zcx_abapgit_exception. " Ignore errors and assume active state rv_active = abap_true. ENDTRY. ENDMETHOD. METHOD is_supported. TRY. create_object( is_item = is_item iv_native_only = iv_native_only ). rv_bool = abap_true. CATCH zcx_abapgit_exception. rv_bool = abap_false. ENDTRY. ENDMETHOD. METHOD is_type_supported. DATA: ls_item TYPE zif_abapgit_definitions=>ty_item, ls_supported_obj_type TYPE ty_supported_types. FIELD-SYMBOLS TYPE ty_supported_types. IF iv_obj_type IS INITIAL. " empty object type should never exist RETURN. ENDIF. READ TABLE gt_supported_obj_types ASSIGNING WITH KEY obj_type = iv_obj_type. IF sy-subrc <> 0. ls_item-obj_type = iv_obj_type. ls_supported_obj_type-obj_type = iv_obj_type. ls_supported_obj_type-supported = is_supported( ls_item ). INSERT ls_supported_obj_type INTO TABLE gt_supported_obj_types. rv_bool = ls_supported_obj_type-supported. RETURN. ENDIF. rv_bool = -supported. ENDMETHOD. METHOD jump. DATA: li_obj TYPE REF TO zif_abapgit_object, lv_exit TYPE abap_bool. " Nothing to do for unsupported objects IF is_type_supported( is_item-obj_type ) = abap_false. zcx_abapgit_exception=>raise( |Object type { is_item-obj_type } is not supported by this system| ). ENDIF. " Nothing to do if object does not exist li_obj = create_object( is_item ). IF li_obj->exists( ) = abap_false. zcx_abapgit_exception=>raise( |Object { is_item-obj_type } { is_item-obj_name } doesn't exist| ). ENDIF. " First priority object-specific handler lv_exit = li_obj->jump( get_extra_from_filename( iv_filename ) ). IF lv_exit = abap_false. " Open object in new window with generic jumper lv_exit = zcl_abapgit_objects_factory=>get_gui_jumper( )->jump( is_item = is_item is_sub_item = is_sub_item iv_line_number = iv_line_number iv_new_window = iv_new_window ). ENDIF. IF lv_exit = abap_false. zcx_abapgit_exception=>raise( |Jump to { is_item-obj_type } { is_item-obj_name } not possible| ). ENDIF. ENDMETHOD. METHOD map_results_to_items. DATA: ls_item LIKE LINE OF rt_items. FIELD-SYMBOLS: TYPE zif_abapgit_definitions=>ty_result. LOOP AT it_results ASSIGNING . ls_item-devclass = -package. ls_item-obj_type = -obj_type. ls_item-obj_name = -obj_name. INSERT ls_item INTO TABLE rt_items. ENDLOOP. ENDMETHOD. METHOD map_tadir_to_items. DATA: ls_item LIKE LINE OF rt_items. FIELD-SYMBOLS: TYPE zif_abapgit_definitions=>ty_tadir. LOOP AT it_tadir ASSIGNING . ls_item-devclass = -devclass. ls_item-obj_type = -object. ls_item-obj_name = -obj_name. INSERT ls_item INTO TABLE rt_items. ENDLOOP. ENDMETHOD. METHOD prioritize_deser. DATA lt_items TYPE zif_abapgit_definitions=>ty_items_tt. DATA ls_item LIKE LINE OF lt_items. DATA lt_requires TYPE zif_abapgit_definitions=>ty_items_tt. DATA ls_require LIKE LINE OF lt_requires. DATA ls_result LIKE LINE OF it_results. DATA lo_graph TYPE REF TO zcl_abapgit_item_graph. lt_items = map_results_to_items( it_results ). CREATE OBJECT lo_graph EXPORTING it_items = lt_items. LOOP AT lt_items INTO ls_item. CLEAR lt_requires. * TODO: BEGIN extract to object handler method in ZIF_ABAPGIT_OBJECT: * METHODS get_deserialize_order * IMPORTING * it_items TYPE ty_items_tt * RETURNING * VALUE(rt_requries) TYPE ty_items_tt CASE ls_item-obj_type. WHEN 'SPRX'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'WEBI'. WHEN 'CLAS'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'SPRX' AND obj_type <> 'INTF' AND obj_type <> 'XSLT'. WHEN 'PROG'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'XSLT'. WHEN 'INTF'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'SPRX' AND obj_type <> 'XSLT'. WHEN 'TABL'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'SPRX'. WHEN 'IARP'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'IASP'. WHEN 'IATU' OR 'IAXU' OR 'IAMU'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'IASP' AND obj_type <> 'PROG' AND obj_type <> 'IARP'. WHEN 'IDOC' OR 'IEXT'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'TABL'. WHEN 'DCLS'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'DDLS'. WHEN 'ODSO'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'IOBJ'. WHEN 'SCP1'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'TOBJ'. WHEN 'CHAR'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'OTGR'. WHEN 'PINF'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'CLAS' AND obj_type <> 'INTF' AND obj_type <> 'TABL' AND obj_type <> 'DOMA' AND obj_type <> 'DTEL'. WHEN 'DEVC'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'PINF'. WHEN 'ENHC'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'ENHO'. WHEN 'ENHO'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'ENSC' AND obj_type <> 'ENHS'. WHEN 'ENSC'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'ENHS'. WHEN 'IWMO' OR 'IWSV' OR 'IWVB'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'SRVB'. WHEN 'SUSH'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'SRVB' AND obj_type <> 'HTTP'. WHEN 'SRVB'. lt_requires = lt_items. DELETE lt_requires WHERE obj_type <> 'SRVD'. ENDCASE. * TODO: END extract to object handler method LOOP AT lt_requires INTO ls_require. lo_graph->add_edge( is_from = ls_require is_to = ls_item ). ENDLOOP. ENDLOOP. WHILE lo_graph->has_vertices( ) = abap_true. ls_item = lo_graph->get_next( ii_log ). READ TABLE it_results INTO ls_result WITH KEY sec_key COMPONENTS obj_name = ls_item-obj_name obj_type = ls_item-obj_type. ASSERT sy-subrc = 0. APPEND ls_result TO rt_results. ENDWHILE. ENDMETHOD. METHOD serialize. DATA: li_obj TYPE REF TO zif_abapgit_object, lx_error TYPE REF TO zcx_abapgit_exception, li_xml TYPE REF TO zif_abapgit_xml_output, lo_files TYPE REF TO zcl_abapgit_objects_files. FIELD-SYMBOLS LIKE LINE OF rs_files_and_item-files. IF is_type_supported( is_item-obj_type ) = abap_false. zcx_abapgit_exception=>raise( |Object type ignored, not supported: { is_item-obj_type }-{ is_item-obj_name }| ). ENDIF. lo_files = zcl_abapgit_objects_files=>new( is_item ). li_obj = create_object( is_item = is_item io_files = lo_files io_i18n_params = io_i18n_params ). CREATE OBJECT li_xml TYPE zcl_abapgit_xml_output. rs_files_and_item-item = is_item. TRY. li_obj->serialize( li_xml ). CATCH zcx_abapgit_exception INTO lx_error. rs_files_and_item-item-inactive = boolc( li_obj->is_active( ) = abap_false ). RAISE EXCEPTION lx_error. ENDTRY. IF io_i18n_params->is_lxe_applicable( ) = abap_true. zcl_abapgit_factory=>get_lxe_texts( )->serialize( iv_object_type = is_item-obj_type iv_object_name = is_item-obj_name io_i18n_params = io_i18n_params io_files = lo_files ii_xml = li_xml ). ENDIF. IF lo_files->is_json_metadata( ) = abap_false. lo_files->add_xml( ii_xml = li_xml is_metadata = li_obj->get_metadata( ) ). ENDIF. rs_files_and_item-files = lo_files->get_files( ). " TODO: Do we need this? " check_duplicates( rs_files_and_item-files ). rs_files_and_item-item-inactive = boolc( li_obj->is_active( ) = abap_false ). LOOP AT rs_files_and_item-files ASSIGNING . -sha1 = zcl_abapgit_hash=>sha1_blob( -data ). ENDLOOP. ENDMETHOD. METHOD supported_list. DATA lt_objects TYPE STANDARD TABLE OF ko100. DATA ls_item TYPE zif_abapgit_definitions=>ty_item. DATA ls_supported_obj_type TYPE ty_supported_types. DATA lt_types TYPE zif_abapgit_exit=>ty_object_types. DATA lv_type LIKE LINE OF lt_types. DATA li_exit TYPE REF TO zif_abapgit_exit. FIELD-SYMBOLS LIKE LINE OF lt_objects. FIELD-SYMBOLS TYPE ty_supported_types. IF gv_supported_obj_types_loaded = abap_true. LOOP AT gt_supported_obj_types ASSIGNING WHERE supported = abap_true. "#EC CI_SORTSEQ INSERT -obj_type INTO TABLE rt_types. ENDLOOP. RETURN. ENDIF. " delete content because it might be filled already by method IS_TYPE_SUPPORTED CLEAR gt_supported_obj_types. CALL FUNCTION 'TR_OBJECT_TABLE' TABLES wt_object_text = lt_objects EXCEPTIONS OTHERS = 1 ##FM_SUBRC_OK. LOOP AT lt_objects ASSIGNING WHERE pgmid = 'R3TR'. INSERT -object INTO TABLE lt_types. ENDLOOP. LOOP AT lt_types INTO lv_type. ls_item-obj_type = lv_type. ls_supported_obj_type-obj_type = lv_type. ls_supported_obj_type-supported = is_supported( ls_item ). INSERT ls_supported_obj_type INTO TABLE gt_supported_obj_types. IF ls_supported_obj_type-supported = abap_true. INSERT ls_supported_obj_type-obj_type INTO TABLE rt_types. ENDIF. ENDLOOP. gv_supported_obj_types_loaded = abap_true. ENDMETHOD. METHOD update_original_system. DATA: lv_srcsystem TYPE tadir-srcsystem, lv_transport_type_from TYPE trfunction, lv_transport_type_to TYPE trfunction, lv_errors TYPE abap_bool, lv_msg TYPE string. FIELD-SYMBOLS LIKE LINE OF it_items. lv_srcsystem = io_dot->get_original_system( ). IF lv_srcsystem IS INITIAL. RETURN. ELSEIF lv_srcsystem = 'SID'. " Change objects to local system and switch repairs to development requests lv_srcsystem = sy-sysid. lv_transport_type_from = zif_abapgit_cts_api=>c_transport_type-wb_repair. lv_transport_type_to = zif_abapgit_cts_api=>c_transport_type-wb_task. ELSE. " Change objects to external system and switch development requests to repairs lv_transport_type_from = zif_abapgit_cts_api=>c_transport_type-wb_task. lv_transport_type_to = zif_abapgit_cts_api=>c_transport_type-wb_repair. ENDIF. ii_log->add_info( |>> Setting original system| ). LOOP AT it_items ASSIGNING . " Local packages are not stored in TADIR IF -obj_type = 'DEVC' AND -obj_name(1) = '$'. CONTINUE. ENDIF. IF exists( ) = abap_true. CALL FUNCTION 'TR_TADIR_INTERFACE' EXPORTING wi_tadir_pgmid = 'R3TR' wi_tadir_object = -obj_type wi_tadir_obj_name = -obj_name wi_tadir_srcsystem = lv_srcsystem wi_test_modus = abap_false EXCEPTIONS tadir_entry_not_existing = 1 tadir_entry_ill_type = 2 no_systemname = 3 no_systemtype = 4 original_system_conflict = 5 object_reserved_for_devclass = 6 object_exists_global = 7 object_exists_local = 8 object_is_distributed = 9 obj_specification_not_unique = 10 no_authorization_to_delete = 11 devclass_not_existing = 12 simultanious_set_remove_repair = 13 order_missing = 14 no_modification_of_head_syst = 15 pgmid_object_not_allowed = 16 masterlanguage_not_specified = 17 devclass_not_specified = 18 specify_owner_unique = 19 loc_priv_objs_no_repair = 20 gtadir_not_reached = 21 object_locked_for_order = 22 change_of_class_not_allowed = 23 no_change_from_sap_to_tmp = 24 OTHERS = 25. IF sy-subrc <> 0. MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 INTO lv_msg. ii_log->add_error( iv_msg = lv_msg is_item = ). lv_errors = abap_true. ENDIF. ENDIF. ENDLOOP. IF lv_errors IS INITIAL. " Since original system has changed, the type of transport request needs to be adjusted zcl_abapgit_factory=>get_cts_api( )->change_transport_type( iv_transport_request = iv_transport iv_transport_type_from = lv_transport_type_from iv_transport_type_to = lv_transport_type_to ). ENDIF. ENDMETHOD. METHOD update_package_tree. DATA: lt_packages TYPE zif_abapgit_sap_package=>ty_devclass_tt, lv_package LIKE LINE OF lt_packages, lv_tree TYPE dirtree-tname. " Make sure all deserialized objects are committed COMMIT WORK AND WAIT. lt_packages = zcl_abapgit_factory=>get_sap_package( iv_package )->list_subpackages( ). APPEND iv_package TO lt_packages. LOOP AT lt_packages INTO lv_package. " Update package tree for SE80 lv_tree = 'EU_' && lv_package. CALL FUNCTION 'WB_TREE_ACTUALIZE' EXPORTING tree_name = lv_tree without_crossreference = abap_true with_tcode_index = abap_true. ENDLOOP. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_abapgit_serialize IMPLEMENTATION. METHOD add_dot_abapgit. FIELD-SYMBOLS: LIKE LINE OF ct_files. APPEND INITIAL LINE TO ct_files ASSIGNING . -file = mo_dot_abapgit->to_file( ). ENDMETHOD. METHOD add_objects. DATA: " lo_filter TYPE REF TO zcl_abapgit_repo_filter, lv_force TYPE abap_bool, lt_found LIKE ct_files, lt_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt. lt_tadir = zcl_abapgit_factory=>get_tadir( )->read( iv_package = iv_package iv_ignore_subpackages = ms_local_settings-ignore_subpackages iv_only_local_objects = ms_local_settings-only_local_objects io_dot = mo_dot_abapgit ii_log = ii_log it_filter = it_filter ). * CREATE OBJECT lo_filter. * * lo_filter->apply( EXPORTING it_filter = it_filter * CHANGING ct_tadir = lt_tadir ). * if there are less than 10 objects run in single thread * this helps a lot when debugging, plus performance gain * with low number of objects does not matter much lv_force = boolc( lines( lt_tadir ) < 10 ). lt_found = serialize( iv_package = iv_package it_tadir = lt_tadir ii_log = ii_log iv_force_sequential = lv_force ). APPEND LINES OF lt_found TO ct_files. ENDMETHOD. METHOD add_to_return. FIELD-SYMBOLS: LIKE LINE OF is_file_item-files, LIKE LINE OF mt_files. LOOP AT is_file_item-files ASSIGNING . APPEND INITIAL LINE TO mt_files ASSIGNING . -file = . -file-path = iv_path. -item = is_file_item-item. ENDLOOP. ENDMETHOD. METHOD constructor. mo_dot_abapgit = io_dot_abapgit. ms_local_settings = is_local_settings. IF io_dot_abapgit IS BOUND. ms_i18n_params = io_dot_abapgit->determine_i18n_parameters( is_local_settings-main_language_only ). ELSE. ms_i18n_params-main_language = sy-langu. ms_i18n_params-main_language_only = is_local_settings-main_language_only. ENDIF. CREATE OBJECT mo_abap_language_version EXPORTING io_dot_abapgit = mo_dot_abapgit. ENDMETHOD. METHOD files_local. add_dot_abapgit( CHANGING ct_files = rt_files ). add_objects( EXPORTING iv_package = iv_package ii_log = ii_log it_filter = it_filter CHANGING ct_files = rt_files ). ENDMETHOD. METHOD filter_ignored_objects. DATA: ls_ignored_count TYPE ty_unsupported_count, lt_ignored_count TYPE ty_unsupported_count_tt, lo_folder_logic TYPE REF TO zcl_abapgit_folder_logic, ls_item TYPE zif_abapgit_definitions=>ty_item, lv_path TYPE string, lv_filename TYPE string. FIELD-SYMBOLS: LIKE LINE OF ct_tadir, TYPE ty_unsupported_count. " Ignore logic requires .abapGit.xml IF mo_dot_abapgit IS INITIAL OR iv_package IS INITIAL OR mi_log IS INITIAL. RETURN. ENDIF. lo_folder_logic = zcl_abapgit_folder_logic=>get_instance( ). LOOP AT ct_tadir ASSIGNING . CLEAR: ls_ignored_count. ls_item-obj_type = -object. ls_item-obj_name = -obj_name. IF -devclass IS NOT INITIAL. lv_path = lo_folder_logic->package_to_path( iv_top = iv_package io_dot = mo_dot_abapgit iv_package = -devclass ). ELSE. lv_path = mo_dot_abapgit->get_starting_folder( ). ENDIF. lv_filename = zcl_abapgit_filename_logic=>object_to_file( is_item = ls_item iv_ext = '*' ). IF mo_dot_abapgit->is_ignored( iv_path = lv_path iv_filename = lv_filename ) = abap_false. CONTINUE. ENDIF. READ TABLE lt_ignored_count ASSIGNING WITH TABLE KEY obj_type = -object. IF sy-subrc <> 0. ls_ignored_count-obj_type = -object. ls_ignored_count-count = 1. ls_ignored_count-obj_name = -obj_name. INSERT ls_ignored_count INTO TABLE lt_ignored_count ASSIGNING . ELSE. CLEAR: -obj_name. -count = -count + 1. ENDIF. " init object so we can remove these entries afterward CLEAR -object. ENDLOOP. IF lt_ignored_count IS INITIAL. RETURN. ENDIF. " remove ignored objects DELETE ct_tadir WHERE object IS INITIAL. LOOP AT lt_ignored_count ASSIGNING . IF -count = 1. mi_log->add_warning( |Object { -obj_type } { -obj_name } ignored| ). ELSE. mi_log->add_warning( |Object type { -obj_type } with | && |{ -count } objects ignored| ). ENDIF. ENDLOOP. ENDMETHOD. METHOD filter_unsupported_objects. DATA: ls_unsupported_count TYPE ty_unsupported_count, lt_supported_types TYPE zif_abapgit_objects=>ty_types_tt, lt_unsupported_count TYPE ty_unsupported_count_tt. FIELD-SYMBOLS: LIKE LINE OF ct_tadir, TYPE ty_unsupported_count. lt_supported_types = /apmg/cl_apm_abapgit_objects=>supported_list( ). LOOP AT ct_tadir ASSIGNING . CLEAR: ls_unsupported_count. READ TABLE lt_supported_types WITH KEY table_line = -object TRANSPORTING NO FIELDS. IF sy-subrc = 0. CONTINUE. ENDIF. READ TABLE lt_unsupported_count ASSIGNING WITH TABLE KEY obj_type = -object. IF sy-subrc <> 0. ls_unsupported_count-obj_type = -object. ls_unsupported_count-count = 1. ls_unsupported_count-obj_name = -obj_name. INSERT ls_unsupported_count INTO TABLE lt_unsupported_count ASSIGNING . ELSE. CLEAR: -obj_name. -count = -count + 1. ENDIF. CLEAR: -object. ENDLOOP. IF lt_unsupported_count IS INITIAL. RETURN. ENDIF. DELETE ct_tadir WHERE object IS INITIAL. IF mi_log IS BOUND. LOOP AT lt_unsupported_count ASSIGNING . IF -count = 1. mi_log->add_error( |Object type { -obj_type } not supported, | && |{ -obj_name } ignored| ). ELSE. mi_log->add_error( |Object type { -obj_type } not supported, | && |{ -count } objects ignored| ). ENDIF. ENDLOOP. ENDIF. ENDMETHOD. METHOD run_sequential. DATA: lx_error TYPE REF TO zcx_abapgit_exception, ls_file_item TYPE zif_abapgit_objects=>ty_serialization. ls_file_item-item-obj_type = is_tadir-object. ls_file_item-item-obj_name = is_tadir-obj_name. ls_file_item-item-devclass = is_tadir-devclass. ls_file_item-item-srcsystem = is_tadir-srcsystem. ls_file_item-item-abap_language_version = mo_abap_language_version->get_repo_abap_language_version( ). TRY. ls_file_item = /apmg/cl_apm_abapgit_objects=>serialize( is_item = ls_file_item-item io_i18n_params = zcl_abapgit_i18n_params=>new( is_params = ms_i18n_params ) ). add_to_return( is_file_item = ls_file_item iv_path = is_tadir-path ). CATCH zcx_abapgit_exception INTO lx_error. IF NOT mi_log IS INITIAL. mi_log->add_exception( ix_exc = lx_error is_item = ls_file_item-item ). ENDIF. ENDTRY. ENDMETHOD. METHOD serialize. * serializes only objects DATA: lv_max TYPE i, lv_count TYPE i, li_progress TYPE REF TO zif_abapgit_progress, li_exit TYPE REF TO zif_abapgit_exit, lo_timer TYPE REF TO zcl_abapgit_timer, lt_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt. FIELD-SYMBOLS: LIKE LINE OF it_tadir. CLEAR mt_files. mi_log = ii_log. lt_tadir = it_tadir. filter_unsupported_objects( CHANGING ct_tadir = lt_tadir ). filter_ignored_objects( EXPORTING iv_package = iv_package CHANGING ct_tadir = lt_tadir ). lv_count = lines( lt_tadir ). li_progress = zcl_abapgit_progress=>get_instance( lv_count ). lo_timer = zcl_abapgit_timer=>create( iv_text = 'Serialize:' iv_count = lv_count )->start( ). LOOP AT lt_tadir ASSIGNING . li_progress->show( iv_current = sy-tabix iv_text = |Serialize { -obj_name }, { lv_max } thread| ). run_sequential( ). ENDLOOP. li_progress->off( ). rt_files = mt_files. FREE mt_files. lo_timer->end( abap_true ). ENDMETHOD. ENDCLASS. ********************************************************************** * UTILS ********************************************************************** INTERFACE lif_kind. TYPES ty_kind TYPE c LENGTH 1. CONSTANTS: any TYPE ty_kind VALUE cl_abap_typedescr=>typekind_any, date TYPE ty_kind VALUE cl_abap_typedescr=>typekind_date, time TYPE ty_kind VALUE cl_abap_typedescr=>typekind_time, packed TYPE ty_kind VALUE cl_abap_typedescr=>typekind_packed, table TYPE ty_kind VALUE cl_abap_typedescr=>typekind_table, struct_flat TYPE ty_kind VALUE cl_abap_typedescr=>typekind_struct1, struct_deep TYPE ty_kind VALUE cl_abap_typedescr=>typekind_struct2, data_ref TYPE ty_kind VALUE cl_abap_typedescr=>typekind_dref, object_ref TYPE ty_kind VALUE cl_abap_typedescr=>typekind_oref, utclong TYPE ty_kind VALUE 'p', " cl_abap_typedescr=>typekind_utclong not in lower releases enum TYPE ty_kind VALUE 'k'. " cl_abap_typedescr=>typekind_enum not in lower releases CONSTANTS: BEGIN OF numeric, int1 TYPE ty_kind VALUE cl_abap_typedescr=>typekind_int1, int2 TYPE ty_kind VALUE cl_abap_typedescr=>typekind_int2, int4 TYPE ty_kind VALUE cl_abap_typedescr=>typekind_int, int8 TYPE ty_kind VALUE '8', " cl_abap_typedescr=>typekind_int8 not in lower releases float TYPE ty_kind VALUE cl_abap_typedescr=>typekind_float, packed TYPE ty_kind VALUE cl_abap_typedescr=>typekind_packed, decfloat16 TYPE ty_kind VALUE cl_abap_typedescr=>typekind_decfloat16, decfloat34 TYPE ty_kind VALUE cl_abap_typedescr=>typekind_decfloat34, END OF numeric. CONSTANTS: BEGIN OF texts, char TYPE ty_kind VALUE cl_abap_typedescr=>typekind_char, numc TYPE ty_kind VALUE cl_abap_typedescr=>typekind_num, string TYPE ty_kind VALUE cl_abap_typedescr=>typekind_string, END OF texts. CONSTANTS: BEGIN OF binary, hex TYPE ty_kind VALUE cl_abap_typedescr=>typekind_hex, xstring TYPE ty_kind VALUE cl_abap_typedescr=>typekind_xstring, END OF binary. CONSTANTS: BEGIN OF deep_targets, table TYPE ty_kind VALUE cl_abap_typedescr=>typekind_table, struct_flat TYPE ty_kind VALUE cl_abap_typedescr=>typekind_struct1, struct_deep TYPE ty_kind VALUE cl_abap_typedescr=>typekind_struct2, data_ref TYPE ty_kind VALUE cl_abap_typedescr=>typekind_dref, object_ref TYPE ty_kind VALUE cl_abap_typedescr=>typekind_oref, END OF deep_targets. ENDINTERFACE. CLASS lcl_utils DEFINITION FINAL. PUBLIC SECTION. CLASS-METHODS normalize_path IMPORTING iv_path TYPE string RETURNING VALUE(rv_path) TYPE string. CLASS-METHODS split_path IMPORTING iv_path TYPE string RETURNING VALUE(rv_path_name) TYPE /apmg/if_apm_ajson_types=>ty_path_name. CLASS-METHODS validate_array_index IMPORTING iv_path TYPE string iv_index TYPE string RETURNING VALUE(rv_index) TYPE i RAISING /apmg/cx_apm_ajson_error. CLASS-METHODS string_to_xstring_utf8 IMPORTING iv_str TYPE string RETURNING VALUE(rv_xstr) TYPE xstring. CLASS-METHODS xstring_to_string_utf8 IMPORTING iv_xstr TYPE xstring RETURNING VALUE(rv_str) TYPE string. CLASS-METHODS any_to_xstring IMPORTING iv_data TYPE any RETURNING VALUE(rv_xstr) TYPE xstring RAISING /apmg/cx_apm_ajson_error. CLASS-METHODS any_to_string IMPORTING iv_data TYPE any RETURNING VALUE(rv_str) TYPE string RAISING /apmg/cx_apm_ajson_error. CLASS-METHODS sanity_check IMPORTING iv_data TYPE csequence RAISING /apmg/cx_apm_ajson_error. ENDCLASS. CLASS lcl_utils IMPLEMENTATION. METHOD string_to_xstring_utf8. DATA lo_conv TYPE REF TO object. DATA lv_out_ce TYPE string. lv_out_ce = 'CL_ABAP_CONV_OUT_CE'. TRY. CALL METHOD ('CL_ABAP_CONV_CODEPAGE')=>create_out RECEIVING instance = lo_conv. CALL METHOD lo_conv->('IF_ABAP_CONV_OUT~CONVERT') EXPORTING source = iv_str RECEIVING result = rv_xstr. CATCH cx_sy_dyn_call_illegal_class. CALL METHOD (lv_out_ce)=>create EXPORTING encoding = 'UTF-8' RECEIVING conv = lo_conv. CALL METHOD lo_conv->('CONVERT') EXPORTING data = iv_str IMPORTING buffer = rv_xstr. ENDTRY. ENDMETHOD. METHOD xstring_to_string_utf8. DATA lo_conv TYPE REF TO object. DATA lv_in_ce TYPE string. lv_in_ce = 'CL_ABAP_CONV_IN_CE'. TRY. CALL METHOD ('CL_ABAP_CONV_CODEPAGE')=>create_in RECEIVING instance = lo_conv. CALL METHOD lo_conv->('IF_ABAP_CONV_IN~CONVERT') EXPORTING source = iv_xstr RECEIVING result = rv_str. CATCH cx_sy_dyn_call_illegal_class. CALL METHOD (lv_in_ce)=>create EXPORTING encoding = 'UTF-8' RECEIVING conv = lo_conv. CALL METHOD lo_conv->('CONVERT') EXPORTING data = iv_xstr IMPORTING buffer = rv_str. ENDTRY. ENDMETHOD. METHOD validate_array_index. IF NOT iv_index CO '0123456789'. /apmg/cx_apm_ajson_error=>raise( |Cannot add non-numeric key [{ iv_index }] to array [{ iv_path }]| ). ENDIF. rv_index = iv_index. IF rv_index = 0. /apmg/cx_apm_ajson_error=>raise( |Cannot add zero key to array [{ iv_path }]| ). ENDIF. ENDMETHOD. METHOD normalize_path. rv_path = iv_path. IF strlen( rv_path ) = 0. rv_path = '/'. ENDIF. IF rv_path+0(1) <> '/'. rv_path = '/' && rv_path. ENDIF. IF substring( val = rv_path off = strlen( rv_path ) - 1 ) <> '/'. rv_path = rv_path && '/'. ENDIF. ENDMETHOD. METHOD split_path. DATA lv_offs TYPE i. DATA lv_len TYPE i. DATA lv_trim_slash TYPE i. lv_len = strlen( iv_path ). IF lv_len = 0 OR iv_path = '/'. RETURN. " empty path is the alias for root item = '' + '' ENDIF. IF substring( val = iv_path off = lv_len - 1 ) = '/'. lv_trim_slash = 1. " ignore last '/' ENDIF. lv_offs = find( val = reverse( iv_path ) sub = '/' off = lv_trim_slash ). IF lv_offs = -1. lv_offs = lv_len. " treat whole string as the 'name' part ENDIF. lv_offs = lv_len - lv_offs. rv_path_name-path = normalize_path( substring( val = iv_path len = lv_offs ) ). rv_path_name-name = substring( val = iv_path off = lv_offs len = lv_len - lv_offs - lv_trim_slash ). " Replace tabs with slash to get original value rv_path_name-name = replace( val = rv_path_name-name sub = cl_abap_char_utilities=>horizontal_tab with = '/' occ = 0 ). ENDMETHOD. METHOD any_to_xstring. " supports xstring, char, string, or string_table as input DATA lo_type TYPE REF TO cl_abap_typedescr. DATA lo_table_type TYPE REF TO cl_abap_tabledescr. DATA lv_str TYPE string. FIELD-SYMBOLS: TYPE STANDARD TABLE. lo_type = cl_abap_typedescr=>describe_by_data( iv_data ). CASE lo_type->type_kind. WHEN lif_kind=>binary-xstring. " in case of binary data, skip the sanity check to have best performance rv_xstr = iv_data. WHEN lif_kind=>texts-string OR lif_kind=>texts-char. sanity_check( iv_data ). rv_xstr = string_to_xstring_utf8( iv_data ). WHEN lif_kind=>table. lo_table_type ?= lo_type. IF lo_table_type->table_kind <> cl_abap_tabledescr=>tablekind_std. /apmg/cx_apm_ajson_error=>raise( 'Unsupported type of input table (must be standard table)' ). ENDIF. TRY. ASSIGN iv_data TO . lv_str = concat_lines_of( table = sep = cl_abap_char_utilities=>newline ). sanity_check( lv_str ). rv_xstr = string_to_xstring_utf8( lv_str ). CATCH cx_root. /apmg/cx_apm_ajson_error=>raise( 'Error converting input table (should be string_table)' ). ENDTRY. WHEN OTHERS. /apmg/cx_apm_ajson_error=>raise( 'Unsupported type of input (must be char, string, string_table, or xstring)' ). ENDCASE. ENDMETHOD. METHOD any_to_string. " supports xstring, char, string, or string_table as input DATA lo_type TYPE REF TO cl_abap_typedescr. DATA lo_table_type TYPE REF TO cl_abap_tabledescr. FIELD-SYMBOLS: TYPE STANDARD TABLE. lo_type = cl_abap_typedescr=>describe_by_data( iv_data ). CASE lo_type->type_kind. WHEN lif_kind=>binary-xstring. rv_str = xstring_to_string_utf8( iv_data ). WHEN lif_kind=>texts-string OR lif_kind=>texts-char. rv_str = iv_data. WHEN lif_kind=>table. lo_table_type ?= lo_type. IF lo_table_type->table_kind <> cl_abap_tabledescr=>tablekind_std. /apmg/cx_apm_ajson_error=>raise( 'Unsupported type of input table (must be standard table)' ). ENDIF. TRY. ASSIGN iv_data TO . rv_str = concat_lines_of( table = sep = cl_abap_char_utilities=>newline ). CATCH cx_root. /apmg/cx_apm_ajson_error=>raise( 'Error converting input table (should be string_table)' ). ENDTRY. WHEN OTHERS. /apmg/cx_apm_ajson_error=>raise( 'Unsupported type of input (must be char, string, string_table, or xstring)' ). ENDCASE. ENDMETHOD. METHOD sanity_check. " A lightweight check covering the top-level JSON value would look like this " ^\s*(\{.*\}|\[.*\]|"(?:\\.|[^"\\])*"|true|false|null|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)\s*$ " Unfortunately, this is quite slow so we use a trivial check of the beginning of the JSON data FIND REGEX '^\s*(true|false|null|-?\d|"|\{|\[)' IN iv_data ##REGEX_POSIX. IF sy-subrc <> 0. /apmg/cx_apm_ajson_error=>raise( iv_msg = |Json parsing error: Not JSON| iv_location = 'Line 1, Offset 1' ). ENDIF. ENDMETHOD. ENDCLASS. ********************************************************************** * PARSER ********************************************************************** CLASS lcl_json_parser DEFINITION FINAL. PUBLIC SECTION. METHODS parse IMPORTING iv_json TYPE any iv_keep_item_order TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rt_json_tree) TYPE /apmg/if_apm_ajson_types=>ty_nodes_tt RAISING /apmg/cx_apm_ajson_error. PRIVATE SECTION. TYPES: ty_stack_tt TYPE STANDARD TABLE OF REF TO /apmg/if_apm_ajson_types=>ty_node. DATA mt_stack TYPE ty_stack_tt. DATA mv_stack_path TYPE string. DATA mv_keep_item_order TYPE abap_bool. METHODS raise IMPORTING iv_error TYPE string RAISING /apmg/cx_apm_ajson_error. METHODS _parse IMPORTING iv_json TYPE xstring RETURNING VALUE(rt_json_tree) TYPE /apmg/if_apm_ajson_types=>ty_nodes_tt RAISING /apmg/cx_apm_ajson_error cx_dynamic_check. " cx_sxml_error is not released on Steampunk #153 METHODS _get_location IMPORTING iv_json TYPE string iv_offset TYPE i RETURNING VALUE(rv_location) TYPE string. ENDCLASS. CLASS lcl_json_parser IMPLEMENTATION. METHOD parse. DATA lx_sxml_parse TYPE REF TO cx_sxml_parse_error. DATA lx_sxml TYPE REF TO cx_dynamic_check. DATA lv_location TYPE string. DATA lv_json TYPE xstring. mv_keep_item_order = iv_keep_item_order. " Includes lightweight sanity check (unless input is binary) lv_json = lcl_utils=>any_to_xstring( iv_json ). TRY. rt_json_tree = _parse( lv_json ). CATCH cx_sxml_parse_error INTO lx_sxml_parse. lv_location = _get_location( iv_json = lcl_utils=>any_to_string( iv_json ) iv_offset = lx_sxml_parse->xml_offset ). /apmg/cx_apm_ajson_error=>raise( iv_msg = |Json parsing error (SXML): { lx_sxml_parse->get_text( ) }| iv_location = lv_location ). CATCH cx_dynamic_check INTO lx_sxml. " cx_sxml_error /apmg/cx_apm_ajson_error=>raise( iv_msg = |Json parsing error (SXML): { lx_sxml->get_text( ) }| iv_location = '@PARSER' ). ENDTRY. ENDMETHOD. METHOD _get_location. DATA lv_json TYPE string. DATA lv_offset TYPE i. DATA lt_text TYPE TABLE OF string. DATA lv_text TYPE string. DATA lv_line TYPE i. DATA lv_pos TYPE i. lv_offset = iv_offset. IF lv_offset < 0. lv_offset = 0. ENDIF. IF lv_offset > strlen( iv_json ). lv_offset = strlen( iv_json ). ENDIF. lv_json = iv_json(lv_offset). REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf IN lv_json WITH cl_abap_char_utilities=>newline. SPLIT lv_json AT cl_abap_char_utilities=>newline INTO TABLE lt_text. lv_line = lines( lt_text ). IF lv_line = 0. lv_line = 1. lv_pos = 1. ELSE. READ TABLE lt_text INDEX lv_line INTO lv_text. lv_pos = strlen( lv_text ) + 1. ENDIF. rv_location = |Line { lv_line }, Offset { lv_pos }|. ENDMETHOD. METHOD _parse. DATA lo_reader TYPE REF TO if_sxml_reader. DATA lr_stack_top LIKE LINE OF mt_stack. DATA lo_node TYPE REF TO if_sxml_node. FIELD-SYMBOLS LIKE LINE OF rt_json_tree. CLEAR mt_stack. CLEAR mv_stack_path. IF iv_json IS INITIAL. RETURN. ENDIF. lo_reader = cl_sxml_string_reader=>create( iv_json ). " TODO: self protection, check non-empty, check starting from object ... DO. lo_node = lo_reader->read_next_node( ). IF lo_node IS NOT BOUND. EXIT. ENDIF. CASE lo_node->type. WHEN if_sxml_node=>co_nt_element_open. DATA lt_attributes TYPE if_sxml_attribute=>attributes. DATA lo_attr LIKE LINE OF lt_attributes. DATA lo_open TYPE REF TO if_sxml_open_element. lo_open ?= lo_node. APPEND INITIAL LINE TO rt_json_tree ASSIGNING . -type = lo_open->qname-name. READ TABLE mt_stack INDEX 1 INTO lr_stack_top. IF sy-subrc = 0. " Using string is faster than rebuilding path from stack -path = mv_stack_path. lr_stack_top->children = lr_stack_top->children + 1. IF lr_stack_top->type = `array`. " This is parser type not ajson type -name = |{ lr_stack_top->children }|. -index = lr_stack_top->children. ELSE. lt_attributes = lo_open->get_attributes( ). " JSON nodes always have one "name" attribute READ TABLE lt_attributes INTO lo_attr INDEX 1. ASSERT sy-subrc = 0 AND lo_attr->qname-name = 'name'. -name = lo_attr->get_value( ). IF mv_keep_item_order = abap_true. -order = lr_stack_top->children. ENDIF. ENDIF. IF -name IS INITIAL. raise( 'Node without name (maybe not JSON)' ). ENDIF. ENDIF. GET REFERENCE OF INTO lr_stack_top. INSERT lr_stack_top INTO mt_stack INDEX 1. " add path component (avoid issues with names containing slashes) mv_stack_path = mv_stack_path && replace( val = -name sub = '/' with = cl_abap_char_utilities=>horizontal_tab occ = 0 ) && '/'. WHEN if_sxml_node=>co_nt_element_close. DATA lo_close TYPE REF TO if_sxml_close_element. lo_close ?= lo_node. READ TABLE mt_stack INDEX 1 INTO lr_stack_top. DELETE mt_stack INDEX 1. IF lo_close->qname-name <> lr_stack_top->type. raise( 'Unexpected closing node type' ). ENDIF. " remove last path component mv_stack_path = substring( val = mv_stack_path len = find( val = mv_stack_path sub = '/' occ = -2 ) + 1 ). WHEN if_sxml_node=>co_nt_value. DATA lo_value TYPE REF TO if_sxml_value_node. lo_value ?= lo_node. -value = lo_value->get_value( ). WHEN OTHERS. raise( 'Unexpected node type' ). ENDCASE. ENDDO. IF lines( mt_stack ) > 0. raise( 'Unexpected end of data' ). ENDIF. ENDMETHOD. METHOD raise. /apmg/cx_apm_ajson_error=>raise( iv_location = mv_stack_path iv_msg = |JSON PARSER: { iv_error } @ { mv_stack_path }| ). ENDMETHOD. ENDCLASS. ********************************************************************** * SERIALIZER ********************************************************************** CLASS lcl_json_serializer DEFINITION FINAL CREATE PRIVATE. PUBLIC SECTION. CLASS-METHODS stringify IMPORTING it_json_tree TYPE /apmg/if_apm_ajson_types=>ty_nodes_ts iv_indent TYPE i DEFAULT 0 iv_keep_item_order TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rv_json_string) TYPE string RAISING /apmg/cx_apm_ajson_error. CLASS-METHODS class_constructor. PRIVATE SECTION. CLASS-DATA gv_comma_with_lf TYPE string. DATA mt_json_tree TYPE /apmg/if_apm_ajson_types=>ty_nodes_ts. DATA mv_keep_item_order TYPE abap_bool. DATA mt_buffer TYPE string_table. DATA mv_indent_step TYPE i. DATA mv_level TYPE i. CLASS-METHODS escape_string IMPORTING iv_unescaped TYPE string RETURNING VALUE(rv_escaped) TYPE string. METHODS _stringify RETURNING VALUE(rv_json_string) TYPE string RAISING /apmg/cx_apm_ajson_error. METHODS stringify_node IMPORTING is_node TYPE /apmg/if_apm_ajson_types=>ty_node RAISING /apmg/cx_apm_ajson_error. METHODS stringify_set IMPORTING iv_parent_path TYPE string iv_array TYPE abap_bool RAISING /apmg/cx_apm_ajson_error. ENDCLASS. CLASS lcl_json_serializer IMPLEMENTATION. METHOD class_constructor. gv_comma_with_lf = ',' && cl_abap_char_utilities=>newline. ENDMETHOD. METHOD stringify. DATA lo TYPE REF TO lcl_json_serializer. CREATE OBJECT lo. lo->mt_json_tree = it_json_tree. lo->mv_indent_step = iv_indent. lo->mv_keep_item_order = iv_keep_item_order. rv_json_string = lo->_stringify( ). ENDMETHOD. METHOD _stringify. FIELD-SYMBOLS LIKE LINE OF mt_json_tree. READ TABLE mt_json_tree ASSIGNING WITH KEY path = '' name = ''. " Root IF sy-subrc <> 0. RETURN. ENDIF. stringify_node( ). rv_json_string = concat_lines_of( table = mt_buffer ). ENDMETHOD. METHOD stringify_node. DATA lv_item TYPE string. DATA lv_indent_prefix TYPE string. IF mv_indent_step > 0. lv_indent_prefix = repeat( val = ` ` occ = mv_indent_step * mv_level ). lv_item = lv_indent_prefix. ENDIF. IF is_node-name IS NOT INITIAL AND is_node-index IS INITIAL. " Not root, not array item IF mv_indent_step > 0. lv_item = lv_item && |"{ is_node-name }": |. ELSE. lv_item = |"{ is_node-name }":|. ENDIF. ENDIF. CASE is_node-type. WHEN /apmg/if_apm_ajson_types=>node_type-array. lv_item = lv_item && '['. WHEN /apmg/if_apm_ajson_types=>node_type-object. lv_item = lv_item && '{'. WHEN /apmg/if_apm_ajson_types=>node_type-string. lv_item = lv_item && |"{ escape_string( is_node-value ) }"|. WHEN /apmg/if_apm_ajson_types=>node_type-boolean OR /apmg/if_apm_ajson_types=>node_type-number. lv_item = lv_item && is_node-value. WHEN /apmg/if_apm_ajson_types=>node_type-null. lv_item = lv_item && 'null'. WHEN OTHERS. /apmg/cx_apm_ajson_error=>raise( iv_msg = |Unexpected type [{ is_node-type }]| iv_location = is_node-path && is_node-name ). ENDCASE. IF mv_indent_step > 0 AND ( is_node-type = /apmg/if_apm_ajson_types=>node_type-array OR is_node-type = /apmg/if_apm_ajson_types=>node_type-object ) AND is_node-children > 0. mv_level = mv_level + 1. lv_item = lv_item && cl_abap_char_utilities=>newline. ENDIF. APPEND lv_item TO mt_buffer. " finish complex item IF is_node-type = /apmg/if_apm_ajson_types=>node_type-array OR is_node-type = /apmg/if_apm_ajson_types=>node_type-object. DATA lv_children_path TYPE string. DATA lv_tail TYPE string. lv_children_path = is_node-path && is_node-name && '/'. " for root: path = '' and name = '', so result is '/' CASE is_node-type. WHEN /apmg/if_apm_ajson_types=>node_type-array. IF is_node-children > 0. stringify_set( iv_parent_path = lv_children_path iv_array = abap_true ). ENDIF. lv_tail = ']'. WHEN /apmg/if_apm_ajson_types=>node_type-object. IF is_node-children > 0. stringify_set( iv_parent_path = lv_children_path iv_array = abap_false ). ENDIF. lv_tail = '}'. ENDCASE. IF mv_indent_step > 0 AND is_node-children > 0. lv_tail = lv_indent_prefix && lv_tail. mv_level = mv_level - 1. ENDIF. APPEND lv_tail TO mt_buffer. ENDIF. ENDMETHOD. METHOD stringify_set. DATA lv_tab_key TYPE string. DATA lv_first_done TYPE abap_bool. FIELD-SYMBOLS LIKE LINE OF mt_json_tree. IF iv_array = abap_true. lv_tab_key = 'array_index'. " path + index ELSEIF mv_keep_item_order = abap_true. lv_tab_key = 'item_order'. " path + order ELSE. lv_tab_key = 'primary_key'. " path + name ENDIF. LOOP AT mt_json_tree ASSIGNING USING KEY (lv_tab_key) WHERE path = iv_parent_path. IF lv_first_done = abap_false. lv_first_done = abap_true. ELSEIF mv_indent_step > 0. APPEND gv_comma_with_lf TO mt_buffer. ELSE. APPEND ',' TO mt_buffer. ENDIF. stringify_node( ). ENDLOOP. IF mv_indent_step > 0 AND lv_first_done = abap_true. " only of items were in the list APPEND cl_abap_char_utilities=>newline TO mt_buffer. ENDIF. ENDMETHOD. METHOD escape_string. rv_escaped = iv_unescaped. IF rv_escaped CA |"\\\t\n\r|. " TODO consider performance ... " see also https://www.json.org/json-en.html rv_escaped = replace( val = rv_escaped sub = '\' with = '\\' occ = 0 ). rv_escaped = replace( val = rv_escaped sub = |\n| with = '\n' occ = 0 ). rv_escaped = replace( val = rv_escaped sub = |\r| with = '\r' occ = 0 ). rv_escaped = replace( val = rv_escaped sub = |\t| with = '\t' occ = 0 ). rv_escaped = replace( val = rv_escaped sub = '"' with = '\"' occ = 0 ). ENDIF. ENDMETHOD. ENDCLASS. ********************************************************************** * JSON_TO_ABAP ********************************************************************** CLASS lcl_json_to_abap DEFINITION FINAL. PUBLIC SECTION. METHODS constructor IMPORTING !iv_corresponding TYPE abap_bool DEFAULT abap_false !ii_custom_mapping TYPE REF TO /apmg/if_apm_ajson_mapping OPTIONAL !ii_refs_initiator TYPE REF TO /apmg/if_apm_ajson_ref_initial OPTIONAL. METHODS to_abap IMPORTING it_nodes TYPE /apmg/if_apm_ajson_types=>ty_nodes_ts CHANGING c_container TYPE any RAISING /apmg/cx_apm_ajson_error. METHODS to_timestamp IMPORTING iv_value TYPE /apmg/if_apm_ajson_types=>ty_node-value RETURNING VALUE(rv_result) TYPE timestamp RAISING /apmg/cx_apm_ajson_error. METHODS to_timestampl IMPORTING iv_value TYPE /apmg/if_apm_ajson_types=>ty_node-value RETURNING VALUE(rv_result) TYPE timestampl RAISING /apmg/cx_apm_ajson_error. METHODS to_date IMPORTING iv_value TYPE /apmg/if_apm_ajson_types=>ty_node-value RETURNING VALUE(rv_result) TYPE d RAISING /apmg/cx_apm_ajson_error. METHODS to_time IMPORTING iv_value TYPE /apmg/if_apm_ajson_types=>ty_node-value RETURNING VALUE(rv_result) TYPE t RAISING /apmg/cx_apm_ajson_error. PRIVATE SECTION. TYPES: BEGIN OF ty_type_cache, type_path TYPE string, target_field_name TYPE string, dd TYPE REF TO cl_abap_datadescr, type_kind LIKE lif_kind=>any, tab_item_buf TYPE REF TO data, END OF ty_type_cache. DATA mt_node_type_cache TYPE HASHED TABLE OF ty_type_cache WITH UNIQUE KEY type_path. DATA mr_nodes TYPE REF TO /apmg/if_apm_ajson_types=>ty_nodes_ts. DATA mi_custom_mapping TYPE REF TO /apmg/if_apm_ajson_mapping. DATA mi_refs_initiator TYPE REF TO /apmg/if_apm_ajson_ref_initial. DATA mv_corresponding TYPE abap_bool. METHODS any_to_abap IMPORTING iv_path TYPE string is_parent_type TYPE ty_type_cache OPTIONAL i_container_ref TYPE REF TO data RAISING /apmg/cx_apm_ajson_error. METHODS value_to_abap IMPORTING is_node TYPE /apmg/if_apm_ajson_types=>ty_node is_node_type TYPE ty_type_cache i_container_ref TYPE REF TO data RAISING /apmg/cx_apm_ajson_error cx_sy_conversion_no_number. METHODS get_node_type IMPORTING is_node TYPE /apmg/if_apm_ajson_types=>ty_node OPTIONAL " Empty for root is_parent_type TYPE ty_type_cache OPTIONAL i_container_ref TYPE REF TO data OPTIONAL RETURNING VALUE(rs_node_type) TYPE ty_type_cache RAISING /apmg/cx_apm_ajson_error. METHODS get_data_ref IMPORTING is_node TYPE /apmg/if_apm_ajson_types=>ty_node RETURNING VALUE(ro_ref) TYPE REF TO data RAISING /apmg/cx_apm_ajson_error. ENDCLASS. CLASS lcl_json_to_abap IMPLEMENTATION. METHOD constructor. mi_custom_mapping = ii_custom_mapping. mi_refs_initiator = ii_refs_initiator. mv_corresponding = iv_corresponding. ENDMETHOD. METHOD to_abap. DATA lr_ref TYPE REF TO data. CLEAR c_container. CLEAR mt_node_type_cache. GET REFERENCE OF c_container INTO lr_ref. GET REFERENCE OF it_nodes INTO mr_nodes. get_node_type( i_container_ref = lr_ref ). " Pre-cache root node type any_to_abap( iv_path = '' i_container_ref = lr_ref ). ENDMETHOD. METHOD get_node_type. DATA lv_node_type_path TYPE string. DATA lo_sdescr TYPE REF TO cl_abap_structdescr. DATA lo_tdescr TYPE REF TO cl_abap_tabledescr. DATA lo_ddescr TYPE REF TO cl_abap_datadescr. " Calculate type path IF is_parent_type-type_kind = lif_kind=>table. lv_node_type_path = is_parent_type-type_path && '/-'. " table item type ELSEIF is_parent_type-type_kind = lif_kind=>data_ref. lv_node_type_path = is_parent_type-type_path && '/+'. " data reference ELSEIF is_parent_type-type_kind IS NOT INITIAL. lv_node_type_path = is_parent_type-type_path && '/' && is_node-name. ENDIF. " For root node lv_node_type_path remains '' " Get or create cached READ TABLE mt_node_type_cache INTO rs_node_type WITH KEY type_path = lv_node_type_path. IF sy-subrc <> 0. rs_node_type-type_path = lv_node_type_path. IF mi_custom_mapping IS BOUND. rs_node_type-target_field_name = to_upper( mi_custom_mapping->to_abap( iv_path = is_node-path iv_name = is_node-name ) ). IF rs_node_type-target_field_name IS INITIAL. rs_node_type-target_field_name = to_upper( is_node-name ). ENDIF. ELSE. rs_node_type-target_field_name = to_upper( is_node-name ). ENDIF. CASE is_parent_type-type_kind. WHEN lif_kind=>table. lo_tdescr ?= is_parent_type-dd. rs_node_type-dd = lo_tdescr->get_table_line_type( ). WHEN lif_kind=>struct_flat OR lif_kind=>struct_deep. lo_sdescr ?= is_parent_type-dd. lo_sdescr->get_component_type( EXPORTING p_name = rs_node_type-target_field_name RECEIVING p_descr_ref = rs_node_type-dd EXCEPTIONS component_not_found = 4 ). IF sy-subrc <> 0. IF mv_corresponding = abap_false. /apmg/cx_apm_ajson_error=>raise( |Path not found| ). ELSE. CLEAR rs_node_type. RETURN. ENDIF. ENDIF. WHEN '' OR lif_kind=>data_ref. " Root node or ref to data rs_node_type-dd ?= cl_abap_typedescr=>describe_by_data_ref( i_container_ref ). WHEN OTHERS. /apmg/cx_apm_ajson_error=>raise( |Unexpected parent type| ). ENDCASE. rs_node_type-type_kind = rs_node_type-dd->type_kind. " for caching and cleaner uninitialized access IF rs_node_type-type_kind = lif_kind=>table. lo_tdescr ?= rs_node_type-dd. IF lo_tdescr->table_kind <> cl_abap_tabledescr=>tablekind_std. lo_ddescr = lo_tdescr->get_table_line_type( ). CREATE DATA rs_node_type-tab_item_buf TYPE HANDLE lo_ddescr. ENDIF. ENDIF. INSERT rs_node_type INTO TABLE mt_node_type_cache. ENDIF. ENDMETHOD. METHOD get_data_ref. IF mi_refs_initiator IS INITIAL. /apmg/cx_apm_ajson_error=>raise( 'Missing ref initiator' ). ENDIF. ro_ref = mi_refs_initiator->get_data_ref( is_node ). IF ro_ref IS INITIAL. /apmg/cx_apm_ajson_error=>raise( 'Cannot use initial data ref' ). ENDIF. ENDMETHOD. METHOD any_to_abap. DATA ls_node_type LIKE LINE OF mt_node_type_cache. DATA lx_ajson TYPE REF TO /apmg/cx_apm_ajson_error. DATA lx_root TYPE REF TO cx_root. DATA lr_target_field TYPE REF TO data. FIELD-SYMBOLS TYPE /apmg/if_apm_ajson_types=>ty_node. FIELD-SYMBOLS TYPE STANDARD TABLE. FIELD-SYMBOLS TYPE ANY TABLE. FIELD-SYMBOLS TYPE any. FIELD-SYMBOLS TYPE any. FIELD-SYMBOLS TYPE any. " Assign container CASE is_parent_type-type_kind. WHEN lif_kind=>table. IF is_parent_type-tab_item_buf IS BOUND. " Indirect hint that table was sorted/hashed, see get_node_type. ASSIGN i_container_ref->* TO . ASSERT sy-subrc = 0. lr_target_field = is_parent_type-tab_item_buf. " For hashed/sorted table - same buffer for all children ASSIGN is_parent_type-tab_item_buf->* TO . ASSERT sy-subrc = 0. ELSE. ASSIGN i_container_ref->* TO . ASSERT sy-subrc = 0. ENDIF. WHEN lif_kind=>struct_flat OR lif_kind=>struct_deep. ASSIGN i_container_ref->* TO . ASSERT sy-subrc = 0. ENDCASE. TRY. " array_index because stringified index goes in wrong order [1, 10, 2 ...] LOOP AT mr_nodes->* ASSIGNING USING KEY array_index WHERE path = iv_path. " Get or create type cache record IF is_parent_type-type_kind <> lif_kind=>table OR ls_node_type-type_kind IS INITIAL. " table records are the same, no need to refetch twice ls_node_type = get_node_type( is_node = is_parent_type = is_parent_type ). IF mv_corresponding = abap_true AND ls_node_type IS INITIAL. CONTINUE. ENDIF. ENDIF. " Validate node type IF ls_node_type-type_kind = lif_kind=>object_ref. " TODO maybe in future /apmg/cx_apm_ajson_error=>raise( 'Cannot assign to ref' ). ENDIF. " Find target field reference CASE is_parent_type-type_kind. WHEN lif_kind=>table. IF NOT ls_node_type-target_field_name CO '0123456789'. " Does not affect anything actually but for integrity /apmg/cx_apm_ajson_error=>raise( 'Need index to access tables' ). ENDIF. IF is_parent_type-tab_item_buf IS NOT BOUND. " Indirect hint that table was srt/hsh, see get_node_type APPEND INITIAL LINE TO REFERENCE INTO lr_target_field. ASSERT sy-subrc = 0. ELSE. CLEAR . ENDIF. WHEN lif_kind=>struct_flat OR lif_kind=>struct_deep. ASSIGN COMPONENT ls_node_type-target_field_name OF STRUCTURE TO . ASSERT sy-subrc = 0. GET REFERENCE OF INTO lr_target_field. WHEN ''. " Root node lr_target_field = i_container_ref. WHEN OTHERS. /apmg/cx_apm_ajson_error=>raise( 'Unexpected parent type' ). ENDCASE. " For data refs, get the type it is pointing to IF ls_node_type-type_kind = lif_kind=>data_ref. lr_target_field = get_data_ref( ). ls_node_type = get_node_type( i_container_ref = lr_target_field is_node = is_parent_type = ls_node_type ). ENDIF. " Process value assignment CASE -type. WHEN /apmg/if_apm_ajson_types=>node_type-object. IF ls_node_type-type_kind <> lif_kind=>struct_flat AND ls_node_type-type_kind <> lif_kind=>struct_deep. /apmg/cx_apm_ajson_error=>raise( 'Expected structure' ). ENDIF. any_to_abap( iv_path = -path && -name && '/' is_parent_type = ls_node_type i_container_ref = lr_target_field ). WHEN /apmg/if_apm_ajson_types=>node_type-array. IF NOT ls_node_type-type_kind = lif_kind=>table. /apmg/cx_apm_ajson_error=>raise( 'Expected table' ). ENDIF. any_to_abap( iv_path = -path && -name && '/' is_parent_type = ls_node_type i_container_ref = lr_target_field ). WHEN OTHERS. value_to_abap( is_node = is_node_type = ls_node_type i_container_ref = lr_target_field ). ENDCASE. IF is_parent_type-tab_item_buf IS BOUND. " Indirect hint that table was sorted/hashed, see get_node_type. TRY. INSERT INTO TABLE . IF sy-subrc <> 0. /apmg/cx_apm_ajson_error=>raise( 'Duplicate insertion' ). ENDIF. CATCH cx_sy_itab_duplicate_key. /apmg/cx_apm_ajson_error=>raise( 'Duplicate insertion' ). ENDTRY. ENDIF. ENDLOOP. CATCH /apmg/cx_apm_ajson_error INTO lx_ajson. IF lx_ajson->location IS INITIAL. lx_ajson->set_location( -path && -name ). ENDIF. RAISE EXCEPTION lx_ajson. CATCH cx_sy_conversion_no_number. /apmg/cx_apm_ajson_error=>raise( iv_msg = 'Source is not a number' iv_location = -path && -name ). CATCH cx_root INTO lx_root. /apmg/cx_apm_ajson_error=>raise( iv_msg = lx_root->get_text( ) iv_location = -path && -name ). ENDTRY. ENDMETHOD. METHOD value_to_abap. FIELD-SYMBOLS TYPE any. IF is_node_type-type_kind CA lif_kind=>deep_targets. /apmg/cx_apm_ajson_error=>raise( |Unsupported target for value [{ is_node_type-type_kind }]| ). ENDIF. ASSIGN i_container_ref->* TO . ASSERT sy-subrc = 0. CASE is_node-type. WHEN /apmg/if_apm_ajson_types=>node_type-null. " Do nothing WHEN /apmg/if_apm_ajson_types=>node_type-boolean. " TODO: check type ? = boolc( is_node-value = 'true' ). WHEN /apmg/if_apm_ajson_types=>node_type-number. " TODO: check type ? = is_node-value. WHEN /apmg/if_apm_ajson_types=>node_type-string. " TODO: check type ? IF is_node-value IS NOT INITIAL. IF is_node_type-type_kind = lif_kind=>date. = to_date( is_node-value ). ELSEIF is_node_type-type_kind = lif_kind=>time. = to_time( is_node-value ). ELSEIF is_node_type-dd->absolute_name = '\TYPE=TIMESTAMP'. = to_timestamp( is_node-value ). ELSEIF is_node_type-dd->absolute_name = '\TYPE=TIMESTAMPL'. = to_timestampl( is_node-value ). ELSEIF is_node_type-type_kind = lif_kind=>packed. " Number as a string, but not a timestamp = is_node-value. ELSE. = is_node-value. ENDIF. ELSE. = is_node-value. ENDIF. WHEN OTHERS. /apmg/cx_apm_ajson_error=>raise( |Unexpected JSON type [{ is_node-type }]| ). ENDCASE. ENDMETHOD. METHOD to_date. DATA lv_y TYPE c LENGTH 4. DATA lv_m TYPE c LENGTH 2. DATA lv_d TYPE c LENGTH 2. FIND FIRST OCCURRENCE OF REGEX '^(\d{4})-(\d{2})-(\d{2})(T|$)' "#EC NOTEXT IN iv_value SUBMATCHES lv_y lv_m lv_d ##REGEX_POSIX. IF sy-subrc <> 0. /apmg/cx_apm_ajson_error=>raise( 'Unexpected date format' ). ENDIF. CONCATENATE lv_y lv_m lv_d INTO rv_result. ENDMETHOD. METHOD to_timestamp. DATA lv_timestampl TYPE timestampl. DATA lv_int_part TYPE string. DATA lv_frac_part TYPE string. lv_timestampl = to_timestampl( iv_value ). SPLIT |{ lv_timestampl }| AT '.' INTO lv_int_part lv_frac_part. " short timestamp must not have any fraction (.000 is acceptable) IF lv_frac_part CA '123456789'. /apmg/cx_apm_ajson_error=>raise( 'Unexpected timestamp format' ). ENDIF. rv_result = lv_int_part. ENDMETHOD. METHOD to_timestampl. CONSTANTS lc_utc TYPE c LENGTH 6 VALUE 'UTC'. CONSTANTS lc_regex_ts_with_hour TYPE string VALUE `^(\d{4})-(\d{2})-(\d{2})(T)(\d{2}):(\d{2}):(\d{2})(\+)(\d{2}):(\d{2})`. "#EC NOTEXT CONSTANTS lc_regex_ts_utc TYPE string VALUE `^(\d{4})-(\d{2})-(\d{2})(T)(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|$)`. "#EC NOTEXT DATA: BEGIN OF ls_timestamp, year TYPE c LENGTH 4, month TYPE c LENGTH 2, day TYPE c LENGTH 2, t TYPE c LENGTH 1, hour TYPE c LENGTH 2, minute TYPE c LENGTH 2, second TYPE c LENGTH 2, frac TYPE c LENGTH 8, local_sign TYPE c LENGTH 1, local_hour TYPE c LENGTH 2, local_minute TYPE c LENGTH 2, END OF ls_timestamp. DATA lv_date TYPE d. DATA lv_time TYPE t. DATA lv_seconds_conv TYPE i. DATA lv_timestamp TYPE timestampl. FIND FIRST OCCURRENCE OF REGEX lc_regex_ts_with_hour IN iv_value SUBMATCHES ls_timestamp-year ls_timestamp-month ls_timestamp-day ls_timestamp-t ls_timestamp-hour ls_timestamp-minute ls_timestamp-second ls_timestamp-local_sign ls_timestamp-local_hour ls_timestamp-local_minute ##REGEX_POSIX. IF sy-subrc = 0. lv_seconds_conv = ( ls_timestamp-local_hour * 3600 ) + ( ls_timestamp-local_minute * 60 ). ELSE. FIND FIRST OCCURRENCE OF REGEX lc_regex_ts_utc IN iv_value SUBMATCHES ls_timestamp-year ls_timestamp-month ls_timestamp-day ls_timestamp-t ls_timestamp-hour ls_timestamp-minute ls_timestamp-second ls_timestamp-frac ##REGEX_POSIX. IF sy-subrc <> 0. /apmg/cx_apm_ajson_error=>raise( 'Unexpected timestamp format' ). ENDIF. ENDIF. CONCATENATE ls_timestamp-year ls_timestamp-month ls_timestamp-day INTO lv_date. CONCATENATE ls_timestamp-hour ls_timestamp-minute ls_timestamp-second INTO lv_time. CONVERT DATE lv_date TIME lv_time INTO TIME STAMP lv_timestamp TIME ZONE lc_utc. " add fraction IF ls_timestamp-frac IS NOT INITIAL. ls_timestamp-frac = '0' && ls_timestamp-frac. lv_timestamp = lv_timestamp + ls_timestamp-frac. ENDIF. TRY. CASE ls_timestamp-local_sign. WHEN '-'. lv_timestamp = cl_abap_tstmp=>add( tstmp = lv_timestamp secs = lv_seconds_conv ). WHEN '+'. lv_timestamp = cl_abap_tstmp=>subtractsecs( tstmp = lv_timestamp secs = lv_seconds_conv ). ENDCASE. CATCH cx_parameter_invalid_range cx_parameter_invalid_type. /apmg/cx_apm_ajson_error=>raise( 'Unexpected error calculating timestamp' ). ENDTRY. IF lv_timestamp IS NOT INITIAL. cl_abap_tstmp=>move( EXPORTING tstmp_src = lv_timestamp IMPORTING tstmp_tgt = rv_result ). ENDIF. ENDMETHOD. METHOD to_time. DATA lv_h TYPE c LENGTH 2. DATA lv_m TYPE c LENGTH 2. DATA lv_s TYPE c LENGTH 2. FIND FIRST OCCURRENCE OF REGEX '^(\d{2}):(\d{2}):(\d{2})(T|$)' "#EC NOTEXT IN iv_value SUBMATCHES lv_h lv_m lv_s ##REGEX_POSIX. IF sy-subrc <> 0. /apmg/cx_apm_ajson_error=>raise( 'Unexpected time format' ). ENDIF. CONCATENATE lv_h lv_m lv_s INTO rv_result. ENDMETHOD. ENDCLASS. ********************************************************************** * ABAP_TO_JSON ********************************************************************** CLASS lcl_abap_to_json DEFINITION FINAL. PUBLIC SECTION. CLASS-METHODS convert IMPORTING iv_data TYPE any is_prefix TYPE /apmg/if_apm_ajson_types=>ty_path_name OPTIONAL iv_array_index TYPE i DEFAULT 0 ii_custom_mapping TYPE REF TO /apmg/if_apm_ajson_mapping OPTIONAL is_opts TYPE /apmg/if_apm_ajson=>ty_opts OPTIONAL iv_item_order TYPE i DEFAULT 0 RETURNING VALUE(rt_nodes) TYPE /apmg/if_apm_ajson_types=>ty_nodes_tt RAISING /apmg/cx_apm_ajson_error. CLASS-METHODS insert_with_type IMPORTING iv_data TYPE any iv_type TYPE /apmg/if_apm_ajson_types=>ty_node_type is_prefix TYPE /apmg/if_apm_ajson_types=>ty_path_name OPTIONAL iv_array_index TYPE i DEFAULT 0 ii_custom_mapping TYPE REF TO /apmg/if_apm_ajson_mapping OPTIONAL is_opts TYPE /apmg/if_apm_ajson=>ty_opts OPTIONAL iv_item_order TYPE i DEFAULT 0 RETURNING VALUE(rt_nodes) TYPE /apmg/if_apm_ajson_types=>ty_nodes_tt RAISING /apmg/cx_apm_ajson_error. CLASS-METHODS format_date IMPORTING iv_date TYPE d RETURNING VALUE(rv_str) TYPE string. CLASS-METHODS format_time IMPORTING iv_time TYPE t RETURNING VALUE(rv_str) TYPE string. CLASS-METHODS format_timestamp IMPORTING iv_ts TYPE timestamp RETURNING VALUE(rv_str) TYPE string. CLASS-METHODS format_timestampl IMPORTING iv_ts TYPE timestampl RETURNING VALUE(rv_str) TYPE string. CLASS-METHODS class_constructor. PRIVATE SECTION. CLASS-DATA gv_ajson_absolute_type_name TYPE string. DATA mi_custom_mapping TYPE REF TO /apmg/if_apm_ajson_mapping. DATA mv_keep_item_order TYPE abap_bool. DATA mv_format_datetime TYPE abap_bool. METHODS convert_any IMPORTING iv_data TYPE any io_type TYPE REF TO cl_abap_typedescr is_prefix TYPE /apmg/if_apm_ajson_types=>ty_path_name iv_index TYPE i DEFAULT 0 iv_item_order TYPE i DEFAULT 0 CHANGING ct_nodes TYPE /apmg/if_apm_ajson_types=>ty_nodes_tt RAISING /apmg/cx_apm_ajson_error. METHODS convert_ajson IMPORTING io_json TYPE REF TO /apmg/if_apm_ajson is_prefix TYPE /apmg/if_apm_ajson_types=>ty_path_name iv_index TYPE i DEFAULT 0 iv_item_order TYPE i DEFAULT 0 CHANGING ct_nodes TYPE /apmg/if_apm_ajson_types=>ty_nodes_tt RAISING /apmg/cx_apm_ajson_error. METHODS convert_value IMPORTING iv_data TYPE any io_type TYPE REF TO cl_abap_typedescr is_prefix TYPE /apmg/if_apm_ajson_types=>ty_path_name iv_index TYPE i DEFAULT 0 iv_item_order TYPE i DEFAULT 0 CHANGING ct_nodes TYPE /apmg/if_apm_ajson_types=>ty_nodes_tt RAISING /apmg/cx_apm_ajson_error. METHODS convert_ref IMPORTING iv_data TYPE any is_prefix TYPE /apmg/if_apm_ajson_types=>ty_path_name iv_index TYPE i DEFAULT 0 iv_item_order TYPE i DEFAULT 0 CHANGING ct_nodes TYPE /apmg/if_apm_ajson_types=>ty_nodes_tt RAISING /apmg/cx_apm_ajson_error. METHODS convert_struc IMPORTING iv_data TYPE any io_type TYPE REF TO cl_abap_typedescr is_prefix TYPE /apmg/if_apm_ajson_types=>ty_path_name iv_index TYPE i DEFAULT 0 iv_item_order TYPE i DEFAULT 0 CHANGING ct_nodes TYPE /apmg/if_apm_ajson_types=>ty_nodes_tt RAISING /apmg/cx_apm_ajson_error. METHODS convert_table IMPORTING iv_data TYPE any io_type TYPE REF TO cl_abap_typedescr is_prefix TYPE /apmg/if_apm_ajson_types=>ty_path_name iv_index TYPE i DEFAULT 0 iv_item_order TYPE i DEFAULT 0 CHANGING ct_nodes TYPE /apmg/if_apm_ajson_types=>ty_nodes_tt RAISING /apmg/cx_apm_ajson_error. METHODS insert_value_with_type IMPORTING iv_data TYPE any iv_type TYPE /apmg/if_apm_ajson_types=>ty_node_type io_type TYPE REF TO cl_abap_typedescr is_prefix TYPE /apmg/if_apm_ajson_types=>ty_path_name iv_index TYPE i DEFAULT 0 iv_item_order TYPE i DEFAULT 0 CHANGING ct_nodes TYPE /apmg/if_apm_ajson_types=>ty_nodes_tt RAISING /apmg/cx_apm_ajson_error. ENDCLASS. CLASS lcl_abap_to_json IMPLEMENTATION. METHOD class_constructor. DATA lo_dummy TYPE REF TO /apmg/cl_apm_ajson. DATA lo_type TYPE REF TO cl_abap_refdescr. lo_type ?= cl_abap_typedescr=>describe_by_data( lo_dummy ). gv_ajson_absolute_type_name = lo_type->get_referenced_type( )->absolute_name. ENDMETHOD. METHOD convert. DATA lo_type TYPE REF TO cl_abap_typedescr. DATA lo_converter TYPE REF TO lcl_abap_to_json. lo_type = cl_abap_typedescr=>describe_by_data( iv_data ). CREATE OBJECT lo_converter. lo_converter->mi_custom_mapping = ii_custom_mapping. lo_converter->mv_keep_item_order = is_opts-keep_item_order. lo_converter->mv_format_datetime = is_opts-format_datetime. lo_converter->convert_any( EXPORTING iv_data = iv_data io_type = lo_type is_prefix = is_prefix iv_index = iv_array_index iv_item_order = iv_item_order CHANGING ct_nodes = rt_nodes ). ENDMETHOD. METHOD convert_any. CASE io_type->kind. WHEN cl_abap_typedescr=>kind_elem. convert_value( EXPORTING iv_data = iv_data io_type = io_type is_prefix = is_prefix iv_index = iv_index iv_item_order = iv_item_order CHANGING ct_nodes = ct_nodes ). WHEN cl_abap_typedescr=>kind_struct. convert_struc( EXPORTING iv_data = iv_data io_type = io_type is_prefix = is_prefix iv_index = iv_index iv_item_order = iv_item_order CHANGING ct_nodes = ct_nodes ). WHEN cl_abap_typedescr=>kind_table. convert_table( EXPORTING iv_data = iv_data io_type = io_type is_prefix = is_prefix iv_index = iv_index iv_item_order = iv_item_order CHANGING ct_nodes = ct_nodes ). WHEN OTHERS. IF io_type->type_kind = lif_kind=>data_ref OR iv_data IS INITIAL. " Convert data references and initial references to other types (like ref to class or interface) " Initial references will result in "null" convert_ref( EXPORTING iv_data = iv_data is_prefix = is_prefix iv_index = iv_index iv_item_order = iv_item_order CHANGING ct_nodes = ct_nodes ). ELSEIF io_type->type_kind = lif_kind=>object_ref AND cl_abap_typedescr=>describe_by_object_ref( iv_data )->absolute_name = gv_ajson_absolute_type_name. convert_ajson( EXPORTING io_json = iv_data is_prefix = is_prefix iv_index = iv_index iv_item_order = iv_item_order CHANGING ct_nodes = ct_nodes ). ELSE. /apmg/cx_apm_ajson_error=>raise( |Unsupported type [{ io_type->type_kind }] @{ is_prefix-path && is_prefix-name }| ). ENDIF. ENDCASE. ENDMETHOD. METHOD convert_ajson. FIELD-SYMBOLS LIKE LINE OF ct_nodes. FIELD-SYMBOLS LIKE LINE OF ct_nodes. IF io_json IS NOT BOUND. RETURN. ENDIF. LOOP AT io_json->mt_json_tree ASSIGNING . APPEND TO ct_nodes ASSIGNING . IF -path IS INITIAL AND -name IS INITIAL. " root node -path = is_prefix-path. -name = is_prefix-name. -index = iv_index. -order = iv_item_order. ELSE. -path = is_prefix-path && is_prefix-name && -path. ENDIF. ENDLOOP. ENDMETHOD. METHOD format_date. IF iv_date IS NOT INITIAL. rv_str = iv_date+0(4) && '-' && iv_date+4(2) && '-' && iv_date+6(2). ENDIF. ENDMETHOD. METHOD format_time. IF iv_time IS NOT INITIAL. rv_str = iv_time+0(2) && ':' && iv_time+2(2) && ':' && iv_time+4(2). ENDIF. ENDMETHOD. METHOD format_timestamp. CONSTANTS lc_utc TYPE c LENGTH 6 VALUE 'UTC'. DATA lv_date TYPE d. DATA lv_time TYPE t. CONVERT TIME STAMP iv_ts TIME ZONE lc_utc INTO DATE lv_date TIME lv_time. rv_str = lv_date+0(4) && '-' && lv_date+4(2) && '-' && lv_date+6(2) && 'T' && lv_time+0(2) && ':' && lv_time+2(2) && ':' && lv_time+4(2) && 'Z'. ENDMETHOD. METHOD format_timestampl. CONSTANTS lc_utc TYPE c LENGTH 6 VALUE 'UTC'. DATA lv_date TYPE d. DATA lv_time TYPE t. DATA lv_frac TYPE string. DATA lv_int TYPE string. CONVERT TIME STAMP iv_ts TIME ZONE lc_utc INTO DATE lv_date TIME lv_time. SPLIT |{ iv_ts }| AT '.' INTO lv_int lv_frac. SHIFT lv_frac RIGHT DELETING TRAILING '0'. SHIFT lv_frac LEFT DELETING LEADING space. IF lv_frac IS INITIAL. lv_frac = '0'. ENDIF. rv_str = lv_date+0(4) && '-' && lv_date+4(2) && '-' && lv_date+6(2) && 'T' && lv_time+0(2) && ':' && lv_time+2(2) && ':' && lv_time+4(2) && '.' && lv_frac && 'Z'. ENDMETHOD. METHOD convert_value. DATA ls_node LIKE LINE OF ct_nodes. DATA lv_timestamp TYPE string. ls_node-path = is_prefix-path. ls_node-name = is_prefix-name. ls_node-index = iv_index. ls_node-order = iv_item_order. IF ls_node-name IS INITIAL. ls_node-name = is_prefix-name. ENDIF. IF io_type->absolute_name = '\TYPE-POOL=ABAP\TYPE=ABAP_BOOL' OR io_type->absolute_name = '\TYPE=ABAP_BOOLEAN' OR io_type->absolute_name = '\TYPE=XSDBOOLEAN' OR io_type->absolute_name = '\TYPE=FLAG' OR io_type->absolute_name = '\TYPE=XFELD'. ls_node-type = /apmg/if_apm_ajson_types=>node_type-boolean. IF iv_data IS NOT INITIAL. ls_node-value = 'true'. ELSE. ls_node-value = 'false'. ENDIF. ELSEIF io_type->absolute_name = '\TYPE=TIMESTAMP'. IF mv_format_datetime = abap_true. ls_node-type = /apmg/if_apm_ajson_types=>node_type-string. ls_node-value = format_timestamp( iv_data ). ELSE. ls_node-type = /apmg/if_apm_ajson_types=>node_type-number. ls_node-value = |{ iv_data }|. ENDIF. ELSEIF io_type->absolute_name = '\TYPE=TIMESTAMPL'. IF mv_format_datetime = abap_true. ls_node-type = /apmg/if_apm_ajson_types=>node_type-string. ls_node-value = format_timestampl( iv_data ). ELSE. ls_node-type = /apmg/if_apm_ajson_types=>node_type-number. ls_node-value = |{ iv_data }|. ENDIF. ELSEIF io_type->type_kind = lif_kind=>utclong. lv_timestamp = replace( val = iv_data sub = ` ` with = `T` ) && 'Z'. ls_node-type = /apmg/if_apm_ajson_types=>node_type-string. ls_node-value = lv_timestamp. ELSEIF io_type->type_kind CO lif_kind=>texts OR io_type->type_kind CO lif_kind=>binary OR io_type->type_kind CO lif_kind=>enum. ls_node-type = /apmg/if_apm_ajson_types=>node_type-string. ls_node-value = |{ iv_data }|. ELSEIF io_type->type_kind = lif_kind=>date. ls_node-type = /apmg/if_apm_ajson_types=>node_type-string. IF mv_format_datetime = abap_true. ls_node-value = format_date( iv_data ). ELSE. ls_node-value = |{ iv_data }|. ENDIF. ELSEIF io_type->type_kind = lif_kind=>time. ls_node-type = /apmg/if_apm_ajson_types=>node_type-string. IF mv_format_datetime = abap_true. ls_node-value = format_time( iv_data ). ELSE. ls_node-value = |{ iv_data }|. ENDIF. ELSEIF io_type->type_kind CO lif_kind=>numeric. ls_node-type = /apmg/if_apm_ajson_types=>node_type-number. ls_node-value = |{ iv_data }|. ELSE. /apmg/cx_apm_ajson_error=>raise( |Unexpected elementary type [{ io_type->type_kind }] @{ is_prefix-path && is_prefix-name }| ). ENDIF. APPEND ls_node TO ct_nodes. ENDMETHOD. METHOD convert_ref. DATA ls_node LIKE LINE OF ct_nodes. DATA lo_type TYPE REF TO cl_abap_typedescr. FIELD-SYMBOLS TYPE any. ls_node-path = is_prefix-path. ls_node-name = is_prefix-name. ls_node-index = iv_index. ls_node-order = iv_item_order. IF mi_custom_mapping IS BOUND. ls_node-name = mi_custom_mapping->to_json( iv_path = is_prefix-path iv_name = is_prefix-name ). ENDIF. IF ls_node-name IS INITIAL. ls_node-name = is_prefix-name. ENDIF. IF iv_data IS INITIAL. ls_node-type = /apmg/if_apm_ajson_types=>node_type-null. ls_node-value = 'null'. APPEND ls_node TO ct_nodes. ELSE. ASSIGN iv_data->* TO . lo_type = cl_abap_typedescr=>describe_by_data( ). convert_any( EXPORTING iv_data = io_type = lo_type is_prefix = is_prefix iv_index = iv_index iv_item_order = iv_item_order CHANGING ct_nodes = ct_nodes ). ENDIF. ENDMETHOD. METHOD convert_struc. DATA lo_struc TYPE REF TO cl_abap_structdescr. DATA lt_comps TYPE cl_abap_structdescr=>included_view. DATA ls_next_prefix LIKE is_prefix. DATA lv_mapping_prefix_name LIKE is_prefix-name. DATA lv_item_order TYPE i. DATA ls_root LIKE LINE OF ct_nodes. FIELD-SYMBOLS LIKE ls_root. FIELD-SYMBOLS LIKE LINE OF lt_comps. FIELD-SYMBOLS TYPE any. " Object root ls_root-path = is_prefix-path. ls_root-name = is_prefix-name. ls_root-type = /apmg/if_apm_ajson_types=>node_type-object. ls_root-index = iv_index. IF mi_custom_mapping IS BOUND. ls_root-name = mi_custom_mapping->to_json( iv_path = is_prefix-path iv_name = is_prefix-name ). ENDIF. IF ls_root-name IS INITIAL. ls_root-name = is_prefix-name. ENDIF. ls_root-order = iv_item_order. APPEND ls_root TO ct_nodes ASSIGNING . " Object attributes lo_struc ?= io_type. lt_comps = lo_struc->get_included_view( ). " replaced call to get_components() with get_included_view() to avoid problems with suffixes in includes. " get_components is potentially much slower than lo_struc->components " but ! we still need it to identify booleans " and rtti seems to cache type descriptions really well (https://github.com/sbcgua/benchmarks.git) " the structures will be repeated in real life ls_next_prefix-path = is_prefix-path && -name && '/'. LOOP AT lt_comps ASSIGNING . CLEAR lv_mapping_prefix_name. -children = -children + 1. ls_next_prefix-name = to_lower( -name ). ASSIGN COMPONENT -name OF STRUCTURE iv_data TO . ASSERT sy-subrc = 0. IF mi_custom_mapping IS BOUND AND -type->kind = cl_abap_typedescr=>kind_elem. lv_mapping_prefix_name = mi_custom_mapping->to_json( iv_path = ls_next_prefix-path iv_name = ls_next_prefix-name ). ENDIF. IF lv_mapping_prefix_name IS NOT INITIAL. ls_next_prefix-name = lv_mapping_prefix_name. ENDIF. IF mv_keep_item_order = abap_true. lv_item_order = -children. ENDIF. convert_any( EXPORTING iv_data = io_type = -type is_prefix = ls_next_prefix iv_item_order = lv_item_order CHANGING ct_nodes = ct_nodes ). ENDLOOP. ENDMETHOD. METHOD convert_table. DATA lo_table TYPE REF TO cl_abap_tabledescr. DATA lo_ltype TYPE REF TO cl_abap_typedescr. DATA ls_next_prefix LIKE is_prefix. DATA lv_tabix TYPE sy-tabix. DATA ls_root LIKE LINE OF ct_nodes. FIELD-SYMBOLS LIKE ls_root. FIELD-SYMBOLS TYPE ANY TABLE. FIELD-SYMBOLS TYPE any. " Array root ls_root-path = is_prefix-path. ls_root-name = is_prefix-name. ls_root-type = /apmg/if_apm_ajson_types=>node_type-array. ls_root-index = iv_index. ls_root-order = iv_item_order. IF mi_custom_mapping IS BOUND. ls_root-name = mi_custom_mapping->to_json( iv_path = is_prefix-path iv_name = is_prefix-name ). ENDIF. IF ls_root-name IS INITIAL. ls_root-name = is_prefix-name. ENDIF. APPEND ls_root TO ct_nodes ASSIGNING . " Array items lo_table ?= io_type. lo_ltype = lo_table->get_table_line_type( ). ls_next_prefix-path = is_prefix-path && -name && '/'. ASSIGN iv_data TO . lv_tabix = 1. LOOP AT ASSIGNING . ls_next_prefix-name = to_lower( |{ lv_tabix }| ). convert_any( EXPORTING iv_data = io_type = lo_ltype is_prefix = ls_next_prefix iv_index = -children + 1 CHANGING ct_nodes = ct_nodes ). -children = -children + 1. lv_tabix = lv_tabix + 1. ENDLOOP. ENDMETHOD. METHOD insert_with_type. DATA lo_type TYPE REF TO cl_abap_typedescr. DATA lo_converter TYPE REF TO lcl_abap_to_json. lo_type = cl_abap_typedescr=>describe_by_data( iv_data ). CREATE OBJECT lo_converter. lo_converter->mi_custom_mapping = ii_custom_mapping. lo_converter->mv_keep_item_order = is_opts-keep_item_order. lo_converter->mv_format_datetime = is_opts-format_datetime. lo_converter->insert_value_with_type( EXPORTING iv_data = iv_data iv_type = iv_type io_type = lo_type is_prefix = is_prefix iv_index = iv_array_index iv_item_order = iv_item_order CHANGING ct_nodes = rt_nodes ). ENDMETHOD. METHOD insert_value_with_type. DATA lv_prefix TYPE string. DATA ls_node LIKE LINE OF ct_nodes. lv_prefix = is_prefix-path && is_prefix-name. IF io_type->type_kind CO lif_kind=>texts OR io_type->type_kind CO lif_kind=>date OR io_type->type_kind CO lif_kind=>time. IF iv_type = /apmg/if_apm_ajson_types=>node_type-boolean AND iv_data <> 'true' AND iv_data <> 'false'. /apmg/cx_apm_ajson_error=>raise( |Unexpected boolean value [{ iv_data }] @{ lv_prefix }| ). ELSEIF iv_type = /apmg/if_apm_ajson_types=>node_type-null AND iv_data IS NOT INITIAL. /apmg/cx_apm_ajson_error=>raise( |Unexpected null value [{ iv_data }] @{ lv_prefix }| ). ELSEIF iv_type = /apmg/if_apm_ajson_types=>node_type-number AND iv_data CN '0123456789. E+-'. /apmg/cx_apm_ajson_error=>raise( |Unexpected numeric value [{ iv_data }] @{ lv_prefix }| ). ELSEIF iv_type <> /apmg/if_apm_ajson_types=>node_type-string AND iv_type <> /apmg/if_apm_ajson_types=>node_type-boolean AND iv_type <> /apmg/if_apm_ajson_types=>node_type-null AND iv_type <> /apmg/if_apm_ajson_types=>node_type-number. /apmg/cx_apm_ajson_error=>raise( |Unexpected type for value [{ iv_type },{ iv_data }] @{ lv_prefix }| ). ENDIF. ELSEIF io_type->type_kind CO lif_kind=>numeric. IF iv_type <> /apmg/if_apm_ajson_types=>node_type-number. /apmg/cx_apm_ajson_error=>raise( |Unexpected value for numeric [{ iv_data }] @{ lv_prefix }| ). ENDIF. ELSE. /apmg/cx_apm_ajson_error=>raise( |Unexpected type [{ io_type->type_kind }] @{ lv_prefix }| ). ENDIF. ls_node-path = is_prefix-path. ls_node-name = is_prefix-name. ls_node-index = iv_index. ls_node-value = iv_data. ls_node-type = iv_type. ls_node-order = iv_item_order. IF mi_custom_mapping IS BOUND. ls_node-name = mi_custom_mapping->to_json( iv_path = is_prefix-path iv_name = is_prefix-name ). ENDIF. IF ls_node-name IS INITIAL. ls_node-name = is_prefix-name. ENDIF. APPEND ls_node TO ct_nodes. ENDMETHOD. ENDCLASS. ********************************************************************** * MUTATOR INTERFACE ********************************************************************** INTERFACE lif_mutator_runner. METHODS run IMPORTING it_source_tree TYPE /apmg/if_apm_ajson_types=>ty_nodes_ts EXPORTING et_dest_tree TYPE /apmg/if_apm_ajson_types=>ty_nodes_ts RAISING /apmg/cx_apm_ajson_error. ENDINTERFACE. ********************************************************************** * FILTER RUNNER ********************************************************************** CLASS lcl_filter_runner DEFINITION FINAL. PUBLIC SECTION. INTERFACES lif_mutator_runner. CLASS-METHODS new IMPORTING ii_filter TYPE REF TO /apmg/if_apm_ajson_filter RETURNING VALUE(ro_instance) TYPE REF TO lcl_filter_runner. METHODS constructor IMPORTING ii_filter TYPE REF TO /apmg/if_apm_ajson_filter. PRIVATE SECTION. DATA mi_filter TYPE REF TO /apmg/if_apm_ajson_filter. DATA mr_source_tree TYPE REF TO /apmg/if_apm_ajson_types=>ty_nodes_ts. DATA mr_dest_tree TYPE REF TO /apmg/if_apm_ajson_types=>ty_nodes_ts. METHODS walk IMPORTING iv_path TYPE string CHANGING cs_parent TYPE /apmg/if_apm_ajson_types=>ty_node OPTIONAL RAISING /apmg/cx_apm_ajson_error. ENDCLASS. CLASS lcl_filter_runner IMPLEMENTATION. METHOD new. CREATE OBJECT ro_instance EXPORTING ii_filter = ii_filter. ENDMETHOD. METHOD constructor. ASSERT ii_filter IS BOUND. mi_filter = ii_filter. ENDMETHOD. METHOD lif_mutator_runner~run. CLEAR et_dest_tree. GET REFERENCE OF it_source_tree INTO mr_source_tree. GET REFERENCE OF et_dest_tree INTO mr_dest_tree. walk( iv_path = '' ). ENDMETHOD. METHOD walk. DATA ls_node TYPE /apmg/if_apm_ajson_types=>ty_node. DATA lv_tab_key TYPE string. IF cs_parent-type = /apmg/if_apm_ajson_types=>node_type-array. lv_tab_key = 'array_index'. " path + index ENDIF. LOOP AT mr_source_tree->* INTO ls_node USING KEY (lv_tab_key) WHERE path = iv_path. CASE ls_node-type. WHEN /apmg/if_apm_ajson_types=>node_type-boolean OR /apmg/if_apm_ajson_types=>node_type-null OR /apmg/if_apm_ajson_types=>node_type-number OR /apmg/if_apm_ajson_types=>node_type-string. IF mi_filter->keep_node( ls_node ) = abap_false. CONTINUE. ENDIF. WHEN /apmg/if_apm_ajson_types=>node_type-array OR /apmg/if_apm_ajson_types=>node_type-object. IF mi_filter->keep_node( is_node = ls_node iv_visit = /apmg/if_apm_ajson_filter=>visit_type-open ) = abap_false. CONTINUE. ENDIF. " Intentionally clear AFTER "open" CLEAR ls_node-children. walk( EXPORTING iv_path = iv_path && ls_node-name && `/` CHANGING cs_parent = ls_node ). IF mi_filter->keep_node( is_node = ls_node iv_visit = /apmg/if_apm_ajson_filter=>visit_type-close ) = abap_false. CONTINUE. ENDIF. WHEN OTHERS. /apmg/cx_apm_ajson_error=>raise( |Unexpected node type { ls_node-type }| ). ENDCASE. IF cs_parent IS SUPPLIED. cs_parent-children = cs_parent-children + 1. IF cs_parent-type = /apmg/if_apm_ajson_types=>node_type-array. ls_node-name = |{ cs_parent-children }|. ls_node-index = cs_parent-children. ENDIF. ENDIF. INSERT ls_node INTO TABLE mr_dest_tree->*. ENDLOOP. ENDMETHOD. ENDCLASS. ********************************************************************** * MAPPER RUNNER ********************************************************************** CLASS lcl_mapper_runner DEFINITION FINAL. PUBLIC SECTION. INTERFACES lif_mutator_runner. CLASS-METHODS new IMPORTING ii_mapper TYPE REF TO /apmg/if_apm_ajson_mapping RETURNING VALUE(ro_instance) TYPE REF TO lcl_mapper_runner. METHODS constructor IMPORTING ii_mapper TYPE REF TO /apmg/if_apm_ajson_mapping. PRIVATE SECTION. DATA mi_mapper TYPE REF TO /apmg/if_apm_ajson_mapping. DATA mr_source_tree TYPE REF TO /apmg/if_apm_ajson_types=>ty_nodes_ts. DATA mr_dest_tree TYPE REF TO /apmg/if_apm_ajson_types=>ty_nodes_ts. METHODS process_deep_node IMPORTING iv_path TYPE string iv_renamed_path TYPE string iv_node_type TYPE /apmg/if_apm_ajson_types=>ty_node-type RAISING /apmg/cx_apm_ajson_error. ENDCLASS. CLASS lcl_mapper_runner IMPLEMENTATION. METHOD new. CREATE OBJECT ro_instance EXPORTING ii_mapper = ii_mapper. ENDMETHOD. METHOD constructor. ASSERT ii_mapper IS BOUND. mi_mapper = ii_mapper. ENDMETHOD. METHOD lif_mutator_runner~run. FIELD-SYMBOLS LIKE LINE OF it_source_tree. READ TABLE it_source_tree WITH KEY path = `` name = `` ASSIGNING . IF sy-subrc <> 0 OR NOT ( -type = /apmg/if_apm_ajson_types=>node_type-array OR -type = /apmg/if_apm_ajson_types=>node_type-object ). " empty or one-value-only tree et_dest_tree = it_source_tree. RETURN. ENDIF. CLEAR et_dest_tree. GET REFERENCE OF it_source_tree INTO mr_source_tree. GET REFERENCE OF et_dest_tree INTO mr_dest_tree. INSERT INTO TABLE et_dest_tree. process_deep_node( iv_path = `/` iv_renamed_path = `/` iv_node_type = -type ). ENDMETHOD. METHOD process_deep_node. FIELD-SYMBOLS LIKE LINE OF mr_source_tree->*. DATA ls_renamed_node LIKE . LOOP AT mr_source_tree->* ASSIGNING WHERE path = iv_path. ls_renamed_node = . IF iv_node_type <> /apmg/if_apm_ajson_types=>node_type-array. " don't rename array item names -> they are numeric index mi_mapper->rename_node( EXPORTING is_node = CHANGING cv_name = ls_renamed_node-name ). IF ls_renamed_node-name IS INITIAL. /apmg/cx_apm_ajson_error=>raise( iv_msg = 'Renamed node name cannot be empty' is_node = ). ENDIF. ENDIF. ls_renamed_node-path = iv_renamed_path. INSERT ls_renamed_node INTO TABLE mr_dest_tree->*. IF sy-subrc <> 0. " = 4 ? /apmg/cx_apm_ajson_error=>raise( iv_msg = 'Renamed node has a duplicate' is_node = ls_renamed_node ). ENDIF. " maybe also catch CX_SY_ITAB_DUPLICATE_KEY but secondary keys are not changed here, so not for now IF -type = /apmg/if_apm_ajson_types=>node_type-array OR -type = /apmg/if_apm_ajson_types=>node_type-object. process_deep_node( iv_path = iv_path && -name && `/` iv_renamed_path = iv_renamed_path && ls_renamed_node-name && `/` iv_node_type = -type ). ENDIF. ENDLOOP. ENDMETHOD. ENDCLASS. ********************************************************************** * MUTATOR QUEUE ********************************************************************** CLASS lcl_mutator_queue DEFINITION FINAL. PUBLIC SECTION. INTERFACES lif_mutator_runner. CLASS-METHODS new RETURNING VALUE(ro_instance) TYPE REF TO lcl_mutator_queue. METHODS add IMPORTING ii_mutator TYPE REF TO lif_mutator_runner RETURNING VALUE(ro_self) TYPE REF TO lcl_mutator_queue. PRIVATE SECTION. DATA mt_queue TYPE STANDARD TABLE OF REF TO lif_mutator_runner. ENDCLASS. CLASS lcl_mutator_queue IMPLEMENTATION. METHOD add. IF ii_mutator IS BOUND. APPEND ii_mutator TO mt_queue. ENDIF. ro_self = me. ENDMETHOD. METHOD new. CREATE OBJECT ro_instance. ENDMETHOD. METHOD lif_mutator_runner~run. DATA li_mutator TYPE REF TO lif_mutator_runner. DATA lv_qsize TYPE i. FIELD-SYMBOLS LIKE it_source_tree. FIELD-SYMBOLS LIKE it_source_tree. DATA lr_buf TYPE REF TO /apmg/if_apm_ajson_types=>ty_nodes_ts. lv_qsize = lines( mt_queue ). IF lv_qsize = 0. et_dest_tree = it_source_tree. RETURN. ENDIF. LOOP AT mt_queue INTO li_mutator. IF sy-tabix = 1. ASSIGN it_source_tree TO . ELSE. ASSIGN lr_buf->* TO . ENDIF. IF sy-tabix = lv_qsize. ASSIGN et_dest_tree TO . ELSE. CREATE DATA lr_buf. ASSIGN lr_buf->* TO . ENDIF. li_mutator->run( EXPORTING it_source_tree = IMPORTING et_dest_tree = ). ENDLOOP. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_ajson IMPLEMENTATION. METHOD constructor. ms_opts-keep_item_order = iv_keep_item_order. ms_opts-to_abap_corresponding_only = iv_to_abap_corresponding_only. format_datetime( iv_format_datetime ). ENDMETHOD. METHOD create_empty. CREATE OBJECT ro_instance EXPORTING iv_to_abap_corresponding_only = iv_to_abap_corresponding_only iv_format_datetime = iv_format_datetime iv_keep_item_order = iv_keep_item_order. ro_instance->mi_custom_mapping = ii_custom_mapping. ENDMETHOD. METHOD create_from. DATA lo_mutator_queue TYPE REF TO lcl_mutator_queue. IF ii_source_json IS NOT BOUND. /apmg/cx_apm_ajson_error=>raise( 'Source not bound' ). ENDIF. CREATE OBJECT ro_instance EXPORTING iv_to_abap_corresponding_only = ii_source_json->opts( )-to_abap_corresponding_only iv_format_datetime = ii_source_json->opts( )-format_datetime iv_keep_item_order = ii_source_json->opts( )-keep_item_order. IF ii_filter IS NOT BOUND AND ii_mapper IS NOT BOUND. ro_instance->mt_json_tree = ii_source_json->mt_json_tree. ELSE. CREATE OBJECT lo_mutator_queue. IF ii_mapper IS BOUND. " Mapping goes first. But maybe it should be a freely definable queue of processors ? lo_mutator_queue->add( lcl_mapper_runner=>new( ii_mapper ) ). ENDIF. IF ii_filter IS BOUND. lo_mutator_queue->add( lcl_filter_runner=>new( ii_filter ) ). ENDIF. lo_mutator_queue->lif_mutator_runner~run( EXPORTING it_source_tree = ii_source_json->mt_json_tree IMPORTING et_dest_tree = ro_instance->mt_json_tree ). ENDIF. ENDMETHOD. METHOD delete_subtree. DATA lv_parent_path TYPE string. DATA lr_parent LIKE ir_parent. READ TABLE mt_json_tree INTO rs_top_node WITH TABLE KEY path = iv_path name = iv_name. IF sy-subrc <> 0. RETURN. " Not found ? nothing to delete ! ENDIF. DELETE mt_json_tree INDEX sy-tabix. "#EC CI_SORTSEQ where path = iv_path and name = iv_name. IF rs_top_node-children > 0. " only for objects and arrays lv_parent_path = iv_path && iv_name && '/*'. DELETE mt_json_tree WHERE path CP lv_parent_path. ENDIF. " decrement parent children IF ir_parent IS SUPPLIED. ir_parent->children = ir_parent->children - 1. ELSE. lr_parent = get_item( iv_path ). IF lr_parent IS NOT INITIAL. lr_parent->children = lr_parent->children - 1. ENDIF. ENDIF. ENDMETHOD. METHOD get_item. DATA ls_path_name TYPE /apmg/if_apm_ajson_types=>ty_path_name. ls_path_name = lcl_utils=>split_path( iv_path ). READ TABLE mt_json_tree REFERENCE INTO rv_item WITH KEY path = ls_path_name-path name = ls_path_name-name. ENDMETHOD. METHOD new. CREATE OBJECT ro_instance EXPORTING iv_to_abap_corresponding_only = iv_to_abap_corresponding_only iv_format_datetime = iv_format_datetime iv_keep_item_order = iv_keep_item_order. ENDMETHOD. METHOD normalize_path. rv_path = lcl_utils=>normalize_path( iv_path ). ENDMETHOD. METHOD parse. DATA lo_parser TYPE REF TO lcl_json_parser. CREATE OBJECT ro_instance. CREATE OBJECT lo_parser. ro_instance->mt_json_tree = lo_parser->parse( iv_json = iv_json iv_keep_item_order = iv_keep_item_order ). ro_instance->mi_custom_mapping = ii_custom_mapping. ro_instance->ms_opts-keep_item_order = iv_keep_item_order. IF iv_freeze = abap_true. ro_instance->freeze( ). ENDIF. ENDMETHOD. METHOD prove_path_exists. DATA lt_path TYPE string_table. DATA lr_node_parent LIKE rr_end_node. DATA lv_cur_path TYPE string. DATA lv_cur_name TYPE string. DATA ls_new_node LIKE LINE OF mt_json_tree. SPLIT iv_path AT '/' INTO TABLE lt_path. DELETE lt_path WHERE table_line IS INITIAL. DO. lr_node_parent = rr_end_node. READ TABLE mt_json_tree REFERENCE INTO rr_end_node WITH TABLE KEY path = lv_cur_path name = lv_cur_name. IF sy-subrc <> 0. " New node, assume it is always object as it has a named child, use touch_array to init array CLEAR ls_new_node. IF lr_node_parent IS NOT INITIAL. " if has parent lr_node_parent->children = lr_node_parent->children + 1. IF lr_node_parent->type = /apmg/if_apm_ajson_types=>node_type-array. ls_new_node-index = lcl_utils=>validate_array_index( iv_path = lv_cur_path iv_index = lv_cur_name ). ENDIF. ENDIF. ls_new_node-path = lv_cur_path. ls_new_node-name = lv_cur_name. ls_new_node-type = /apmg/if_apm_ajson_types=>node_type-object. INSERT ls_new_node INTO TABLE mt_json_tree REFERENCE INTO rr_end_node. ENDIF. lv_cur_path = lv_cur_path && lv_cur_name && '/'. READ TABLE lt_path INDEX sy-index INTO lv_cur_name. IF sy-subrc <> 0. EXIT. " no more segments ENDIF. ENDDO. ENDMETHOD. METHOD read_only_watchdog. IF ms_opts-read_only = abap_true. /apmg/cx_apm_ajson_error=>raise( 'This json instance is read only' ). ENDIF. ENDMETHOD. METHOD /apmg/if_apm_ajson~array_to_string_table. DATA lv_normalized_path TYPE string. DATA lr_node TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. FIELD-SYMBOLS LIKE LINE OF mt_json_tree. lv_normalized_path = lcl_utils=>normalize_path( iv_path ). lr_node = get_item( iv_path ). IF lr_node IS INITIAL. /apmg/cx_apm_ajson_error=>raise( |Path not found: { iv_path }| ). ENDIF. IF lr_node->type <> /apmg/if_apm_ajson_types=>node_type-array. /apmg/cx_apm_ajson_error=>raise( |Array expected at: { iv_path }| ). ENDIF. LOOP AT mt_json_tree ASSIGNING WHERE path = lv_normalized_path. CASE -type. WHEN /apmg/if_apm_ajson_types=>node_type-number OR /apmg/if_apm_ajson_types=>node_type-string. APPEND -value TO rt_string_table. WHEN /apmg/if_apm_ajson_types=>node_type-null. APPEND '' TO rt_string_table. WHEN /apmg/if_apm_ajson_types=>node_type-boolean. DATA lv_tmp TYPE string. IF -value = 'true'. lv_tmp = abap_true. ELSE. CLEAR lv_tmp. ENDIF. APPEND lv_tmp TO rt_string_table. WHEN OTHERS. /apmg/cx_apm_ajson_error=>raise( |Cannot convert [{ -type }] to string at [{ -path }{ -name }]| ). ENDCASE. ENDLOOP. ENDMETHOD. METHOD /apmg/if_apm_ajson~clear. read_only_watchdog( ). CLEAR mt_json_tree. ENDMETHOD. METHOD /apmg/if_apm_ajson~clone. ri_json = create_from( me ). ENDMETHOD. METHOD /apmg/if_apm_ajson~delete. read_only_watchdog( ). DATA ls_split_path TYPE /apmg/if_apm_ajson_types=>ty_path_name. ls_split_path = lcl_utils=>split_path( iv_path ). delete_subtree( iv_path = ls_split_path-path iv_name = ls_split_path-name ). ri_json = me. ENDMETHOD. METHOD /apmg/if_apm_ajson~exists. rv_exists = boolc( get_item( iv_path ) IS NOT INITIAL ). ENDMETHOD. METHOD /apmg/if_apm_ajson~filter. ri_json = create_from( ii_source_json = me ii_filter = ii_filter ). ENDMETHOD. METHOD /apmg/if_apm_ajson~format_datetime. ms_opts-format_datetime = iv_use_iso. ri_json = me. ENDMETHOD. METHOD /apmg/if_apm_ajson~freeze. ms_opts-read_only = abap_true. ENDMETHOD. METHOD /apmg/if_apm_ajson~get. DATA lr_item TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. lr_item = get_item( iv_path ). IF lr_item IS NOT INITIAL. rv_value = lr_item->value. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_ajson~get_boolean. DATA lr_item TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. lr_item = get_item( iv_path ). IF lr_item IS INITIAL OR lr_item->type = /apmg/if_apm_ajson_types=>node_type-null. RETURN. ELSEIF lr_item->type = /apmg/if_apm_ajson_types=>node_type-boolean. rv_value = boolc( lr_item->value = 'true' ). ELSEIF lr_item->value IS NOT INITIAL. rv_value = abap_true. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_ajson~get_date. DATA lr_item TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. DATA lv_y TYPE c LENGTH 4. DATA lv_m TYPE c LENGTH 2. DATA lv_d TYPE c LENGTH 2. lr_item = get_item( iv_path ). IF lr_item IS NOT INITIAL AND lr_item->type = /apmg/if_apm_ajson_types=>node_type-string. FIND FIRST OCCURRENCE OF REGEX '^(\d{4})-(\d{2})-(\d{2})(T|$)' "#EC NOTEXT IN lr_item->value SUBMATCHES lv_y lv_m lv_d ##REGEX_POSIX. CONCATENATE lv_y lv_m lv_d INTO rv_value. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_ajson~get_integer. DATA lr_item TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. lr_item = get_item( iv_path ). IF lr_item IS NOT INITIAL AND lr_item->type = /apmg/if_apm_ajson_types=>node_type-number. rv_value = lr_item->value. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_ajson~get_node_type. DATA lr_item TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. lr_item = get_item( iv_path ). IF lr_item IS NOT INITIAL. rv_node_type = lr_item->type. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_ajson~get_number. DATA lr_item TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. lr_item = get_item( iv_path ). IF lr_item IS NOT INITIAL AND lr_item->type = /apmg/if_apm_ajson_types=>node_type-number. rv_value = lr_item->value. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_ajson~get_string. DATA lr_item TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. lr_item = get_item( iv_path ). IF lr_item IS NOT INITIAL AND lr_item->type <> /apmg/if_apm_ajson_types=>node_type-null. rv_value = lr_item->value. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_ajson~get_timestamp. DATA lo_to_abap TYPE REF TO lcl_json_to_abap. DATA lr_item TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. lr_item = get_item( iv_path ). IF lr_item IS INITIAL. RETURN. ENDIF. CREATE OBJECT lo_to_abap. TRY. rv_value = lo_to_abap->to_timestamp( lr_item->value ). CATCH /apmg/cx_apm_ajson_error. RETURN. ENDTRY. ENDMETHOD. METHOD /apmg/if_apm_ajson~get_timestampl. DATA lo_to_abap TYPE REF TO lcl_json_to_abap. DATA lr_item TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. lr_item = get_item( iv_path ). IF lr_item IS INITIAL. RETURN. ENDIF. CREATE OBJECT lo_to_abap. TRY. rv_value = lo_to_abap->to_timestampl( lr_item->value ). CATCH /apmg/cx_apm_ajson_error. RETURN. ENDTRY. ENDMETHOD. METHOD /apmg/if_apm_ajson~is_empty. rv_yes = boolc( lines( mt_json_tree ) = 0 ). ENDMETHOD. METHOD /apmg/if_apm_ajson~keep_item_order. ms_opts-keep_item_order = abap_true. ri_json = me. ENDMETHOD. METHOD /apmg/if_apm_ajson~map. ri_json = create_from( ii_source_json = me ii_mapper = ii_mapper ). ENDMETHOD. METHOD /apmg/if_apm_ajson~members. DATA lv_normalized_path TYPE string. FIELD-SYMBOLS LIKE LINE OF mt_json_tree. lv_normalized_path = lcl_utils=>normalize_path( iv_path ). LOOP AT mt_json_tree ASSIGNING WHERE path = lv_normalized_path. APPEND -name TO rt_members. ENDLOOP. ENDMETHOD. METHOD /apmg/if_apm_ajson~opts. rs_opts = ms_opts. ENDMETHOD. METHOD /apmg/if_apm_ajson~push. DATA lr_parent TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. DATA lr_new_node TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. read_only_watchdog( ). lr_parent = get_item( iv_path ). IF lr_parent IS INITIAL. /apmg/cx_apm_ajson_error=>raise( |Path [{ iv_path }] does not exist| ). ENDIF. IF lr_parent->type <> /apmg/if_apm_ajson_types=>node_type-array. /apmg/cx_apm_ajson_error=>raise( |Path [{ iv_path }] is not array| ). ENDIF. DATA lt_new_nodes TYPE /apmg/if_apm_ajson_types=>ty_nodes_tt. DATA ls_new_path TYPE /apmg/if_apm_ajson_types=>ty_path_name. DATA lv_new_index TYPE i. lv_new_index = lr_parent->children + 1. ls_new_path-path = lcl_utils=>normalize_path( iv_path ). ls_new_path-name = |{ lv_new_index }|. lt_new_nodes = lcl_abap_to_json=>convert( is_opts = ms_opts iv_data = iv_val is_prefix = ls_new_path ). READ TABLE lt_new_nodes INDEX 1 REFERENCE INTO lr_new_node. " assume first record is the array item - not ideal ! ASSERT sy-subrc = 0. lr_new_node->index = lv_new_index. " update data lr_parent->children = lv_new_index. INSERT LINES OF lt_new_nodes INTO TABLE mt_json_tree. ri_json = me. ENDMETHOD. METHOD /apmg/if_apm_ajson~set. DATA ls_split_path TYPE /apmg/if_apm_ajson_types=>ty_path_name. DATA lr_parent TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. DATA ls_deleted_node TYPE /apmg/if_apm_ajson_types=>ty_node. DATA lv_item_order TYPE /apmg/if_apm_ajson_types=>ty_node-order. read_only_watchdog( ). ri_json = me. IF iv_val IS INITIAL AND iv_ignore_empty = abap_true AND iv_node_type IS INITIAL. RETURN. " nothing to assign ENDIF. IF iv_node_type IS NOT INITIAL AND iv_node_type <> /apmg/if_apm_ajson_types=>node_type-boolean AND iv_node_type <> /apmg/if_apm_ajson_types=>node_type-null AND iv_node_type <> /apmg/if_apm_ajson_types=>node_type-number AND iv_node_type <> /apmg/if_apm_ajson_types=>node_type-string. /apmg/cx_apm_ajson_error=>raise( |Unexpected type { iv_node_type }| ). ENDIF. ls_split_path = lcl_utils=>split_path( iv_path ). IF ls_split_path IS INITIAL. " Assign root, exceptional processing IF iv_node_type IS NOT INITIAL. mt_json_tree = lcl_abap_to_json=>insert_with_type( is_opts = ms_opts iv_data = iv_val iv_type = iv_node_type is_prefix = ls_split_path ii_custom_mapping = mi_custom_mapping ). ELSE. mt_json_tree = lcl_abap_to_json=>convert( is_opts = ms_opts iv_data = iv_val is_prefix = ls_split_path ii_custom_mapping = mi_custom_mapping ). ENDIF. RETURN. ENDIF. " Ensure whole path exists lr_parent = prove_path_exists( ls_split_path-path ). ASSERT lr_parent IS NOT INITIAL. " delete if exists with subtree ls_deleted_node = delete_subtree( ir_parent = lr_parent iv_path = ls_split_path-path iv_name = ls_split_path-name ). lv_item_order = ls_deleted_node-order. " convert to json DATA lt_new_nodes TYPE /apmg/if_apm_ajson_types=>ty_nodes_tt. DATA lv_array_index TYPE i. IF lr_parent->type = /apmg/if_apm_ajson_types=>node_type-array. lv_array_index = lcl_utils=>validate_array_index( iv_path = ls_split_path-path iv_index = ls_split_path-name ). ELSEIF lr_parent->type = /apmg/if_apm_ajson_types=>node_type-object AND lv_item_order = 0 AND ms_opts-keep_item_order = abap_true. lv_item_order = lr_parent->children + 1. ENDIF. IF iv_node_type IS NOT INITIAL. lt_new_nodes = lcl_abap_to_json=>insert_with_type( is_opts = ms_opts iv_item_order = lv_item_order iv_data = iv_val iv_type = iv_node_type iv_array_index = lv_array_index is_prefix = ls_split_path ii_custom_mapping = mi_custom_mapping ). ELSE. lt_new_nodes = lcl_abap_to_json=>convert( is_opts = ms_opts iv_item_order = lv_item_order iv_data = iv_val iv_array_index = lv_array_index is_prefix = ls_split_path ii_custom_mapping = mi_custom_mapping ). ENDIF. " update nodes IF lines( lt_new_nodes ) > 0. lr_parent->children = lr_parent->children + 1. INSERT LINES OF lt_new_nodes INTO TABLE mt_json_tree. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_ajson~setx. DATA lv_path TYPE string. DATA lv_val TYPE string. DATA lv_int TYPE i. DATA lv_dec TYPE decfloat34. DATA lv_last TYPE i. IF iv_param IS INITIAL. ri_json = me. RETURN. ENDIF. SPLIT iv_param AT ':' INTO lv_path lv_val. CONDENSE lv_path. CONDENSE lv_val. IF lv_val IS INITIAL. ri_json = me. RETURN. " Hmm ? or empty string ? or null ? ENDIF. IF go_float_regex IS NOT BOUND. CREATE OBJECT go_float_regex EXPORTING pattern = '^([1-9][0-9]*|0)\.[0-9]+$' ##REGEX_POSIX. " expects fractional, because ints are detected separately ENDIF. IF lv_val = 'null'. /apmg/if_apm_ajson~set_null( lv_path ). ELSEIF lv_val = 'true'. /apmg/if_apm_ajson~set_boolean( iv_path = lv_path iv_val = abap_true ). ELSEIF lv_val = 'false'. /apmg/if_apm_ajson~set_boolean( iv_path = lv_path iv_val = abap_false ). ELSEIF lv_val CO '0123456789'. lv_int = lv_val. /apmg/if_apm_ajson~set_integer( iv_path = lv_path iv_val = lv_int ). ELSEIF lv_val CO '0123456789.' AND go_float_regex->create_matcher( text = lv_val )->match( ) = abap_true. lv_dec = lv_val. /apmg/if_apm_ajson~set( iv_path = lv_path iv_val = lv_dec ). ELSEIF lv_val+0(1) = '{' OR lv_val+0(1) = '['. "Expect object/array, but no further checks, parser will catch errors /apmg/if_apm_ajson~set( iv_path = lv_path iv_val = parse( iv_json = lv_val iv_keep_item_order = ms_opts-keep_item_order ) ). ELSE. " string lv_last = strlen( lv_val ) - 1. IF lv_val+0(1) = '"' AND lv_val+lv_last(1) = '"'. lv_val = substring( val = lv_val off = 1 len = lv_last - 1 ). ENDIF. /apmg/if_apm_ajson~set_string( iv_path = lv_path iv_val = lv_val ). ENDIF. ri_json = me. ENDMETHOD. METHOD /apmg/if_apm_ajson~set_boolean. ri_json = me. DATA lv_bool TYPE abap_bool. lv_bool = boolc( iv_val IS NOT INITIAL ). /apmg/if_apm_ajson~set( iv_ignore_empty = abap_false iv_path = iv_path iv_val = lv_bool ). ENDMETHOD. METHOD /apmg/if_apm_ajson~set_date. ri_json = me. DATA lv_val TYPE string. lv_val = lcl_abap_to_json=>format_date( iv_val ). /apmg/if_apm_ajson~set( iv_ignore_empty = abap_false iv_path = iv_path iv_val = lv_val ). ENDMETHOD. METHOD /apmg/if_apm_ajson~set_integer. ri_json = me. /apmg/if_apm_ajson~set( iv_ignore_empty = abap_false iv_path = iv_path iv_val = iv_val ). ENDMETHOD. METHOD /apmg/if_apm_ajson~set_null. ri_json = me. DATA lv_null_ref TYPE REF TO data. /apmg/if_apm_ajson~set( iv_ignore_empty = abap_false iv_path = iv_path iv_val = lv_null_ref ). ENDMETHOD. METHOD /apmg/if_apm_ajson~set_string. ri_json = me. DATA lv_val TYPE string. lv_val = iv_val. /apmg/if_apm_ajson~set( iv_ignore_empty = abap_false iv_path = iv_path iv_val = lv_val ). ENDMETHOD. METHOD /apmg/if_apm_ajson~set_timestamp. ri_json = me. DATA lv_timestamp_iso TYPE string. lv_timestamp_iso = lcl_abap_to_json=>format_timestamp( iv_val ). /apmg/if_apm_ajson~set( iv_ignore_empty = abap_false iv_path = iv_path iv_val = lv_timestamp_iso ). ENDMETHOD. METHOD /apmg/if_apm_ajson~set_timestampl. ri_json = me. DATA lv_timestamp_iso TYPE string. lv_timestamp_iso = lcl_abap_to_json=>format_timestampl( iv_val ). /apmg/if_apm_ajson~set( iv_ignore_empty = abap_false iv_path = iv_path iv_val = lv_timestamp_iso ). ENDMETHOD. METHOD /apmg/if_apm_ajson~slice. " TODO: idea " read only mode (for read only jsons or a param) " which would reuse the original tree, so copy a reference of the tree, presuming that it is not changed " this will be faster, in particular for array iterations DATA lo_section TYPE REF TO /apmg/cl_apm_ajson. DATA ls_item LIKE LINE OF mt_json_tree. DATA lv_normalized_path TYPE string. DATA ls_path_parts TYPE /apmg/if_apm_ajson_types=>ty_path_name. DATA lv_path_len TYPE i. DATA lv_path_pattern TYPE string. CREATE OBJECT lo_section. lo_section->mi_custom_mapping = mi_custom_mapping. lv_normalized_path = lcl_utils=>normalize_path( iv_path ). lv_path_len = strlen( lv_normalized_path ). ls_path_parts = lcl_utils=>split_path( lv_normalized_path ). READ TABLE mt_json_tree INTO ls_item WITH KEY path = ls_path_parts-path name = ls_path_parts-name. IF sy-subrc <> 0. RETURN. ENDIF. CLEAR: ls_item-path, ls_item-name, ls_item-order. " this becomes a new root INSERT ls_item INTO TABLE lo_section->mt_json_tree. lv_path_pattern = lv_normalized_path && `*`. LOOP AT mt_json_tree INTO ls_item WHERE path CP lv_path_pattern. "#EC CI_SORTSEQ ls_item-path = substring( val = ls_item-path off = lv_path_len - 1 ). " less closing '/' INSERT ls_item INTO TABLE lo_section->mt_json_tree. ENDLOOP. ri_json = lo_section. ENDMETHOD. METHOD /apmg/if_apm_ajson~stringify. rv_json = lcl_json_serializer=>stringify( it_json_tree = mt_json_tree iv_keep_item_order = ms_opts-keep_item_order iv_indent = iv_indent ). ENDMETHOD. METHOD /apmg/if_apm_ajson~touch_array. DATA lr_node TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. DATA ls_deleted_node TYPE /apmg/if_apm_ajson_types=>ty_node. DATA ls_new_node LIKE LINE OF mt_json_tree. DATA ls_split_path TYPE /apmg/if_apm_ajson_types=>ty_path_name. read_only_watchdog( ). ls_split_path = lcl_utils=>split_path( iv_path ). IF ls_split_path IS INITIAL. " Assign root, exceptional processing ls_new_node-path = ls_split_path-path. ls_new_node-name = ls_split_path-name. ls_new_node-type = /apmg/if_apm_ajson_types=>node_type-array. INSERT ls_new_node INTO TABLE mt_json_tree. RETURN. ENDIF. IF iv_clear = abap_true. ls_deleted_node = delete_subtree( iv_path = ls_split_path-path iv_name = ls_split_path-name ). ELSE. lr_node = get_item( iv_path ). ENDIF. IF lr_node IS INITIAL. " Or node was cleared DATA lr_parent TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. lr_parent = prove_path_exists( ls_split_path-path ). ASSERT lr_parent IS NOT INITIAL. lr_parent->children = lr_parent->children + 1. ls_new_node-path = ls_split_path-path. ls_new_node-name = ls_split_path-name. ls_new_node-type = /apmg/if_apm_ajson_types=>node_type-array. IF ms_opts-keep_item_order = abap_true. IF ls_deleted_node IS NOT INITIAL. ls_new_node-order = ls_deleted_node-order. ELSE. ls_new_node-order = lr_parent->children. ENDIF. ENDIF. INSERT ls_new_node INTO TABLE mt_json_tree. ELSEIF lr_node->type <> /apmg/if_apm_ajson_types=>node_type-array. /apmg/cx_apm_ajson_error=>raise( |Path [{ iv_path }] already used and is not array| ). ENDIF. ri_json = me. ENDMETHOD. METHOD /apmg/if_apm_ajson~to_abap. DATA lo_to_abap TYPE REF TO lcl_json_to_abap. CLEAR ev_container. CREATE OBJECT lo_to_abap EXPORTING iv_corresponding = boolc( iv_corresponding = abap_true OR ms_opts-to_abap_corresponding_only = abap_true ) ii_custom_mapping = mi_custom_mapping ii_refs_initiator = ii_refs_initiator. lo_to_abap->to_abap( EXPORTING it_nodes = /apmg/if_apm_ajson~mt_json_tree CHANGING c_container = ev_container ). ENDMETHOD. METHOD /apmg/if_apm_ajson~to_abap_corresponding_only. ms_opts-to_abap_corresponding_only = iv_enable. ri_json = me. ENDMETHOD. ENDCLASS. " " MAPPINGS " CLASS lcl_to_camel_case_underscore DEFINITION. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_mapping. ENDCLASS. CLASS lcl_to_camel_case_underscore IMPLEMENTATION. METHOD /apmg/if_apm_ajson_mapping~rename_node. TYPES ty_token TYPE c LENGTH 255. DATA from TYPE i. DATA tokens TYPE STANDARD TABLE OF ty_token WITH KEY table_line. FIELD-SYMBOLS LIKE LINE OF tokens. from = 2. IF cv_name(1) = '_'. from = 3. ENDIF. SPLIT cv_name AT '_' INTO TABLE tokens. LOOP AT tokens ASSIGNING FROM from. TRANSLATE +0(1) TO UPPER CASE. ENDLOOP. CONCATENATE LINES OF tokens INTO cv_name. IF from = 3. cv_name = '_' && cv_name. ENDIF. TRANSLATE cv_name USING '/_:_~_*_'. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_abap. " deprecated ASSERT 0 = 0. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_json. " deprecated ASSERT 0 = 0. ENDMETHOD. ENDCLASS. CLASS lcl_from_camel_case_underscore DEFINITION. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_mapping. ENDCLASS. CLASS lcl_from_camel_case_underscore IMPLEMENTATION. METHOD /apmg/if_apm_ajson_mapping~rename_node. REPLACE ALL OCCURRENCES OF REGEX `([a-z])([A-Z])` IN cv_name WITH `$1_$2` ##REGEX_POSIX. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_abap. " deprecated ASSERT 0 = 0. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_json. " deprecated ASSERT 0 = 0. ENDMETHOD. ENDCLASS. " " FILTERS " CLASS lcl_empty_zero_null DEFINITION FINAL. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_filter. ENDCLASS. CLASS lcl_empty_zero_null IMPLEMENTATION. METHOD /apmg/if_apm_ajson_filter~keep_node. rv_keep = boolc( ( iv_visit = /apmg/if_apm_ajson_filter=>visit_type-value AND ( is_node-type = /apmg/if_apm_ajson_types=>node_type-string AND is_node-value IS NOT INITIAL OR is_node-type = /apmg/if_apm_ajson_types=>node_type-boolean OR is_node-type = /apmg/if_apm_ajson_types=>node_type-number AND is_node-value <> 0 ) ) OR ( iv_visit <> /apmg/if_apm_ajson_filter=>visit_type-value AND is_node-children > 0 ) ). ENDMETHOD. ENDCLASS. CLASS lcl_deprecated DEFINITION FINAL. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_filter. ENDCLASS. CLASS lcl_deprecated IMPLEMENTATION. METHOD /apmg/if_apm_ajson_filter~keep_node. rv_keep = boolc( ( iv_visit = /apmg/if_apm_ajson_filter=>visit_type-value AND ( is_node-type = /apmg/if_apm_ajson_types=>node_type-string AND is_node-value IS NOT INITIAL OR is_node-type = /apmg/if_apm_ajson_types=>node_type-boolean OR is_node-type = /apmg/if_apm_ajson_types=>node_type-number AND is_node-value <> 0 OR is_node-name = 'deprecated' ) ) OR ( iv_visit <> /apmg/if_apm_ajson_filter=>visit_type-value AND is_node-children > 0 ) ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_ajson_extensions IMPLEMENTATION. METHOD filter_deprecated. CREATE OBJECT result TYPE lcl_deprecated. ENDMETHOD. METHOD filter_empty_zero_null. CREATE OBJECT result TYPE lcl_empty_zero_null. ENDMETHOD. METHOD from_camel_case_underscore. CREATE OBJECT result TYPE lcl_from_camel_case_underscore. ENDMETHOD. METHOD to_camel_case_underscore. CREATE OBJECT result TYPE lcl_to_camel_case_underscore. ENDMETHOD. ENDCLASS. ********************************************************************** * FILTER EMPTY VALUES ********************************************************************** CLASS lcl_empty_filter DEFINITION FINAL. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_filter. ENDCLASS. CLASS lcl_empty_filter IMPLEMENTATION. METHOD /apmg/if_apm_ajson_filter~keep_node. rv_keep = boolc( ( iv_visit = /apmg/if_apm_ajson_filter=>visit_type-value AND is_node-value IS NOT INITIAL ) OR ( iv_visit <> /apmg/if_apm_ajson_filter=>visit_type-value AND is_node-children > 0 ) ). " children = 0 on open for initially empty nodes and on close for filtered ones ENDMETHOD. ENDCLASS. ********************************************************************** * FILTER PREDEFINED PATHS ********************************************************************** CLASS lcl_paths_filter DEFINITION FINAL. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_filter. METHODS constructor IMPORTING it_skip_paths TYPE string_table OPTIONAL iv_skip_paths TYPE string OPTIONAL iv_pattern_search TYPE abap_bool RAISING /apmg/cx_apm_ajson_error. PRIVATE SECTION. DATA mt_skip_paths TYPE HASHED TABLE OF string WITH UNIQUE KEY table_line. DATA mv_pattern_search TYPE abap_bool. ENDCLASS. CLASS lcl_paths_filter IMPLEMENTATION. METHOD /apmg/if_apm_ajson_filter~keep_node. DATA lv_full_path TYPE string. FIELD-SYMBOLS

LIKE LINE OF mt_skip_paths. lv_full_path = is_node-path && is_node-name. IF mv_pattern_search = abap_true. rv_keep = abap_true. LOOP AT mt_skip_paths ASSIGNING

. IF lv_full_path CP

. rv_keep = abap_false. EXIT. ENDIF. ENDLOOP. ELSE. READ TABLE mt_skip_paths WITH KEY table_line = lv_full_path TRANSPORTING NO FIELDS. rv_keep = boolc( sy-subrc <> 0 ). ENDIF. ENDMETHOD. METHOD constructor. DATA lv_s TYPE string. DATA lt_tab TYPE string_table. FIELD-SYMBOLS TYPE string. IF boolc( iv_skip_paths IS INITIAL ) = boolc( it_skip_paths IS INITIAL ). " XOR /apmg/cx_apm_ajson_error=>raise( 'no filter path specified' ). ENDIF. LOOP AT it_skip_paths INTO lv_s. lv_s = condense( lv_s ). APPEND lv_s TO lt_tab. ENDLOOP. IF iv_skip_paths IS NOT INITIAL. SPLIT iv_skip_paths AT ',' INTO TABLE lt_tab. LOOP AT lt_tab ASSIGNING . IF IS INITIAL. DELETE lt_tab INDEX sy-tabix. CONTINUE. ENDIF. = condense( ). ENDLOOP. ENDIF. SORT lt_tab BY table_line. DELETE ADJACENT DUPLICATES FROM lt_tab. mt_skip_paths = lt_tab. mv_pattern_search = iv_pattern_search. ENDMETHOD. ENDCLASS. ********************************************************************** * MULTI FILTER ********************************************************************** CLASS lcl_and_filter DEFINITION FINAL. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_filter. METHODS constructor IMPORTING it_filters TYPE /apmg/if_apm_ajson_filter=>ty_filter_tab RAISING /apmg/cx_apm_ajson_error. PRIVATE SECTION. DATA mt_filters TYPE /apmg/if_apm_ajson_filter=>ty_filter_tab. ENDCLASS. CLASS lcl_and_filter IMPLEMENTATION. METHOD /apmg/if_apm_ajson_filter~keep_node. DATA li_filter LIKE LINE OF mt_filters. rv_keep = abap_true. LOOP AT mt_filters INTO li_filter. rv_keep = li_filter->keep_node( is_node = is_node iv_visit = iv_visit ). IF rv_keep = abap_false. RETURN. ENDIF. ENDLOOP. ENDMETHOD. METHOD constructor. DATA li_filter LIKE LINE OF it_filters. LOOP AT it_filters INTO li_filter WHERE table_line IS BOUND. APPEND li_filter TO mt_filters. ENDLOOP. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_ajson_filter_lib IMPLEMENTATION. METHOD create_and_filter. CREATE OBJECT ri_filter TYPE lcl_and_filter EXPORTING it_filters = it_filters. ENDMETHOD. METHOD create_empty_filter. CREATE OBJECT ri_filter TYPE lcl_empty_filter. ENDMETHOD. METHOD create_path_filter. CREATE OBJECT ri_filter TYPE lcl_paths_filter EXPORTING iv_pattern_search = iv_pattern_search it_skip_paths = it_skip_paths iv_skip_paths = iv_skip_paths. ENDMETHOD. ENDCLASS. CLASS lcl_mapping_fields IMPLEMENTATION. "DEPRECATED METHOD constructor. DATA ls_mapping_field LIKE LINE OF mt_mapping_fields. LOOP AT it_mapping_fields INTO ls_mapping_field. ls_mapping_field-abap = to_upper( ls_mapping_field-abap ). INSERT ls_mapping_field INTO TABLE mt_mapping_fields. ENDLOOP. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_abap. DATA ls_mapping_field LIKE LINE OF mt_mapping_fields. READ TABLE mt_mapping_fields INTO ls_mapping_field WITH KEY json COMPONENTS json = iv_name. IF sy-subrc = 0. rv_result = ls_mapping_field-abap. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_json. DATA lv_field TYPE string. DATA ls_mapping_field LIKE LINE OF mt_mapping_fields. lv_field = to_upper( iv_name ). READ TABLE mt_mapping_fields INTO ls_mapping_field WITH KEY abap COMPONENTS abap = lv_field. IF sy-subrc = 0. rv_result = ls_mapping_field-json. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~rename_node. ENDMETHOD. ENDCLASS. CLASS lcl_rename IMPLEMENTATION. METHOD constructor. mt_rename_map = it_rename_map. mv_rename_by = iv_rename_by. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_abap. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_json. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~rename_node. DATA lv_full_path TYPE string. DATA lv_pair_found TYPE abap_bool. FIELD-SYMBOLS LIKE LINE OF mt_rename_map. CASE mv_rename_by. WHEN /apmg/cl_apm_ajson_mapping=>rename_by-attr_name. READ TABLE mt_rename_map ASSIGNING WITH TABLE KEY by_name COMPONENTS from = cv_name. lv_pair_found = boolc( sy-subrc = 0 ). WHEN /apmg/cl_apm_ajson_mapping=>rename_by-full_path. lv_full_path = is_node-path && cv_name. READ TABLE mt_rename_map ASSIGNING WITH TABLE KEY by_name COMPONENTS from = lv_full_path. lv_pair_found = boolc( sy-subrc = 0 ). WHEN /apmg/cl_apm_ajson_mapping=>rename_by-pattern. lv_full_path = is_node-path && cv_name. LOOP AT mt_rename_map ASSIGNING . IF lv_full_path CP -from. lv_pair_found = abap_true. EXIT. ENDIF. ENDLOOP. WHEN OTHERS. lv_pair_found = abap_false. " No rename ENDCASE. IF lv_pair_found = abap_true. cv_name = -to. ENDIF. ENDMETHOD. ENDCLASS. CLASS lcl_mapping_to_upper IMPLEMENTATION. METHOD constructor. mi_mapping_fields = /apmg/cl_apm_ajson_mapping=>create_field_mapping( it_mapping_fields ). ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_abap. rv_result = mi_mapping_fields->to_abap( iv_path = iv_path iv_name = iv_name ). ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_json. rv_result = mi_mapping_fields->to_json( iv_path = iv_path iv_name = iv_name ). IF rv_result IS NOT INITIAL. " Mapping found RETURN. ENDIF. rv_result = to_upper( iv_name ). ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~rename_node. cv_name = to_upper( cv_name ). ENDMETHOD. ENDCLASS. CLASS lcl_mapping_to_lower IMPLEMENTATION. METHOD constructor. mi_mapping_fields = /apmg/cl_apm_ajson_mapping=>create_field_mapping( it_mapping_fields ). ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_abap. rv_result = mi_mapping_fields->to_abap( iv_path = iv_path iv_name = iv_name ). ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_json. rv_result = mi_mapping_fields->to_json( iv_path = iv_path iv_name = iv_name ). IF rv_result IS NOT INITIAL. " Mapping found RETURN. ENDIF. rv_result = to_lower( iv_name ). ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~rename_node. cv_name = to_lower( cv_name ). ENDMETHOD. ENDCLASS. CLASS lcl_mapping_camel IMPLEMENTATION. "DEPRECATED METHOD constructor. mi_mapping_fields = /apmg/cl_apm_ajson_mapping=>create_field_mapping( it_mapping_fields ). mv_first_json_upper = iv_first_json_upper. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_abap. rv_result = mi_mapping_fields->to_abap( iv_path = iv_path iv_name = iv_name ). IF rv_result IS NOT INITIAL. " Mapping found RETURN. ENDIF. rv_result = iv_name. REPLACE ALL OCCURRENCES OF REGEX `([a-z])([A-Z])` IN rv_result WITH `$1_$2` ##REGEX_POSIX. "#EC NOTEXT ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_json. TYPES ty_token TYPE c LENGTH 255. DATA lt_tokens TYPE STANDARD TABLE OF ty_token. DATA lv_from TYPE i. FIELD-SYMBOLS LIKE LINE OF lt_tokens. rv_result = mi_mapping_fields->to_json( iv_path = iv_path iv_name = iv_name ). IF rv_result IS NOT INITIAL. " Mapping found RETURN. ENDIF. rv_result = iv_name. REPLACE ALL OCCURRENCES OF `__` IN rv_result WITH `*`. TRANSLATE rv_result TO LOWER CASE. TRANSLATE rv_result USING `/_:_~_`. IF mv_first_json_upper = abap_true. lv_from = 1. ELSE. lv_from = 2. ENDIF. SPLIT rv_result AT `_` INTO TABLE lt_tokens. LOOP AT lt_tokens ASSIGNING FROM lv_from. TRANSLATE (1) TO UPPER CASE. ENDLOOP. CONCATENATE LINES OF lt_tokens INTO rv_result. REPLACE ALL OCCURRENCES OF `*` IN rv_result WITH `_`. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~rename_node. ENDMETHOD. ENDCLASS. CLASS lcl_compound_mapper IMPLEMENTATION. METHOD constructor. mt_queue = it_queue. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~rename_node. DATA ls_node LIKE is_node. DATA li_mapper LIKE LINE OF mt_queue. ls_node = is_node. LOOP AT mt_queue INTO li_mapper. li_mapper->rename_node( EXPORTING is_node = ls_node CHANGING cv_name = cv_name ). ls_node-name = cv_name. ENDLOOP. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_abap. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_json. ENDMETHOD. ENDCLASS. CLASS lcl_to_snake IMPLEMENTATION. METHOD /apmg/if_apm_ajson_mapping~rename_node. REPLACE ALL OCCURRENCES OF REGEX `([a-z])([A-Z])` IN cv_name WITH `$1_$2` ##REGEX_POSIX. "#EC NOTEXT cv_name = to_lower( cv_name ). ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_abap. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_json. ENDMETHOD. ENDCLASS. CLASS lcl_to_camel IMPLEMENTATION. METHOD constructor. mv_first_json_upper = iv_first_json_upper. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~rename_node. TYPES lty_token TYPE c LENGTH 255. CONSTANTS lc_forced_underscore_marker TYPE c LENGTH 1 VALUE cl_abap_char_utilities=>horizontal_tab. DATA lt_tokens TYPE STANDARD TABLE OF lty_token. DATA lv_from TYPE i. FIELD-SYMBOLS LIKE LINE OF lt_tokens. IF mv_first_json_upper = abap_true. lv_from = 1. ELSE. lv_from = 2. ENDIF. REPLACE ALL OCCURRENCES OF `__` IN cv_name WITH lc_forced_underscore_marker. " Force underscore SPLIT cv_name AT `_` INTO TABLE lt_tokens. DELETE lt_tokens WHERE table_line IS INITIAL. LOOP AT lt_tokens ASSIGNING FROM lv_from. TRANSLATE +0(1) TO UPPER CASE. ENDLOOP. CONCATENATE LINES OF lt_tokens INTO cv_name. REPLACE ALL OCCURRENCES OF lc_forced_underscore_marker IN cv_name WITH `_`. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_abap. ENDMETHOD. METHOD /apmg/if_apm_ajson_mapping~to_json. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_ajson_mapping IMPLEMENTATION. METHOD create_camel_case. CREATE OBJECT ri_mapping TYPE lcl_mapping_camel EXPORTING it_mapping_fields = it_mapping_fields iv_first_json_upper = iv_first_json_upper. ENDMETHOD. METHOD create_compound_mapper. DATA lt_queue TYPE /apmg/if_apm_ajson_mapping=>ty_table_of. APPEND ii_mapper1 TO lt_queue. APPEND ii_mapper2 TO lt_queue. APPEND ii_mapper3 TO lt_queue. APPEND LINES OF it_more TO lt_queue. DELETE lt_queue WHERE table_line IS INITIAL. CREATE OBJECT ri_mapping TYPE lcl_compound_mapper EXPORTING it_queue = lt_queue. ENDMETHOD. METHOD create_field_mapping. CREATE OBJECT ri_mapping TYPE lcl_mapping_fields EXPORTING it_mapping_fields = it_mapping_fields. ENDMETHOD. METHOD create_lower_case. CREATE OBJECT ri_mapping TYPE lcl_mapping_to_lower EXPORTING it_mapping_fields = it_mapping_fields. ENDMETHOD. METHOD create_rename. CREATE OBJECT ri_mapping TYPE lcl_rename EXPORTING it_rename_map = it_rename_map iv_rename_by = iv_rename_by. ENDMETHOD. METHOD create_to_camel_case. CREATE OBJECT ri_mapping TYPE lcl_to_camel EXPORTING iv_first_json_upper = iv_first_json_upper. ENDMETHOD. METHOD create_to_snake_case. CREATE OBJECT ri_mapping TYPE lcl_to_snake. ENDMETHOD. METHOD create_upper_case. CREATE OBJECT ri_mapping TYPE lcl_mapping_to_upper EXPORTING it_mapping_fields = it_mapping_fields. ENDMETHOD. ENDCLASS. ********************************************************************** * INITIALIZE REFS BY PATH ********************************************************************** CLASS lcl_path_refs_init DEFINITION. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_refs_init. METHODS constructor IMPORTING !it_data_refs TYPE /apmg/if_apm_ajson_refs_init~tty_data_refs. PRIVATE SECTION. DATA mt_data_refs TYPE /apmg/if_apm_ajson_refs_init~tty_data_refs. ENDCLASS. CLASS lcl_path_refs_init IMPLEMENTATION. METHOD constructor. mt_data_refs = it_data_refs. ENDMETHOD. METHOD /apmg/if_apm_ajson_refs_init~get_data_ref. FIELD-SYMBOLS LIKE LINE OF mt_data_refs. READ TABLE mt_data_refs ASSIGNING WITH KEY by_path COMPONENTS path = is_node-path name = is_node-name. IF sy-subrc = 0. ro_ref = -dref. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_ajson_refs_init_l IMPLEMENTATION. METHOD create_path_refs_init. CREATE OBJECT ri_refs_init TYPE lcl_path_refs_init EXPORTING it_data_refs = it_data_refs. ENDMETHOD. ENDCLASS. ********************************************************************** * INITIALIZE REFS BY PATH ********************************************************************** CLASS LCL_PATH_REFS_INIT_ DEFINITION. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_ref_initial. METHODS constructor IMPORTING !it_data_refs TYPE /apmg/if_apm_ajson_ref_initial~tty_data_refs. PRIVATE SECTION. DATA mt_data_refs TYPE /apmg/if_apm_ajson_ref_initial~tty_data_refs. ENDCLASS. CLASS LCL_PATH_REFS_INIT_ IMPLEMENTATION. METHOD constructor. mt_data_refs = it_data_refs. ENDMETHOD. METHOD /apmg/if_apm_ajson_ref_initial~get_data_ref. FIELD-SYMBOLS LIKE LINE OF mt_data_refs. READ TABLE mt_data_refs ASSIGNING WITH KEY by_path COMPONENTS path = is_node-path name = is_node-name. IF sy-subrc = 0. ro_ref = -dref. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_ajson_ref_initial IMPLEMENTATION. METHOD create_path_refs_init. CREATE OBJECT ri_refs_init TYPE LCL_PATH_REFS_INIT_ EXPORTING it_data_refs = it_data_refs. ENDMETHOD. ENDCLASS. ********************************************************************** * ITERATOR ********************************************************************** CLASS lcl_node_iterator DEFINITION FINAL. PUBLIC SECTION. INTERFACES /apmg/if_apm_ajson_iterator. METHODS constructor IMPORTING ii_json TYPE REF TO /apmg/if_apm_ajson iv_path TYPE string iv_node_type TYPE /apmg/if_apm_ajson_types=>ty_node_type RAISING /apmg/cx_apm_ajson_error. PRIVATE SECTION. DATA mi_json TYPE REF TO /apmg/if_apm_ajson. DATA mv_node_type TYPE /apmg/if_apm_ajson_types=>ty_node_type. DATA mv_base_path TYPE string. DATA mr_cursor TYPE REF TO /apmg/if_apm_ajson_types=>ty_node. DATA mv_tabix TYPE i. DATA mv_has_next TYPE abap_bool. METHODS find_first_node. ENDCLASS. CLASS lcl_node_iterator IMPLEMENTATION. METHOD constructor. IF NOT ( iv_node_type = /apmg/if_apm_ajson_types=>node_type-array OR iv_node_type = /apmg/if_apm_ajson_types=>node_type-object ). /apmg/cx_apm_ajson_error=>raise( |Iterator can iterate arrays or objects only ("{ iv_node_type }" passed)| ). ENDIF. mv_base_path = /apmg/cl_apm_ajson=>normalize_path( iv_path ). mv_node_type = iv_node_type. mi_json = ii_json. DATA lv_node_type LIKE mv_node_type. lv_node_type = ii_json->get_node_type( mv_base_path ). IF lv_node_type IS INITIAL. /apmg/cx_apm_ajson_error=>raise( |Path not found: { iv_path }| ). ELSEIF mv_node_type = /apmg/if_apm_ajson_types=>node_type-array AND lv_node_type <> mv_node_type. /apmg/cx_apm_ajson_error=>raise( |Array expected at: { iv_path }| ). ELSEIF mv_node_type = /apmg/if_apm_ajson_types=>node_type-object AND lv_node_type <> mv_node_type. /apmg/cx_apm_ajson_error=>raise( |Object expected at: { iv_path }| ). ENDIF. find_first_node( ). ENDMETHOD. METHOD find_first_node. CASE mv_node_type. WHEN /apmg/if_apm_ajson_types=>node_type-array. " path + array index key LOOP AT mi_json->mt_json_tree REFERENCE INTO mr_cursor USING KEY array_index WHERE path = mv_base_path. mv_has_next = abap_true. mv_tabix = sy-tabix. EXIT. " first found ENDLOOP. WHEN /apmg/if_apm_ajson_types=>node_type-object. " regular path + name key LOOP AT mi_json->mt_json_tree REFERENCE INTO mr_cursor WHERE path = mv_base_path. mv_has_next = abap_true. mv_tabix = sy-tabix. EXIT. " first found ENDLOOP. WHEN OTHERS. ASSERT 1 = 0. ENDCASE. ENDMETHOD. METHOD /apmg/if_apm_ajson_iterator~has_next. rv_yes = mv_has_next. ENDMETHOD. METHOD /apmg/if_apm_ajson_iterator~next. IF mv_has_next = abap_false. RETURN. ENDIF. ri_item = mi_json->slice( |{ mr_cursor->path }{ mr_cursor->name }| ). " TODO: improve performance, see comment in slice, maybe reuse read only reference to node_tree mv_tabix = mv_tabix + 1. CASE mv_node_type. WHEN /apmg/if_apm_ajson_types=>node_type-array. " path + array index key READ TABLE mi_json->mt_json_tree INDEX mv_tabix USING KEY array_index REFERENCE INTO mr_cursor. WHEN /apmg/if_apm_ajson_types=>node_type-object. " regular path + name key READ TABLE mi_json->mt_json_tree INDEX mv_tabix REFERENCE INTO mr_cursor. WHEN OTHERS. ASSERT 1 = 0. ENDCASE. mv_has_next = boolc( sy-subrc = 0 AND mr_cursor->path = mv_base_path ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_ajson_utilities IMPLEMENTATION. METHOD delete_empty_nodes. DATA ls_json_tree LIKE LINE OF io_json->mt_json_tree. DATA lv_done TYPE abap_bool. DO. lv_done = abap_true. IF iv_keep_empty_arrays = abap_false. LOOP AT io_json->mt_json_tree INTO ls_json_tree WHERE type = /apmg/if_apm_ajson_types=>node_type-array AND children = 0. "#EC CI_SORTSEQ io_json->delete( ls_json_tree-path && ls_json_tree-name ). ENDLOOP. IF sy-subrc = 0. lv_done = abap_false. ENDIF. ENDIF. LOOP AT io_json->mt_json_tree INTO ls_json_tree WHERE type = /apmg/if_apm_ajson_types=>node_type-object AND children = 0. "#EC CI_SORTSEQ io_json->delete( ls_json_tree-path && ls_json_tree-name ). ENDLOOP. IF sy-subrc = 0. lv_done = abap_false. ENDIF. IF lv_done = abap_true. EXIT. " nothing else to delete ENDIF. ENDDO. ENDMETHOD. METHOD diff. mo_json_a = normalize_input( iv_json = iv_json_a io_json = io_json_a ). mo_json_b = normalize_input( iv_json = iv_json_b io_json = io_json_b ). mo_insert = /apmg/cl_apm_ajson=>create_empty( ). mo_delete = /apmg/cl_apm_ajson=>create_empty( ). mo_change = /apmg/cl_apm_ajson=>create_empty( ). diff_a_b( '/' ). diff_b_a( '/' ). eo_insert ?= mo_insert. eo_delete ?= mo_delete. eo_change ?= mo_change. delete_empty_nodes( io_json = eo_insert iv_keep_empty_arrays = iv_keep_empty_arrays ). delete_empty_nodes( io_json = eo_delete iv_keep_empty_arrays = iv_keep_empty_arrays ). delete_empty_nodes( io_json = eo_change iv_keep_empty_arrays = iv_keep_empty_arrays ). ENDMETHOD. METHOD diff_a_b. DATA: lv_path_a TYPE string, lv_path_b TYPE string. FIELD-SYMBOLS: LIKE LINE OF mo_json_a->mt_json_tree, LIKE LINE OF mo_json_a->mt_json_tree. LOOP AT mo_json_a->mt_json_tree ASSIGNING WHERE path = iv_path. lv_path_a = -path && -name && '/'. READ TABLE mo_json_b->mt_json_tree ASSIGNING WITH TABLE KEY path = -path name = -name. IF sy-subrc = 0. lv_path_b = -path && -name && '/'. IF -type = -type. CASE -type. WHEN /apmg/if_apm_ajson_types=>node_type-array. mo_insert->touch_array( lv_path_a ). mo_change->touch_array( lv_path_a ). mo_delete->touch_array( lv_path_a ). diff_a_b( lv_path_a ). WHEN /apmg/if_apm_ajson_types=>node_type-object. diff_a_b( lv_path_a ). WHEN OTHERS. IF -value <> -value. " save as changed value mo_change->set( iv_path = lv_path_b iv_val = -value iv_node_type = -type ). ENDIF. ENDCASE. ELSE. " save changed type as delete + insert CASE -type. WHEN /apmg/if_apm_ajson_types=>node_type-array. mo_delete->touch_array( lv_path_a ). diff_a_b( lv_path_a ). WHEN /apmg/if_apm_ajson_types=>node_type-object. diff_a_b( lv_path_a ). WHEN OTHERS. mo_delete->set( iv_path = lv_path_a iv_val = -value iv_node_type = -type ). ENDCASE. CASE -type. WHEN /apmg/if_apm_ajson_types=>node_type-array. mo_insert->touch_array( lv_path_b ). diff_b_a( lv_path_b ). WHEN /apmg/if_apm_ajson_types=>node_type-object. diff_b_a( lv_path_b ). WHEN OTHERS. mo_insert->set( iv_path = lv_path_b iv_val = -value iv_node_type = -type ). ENDCASE. ENDIF. ELSE. " save as delete CASE -type. WHEN /apmg/if_apm_ajson_types=>node_type-array. mo_delete->touch_array( lv_path_a ). diff_a_b( lv_path_a ). WHEN /apmg/if_apm_ajson_types=>node_type-object. diff_a_b( lv_path_a ). WHEN OTHERS. mo_delete->set( iv_path = lv_path_a iv_val = -value iv_node_type = -type ). ENDCASE. ENDIF. ENDLOOP. ENDMETHOD. METHOD diff_b_a. DATA lv_path TYPE string. FIELD-SYMBOLS LIKE LINE OF mo_json_b->mt_json_tree. LOOP AT mo_json_b->mt_json_tree ASSIGNING WHERE path = iv_path. lv_path = -path && -name && '/'. CASE -type. WHEN /apmg/if_apm_ajson_types=>node_type-array. mo_insert->touch_array( lv_path ). diff_b_a( iv_path = lv_path iv_array = abap_true ). WHEN /apmg/if_apm_ajson_types=>node_type-object. diff_b_a( lv_path ). WHEN OTHERS. IF iv_array = abap_false. READ TABLE mo_json_a->mt_json_tree TRANSPORTING NO FIELDS WITH TABLE KEY path = -path name = -name. IF sy-subrc <> 0. " save as insert mo_insert->set( iv_path = lv_path iv_val = -value iv_node_type = -type ). ENDIF. ELSE. READ TABLE mo_insert->mt_json_tree TRANSPORTING NO FIELDS WITH KEY path = -path value = -value. IF sy-subrc <> 0. " save as new array value mo_insert->push( iv_path = iv_path iv_val = -value ). ENDIF. ENDIF. ENDCASE. ENDLOOP. ENDMETHOD. METHOD is_equal. DATA li_ins TYPE REF TO /apmg/if_apm_ajson. DATA li_del TYPE REF TO /apmg/if_apm_ajson. DATA li_mod TYPE REF TO /apmg/if_apm_ajson. diff( EXPORTING iv_json_a = iv_json_a iv_json_b = iv_json_b io_json_a = ii_json_a io_json_b = ii_json_b IMPORTING eo_insert = li_ins eo_delete = li_del eo_change = li_mod ). rv_yes = boolc( li_ins->is_empty( ) = abap_true AND li_del->is_empty( ) = abap_true AND li_mod->is_empty( ) = abap_true ). ENDMETHOD. METHOD iterate_array. CREATE OBJECT ri_iterator TYPE lcl_node_iterator EXPORTING iv_node_type = /apmg/if_apm_ajson_types=>node_type-array ii_json = ii_json iv_path = iv_path. ENDMETHOD. METHOD iterate_object. CREATE OBJECT ri_iterator TYPE lcl_node_iterator EXPORTING iv_node_type = /apmg/if_apm_ajson_types=>node_type-object ii_json = ii_json iv_path = iv_path. ENDMETHOD. METHOD merge. mo_json_a = normalize_input( iv_json = iv_json_a io_json = io_json_a ). mo_json_b = normalize_input( iv_json = iv_json_b io_json = io_json_b ). " Start with first JSON... mo_insert = mo_json_a. " ...and add all nodes from second JSON diff_b_a( '/' ). ro_json ?= mo_insert. delete_empty_nodes( io_json = ro_json iv_keep_empty_arrays = iv_keep_empty_arrays ). ENDMETHOD. METHOD new. CREATE OBJECT ro_instance. ENDMETHOD. METHOD normalize_input. IF boolc( iv_json IS INITIAL ) = boolc( io_json IS INITIAL ). /apmg/cx_apm_ajson_error=>raise( 'Either supply JSON string or instance, but not both' ). ENDIF. IF iv_json IS NOT INITIAL. ro_json = /apmg/cl_apm_ajson=>parse( iv_json ). ELSEIF io_json IS NOT INITIAL. ro_json = io_json. ELSE. /apmg/cx_apm_ajson_error=>raise( 'Supply either JSON string or instance' ). ENDIF. ENDMETHOD. METHOD sort. DATA lo_json TYPE REF TO /apmg/if_apm_ajson. lo_json = normalize_input( iv_json = iv_json io_json = io_json ). " Nodes are parsed into a sorted table, so no explicit sorting required rv_sorted = lo_json->stringify( 2 ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_arborist IMPLEMENTATION. METHOD /apmg/if_apm_arborist~build_ideal_tree. " TODO: Future implementation ASSERT 0 = 1. ENDMETHOD. METHOD /apmg/if_apm_arborist~get_log. result = log. ENDMETHOD. METHOD /apmg/if_apm_arborist~get_tree. result = /apmg/cl_apm_arborist_node=>get_all( ). ENDMETHOD. METHOD /apmg/if_apm_arborist~load_actual_tree. " Clear previous tree and state /apmg/cl_apm_arborist_node=>clear( ). CLEAR: log, visited, processing_stack. add_log( type = /apmg/if_apm_arborist=>c_log_type-info message = 'Starting to load actual tree' ). " Step 1: Get all installed packages with their metadata DATA(packages) = /apmg/cl_apm_package_json=>list( instanciate = abap_true is_bundle = abap_false ). add_log( type = /apmg/if_apm_arborist=>c_log_type-info message = |Found { lines( packages ) } installed packages| ). " Step 2: Create nodes for all installed packages first " This ensures all nodes exist before we create edges LOOP AT packages ASSIGNING FIELD-SYMBOL(). TRY. DATA(manifest) = -instance->get( ). /apmg/cl_apm_arborist_node=>create( package = -package manifest = manifest installed = abap_true ). INSERT VALUE #( name = -name ) INTO TABLE visited. CATCH /apmg/cx_apm_error INTO DATA(error). add_log( type = /apmg/if_apm_arborist=>c_log_type-warning message = |Error loading package { -name }: { error->get_text( ) }| name = -name version = -version ). ENDTRY. ENDLOOP. " Step 3: Process dependencies for each installed package " Now that all nodes exist, create edges LOOP AT packages ASSIGNING . process_package( package_info = depth = 0 ). ENDLOOP. " Step 4: Process uninstalled dependencies recursively " This finds dependencies that are declared but not installed process_uninstalled( ). " Step 5: Re-resolve all edges now that all nodes are created DATA(final_nodes) = resolve( ). " Log summary DATA(total_nodes) = lines( final_nodes ). DATA(installed_count) = 0. DATA(missing_count) = 0. DATA(invalid_count) = 0. LOOP AT final_nodes ASSIGNING FIELD-SYMBOL(). IF ->installed = abap_true. installed_count = installed_count + 1. ENDIF. LOOP AT ->edges_out ASSIGNING FIELD-SYMBOL(). IF ->is_missing( ). missing_count = missing_count + 1. ELSEIF ->is_invalid( ). invalid_count = invalid_count + 1. ENDIF. ENDLOOP. ENDLOOP. add_log( type = /apmg/if_apm_arborist=>c_log_type-info message = |Tree complete: { total_nodes } nodes, { installed_count } installed, | && |{ missing_count } missing deps, { invalid_count } invalid deps| ). result = final_nodes. ENDMETHOD. METHOD /apmg/if_apm_arborist~load_virtual_tree. " TODO: Future implementation - read from package-lock.abap.json ASSERT 0 = 1. ENDMETHOD. METHOD /apmg/if_apm_arborist~reify_tree. " TODO: Future implementation ASSERT 0 = 1. ENDMETHOD. METHOD add_log. DATA(entry) = VALUE /apmg/if_apm_arborist=>ty_log_entry( type = type message = message name = name version = version spec = spec ). INSERT entry INTO TABLE log. ENDMETHOD. METHOD constructor. me->registry = registry. me->with_bundle_dependencies = with_bundle_dependencies. ENDMETHOD. METHOD create_edges. IF node IS NOT BOUND OR dependencies IS INITIAL. RETURN. ENDIF. LOOP AT dependencies ASSIGNING FIELD-SYMBOL(). IF with_bundle_dependencies = abap_false AND line_exists( bundle_dependencies[ table_line = -key ] ). CONTINUE. ENDIF. " Create edge (constructor automatically resolves target) /apmg/cl_apm_arborist_edge=>create( from = node type = type name = -key spec = -range ). ENDLOOP. ENDMETHOD. METHOD factory. IF instance IS INITIAL. result = NEW /apmg/cl_apm_arborist( registry = registry with_bundle_dependencies = with_bundle_dependencies ). ELSE. result = instance. ENDIF. ENDMETHOD. METHOD get_manifest. " First try to get from already loaded node DATA(existing_node) = /apmg/cl_apm_arborist_node=>get_by_name( name ). IF existing_node IS BOUND. result-name = existing_node->name. result-version = existing_node->version. result-dependencies = existing_node->dependencies. result-dev_dependencies = existing_node->dev_dependencies. result-optional_dependencies = existing_node->optional_dependencies. result-peer_dependencies = existing_node->peer_dependencies. result-bundle_dependencies = existing_node->bundle_dependencies. RETURN. ENDIF. " Try to get from pacote (registry cache) TRY. DATA(pacote) = /apmg/cl_apm_pacote=>factory( registry = registry name = name ). IF pacote->exists( ). DATA(packument) = pacote->get( ). IF version IS NOT INITIAL. DATA(manifest) = pacote->get_version( version ). result = CORRESPONDING #( manifest ). ELSEIF packument-dist_tags IS NOT INITIAL. " Get latest version READ TABLE packument-dist_tags ASSIGNING FIELD-SYMBOL() WITH KEY key = 'latest'. IF sy-subrc = 0. manifest = pacote->get_version( -value ). result = CORRESPONDING #( manifest ). ENDIF. ENDIF. ENDIF. CATCH /apmg/cx_apm_error INTO DATA(error). add_log( type = /apmg/if_apm_arborist=>c_log_type-warning message = |Could not fetch manifest for { name }: { error->get_text( ) }| name = name ). ENDTRY. ENDMETHOD. METHOD get_versions. TRY. DATA(pacote) = /apmg/cl_apm_pacote=>factory( registry = registry name = name ). pacote->packument( ). result = pacote->get_versions( ). CATCH /apmg/cx_apm_error INTO DATA(error). add_log( type = /apmg/if_apm_arborist=>c_log_type-warning message = |Could not fetch packument for { name }: { error->get_text( ) }| name = name ). ENDTRY. ENDMETHOD. METHOD injector. instance = mock. ENDMETHOD. METHOD is_circular. result = xsdbool( line_exists( processing_stack[ table_line = name ] ) ). ENDMETHOD. METHOD process_dependencies. IF node IS NOT BOUND. RETURN. ENDIF. " Check for circular dependency IF is_circular( node->name ). add_log( type = /apmg/if_apm_arborist=>c_log_type-circular message = |Circular dependency detected: { node->name }| name = node->name ). RETURN. ENDIF. " Check for maximum depth IF depth > c_max_depth. add_log( type = /apmg/if_apm_arborist=>c_log_type-depth message = |Maximum depth reached: { node->name }| name = node->name ). RETURN. ENDIF. " Add to processing stack INSERT node->name INTO TABLE processing_stack. " Create edges for production dependencies create_edges( node = node dependencies = node->dependencies bundle_dependencies = node->bundle_dependencies type = /apmg/if_apm_arborist=>c_dependency_type-prod ). " Create edges for dev dependencies create_edges( node = node dependencies = node->dev_dependencies bundle_dependencies = node->bundle_dependencies type = /apmg/if_apm_arborist=>c_dependency_type-dev ). " Create edges for optional dependencies create_edges( node = node dependencies = node->optional_dependencies type = /apmg/if_apm_arborist=>c_dependency_type-optional ). " Create edges for peer dependencies create_edges( node = node dependencies = node->peer_dependencies type = /apmg/if_apm_arborist=>c_dependency_type-peer ). " Remove from processing stack DELETE processing_stack WHERE table_line = node->name. ENDMETHOD. METHOD process_package. " Skip if no instance or no name IF package_info-instance IS NOT BOUND OR package_info-name IS INITIAL. RETURN. ENDIF. " Get the node from the tree DATA(node) = /apmg/cl_apm_arborist_node=>get_by_name( package_info-name ). IF node IS INITIAL. RETURN. ENDIF. " Process dependencies process_dependencies( node = node depth = depth + 1 ). ENDMETHOD. METHOD process_uninstalled. DATA(iteration) = 0. DATA(nodes_to_process) = VALUE /apmg/if_apm_arborist~ty_node_refs( ). DO. iteration = iteration + 1. IF iteration > c_max_iterations. add_log( type = /apmg/if_apm_arborist=>c_log_type-warning message = |Stopped processing after { c_max_iterations } iterations to prevent infinite loop| ). EXIT. ENDIF. " Find nodes with unresolved dependencies CLEAR nodes_to_process. DATA(all_nodes) = /apmg/cl_apm_arborist_node=>get_all( ). LOOP AT all_nodes ASSIGNING FIELD-SYMBOL(). LOOP AT ->edges_out ASSIGNING FIELD-SYMBOL(). " Check if we haven't visited this dependency yet IF ->is_missing( ) AND NOT line_exists( visited[ name = ->name ] ). " Try to get manifest from registry for uninstalled dependency DATA(uninstalled_manifest) = get_manifest( ->name ). IF uninstalled_manifest IS NOT INITIAL. " Create placeholder node for uninstalled package DATA(new_node) = /apmg/cl_apm_arborist_node=>create( manifest = uninstalled_manifest installed = abap_false ). INSERT VALUE #( name = ->name ) INTO TABLE visited. INSERT new_node INTO TABLE nodes_to_process. add_log( type = /apmg/if_apm_arborist=>c_log_type-warning message = |Dependency { ->name }@{ ->spec } is not installed| name = ->name spec = ->spec ). ENDIF. ENDIF. ENDLOOP. ENDLOOP. " Process dependencies of newly added nodes IF nodes_to_process IS INITIAL. EXIT. " No more unresolved dependencies ENDIF. LOOP AT nodes_to_process ASSIGNING FIELD-SYMBOL(). process_dependencies( node = depth = iteration ). ENDLOOP. ENDDO. ENDMETHOD. METHOD resolve. result = /apmg/cl_apm_arborist_node=>get_all( ). LOOP AT result ASSIGNING FIELD-SYMBOL(). LOOP AT ->edges_out ASSIGNING FIELD-SYMBOL(). ->resolve( ). ENDLOOP. " Aggregate required versions from incoming edges and check satisfaction DATA(required_specs) = VALUE string_table( ). DATA(all_satisfied) = abap_true. DATA(max_satisfying) = ->version. LOOP AT ->edges_in ASSIGNING . " Collect all specs from incoming edges INSERT ->spec INTO TABLE required_specs. " Check if current node version satisfies this requirement IF ->satisfies( ->spec ) = abap_false. all_satisfied = abap_false. ENDIF. ENDLOOP. " Determine max_satisfying: if current version satisfies all requirements, use it " Otherwise, max_satisfying needs to be calculated from available versions in registry IF all_satisfied = abap_false AND required_specs IS NOT INITIAL. DATA(available_versions) = get_versions( ->name ). " Current version doesn't satisfy all requirements " max_satisfying would ideally be the maximum version from registry that satisfies all specs max_satisfying = ->max_satisfying( versions = available_versions specs = required_specs ). ENDIF. ->set_max_satisfying( max_satisfying ). ENDLOOP. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_arborist_edge IMPLEMENTATION. METHOD constructor. me->from = from. me->type = type. me->name = name. me->spec = spec. " Resolve target node immediately resolve( ). ENDMETHOD. METHOD create. result = NEW #( from = from type = type name = name spec = spec ). " Add edge to source node's outgoing edges IF from IS BOUND. from->add_edge_out( result ). ENDIF. " Add edge to target node's incoming edges IF result->to IS BOUND. result->to->add_edge_in( result ). ENDIF. ENDMETHOD. METHOD get_error_description. CASE error. WHEN /apmg/if_apm_arborist=>c_error_type-missing. result = |Dependency { name }@{ spec } is not installed|. WHEN /apmg/if_apm_arborist=>c_error_type-invalid. IF to IS BOUND. result = |Dependency { name }@{ spec } not satisfied by installed { to->version }|. ELSE. result = |Dependency { name }@{ spec } is invalid|. ENDIF. WHEN /apmg/if_apm_arborist=>c_error_type-peer_local. result = |Peer dependency { name }@{ spec } should be installed at root level|. WHEN /apmg/if_apm_arborist=>c_error_type-detached. result = |Dependency { name } is detached from the tree|. WHEN OTHERS. result = ''. ENDCASE. ENDMETHOD. METHOD is_invalid. result = xsdbool( error = /apmg/if_apm_arborist=>c_error_type-invalid ). ENDMETHOD. METHOD is_missing. result = xsdbool( error = /apmg/if_apm_arborist=>c_error_type-missing ). ENDMETHOD. METHOD resolve. " Try to find the target node in the global tree to = /apmg/cl_apm_arborist_node=>get_by_name( name ). IF to IS NOT BOUND. " Dependency is missing valid = abap_false. error = /apmg/if_apm_arborist=>c_error_type-missing. ELSE. " Check if installed version satisfies the spec valid = to->satisfies( spec ). IF valid = abap_false. error = /apmg/if_apm_arborist=>c_error_type-invalid. ENDIF. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_arborist_node IMPLEMENTATION. METHOD add_edge_in. INSERT edge INTO TABLE edges_in. ENDMETHOD. METHOD add_edge_out. INSERT edge INTO TABLE edges_out. ENDMETHOD. METHOD add_error. INSERT message INTO TABLE errors. ENDMETHOD. METHOD clear. CLEAR tree. ENDMETHOD. METHOD constructor. me->package = package. me->name = manifest-name. me->version = manifest-version. me->dependencies = manifest-dependencies. me->dev_dependencies = manifest-dev_dependencies. me->peer_dependencies = manifest-peer_dependencies. me->optional_dependencies = manifest-optional_dependencies. me->bundle_dependencies = manifest-bundle_dependencies. me->installed = installed. ENDMETHOD. METHOD create. " Check if node already exists IF exists( manifest-name ). result = get_by_name( manifest-name ). RETURN. ENDIF. " Create new node result = NEW /apmg/cl_apm_arborist_node( package = package manifest = manifest installed = installed ). " Add to global tree DATA(entry) = VALUE ty_node_entry( name = manifest-name package = package instance = result ). INSERT entry INTO TABLE tree. ENDMETHOD. METHOD exists. result = xsdbool( line_exists( tree[ name = name ] ) ). ENDMETHOD. METHOD get_all. LOOP AT tree ASSIGNING FIELD-SYMBOL(). INSERT -instance INTO TABLE result. ENDLOOP. ENDMETHOD. METHOD get_all_dependencies. " Combine all dependency types APPEND LINES OF dependencies TO result. APPEND LINES OF dev_dependencies TO result. APPEND LINES OF peer_dependencies TO result. APPEND LINES OF optional_dependencies TO result. ENDMETHOD. METHOD get_by_name. READ TABLE tree ASSIGNING FIELD-SYMBOL() WITH TABLE KEY name = name. IF sy-subrc = 0. result = -instance. ENDIF. ENDMETHOD. METHOD get_by_package. LOOP AT tree ASSIGNING FIELD-SYMBOL() WHERE package = package. result = -instance. EXIT. ENDLOOP. ENDMETHOD. METHOD max_satisfying. " Concatenate specs into a range (AND) condition DATA(range) = concat_lines_of( table = specs sep = ` ` ). TRY. result = /apmg/cl_apm_semver_ranges=>max_satisfying( versions = versions range = range ). CATCH /apmg/cx_apm_error. result = ''. ENDTRY. ENDMETHOD. METHOD satisfies. TRY. result = /apmg/cl_apm_semver_functions=>satisfies( version = version range = range ). CATCH /apmg/cx_apm_error. result = abap_false. ENDTRY. ENDMETHOD. METHOD set_max_satisfying. CASE max_satisfying. WHEN ''. installed = abap_false. add_error( 'No version satisfies required specs' ). WHEN version. " current version satisfies installed = abap_true. WHEN OTHERS. installed = abap_false. add_error( |New version { max_satisfying } satisfies required specs| ). ENDCASE. max_satisfying_version = max_satisfying. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_auth IMPLEMENTATION. METHOD is_package_allowed. " Check if package owned by SAP is allowed (new packages are ok, since they are created automatically) DATA(username) = zcl_abapgit_factory=>get_sap_package( package )->read_responsible( ). " TODO: This uses abapGit exit. Replace with apm logic result = xsdbool( username <> 'SAP' OR zcl_abapgit_factory=>get_environment( )->is_sap_object_allowed( ) = abap_true ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_code_importer IMPLEMENTATION. METHOD get_class_name_from_token. result = token. IF result IS NOT INITIAL AND result(1) = '('. SPLIT result+1 AT ')' INTO result DATA(rest). IF result IS NOT INITIAL AND result(1) = ''''. " ('zcl_test')=>method SPLIT result+1 AT '''' INTO result rest. RETURN. ENDIF. " (zcl_test=>const)->method, (zcl_test=>const) ENDIF. IF result CS '~'. " zif_test~method SPLIT result AT '~' INTO result rest. ENDIF. IF result CS '('. " zcl_test( ) SPLIT result AT '(' INTO result rest. ENDIF. IF result CS '=>'. " zcl_test=>method SPLIT result AT '=' INTO result rest. ELSEIF result CS '->'. " zcl_test->method SPLIT result AT '->' INTO result rest. ENDIF. " @zcl_test=>constant IF result(1) = '@'. result = result+1. ENDIF. ENDMETHOD. METHOD get_interface_name_from_token. result = token. IF result CS '=>'. " zcl_test=>zif_test~method SPLIT result AT '>' INTO DATA(rest) result. ELSEIF result CS '->'. " zcl_test->zif_test~method SPLIT result AT '>' INTO rest result. ENDIF. IF result IS NOT INITIAL AND result(1) = '('. SPLIT result+1 AT ')' INTO result rest. IF result IS NOT INITIAL AND result(1) = ''''. " zcl_test->('zif_test~method') SPLIT result+1 AT '''' INTO result rest. ENDIF. " zcl_test->(zif_test=>const) ENDIF. IF result CS '~'. " zif_test~method SPLIT result AT '~' INTO result rest. ELSEIF result CS '=>'. " zif_test=>const SPLIT result AT '=' INTO result rest. ENDIF. ENDMETHOD. METHOD get_object_name_from_abapdoc. result = token. " Example: " "! @raising zcx_test_error | Exception FIND REGEX '"! @raising (.*) \|' IN token SUBMATCHES result ##REGEX_POSIX. IF sy-subrc = 0. result = to_upper( result ). ENDIF. ENDMETHOD. METHOD import. CONSTANTS c_token_types TYPE string VALUE 'HIlm34CA'. DATA: tokens TYPE stokesx_tab, tokens_checked TYPE stokesx_tab. DATA(code) = read( program_name = program_name program_source = program_source ). tokens = scan( program_name = program_name program_source = code ). IF tokens IS INITIAL. RETURN. ENDIF. " Collect all tokens that have a mapping LOOP AT tokens ASSIGNING FIELD-SYMBOL() WHERE type CA c_token_types. " FIXME: this does not work if there are objects with different type but same name CASE -type. WHEN 'C'. " abapDoc comments READ TABLE map ASSIGNING FIELD-SYMBOL() WITH TABLE KEY old_object = get_object_name_from_abapdoc( -str ). IF sy-subrc = 0. -str = replace( val = -str sub = -old_object with = to_lower( -new_object ) case = abap_false ). INSERT INTO TABLE tokens_checked. ENDIF. WHEN 'A'. " program name READ TABLE map ASSIGNING WITH TABLE KEY old_object = -str. IF sy-subrc = 0. -str = replace( val = -str sub = -old_object with = to_lower( -new_object ) case = abap_false ). INSERT INTO TABLE tokens_checked. ENDIF. WHEN OTHERS. " Other statements READ TABLE map ASSIGNING WITH TABLE KEY old_object = get_class_name_from_token( -str ). IF sy-subrc = 0. -str = replace( val = -str sub = -old_object with = -new_object case = abap_false ). INSERT INTO TABLE tokens_checked. ENDIF. READ TABLE map ASSIGNING WITH TABLE KEY old_object = get_interface_name_from_token( -str ). IF sy-subrc = 0. -str = replace( val = -str sub = -old_object with = -new_object case = abap_false ). INSERT INTO TABLE tokens_checked. ENDIF. ENDCASE. ENDLOOP. " New tokens might be different in length. Sort tokens back to front " so source can be adjusted without problems SORT tokens_checked BY row DESCENDING col DESCENDING. LOOP AT tokens_checked ASSIGNING . READ TABLE code ASSIGNING FIELD-SYMBOL() INDEX -row. ASSERT sy-subrc = 0. IF -len1 > 0. = substring( val = len = -col ) && -str && substring( val = off = -col + -len1 ). ELSEIF -len2 > 0. " We don't have a test case so not sure if this is correct :shrug: = substring( val = len = -col ) && -str && substring( val = off = -col + -len2 ). ELSEIF -len3 > 0. " Example: " INSERT (zif_test=>c_tabname) FROM test = substring( val = len = -col ) && -str && substring( val = off = -col + -len3 + 2 ). ENDIF. IF strlen( ) > 255. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Line length overflow in { program_name }, Line { -row }|. ENDIF. ENDLOOP. IF is_pretty = abap_true. CALL FUNCTION 'PRETTY_PRINTER' EXPORTING inctoo = abap_false TABLES ntext = result otext = code EXCEPTIONS enqueue_table_full = 1 include_enqueued = 2 include_readerror = 3 include_writeerror = 4 OTHERS = 5. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error pretty printing code: { program_name }|. ENDIF. ELSE. result = code. ENDIF. ENDMETHOD. METHOD prepare. result = program_source. LOOP AT result ASSIGNING FIELD-SYMBOL(). DO 3 TIMES. CASE sy-index. WHEN 1. DATA(prefix) = '* @@'. WHEN 2. prefix = '" @@'. WHEN 3. prefix = '##'. ENDCASE. = replace( val = sub = |{ prefix }IMPORT| with = 'IMPORT' case = abap_false occ = 1 ). ENDDO. ENDLOOP. ENDMETHOD. METHOD read. IF program_source IS INITIAL. SELECT COUNT(*) FROM trdir INTO @DATA(program) WHERE name = @program_name. IF program = 1. READ REPORT program_name INTO result. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error reading program { program_name }|. ENDIF. ENDIF. ELSE. result = program_source. ENDIF. ENDMETHOD. METHOD scan. DATA statements TYPE STANDARD TABLE OF sstmnt WITH KEY level struc from to number. DATA(source_code) = read( program_name = program_name program_source = program_source ). IF source_code IS INITIAL. RETURN. ENDIF. " Preprocess source to allow IMPORT as comments or pargmas source_code = prepare( source_code ). SCAN ABAP-SOURCE source_code TOKENS INTO result STATEMENTS INTO statements WITH ANALYSIS WITH COMMENTS WITHOUT TRMAC. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error scanning program { program_name }|. ENDIF. IF statements IS INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |No statements found in program { program_name }|. ENDIF. " Process statements with " - keywords (K) " - short form of method call (A) " - native SQL (E) " - type-pools (T,V) " - includes (I,J) " - compute (C) " - comments (P,S) " - macros (R,D,M) " - blank (N) " - unknown (B,U) TRY. LOOP AT statements ASSIGNING FIELD-SYMBOL() WHERE type CA 'KATC'. cl_abap_parser=>qualify_tokens( EXPORTING index_from = -from index_to = -to statement_type = -type simplified = abap_false CHANGING stokesx_tab = result ). ENDLOOP. CATCH cx_abap_parser INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error parsing code: { error->get_text( ) }|. ENDTRY. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_code_import_rules IMPLEMENTATION. METHOD check_result. LOOP AT result INTO DATA(rule). LOOP AT result FROM sy-tabix INTO DATA(rule_check) WHERE name <> rule-name AND target_package = rule-target_package. IF is_logging = abap_true. FORMAT COLOR COL_TOTAL. WRITE: / rule-old_object, AT 37 rule-target_package, AT 69 rule-new_object, AT 94 rule-name, AT 120 rule-version, AT c_width space. FORMAT COLOR COL_NEGATIVE. WRITE: / rule_check-old_object, AT 37 rule_check-target_package, AT 69 rule_check-new_object, AT 94 rule_check-name, AT 120 rule_check-version, AT c_width space. ENDIF. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Inconsistent mapping of modules to SAP packages' longtext = |Modules { rule-name } and { rule_check-name } are mapped to SAP package | && |{ rule-target_package }. Specify a different SAP package in "TO" (see documentation)|. ENDLOOP. ENDLOOP. ENDMETHOD. METHOD get. LOOP AT programs ASSIGNING FIELD-SYMBOL(). IF is_logging = abap_true. FORMAT COLOR COL_HEADING. WRITE: / 'Include:', AT c_width space. SKIP. FORMAT COLOR OFF. WRITE: / -program, AT c_width space. SKIP. ENDIF. DATA(rules) = get_import_rules( program = is_logging = is_logging ). INSERT LINES OF rules INTO TABLE result. ENDLOOP. LOOP AT result ASSIGNING FIELD-SYMBOL() WHERE old_object = '*'. -old_object = default_rule. ENDLOOP. ENDMETHOD. METHOD get_import_rules. DATA rule TYPE /apmg/if_apm_importer=>ty_rule. DATA(tokens) = /apmg/cl_apm_code_importer=>scan( program-program ). " Process all IMPORT statements " " Example 1: " " IMPORT zif_ajson TO zif_my_app_ajson FROM '@sbcgua/ajson'. " " IMPORT c " ZIF_AJSON r " TO b " ZIF_MY_APP_AJSON m " FROM b " '@sbcgua/ajson' ! " " Example 2: " " IMPORT '*' TO 'z$1_my_app$2' FROM '@sbcgua/ajson'. " " IMPORT c " '*' ! " TO b " 'z$1_my_app$2' m " FROM b " '@sbcgua/ajson' m LOOP AT tokens TRANSPORTING NO FIELDS WHERE type = 'c' AND str = 'IMPORT'. DATA(tabix) = sy-tabix. CLEAR rule. DO 5 TIMES. DATA(pos) = |(Tabix { tabix } Index { sy-index })|. READ TABLE tokens ASSIGNING FIELD-SYMBOL() INDEX tabix + sy-index. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error parsing IMPORT statement { pos }|. ENDIF. CASE sy-index. WHEN 1. " mapping from old object rule-old_object = get_old_object( token = pos = pos ). WHEN 2. IF -type <> 'b' OR -str <> 'TO'. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error parsing IMPORT statement. Expecting "TO" { pos }|. ENDIF. WHEN 3. " mapping to new object and package rule-new_object = get_new_object( token = pos = pos ). " Check if mapping includes a specific target package DATA(separator) = ''. IF rule-new_object CS ':'. separator = ':'. ENDIF. IF separator IS NOT INITIAL. SPLIT rule-new_object AT separator INTO rule-target_package rule-new_object. " Install into a sub package of where the IMPORT was found " Note: This assumes prefix folder mode " FUTURE: support full and mixed folder modes rule-parent_package = program-package. rule-target_package = |{ program-package }_{ rule-target_package }|. ENDIF. WHEN 4. IF -type <> 'b' OR -str <> 'FROM'. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error parsing IMPORT statement. Expecting "FROM" { pos }|. ENDIF. WHEN 5. " Module name rule-name = get_module_name( token = pos = pos ). " name, @scope/name, name@x.y.z, @scope/name@x.y.z, name@tag, @scope/name@tag IF rule-name+1(*) CS '@'. SPLIT rule-name AT '@' INTO rule-name rule-version. ELSE. rule-version = 'latest'. ENDIF. " Default mapping uses module name " Note: this might truncate or lead to conflicts which we check for below IF rule-target_package IS INITIAL. DATA(target_package) = to_upper( rule-name ). IF target_package(1) = '@' AND target_package CS '/'. SPLIT target_package AT '/' INTO DATA(rest) target_package ##NEEDED. ENDIF. " Install into a sub package of where the IMPORT was found " Note: This assumes prefix folder mode " FUTURE: support full and mixed folder modes rule-parent_package = program-package. rule-target_package = |{ program-package }_{ target_package }|. ENDIF. ENDCASE. ENDDO. INSERT rule INTO TABLE result. ENDLOOP. " Check that we don't have mapping of different modules to same SAP package check_result( result = result is_logging = is_logging ). IF is_logging = abap_true. FORMAT COLOR COL_NORMAL. WRITE: / 'Rules:', AT c_width space. SKIP. LOOP AT result ASSIGNING FIELD-SYMBOL(). FORMAT COLOR COL_POSITIVE. WRITE: / -old_object, AT 37 -target_package, AT 69 -new_object, AT 94 -name, AT 120 -version, AT c_width space. ENDLOOP. SKIP. FORMAT COLOR OFF. ENDIF. ENDMETHOD. METHOD get_module_name. CASE token-type. WHEN 'r'. " reference result = token-str. WHEN '!' OR 'm'. " literal result = to_lower( replace( val = token-str sub = '''' with = '' occ = 0 ) ). WHEN OTHERS. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Unknown identifier: { token-str } { pos }|. ENDCASE. IF result IS INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Initial package spec: { token-str } { pos }|. ENDIF. ENDMETHOD. METHOD get_new_object. CASE token-type. WHEN 'r'. " reference result = token-str. WHEN '!' OR 'm'. " literal result = to_upper( replace( val = token-str sub = '''' with = '' occ = 0 ) ). WHEN OTHERS. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Unknown identifier: { token-str } { pos }|. ENDCASE. IF result IS INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Initial new object: { token-str } { pos }|. ENDIF. ENDMETHOD. METHOD get_old_object. CASE token-type. WHEN 'r'. " reference result = token-str. WHEN '!' OR 'm'. " literal result = to_upper( replace( val = token-str sub = '''' with = '' occ = 0 ) ). WHEN OTHERS. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Unknown identifier: { token-str } { pos }|. ENDCASE. IF result IS INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Initial original object: { token-str } { pos }|. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_code_mapper IMPLEMENTATION. METHOD get. " Get list of original objects DATA(tadir) = get_tadir_objects( source_package = source_package object_types = object_types object_names = object_names is_logging = is_logging ). " Process all objects and apply rules LOOP AT tadir ASSIGNING FIELD-SYMBOL(). " No programs for production (just classes and interfaces) IF is_production = abap_true AND -object = 'PROG'. CONTINUE. ENDIF. DATA(map) = VALUE /apmg/if_apm_importer=>ty_map_item( source_package = source_package target_package = target_package object_type = -object old_object = -obj_name ). " Collect first matching rule DATA(found) = abap_false. DATA(import_all) = abap_false. LOOP AT rules ASSIGNING FIELD-SYMBOL() WHERE target_package = target_package. IF -old_object = /apmg/if_apm_importer=>c_default_import_rule. import_all = abap_true. ENDIF. map-new_object = -obj_name. IF -old_object = -obj_name. " Direct mapping map-new_object = -new_object. ELSE. " Regex replacement map-new_object = replace( val = -obj_name regex = -old_object with = -new_object ) ##REGEX_POSIX. " Remove first underscore after namespace: " PROG Z_TEST becomes /NAMESPACE/TEST (instead of /NAMESPACE/_TEST) map-new_object = replace( val = map-new_object regex = '(/.+/)_' with = '$1' ) ##REGEX_POSIX. ENDIF. IF map-new_object <> -obj_name. IF strlen( map-new_object ) > 30. map-new_object = map-new_object(30). IF is_logging = abap_true. WRITE: / -object, -obj_name, map-new_object, 'object name shortened to 30 characters' COLOR COL_TOTAL. DATA(warnings) = abap_true. ENDIF. ENDIF. found = abap_true. EXIT. ENDIF. ENDLOOP. IF found = abap_true. INSERT map INTO TABLE result. ELSEIF import_all = abap_true. " With IMPORT '*' we expect all objects to be mapped " Most likely, the object name became too long requiring an additional rule WRITE: / -object, -obj_name, 'no mapping rule found' COLOR COL_TOTAL. DATA(missing_rule) = abap_true. ENDIF. ENDLOOP. IF is_logging = abap_true AND ( warnings = abap_true OR missing_rule = abap_true ). SKIP. ENDIF. IF is_logging = abap_true AND result IS NOT INITIAL. FORMAT COLOR COL_NORMAL. WRITE: / 'Mapping:', AT c_width space. SKIP. FORMAT COLOR COL_POSITIVE. LOOP AT result ASSIGNING FIELD-SYMBOL(). WRITE: / -source_package, -target_package, -old_object, -new_object, AT c_width space. ENDLOOP. FORMAT COLOR OFF. SKIP. ENDIF. IF missing_rule = abap_true. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'No mapping rule for some objects'. ENDIF. ENDMETHOD. METHOD get_tadir_objects. " Get list of original objects TRY. result = zcl_abapgit_factory=>get_tadir( )->read( source_package ). CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. " Only classes and interfaces " FUTURE: support other object types DELETE result WHERE NOT ( object = 'CLAS' OR object = 'INTF' OR object = 'PROG' ). " Filter objects (for testing) DELETE result WHERE NOT ( object IN object_types AND obj_name IN object_names ). IF is_logging = abap_true AND result IS NOT INITIAL. FORMAT COLOR COL_NORMAL. WRITE: / 'Objects:' COLOR COL_NORMAL, AT c_width space. SKIP. FORMAT COLOR OFF. LOOP AT result ASSIGNING FIELD-SYMBOL(). WRITE: / -object, -obj_name. ENDLOOP. SKIP. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_command_deprecate IMPLEMENTATION. METHOD deprecate_package_version. DATA(payload) = /apmg/cl_apm_pacote=>convert_packument_to_json( packument = packument is_deprecated = abap_true ). DATA(response) = /apmg/cl_apm_command_utils=>fetch_registry( registry = registry url = |{ registry }/{ packument-name }/-rev/{ packument-_rev }| method = /apmg/if_apm_http_agent=>c_method-put payload = payload ). result = /apmg/cl_apm_command_utils=>check_response( response = response text = 'Error deprecating package version' ). ENDMETHOD. METHOD execute. " 1. Get packument from registry DATA(packument) = /apmg/cl_apm_command_utils=>get_packument_from_registry( registry = registry name = name write = abap_true ). packument = update_deprecate_message( packument = packument range = range message_text = message_text ). DATA(message) = deprecate_package_version( registry = registry packument = packument ). IF message IS NOT INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = message. ENDIF. MESSAGE 'Package version(s) deprecated successfully' TYPE 'S'. ENDMETHOD. METHOD run. DATA(command) = NEW /apmg/cl_apm_command_deprecate( ). command->execute( registry = registry name = name range = range message_text = message_text ). ENDMETHOD. METHOD update_deprecate_message. result = packument. " TODO: Log all deprecated versions LOOP AT result-versions ASSIGNING FIELD-SYMBOL(). DATA(satisfies) = /apmg/cl_apm_semver_functions=>satisfies( version = -key range = range ). IF satisfies = abap_true. " Note: Empty text will undeprecate -manifest-deprecated = message_text. ENDIF. ENDLOOP. " Remove attachments which distinguishes this update from publishing a package DELETE result-_attachments WHERE key IS NOT INITIAL. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_command_init IMPLEMENTATION. METHOD execute. " Package JSON DATA(package_json_service) = /apmg/cl_apm_package_json=>factory( package = package name = package_json-name version = package_json-version ). IF package_json_service->exists( ) = abap_true. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Package { package } is already initialized|. ENDIF. " Remove readme which is stored separately DATA(package_json_wo_readme) = package_json. CLEAR package_json_wo_readme-readme. package_json_service->set( package_json_wo_readme )->save( ). " Readme IF package_json-readme IS INITIAL. DATA(markdown) = |# { package_json-name } - { package_json-description }|. ELSE. markdown = package_json-readme. markdown = replace( val = markdown sub = '\n' with = cl_abap_char_utilities=>newline occ = 0 ). ENDIF. DATA(readme_service) = /apmg/cl_apm_readme=>factory( package = package markdown = markdown ). readme_service->save( ). MESSAGE 'Package successfully initialized' TYPE 'S'. ENDMETHOD. METHOD run. DATA(command) = NEW /apmg/cl_apm_command_init( ). command->execute( package = package package_json = package_json ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_command_install IMPLEMENTATION. METHOD check_actions. " TODO: log all warnings and errors IF actions-errors IS NOT INITIAL. DATA(text) = concat_lines_of( table = actions-errors sep = |\n| ). RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = text. ENDIF. ENDMETHOD. METHOD check_dependencies. " Get all installed packages DATA(list) = /apmg/cl_apm_package_json=>list( ). LOOP AT manifest-dependencies ASSIGNING FIELD-SYMBOL(). IF NOT line_exists( manifest-bundle_dependencies[ table_line = -key ] ). DATA(action) = check_dependency( list = list dependency = category = 'Dependency' is_force = is_force ). collect_actions( EXPORTING action = action CHANGING result = result ). ENDIF. ENDLOOP. IF is_production = abap_false. LOOP AT manifest-dev_dependencies ASSIGNING . action = check_dependency( list = list dependency = category = 'devDependency' is_force = is_force ). collect_actions( EXPORTING action = action CHANGING result = result ). ENDLOOP. ENDIF. LOOP AT manifest-optional_dependencies ASSIGNING . action = check_dependency( list = list dependency = category = 'optionalDependency' is_force = is_force is_optional = abap_true ). collect_actions( EXPORTING action = action CHANGING result = result ). ENDLOOP. LOOP AT manifest-peer_dependencies ASSIGNING . action = check_dependency( list = list dependency = category = 'peerDependency' is_force = is_force is_optional = abap_true ). collect_actions( EXPORTING action = action CHANGING result = result ). ENDLOOP. ENDMETHOD. METHOD check_dependency. READ TABLE list ASSIGNING FIELD-SYMBOL() WITH KEY name COMPONENTS name = dependency-key. IF sy-subrc = 0. DATA(satisfies) = /apmg/cl_apm_semver_functions=>satisfies( version = -version range = dependency-range ). IF satisfies = abap_false. IF is_optional = abap_true OR is_force = abap_true. result-warning = |{ category } { dependency-key } is installed in version { -version } | && |and does not satisfy { dependency-range } but is optional|. ELSE. result-invalid = dependency. result-error = |{ category } { dependency-key } is installed in version { -version } | && |but does not satisfy { dependency-range }|. ENDIF. ENDIF. ELSE. IF is_optional = abap_true OR is_force = abap_true. result-warning = |{ category } { dependency-key } is not installed but optional|. ELSE. result-missing = dependency. result-error = |{ category } { dependency-key } is not installed|. ENDIF. ENDIF. ENDMETHOD. METHOD check_package. DATA(package_json_service) = /apmg/cl_apm_package_json=>factory( package ). IF package_json_service->exists( ) = abap_true. DATA(existing_name) = package_json_service->get( )-name. IF existing_name = name. " TODO: log warning ELSE. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |{ package } already contains package "{ existing_name }"|. ENDIF. ENDIF. SELECT COUNT(*) FROM tadir INTO @DATA(count) WHERE devclass = @package. "#EC CI_SGLSELECT IF count > 1. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |{ package } already contains { count } objects|. ENDIF. ENDMETHOD. METHOD check_prerequisites. " apm version READ TABLE manifest-engines ASSIGNING FIELD-SYMBOL() WITH KEY key = 'apm'. IF sy-subrc = 0. check_semver( name = 'apm' version = /apmg/if_apm_version=>c_version range = -range category = 'Engine' is_force = is_force ). ENDIF. " abap release READ TABLE manifest-engines ASSIGNING WITH KEY key = 'abap'. IF sy-subrc = 0. DATA(abap_version) = /apmg/cl_apm_command_utils=>get_abap_version( ). check_semver( name = 'ABAP' version = abap_version range = -range category = 'Engine' is_force = is_force ). ENDIF. " TODO: Check os & cpu (requires "env" package which is =WIP=) ENDMETHOD. METHOD check_semver. DATA(satisfies) = /apmg/cl_apm_semver_functions=>satisfies( version = version range = range ). IF satisfies = abap_false. IF is_optional = abap_true OR is_force = abap_true. " TODO: Log warning ELSE. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |{ category } { name } is installed in version { version } | && |but does not satisfy { range }|. ENDIF. ENDIF. ENDMETHOD. METHOD collect_actions. IF action-missing IS NOT INITIAL. INSERT action-missing INTO TABLE result-missing. ENDIF. IF action-invalid IS NOT INITIAL. INSERT action-invalid INTO TABLE result-invalid. ENDIF. IF action-error IS NOT INITIAL. INSERT action-error INTO TABLE result-errors. ENDIF. IF action-warning IS NOT INITIAL. INSERT action-warning INTO TABLE result-warnings. ENDIF. ENDMETHOD. METHOD execute. DATA package_json_init TYPE /apmg/if_apm_types=>ty_package_json. " 1. Check if something else is already installed check_package( package = package name = package_json-name ). " 2. Get manifest DATA(manifest) = /apmg/cl_apm_command_utils=>get_manifest_from_registry( registry = registry name = package_json-name version = package_json-version ). " 3. Check prerequisites (os, cpu, engines) check_prerequisites( manifest = manifest is_force = is_force ). " TODO!: Instead of just checking if dependencies are installed, it should install them. " For that to happen, we need arborist to build the dependency tree and pass it here. " This needs to include the target SAP package for each dependency :-) " 4. Check dependencies (not recursive!) DATA(actions) = check_dependencies( manifest = manifest is_force = is_force ). check_actions( actions ). " TODO: 4. Get dependencies " TODO: 5. Install dependencies " 6. Get tarball from registry and install it into package /apmg/cl_apm_command_installer=>install_package( registry = registry manifest = manifest package = package name = package_json-name version = package_json-version is_production = is_production ). " 7. Save package.abap.json and readme package_json_init = CORRESPONDING #( manifest ). /apmg/cl_apm_command_init=>run( package = package package_json = package_json_init ). MESSAGE 'Package successfully installed' TYPE 'S'. ENDMETHOD. METHOD run. DATA(command) = NEW /apmg/cl_apm_command_install( ). command->execute( registry = registry package = package package_json = package_json is_production = is_production is_force = is_force is_dry_run = is_dry_run ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_command_installer IMPLEMENTATION. METHOD install_package. " TODO: Currently hardcoded to local packages (no transport) DATA transport TYPE trkorr. DATA(tarball) = /apmg/cl_apm_command_utils=>get_tarball_from_registry( registry = registry name = name tarball = manifest-dist-tarball ). /apmg/cl_apm_command_integrity=>check_integrity( tarball = tarball dist = manifest-dist ). " FUTURE: Allow other folder logic than prefix /apmg/cl_apm_installer=>install( name = name version = version data = tarball package = package transport = transport enum_source = /apmg/cl_apm_installer=>c_enum_source-registry enum_folder_logic = /apmg/cl_apm_installer=>c_enum_folder_logic-prefix is_production = is_production ). ENDMETHOD. METHOD uninstall_package. /apmg/cl_apm_installer=>uninstall( name = name version = version package = package ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_command_integrity IMPLEMENTATION. METHOD calc_sha1. TRY. " Simple Shasum cl_abap_hmac=>calculate_hmac_for_raw( EXPORTING if_algorithm = 'SHA1' if_key = c_initial_key if_data = data IMPORTING ef_hmacstring = DATA(sha1) ). result = to_lower( sha1 ). CATCH cx_abap_message_digest INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD calc_sha512. " Integrity Checksum (sha512) " https://www.npmjs.com/package/ssri " Note: It's not clear which ABAP kernel version is required for this TRY. cl_abap_hmac=>calculate_hmac_for_raw( EXPORTING if_algorithm = 'SHA512' if_key = c_initial_key if_data = data IMPORTING ef_hmacb64string = DATA(sha512) ). result = |sha512-{ sha512 }|. CATCH cx_abap_message_digest INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD check_integrity. DATA(shasum) = calc_sha1( tarball ). IF shasum <> dist-shasum. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Checksum error for tarball (sha1)'. ENDIF. DATA(integrity) = calc_sha512( tarball ). IF integrity <> dist-integrity. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Checksum error for tarball (sha512)'. ENDIF. ENDMETHOD. METHOD get_integrity. result = VALUE #( shasum = calc_sha1( tarball ) integrity = calc_sha512( tarball ) ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_command_login IMPLEMENTATION. METHOD execute. DATA(login_request) = VALUE ty_request( name = username password = password ). DATA(payload) = /apmg/cl_apm_json=>to_string( login_request ). " Send login request DATA(response) = /apmg/cl_apm_command_utils=>fetch_registry( command = 'login' registry = registry url = |{ registry }/-/user/org.couchdb.user:{ username }| method = /apmg/if_apm_http_agent=>c_method-put payload = payload auth_type = auth_type username = username password = password ). DATA(message) = /apmg/cl_apm_command_utils=>check_response( response = response text = 'Login error' ). DATA(login_response) = VALUE ty_response( ). /apmg/cl_apm_json=>to_abap( EXPORTING json = response->cdata( ) CHANGING result = login_response ). " Set token for subsequent requests (overwrites basic authentication) /apmg/cl_apm_http_login_manage=>set( host = registry username = username password = login_response-token is_basic = abap_false ). IF message IS INITIAL. MESSAGE login_response-ok TYPE 'S'. ELSE. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = message. ENDIF. ENDMETHOD. METHOD run. DATA(command) = NEW /apmg/cl_apm_command_login( ). command->execute( registry = registry username = username password = password auth_type = auth_type ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_command_publish IMPLEMENTATION. METHOD attach_object_list. " Include a list of all objects which the registry will compare against the global object " directory (GTADIR) to avoid name conflicts with other packages LOOP AT files ASSIGNING FIELD-SYMBOL(). DATA(item) = |{ -item-obj_type },{ -item-obj_name }|. COLLECT item INTO packument-_objects. ENDLOOP. ENDMETHOD. METHOD attach_tarball. DATA(tarball) = tar->gzip( tar->save( ) ). DATA(dist) = /apmg/cl_apm_command_integrity=>get_integrity( tarball ). DATA(package_name) = /apmg/cl_apm_command_utils=>get_package_from_name( packument-name ). DATA(filename) = |{ package_name }-{ version }.tgz|. dist-file_count = tar->file_count( ). dist-unpacked_size = tar->unpacked_size( ). dist-tarball = |{ registry }/{ packument-name }/-/{ filename }|. packument-versions[ key = version ]-manifest-dist = dist. DATA(attachment) = VALUE /apmg/if_apm_types=>ty_attachment( key = filename tarball-content_type = /apmg/if_apm_http_agent=>c_content_type-bin tarball-data = cl_http_utility=>encode_x_base64( tarball ) tarball-length = xstrlen( tarball ) ). INSERT attachment INTO TABLE packument-_attachments. ENDMETHOD. METHOD check_package. DATA(package_json_service) = /apmg/cl_apm_package_json=>factory( package ). IF package_json_service->exists( ) = abap_false. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |{ package } does not exist or is not initialized|. ENDIF. ENDMETHOD. METHOD check_packument. IF line_exists( packument-versions[ key = package_json-version ] ). RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Version { package_json-version } already published|. ENDIF. ENDMETHOD. METHOD execute. " 1. Check if package exists and is initialized check_package( package ). " 2. Get package.abap.json and readme DATA(package_json) = get_package_json( package ). IF package_json-private = abap_true. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Private packages can not be published'. ENDIF. " 3. Get packument from registry TRY. DATA(packument) = /apmg/cl_apm_command_utils=>get_packument_from_registry( registry = registry name = package_json-name write = abap_true ). CATCH /apmg/cx_apm_error ##NO_HANDLER. " ignore if not found ENDTRY. " 4. Check if version already exist in registry check_packument( packument = packument package_json = package_json ). " 5. Initialize packument for publishing DATA(packument_publish) = init_package( packument = packument package_json = package_json ). " 6. Serialize all objects of package DATA(files) = serialize_package( package ). " 7. Attach object list to packument attach_object_list( EXPORTING files = files CHANGING packument = packument_publish ). " 8. Get tarball DATA(tar) = get_tar( package_json = package_json files = files ). " 9. Attach tarball to packument attach_tarball( EXPORTING registry = registry version = package_json-version tar = tar CHANGING packument = packument_publish ). " 10. Publish package to registry DATA(message) = publish_package( registry = registry packument = packument_publish ). IF message IS INITIAL. MESSAGE 'Package successfully published' TYPE 'S'. ELSE. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = message. ENDIF. ENDMETHOD. METHOD get_package_json. result = /apmg/cl_apm_package_json=>factory( package )->load( )->get( ). result-readme = /apmg/cl_apm_readme=>factory( package )->load( )->get( ). ENDMETHOD. METHOD get_tar. " TODO: Move this and all called methods to local part of class CONSTANTS c_null TYPE xstring VALUE ''. " 2. Tar and gzip files DATA(tar) = /apmg/cl_apm_tar=>new( ). LOOP AT files ASSIGNING FIELD-SYMBOL(). AT NEW file-path. IF -file-path <> '/'. tar->append( name = |{ -file-path+1 }| content = c_null typeflag = /apmg/cl_apm_tar=>c_typeflag-directory ). ENDIF. ENDAT. IF -file-path = '/'. DATA(name) = -file-filename. ELSE. name = |{ -file-path+1 }{ -file-filename }|. ENDIF. tar->append( name = name content = -file-data ). ENDLOOP. " 3. Add package.json and readme DATA(manifest) = CORRESPONDING /apmg/if_apm_types=>ty_manifest( package_json ). DATA(json) = /apmg/cl_apm_package_json=>convert_manifest_to_json( manifest = manifest is_package_json = abap_true ). TRY. tar->append( name = CONV string( /apmg/if_apm_types=>c_package_json_file ) content = zcl_abapgit_convert=>string_to_xstring_utf8( json ) ). tar->append( name = /apmg/if_apm_types=>c_readme_file content = zcl_abapgit_convert=>string_to_xstring_utf8( package_json-readme ) ). CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. result = tar. ENDMETHOD. METHOD init_package. CONSTANTS c_latest TYPE string VALUE 'latest'. IF packument IS INITIAL. result = CORRESPONDING #( package_json ). result-_id = package_json-name. ELSE. result = packument. ENDIF. CLEAR: result-_rev, result-author, result-bugs, result-dist_tags, result-homepage, result-icon, result-keywords, result-license, result-maintainers, result-readme, result-repository, result-time, result-users, result-versions. " Update LATEST dist-tag " TODO: Allow publishing with other tags DATA(dist_tag) = VALUE /apmg/if_apm_types=>ty_generic( key = c_latest value = package_json-version ). INSERT dist_tag INTO TABLE result-dist_tags. " Add new version DATA(version) = VALUE /apmg/if_apm_types=>ty_version_manifest( key = package_json-version ). version-manifest = CORRESPONDING #( package_json ). version-manifest-_id = |{ package_json-name }@{ package_json-version }|. version-manifest-_abap_version = /apmg/cl_apm_command_utils=>get_abap_version( ). version-manifest-_apm_version = /apmg/if_apm_version=>c_version. INSERT version INTO TABLE result-versions. ENDMETHOD. METHOD publish_package. DATA(json) = /apmg/cl_apm_pacote=>convert_packument_to_json( packument ). DATA(response) = /apmg/cl_apm_command_utils=>fetch_registry( registry = registry url = |{ registry }/{ packument-name }| method = /apmg/if_apm_http_agent=>c_method-put payload = json ). result = /apmg/cl_apm_command_utils=>check_response( response = response text = 'Error publishing package' ). ENDMETHOD. METHOD run. DATA(command) = NEW /apmg/cl_apm_command_publish( ). command->execute( registry = registry package = package is_dry_run = is_dry_run ). ENDMETHOD. METHOD serialize_package. TRY. DATA(logger) = NEW zcl_abapgit_log( ). DATA(local_settings) = VALUE zif_abapgit_persistence=>ty_local_settings( ignore_subpackages = abap_false only_local_objects = abap_false ). " Hardcoded to prefix folder logic and /src/ starting folder " TODO!: Support full and mixed folder logic DATA(dot_abapgit) = zcl_abapgit_dot_abapgit=>build_default( ). DATA(serializer) = NEW /apmg/cl_apm_abapgit_serialize( io_dot_abapgit = dot_abapgit is_local_settings = local_settings ). result = serializer->files_local( iv_package = package ii_log = logger ). SORT result BY file-path file-filename. CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_command_uninstall IMPLEMENTATION. METHOD check_package. DATA(package_json_service) = /apmg/cl_apm_package_json=>factory( package ). IF package_json_service->exists( ) = abap_false. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Package { package } not found|. ENDIF. result = package_json_service->get( ). ENDMETHOD. METHOD execute. DATA(package_json) = check_package( package ). /apmg/cl_apm_command_installer=>uninstall_package( name = package_json-name version = package_json-version package = package ). MESSAGE 'Package successfully uninstalled' TYPE 'S'. ENDMETHOD. METHOD run. DATA(command) = NEW /apmg/cl_apm_command_uninstall( ). command->execute( package ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_command_unpublish IMPLEMENTATION. METHOD delete_tarball. DATA(response) = /apmg/cl_apm_command_utils=>fetch_registry( registry = registry url = |{ tarball }/-rev/{ packument-_rev }| method = /apmg/if_apm_http_agent=>c_method-delete ). result = /apmg/cl_apm_command_utils=>check_response( response = response text = 'Error deleting tarball' ). ENDMETHOD. METHOD execute. " 1. Get packument from registry DATA(packument) = /apmg/cl_apm_command_utils=>get_packument_from_registry( registry = registry name = name write = abap_true ). IF version IS INITIAL. " 2a. Delete complete package DATA(message) = unpublish_complete_package( registry = registry packument = packument ). IF message IS NOT INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = message. ENDIF. MESSAGE 'Complete package unpublished successfully' TYPE 'S'. ELSE. " 2b. Get tarball name DATA(tarball) = get_tarball( packument = packument version = version ). " 3. Remove version from packument packument = remove_version( packument = packument version = version tarball = tarball ). " 4. Update LATEST dist-tag (and others) packument = update_dist_tags( packument = packument version = version ). " 5. Unpublish package from registry message = unpublish_package_version( registry = registry packument = packument ). IF message IS NOT INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = message. ENDIF. " 6. Delete tarball from registry message = delete_tarball( registry = registry packument = packument tarball = tarball ). IF message IS NOT INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = message. ENDIF. MESSAGE 'Package version unpublished successfully' TYPE 'S'. ENDIF. ENDMETHOD. METHOD get_tarball. READ TABLE packument-versions ASSIGNING FIELD-SYMBOL() WITH KEY key = version. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Version { version } does not exist in package { packument-name }|. ENDIF. result = -manifest-dist-tarball. ENDMETHOD. METHOD remove_version. result = packument. DELETE result-versions WHERE key = version. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Version { version } does not exist in package { packument-name }|. ENDIF. IF lines( result-versions ) = 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Version { version } is the last version of package { packument-name }. You may unpublish the complete package, instead|. ENDIF. DELETE result-time WHERE key = version ##SUBRC_OK. DELETE result-_attachments WHERE key = tarball. ENDMETHOD. METHOD run. DATA(command) = NEW /apmg/cl_apm_command_unpublish( ). command->execute( registry = registry name = name version = version ). ENDMETHOD. METHOD unpublish_complete_package. DATA(response) = /apmg/cl_apm_command_utils=>fetch_registry( registry = registry url = |{ registry }/{ packument-name }/-rev/{ packument-_rev }| method = /apmg/if_apm_http_agent=>c_method-delete ). result = /apmg/cl_apm_command_utils=>check_response( response = response text = 'Error unpublishing complete package' ). ENDMETHOD. METHOD unpublish_package_version. DATA(payload) = /apmg/cl_apm_pacote=>convert_packument_to_json( packument ). DATA(response) = /apmg/cl_apm_command_utils=>fetch_registry( registry = registry url = |{ registry }/{ packument-name }/-rev/{ packument-_rev }| method = /apmg/if_apm_http_agent=>c_method-put payload = payload ). result = /apmg/cl_apm_command_utils=>check_response( response = response text = 'Error unpublishing package version' ). ENDMETHOD. METHOD update_dist_tags. " TODO DATA(ver) = version ##NEEDED. result = packument. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_command_update IMPLEMENTATION. METHOD execute. " 1. Check package is installed and get version details DATA(package_json) = get_package( package ). " 2. Get updated manifest DATA(manifest) = /apmg/cl_apm_command_utils=>get_manifest_from_registry( registry = registry name = package_json-name version = package_json-version ). " 3. Update the package DATA(is_newer) = is_newer( version_installed = package_json-version version_next = manifest-version ). " 4. Get vendored dependencies DATA(dependencies) = get_bundle_dependencies( package = package manifest = manifest ). " 5. Add, remove, or update dependencies DATA(import_dependencies) = process_dependencies( registry = registry dependencies = dependencies ). " 6. Import dependencies /apmg/cl_apm_importer=>run( package = package dependencies = import_dependencies is_dry_run = is_dry_run is_production = is_production is_logging = abap_false ). " 7. Update package IF is_newer = abap_true. /apmg/cl_apm_command_installer=>install_package( registry = registry manifest = manifest package = package name = manifest-name version = manifest-version is_production = is_production ). ENDIF. " 8. Save package to apm save_package( package = package manifest = manifest ). MESSAGE 'Package and dependencies successfully updated' TYPE 'S'. ENDMETHOD. METHOD get_bundle_dependencies. " XXX: Major rewrite " Get all installed packages DATA(list) = /apmg/cl_apm_package_json=>list( instanciate = abap_true ). " Get all sub packages where a dependency could be installed DATA(sub_packages) = zcl_abapgit_factory=>get_sap_package( package )->list_subpackages( ). LOOP AT sub_packages ASSIGNING FIELD-SYMBOL(). READ TABLE list ASSIGNING FIELD-SYMBOL() WITH KEY package COMPONENTS package = . IF sy-subrc = 0. IF line_exists( manifest-bundle_dependencies[ table_line = -name ] ). READ TABLE manifest-dependencies ASSIGNING FIELD-SYMBOL() WITH KEY key = -name. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Bundle dependency { -name } missing from dependencies|. ENDIF. " Installed bundle dependency which will be updated (if available) DATA(dependency) = VALUE /apmg/if_apm_importer=>ty_dependency( name = -name version = -version package = range = -range action = /apmg/if_apm_importer=>c_action-update ). ELSE. " Dependency which is not bundled (anymore) and will be removed dependency = VALUE /apmg/if_apm_importer=>ty_dependency( name = -name version = -version package = action = /apmg/if_apm_importer=>c_action-remove ). ENDIF. INSERT dependency INTO TABLE result. ENDIF. ENDLOOP. LOOP AT manifest-bundle_dependencies ASSIGNING FIELD-SYMBOL(). IF NOT line_exists( result[ name = ] ). IF NOT line_exists( manifest-dependencies[ key = ] ). RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Bundle dependency { } missing from dependencies|. ENDIF. " New bundle dependency which will be added dependency = VALUE /apmg/if_apm_importer=>ty_dependency( name = range = -range action = /apmg/if_apm_importer=>c_action-add ). INSERT dependency INTO TABLE result. ENDIF. ENDLOOP. ENDMETHOD. METHOD get_max_satisfying_version. DATA versions TYPE string_table. DATA(packument) = /apmg/cl_apm_command_utils=>get_packument_from_registry( registry = registry name = name ). LOOP AT packument-versions ASSIGNING FIELD-SYMBOL(). INSERT -key INTO TABLE versions. ENDLOOP. result = /apmg/cl_apm_semver_ranges=>max_satisfying( versions = versions range = range ). ENDMETHOD. METHOD get_package. DATA(package_json_service) = /apmg/cl_apm_package_json=>factory( package ). IF package_json_service->exists( ) = abap_true. result = package_json_service->get( ). ELSE. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |{ package } does not contain any installed package|. ENDIF. ENDMETHOD. METHOD is_newer. " Is the next version greater than the installed version? result = /apmg/cl_apm_semver_functions=>gt( a = version_next b = version_installed ). ENDMETHOD. METHOD process_dependencies. " Install new or update existing dependencies LOOP AT dependencies ASSIGNING FIELD-SYMBOL() WHERE action <> /apmg/if_apm_importer=>c_action-remove. DATA(max_version) = get_max_satisfying_version( registry = registry name = -name range = -range ). IF max_version IS INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |No matching version found for package { -name }| && | and range { -range }|. ENDIF. CASE -action. WHEN /apmg/if_apm_importer=>c_action-add. -version = max_version. INSERT INTO TABLE result. WHEN /apmg/if_apm_importer=>c_action-update. DATA(is_newer) = is_newer( version_installed = -version version_next = max_version ). IF is_newer = abap_true. " Update to this version -version = max_version. INSERT INTO TABLE result. ENDIF. ENDCASE. ENDLOOP. " Uninstall removed dependencies LOOP AT dependencies ASSIGNING WHERE action = /apmg/if_apm_importer=>c_action-remove. /apmg/cl_apm_command_installer=>uninstall_package( name = -name version = -version package = -package ). ENDLOOP. ENDMETHOD. METHOD run. DATA(command) = NEW /apmg/cl_apm_command_update( ). command->execute( registry = registry package = package is_dry_run = is_dry_run is_production = is_production ). ENDMETHOD. METHOD save_package. DATA(package_json_service) = /apmg/cl_apm_package_json=>factory( package ). DATA(package_json) = CORRESPONDING /apmg/if_apm_types=>ty_package_json( manifest ). package_json_service->set( package_json )->save( ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_command_utils IMPLEMENTATION. METHOD check_response. IF response->is_ok( ) = abap_false. result = |{ text } ({ response->code( ) }): { get_error( response->error( ) ) }|. ENDIF. ENDMETHOD. METHOD fetch_registry. DATA(headers) = /apmg/cl_apm_string_map=>create( ). IF command IS NOT INITIAL. headers->set( iv_key = c_header-apm_command iv_val = command ). ENDIF. IF auth_type IS NOT INITIAL. headers->set( iv_key = c_header-apm_auth_type iv_val = auth_type ). ENDIF. IF username IS NOT INITIAL AND password IS NOT INITIAL. /apmg/cl_apm_http_login_manage=>set( host = registry username = username password = password ). ENDIF. /apmg/cl_apm_trace=>cdata( |{ method } { url }\n\n{ payload }| ). result = get_agent( registry )->request( url = url headers = headers method = method payload = payload ). /apmg/cl_apm_trace=>cdata( |{ result->code( ) }\n\n{ result->cdata( ) }| ). ENDMETHOD. METHOD get_abap_version. TRY. result = NEW /apmg/cl_apm_semver_sap( )->sap_component_to_semver( 'SAP_BASIS' ). CATCH cx_abap_invalid_value INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD get_agent. result = /apmg/cl_apm_http_agent=>create( ). result->global_headers( )->set( iv_key = /apmg/if_apm_http_agent=>c_header-accept iv_val = /apmg/if_apm_http_agent=>c_content_type-json ). result->global_headers( )->set( iv_key = /apmg/if_apm_http_agent=>c_header-content_type iv_val = /apmg/if_apm_http_agent=>c_content_type-json ). " TODO: Add OS and DB result->global_headers( )->set( iv_key = /apmg/if_apm_http_agent=>c_header-user_agent iv_val = |apm/{ /apmg/if_apm_version=>c_version } abap/{ get_abap_version( ) }| ). DATA(url) = /apmg/cl_apm_url=>parse( host )->components. " Authorization token DATA(token) = /apmg/cl_apm_http_login_manage=>get( url-host ). IF token IS NOT INITIAL. result->global_headers( )->set( iv_key = /apmg/if_apm_http_agent=>c_header-authorization iv_val = token ). ENDIF. ENDMETHOD. METHOD get_error. CHECK response IS NOT INITIAL. IF response(1) <> '{'. result = response. ELSE. result = /apmg/cl_apm_json=>get( json = response path = '/error' ). ENDIF. IF result IS NOT INITIAL. result = to_upper( result(1) ) && result+1. ENDIF. ENDMETHOD. METHOD get_manifest_from_registry. " The abbreviated manifest would be sufficient for installer " however we also want to get the description and readme DATA(pacote) = /apmg/cl_apm_pacote=>factory( registry = registry name = name ). DATA(json) = pacote->manifest( version = version write = write ). result = /apmg/cl_apm_package_json=>convert_json_to_manifest( json ). ENDMETHOD. METHOD get_name_from_scope_package. IF scope IS INITIAL. result = package. ELSE. result = |{ scope }/{ package }|. ENDIF. ENDMETHOD. METHOD get_package_from_name. IF name(1) = '@' AND name CA '/'. result = substring_after( val = name sub = '/' ). ELSE. result = name. ENDIF. ENDMETHOD. METHOD get_packument_from_registry. " The abbreviated manifest would be sufficient for installer " however we also want to get the description and readme DATA(pacote) = /apmg/cl_apm_pacote=>factory( registry = registry name = name ). pacote->packument( write ). result = pacote->get( ). ENDMETHOD. METHOD get_scope_from_name. IF name(1) = '@' AND name CA '/'. result = substring_before( val = name sub = '/' ). ELSE. result = ''. ENDIF. ENDMETHOD. METHOD get_tarball_from_registry. result = /apmg/cl_apm_pacote=>factory( registry = registry name = name )->tarball( tarball ). ENDMETHOD. METHOD get_versions_from_packument. LOOP AT packument-versions ASSIGNING FIELD-SYMBOL(). INSERT -key INTO TABLE result. ENDLOOP. result = /apmg/cl_apm_semver_functions=>sort( result ). ENDMETHOD. ENDCLASS. ************************************************************************ * ABAP Emoji * * Copyright 2024 apm.to Inc. * SPDX-License-Identifier: MIT ************************************************************************ * Unicode Emoji provided by GitHub (v8, 2025-04-05) * Count: 1935 * * API for JSON: https://api.github.com/emojis * Base URL: https://github.githubassets.com/images/icons/emoji/ CLASS lcl_github_emoji DEFINITION. PUBLIC SECTION. CLASS-METHODS get RETURNING VALUE(result) TYPE /apmg/cl_apm_emoji=>ty_emojis. PRIVATE SECTION. CLASS-METHODS list RETURNING VALUE(result) TYPE string_table. CLASS-METHODS unicode_to_utf16_string IMPORTING codepoint TYPE string RETURNING VALUE(result) TYPE string. ENDCLASS. CLASS lcl_github_emoji IMPLEMENTATION. METHOD get. DATA emoji LIKE LINE OF result. DATA(list) = list( ). LOOP AT list ASSIGNING FIELD-SYMBOL(). CLEAR emoji. SPLIT AT ':' INTO emoji-name emoji-img. DATA(charcodes) = substring_after( val = emoji-img sub = '/' ). SPLIT charcodes AT '-' INTO TABLE DATA(codes). IF lines( codes ) = 1. emoji-hex = to_lower( charcodes ). ENDIF. LOOP AT codes INTO DATA(charcode). emoji-code = emoji-code && unicode_to_utf16_string( to_upper( charcode ) ). ENDLOOP. emoji-img = emoji-img && '.png'. INSERT emoji INTO TABLE result. ENDLOOP. ENDMETHOD. METHOD unicode_to_utf16_string. CONSTANTS: x10000 TYPE x LENGTH 4 VALUE '00010000', xffff TYPE x LENGTH 2 VALUE 'FFFF', xd800 TYPE x LENGTH 2 VALUE 'D800', xdc00 TYPE x LENGTH 2 VALUE 'DC00', x0400 TYPE x LENGTH 2 VALUE '0400', x7f TYPE x LENGTH 1 VALUE '7F'. TYPES ty_four_bytes TYPE x LENGTH 4. CHECK strlen( codepoint ) BETWEEN 1 AND 8. " Convert the Unicode code point string to an integer DATA(code_string) = codepoint. DO 8 - strlen( codepoint ) TIMES. code_string = '0' && code_string. ENDDO. DATA(code_x) = CONV ty_four_bytes( code_string ). DATA(code_i) = CONV i( code_x ). IF code_i > xffff. " Calculate high and low surrogate DATA(high_surrogate) = CONV xstring( xd800 + ( code_i - x10000 ) DIV x0400 ). DATA(low_surrogate) = CONV xstring( xdc00 + ( code_i - x10000 ) MOD x0400 ). DATA(utf16) = CONV xstring( high_surrogate && low_surrogate ). ELSEIF code_i > x7f. " Directly convert for non-surrogate values utf16 = code_x+2(2). ELSE. " Ignore ASCII RETURN. ENDIF. result = cl_binary_convert=>xstring_utf16be_to_string( utf16 ). ENDMETHOD. METHOD list. DATA(list) = `+1:unicode/1f44d,` && `-1:unicode/1f44e,` && `100:unicode/1f4af,` && `1234:unicode/1f522,` && `1st_place_medal:unicode/1f947,` && `2nd_place_medal:unicode/1f948,` && `3rd_place_medal:unicode/1f949,` && `8ball:unicode/1f3b1,` && `a:unicode/1f170,` && `ab:unicode/1f18e,` && `abacus:unicode/1f9ee,` && `abc:unicode/1f524,` && `abcd:unicode/1f521,` && `accept:unicode/1f251,` && `accessibility:accessibility,` && `accordion:unicode/1fa97,` && `adhesive_bandage:unicode/1fa79,` && `adult:unicode/1f9d1,` && `aerial_tramway:unicode/1f6a1,` && `afghanistan:unicode/1f1e6-1f1eb,` && `airplane:unicode/2708,` && `aland_islands:unicode/1f1e6-1f1fd,` && `alarm_clock:unicode/23f0,` && `albania:unicode/1f1e6-1f1f1,` && `alembic:unicode/2697,` && `algeria:unicode/1f1e9-1f1ff,` && `alien:unicode/1f47d,` && `ambulance:unicode/1f691,` && `american_samoa:unicode/1f1e6-1f1f8,` && `amphora:unicode/1f3fa,` && `anatomical_heart:unicode/1fac0,` && `anchor:unicode/2693,` && `andorra:unicode/1f1e6-1f1e9,` && `angel:unicode/1f47c,` && `anger:unicode/1f4a2,` && `angola:unicode/1f1e6-1f1f4,` && `angry:unicode/1f620,` && `anguilla:unicode/1f1e6-1f1ee,` && `anguished:unicode/1f627,` && `ant:unicode/1f41c,` && `antarctica:unicode/1f1e6-1f1f6,` && `antigua_barbuda:unicode/1f1e6-1f1ec,` && `apple:unicode/1f34e,` && `aquarius:unicode/2652,` && `argentina:unicode/1f1e6-1f1f7,` && `aries:unicode/2648,` && `armenia:unicode/1f1e6-1f1f2,` && `arrow_backward:unicode/25c0,` && `arrow_double_down:unicode/23ec,` && `arrow_double_up:unicode/23eb,` && `arrow_down:unicode/2b07,` && `arrow_down_small:unicode/1f53d,` && `arrow_forward:unicode/25b6,` && `arrow_heading_down:unicode/2935,` && `arrow_heading_up:unicode/2934,` && `arrow_left:unicode/2b05,` && `arrow_lower_left:unicode/2199,` && `arrow_lower_right:unicode/2198,` && `arrow_right:unicode/27a1,` && `arrow_right_hook:unicode/21aa,` && `arrow_up:unicode/2b06,` && `arrow_up_down:unicode/2195,` && `arrow_up_small:unicode/1f53c,` && `arrow_upper_left:unicode/2196,` && `arrow_upper_right:unicode/2197,` && `arrows_clockwise:unicode/1f503,` && `arrows_counterclockwise:unicode/1f504,` && `art:unicode/1f3a8,` && `articulated_lorry:unicode/1f69b,` && `artificial_satellite:unicode/1f6f0,` && `artist:unicode/1f9d1-1f3a8,` && `aruba:unicode/1f1e6-1f1fc,` && `ascension_island:unicode/1f1e6-1f1e8,` && `asterisk:unicode/002a-20e3,` && `astonished:unicode/1f632,` && `astronaut:unicode/1f9d1-1f680,` && `athletic_shoe:unicode/1f45f,` && `atm:unicode/1f3e7,` && `atom:atom,` && `atom_symbol:unicode/269b,` && `australia:unicode/1f1e6-1f1fa,` && `austria:unicode/1f1e6-1f1f9,` && `auto_rickshaw:unicode/1f6fa,` && `avocado:unicode/1f951,` && `axe:unicode/1fa93,` && `azerbaijan:unicode/1f1e6-1f1ff,` && `b:unicode/1f171,` && `baby:unicode/1f476,` && `baby_bottle:unicode/1f37c,` && `baby_chick:unicode/1f424,` && `baby_symbol:unicode/1f6bc,` && `back:unicode/1f519,` && `bacon:unicode/1f953,` && `badger:unicode/1f9a1,` && `badminton:unicode/1f3f8,` && `bagel:unicode/1f96f,` && `baggage_claim:unicode/1f6c4,` && `baguette_bread:unicode/1f956,` && `bahamas:unicode/1f1e7-1f1f8,` && `bahrain:unicode/1f1e7-1f1ed,` && `balance_scale:unicode/2696,` && `bald_man:unicode/1f468-1f9b2,` && `bald_woman:unicode/1f469-1f9b2,` && `ballet_shoes:unicode/1fa70,` && `balloon:unicode/1f388,` && `ballot_box:unicode/1f5f3,` && `ballot_box_with_check:unicode/2611,` && `bamboo:unicode/1f38d,` && `banana:unicode/1f34c,` && `bangbang:unicode/203c,` && `bangladesh:unicode/1f1e7-1f1e9,` && `banjo:unicode/1fa95,` && `bank:unicode/1f3e6,` && `bar_chart:unicode/1f4ca,` && `barbados:unicode/1f1e7-1f1e7,` && `barber:unicode/1f488,` && `baseball:unicode/26be,` && `basecamp:basecamp,` && `basecampy:basecampy,` && `basket:unicode/1f9fa,` && `basketball:unicode/1f3c0,` && `basketball_man:unicode/26f9-2642,` && `basketball_woman:unicode/26f9-2640,` && `bat:unicode/1f987,` && `bath:unicode/1f6c0,` && `bathtub:unicode/1f6c1,` && `battery:unicode/1f50b,` && `beach_umbrella:unicode/1f3d6,` && `beans:unicode/1fad8,` && `bear:unicode/1f43b,` && `bearded_person:unicode/1f9d4,` && `beaver:unicode/1f9ab,` && `bed:unicode/1f6cf,` && `bee:unicode/1f41d,` && `beer:unicode/1f37a,` && `beers:unicode/1f37b,` && `beetle:unicode/1fab2,` && `beginner:unicode/1f530,` && `belarus:unicode/1f1e7-1f1fe,` && `belgium:unicode/1f1e7-1f1ea,` && `belize:unicode/1f1e7-1f1ff,` && `bell:unicode/1f514,` && `bell_pepper:unicode/1fad1,` && `bellhop_bell:unicode/1f6ce,` && `benin:unicode/1f1e7-1f1ef,` && `bento:unicode/1f371,` && `bermuda:unicode/1f1e7-1f1f2,` && `beverage_box:unicode/1f9c3,` && `bhutan:unicode/1f1e7-1f1f9,` && `bicyclist:unicode/1f6b4,` && `bike:unicode/1f6b2,` && `biking_man:unicode/1f6b4-2642,` && `biking_woman:unicode/1f6b4-2640,` && `bikini:unicode/1f459,` && `billed_cap:unicode/1f9e2,` && `biohazard:unicode/2623,` && `bird:unicode/1f426,` && `birthday:unicode/1f382,` && `bison:unicode/1f9ac,` && `biting_lip:unicode/1fae6,` && `black_bird:unicode/1f426-2b1b,` && `black_cat:unicode/1f408-2b1b,` && `black_circle:unicode/26ab,` && `black_flag:unicode/1f3f4,` && `black_heart:unicode/1f5a4,` && `black_joker:unicode/1f0cf,` && `black_large_square:unicode/2b1b,` && `black_medium_small_square:unicode/25fe,` && `black_medium_square:unicode/25fc,` && `black_nib:unicode/2712,` && `black_small_square:unicode/25aa,` && `black_square_button:unicode/1f532,` && `blond_haired_man:unicode/1f471-2642,` && `blond_haired_person:unicode/1f471,` && `blond_haired_woman:unicode/1f471-2640,` && `blonde_woman:unicode/1f471-2640,` && `blossom:unicode/1f33c,` && `blowfish:unicode/1f421,` && `blue_book:unicode/1f4d8,` && `blue_car:unicode/1f699,` && `blue_heart:unicode/1f499,` && `blue_square:unicode/1f7e6,` && `blueberries:unicode/1fad0,` && `blush:unicode/1f60a,` && `boar:unicode/1f417,` && `boat:unicode/26f5,` && `bolivia:unicode/1f1e7-1f1f4,` && `bomb:unicode/1f4a3,` && `bone:unicode/1f9b4,` && `book:unicode/1f4d6,` && `bookmark:unicode/1f516,` && `bookmark_tabs:unicode/1f4d1,` && `books:unicode/1f4da,` && `boom:unicode/1f4a5,` && `boomerang:unicode/1fa83,` && `boot:unicode/1f462,` && `bosnia_herzegovina:unicode/1f1e7-1f1e6,` && `botswana:unicode/1f1e7-1f1fc,` && `bouncing_ball_man:unicode/26f9-2642,` && `bouncing_ball_person:unicode/26f9,` && `bouncing_ball_woman:unicode/26f9-2640,` && `bouquet:unicode/1f490,` && `bouvet_island:unicode/1f1e7-1f1fb,` && `bow:unicode/1f647,` && `bow_and_arrow:unicode/1f3f9,` && `bowing_man:unicode/1f647-2642,` && `bowing_woman:unicode/1f647-2640,` && `bowl_with_spoon:unicode/1f963,` && `bowling:unicode/1f3b3,` && `bowtie:bowtie,` && `boxing_glove:unicode/1f94a,` && `boy:unicode/1f466,` && `brain:unicode/1f9e0,` && `brazil:unicode/1f1e7-1f1f7,` && `bread:unicode/1f35e,` && `breast_feeding:unicode/1f931,` && `bricks:unicode/1f9f1,` && `bride_with_veil:unicode/1f470-2640,` && `bridge_at_night:unicode/1f309,` && `briefcase:unicode/1f4bc,` && `british_indian_ocean_territory:unicode/1f1ee-1f1f4,` && `british_virgin_islands:unicode/1f1fb-1f1ec,` && `broccoli:unicode/1f966,` && `broken_heart:unicode/1f494,` && `broom:unicode/1f9f9,` && `brown_circle:unicode/1f7e4,` && `brown_heart:unicode/1f90e,` && `brown_square:unicode/1f7eb,` && `brunei:unicode/1f1e7-1f1f3,` && `bubble_tea:unicode/1f9cb,` && `bubbles:unicode/1fae7,` && `bucket:unicode/1faa3,` && `bug:unicode/1f41b,` && `building_construction:unicode/1f3d7,` && `bulb:unicode/1f4a1,` && `bulgaria:unicode/1f1e7-1f1ec,` && `bullettrain_front:unicode/1f685,` && `bullettrain_side:unicode/1f684,` && `burkina_faso:unicode/1f1e7-1f1eb,` && `burrito:unicode/1f32f,` && `burundi:unicode/1f1e7-1f1ee,` && `bus:unicode/1f68c,` && `business_suit_levitating:unicode/1f574,` && `busstop:unicode/1f68f,` && `bust_in_silhouette:unicode/1f464,` && `busts_in_silhouette:unicode/1f465,` && `butter:unicode/1f9c8,` && `butterfly:unicode/1f98b,` && `cactus:unicode/1f335,` && `cake:unicode/1f370,` && `calendar:unicode/1f4c6,` && `call_me_hand:unicode/1f919,` && `calling:unicode/1f4f2,` && `cambodia:unicode/1f1f0-1f1ed,` && `camel:unicode/1f42b,` && `camera:unicode/1f4f7,` && `camera_flash:unicode/1f4f8,` && `cameroon:unicode/1f1e8-1f1f2,` && `camping:unicode/1f3d5,` && `canada:unicode/1f1e8-1f1e6,` && `canary_islands:unicode/1f1ee-1f1e8,` && `cancer:unicode/264b,` && `candle:unicode/1f56f,` && `candy:unicode/1f36c,` && `canned_food:unicode/1f96b,` && `canoe:unicode/1f6f6,` && `cape_verde:unicode/1f1e8-1f1fb,` && `capital_abcd:unicode/1f520,` && `capricorn:unicode/2651,` && `car:unicode/1f697,` && `card_file_box:unicode/1f5c3,` && `card_index:unicode/1f4c7,` && `card_index_dividers:unicode/1f5c2,` && `caribbean_netherlands:unicode/1f1e7-1f1f6,` && `carousel_horse:unicode/1f3a0,` && `carpentry_saw:unicode/1fa9a,` && `carrot:unicode/1f955,` && `cartwheeling:unicode/1f938,` && `cat:unicode/1f431,` && `cat2:unicode/1f408,` && `cayman_islands:unicode/1f1f0-1f1fe,` && `cd:unicode/1f4bf,` && `central_african_republic:unicode/1f1e8-1f1eb,` && `ceuta_melilla:unicode/1f1ea-1f1e6,` && `chad:unicode/1f1f9-1f1e9,` && `chains:unicode/26d3,` && `chair:unicode/1fa91,` && `champagne:unicode/1f37e,` && `chart:unicode/1f4b9,` && `chart_with_downwards_trend:unicode/1f4c9,` && `chart_with_upwards_trend:unicode/1f4c8,` && `checkered_flag:unicode/1f3c1,` && `cheese:unicode/1f9c0,` && `cherries:unicode/1f352,` && `cherry_blossom:unicode/1f338,` && `chess_pawn:unicode/265f,` && `chestnut:unicode/1f330,` && `chicken:unicode/1f414,` && `child:unicode/1f9d2,` && `children_crossing:unicode/1f6b8,` && `chile:unicode/1f1e8-1f1f1,` && `chipmunk:unicode/1f43f,` && `chocolate_bar:unicode/1f36b,` && `chopsticks:unicode/1f962,` && `christmas_island:unicode/1f1e8-1f1fd,` && `christmas_tree:unicode/1f384,` && `church:unicode/26ea,` && `cinema:unicode/1f3a6,` && `circus_tent:unicode/1f3aa,` && `city_sunrise:unicode/1f307,` && `city_sunset:unicode/1f306,` && `cityscape:unicode/1f3d9,` && `cl:unicode/1f191,` && `clamp:unicode/1f5dc,` && `clap:unicode/1f44f,` && `clapper:unicode/1f3ac,` && `classical_building:unicode/1f3db,` && `climbing:unicode/1f9d7,` && `climbing_man:unicode/1f9d7-2642,` && `climbing_woman:unicode/1f9d7-2640,` && `clinking_glasses:unicode/1f942,` && `clipboard:unicode/1f4cb,` && `clipperton_island:unicode/1f1e8-1f1f5,` && `clock1:unicode/1f550,` && `clock10:unicode/1f559,` && `clock1030:unicode/1f565,` && `clock11:unicode/1f55a,` && `clock1130:unicode/1f566,` && `clock12:unicode/1f55b,` && `clock1230:unicode/1f567,` && `clock130:unicode/1f55c,` && `clock2:unicode/1f551,` && `clock230:unicode/1f55d,` && `clock3:unicode/1f552,` && `clock330:unicode/1f55e,` && `clock4:unicode/1f553,` && `clock430:unicode/1f55f,` && `clock5:unicode/1f554,` && `clock530:unicode/1f560,` && `clock6:unicode/1f555,` && `clock630:unicode/1f561,` && `clock7:unicode/1f556,` && `clock730:unicode/1f562,` && `clock8:unicode/1f557,` && `clock830:unicode/1f563,` && `clock9:unicode/1f558,` && `clock930:unicode/1f564,` && `closed_book:unicode/1f4d5,` && `closed_lock_with_key:unicode/1f510,` && `closed_umbrella:unicode/1f302,` && `cloud:unicode/2601,` && `cloud_with_lightning:unicode/1f329,` && `cloud_with_lightning_and_rain:unicode/26c8,` && `cloud_with_rain:unicode/1f327,` && `cloud_with_snow:unicode/1f328,` && `clown_face:unicode/1f921,` && `clubs:unicode/2663,` && `cn:unicode/1f1e8-1f1f3,` && `coat:unicode/1f9e5,` && `cockroach:unicode/1fab3,` && `cocktail:unicode/1f378,` && `coconut:unicode/1f965,` && `cocos_islands:unicode/1f1e8-1f1e8,` && `coffee:unicode/2615,` && `coffin:unicode/26b0,` && `coin:unicode/1fa99,` && `cold_face:unicode/1f976,` && `cold_sweat:unicode/1f630,` && `collision:unicode/1f4a5,` && `colombia:unicode/1f1e8-1f1f4,` && `comet:unicode/2604,` && `comoros:unicode/1f1f0-1f1f2,` && `compass:unicode/1f9ed,` && `computer:unicode/1f4bb,` && `computer_mouse:unicode/1f5b1,` && `confetti_ball:unicode/1f38a,` && `confounded:unicode/1f616,` && `confused:unicode/1f615,` && `congo_brazzaville:unicode/1f1e8-1f1ec,` && `congo_kinshasa:unicode/1f1e8-1f1e9,` && `congratulations:unicode/3297,` && `construction:unicode/1f6a7,` && `construction_worker:unicode/1f477,` && `construction_worker_man:unicode/1f477-2642,` && `construction_worker_woman:unicode/1f477-2640,` && `control_knobs:unicode/1f39b,` && `convenience_store:unicode/1f3ea,` && `cook:unicode/1f9d1-1f373,` && `cook_islands:unicode/1f1e8-1f1f0,` && `cookie:unicode/1f36a,` && `cool:unicode/1f192,` && `cop:unicode/1f46e,` && `copyright:unicode/00a9,` && `coral:unicode/1fab8,` && `corn:unicode/1f33d,` && `costa_rica:unicode/1f1e8-1f1f7,` && `cote_divoire:unicode/1f1e8-1f1ee,` && `couch_and_lamp:unicode/1f6cb,` && `couple:unicode/1f46b,` && `couple_with_heart:unicode/1f491,` && `couple_with_heart_man_man:unicode/1f468-2764-1f468,` && `couple_with_heart_woman_man:unicode/1f469-2764-1f468,` && `couple_with_heart_woman_woman:unicode/1f469-2764-1f469,` && `couplekiss:unicode/1f48f,` && `couplekiss_man_man:unicode/1f468-2764-1f48b-1f468,` && `couplekiss_man_woman:unicode/1f469-2764-1f48b-1f468,` && `couplekiss_woman_woman:unicode/1f469-2764-1f48b-1f469,` && `cow:unicode/1f42e,` && `cow2:unicode/1f404,` && `cowboy_hat_face:unicode/1f920,` && `crab:unicode/1f980,` && `crayon:unicode/1f58d,` && `credit_card:unicode/1f4b3,` && `crescent_moon:unicode/1f319,` && `cricket:unicode/1f997,` && `cricket_game:unicode/1f3cf,` && `croatia:unicode/1f1ed-1f1f7,` && `crocodile:unicode/1f40a,` && `croissant:unicode/1f950,` && `crossed_fingers:unicode/1f91e,` && `crossed_flags:unicode/1f38c,` && `crossed_swords:unicode/2694,` && `crown:unicode/1f451,` && `crutch:unicode/1fa7c,` && `cry:unicode/1f622,` && `crying_cat_face:unicode/1f63f,` && `crystal_ball:unicode/1f52e,` && `cuba:unicode/1f1e8-1f1fa,` && `cucumber:unicode/1f952,` && `cup_with_straw:unicode/1f964,` && `cupcake:unicode/1f9c1,` && `cupid:unicode/1f498,` && `curacao:unicode/1f1e8-1f1fc,` && `curling_stone:unicode/1f94c,` && `curly_haired_man:unicode/1f468-1f9b1,` && `curly_haired_woman:unicode/1f469-1f9b1,` && `curly_loop:unicode/27b0,` && `currency_exchange:unicode/1f4b1,` && `curry:unicode/1f35b,` && `cursing_face:unicode/1f92c,` && `custard:unicode/1f36e,` && `customs:unicode/1f6c3,` && `cut_of_meat:unicode/1f969,` && `cyclone:unicode/1f300,` && `cyprus:unicode/1f1e8-1f1fe,` && `czech_republic:unicode/1f1e8-1f1ff,` && `dagger:unicode/1f5e1,` && `dancer:unicode/1f483,` && `dancers:unicode/1f46f,` && `dancing_men:unicode/1f46f-2642,` && `dancing_women:unicode/1f46f-2640,` && `dango:unicode/1f361,` && `dark_sunglasses:unicode/1f576,` && `dart:unicode/1f3af,` && `dash:unicode/1f4a8,` && `date:unicode/1f4c5,` && `de:unicode/1f1e9-1f1ea,` && `deaf_man:unicode/1f9cf-2642,` && `deaf_person:unicode/1f9cf,` && `deaf_woman:unicode/1f9cf-2640,` && `deciduous_tree:unicode/1f333,` && `deer:unicode/1f98c,` && `denmark:unicode/1f1e9-1f1f0,` && `department_store:unicode/1f3ec,` && `dependabot:dependabot,` && `derelict_house:unicode/1f3da,` && `desert:unicode/1f3dc,` && `desert_island:unicode/1f3dd,` && `desktop_computer:unicode/1f5a5,` && `detective:unicode/1f575,` && `diamond_shape_with_a_dot_inside:unicode/1f4a0,` && `diamonds:unicode/2666,` && `diego_garcia:unicode/1f1e9-1f1ec,` && `disappointed:unicode/1f61e,` && `disappointed_relieved:unicode/1f625,` && `disguised_face:unicode/1f978,` && `diving_mask:unicode/1f93f,` && `diya_lamp:unicode/1fa94,` && `dizzy:unicode/1f4ab,` && `dizzy_face:unicode/1f635,` && `djibouti:unicode/1f1e9-1f1ef,` && `dna:unicode/1f9ec,` && `do_not_litter:unicode/1f6af,` && `dodo:unicode/1f9a4,` && `dog:unicode/1f436,` && `dog2:unicode/1f415,` && `dollar:unicode/1f4b5,` && `dolls:unicode/1f38e,` && `dolphin:unicode/1f42c,` && `dominica:unicode/1f1e9-1f1f2,` && `dominican_republic:unicode/1f1e9-1f1f4,` && `donkey:unicode/1facf,` && `door:unicode/1f6aa,` && `dotted_line_face:unicode/1fae5,` && `doughnut:unicode/1f369,` && `dove:unicode/1f54a,` && `dragon:unicode/1f409,` && `dragon_face:unicode/1f432,` && `dress:unicode/1f457,`. list = list && `dromedary_camel:unicode/1f42a,` && `drooling_face:unicode/1f924,` && `drop_of_blood:unicode/1fa78,` && `droplet:unicode/1f4a7,` && `drum:unicode/1f941,` && `duck:unicode/1f986,` && `dumpling:unicode/1f95f,` && `dvd:unicode/1f4c0,` && `e-mail:unicode/1f4e7,` && `eagle:unicode/1f985,` && `ear:unicode/1f442,` && `ear_of_rice:unicode/1f33e,` && `ear_with_hearing_aid:unicode/1f9bb,` && `earth_africa:unicode/1f30d,` && `earth_americas:unicode/1f30e,` && `earth_asia:unicode/1f30f,` && `ecuador:unicode/1f1ea-1f1e8,` && `egg:unicode/1f95a,` && `eggplant:unicode/1f346,` && `egypt:unicode/1f1ea-1f1ec,` && `eight:unicode/0038-20e3,` && `eight_pointed_black_star:unicode/2734,` && `eight_spoked_asterisk:unicode/2733,` && `eject_button:unicode/23cf,` && `el_salvador:unicode/1f1f8-1f1fb,` && `electric_plug:unicode/1f50c,` && `electron:electron,` && `elephant:unicode/1f418,` && `elevator:unicode/1f6d7,` && `elf:unicode/1f9dd,` && `elf_man:unicode/1f9dd-2642,` && `elf_woman:unicode/1f9dd-2640,` && `email:unicode/1f4e7,` && `empty_nest:unicode/1fab9,` && `end:unicode/1f51a,` && `england:unicode/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f,` && `envelope:unicode/2709,` && `envelope_with_arrow:unicode/1f4e9,` && `equatorial_guinea:unicode/1f1ec-1f1f6,` && `eritrea:unicode/1f1ea-1f1f7,` && `es:unicode/1f1ea-1f1f8,` && `estonia:unicode/1f1ea-1f1ea,` && `ethiopia:unicode/1f1ea-1f1f9,` && `eu:unicode/1f1ea-1f1fa,` && `euro:unicode/1f4b6,` && `european_castle:unicode/1f3f0,` && `european_post_office:unicode/1f3e4,` && `european_union:unicode/1f1ea-1f1fa,` && `evergreen_tree:unicode/1f332,` && `exclamation:unicode/2757,` && `exploding_head:unicode/1f92f,` && `expressionless:unicode/1f611,` && `eye:unicode/1f441,` && `eye_speech_bubble:unicode/1f441-1f5e8,` && `eyeglasses:unicode/1f453,` && `eyes:unicode/1f440,` && `face_exhaling:unicode/1f62e-1f4a8,` && `face_holding_back_tears:unicode/1f979,` && `face_in_clouds:unicode/1f636-1f32b,` && `face_with_diagonal_mouth:unicode/1fae4,` && `face_with_head_bandage:unicode/1f915,` && `face_with_open_eyes_and_hand_over_mouth:unicode/1fae2,` && `face_with_peeking_eye:unicode/1fae3,` && `face_with_spiral_eyes:unicode/1f635-1f4ab,` && `face_with_thermometer:unicode/1f912,` && `facepalm:unicode/1f926,` && `facepunch:unicode/1f44a,` && `factory:unicode/1f3ed,` && `factory_worker:unicode/1f9d1-1f3ed,` && `fairy:unicode/1f9da,` && `fairy_man:unicode/1f9da-2642,` && `fairy_woman:unicode/1f9da-2640,` && `falafel:unicode/1f9c6,` && `falkland_islands:unicode/1f1eb-1f1f0,` && `fallen_leaf:unicode/1f342,` && `family:unicode/1f46a,` && `family_man_boy:unicode/1f468-1f466,` && `family_man_boy_boy:unicode/1f468-1f466-1f466,` && `family_man_girl:unicode/1f468-1f467,` && `family_man_girl_boy:unicode/1f468-1f467-1f466,` && `family_man_girl_girl:unicode/1f468-1f467-1f467,` && `family_man_man_boy:unicode/1f468-1f468-1f466,` && `family_man_man_boy_boy:unicode/1f468-1f468-1f466-1f466,` && `family_man_man_girl:unicode/1f468-1f468-1f467,` && `family_man_man_girl_boy:unicode/1f468-1f468-1f467-1f466,` && `family_man_man_girl_girl:unicode/1f468-1f468-1f467-1f467,` && `family_man_woman_boy:unicode/1f468-1f469-1f466,` && `family_man_woman_boy_boy:unicode/1f468-1f469-1f466-1f466,` && `family_man_woman_girl:unicode/1f468-1f469-1f467,` && `family_man_woman_girl_boy:unicode/1f468-1f469-1f467-1f466,` && `family_man_woman_girl_girl:unicode/1f468-1f469-1f467-1f467,` && `family_woman_boy:unicode/1f469-1f466,` && `family_woman_boy_boy:unicode/1f469-1f466-1f466,` && `family_woman_girl:unicode/1f469-1f467,` && `family_woman_girl_boy:unicode/1f469-1f467-1f466,` && `family_woman_girl_girl:unicode/1f469-1f467-1f467,` && `family_woman_woman_boy:unicode/1f469-1f469-1f466,` && `family_woman_woman_boy_boy:unicode/1f469-1f469-1f466-1f466,` && `family_woman_woman_girl:unicode/1f469-1f469-1f467,` && `family_woman_woman_girl_boy:unicode/1f469-1f469-1f467-1f466,` && `family_woman_woman_girl_girl:unicode/1f469-1f469-1f467-1f467,` && `farmer:unicode/1f9d1-1f33e,` && `faroe_islands:unicode/1f1eb-1f1f4,` && `fast_forward:unicode/23e9,` && `fax:unicode/1f4e0,` && `fearful:unicode/1f628,` && `feather:unicode/1fab6,` && `feelsgood:feelsgood,` && `feet:unicode/1f43e,` && `female_detective:unicode/1f575-2640,` && `female_sign:unicode/2640,` && `ferris_wheel:unicode/1f3a1,` && `ferry:unicode/26f4,` && `field_hockey:unicode/1f3d1,` && `fiji:unicode/1f1eb-1f1ef,` && `file_cabinet:unicode/1f5c4,` && `file_folder:unicode/1f4c1,` && `film_projector:unicode/1f4fd,` && `film_strip:unicode/1f39e,` && `finland:unicode/1f1eb-1f1ee,` && `finnadie:finnadie,` && `fire:unicode/1f525,` && `fire_engine:unicode/1f692,` && `fire_extinguisher:unicode/1f9ef,` && `firecracker:unicode/1f9e8,` && `firefighter:unicode/1f9d1-1f692,` && `fireworks:unicode/1f386,` && `first_quarter_moon:unicode/1f313,` && `first_quarter_moon_with_face:unicode/1f31b,` && `fish:unicode/1f41f,` && `fish_cake:unicode/1f365,` && `fishing_pole_and_fish:unicode/1f3a3,` && `fishsticks:fishsticks,` && `fist:unicode/270a,` && `fist_left:unicode/1f91b,` && `fist_oncoming:unicode/1f44a,` && `fist_raised:unicode/270a,` && `fist_right:unicode/1f91c,` && `five:unicode/0035-20e3,` && `flags:unicode/1f38f,` && `flamingo:unicode/1f9a9,` && `flashlight:unicode/1f526,` && `flat_shoe:unicode/1f97f,` && `flatbread:unicode/1fad3,` && `fleur_de_lis:unicode/269c,` && `flight_arrival:unicode/1f6ec,` && `flight_departure:unicode/1f6eb,` && `flipper:unicode/1f42c,` && `floppy_disk:unicode/1f4be,` && `flower_playing_cards:unicode/1f3b4,` && `flushed:unicode/1f633,` && `flute:unicode/1fa88,` && `fly:unicode/1fab0,` && `flying_disc:unicode/1f94f,` && `flying_saucer:unicode/1f6f8,` && `fog:unicode/1f32b,` && `foggy:unicode/1f301,` && `folding_hand_fan:unicode/1faad,` && `fondue:unicode/1fad5,` && `foot:unicode/1f9b6,` && `football:unicode/1f3c8,` && `footprints:unicode/1f463,` && `fork_and_knife:unicode/1f374,` && `fortune_cookie:unicode/1f960,` && `fountain:unicode/26f2,` && `fountain_pen:unicode/1f58b,` && `four:unicode/0034-20e3,` && `four_leaf_clover:unicode/1f340,` && `fox_face:unicode/1f98a,` && `fr:unicode/1f1eb-1f1f7,` && `framed_picture:unicode/1f5bc,` && `free:unicode/1f193,` && `french_guiana:unicode/1f1ec-1f1eb,` && `french_polynesia:unicode/1f1f5-1f1eb,` && `french_southern_territories:unicode/1f1f9-1f1eb,` && `fried_egg:unicode/1f373,` && `fried_shrimp:unicode/1f364,` && `fries:unicode/1f35f,` && `frog:unicode/1f438,` && `frowning:unicode/1f626,` && `frowning_face:unicode/2639,` && `frowning_man:unicode/1f64d-2642,` && `frowning_person:unicode/1f64d,` && `frowning_woman:unicode/1f64d-2640,` && `fu:unicode/1f595,` && `fuelpump:unicode/26fd,` && `full_moon:unicode/1f315,` && `full_moon_with_face:unicode/1f31d,` && `funeral_urn:unicode/26b1,` && `gabon:unicode/1f1ec-1f1e6,` && `gambia:unicode/1f1ec-1f1f2,` && `game_die:unicode/1f3b2,` && `garlic:unicode/1f9c4,` && `gb:unicode/1f1ec-1f1e7,` && `gear:unicode/2699,` && `gem:unicode/1f48e,` && `gemini:unicode/264a,` && `genie:unicode/1f9de,` && `genie_man:unicode/1f9de-2642,` && `genie_woman:unicode/1f9de-2640,` && `georgia:unicode/1f1ec-1f1ea,` && `ghana:unicode/1f1ec-1f1ed,` && `ghost:unicode/1f47b,` && `gibraltar:unicode/1f1ec-1f1ee,` && `gift:unicode/1f381,` && `gift_heart:unicode/1f49d,` && `ginger_root:unicode/1fada,` && `giraffe:unicode/1f992,` && `girl:unicode/1f467,` && `globe_with_meridians:unicode/1f310,` && `gloves:unicode/1f9e4,` && `goal_net:unicode/1f945,` && `goat:unicode/1f410,` && `goberserk:goberserk,` && `godmode:godmode,` && `goggles:unicode/1f97d,` && `golf:unicode/26f3,` && `golfing:unicode/1f3cc,` && `golfing_man:unicode/1f3cc-2642,` && `golfing_woman:unicode/1f3cc-2640,` && `goose:unicode/1fabf,` && `gorilla:unicode/1f98d,` && `grapes:unicode/1f347,` && `greece:unicode/1f1ec-1f1f7,` && `green_apple:unicode/1f34f,` && `green_book:unicode/1f4d7,` && `green_circle:unicode/1f7e2,` && `green_heart:unicode/1f49a,` && `green_salad:unicode/1f957,` && `green_square:unicode/1f7e9,` && `greenland:unicode/1f1ec-1f1f1,` && `grenada:unicode/1f1ec-1f1e9,` && `grey_exclamation:unicode/2755,` && `grey_heart:unicode/1fa76,` && `grey_question:unicode/2754,` && `grimacing:unicode/1f62c,` && `grin:unicode/1f601,` && `grinning:unicode/1f600,` && `guadeloupe:unicode/1f1ec-1f1f5,` && `guam:unicode/1f1ec-1f1fa,` && `guard:unicode/1f482,` && `guardsman:unicode/1f482-2642,` && `guardswoman:unicode/1f482-2640,` && `guatemala:unicode/1f1ec-1f1f9,` && `guernsey:unicode/1f1ec-1f1ec,` && `guide_dog:unicode/1f9ae,` && `guinea:unicode/1f1ec-1f1f3,` && `guinea_bissau:unicode/1f1ec-1f1fc,` && `guitar:unicode/1f3b8,` && `gun:unicode/1f52b,` && `guyana:unicode/1f1ec-1f1fe,` && `hair_pick:unicode/1faae,` && `haircut:unicode/1f487,` && `haircut_man:unicode/1f487-2642,` && `haircut_woman:unicode/1f487-2640,` && `haiti:unicode/1f1ed-1f1f9,` && `hamburger:unicode/1f354,` && `hammer:unicode/1f528,` && `hammer_and_pick:unicode/2692,` && `hammer_and_wrench:unicode/1f6e0,` && `hamsa:unicode/1faac,` && `hamster:unicode/1f439,` && `hand:unicode/270b,` && `hand_over_mouth:unicode/1f92d,` && `hand_with_index_finger_and_thumb_crossed:unicode/1faf0,` && `handbag:unicode/1f45c,` && `handball_person:unicode/1f93e,` && `handshake:unicode/1f91d,` && `hankey:unicode/1f4a9,` && `hash:unicode/0023-20e3,` && `hatched_chick:unicode/1f425,` && `hatching_chick:unicode/1f423,` && `headphones:unicode/1f3a7,` && `headstone:unicode/1faa6,` && `health_worker:unicode/1f9d1-2695,` && `hear_no_evil:unicode/1f649,` && `heard_mcdonald_islands:unicode/1f1ed-1f1f2,` && `heart:unicode/2764,` && `heart_decoration:unicode/1f49f,` && `heart_eyes:unicode/1f60d,` && `heart_eyes_cat:unicode/1f63b,` && `heart_hands:unicode/1faf6,` && `heart_on_fire:unicode/2764-1f525,` && `heartbeat:unicode/1f493,` && `heartpulse:unicode/1f497,` && `hearts:unicode/2665,` && `heavy_check_mark:unicode/2714,` && `heavy_division_sign:unicode/2797,` && `heavy_dollar_sign:unicode/1f4b2,` && `heavy_equals_sign:unicode/1f7f0,` && `heavy_exclamation_mark:unicode/2757,` && `heavy_heart_exclamation:unicode/2763,` && `heavy_minus_sign:unicode/2796,` && `heavy_multiplication_x:unicode/2716,` && `heavy_plus_sign:unicode/2795,` && `hedgehog:unicode/1f994,` && `helicopter:unicode/1f681,` && `herb:unicode/1f33f,` && `hibiscus:unicode/1f33a,` && `high_brightness:unicode/1f506,` && `high_heel:unicode/1f460,` && `hiking_boot:unicode/1f97e,` && `hindu_temple:unicode/1f6d5,` && `hippopotamus:unicode/1f99b,` && `hocho:unicode/1f52a,` && `hole:unicode/1f573,` && `honduras:unicode/1f1ed-1f1f3,` && `honey_pot:unicode/1f36f,` && `honeybee:unicode/1f41d,` && `hong_kong:unicode/1f1ed-1f1f0,` && `hook:unicode/1fa9d,` && `horse:unicode/1f434,` && `horse_racing:unicode/1f3c7,` && `hospital:unicode/1f3e5,` && `hot_face:unicode/1f975,` && `hot_pepper:unicode/1f336,` && `hotdog:unicode/1f32d,` && `hotel:unicode/1f3e8,` && `hotsprings:unicode/2668,` && `hourglass:unicode/231b,` && `hourglass_flowing_sand:unicode/23f3,` && `house:unicode/1f3e0,` && `house_with_garden:unicode/1f3e1,` && `houses:unicode/1f3d8,` && `hugs:unicode/1f917,` && `hungary:unicode/1f1ed-1f1fa,` && `hurtrealbad:hurtrealbad,` && `hushed:unicode/1f62f,` && `hut:unicode/1f6d6,` && `hyacinth:unicode/1fabb,` && `ice_cream:unicode/1f368,` && `ice_cube:unicode/1f9ca,` && `ice_hockey:unicode/1f3d2,` && `ice_skate:unicode/26f8,` && `icecream:unicode/1f366,` && `iceland:unicode/1f1ee-1f1f8,` && `id:unicode/1f194,` && `identification_card:unicode/1faaa,` && `ideograph_advantage:unicode/1f250,` && `imp:unicode/1f47f,` && `inbox_tray:unicode/1f4e5,` && `incoming_envelope:unicode/1f4e8,` && `index_pointing_at_the_viewer:unicode/1faf5,` && `india:unicode/1f1ee-1f1f3,` && `indonesia:unicode/1f1ee-1f1e9,` && `infinity:unicode/267e,` && `information_desk_person:unicode/1f481,` && `information_source:unicode/2139,` && `innocent:unicode/1f607,` && `interrobang:unicode/2049,` && `iphone:unicode/1f4f1,` && `iran:unicode/1f1ee-1f1f7,` && `iraq:unicode/1f1ee-1f1f6,` && `ireland:unicode/1f1ee-1f1ea,` && `isle_of_man:unicode/1f1ee-1f1f2,` && `israel:unicode/1f1ee-1f1f1,` && `it:unicode/1f1ee-1f1f9,` && `izakaya_lantern:unicode/1f3ee,` && `jack_o_lantern:unicode/1f383,` && `jamaica:unicode/1f1ef-1f1f2,` && `japan:unicode/1f5fe,` && `japanese_castle:unicode/1f3ef,` && `japanese_goblin:unicode/1f47a,` && `japanese_ogre:unicode/1f479,` && `jar:unicode/1fad9,` && `jeans:unicode/1f456,` && `jellyfish:unicode/1fabc,` && `jersey:unicode/1f1ef-1f1ea,` && `jigsaw:unicode/1f9e9,` && `jordan:unicode/1f1ef-1f1f4,` && `joy:unicode/1f602,` && `joy_cat:unicode/1f639,` && `joystick:unicode/1f579,` && `jp:unicode/1f1ef-1f1f5,` && `judge:unicode/1f9d1-2696,` && `juggling_person:unicode/1f939,` && `kaaba:unicode/1f54b,` && `kangaroo:unicode/1f998,` && `kazakhstan:unicode/1f1f0-1f1ff,` && `kenya:unicode/1f1f0-1f1ea,` && `key:unicode/1f511,` && `keyboard:unicode/2328,` && `keycap_ten:unicode/1f51f,` && `khanda:unicode/1faaf,` && `kick_scooter:unicode/1f6f4,` && `kimono:unicode/1f458,` && `kiribati:unicode/1f1f0-1f1ee,` && `kiss:unicode/1f48b,` && `kissing:unicode/1f617,` && `kissing_cat:unicode/1f63d,` && `kissing_closed_eyes:unicode/1f61a,` && `kissing_heart:unicode/1f618,` && `kissing_smiling_eyes:unicode/1f619,` && `kite:unicode/1fa81,` && `kiwi_fruit:unicode/1f95d,` && `kneeling_man:unicode/1f9ce-2642,` && `kneeling_person:unicode/1f9ce,` && `kneeling_woman:unicode/1f9ce-2640,` && `knife:unicode/1f52a,` && `knot:unicode/1faa2,` && `koala:unicode/1f428,` && `koko:unicode/1f201,` && `kosovo:unicode/1f1fd-1f1f0,` && `kr:unicode/1f1f0-1f1f7,` && `kuwait:unicode/1f1f0-1f1fc,` && `kyrgyzstan:unicode/1f1f0-1f1ec,` && `lab_coat:unicode/1f97c,` && `label:unicode/1f3f7,` && `lacrosse:unicode/1f94d,` && `ladder:unicode/1fa9c,` && `lady_beetle:unicode/1f41e,` && `lantern:unicode/1f3ee,` && `laos:unicode/1f1f1-1f1e6,` && `large_blue_circle:unicode/1f535,` && `large_blue_diamond:unicode/1f537,` && `large_orange_diamond:unicode/1f536,` && `last_quarter_moon:unicode/1f317,` && `last_quarter_moon_with_face:unicode/1f31c,` && `latin_cross:unicode/271d,` && `latvia:unicode/1f1f1-1f1fb,` && `laughing:unicode/1f606,` && `leafy_green:unicode/1f96c,` && `leaves:unicode/1f343,` && `lebanon:unicode/1f1f1-1f1e7,` && `ledger:unicode/1f4d2,` && `left_luggage:unicode/1f6c5,` && `left_right_arrow:unicode/2194,` && `left_speech_bubble:unicode/1f5e8,` && `leftwards_arrow_with_hook:unicode/21a9,` && `leftwards_hand:unicode/1faf2,` && `leftwards_pushing_hand:unicode/1faf7,` && `leg:unicode/1f9b5,` && `lemon:unicode/1f34b,` && `leo:unicode/264c,` && `leopard:unicode/1f406,` && `lesotho:unicode/1f1f1-1f1f8,` && `level_slider:unicode/1f39a,` && `liberia:unicode/1f1f1-1f1f7,` && `libra:unicode/264e,` && `libya:unicode/1f1f1-1f1fe,` && `liechtenstein:unicode/1f1f1-1f1ee,` && `light_blue_heart:unicode/1fa75,` && `light_rail:unicode/1f688,` && `link:unicode/1f517,` && `lion:unicode/1f981,` && `lips:unicode/1f444,` && `lipstick:unicode/1f484,` && `lithuania:unicode/1f1f1-1f1f9,` && `lizard:unicode/1f98e,` && `llama:unicode/1f999,` && `lobster:unicode/1f99e,` && `lock:unicode/1f512,` && `lock_with_ink_pen:unicode/1f50f,` && `lollipop:unicode/1f36d,` && `long_drum:unicode/1fa98,` && `loop:unicode/27bf,` && `lotion_bottle:unicode/1f9f4,` && `lotus:unicode/1fab7,` && `lotus_position:unicode/1f9d8,` && `lotus_position_man:unicode/1f9d8-2642,` && `lotus_position_woman:unicode/1f9d8-2640,` && `loud_sound:unicode/1f50a,` && `loudspeaker:unicode/1f4e2,` && `love_hotel:unicode/1f3e9,` && `love_letter:unicode/1f48c,` && `love_you_gesture:unicode/1f91f,` && `low_battery:unicode/1faab,` && `low_brightness:unicode/1f505,` && `luggage:unicode/1f9f3,` && `lungs:unicode/1fac1,` && `luxembourg:unicode/1f1f1-1f1fa,` && `lying_face:unicode/1f925,` && `m:unicode/24c2,` && `macau:unicode/1f1f2-1f1f4,` && `macedonia:unicode/1f1f2-1f1f0,` && `madagascar:unicode/1f1f2-1f1ec,` && `mag:unicode/1f50d,` && `mag_right:unicode/1f50e,` && `mage:unicode/1f9d9,` && `mage_man:unicode/1f9d9-2642,` && `mage_woman:unicode/1f9d9-2640,` && `magic_wand:unicode/1fa84,` && `magnet:unicode/1f9f2,` && `mahjong:unicode/1f004,` && `mailbox:unicode/1f4eb,` && `mailbox_closed:unicode/1f4ea,` && `mailbox_with_mail:unicode/1f4ec,` && `mailbox_with_no_mail:unicode/1f4ed,` && `malawi:unicode/1f1f2-1f1fc,` && `malaysia:unicode/1f1f2-1f1fe,` && `maldives:unicode/1f1f2-1f1fb,` && `male_detective:unicode/1f575-2642,` && `male_sign:unicode/2642,` && `mali:unicode/1f1f2-1f1f1,` && `malta:unicode/1f1f2-1f1f9,` && `mammoth:unicode/1f9a3,` && `man:unicode/1f468,` && `man_artist:unicode/1f468-1f3a8,` && `man_astronaut:unicode/1f468-1f680,`. list = list && `man_beard:unicode/1f9d4-2642,` && `man_cartwheeling:unicode/1f938-2642,` && `man_cook:unicode/1f468-1f373,` && `man_dancing:unicode/1f57a,` && `man_facepalming:unicode/1f926-2642,` && `man_factory_worker:unicode/1f468-1f3ed,` && `man_farmer:unicode/1f468-1f33e,` && `man_feeding_baby:unicode/1f468-1f37c,` && `man_firefighter:unicode/1f468-1f692,` && `man_health_worker:unicode/1f468-2695,` && `man_in_manual_wheelchair:unicode/1f468-1f9bd,` && `man_in_motorized_wheelchair:unicode/1f468-1f9bc,` && `man_in_tuxedo:unicode/1f935-2642,` && `man_judge:unicode/1f468-2696,` && `man_juggling:unicode/1f939-2642,` && `man_mechanic:unicode/1f468-1f527,` && `man_office_worker:unicode/1f468-1f4bc,` && `man_pilot:unicode/1f468-2708,` && `man_playing_handball:unicode/1f93e-2642,` && `man_playing_water_polo:unicode/1f93d-2642,` && `man_scientist:unicode/1f468-1f52c,` && `man_shrugging:unicode/1f937-2642,` && `man_singer:unicode/1f468-1f3a4,` && `man_student:unicode/1f468-1f393,` && `man_teacher:unicode/1f468-1f3eb,` && `man_technologist:unicode/1f468-1f4bb,` && `man_with_gua_pi_mao:unicode/1f472,` && `man_with_probing_cane:unicode/1f468-1f9af,` && `man_with_turban:unicode/1f473-2642,` && `man_with_veil:unicode/1f470-2642,` && `mandarin:unicode/1f34a,` && `mango:unicode/1f96d,` && `mans_shoe:unicode/1f45e,` && `mantelpiece_clock:unicode/1f570,` && `manual_wheelchair:unicode/1f9bd,` && `maple_leaf:unicode/1f341,` && `maracas:unicode/1fa87,` && `marshall_islands:unicode/1f1f2-1f1ed,` && `martial_arts_uniform:unicode/1f94b,` && `martinique:unicode/1f1f2-1f1f6,` && `mask:unicode/1f637,` && `massage:unicode/1f486,` && `massage_man:unicode/1f486-2642,` && `massage_woman:unicode/1f486-2640,` && `mate:unicode/1f9c9,` && `mauritania:unicode/1f1f2-1f1f7,` && `mauritius:unicode/1f1f2-1f1fa,` && `mayotte:unicode/1f1fe-1f1f9,` && `meat_on_bone:unicode/1f356,` && `mechanic:unicode/1f9d1-1f527,` && `mechanical_arm:unicode/1f9be,` && `mechanical_leg:unicode/1f9bf,` && `medal_military:unicode/1f396,` && `medal_sports:unicode/1f3c5,` && `medical_symbol:unicode/2695,` && `mega:unicode/1f4e3,` && `melon:unicode/1f348,` && `melting_face:unicode/1fae0,` && `memo:unicode/1f4dd,` && `men_wrestling:unicode/1f93c-2642,` && `mending_heart:unicode/2764-1fa79,` && `menorah:unicode/1f54e,` && `mens:unicode/1f6b9,` && `mermaid:unicode/1f9dc-2640,` && `merman:unicode/1f9dc-2642,` && `merperson:unicode/1f9dc,` && `metal:unicode/1f918,` && `metro:unicode/1f687,` && `mexico:unicode/1f1f2-1f1fd,` && `microbe:unicode/1f9a0,` && `micronesia:unicode/1f1eb-1f1f2,` && `microphone:unicode/1f3a4,` && `microscope:unicode/1f52c,` && `middle_finger:unicode/1f595,` && `military_helmet:unicode/1fa96,` && `milk_glass:unicode/1f95b,` && `milky_way:unicode/1f30c,` && `minibus:unicode/1f690,` && `minidisc:unicode/1f4bd,` && `mirror:unicode/1fa9e,` && `mirror_ball:unicode/1faa9,` && `mobile_phone_off:unicode/1f4f4,` && `moldova:unicode/1f1f2-1f1e9,` && `monaco:unicode/1f1f2-1f1e8,` && `money_mouth_face:unicode/1f911,` && `money_with_wings:unicode/1f4b8,` && `moneybag:unicode/1f4b0,` && `mongolia:unicode/1f1f2-1f1f3,` && `monkey:unicode/1f412,` && `monkey_face:unicode/1f435,` && `monocle_face:unicode/1f9d0,` && `monorail:unicode/1f69d,` && `montenegro:unicode/1f1f2-1f1ea,` && `montserrat:unicode/1f1f2-1f1f8,` && `moon:unicode/1f314,` && `moon_cake:unicode/1f96e,` && `moose:unicode/1face,` && `morocco:unicode/1f1f2-1f1e6,` && `mortar_board:unicode/1f393,` && `mosque:unicode/1f54c,` && `mosquito:unicode/1f99f,` && `motor_boat:unicode/1f6e5,` && `motor_scooter:unicode/1f6f5,` && `motorcycle:unicode/1f3cd,` && `motorized_wheelchair:unicode/1f9bc,` && `motorway:unicode/1f6e3,` && `mount_fuji:unicode/1f5fb,` && `mountain:unicode/26f0,` && `mountain_bicyclist:unicode/1f6b5,` && `mountain_biking_man:unicode/1f6b5-2642,` && `mountain_biking_woman:unicode/1f6b5-2640,` && `mountain_cableway:unicode/1f6a0,` && `mountain_railway:unicode/1f69e,` && `mountain_snow:unicode/1f3d4,` && `mouse:unicode/1f42d,` && `mouse2:unicode/1f401,` && `mouse_trap:unicode/1faa4,` && `movie_camera:unicode/1f3a5,` && `moyai:unicode/1f5ff,` && `mozambique:unicode/1f1f2-1f1ff,` && `mrs_claus:unicode/1f936,` && `muscle:unicode/1f4aa,` && `mushroom:unicode/1f344,` && `musical_keyboard:unicode/1f3b9,` && `musical_note:unicode/1f3b5,` && `musical_score:unicode/1f3bc,` && `mute:unicode/1f507,` && `mx_claus:unicode/1f9d1-1f384,` && `myanmar:unicode/1f1f2-1f1f2,` && `nail_care:unicode/1f485,` && `name_badge:unicode/1f4db,` && `namibia:unicode/1f1f3-1f1e6,` && `national_park:unicode/1f3de,` && `nauru:unicode/1f1f3-1f1f7,` && `nauseated_face:unicode/1f922,` && `nazar_amulet:unicode/1f9ff,` && `neckbeard:neckbeard,` && `necktie:unicode/1f454,` && `negative_squared_cross_mark:unicode/274e,` && `nepal:unicode/1f1f3-1f1f5,` && `nerd_face:unicode/1f913,` && `nest_with_eggs:unicode/1faba,` && `nesting_dolls:unicode/1fa86,` && `netherlands:unicode/1f1f3-1f1f1,` && `neutral_face:unicode/1f610,` && `new:unicode/1f195,` && `new_caledonia:unicode/1f1f3-1f1e8,` && `new_moon:unicode/1f311,` && `new_moon_with_face:unicode/1f31a,` && `new_zealand:unicode/1f1f3-1f1ff,` && `newspaper:unicode/1f4f0,` && `newspaper_roll:unicode/1f5de,` && `next_track_button:unicode/23ed,` && `ng:unicode/1f196,` && `ng_man:unicode/1f645-2642,` && `ng_woman:unicode/1f645-2640,` && `nicaragua:unicode/1f1f3-1f1ee,` && `niger:unicode/1f1f3-1f1ea,` && `nigeria:unicode/1f1f3-1f1ec,` && `night_with_stars:unicode/1f303,` && `nine:unicode/0039-20e3,` && `ninja:unicode/1f977,` && `niue:unicode/1f1f3-1f1fa,` && `no_bell:unicode/1f515,` && `no_bicycles:unicode/1f6b3,` && `no_entry:unicode/26d4,` && `no_entry_sign:unicode/1f6ab,` && `no_good:unicode/1f645,` && `no_good_man:unicode/1f645-2642,` && `no_good_woman:unicode/1f645-2640,` && `no_mobile_phones:unicode/1f4f5,` && `no_mouth:unicode/1f636,` && `no_pedestrians:unicode/1f6b7,` && `no_smoking:unicode/1f6ad,` && `non-potable_water:unicode/1f6b1,` && `norfolk_island:unicode/1f1f3-1f1eb,` && `north_korea:unicode/1f1f0-1f1f5,` && `northern_mariana_islands:unicode/1f1f2-1f1f5,` && `norway:unicode/1f1f3-1f1f4,` && `nose:unicode/1f443,` && `notebook:unicode/1f4d3,` && `notebook_with_decorative_cover:unicode/1f4d4,` && `notes:unicode/1f3b6,` && `nut_and_bolt:unicode/1f529,` && `o:unicode/2b55,` && `o2:unicode/1f17e,` && `ocean:unicode/1f30a,` && `octocat:octocat,` && `octopus:unicode/1f419,` && `oden:unicode/1f362,` && `office:unicode/1f3e2,` && `office_worker:unicode/1f9d1-1f4bc,` && `oil_drum:unicode/1f6e2,` && `ok:unicode/1f197,` && `ok_hand:unicode/1f44c,` && `ok_man:unicode/1f646-2642,` && `ok_person:unicode/1f646,` && `ok_woman:unicode/1f646-2640,` && `old_key:unicode/1f5dd,` && `older_adult:unicode/1f9d3,` && `older_man:unicode/1f474,` && `older_woman:unicode/1f475,` && `olive:unicode/1fad2,` && `om:unicode/1f549,` && `oman:unicode/1f1f4-1f1f2,` && `on:unicode/1f51b,` && `oncoming_automobile:unicode/1f698,` && `oncoming_bus:unicode/1f68d,` && `oncoming_police_car:unicode/1f694,` && `oncoming_taxi:unicode/1f696,` && `one:unicode/0031-20e3,` && `one_piece_swimsuit:unicode/1fa71,` && `onion:unicode/1f9c5,` && `open_book:unicode/1f4d6,` && `open_file_folder:unicode/1f4c2,` && `open_hands:unicode/1f450,` && `open_mouth:unicode/1f62e,` && `open_umbrella:unicode/2602,` && `ophiuchus:unicode/26ce,` && `orange:unicode/1f34a,` && `orange_book:unicode/1f4d9,` && `orange_circle:unicode/1f7e0,` && `orange_heart:unicode/1f9e1,` && `orange_square:unicode/1f7e7,` && `orangutan:unicode/1f9a7,` && `orthodox_cross:unicode/2626,` && `otter:unicode/1f9a6,` && `outbox_tray:unicode/1f4e4,` && `owl:unicode/1f989,` && `ox:unicode/1f402,` && `oyster:unicode/1f9aa,` && `package:unicode/1f4e6,` && `page_facing_up:unicode/1f4c4,` && `page_with_curl:unicode/1f4c3,` && `pager:unicode/1f4df,` && `paintbrush:unicode/1f58c,` && `pakistan:unicode/1f1f5-1f1f0,` && `palau:unicode/1f1f5-1f1fc,` && `palestinian_territories:unicode/1f1f5-1f1f8,` && `palm_down_hand:unicode/1faf3,` && `palm_tree:unicode/1f334,` && `palm_up_hand:unicode/1faf4,` && `palms_up_together:unicode/1f932,` && `panama:unicode/1f1f5-1f1e6,` && `pancakes:unicode/1f95e,` && `panda_face:unicode/1f43c,` && `paperclip:unicode/1f4ce,` && `paperclips:unicode/1f587,` && `papua_new_guinea:unicode/1f1f5-1f1ec,` && `parachute:unicode/1fa82,` && `paraguay:unicode/1f1f5-1f1fe,` && `parasol_on_ground:unicode/26f1,` && `parking:unicode/1f17f,` && `parrot:unicode/1f99c,` && `part_alternation_mark:unicode/303d,` && `partly_sunny:unicode/26c5,` && `partying_face:unicode/1f973,` && `passenger_ship:unicode/1f6f3,` && `passport_control:unicode/1f6c2,` && `pause_button:unicode/23f8,` && `paw_prints:unicode/1f43e,` && `pea_pod:unicode/1fadb,` && `peace_symbol:unicode/262e,` && `peach:unicode/1f351,` && `peacock:unicode/1f99a,` && `peanuts:unicode/1f95c,` && `pear:unicode/1f350,` && `pen:unicode/1f58a,` && `pencil:unicode/1f4dd,` && `pencil2:unicode/270f,` && `penguin:unicode/1f427,` && `pensive:unicode/1f614,` && `people_holding_hands:unicode/1f9d1-1f91d-1f9d1,` && `people_hugging:unicode/1fac2,` && `performing_arts:unicode/1f3ad,` && `persevere:unicode/1f623,` && `person_bald:unicode/1f9d1-1f9b2,` && `person_curly_hair:unicode/1f9d1-1f9b1,` && `person_feeding_baby:unicode/1f9d1-1f37c,` && `person_fencing:unicode/1f93a,` && `person_in_manual_wheelchair:unicode/1f9d1-1f9bd,` && `person_in_motorized_wheelchair:unicode/1f9d1-1f9bc,` && `person_in_tuxedo:unicode/1f935,` && `person_red_hair:unicode/1f9d1-1f9b0,` && `person_white_hair:unicode/1f9d1-1f9b3,` && `person_with_crown:unicode/1fac5,` && `person_with_probing_cane:unicode/1f9d1-1f9af,` && `person_with_turban:unicode/1f473,` && `person_with_veil:unicode/1f470,` && `peru:unicode/1f1f5-1f1ea,` && `petri_dish:unicode/1f9eb,` && `philippines:unicode/1f1f5-1f1ed,` && `phone:unicode/260e,` && `pick:unicode/26cf,` && `pickup_truck:unicode/1f6fb,` && `pie:unicode/1f967,` && `pig:unicode/1f437,` && `pig2:unicode/1f416,` && `pig_nose:unicode/1f43d,` && `pill:unicode/1f48a,` && `pilot:unicode/1f9d1-2708,` && `pinata:unicode/1fa85,` && `pinched_fingers:unicode/1f90c,` && `pinching_hand:unicode/1f90f,` && `pineapple:unicode/1f34d,` && `ping_pong:unicode/1f3d3,` && `pink_heart:unicode/1fa77,` && `pirate_flag:unicode/1f3f4-2620,` && `pisces:unicode/2653,` && `pitcairn_islands:unicode/1f1f5-1f1f3,` && `pizza:unicode/1f355,` && `placard:unicode/1faa7,` && `place_of_worship:unicode/1f6d0,` && `plate_with_cutlery:unicode/1f37d,` && `play_or_pause_button:unicode/23ef,` && `playground_slide:unicode/1f6dd,` && `pleading_face:unicode/1f97a,` && `plunger:unicode/1faa0,` && `point_down:unicode/1f447,` && `point_left:unicode/1f448,` && `point_right:unicode/1f449,` && `point_up:unicode/261d,` && `point_up_2:unicode/1f446,` && `poland:unicode/1f1f5-1f1f1,` && `polar_bear:unicode/1f43b-2744,` && `police_car:unicode/1f693,` && `police_officer:unicode/1f46e,` && `policeman:unicode/1f46e-2642,` && `policewoman:unicode/1f46e-2640,` && `poodle:unicode/1f429,` && `poop:unicode/1f4a9,` && `popcorn:unicode/1f37f,` && `portugal:unicode/1f1f5-1f1f9,` && `post_office:unicode/1f3e3,` && `postal_horn:unicode/1f4ef,` && `postbox:unicode/1f4ee,` && `potable_water:unicode/1f6b0,` && `potato:unicode/1f954,` && `potted_plant:unicode/1fab4,` && `pouch:unicode/1f45d,` && `poultry_leg:unicode/1f357,` && `pound:unicode/1f4b7,` && `pouring_liquid:unicode/1fad7,` && `pout:unicode/1f621,` && `pouting_cat:unicode/1f63e,` && `pouting_face:unicode/1f64e,` && `pouting_man:unicode/1f64e-2642,` && `pouting_woman:unicode/1f64e-2640,` && `pray:unicode/1f64f,` && `prayer_beads:unicode/1f4ff,` && `pregnant_man:unicode/1fac3,` && `pregnant_person:unicode/1fac4,` && `pregnant_woman:unicode/1f930,` && `pretzel:unicode/1f968,` && `previous_track_button:unicode/23ee,` && `prince:unicode/1f934,` && `princess:unicode/1f478,` && `printer:unicode/1f5a8,` && `probing_cane:unicode/1f9af,` && `puerto_rico:unicode/1f1f5-1f1f7,` && `punch:unicode/1f44a,` && `purple_circle:unicode/1f7e3,` && `purple_heart:unicode/1f49c,` && `purple_square:unicode/1f7ea,` && `purse:unicode/1f45b,` && `pushpin:unicode/1f4cc,` && `put_litter_in_its_place:unicode/1f6ae,` && `qatar:unicode/1f1f6-1f1e6,` && `question:unicode/2753,` && `rabbit:unicode/1f430,` && `rabbit2:unicode/1f407,` && `raccoon:unicode/1f99d,` && `racehorse:unicode/1f40e,` && `racing_car:unicode/1f3ce,` && `radio:unicode/1f4fb,` && `radio_button:unicode/1f518,` && `radioactive:unicode/2622,` && `rage:unicode/1f621,` && `rage1:rage1,` && `rage2:rage2,` && `rage3:rage3,` && `rage4:rage4,` && `railway_car:unicode/1f683,` && `railway_track:unicode/1f6e4,` && `rainbow:unicode/1f308,` && `rainbow_flag:unicode/1f3f3-1f308,` && `raised_back_of_hand:unicode/1f91a,` && `raised_eyebrow:unicode/1f928,` && `raised_hand:unicode/270b,` && `raised_hand_with_fingers_splayed:unicode/1f590,` && `raised_hands:unicode/1f64c,` && `raising_hand:unicode/1f64b,` && `raising_hand_man:unicode/1f64b-2642,` && `raising_hand_woman:unicode/1f64b-2640,` && `ram:unicode/1f40f,` && `ramen:unicode/1f35c,` && `rat:unicode/1f400,` && `razor:unicode/1fa92,` && `receipt:unicode/1f9fe,` && `record_button:unicode/23fa,` && `recycle:unicode/267b,` && `red_car:unicode/1f697,` && `red_circle:unicode/1f534,` && `red_envelope:unicode/1f9e7,` && `red_haired_man:unicode/1f468-1f9b0,` && `red_haired_woman:unicode/1f469-1f9b0,` && `red_square:unicode/1f7e5,` && `registered:unicode/00ae,` && `relaxed:unicode/263a,` && `relieved:unicode/1f60c,` && `reminder_ribbon:unicode/1f397,` && `repeat:unicode/1f501,` && `repeat_one:unicode/1f502,` && `rescue_worker_helmet:unicode/26d1,` && `restroom:unicode/1f6bb,` && `reunion:unicode/1f1f7-1f1ea,` && `revolving_hearts:unicode/1f49e,` && `rewind:unicode/23ea,` && `rhinoceros:unicode/1f98f,` && `ribbon:unicode/1f380,` && `rice:unicode/1f35a,` && `rice_ball:unicode/1f359,` && `rice_cracker:unicode/1f358,` && `rice_scene:unicode/1f391,` && `right_anger_bubble:unicode/1f5ef,` && `rightwards_hand:unicode/1faf1,` && `rightwards_pushing_hand:unicode/1faf8,` && `ring:unicode/1f48d,` && `ring_buoy:unicode/1f6df,` && `ringed_planet:unicode/1fa90,` && `robot:unicode/1f916,` && `rock:unicode/1faa8,` && `rocket:unicode/1f680,` && `rofl:unicode/1f923,` && `roll_eyes:unicode/1f644,` && `roll_of_paper:unicode/1f9fb,` && `roller_coaster:unicode/1f3a2,` && `roller_skate:unicode/1f6fc,` && `romania:unicode/1f1f7-1f1f4,` && `rooster:unicode/1f413,` && `rose:unicode/1f339,` && `rosette:unicode/1f3f5,` && `rotating_light:unicode/1f6a8,` && `round_pushpin:unicode/1f4cd,` && `rowboat:unicode/1f6a3,` && `rowing_man:unicode/1f6a3-2642,` && `rowing_woman:unicode/1f6a3-2640,` && `ru:unicode/1f1f7-1f1fa,` && `rugby_football:unicode/1f3c9,` && `runner:unicode/1f3c3,` && `running:unicode/1f3c3,` && `running_man:unicode/1f3c3-2642,` && `running_shirt_with_sash:unicode/1f3bd,` && `running_woman:unicode/1f3c3-2640,` && `rwanda:unicode/1f1f7-1f1fc,` && `sa:unicode/1f202,` && `safety_pin:unicode/1f9f7,` && `safety_vest:unicode/1f9ba,` && `sagittarius:unicode/2650,` && `sailboat:unicode/26f5,` && `sake:unicode/1f376,` && `salt:unicode/1f9c2,` && `saluting_face:unicode/1fae1,` && `samoa:unicode/1f1fc-1f1f8,` && `san_marino:unicode/1f1f8-1f1f2,` && `sandal:unicode/1f461,` && `sandwich:unicode/1f96a,` && `santa:unicode/1f385,` && `sao_tome_principe:unicode/1f1f8-1f1f9,` && `sari:unicode/1f97b,` && `sassy_man:unicode/1f481-2642,` && `sassy_woman:unicode/1f481-2640,` && `satellite:unicode/1f4e1,` && `satisfied:unicode/1f606,` && `saudi_arabia:unicode/1f1f8-1f1e6,` && `sauna_man:unicode/1f9d6-2642,` && `sauna_person:unicode/1f9d6,` && `sauna_woman:unicode/1f9d6-2640,` && `sauropod:unicode/1f995,` && `saxophone:unicode/1f3b7,` && `scarf:unicode/1f9e3,` && `school:unicode/1f3eb,` && `school_satchel:unicode/1f392,` && `scientist:unicode/1f9d1-1f52c,` && `scissors:unicode/2702,` && `scorpion:unicode/1f982,` && `scorpius:unicode/264f,` && `scotland:unicode/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f,` && `scream:unicode/1f631,` && `scream_cat:unicode/1f640,` && `screwdriver:unicode/1fa9b,` && `scroll:unicode/1f4dc,` && `seal:unicode/1f9ad,` && `seat:unicode/1f4ba,` && `secret:unicode/3299,` && `see_no_evil:unicode/1f648,` && `seedling:unicode/1f331,` && `selfie:unicode/1f933,` && `senegal:unicode/1f1f8-1f1f3,`. list = list && `serbia:unicode/1f1f7-1f1f8,` && `service_dog:unicode/1f415-1f9ba,` && `seven:unicode/0037-20e3,` && `sewing_needle:unicode/1faa1,` && `seychelles:unicode/1f1f8-1f1e8,` && `shaking_face:unicode/1fae8,` && `shallow_pan_of_food:unicode/1f958,` && `shamrock:unicode/2618,` && `shark:unicode/1f988,` && `shaved_ice:unicode/1f367,` && `sheep:unicode/1f411,` && `shell:unicode/1f41a,` && `shield:unicode/1f6e1,` && `shinto_shrine:unicode/26e9,` && `ship:unicode/1f6a2,` && `shipit:shipit,` && `shirt:unicode/1f455,` && `shit:unicode/1f4a9,` && `shoe:unicode/1f45e,` && `shopping:unicode/1f6cd,` && `shopping_cart:unicode/1f6d2,` && `shorts:unicode/1fa73,` && `shower:unicode/1f6bf,` && `shrimp:unicode/1f990,` && `shrug:unicode/1f937,` && `shushing_face:unicode/1f92b,` && `sierra_leone:unicode/1f1f8-1f1f1,` && `signal_strength:unicode/1f4f6,` && `singapore:unicode/1f1f8-1f1ec,` && `singer:unicode/1f9d1-1f3a4,` && `sint_maarten:unicode/1f1f8-1f1fd,` && `six:unicode/0036-20e3,` && `six_pointed_star:unicode/1f52f,` && `skateboard:unicode/1f6f9,` && `ski:unicode/1f3bf,` && `skier:unicode/26f7,` && `skull:unicode/1f480,` && `skull_and_crossbones:unicode/2620,` && `skunk:unicode/1f9a8,` && `sled:unicode/1f6f7,` && `sleeping:unicode/1f634,` && `sleeping_bed:unicode/1f6cc,` && `sleepy:unicode/1f62a,` && `slightly_frowning_face:unicode/1f641,` && `slightly_smiling_face:unicode/1f642,` && `slot_machine:unicode/1f3b0,` && `sloth:unicode/1f9a5,` && `slovakia:unicode/1f1f8-1f1f0,` && `slovenia:unicode/1f1f8-1f1ee,` && `small_airplane:unicode/1f6e9,` && `small_blue_diamond:unicode/1f539,` && `small_orange_diamond:unicode/1f538,` && `small_red_triangle:unicode/1f53a,` && `small_red_triangle_down:unicode/1f53b,` && `smile:unicode/1f604,` && `smile_cat:unicode/1f638,` && `smiley:unicode/1f603,` && `smiley_cat:unicode/1f63a,` && `smiling_face_with_tear:unicode/1f972,` && `smiling_face_with_three_hearts:unicode/1f970,` && `smiling_imp:unicode/1f608,` && `smirk:unicode/1f60f,` && `smirk_cat:unicode/1f63c,` && `smoking:unicode/1f6ac,` && `snail:unicode/1f40c,` && `snake:unicode/1f40d,` && `sneezing_face:unicode/1f927,` && `snowboarder:unicode/1f3c2,` && `snowflake:unicode/2744,` && `snowman:unicode/26c4,` && `snowman_with_snow:unicode/2603,` && `soap:unicode/1f9fc,` && `sob:unicode/1f62d,` && `soccer:unicode/26bd,` && `socks:unicode/1f9e6,` && `softball:unicode/1f94e,` && `solomon_islands:unicode/1f1f8-1f1e7,` && `somalia:unicode/1f1f8-1f1f4,` && `soon:unicode/1f51c,` && `sos:unicode/1f198,` && `sound:unicode/1f509,` && `south_africa:unicode/1f1ff-1f1e6,` && `south_georgia_south_sandwich_islands:unicode/1f1ec-1f1f8,` && `south_sudan:unicode/1f1f8-1f1f8,` && `space_invader:unicode/1f47e,` && `spades:unicode/2660,` && `spaghetti:unicode/1f35d,` && `sparkle:unicode/2747,` && `sparkler:unicode/1f387,` && `sparkles:unicode/2728,` && `sparkling_heart:unicode/1f496,` && `speak_no_evil:unicode/1f64a,` && `speaker:unicode/1f508,` && `speaking_head:unicode/1f5e3,` && `speech_balloon:unicode/1f4ac,` && `speedboat:unicode/1f6a4,` && `spider:unicode/1f577,` && `spider_web:unicode/1f578,` && `spiral_calendar:unicode/1f5d3,` && `spiral_notepad:unicode/1f5d2,` && `sponge:unicode/1f9fd,` && `spoon:unicode/1f944,` && `squid:unicode/1f991,` && `sri_lanka:unicode/1f1f1-1f1f0,` && `st_barthelemy:unicode/1f1e7-1f1f1,` && `st_helena:unicode/1f1f8-1f1ed,` && `st_kitts_nevis:unicode/1f1f0-1f1f3,` && `st_lucia:unicode/1f1f1-1f1e8,` && `st_martin:unicode/1f1f2-1f1eb,` && `st_pierre_miquelon:unicode/1f1f5-1f1f2,` && `st_vincent_grenadines:unicode/1f1fb-1f1e8,` && `stadium:unicode/1f3df,` && `standing_man:unicode/1f9cd-2642,` && `standing_person:unicode/1f9cd,` && `standing_woman:unicode/1f9cd-2640,` && `star:unicode/2b50,` && `star2:unicode/1f31f,` && `star_and_crescent:unicode/262a,` && `star_of_david:unicode/2721,` && `star_struck:unicode/1f929,` && `stars:unicode/1f320,` && `station:unicode/1f689,` && `statue_of_liberty:unicode/1f5fd,` && `steam_locomotive:unicode/1f682,` && `stethoscope:unicode/1fa7a,` && `stew:unicode/1f372,` && `stop_button:unicode/23f9,` && `stop_sign:unicode/1f6d1,` && `stopwatch:unicode/23f1,` && `straight_ruler:unicode/1f4cf,` && `strawberry:unicode/1f353,` && `stuck_out_tongue:unicode/1f61b,` && `stuck_out_tongue_closed_eyes:unicode/1f61d,` && `stuck_out_tongue_winking_eye:unicode/1f61c,` && `student:unicode/1f9d1-1f393,` && `studio_microphone:unicode/1f399,` && `stuffed_flatbread:unicode/1f959,` && `sudan:unicode/1f1f8-1f1e9,` && `sun_behind_large_cloud:unicode/1f325,` && `sun_behind_rain_cloud:unicode/1f326,` && `sun_behind_small_cloud:unicode/1f324,` && `sun_with_face:unicode/1f31e,` && `sunflower:unicode/1f33b,` && `sunglasses:unicode/1f60e,` && `sunny:unicode/2600,` && `sunrise:unicode/1f305,` && `sunrise_over_mountains:unicode/1f304,` && `superhero:unicode/1f9b8,` && `superhero_man:unicode/1f9b8-2642,` && `superhero_woman:unicode/1f9b8-2640,` && `supervillain:unicode/1f9b9,` && `supervillain_man:unicode/1f9b9-2642,` && `supervillain_woman:unicode/1f9b9-2640,` && `surfer:unicode/1f3c4,` && `surfing_man:unicode/1f3c4-2642,` && `surfing_woman:unicode/1f3c4-2640,` && `suriname:unicode/1f1f8-1f1f7,` && `sushi:unicode/1f363,` && `suspect:suspect,` && `suspension_railway:unicode/1f69f,` && `svalbard_jan_mayen:unicode/1f1f8-1f1ef,` && `swan:unicode/1f9a2,` && `swaziland:unicode/1f1f8-1f1ff,` && `sweat:unicode/1f613,` && `sweat_drops:unicode/1f4a6,` && `sweat_smile:unicode/1f605,` && `sweden:unicode/1f1f8-1f1ea,` && `sweet_potato:unicode/1f360,` && `swim_brief:unicode/1fa72,` && `swimmer:unicode/1f3ca,` && `swimming_man:unicode/1f3ca-2642,` && `swimming_woman:unicode/1f3ca-2640,` && `switzerland:unicode/1f1e8-1f1ed,` && `symbols:unicode/1f523,` && `synagogue:unicode/1f54d,` && `syria:unicode/1f1f8-1f1fe,` && `syringe:unicode/1f489,` && `t-rex:unicode/1f996,` && `taco:unicode/1f32e,` && `tada:unicode/1f389,` && `taiwan:unicode/1f1f9-1f1fc,` && `tajikistan:unicode/1f1f9-1f1ef,` && `takeout_box:unicode/1f961,` && `tamale:unicode/1fad4,` && `tanabata_tree:unicode/1f38b,` && `tangerine:unicode/1f34a,` && `tanzania:unicode/1f1f9-1f1ff,` && `taurus:unicode/2649,` && `taxi:unicode/1f695,` && `tea:unicode/1f375,` && `teacher:unicode/1f9d1-1f3eb,` && `teapot:unicode/1fad6,` && `technologist:unicode/1f9d1-1f4bb,` && `teddy_bear:unicode/1f9f8,` && `telephone:unicode/260e,` && `telephone_receiver:unicode/1f4de,` && `telescope:unicode/1f52d,` && `tennis:unicode/1f3be,` && `tent:unicode/26fa,` && `test_tube:unicode/1f9ea,` && `thailand:unicode/1f1f9-1f1ed,` && `thermometer:unicode/1f321,` && `thinking:unicode/1f914,` && `thong_sandal:unicode/1fa74,` && `thought_balloon:unicode/1f4ad,` && `thread:unicode/1f9f5,` && `three:unicode/0033-20e3,` && `thumbsdown:unicode/1f44e,` && `thumbsup:unicode/1f44d,` && `ticket:unicode/1f3ab,` && `tickets:unicode/1f39f,` && `tiger:unicode/1f42f,` && `tiger2:unicode/1f405,` && `timer_clock:unicode/23f2,` && `timor_leste:unicode/1f1f9-1f1f1,` && `tipping_hand_man:unicode/1f481-2642,` && `tipping_hand_person:unicode/1f481,` && `tipping_hand_woman:unicode/1f481-2640,` && `tired_face:unicode/1f62b,` && `tm:unicode/2122,` && `togo:unicode/1f1f9-1f1ec,` && `toilet:unicode/1f6bd,` && `tokelau:unicode/1f1f9-1f1f0,` && `tokyo_tower:unicode/1f5fc,` && `tomato:unicode/1f345,` && `tonga:unicode/1f1f9-1f1f4,` && `tongue:unicode/1f445,` && `toolbox:unicode/1f9f0,` && `tooth:unicode/1f9b7,` && `toothbrush:unicode/1faa5,` && `top:unicode/1f51d,` && `tophat:unicode/1f3a9,` && `tornado:unicode/1f32a,` && `tr:unicode/1f1f9-1f1f7,` && `trackball:unicode/1f5b2,` && `tractor:unicode/1f69c,` && `traffic_light:unicode/1f6a5,` && `train:unicode/1f68b,` && `train2:unicode/1f686,` && `tram:unicode/1f68a,` && `transgender_flag:unicode/1f3f3-26a7,` && `transgender_symbol:unicode/26a7,` && `triangular_flag_on_post:unicode/1f6a9,` && `triangular_ruler:unicode/1f4d0,` && `trident:unicode/1f531,` && `trinidad_tobago:unicode/1f1f9-1f1f9,` && `tristan_da_cunha:unicode/1f1f9-1f1e6,` && `triumph:unicode/1f624,` && `troll:unicode/1f9cc,` && `trolleybus:unicode/1f68e,` && `trollface:trollface,` && `trophy:unicode/1f3c6,` && `tropical_drink:unicode/1f379,` && `tropical_fish:unicode/1f420,` && `truck:unicode/1f69a,` && `trumpet:unicode/1f3ba,` && `tshirt:unicode/1f455,` && `tulip:unicode/1f337,` && `tumbler_glass:unicode/1f943,` && `tunisia:unicode/1f1f9-1f1f3,` && `turkey:unicode/1f983,` && `turkmenistan:unicode/1f1f9-1f1f2,` && `turks_caicos_islands:unicode/1f1f9-1f1e8,` && `turtle:unicode/1f422,` && `tuvalu:unicode/1f1f9-1f1fb,` && `tv:unicode/1f4fa,` && `twisted_rightwards_arrows:unicode/1f500,` && `two:unicode/0032-20e3,` && `two_hearts:unicode/1f495,` && `two_men_holding_hands:unicode/1f46c,` && `two_women_holding_hands:unicode/1f46d,` && `u5272:unicode/1f239,` && `u5408:unicode/1f234,` && `u55b6:unicode/1f23a,` && `u6307:unicode/1f22f,` && `u6708:unicode/1f237,` && `u6709:unicode/1f236,` && `u6e80:unicode/1f235,` && `u7121:unicode/1f21a,` && `u7533:unicode/1f238,` && `u7981:unicode/1f232,` && `u7a7a:unicode/1f233,` && `uganda:unicode/1f1fa-1f1ec,` && `uk:unicode/1f1ec-1f1e7,` && `ukraine:unicode/1f1fa-1f1e6,` && `umbrella:unicode/2614,` && `unamused:unicode/1f612,` && `underage:unicode/1f51e,` && `unicorn:unicode/1f984,` && `united_arab_emirates:unicode/1f1e6-1f1ea,` && `united_nations:unicode/1f1fa-1f1f3,` && `unlock:unicode/1f513,` && `up:unicode/1f199,` && `upside_down_face:unicode/1f643,` && `uruguay:unicode/1f1fa-1f1fe,` && `us:unicode/1f1fa-1f1f8,` && `us_outlying_islands:unicode/1f1fa-1f1f2,` && `us_virgin_islands:unicode/1f1fb-1f1ee,` && `uzbekistan:unicode/1f1fa-1f1ff,` && `v:unicode/270c,` && `vampire:unicode/1f9db,` && `vampire_man:unicode/1f9db-2642,` && `vampire_woman:unicode/1f9db-2640,` && `vanuatu:unicode/1f1fb-1f1fa,` && `vatican_city:unicode/1f1fb-1f1e6,` && `venezuela:unicode/1f1fb-1f1ea,` && `vertical_traffic_light:unicode/1f6a6,` && `vhs:unicode/1f4fc,` && `vibration_mode:unicode/1f4f3,` && `video_camera:unicode/1f4f9,` && `video_game:unicode/1f3ae,` && `vietnam:unicode/1f1fb-1f1f3,` && `violin:unicode/1f3bb,` && `virgo:unicode/264d,` && `volcano:unicode/1f30b,` && `volleyball:unicode/1f3d0,` && `vomiting_face:unicode/1f92e,` && `vs:unicode/1f19a,` && `vulcan_salute:unicode/1f596,` && `waffle:unicode/1f9c7,` && `wales:unicode/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f,` && `walking:unicode/1f6b6,` && `walking_man:unicode/1f6b6-2642,` && `walking_woman:unicode/1f6b6-2640,` && `wallis_futuna:unicode/1f1fc-1f1eb,` && `waning_crescent_moon:unicode/1f318,` && `waning_gibbous_moon:unicode/1f316,` && `warning:unicode/26a0,` && `wastebasket:unicode/1f5d1,` && `watch:unicode/231a,` && `water_buffalo:unicode/1f403,` && `water_polo:unicode/1f93d,` && `watermelon:unicode/1f349,` && `wave:unicode/1f44b,` && `wavy_dash:unicode/3030,` && `waxing_crescent_moon:unicode/1f312,` && `waxing_gibbous_moon:unicode/1f314,` && `wc:unicode/1f6be,` && `weary:unicode/1f629,` && `wedding:unicode/1f492,` && `weight_lifting:unicode/1f3cb,` && `weight_lifting_man:unicode/1f3cb-2642,` && `weight_lifting_woman:unicode/1f3cb-2640,` && `western_sahara:unicode/1f1ea-1f1ed,` && `whale:unicode/1f433,` && `whale2:unicode/1f40b,` && `wheel:unicode/1f6de,` && `wheel_of_dharma:unicode/2638,` && `wheelchair:unicode/267f,` && `white_check_mark:unicode/2705,` && `white_circle:unicode/26aa,` && `white_flag:unicode/1f3f3,` && `white_flower:unicode/1f4ae,` && `white_haired_man:unicode/1f468-1f9b3,` && `white_haired_woman:unicode/1f469-1f9b3,` && `white_heart:unicode/1f90d,` && `white_large_square:unicode/2b1c,` && `white_medium_small_square:unicode/25fd,` && `white_medium_square:unicode/25fb,` && `white_small_square:unicode/25ab,` && `white_square_button:unicode/1f533,` && `wilted_flower:unicode/1f940,` && `wind_chime:unicode/1f390,` && `wind_face:unicode/1f32c,` && `window:unicode/1fa9f,` && `wine_glass:unicode/1f377,` && `wing:unicode/1fabd,` && `wink:unicode/1f609,` && `wireless:unicode/1f6dc,` && `wolf:unicode/1f43a,` && `woman:unicode/1f469,` && `woman_artist:unicode/1f469-1f3a8,` && `woman_astronaut:unicode/1f469-1f680,` && `woman_beard:unicode/1f9d4-2640,` && `woman_cartwheeling:unicode/1f938-2640,` && `woman_cook:unicode/1f469-1f373,` && `woman_dancing:unicode/1f483,` && `woman_facepalming:unicode/1f926-2640,` && `woman_factory_worker:unicode/1f469-1f3ed,` && `woman_farmer:unicode/1f469-1f33e,` && `woman_feeding_baby:unicode/1f469-1f37c,` && `woman_firefighter:unicode/1f469-1f692,` && `woman_health_worker:unicode/1f469-2695,` && `woman_in_manual_wheelchair:unicode/1f469-1f9bd,` && `woman_in_motorized_wheelchair:unicode/1f469-1f9bc,` && `woman_in_tuxedo:unicode/1f935-2640,` && `woman_judge:unicode/1f469-2696,` && `woman_juggling:unicode/1f939-2640,` && `woman_mechanic:unicode/1f469-1f527,` && `woman_office_worker:unicode/1f469-1f4bc,` && `woman_pilot:unicode/1f469-2708,` && `woman_playing_handball:unicode/1f93e-2640,` && `woman_playing_water_polo:unicode/1f93d-2640,` && `woman_scientist:unicode/1f469-1f52c,` && `woman_shrugging:unicode/1f937-2640,` && `woman_singer:unicode/1f469-1f3a4,` && `woman_student:unicode/1f469-1f393,` && `woman_teacher:unicode/1f469-1f3eb,` && `woman_technologist:unicode/1f469-1f4bb,` && `woman_with_headscarf:unicode/1f9d5,` && `woman_with_probing_cane:unicode/1f469-1f9af,` && `woman_with_turban:unicode/1f473-2640,` && `woman_with_veil:unicode/1f470-2640,` && `womans_clothes:unicode/1f45a,` && `womans_hat:unicode/1f452,` && `women_wrestling:unicode/1f93c-2640,` && `womens:unicode/1f6ba,` && `wood:unicode/1fab5,` && `woozy_face:unicode/1f974,` && `world_map:unicode/1f5fa,` && `worm:unicode/1fab1,` && `worried:unicode/1f61f,` && `wrench:unicode/1f527,` && `wrestling:unicode/1f93c,` && `writing_hand:unicode/270d,` && `x:unicode/274c,` && `x_ray:unicode/1fa7b,` && `yarn:unicode/1f9f6,` && `yawning_face:unicode/1f971,` && `yellow_circle:unicode/1f7e1,` && `yellow_heart:unicode/1f49b,` && `yellow_square:unicode/1f7e8,` && `yemen:unicode/1f1fe-1f1ea,` && `yen:unicode/1f4b4,` && `yin_yang:unicode/262f,` && `yo_yo:unicode/1fa80,` && `yum:unicode/1f60b,` && `zambia:unicode/1f1ff-1f1f2,` && `zany_face:unicode/1f92a,` && `zap:unicode/26a1,` && `zebra:unicode/1f993,` && `zero:unicode/0030-20e3,` && `zimbabwe:unicode/1f1ff-1f1fc,` && `zipper_mouth_face:unicode/1f910,` && `zombie:unicode/1f9df,` && `zombie_man:unicode/1f9df-2642,` && `zombie_woman:unicode/1f9df-2640,` && `zzz:unicode/1f4a4`. SPLIT list AT ',' INTO TABLE result. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_emoji IMPLEMENTATION. METHOD constructor. init_emoji_list( ). ENDMETHOD. METHOD create. IF emoji IS INITIAL. emoji = NEW #( ). ENDIF. result = emoji. ENDMETHOD. METHOD find_emoji. LOOP AT emojis ASSIGNING FIELD-SYMBOL(). IF find( val = -name regex = regex case = abap_false ) >= 0 ##REGEX_POSIX. INSERT -name INTO TABLE result. ENDIF. ENDLOOP. ENDMETHOD. METHOD format_emoji. result = line. IF base_url IS INITIAL. DATA(base) = c_base_url. ELSE. base = base_url. ENDIF. IF substring( val = base off = strlen( base ) - 1 len = 1 ) <> '/'. base = base && '/'. ENDIF. DATA(has_names) = xsdbool( result CA ':' ). DATA(has_hex) = xsdbool( result CS '&#x' ). LOOP AT emojis ASSIGNING FIELD-SYMBOL(). DATA(html) = ||. IF has_names = abap_true. DATA(emoji) = |:{ -name }:|. REPLACE ALL OCCURRENCES OF emoji IN result WITH html IGNORING CASE. ENDIF. IF has_hex = abap_true AND -hex IS NOT INITIAL. DATA(code) = |&#x{ -hex };|. REPLACE ALL OCCURRENCES OF code IN result WITH html IGNORING CASE. ENDIF. IF -code IS NOT INITIAL. REPLACE ALL OCCURRENCES OF -code IN result WITH html. ENDIF. ENDLOOP. ENDMETHOD. METHOD get_emoji_css. INSERT `.emoji {` INTO TABLE result. INSERT ` display: inline-block;` INTO TABLE result. INSERT ` min-width: 1ch;` INTO TABLE result. INSERT ` font-family: "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";` INTO TABLE result. INSERT ` font-size: 1em;` INTO TABLE result. INSERT ` font-style: normal !important;` INTO TABLE result. INSERT ` font-weight: 400;` INTO TABLE result. INSERT | height: { size_in_px }px;| INTO TABLE result. INSERT | width: { size_in_px }px;| INTO TABLE result. INSERT ` line-height: 1;` INTO TABLE result. INSERT ` vertical-align: -3px;` INTO TABLE result. INSERT `}` INTO TABLE result. ENDMETHOD. METHOD get_emoji_list. LOOP AT emojis ASSIGNING FIELD-SYMBOL(). INSERT -name INTO TABLE result. ENDLOOP. ENDMETHOD. METHOD init_emoji_list. emojis = lcl_github_emoji=>get( ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_exception_viewer IMPLEMENTATION. METHOD add_row. DATA: lo_row TYPE REF TO cl_salv_form_layout_flow. lo_row = io_grid->add_row( ). lo_row->create_label( position = 1 text = iv_col_1 ). lo_row->create_label( position = 2 text = iv_col_2 ). ENDMETHOD. METHOD build_top_of_list. DATA: lo_grid TYPE REF TO cl_salv_form_layout_grid. CREATE OBJECT lo_grid EXPORTING columns = 2. add_row( io_grid = lo_grid iv_col_1 = 'Main program:' iv_col_2 = is_top_of_stack-mainprogram ). add_row( io_grid = lo_grid iv_col_1 = 'Include name:' iv_col_2 = is_top_of_stack-include ). add_row( io_grid = lo_grid iv_col_1 = 'Source line' iv_col_2 = |{ is_top_of_stack-line }| ). ro_form = lo_grid. ENDMETHOD. METHOD constructor. mx_error = ix_error. mt_callstack = mx_error->callstack. ENDMETHOD. METHOD extract_classname. rv_classname = substring_before( val = iv_mainprogram regex = '=*CP$' ) ##REGEX_POSIX. ENDMETHOD. METHOD get_top_of_callstack. READ TABLE mt_callstack INDEX 1 INTO rs_top_of_callstack. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Callstack is empty'. ENDIF. ENDMETHOD. METHOD goto_message. DATA: lv_msg TYPE c LENGTH 100. DATA: lt_bdcdata TYPE STANDARD TABLE OF bdcdata, ls_bdcdata LIKE LINE OF lt_bdcdata. ls_bdcdata-program = 'SAPLWBMESSAGES'. ls_bdcdata-dynpro = '0100'. ls_bdcdata-dynbegin = abap_true. INSERT ls_bdcdata INTO TABLE lt_bdcdata. CLEAR: ls_bdcdata. ls_bdcdata-fnam = 'RSDAG-ARBGB'. ls_bdcdata-fval = mx_error->if_t100_message~t100key-msgid. INSERT ls_bdcdata INTO TABLE lt_bdcdata. CLEAR: ls_bdcdata. ls_bdcdata-fnam = 'MSG_NUMMER'. ls_bdcdata-fval = mx_error->if_t100_message~t100key-msgno. INSERT ls_bdcdata INTO TABLE lt_bdcdata. CLEAR: ls_bdcdata. ls_bdcdata-fnam = 'RSDAG-MSGFLAG'. ls_bdcdata-fval = 'X'. INSERT ls_bdcdata INTO TABLE lt_bdcdata. CLEAR: ls_bdcdata. ls_bdcdata-fnam = 'BDC_OKCODE'. ls_bdcdata-fval = '=WB_DISPLAY'. INSERT ls_bdcdata INTO TABLE lt_bdcdata. CALL FUNCTION 'ABAP4_CALL_TRANSACTION' STARTING NEW TASK 'APM' EXPORTING tcode = 'SE91' mode_val = 'E' TABLES using_tab = lt_bdcdata EXCEPTIONS system_failure = 1 MESSAGE lv_msg communication_failure = 2 MESSAGE lv_msg resource_failure = 3 OTHERS = 4. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = lv_msg. ENDIF. ENDMETHOD. METHOD goto_source. goto_source_code( get_top_of_callstack( ) ). ENDMETHOD. METHOD goto_source_code. CONSTANTS: BEGIN OF lc_obj_type, class TYPE trobjtype VALUE `CLAS`, program TYPE trobjtype VALUE `PROG`, END OF lc_obj_type. DATA: ls_item TYPE zif_abapgit_definitions=>ty_item, ls_sub_item TYPE zif_abapgit_definitions=>ty_item, lv_classname LIKE ls_item-obj_name. " you should remember that we distinct two cases " 1) we navigate to a global class " 2) we navigate to a program " the latter one is the default case lv_classname = extract_classname( is_callstack-mainprogram ). IF lv_classname IS NOT INITIAL. ls_item-obj_name = lv_classname. ls_item-obj_type = lc_obj_type-class. ELSE. ls_item-obj_name = is_callstack-mainprogram. ls_item-obj_type = lc_obj_type-program. ENDIF. ls_sub_item-obj_name = is_callstack-include. ls_sub_item-obj_type = lc_obj_type-program. TRY. /apmg/cl_apm_abapgit_objects=>jump( is_item = ls_item is_sub_item = ls_sub_item iv_line_number = is_callstack-line ). CATCH cx_root ##NO_HANDLER. ENDTRY. ENDMETHOD. METHOD on_double_click. DATA: lx_error TYPE REF TO /apmg/cx_apm_error. FIELD-SYMBOLS: TYPE abap_callstack_line. READ TABLE mt_callstack ASSIGNING INDEX row. IF sy-subrc <> 0. RETURN. ENDIF. TRY. goto_source_code( ). CATCH /apmg/cx_apm_error INTO lx_error. MESSAGE lx_error TYPE 'S' DISPLAY LIKE 'E'. ENDTRY. ENDMETHOD. METHOD set_text. DATA: lo_column TYPE REF TO cl_salv_column, lv_short_text TYPE scrtext_s, lv_medium_text TYPE scrtext_m, lv_long_text TYPE scrtext_l. lo_column = io_columns->get_column( iv_column ). lv_short_text = iv_text. lv_medium_text = iv_text. lv_long_text = iv_text. lo_column->set_short_text( lv_short_text ). lo_column->set_medium_text( lv_medium_text ). lo_column->set_long_text( lv_long_text ). ENDMETHOD. METHOD show_callstack. DATA: lx_error TYPE REF TO cx_static_check, lo_event TYPE REF TO cl_salv_events_table, lo_columns TYPE REF TO cl_salv_columns_table, lo_alv TYPE REF TO cl_salv_table. DATA ls_position TYPE /apmg/if_apm_popups=>ty_popup_position. TRY. cl_salv_table=>factory( IMPORTING r_salv_table = lo_alv CHANGING t_table = mt_callstack ). lo_alv->get_columns( )->set_optimize( ). lo_alv->set_top_of_list( build_top_of_list( get_top_of_callstack( ) ) ). ls_position = /apmg/cl_apm_popups=>center( iv_width = 150 iv_height = 25 ). lo_alv->set_screen_popup( start_column = ls_position-start_column end_column = ls_position-end_column start_line = ls_position-start_row end_line = ls_position-end_row ). lo_event = lo_alv->get_event( ). lo_columns = lo_alv->get_columns( ). set_text( io_columns = lo_columns iv_column = |LINE| iv_text = |Line| ). set_text( io_columns = lo_columns iv_column = |LINE| iv_text = |Line| ). set_text( io_columns = lo_columns iv_column = |BLOCKTYPE| iv_text = |Event Type| ). set_text( io_columns = lo_columns iv_column = |BLOCKNAME| iv_text = |Event| ). set_text( io_columns = lo_columns iv_column = |FLAG_SYSTEM| iv_text = |System| ). SET HANDLER on_double_click FOR lo_event. lo_alv->display( ). CATCH cx_static_check INTO lx_error. MESSAGE lx_error TYPE 'S' DISPLAY LIKE 'E'. ENDTRY. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_file_importer IMPLEMENTATION. METHOD /apmg/if_apm_file_importer~get_abap. DATA(code) = get_file_content( extra = extra extension = 'abap' ). SPLIT code AT cl_abap_char_utilities=>newline INTO TABLE result. ENDMETHOD. METHOD /apmg/if_apm_file_importer~get_json. result = get_file_content( 'json' ). ENDMETHOD. METHOD /apmg/if_apm_file_importer~get_xml. result = get_file_content( 'xml' ). ENDMETHOD. METHOD /apmg/if_apm_file_importer~get_xml_parsed. TRY. result = NEW zcl_abapgit_xml_input( iv_xml = get_file_content( 'xml' ) iv_filename = get_file_name( 'xml' ) ). CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD constructor. me->item = item. me->files = files. ENDMETHOD. METHOD get_file_content. DATA(filename) = get_file_name( extra = extra extension = extension ). READ TABLE files ASSIGNING FIELD-SYMBOL() WITH KEY file COMPONENTS filename = filename. IF sy-subrc = 0. TRY. " TODO: Replace with ZCL_CONVERT result = zcl_abapgit_convert=>xstring_to_string_utf8( -data ). CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ELSE. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |File { filename } not found|. ENDIF. ENDMETHOD. METHOD get_file_name. IF extra IS INITIAL. result = |{ item-obj_name }.{ item-obj_type }.{ extension }|. ELSE. result = |{ item-obj_name }.{ item-obj_type }.{ extra }.{ extension }|. ENDIF. result = condense( to_lower( result ) ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_frontend_services IMPLEMENTATION. METHOD /apmg/if_apm_frontend_services~clipboard_export. DATA lv_rc TYPE i. " Note: do not use a string table for 'it_data'! TRY. CALL METHOD cl_gui_frontend_services=>('CLIPBOARD_EXPORT') EXPORTING no_auth_check = iv_no_auth_check " >= 740 IMPORTING data = it_data CHANGING rc = lv_rc EXCEPTIONS cntl_error = 1 error_no_gui = 2 not_supported_by_gui = 3 no_authority = 4 OTHERS = 5. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. CATCH cx_sy_dyn_call_param_missing. cl_gui_frontend_services=>clipboard_export( IMPORTING data = it_data CHANGING rc = lv_rc EXCEPTIONS cntl_error = 1 error_no_gui = 2 not_supported_by_gui = 3 no_authority = 4 OTHERS = 5 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. ENDTRY. ENDMETHOD. METHOD /apmg/if_apm_frontend_services~directory_browse. IF iv_initial_folder IS NOT INITIAL. gv_initial_folder = iv_initial_folder. ENDIF. cl_gui_frontend_services=>directory_browse( EXPORTING window_title = iv_window_title initial_folder = gv_initial_folder CHANGING selected_folder = cv_selected_folder EXCEPTIONS cntl_error = 1 error_no_gui = 2 not_supported_by_gui = 3 OTHERS = 4 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. gv_initial_folder = cv_selected_folder. ENDMETHOD. METHOD /apmg/if_apm_frontend_services~directory_create. cl_gui_frontend_services=>directory_create( EXPORTING directory = iv_directory CHANGING rc = cv_rc EXCEPTIONS directory_create_failed = 1 cntl_error = 2 error_no_gui = 3 directory_access_denied = 4 directory_already_exists = 5 path_not_found = 6 unknown_error = 7 not_supported_by_gui = 8 wrong_parameter = 9 OTHERS = 10 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_frontend_services~directory_exist. cl_gui_frontend_services=>directory_exist( EXPORTING directory = iv_directory RECEIVING result = rv_exists EXCEPTIONS cntl_error = 1 error_no_gui = 2 wrong_parameter = 3 not_supported_by_gui = 4 OTHERS = 5 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_frontend_services~execute. cl_gui_frontend_services=>execute( EXPORTING document = iv_document application = iv_application parameter = iv_parameter default_directory = iv_default_directory maximized = iv_maximized minimized = iv_minimized synchronous = iv_synchronous operation = iv_operation EXCEPTIONS cntl_error = 1 error_no_gui = 2 bad_parameter = 3 file_not_found = 4 path_not_found = 5 file_extension_unknown = 6 error_execute_failed = 7 synchronous_failed = 8 not_supported_by_gui = 9 OTHERS = 10 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_frontend_services~file_download. TYPES ty_hex TYPE x LENGTH 200. DATA lt_rawdata TYPE STANDARD TABLE OF ty_hex WITH DEFAULT KEY. zcl_abapgit_convert=>xstring_to_bintab( EXPORTING iv_xstr = iv_xstr IMPORTING et_bintab = lt_rawdata ). cl_gui_frontend_services=>gui_download( EXPORTING bin_filesize = xstrlen( iv_xstr ) filename = iv_path filetype = 'BIN' CHANGING data_tab = lt_rawdata EXCEPTIONS file_write_error = 1 no_batch = 2 gui_refuse_filetransfer = 3 invalid_type = 4 no_authority = 5 unknown_error = 6 header_not_allowed = 7 separator_not_allowed = 8 filesize_not_allowed = 9 header_too_long = 10 dp_error_create = 11 dp_error_send = 12 dp_error_write = 13 unknown_dp_error = 14 access_denied = 15 dp_out_of_memory = 16 disk_full = 17 dp_timeout = 18 file_not_found = 19 dataprovider_exception = 20 control_flush_error = 21 not_supported_by_gui = 22 error_no_gui = 23 OTHERS = 24 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_frontend_services~file_upload. TYPES: ty_hex TYPE x LENGTH 255. DATA: lt_data TYPE TABLE OF ty_hex WITH DEFAULT KEY, lv_length TYPE i. cl_gui_frontend_services=>gui_upload( EXPORTING filename = iv_path filetype = 'BIN' IMPORTING filelength = lv_length CHANGING data_tab = lt_data EXCEPTIONS file_open_error = 1 file_read_error = 2 no_batch = 3 gui_refuse_filetransfer = 4 invalid_type = 5 no_authority = 6 unknown_error = 7 bad_data_format = 8 header_not_allowed = 9 separator_not_allowed = 10 header_too_long = 11 unknown_dp_error = 12 access_denied = 13 dp_out_of_memory = 14 disk_full = 15 dp_timeout = 16 not_supported_by_gui = 17 error_no_gui = 18 OTHERS = 19 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. CONCATENATE LINES OF lt_data INTO rv_xstr IN BYTE MODE. rv_xstr = rv_xstr(lv_length). ENDMETHOD. METHOD /apmg/if_apm_frontend_services~get_file_separator. cl_gui_frontend_services=>get_file_separator( CHANGING file_separator = cv_file_separator EXCEPTIONS not_supported_by_gui = 1 error_no_gui = 2 cntl_error = 3 OTHERS = 4 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_frontend_services~get_gui_version. DATA: lt_version_table TYPE filetable, lv_rc TYPE i, ls_version LIKE LINE OF lt_version_table. cl_gui_frontend_services=>get_gui_version( CHANGING version_table = lt_version_table rc = lv_rc EXCEPTIONS get_gui_version_failed = 1 cant_write_version_table = 2 gui_no_version = 3 cntl_error = 4 error_no_gui = 5 not_supported_by_gui = 6 OTHERS = 7 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. READ TABLE lt_version_table INTO ls_version INDEX 1. " gui release ev_gui_release = ls_version-filename. READ TABLE lt_version_table INTO ls_version INDEX 2. " gui sp ev_gui_sp = ls_version-filename. READ TABLE lt_version_table INTO ls_version INDEX 3. " gui patch ev_gui_patch = ls_version-filename. ev_gui_version_string = |{ ev_gui_release }.{ condense( ev_gui_sp ) }.{ condense( ev_gui_patch ) }|. ENDMETHOD. METHOD /apmg/if_apm_frontend_services~get_system_directory. cl_gui_frontend_services=>get_system_directory( CHANGING system_directory = cv_system_directory EXCEPTIONS cntl_error = 1 error_no_gui = 2 not_supported_by_gui = 3 OTHERS = 4 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_frontend_services~gui_is_available. TRY. CALL FUNCTION 'GUI_IS_AVAILABLE' IMPORTING return = rv_gui_is_available. CATCH cx_sy_dyn_call_illegal_func. * when running on open-abap RETURN. ENDTRY. ENDMETHOD. METHOD /apmg/if_apm_frontend_services~is_sapgui_for_java. TRY. CALL FUNCTION 'GUI_HAS_JAVABEANS' IMPORTING return = rv_result. CATCH cx_sy_dyn_call_illegal_func. * when running on open-abap RETURN. ENDTRY. ENDMETHOD. METHOD /apmg/if_apm_frontend_services~is_sapgui_for_windows. TRY. CALL FUNCTION 'GUI_HAS_ACTIVEX' IMPORTING return = rv_result. CATCH cx_sy_dyn_call_illegal_func. * when running on open-abap RETURN. ENDTRY. ENDMETHOD. METHOD /apmg/if_apm_frontend_services~is_webgui. TRY. CALL FUNCTION 'GUI_IS_ITS' IMPORTING return = rv_is_webgui. CATCH cx_sy_dyn_call_illegal_func. * when running on open-abap RETURN. ENDTRY. ENDMETHOD. METHOD /apmg/if_apm_frontend_services~open_ie_devtools. DATA: lv_system_directory TYPE string, lv_exe_full_path TYPE string. IF /apmg/if_apm_frontend_services~is_sapgui_for_windows( ) = abap_false. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'IE DevTools not supported on frontend OS'. ENDIF. /apmg/if_apm_frontend_services~get_system_directory( CHANGING cv_system_directory = lv_system_directory ). cl_gui_cfw=>flush( ). lv_exe_full_path = lv_system_directory && `\F12\IEChooser.exe`. /apmg/if_apm_frontend_services~execute( iv_application = lv_exe_full_path ). ENDMETHOD. METHOD /apmg/if_apm_frontend_services~show_file_open_dialog. DATA: lt_file_table TYPE filetable, ls_file_table LIKE LINE OF lt_file_table, lv_filter TYPE string, lv_action TYPE i, lv_rc TYPE i. IF iv_extension = 'zip'. lv_filter = 'ZIP Files (*.zip)|*.zip|' && cl_gui_frontend_services=>filetype_all. ENDIF. cl_gui_frontend_services=>file_open_dialog( EXPORTING window_title = iv_title default_filename = iv_default_filename file_filter = lv_filter initial_directory = gv_initial_folder CHANGING file_table = lt_file_table rc = lv_rc user_action = lv_action EXCEPTIONS file_open_dialog_failed = 1 cntl_error = 2 error_no_gui = 3 not_supported_by_gui = 4 OTHERS = 5 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. IF lv_action = cl_gui_frontend_services=>action_cancel. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Cancelled'. ENDIF. READ TABLE lt_file_table INDEX 1 INTO ls_file_table. ASSERT sy-subrc = 0. rv_path = ls_file_table-filename. gv_initial_folder = get_path_from_fullname( rv_path ). ENDMETHOD. METHOD /apmg/if_apm_frontend_services~show_file_save_dialog. DATA: lv_action TYPE i, lv_filter TYPE string, lv_filename TYPE string, lv_path TYPE string. IF iv_extension = 'zip'. lv_filter = 'ZIP Files (*.zip)|*.zip|' && cl_gui_frontend_services=>filetype_all. ENDIF. cl_gui_frontend_services=>file_save_dialog( EXPORTING window_title = iv_title default_extension = iv_extension default_file_name = iv_default_filename file_filter = lv_filter initial_directory = gv_initial_folder CHANGING filename = lv_filename path = lv_path fullpath = rv_path user_action = lv_action EXCEPTIONS cntl_error = 1 error_no_gui = 2 not_supported_by_gui = 3 OTHERS = 4 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. IF lv_action = cl_gui_frontend_services=>action_cancel. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Cancelled'. ENDIF. gv_initial_folder = lv_path. ENDMETHOD. METHOD get_path_from_fullname. DATA lv_len TYPE i. FIND FIRST OCCURRENCE OF REGEX '^/(.*/)?' IN iv_fullname MATCH LENGTH lv_len ##REGEX_POSIX. IF sy-subrc = 0. rv_path = iv_fullname(lv_len). ELSE. FIND FIRST OCCURRENCE OF REGEX '^(.*\\)?' IN iv_fullname MATCH LENGTH lv_len ##REGEX_POSIX. IF sy-subrc = 0. rv_path = iv_fullname(lv_len). ENDIF. ENDIF. ENDMETHOD. ENDCLASS. CLASS zcl_abapgit_log IMPLEMENTATION. METHOD constructor. zif_abapgit_log~set_title( iv_title ). ENDMETHOD. METHOD from_exception. CREATE OBJECT ro_log. IF io_x IS BOUND. ro_log->zif_abapgit_log~add_exception( io_x ). ENDIF. ENDMETHOD. METHOD get_messages_status. DATA lr_msg TYPE REF TO zif_abapgit_log=>ty_msg. rv_status = 'S'. LOOP AT it_msg REFERENCE INTO lr_msg. CASE lr_msg->type. WHEN 'E' OR 'A' OR 'X'. rv_status = 'E'. "not okay EXIT. WHEN 'W'. rv_status = 'W'. "maybe CONTINUE. WHEN 'S' OR 'I'. IF rv_status <> 'W'. rv_status = 'S'. "okay ENDIF. CONTINUE. WHEN OTHERS. "unknown CONTINUE. ENDCASE. ENDLOOP. ENDMETHOD. METHOD zif_abapgit_log~add. FIELD-SYMBOLS: LIKE LINE OF mt_log. APPEND INITIAL LINE TO mt_log ASSIGNING . -msg-text = iv_msg. -msg-type = iv_type. -msg-id = iv_class. -msg-number = iv_number. -item = is_item. -exception = ix_exc. CASE iv_type. WHEN 'E' OR 'A' OR 'X'. -msg-level = zif_abapgit_log=>c_log_level-error. WHEN 'W'. -msg-level = zif_abapgit_log=>c_log_level-warning. WHEN 'S' OR 'I'. -msg-level = zif_abapgit_log=>c_log_level-info. WHEN OTHERS. "unknown ASSERT 0 = 1. ENDCASE. ENDMETHOD. METHOD zif_abapgit_log~add_error. zif_abapgit_log~add( iv_msg = iv_msg iv_type = 'E' is_item = is_item ). ENDMETHOD. METHOD zif_abapgit_log~add_exception. DATA lx_exc TYPE REF TO cx_root. DATA lv_msg TYPE string. lx_exc = ix_exc. DO. lv_msg = lx_exc->get_text( ). zif_abapgit_log~add( iv_msg = lv_msg iv_type = 'E' is_item = is_item ix_exc = lx_exc ). IF lx_exc->previous IS BOUND. lx_exc = lx_exc->previous. ELSE. EXIT. ENDIF. ENDDO. ENDMETHOD. METHOD zif_abapgit_log~add_info. zif_abapgit_log~add( iv_msg = iv_msg iv_type = 'I' is_item = is_item ). ENDMETHOD. METHOD zif_abapgit_log~add_success. zif_abapgit_log~add( iv_msg = iv_msg iv_type = 'S' is_item = is_item ). ENDMETHOD. METHOD zif_abapgit_log~add_warning. zif_abapgit_log~add( iv_msg = iv_msg iv_type = 'W' is_item = is_item ). ENDMETHOD. METHOD zif_abapgit_log~clear. CLEAR mt_log. ENDMETHOD. METHOD zif_abapgit_log~clone. DATA lo_log TYPE REF TO zcl_abapgit_log. CREATE OBJECT lo_log EXPORTING iv_title = mv_title. lo_log->mt_log = mt_log. ri_log = lo_log. ENDMETHOD. METHOD zif_abapgit_log~count. rv_count = lines( mt_log ). ENDMETHOD. METHOD zif_abapgit_log~get_item_status. DATA lr_log TYPE REF TO ty_log. DATA ls_msg TYPE zif_abapgit_log=>ty_msg. DATA ls_item_status TYPE zif_abapgit_log=>ty_item_status_out. DATA lr_item_status TYPE REF TO zif_abapgit_log=>ty_item_status_out. "collect all message for all objects LOOP AT mt_log REFERENCE INTO lr_log. CLEAR ls_item_status. ls_item_status-item = lr_log->item. READ TABLE rt_item_status REFERENCE INTO lr_item_status WITH KEY item-obj_type = ls_item_status-item-obj_type item-obj_name = ls_item_status-item-obj_name. IF sy-subrc <> 0. INSERT ls_item_status INTO TABLE rt_item_status. GET REFERENCE OF ls_item_status INTO lr_item_status. ENDIF. CLEAR ls_msg. ls_msg-type = lr_log->msg-type. ls_msg-text = lr_log->msg-text. INSERT ls_msg INTO TABLE lr_item_status->messages. ENDLOOP. "determine object status from object messages LOOP AT rt_item_status REFERENCE INTO lr_item_status. lr_item_status->status = get_messages_status( lr_item_status->messages ). IF lr_item_status->messages IS INITIAL. CLEAR ls_msg. ls_msg-type = 'I'. ls_msg-text = 'No message'. INSERT ls_msg INTO TABLE lr_item_status->messages. ENDIF. ENDLOOP. ENDMETHOD. METHOD zif_abapgit_log~get_log_level. FIELD-SYMBOLS LIKE LINE OF mt_log. rv_level = zif_abapgit_log=>c_log_level-empty. LOOP AT mt_log ASSIGNING . IF -msg-level = zif_abapgit_log=>c_log_level-error. rv_level = zif_abapgit_log=>c_log_level-error. EXIT. ELSEIF -msg-level > rv_level. rv_level = -msg-level. ENDIF. ENDLOOP. ENDMETHOD. METHOD zif_abapgit_log~get_messages. DATA ls_msg TYPE zif_abapgit_log~ty_log_out. FIELD-SYMBOLS TYPE ty_log. LOOP AT mt_log ASSIGNING . ls_msg-type = -msg-type. ls_msg-id = -msg-id. ls_msg-number = -msg-number. ls_msg-text = -msg-text. ls_msg-obj_type = -item-obj_type. ls_msg-obj_name = -item-obj_name. ls_msg-exception = -exception. APPEND ls_msg TO rt_msg. ENDLOOP. DELETE ADJACENT DUPLICATES FROM rt_msg. ENDMETHOD. METHOD zif_abapgit_log~get_status. DATA lr_log TYPE REF TO ty_log. rv_status = zif_abapgit_log=>c_status-ok. LOOP AT mt_log REFERENCE INTO lr_log. CASE lr_log->msg-type. WHEN 'E' OR 'A' OR 'X'. rv_status = zif_abapgit_log=>c_status-error. EXIT. WHEN 'W'. rv_status = zif_abapgit_log=>c_status-warning. CONTINUE. WHEN 'S' OR 'I'. IF rv_status <> zif_abapgit_log=>c_status-warning. rv_status = zif_abapgit_log=>c_status-ok. ENDIF. CONTINUE. WHEN OTHERS. "unknown ASSERT 0 = 1. ENDCASE. ENDLOOP. ENDMETHOD. METHOD zif_abapgit_log~get_title. rv_title = mv_title. IF rv_title IS INITIAL. rv_title = 'Log'. ENDIF. ENDMETHOD. METHOD zif_abapgit_log~merge_with. DATA lo_log TYPE REF TO zcl_abapgit_log. DATA lt_log_temp LIKE lo_log->mt_log. IF ii_log IS BOUND. lo_log ?= ii_log. IF iv_min_level > 0. lt_log_temp = lo_log->mt_log. DELETE lt_log_temp WHERE msg-level < iv_min_level. APPEND LINES OF lt_log_temp TO mt_log. ELSE. APPEND LINES OF lo_log->mt_log TO mt_log. ENDIF. ENDIF. ri_log = me. ENDMETHOD. METHOD zif_abapgit_log~set_title. mv_title = iv_title. ri_log = me. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_event IMPLEMENTATION. METHOD /apmg/if_apm_gui_event~form_data. IF mo_form_data IS NOT BOUND. mo_form_data = fields_to_map( parse_post_form_data( /apmg/if_apm_gui_event~mt_postdata ) ). mo_form_data->freeze( ). ENDIF. ro_string_map = mo_form_data. ENDMETHOD. METHOD /apmg/if_apm_gui_event~query. IF mo_query IS NOT BOUND. mo_query = fields_to_map( parse_fields( /apmg/if_apm_gui_event~mv_getdata ) ). mo_query->freeze( ). ENDIF. ro_string_map = mo_query. ENDMETHOD. METHOD class_constructor. CONSTANTS lc_nbsp TYPE xstring VALUE 'C2A0'. "   TRY. gv_non_breaking_space = zcl_abapgit_convert=>xstring_to_string_utf8( lc_nbsp ). CATCH zcx_abapgit_exception. " Fallback for non-Unicode systems IF cl_abap_char_utilities=>charsize < 2. gv_non_breaking_space = |X'A0'|. ELSE. ASSERT 0 = 1. ENDIF. ENDTRY. ENDMETHOD. METHOD constructor. " Edge Webview control returns upper case action but abapGit requires lower case (#4841) /apmg/if_apm_gui_event~mi_gui_services = ii_gui_services. /apmg/if_apm_gui_event~mv_action = to_lower( iv_action ). /apmg/if_apm_gui_event~mv_getdata = iv_getdata. /apmg/if_apm_gui_event~mt_postdata = it_postdata. IF ii_gui_services IS BOUND. /apmg/if_apm_gui_event~mv_current_page_name = ii_gui_services->get_current_page_name( ). ENDIF. ENDMETHOD. METHOD fields_to_map. FIELD-SYMBOLS LIKE LINE OF it_fields. CREATE OBJECT ro_string_map EXPORTING iv_case_insensitive = abap_true. LOOP AT it_fields ASSIGNING . ro_string_map->set( iv_key = -name iv_val = -value ). ENDLOOP. ENDMETHOD. METHOD field_keys_to_upper. FIELD-SYMBOLS LIKE LINE OF ct_fields. LOOP AT ct_fields ASSIGNING . -name = to_upper( -name ). ENDLOOP. ENDMETHOD. METHOD new. CREATE OBJECT ro_instance EXPORTING ii_gui_services = ii_gui_services iv_action = iv_action iv_getdata = iv_getdata it_postdata = it_postdata. ENDMETHOD. METHOD parse_fields. DATA: lt_substrings TYPE string_table, ls_field LIKE LINE OF rt_fields. FIELD-SYMBOLS LIKE LINE OF lt_substrings. SPLIT iv_string AT '&' INTO TABLE lt_substrings. LOOP AT lt_substrings ASSIGNING . CLEAR ls_field. " On attempt to change unescaping -> run unit tests to check ! " Unescape name and value separately ls_field-name = unescape( substring_before( val = sub = '=' ) ). ls_field-value = unescape( substring_after( val = sub = '=' ) ). IF ls_field IS INITIAL. " Not a field with proper structure CONTINUE. ENDIF. APPEND ls_field TO rt_fields. ENDLOOP. IF iv_upper_cased = abap_true. field_keys_to_upper( CHANGING ct_fields = rt_fields ). ENDIF. ENDMETHOD. METHOD parse_fields_upper_case_name. rt_fields = parse_fields( iv_string = iv_string iv_upper_cased = abap_true ). ENDMETHOD. METHOD parse_post_form_data. DATA lv_serialized_post_data TYPE string. lv_serialized_post_data = translate_postdata( it_post_data ). IF iv_upper_cased = abap_true. rt_fields = parse_fields_upper_case_name( lv_serialized_post_data ). ELSE. rt_fields = parse_fields( lv_serialized_post_data ). ENDIF. ENDMETHOD. METHOD translate_postdata. DATA: lt_post_data TYPE /apmg/if_apm_html_viewer=>ty_post_data, ls_last_line LIKE LINE OF it_postdata, lv_last_line_index TYPE i. IF it_postdata IS INITIAL. RETURN. "Nothing to do ENDIF. lt_post_data = it_postdata. "Save the last line for separate merge, because we don't need its trailing spaces WHILE ls_last_line IS INITIAL. lv_last_line_index = lines( lt_post_data ). READ TABLE lt_post_data INTO ls_last_line INDEX lv_last_line_index. DELETE lt_post_data INDEX lv_last_line_index. ENDWHILE. CONCATENATE LINES OF lt_post_data INTO rv_string IN CHARACTER MODE RESPECTING BLANKS. CONCATENATE rv_string ls_last_line INTO rv_string IN CHARACTER MODE. ENDMETHOD. METHOD unescape. * do not use cl_http_utility as it does strange things with the encoding rv_string = iv_string. * todo, more to be added here REPLACE ALL OCCURRENCES OF '%3A' IN rv_string WITH ':' IGNORING CASE. REPLACE ALL OCCURRENCES OF '%3F' IN rv_string WITH '?' IGNORING CASE. REPLACE ALL OCCURRENCES OF '%3D' IN rv_string WITH '=' IGNORING CASE. REPLACE ALL OCCURRENCES OF '%2F' IN rv_string WITH '/' IGNORING CASE. REPLACE ALL OCCURRENCES OF '%25' IN rv_string WITH '%' IGNORING CASE. REPLACE ALL OCCURRENCES OF '%26' IN rv_string WITH '&' IGNORING CASE. REPLACE ALL OCCURRENCES OF gv_non_breaking_space IN rv_string WITH ` `. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui IMPLEMENTATION. METHOD /apmg/if_apm_gui_services~cache_asset. TYPES ty_hex TYPE x LENGTH 200. TYPES ty_char TYPE c LENGTH 200. DATA: lt_xdata TYPE STANDARD TABLE OF ty_hex WITH DEFAULT KEY, lv_size TYPE i, lt_html TYPE STANDARD TABLE OF ty_char WITH DEFAULT KEY. ASSERT iv_text IS SUPPLIED OR iv_xdata IS SUPPLIED. IF iv_text IS SUPPLIED. " String input zcl_abapgit_convert=>string_to_tab( EXPORTING iv_str = iv_text IMPORTING ev_size = lv_size et_tab = lt_html ). mi_html_viewer->load_data( EXPORTING iv_type = iv_type iv_subtype = iv_subtype iv_size = lv_size iv_url = iv_url IMPORTING ev_assigned_url = rv_url CHANGING ct_data_table = lt_html ). ELSE. " Raw input zcl_abapgit_convert=>xstring_to_bintab( EXPORTING iv_xstr = iv_xdata IMPORTING ev_size = lv_size et_bintab = lt_xdata ). mi_html_viewer->load_data( EXPORTING iv_type = iv_type iv_subtype = iv_subtype iv_size = lv_size iv_url = iv_url IMPORTING ev_assigned_url = rv_url CHANGING ct_data_table = lt_xdata ). ENDIF. ASSERT sy-subrc = 0. " Image data error ENDMETHOD. METHOD /apmg/if_apm_gui_services~get_current_page_name. DATA li_page_hoc TYPE REF TO /apmg/cl_apm_gui_page_hoc. IF mi_cur_page IS BOUND. rv_page_name = cl_abap_classdescr=>describe_by_object_ref( mi_cur_page )->get_relative_name( ). " For HOC components return name of child component instead IF rv_page_name = '/APMG/CL_APM_GUI_PAGE_HOC'. li_page_hoc ?= mi_cur_page. IF li_page_hoc->get_child( ) IS BOUND. rv_page_name = cl_abap_classdescr=>describe_by_object_ref( li_page_hoc->get_child( ) )->get_relative_name( ). ENDIF. ENDIF. ENDIF." ELSE - return is empty => initial page ENDMETHOD. METHOD /apmg/if_apm_gui_services~get_hotkeys_ctl. ri_hotkey_ctl = mi_hotkey_ctl. ENDMETHOD. METHOD /apmg/if_apm_gui_services~get_html_parts. ro_parts = mo_html_parts. ENDMETHOD. METHOD /apmg/if_apm_gui_services~get_log. IF iv_create_new = abap_true OR mi_common_log IS NOT BOUND. CREATE OBJECT mi_common_log TYPE zcl_abapgit_log. ENDIF. ri_log = mi_common_log. ENDMETHOD. METHOD /apmg/if_apm_gui_services~register_event_handler. ASSERT ii_event_handler IS BOUND. INSERT ii_event_handler INTO mt_event_handlers INDEX 1. ENDMETHOD. METHOD /apmg/if_apm_gui_services~register_page_asset. " Maybe forbid registering cacheable existing assets, maybe this is the right place (see also asset_man comments) " This registering will happen after initialization so all cacheable already cached mi_asset_man->register_asset( iv_url = iv_url iv_type = iv_type iv_mime_name = iv_mime_name iv_inline = iv_inline iv_cacheable = abap_false ). ENDMETHOD. METHOD back. DATA lv_index TYPE i. DATA ls_stack LIKE LINE OF mt_stack. " If viewer is showing Internet page, then use browser navigation IF mi_html_viewer->get_url( ) CP 'http*'. mi_html_viewer->back( ). RETURN. ENDIF. lv_index = lines( mt_stack ). IF lv_index = 0. rv_exit = abap_true. RETURN. ENDIF. IF iv_graceful = abap_true AND back_graceful( ) = abap_true. RETURN. ENDIF. DO lv_index TIMES. READ TABLE mt_stack INDEX lv_index INTO ls_stack. ASSERT sy-subrc = 0. DELETE mt_stack INDEX lv_index. ASSERT sy-subrc = 0. lv_index = lv_index - 1. IF iv_to_bookmark = abap_false OR ls_stack-bookmark = abap_true. EXIT. ENDIF. ENDDO. mi_cur_page = ls_stack-page. " last page always stays render( ). ENDMETHOD. METHOD back_graceful. DATA li_handler TYPE REF TO /apmg/if_apm_gui_event_handler. DATA ls_handled TYPE /apmg/if_apm_gui_event_handler=>ty_handling_result. " This code can be potentially improved " Why send go_back to the topmost handler only ? It makes sense to notify the whole stack " But than how to handle re-render ? render if at least one handler asks for it ? " Probably that's the way but needs a relevant example. Postponed arch decision. READ TABLE mt_event_handlers INTO li_handler INDEX 1. IF sy-subrc = 0. ls_handled = li_handler->on_event( /apmg/cl_apm_gui_event=>new( iv_action = /apmg/if_apm_gui_router=>c_action-go_back ii_gui_services = me ) ). IF ls_handled-state = c_event_state-re_render. " soft exit, probably popup render( ). rv_handled = abap_true. ELSEIF ls_handled-state = c_event_state-no_more_act. " soft exit, probably GUI popup rv_handled = abap_true. ENDIF. ENDIF. ENDMETHOD. METHOD cache_html. rv_url = /apmg/if_apm_gui_services~cache_asset( iv_text = iv_text iv_type = 'text' iv_subtype = 'html' ). ENDMETHOD. METHOD call_page. DATA: ls_stack TYPE ty_page_stack. IF iv_replacing = abap_false AND NOT mi_cur_page IS INITIAL. ls_stack-page = mi_cur_page. ls_stack-bookmark = iv_with_bookmark. APPEND ls_stack TO mt_stack. ENDIF. mi_cur_page = ii_page. render( ). ENDMETHOD. METHOD constructor. IF io_component IS BOUND. IF /apmg/cl_apm_gui_utils=>is_renderable( io_component ) = abap_true. mi_cur_page ?= io_component. " direct page ELSE. IF /apmg/cl_apm_gui_utils=>is_event_handler( io_component ) = abap_false. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Component must be renderable or be an event handler'. ENDIF. mi_router ?= io_component. ENDIF. ENDIF. CREATE OBJECT mo_html_parts. mv_rollback_on_error = iv_rollback_on_error. mi_asset_man = ii_asset_man. mi_hotkey_ctl = ii_hotkey_ctl. mi_html_processor = ii_html_processor. " Maybe improve to middlewares stack ?? startup( ). ENDMETHOD. METHOD free. SET HANDLER on_event FOR mi_html_viewer ACTIVATION space. mi_html_viewer->close_document( ). mi_html_viewer->free( ). FREE mi_html_viewer. ENDMETHOD. METHOD go_home. DATA ls_stack LIKE LINE OF mt_stack. IF mi_router IS BOUND. CLEAR: mt_stack, mt_event_handlers. APPEND mi_router TO mt_event_handlers. on_event( action = |{ iv_action }| ). ELSE. IF lines( mt_stack ) > 0. READ TABLE mt_stack INTO ls_stack INDEX 1. mi_cur_page = ls_stack-page. ENDIF. render( ). ENDIF. ENDMETHOD. METHOD handle_action. DATA: lx_exception TYPE REF TO /apmg/cx_apm_error, li_handler TYPE REF TO /apmg/if_apm_gui_event_handler, li_event TYPE REF TO /apmg/if_apm_gui_event, ls_handled TYPE /apmg/if_apm_gui_event_handler=>ty_handling_result. CREATE OBJECT li_event TYPE /apmg/cl_apm_gui_event EXPORTING ii_gui_services = me iv_action = iv_action iv_getdata = iv_getdata it_postdata = it_postdata. TRY. "ls_handled = zcl_abapgit_exit=>get_instance( )->on_event( li_event ) IF ls_handled-state = c_event_state-not_handled. LOOP AT mt_event_handlers INTO li_handler. ls_handled = li_handler->on_event( li_event ). IF ls_handled-state IS NOT INITIAL AND ls_handled-state <> c_event_state-not_handled. " is handled EXIT. ENDIF. ENDLOOP. ENDIF. IF is_page_modal( mi_cur_page ) = abap_true AND NOT ( ls_handled-state = c_event_state-re_render OR ls_handled-state = c_event_state-go_back OR ls_handled-state = c_event_state-no_more_act ). " Restrict new page switching from modals ls_handled-state = c_event_state-no_more_act. ENDIF. CASE ls_handled-state. WHEN c_event_state-re_render. render( ). WHEN c_event_state-new_page. call_page( ls_handled-page ). WHEN c_event_state-new_page_w_bookmark. call_page( ii_page = ls_handled-page iv_with_bookmark = abap_true ). WHEN c_event_state-new_page_replacing. call_page( ii_page = ls_handled-page iv_replacing = abap_true ). WHEN c_event_state-go_back. back( ). WHEN c_event_state-go_back_to_bookmark. back( iv_to_bookmark = abap_true ). WHEN c_event_state-no_more_act. " Do nothing, handling completed WHEN OTHERS. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Unknown action: { iv_action }|. ENDCASE. CATCH /apmg/cx_apm_cancel ##NO_HANDLER. " Do nothing = c_event_state-no_more_act CATCH /apmg/cx_apm_error INTO lx_exception. handle_error( lx_exception ). ENDTRY. ENDMETHOD. METHOD handle_error. DATA: li_gui_error_handler TYPE REF TO /apmg/if_apm_gui_error_handler, lx_exception TYPE REF TO cx_root. IF mv_rollback_on_error = abap_true. ROLLBACK WORK. ENDIF. TRY. li_gui_error_handler ?= mi_cur_page. IF li_gui_error_handler IS BOUND AND li_gui_error_handler->handle_error( ix_exception ) = abap_true. " We rerender the current page to display the error box render( ). " TODO: Show log * ELSEIF ix_exception->mi_log IS BOUND * mi_common_log = ix_exception->mi_log * IF mi_common_log->get_log_level( ) >= /apmg/if_apm_log=>c_log_level-warning * /apmg/cl_apm_log_viewer=>show_log( mi_common_log ) * ENDIF ELSE. MESSAGE ix_exception TYPE 'S' DISPLAY LIKE 'E'. ENDIF. CATCH /apmg/cx_apm_error cx_sy_move_cast_error INTO lx_exception. " In case of fire we just fallback to plain old message MESSAGE lx_exception TYPE 'S' DISPLAY LIKE 'E'. ENDTRY. ENDMETHOD. METHOD is_page_modal. DATA li_modal TYPE REF TO /apmg/if_apm_gui_modal. TRY. IF ii_page IS BOUND. li_modal ?= ii_page. rv_yes = li_modal->is_modal( ). ENDIF. CATCH cx_sy_move_cast_error ##NO_HANDLER. ENDTRY. ENDMETHOD. METHOD on_event. handle_action( iv_action = action iv_getdata = getdata it_postdata = postdata ). ENDMETHOD. METHOD render. DATA: lv_url TYPE string, lv_html TYPE string, li_html TYPE REF TO /apmg/if_apm_html. IF mi_cur_page IS NOT BOUND. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'GUI error: no current page'. ENDIF. CLEAR mt_event_handlers. mo_html_parts->clear( ). IF mi_router IS BOUND AND is_page_modal( mi_cur_page ) = abap_false. " No global commands in modals APPEND mi_router TO mt_event_handlers. ENDIF. IF mi_hotkey_ctl IS BOUND. mi_hotkey_ctl->reset( ). ENDIF. li_html = mi_cur_page->render( ). lv_html = li_html->render( iv_no_indent_jscss = abap_true ). IF mi_html_processor IS BOUND. lv_html = mi_html_processor->process( iv_html = lv_html ii_gui_services = me ). ENDIF. lv_url = cache_html( lv_html ). mi_html_viewer->show_url( lv_url ). ENDMETHOD. METHOD set_focus. mi_html_viewer->set_focus( ). ENDMETHOD. METHOD startup. DATA: lt_events TYPE cntl_simple_events, ls_event LIKE LINE OF lt_events, lt_assets TYPE /apmg/if_apm_gui_asset_manager=>ty_web_assets. FIELD-SYMBOLS LIKE LINE OF lt_assets. mi_html_viewer = /apmg/cl_apm_gui_factory=>get_html_viewer( ). IF mi_asset_man IS BOUND. lt_assets = mi_asset_man->get_all_assets( ). LOOP AT lt_assets ASSIGNING WHERE is_cacheable = abap_true. /apmg/if_apm_gui_services~cache_asset( iv_xdata = -content iv_url = -url iv_type = -type iv_subtype = -subtype ). ENDLOOP. ENDIF. ls_event-eventid = mi_html_viewer->c_id_sapevent. ls_event-appl_event = abap_true. APPEND ls_event TO lt_events. mi_html_viewer->set_registered_events( lt_events ). SET HANDLER on_event FOR mi_html_viewer. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_asset_manager IMPLEMENTATION. METHOD /apmg/if_apm_gui_asset_manager~get_all_assets. FIELD-SYMBOLS LIKE LINE OF mt_asset_register. LOOP AT mt_asset_register ASSIGNING . APPEND load_asset( ) TO rt_assets. ENDLOOP. ENDMETHOD. METHOD /apmg/if_apm_gui_asset_manager~get_asset. FIELD-SYMBOLS LIKE LINE OF mt_asset_register. READ TABLE mt_asset_register WITH KEY url = iv_url ASSIGNING . IF IS NOT ASSIGNED. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Cannot find GUI asset: { iv_url }|. ENDIF. rs_asset = load_asset( ). ENDMETHOD. METHOD /apmg/if_apm_gui_asset_manager~get_text_asset. DATA ls_asset TYPE /apmg/if_apm_gui_asset_manager~ty_web_asset. ls_asset = /apmg/if_apm_gui_asset_manager~get_asset( iv_url ). IF ls_asset-type <> 'text'. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Not a text asset: { iv_url }|. ENDIF. IF iv_assert_subtype IS NOT INITIAL AND ls_asset-subtype <> iv_assert_subtype. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Wrong subtype ({ iv_assert_subtype }): { iv_url }|. ENDIF. TRY. rv_asset = zcl_abapgit_convert=>xstring_to_string_utf8( ls_asset-content ). CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD /apmg/if_apm_gui_asset_manager~register_asset. DATA ls_asset LIKE LINE OF mt_asset_register. SPLIT iv_type AT '/' INTO ls_asset-type ls_asset-subtype. ls_asset-url = iv_url. ls_asset-mime_name = iv_mime_name. ls_asset-is_cacheable = iv_cacheable. TRY. IF iv_base64 IS NOT INITIAL. ls_asset-content = zcl_abapgit_convert=>base64_to_xstring( iv_base64 ). ELSEIF iv_inline IS NOT INITIAL. ls_asset-content = zcl_abapgit_convert=>string_to_xstring( iv_inline ). ENDIF. CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. DELETE mt_asset_register WHERE url = iv_url. " TODO: Maybe forbid overwriting cacheable assets as they were probably already cached ... arguable APPEND ls_asset TO mt_asset_register. ENDMETHOD. METHOD create. CREATE OBJECT ri_asset_manager TYPE /apmg/cl_apm_gui_asset_manager. ENDMETHOD. METHOD get_mime_asset. DATA: ls_key TYPE wwwdatatab, lv_size_c TYPE wwwparams-value, lv_size TYPE i, lt_w3mime TYPE STANDARD TABLE OF w3mime, ls_w3mime LIKE LINE OF lt_w3mime. ls_key-relid = 'MI'. ls_key-objid = iv_mime_name. " Get exact file size CALL FUNCTION 'WWWPARAMS_READ' EXPORTING relid = ls_key-relid objid = ls_key-objid name = 'filesize' IMPORTING value = lv_size_c EXCEPTIONS entry_not_exists = 1. IF sy-subrc IS NOT INITIAL. RETURN. ENDIF. lv_size = lv_size_c. " Get binary data CALL FUNCTION 'WWWDATA_IMPORT' EXPORTING key = ls_key TABLES mime = lt_w3mime EXCEPTIONS wrong_object_type = 1 import_error = 2. IF sy-subrc IS NOT INITIAL. RETURN. ENDIF. LOOP AT lt_w3mime INTO ls_w3mime. CONCATENATE rv_xdata ls_w3mime-line INTO rv_xdata IN BYTE MODE. ENDLOOP. rv_xdata = rv_xdata(lv_size). ENDMETHOD. METHOD load_asset. MOVE-CORRESPONDING is_asset_entry TO rs_asset. IF rs_asset-content IS INITIAL AND is_asset_entry-mime_name IS NOT INITIAL. " inline content has the priority rs_asset-content = get_mime_asset( is_asset_entry-mime_name ). ENDIF. IF rs_asset-content IS INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |failed to load GUI asset: { is_asset_entry-url }|. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_buttons IMPLEMENTATION. METHOD advanced. result = /apmg/cl_apm_html=>icon( iv_name = 'tools-solid' iv_hint = 'Utilities' ). ENDMETHOD. METHOD experimental. result = /apmg/cl_apm_html=>icon( iv_name = 'vial-solid/red' iv_hint = 'Experimental Features are Enabled' ). ENDMETHOD. METHOD help. result = /apmg/cl_apm_html=>icon( iv_name = 'question-circle-solid' iv_hint = 'Help' ). ENDMETHOD. METHOD package_list. result = /apmg/cl_apm_html=>icon( iv_name = 'bars' iv_hint = 'Package List' ) && ' Package List'. ENDMETHOD. METHOD settings. result = /apmg/cl_apm_html=>icon( iv_name = 'cog' iv_hint = 'Settings' ) && ' Settings'. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_chunk_lib IMPLEMENTATION. METHOD class_constructor. DATA lv_fm TYPE string. lv_fm = 'GET_SYSTEM_TIMEZONE'. TRY. CALL METHOD ('CL_ABAP_TSTMP')=>get_system_timezone RECEIVING system_timezone = gv_time_zone. CATCH cx_sy_dyn_call_illegal_method. CALL FUNCTION lv_fm IMPORTING timezone = gv_time_zone EXCEPTIONS customizing_missing = 1 OTHERS = 2 ##FM_SUBRC_OK. ENDTRY. ENDMETHOD. METHOD get_t100_text. MESSAGE ID iv_msgid TYPE 'S' NUMBER iv_msgno WITH '&1' '&2' '&3' '&4' INTO rv_text. " Don't return any generic messages like `&1 &2 &3 &4` IF rv_text CO ' 0123456789&'. CLEAR rv_text. ENDIF. ENDMETHOD. METHOD normalize_program_name. rv_normalized_program_name = substring_before( val = iv_program_name regex = `(=+CP)?$` ) ##REGEX_POSIX. ENDMETHOD. METHOD render_error. DATA lv_error TYPE string. DATA lv_class TYPE string VALUE 'panel error center'. IF iv_extra_style IS NOT INITIAL. lv_class = lv_class && ` ` && iv_extra_style. ENDIF. ri_html = /apmg/cl_apm_html=>create( ). IF ix_error IS BOUND. lv_error = ix_error->get_text( ). ELSE. lv_error = iv_error. ENDIF. ri_html->add( |

| ). ri_html->add( |{ ri_html->icon( 'exclamation-circle/red' ) } { lv_error }| ). ri_html->add( '
' ). ENDMETHOD. METHOD render_error_message_box. CONSTANTS: BEGIN OF c_section_text, cause TYPE string VALUE `Cause`, system_response TYPE string VALUE `System response`, what_to_do TYPE string VALUE `Procedure`, sys_admin TYPE string VALUE `System administration`, END OF c_section_text . CONSTANTS: BEGIN OF c_section_token, cause TYPE string VALUE `&CAUSE&`, system_response TYPE string VALUE `&SYSTEM_RESPONSE&`, what_to_do TYPE string VALUE `&WHAT_TO_DO&`, sys_admin TYPE string VALUE `&SYS_ADMIN&`, END OF c_section_token . DATA: lv_error_text TYPE string, lv_longtext TYPE string, lt_longtext_paragraphs TYPE string_table, lv_program_name TYPE sy-repid, lv_title TYPE string, lv_text TYPE string. FIELD-SYMBOLS: TYPE string. ri_html = /apmg/cl_apm_html=>create( ). lv_error_text = ix_error->get_text( ). lv_longtext = ix_error->if_message~get_longtext( abap_true ). IF lv_longtext IS NOT INITIAL. lv_error_text = |{ lv_error_text } More...|. REPLACE FIRST OCCURRENCE OF REGEX |({ c_section_text-cause }{ cl_abap_char_utilities=>newline })| IN lv_longtext WITH |

$1

| ##REGEX_POSIX. REPLACE FIRST OCCURRENCE OF REGEX |({ c_section_text-system_response }{ cl_abap_char_utilities=>newline })| IN lv_longtext WITH |

$1

| ##REGEX_POSIX. REPLACE FIRST OCCURRENCE OF REGEX |({ c_section_text-what_to_do }{ cl_abap_char_utilities=>newline })| IN lv_longtext WITH |

$1

| ##REGEX_POSIX. REPLACE FIRST OCCURRENCE OF REGEX |({ c_section_text-sys_admin }{ cl_abap_char_utilities=>newline })| IN lv_longtext WITH |

$1

| ##REGEX_POSIX. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf IN lv_longtext WITH cl_abap_char_utilities=>newline. SPLIT lv_longtext AT cl_abap_char_utilities=>newline INTO TABLE lt_longtext_paragraphs. CLEAR lv_longtext. LOOP AT lt_longtext_paragraphs ASSIGNING . CONDENSE . IF IS INITIAL. CONTINUE. ENDIF. lv_longtext = |{ lv_longtext }

{ }

{ cl_abap_char_utilities=>newline }|. ENDLOOP. lv_longtext = |{ lv_longtext }
|. ENDIF. ri_html->add( |
| ). ri_html->add( |{ ri_html->icon( 'exclamation-circle/red' ) } { lv_error_text }| ). ri_html->add( |
| ). ri_html->add_a( iv_txt = `❌` iv_act = `toggleDisplay('message')` iv_class = `close-btn` iv_typ = /apmg/if_apm_html=>c_action_type-onclick ). ri_html->add( |
| ). ri_html->add( |
| ). IF ix_error->if_t100_message~t100key-msgid IS NOT INITIAL. lv_title = get_t100_text( iv_msgid = ix_error->if_t100_message~t100key-msgid iv_msgno = ix_error->if_t100_message~t100key-msgno ). IF lv_title IS NOT INITIAL. lv_text = |Message E{ ix_error->if_t100_message~t100key-msgno }({ ix_error->if_t100_message~t100key-msgid })|. ri_html->add_a( iv_txt = lv_text iv_typ = /apmg/if_apm_html=>c_action_type-sapevent iv_act = /apmg/if_apm_gui_router=>c_action-goto_message iv_title = lv_title iv_id = `a_goto_message` ). ENDIF. ENDIF. ix_error->get_source_position( IMPORTING program_name = lv_program_name ). lv_title = normalize_program_name( lv_program_name ). ri_html->add_a( iv_txt = `Goto source` iv_act = /apmg/if_apm_gui_router=>c_action-goto_source iv_typ = /apmg/if_apm_html=>c_action_type-sapevent iv_title = lv_title iv_id = `a_goto_source` ). ri_html->add_a( iv_txt = `Callstack` iv_act = /apmg/if_apm_gui_router=>c_action-show_callstack iv_typ = /apmg/if_apm_html=>c_action_type-sapevent iv_id = `a_callstack` ). ri_html->add( |
| ). ri_html->add( |
| ). ri_html->add( |{ lv_longtext }| ). ri_html->add( |
| ). ri_html->add( |
| ). ENDMETHOD. METHOD render_help_hint. DATA li_html TYPE REF TO /apmg/if_apm_html. DATA lv_add_class TYPE string. li_html = /apmg/cl_apm_html=>create( ). IF iv_add_class IS NOT INITIAL. lv_add_class = ` ` && iv_add_class. ENDIF. li_html->add( |
| ). li_html->add_icon( iv_name = 'question-circle-solid' iv_class = 'blue' ). li_html->add( `
` ). li_html->add( iv_text_to_wrap ). li_html->add( `
` ). li_html->add( `
` ). rv_html = li_html->render( iv_no_line_breaks = abap_true ). ENDMETHOD. METHOD render_infopanel. DATA lv_display TYPE string. DATA lv_class TYPE string. ri_html = /apmg/cl_apm_html=>create( ). IF iv_hide = abap_true. " Initially hide lv_display = 'display:none'. ENDIF. lv_class = 'info-panel'. IF iv_scrollable = abap_false. " Initially hide lv_class = lv_class && ' info-panel-fixed'. ENDIF. ri_html->add( |
| ). ri_html->add( |
{ iv_title }| && '
' && ri_html->a( iv_txt = '❌' iv_typ = /apmg/if_apm_html=>c_action_type-onclick iv_act = |toggleDisplay('{ iv_div_id }')| iv_class = 'close-btn' ) && '
' ). IF iv_hint IS NOT INITIAL. ri_html->add( '
' && ri_html->icon( iv_name = 'exclamation-triangle' iv_class = 'pad-right' ) && iv_hint && '
' ). ENDIF. ri_html->add( |
| ). ri_html->add( io_content ). ri_html->add( '
' ). ri_html->add( '
' ). ENDMETHOD. METHOD render_js_error_banner. ri_html = /apmg/cl_apm_html=>create( ). ri_html->add( '
' ). ri_html->add( |{ ri_html->icon( 'exclamation-triangle/red' ) }| && ' If this does not disappear soon,' && ' then there is a JS init error, please log an issue' ). ri_html->add( '
' ). ENDMETHOD. METHOD render_label_list. " FUTURE ENDMETHOD. METHOD render_package_name. DATA: lv_obj_name TYPE tadir-obj_name, lv_jump TYPE string, lv_title TYPE string. ri_html = /apmg/cl_apm_html=>create( ). IF iv_package IS INITIAL. RETURN. ENDIF. IF iv_suppress_title = abap_false. lv_title = zcl_abapgit_factory=>get_sap_package( iv_package )->read_description( ). ENDIF. lv_obj_name = iv_package. lv_jump = /apmg/cl_apm_html_action_utils=>jump_encode( iv_obj_type = 'DEVC' iv_obj_name = lv_obj_name ). ri_html->add( || ). ri_html->add_icon( iv_name = 'box/grey70' iv_hint = 'SAP package' ). IF iv_interactive = abap_true. ri_html->add_a( iv_act = |{ /apmg/if_apm_gui_router=>c_action-jump }?{ lv_jump }| iv_title = lv_title iv_txt = |{ iv_package }| ). ELSE. ri_html->add( iv_package ). ENDIF. ri_html->add( '' ). ENDMETHOD. METHOD render_registry_link. ri_html = /apmg/cl_apm_html=>create( ). ri_html->add( || ). * ri_html = ri_html->add_a( * iv_txt = shorten_repo_url( lv_url ) * iv_title = lv_url * iv_act = |{ /apmg/if_apm_gui_router=>c_action-url }?url={ lv_url }| * iv_class = 'url' ) ri_html->add( '' ). ENDMETHOD. METHOD render_success. ri_html = /apmg/cl_apm_html=>create( ). ri_html->add( '
' ). ri_html->add_icon( 'check' ). ri_html->add( iv_message ). ri_html->add( '
' ). ENDMETHOD. METHOD render_table_footer. ri_html = /apmg/cl_apm_html=>create( ). ri_html->add( '' ). ri_html->add( '' ). ri_html->add( '' ). ri_html->add( iv_message ). ri_html->add( '' ). ri_html->add( '' ). ri_html->add( '' ). ENDMETHOD. METHOD render_table_header. DATA: lv_tmp TYPE string, lv_disp_name TYPE string. FIELD-SYMBOLS LIKE LINE OF it_col_spec. ri_html = /apmg/cl_apm_html=>create( ). ri_html->add( '' ). ri_html->add( '' ). LOOP AT it_col_spec ASSIGNING . " e.g. Created at [{ gv_time_zone }] lv_tmp = '-css_class IS NOT INITIAL. lv_tmp = lv_tmp && | class="{ -css_class }"|. ENDIF. lv_tmp = lv_tmp && '>'. IF -display_name IS NOT INITIAL. lv_disp_name = -display_name. IF -add_tz = abap_true. lv_disp_name = lv_disp_name && | [{ gv_time_zone }]|. ENDIF. IF -tech_name = iv_order_by. IF iv_order_descending = abap_true. lv_tmp = lv_tmp && ri_html->a( iv_txt = lv_disp_name iv_act = /apmg/if_apm_gui_router=>c_action-change_order_by iv_title = -title ). ELSE. lv_tmp = lv_tmp && ri_html->a( iv_txt = lv_disp_name iv_act = |{ /apmg/if_apm_gui_router=>c_action-direction }?direction=DESCENDING| iv_title = -title ). ENDIF. ELSEIF -allow_order_by = abap_true. lv_tmp = lv_tmp && ri_html->a( iv_txt = lv_disp_name iv_act = |{ /apmg/if_apm_gui_router=>c_action-change_order_by }?orderBy={ -tech_name }| iv_title = -title ). ELSE. lv_tmp = lv_tmp && lv_disp_name. ENDIF. ENDIF. IF -tech_name = iv_order_by AND iv_order_by IS NOT INITIAL. IF iv_order_descending = abap_true. lv_tmp = lv_tmp && | ▾|. " arrow down ELSE. lv_tmp = lv_tmp && | ▴|. " arrow up ENDIF. ENDIF. lv_tmp = lv_tmp && ''. ri_html->add( lv_tmp ). ENDLOOP. ri_html->add( '' ). ri_html->add( '' ). ENDMETHOD. METHOD render_text_input. DATA lv_attrs TYPE string. ri_html = /apmg/cl_apm_html=>create( ). IF iv_value IS NOT INITIAL. lv_attrs = | value="{ iv_value }"|. ENDIF. IF iv_max_length IS NOT INITIAL. lv_attrs = lv_attrs && | maxlength="{ iv_max_length }"|. ENDIF. IF iv_autofocus = abap_true. lv_attrs = lv_attrs && | autofocus|. ENDIF. ri_html->add( || ). ri_html->add( || ). ENDMETHOD. METHOD render_timestamp. DATA lv_date TYPE d. DATA lv_time TYPE t. CONVERT TIME STAMP iv_timestamp TIME ZONE gv_time_zone INTO DATE lv_date TIME lv_time. rv_rendered = |{ lv_date DATE = USER } { lv_time TIME = USER }|. ENDMETHOD. METHOD render_transport. DATA: lv_title TYPE string, lv_jump TYPE string. ri_html = /apmg/cl_apm_html=>create( ). IF iv_transport IS INITIAL. RETURN. ENDIF. lv_title = zcl_abapgit_factory=>get_cts_api( )->read_description( iv_transport ). lv_jump = |{ /apmg/if_apm_gui_router=>c_action-jump_transport }?transport={ iv_transport }|. IF iv_icon_only = abap_true. ri_html->add_a( iv_act = lv_jump iv_title = |Transport { iv_transport }| iv_txt = /apmg/cl_apm_html=>icon( 'truck-solid/darkgrey' ) ). ELSE. ri_html->add( || ). ri_html->add_icon( iv_name = 'truck-solid/grey70' iv_hint = 'Transport' ). IF iv_interactive = abap_true. ri_html->add_a( iv_act = lv_jump iv_title = lv_title iv_txt = |{ iv_transport }| ). ELSE. ri_html->add( iv_transport ). ENDIF. ri_html->add( '' ). ENDIF. ENDMETHOD. METHOD render_user_name. DATA: lv_title TYPE string, lv_jump TYPE string. ri_html = /apmg/cl_apm_html=>create( ). IF iv_username IS INITIAL. RETURN. ENDIF. IF iv_username <> zcl_abapgit_objects_super=>c_user_unknown AND iv_suppress_title = abap_false. lv_title = zcl_abapgit_env_factory=>get_user_record( )->get_title( iv_username ). ENDIF. lv_jump = |{ /apmg/if_apm_gui_router=>c_action-jump_user }?user={ iv_username }|. IF iv_icon_only = abap_true. ri_html->add_a( iv_act = lv_jump iv_title = lv_title iv_txt = /apmg/cl_apm_html=>icon( 'user-solid/darkgrey' ) ). ELSE. ri_html->add( || ). ri_html->add_icon( iv_name = 'user-solid/grey70' iv_hint = 'User name' ). IF iv_interactive = abap_true AND iv_username <> zcl_abapgit_objects_super=>c_user_unknown. ri_html->add_a( iv_act = lv_jump iv_title = lv_title iv_txt = |{ iv_username }| ). ELSE. ri_html->add( iv_username ). ENDIF. ri_html->add( '' ). ENDIF. ENDMETHOD. METHOD render_warning_banner. ri_html = /apmg/cl_apm_html=>create( ). ri_html->add( '
' ). ri_html->add( |{ ri_html->icon( 'exclamation-triangle/yellow' ) } { iv_text }| ). ri_html->add( '
' ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_component IMPLEMENTATION. METHOD gui_services. IF gui_service IS NOT BOUND. gui_service = /apmg/cl_apm_gui_factory=>get_gui_services( ). " apm ENDIF. result = gui_service. ENDMETHOD. METHOD register_deferred_script. gui_services( )->get_html_parts( )->add_part( iv_collection = c_html_parts-scripts ii_part = part ). ENDMETHOD. METHOD register_event_handler. IF event_handler IS BOUND. DATA(handler) = event_handler. ELSE. TRY. handler ?= me. CATCH cx_root. RETURN. ENDTRY. ENDIF. gui_services( )->register_event_handler( handler ). ENDMETHOD. METHOD register_handlers. register_event_handler( ). register_hotkeys( ). ENDMETHOD. METHOD register_hotkeys. IF hotkey_provider IS BOUND. DATA(provider) = hotkey_provider. ELSE. TRY. provider ?= me. CATCH cx_root. RETURN. ENDTRY. ENDIF. gui_services( )->get_hotkeys_ctl( )->register_hotkeys( provider->get_hotkey_actions( ) ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_css_processor IMPLEMENTATION. METHOD add_file. APPEND iv_url TO mt_files. ENDMETHOD. METHOD constructor. mi_asset_manager = ii_asset_manager. ENDMETHOD. METHOD get_css_vars_in_string. CONSTANTS: lc_root_pattern TYPE string VALUE `:root\s*\{([^\}]*)\}`, lc_variable_pattern TYPE string VALUE `\-\-([\w\d-]+)\s*:\s*([^\n\r;]*);`. DATA: lv_root TYPE string, lo_matcher TYPE REF TO cl_abap_matcher, lo_regex TYPE REF TO cl_abap_regex, ls_variable LIKE LINE OF rt_variables. " Only the :root element may define variables for now FIND FIRST OCCURRENCE OF REGEX lc_root_pattern IN iv_string SUBMATCHES lv_root ##REGEX_POSIX. IF sy-subrc = 0 AND lv_root IS NOT INITIAL. CREATE OBJECT lo_regex EXPORTING pattern = lc_variable_pattern ##REGEX_POSIX. lo_matcher = lo_regex->create_matcher( text = lv_root ). WHILE lo_matcher->find_next( ) = abap_true. ls_variable-name = lo_matcher->get_submatch( 1 ). ls_variable-value = lo_matcher->get_submatch( 2 ). INSERT ls_variable INTO TABLE rt_variables. IF sy-subrc <> 0. MODIFY TABLE rt_variables FROM ls_variable. ENDIF. ENDWHILE. ENDIF. ENDMETHOD. METHOD process. DATA: lt_contents TYPE STANDARD TABLE OF string, lv_content TYPE string, lt_css_variables TYPE ty_css_vars, lt_css_vars_in_file TYPE ty_css_vars. FIELD-SYMBOLS: TYPE string, LIKE LINE OF lt_css_vars_in_file, LIKE LINE OF lt_contents. " 1. Determine all variables and their values. Later definitions overwrite previous ones. LOOP AT mt_files ASSIGNING . lv_content = mi_asset_manager->get_text_asset( iv_url = iv_assert_subtype = 'css' ). lt_css_vars_in_file = get_css_vars_in_string( lv_content ). LOOP AT lt_css_vars_in_file ASSIGNING . INSERT INTO TABLE lt_css_variables. IF sy-subrc <> 0. MODIFY TABLE lt_css_variables FROM . ENDIF. ENDLOOP. APPEND lv_content TO lt_contents. ENDLOOP. " 2. Replace all variable usages in variables LOOP AT lt_css_variables ASSIGNING WHERE value CS 'var(--'. "#EC CI_SORTSEQ resolve_var_recursively( EXPORTING iv_variable_name = -name CHANGING ct_variables = lt_css_variables ). ENDLOOP. " 3. Replace all other variable usages by inlining the values. LOOP AT lt_contents ASSIGNING . LOOP AT lt_css_variables ASSIGNING . REPLACE ALL OCCURRENCES OF |var(--{ -name })| IN WITH -value. ENDLOOP. ENDLOOP. rv_result = concat_lines_of( table = lt_contents sep = cl_abap_char_utilities=>newline ). ENDMETHOD. METHOD resolve_var_recursively. CONSTANTS: lc_variable_usage_pattern TYPE string VALUE `var\(\-\-([^\)]*)\)`. DATA: lv_variable_name TYPE string. FIELD-SYMBOLS: LIKE LINE OF ct_variables, LIKE LINE OF ct_variables. READ TABLE ct_variables WITH TABLE KEY name = iv_variable_name ASSIGNING . IF sy-subrc = 0. DO. FIND FIRST OCCURRENCE OF REGEX lc_variable_usage_pattern IN -value SUBMATCHES lv_variable_name ##REGEX_POSIX. IF sy-subrc = 0. resolve_var_recursively( EXPORTING iv_variable_name = lv_variable_name CHANGING ct_variables = ct_variables ). READ TABLE ct_variables WITH TABLE KEY name = lv_variable_name ASSIGNING . REPLACE FIRST OCCURRENCE OF |var(--{ lv_variable_name })| IN -value WITH -value. ELSE. EXIT. ENDIF. ENDDO. ELSE. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |CSS variable { iv_variable_name } not resolvable|. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_dlg_deprecate IMPLEMENTATION. METHOD /apmg/if_apm_gui_event_handler~on_event. form_data = form_util->normalize_abapgit( ii_event->form_data( ) ). CASE ii_event->mv_action. WHEN c_action-choose_package. form_data->set( iv_key = c_id-package iv_val = /apmg/cl_apm_gui_factory=>get_popups( )->popup_search_help( 'TDEVC-DEVCLASS' ) ). IF form_data->get( c_id-package ) IS NOT INITIAL. validation_log = validate_form( form_data ). ELSE. form_data = read_package( |{ form_data->get( c_id-package ) }| ). ENDIF. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-refresh. form_data = read_package( |{ form_data->get( c_id-package ) }| ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-deprecate. validation_log = validate_form( form_data ). IF validation_log->is_empty( ) = abap_true. DATA(params) = get_parameters( form_data ). IF confirm_popup_version( params ) = abap_true. /apmg/cl_apm_command_deprecate=>run( registry = registry name = params-name range = params-version message_text = params-message ). ENDIF. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-go_back. ELSE. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. " Display errors ENDIF. ENDCASE. ENDMETHOD. METHOD /apmg/if_apm_gui_menu_provider~get_menu. ro_toolbar = /apmg/cl_apm_gui_menus=>registry( registry ). ENDMETHOD. METHOD /apmg/if_apm_gui_renderable~render. register_handlers( ). DATA(html) = /apmg/cl_apm_html=>create( ). html->add( '
' ). html->add( form->render( io_values = form_data io_validation_log = validation_log ) ). html->add( '
' ). ri_html = html. ENDMETHOD. METHOD confirm_popup_version. DATA(question) = |This will DEPRECATE { params-name } { params-version }|. DATA(answer) = /apmg/cl_apm_gui_factory=>get_popups( )->popup_to_confirm( iv_titlebar = 'Deprecate Version' iv_text_question = question iv_text_button_1 = 'Deprecate' iv_icon_button_1 = 'ICON_REJECT' iv_text_button_2 = 'Cancel' iv_icon_button_2 = 'ICON_CANCEL' iv_default_button = '2' iv_display_cancel_button = abap_false iv_popup_type = 'ICON_MESSAGE_WARNING' ). IF answer = '2'. MESSAGE 'Deprecate cancelled' TYPE 'S'. RETURN. ENDIF. result = abap_true. ENDMETHOD. METHOD constructor. super->constructor( ). validation_log = NEW #( ). form_data = NEW #( ). form = get_form_schema( ). form_util = /apmg/cl_apm_html_form_utils=>create( form ). deprecate_package = package. IF deprecate_package IS NOT INITIAL. form_data = read_package( deprecate_package ). ENDIF. registry = /apmg/cl_apm_settings=>factory( )->get( )-registry. ENDMETHOD. METHOD create. DATA(component) = NEW /apmg/cl_apm_gui_dlg_deprecate( package ). result = /apmg/cl_apm_gui_page_hoc=>create( page_title = 'Deprecate Package' child_component = component ). ENDMETHOD. METHOD get_form_schema. result = /apmg/cl_apm_html_form=>create( iv_form_id = 'deprecate-package-form' iv_help_page = 'https://docs.abappm.com/' ). " TODO result->text( iv_name = c_id-name iv_label = 'Name' )->text( iv_name = c_id-version iv_label = 'Version or Range of Versions' )->textarea( iv_name = c_id-message iv_label = 'Message' iv_rows = 3 iv_required = abap_true ). result->command( iv_label = 'Deprecate' iv_cmd_type = /apmg/if_apm_html_form=>c_cmd_type-input_main iv_action = c_action-deprecate )->command( iv_label = 'Back' iv_action = /apmg/if_apm_gui_router=>c_action-go_back ). ENDMETHOD. METHOD get_parameters. form_data->to_struc( CHANGING cs_container = result ). ENDMETHOD. METHOD read_package. DATA(package_json) = /apmg/cl_apm_package_json=>factory( package )->get( ). result = NEW #( ). result->set( iv_key = c_id-package iv_val = package )->set( iv_key = c_id-name iv_val = package_json-name )->set( iv_key = c_id-version iv_val = package_json-version ). ENDMETHOD. METHOD validate_form. result = form_util->validate( form_data ). DATA(package) = CONV devclass( form_data->get( c_id-package ) ). IF package IS NOT INITIAL. TRY. zcl_abapgit_factory=>get_sap_package( package )->validate_name( ). CATCH zcx_abapgit_exception INTO DATA(error). result->set( iv_key = c_id-package iv_val = error->get_text( ) ). ENDTRY. IF /apmg/cl_apm_auth=>is_package_allowed( package ) = abap_false. result->set( iv_key = c_id-package iv_val = 'Package not allowed (responsible user = "SAP")' ). ENDIF. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_name( form_data->get( c_id-name ) ) = abap_false. result->set( iv_key = c_id-name iv_val = 'Invalid name' ). ENDIF. IF /apmg/cl_apm_semver_ranges=>valid_range( form_data->get( c_id-version ) ) = abap_false. result->set( iv_key = c_id-version iv_val = 'Invalid version or range' ). ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_dlg_init IMPLEMENTATION. METHOD /apmg/if_apm_gui_event_handler~on_event. form_data = form_util->normalize_abapgit( ii_event->form_data( ) ). CASE ii_event->mv_action. WHEN c_action-create_package. form_data->set( iv_key = c_id-package iv_val = /apmg/cl_apm_popup_utils=>create_package( form_data->get( c_id-package ) ) ). IF form_data->get( c_id-package ) IS NOT INITIAL. validation_log = validate_form( form_data ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. ELSE. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. ENDIF. WHEN c_action-choose_package. form_data->set( iv_key = c_id-package iv_val = /apmg/cl_apm_gui_factory=>get_popups( )->popup_search_help( 'TDEVC-DEVCLASS' ) ). IF form_data->get( c_id-package ) IS NOT INITIAL. validation_log = validate_form( form_data ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. ELSE. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. ENDIF. WHEN c_action-choose_labels. choose_labels( ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-init_package. validation_log = validate_form( form_data ). IF validation_log->is_empty( ) = abap_true. DATA(params) = get_parameters( form_data ). /apmg/cl_apm_command_init=>run( package = params-package package_json = params-package_json ). rs_handled-page = /apmg/cl_apm_gui_page_package=>create( params-package ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-new_page_replacing. ELSE. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. " Display errors ENDIF. ENDCASE. ENDMETHOD. METHOD /apmg/if_apm_gui_renderable~render. register_handlers( ). DATA(html) = /apmg/cl_apm_html=>create( ). html->add( '
' ). html->add( form->render( io_values = form_data io_validation_log = validation_log ) ). html->add( '
' ). ri_html = html. ENDMETHOD. METHOD choose_labels. ASSERT 0 = 0. " FUTURE * DATA(old_labels) = form_data->get( c_id-labels ). * * DATA(new_labels) = /apmg/cl_apm_ui_factory=>get_popups( )->popup_to_select_labels( old_labels ). * * form_data->set( * iv_key = c_id-labels * iv_val = new_labels ). ENDMETHOD. METHOD constructor. super->constructor( ). validation_log = NEW #( ). form_data = NEW #( ). form = get_form_schema( ). form_util = /apmg/cl_apm_html_form_utils=>create( form ). ENDMETHOD. METHOD create. DATA(component) = NEW /apmg/cl_apm_gui_dlg_init( ). result = /apmg/cl_apm_gui_page_hoc=>create( page_title = 'Init Package' child_component = component ). ENDMETHOD. METHOD get_form_schema. result = /apmg/cl_apm_html_form=>create( iv_form_id = 'init-package-form' iv_help_page = 'https://docs.abappm.com/' ). " TODO result->text( iv_name = c_id-package iv_side_action = c_action-choose_package iv_required = abap_true iv_upper_case = abap_true iv_label = 'Package' iv_hint = 'SAP package (should be a dedicated one)' iv_placeholder = 'Z... / $...' iv_max = 30 )->text( iv_name = c_id-name iv_required = abap_true iv_label = 'Name' iv_hint = 'Unique name for package' iv_min = /apmg/if_apm_types=>c_package_name-min_length iv_max = /apmg/if_apm_types=>c_package_name-max_length )->text( iv_name = c_id-version iv_required = abap_true iv_label = 'Version' iv_hint = 'Semantic version (x.y.z)' )->text( iv_name = c_id-description iv_label = 'Description' * FUTURE * )->text( * iv_name = c_id-labels * iv_side_action = c_action-choose_labels * iv_label = |Labels (comma-separated, allowed chars: "{ /apmg/cl_apm_repo_labels=>c_allowed_chars }")| * iv_hint = 'Comma-separated labels for grouping and repo organization (optional)' )->checkbox( iv_name = c_id-private iv_label = 'Private Package (will not become public)' ). * FUTURE * IF /apmg/cl_apm_feature=>is_enabled( /apmg/cl_apm_abap_language_vers=>c_feature_flag ) = abap_true. * result->radio( * iv_name = c_id-abap_lang_vers * iv_default_value = '' * iv_label = 'ABAP Language Version' * iv_hint = 'Define the ABAP language version for objects in the repository' * )->option( * iv_label = 'Any' * iv_value = '' * )->option( * iv_label = 'Ignore' * iv_value = zif_abapgit_dot_abapgit=>c_abap_language_version-ignore * )->option( * iv_label = 'Standard' * iv_value = zif_abapgit_dot_abapgit=>c_abap_language_version-standard * )->option( * iv_label = 'For Key Users' * iv_value = zif_abapgit_dot_abapgit=>c_abap_language_version-key_user * )->option( * iv_label = 'For Cloud Development' * iv_value = zif_abapgit_dot_abapgit=>c_abap_language_version-cloud_development ) * ENDIF result->command( iv_label = 'Init Package' iv_cmd_type = /apmg/if_apm_html_form=>c_cmd_type-input_main iv_action = c_action-init_package )->command( iv_label = 'Create Package' iv_action = c_action-create_package )->command( iv_label = 'Back' iv_action = /apmg/if_apm_gui_router=>c_action-go_back ). ENDMETHOD. METHOD get_parameters. form_data->to_struc( CHANGING cs_container = result ). result-package_json = CORRESPONDING #( result ). ENDMETHOD. METHOD validate_form. result = form_util->validate( form_data ). DATA(package) = CONV devclass( form_data->get( c_id-package ) ). IF package IS NOT INITIAL. TRY. zcl_abapgit_factory=>get_sap_package( package )->validate_name( ). " Check if package owned by SAP is allowed (new packages are ok, since they are created automatically) DATA(username) = zcl_abapgit_factory=>get_sap_package( package )->read_responsible( ). IF sy-subrc = 0 AND username = 'SAP' AND zcl_abapgit_factory=>get_environment( )->is_sap_object_allowed( ) = abap_false. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Package { package } not allowed, responsible user = 'SAP'|. ENDIF. CATCH zcx_abapgit_exception INTO DATA(error). result->set( iv_key = c_id-package iv_val = error->get_text( ) ). ENDTRY. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_name( form_data->get( c_id-name ) ) = abap_false. result->set( iv_key = c_id-name iv_val = 'Invalid name' ). ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_version( form_data->get( c_id-version ) ) = abap_false. result->set( iv_key = c_id-version iv_val = 'Invalid version' ). ENDIF. " FUTURE " TRY " /apmg/cl_apm_repo_labels=>validate( form_data->get( c_id-labels ) ) " CATCH zcx_abapgit_exception INTO error " result->set( " iv_key = c_id-labels " iv_val = error->get_text( ) ) " ENDTRY ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_dlg_install IMPLEMENTATION. METHOD /apmg/if_apm_gui_event_handler~on_event. form_data = form_util->normalize_abapgit( ii_event->form_data( ) ). CASE ii_event->mv_action. WHEN c_action-create_package. form_data->set( iv_key = c_id-package iv_val = /apmg/cl_apm_popup_utils=>create_package( form_data->get( c_id-package ) ) ). IF form_data->get( c_id-package ) IS NOT INITIAL. validation_log = validate_form( form_data ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. ELSE. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. ENDIF. WHEN c_action-choose_package. form_data->set( iv_key = c_id-package iv_val = /apmg/cl_apm_gui_factory=>get_popups( )->popup_search_help( 'TDEVC-DEVCLASS' ) ). IF form_data->get( c_id-package ) IS NOT INITIAL. validation_log = validate_form( form_data ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. ELSE. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. ENDIF. WHEN c_action-install_package. validation_log = validate_form( form_data ). IF validation_log->is_empty( ) = abap_true. DATA(params) = get_parameters( form_data ). /apmg/cl_apm_command_install=>run( registry = registry package = params-package package_json = params-package_json ). rs_handled-page = /apmg/cl_apm_gui_page_package=>create( params-package ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-new_page_replacing. ELSE. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. " Display errors ENDIF. ENDCASE. ENDMETHOD. METHOD /apmg/if_apm_gui_menu_provider~get_menu. ro_toolbar = /apmg/cl_apm_gui_menus=>registry( registry ). ENDMETHOD. METHOD /apmg/if_apm_gui_renderable~render. register_handlers( ). DATA(html) = /apmg/cl_apm_html=>create( ). html->add( '
' ). html->add( form->render( io_values = form_data io_validation_log = validation_log ) ). html->add( '
' ). ri_html = html. ENDMETHOD. METHOD constructor. super->constructor( ). validation_log = NEW #( ). form_data = NEW #( ). form = get_form_schema( ). form_util = /apmg/cl_apm_html_form_utils=>create( form ). registry = /apmg/cl_apm_settings=>factory( )->get( )-registry. ENDMETHOD. METHOD create. DATA(component) = NEW /apmg/cl_apm_gui_dlg_install( ). result = /apmg/cl_apm_gui_page_hoc=>create( page_title = 'Install Package' child_component = component ). ENDMETHOD. METHOD get_form_schema. result = /apmg/cl_apm_html_form=>create( iv_form_id = 'install-package-form' iv_help_page = 'https://docs.abappm.com/' ). " TODO result->text( iv_name = c_id-package iv_side_action = c_action-choose_package iv_required = abap_true iv_upper_case = abap_true iv_label = 'Package' iv_hint = 'SAP package (should be a dedicated one)' iv_placeholder = 'Z... / $...' iv_min = 2 iv_max = 30 )->text( iv_name = c_id-name iv_required = abap_true iv_label = 'Name' iv_hint = 'Name of the package' iv_min = /apmg/if_apm_types=>c_package_name-min_length iv_max = /apmg/if_apm_types=>c_package_name-max_length )->text( iv_name = c_id-version iv_required = abap_true iv_label = 'Version' iv_hint = 'Semantic version (x.y.z)' ). result->command( iv_label = 'Install Package' iv_cmd_type = /apmg/if_apm_html_form=>c_cmd_type-input_main iv_action = c_action-install_package )->command( iv_label = 'Create Package' iv_action = c_action-create_package )->command( iv_label = 'Back' iv_action = /apmg/if_apm_gui_router=>c_action-go_back ). ENDMETHOD. METHOD get_parameters. form_data->to_struc( CHANGING cs_container = result ). result-package_json = CORRESPONDING #( result ). ENDMETHOD. METHOD validate_form. result = form_util->validate( form_data ). DATA(package) = CONV devclass( form_data->get( c_id-package ) ). IF package IS NOT INITIAL. TRY. zcl_abapgit_factory=>get_sap_package( package )->validate_name( ). " Check if package owned by SAP is allowed (new packages are ok, since they are created automatically) DATA(username) = zcl_abapgit_factory=>get_sap_package( package )->read_responsible( ). IF sy-subrc = 0 AND username = 'SAP' AND zcl_abapgit_factory=>get_environment( )->is_sap_object_allowed( ) = abap_false. zcx_abapgit_exception=>raise( |Package { package } not allowed, responsible user = 'SAP'| ). ENDIF. CATCH zcx_abapgit_exception INTO DATA(error). result->set( iv_key = c_id-package iv_val = error->get_text( ) ). ENDTRY. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_name( form_data->get( c_id-name ) ) = abap_false. result->set( iv_key = c_id-name iv_val = 'Invalid name' ). ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_version( form_data->get( c_id-version ) ) = abap_false. result->set( iv_key = c_id-version iv_val = 'Invalid version' ). ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_dlg_publish IMPLEMENTATION. METHOD /apmg/if_apm_gui_event_handler~on_event. form_data = form_util->normalize_abapgit( ii_event->form_data( ) ). CASE ii_event->mv_action. WHEN c_action-choose_package. form_data->set( iv_key = c_id-package iv_val = /apmg/cl_apm_gui_factory=>get_popups( )->popup_search_help( 'TDEVC-DEVCLASS' ) ). IF form_data->get( c_id-package ) IS NOT INITIAL. validation_log = validate_form( form_data ). ELSE. form_data = read_package( |{ form_data->get( c_id-package ) }| ). ENDIF. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-refresh. form_data = read_package( |{ form_data->get( c_id-package ) }| ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-publish_package. validation_log = validate_form( form_data ). IF validation_log->is_empty( ) = abap_true. DATA(params) = get_parameters( form_data ). IF confirm_popup( params ) = abap_true. /apmg/cl_apm_command_publish=>run( registry = registry package = params-package ). ENDIF. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-go_back. ELSE. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. " Display errors ENDIF. ENDCASE. ENDMETHOD. METHOD /apmg/if_apm_gui_menu_provider~get_menu. ro_toolbar = /apmg/cl_apm_gui_menus=>registry( registry ). ENDMETHOD. METHOD /apmg/if_apm_gui_renderable~render. register_handlers( ). DATA(html) = /apmg/cl_apm_html=>create( ). html->add( '
' ). html->add( form->render( io_values = form_data io_validation_log = validation_log ) ). html->add( '
' ). ri_html = html. ENDMETHOD. METHOD confirm_popup. DATA(question) = |This will PUBLISH all objects in { params-package } | && |including subpackages as { params-name } { params-version } | && |to the registry|. DATA(answer) = /apmg/cl_apm_gui_factory=>get_popups( )->popup_to_confirm( iv_titlebar = 'Publish' iv_text_question = question iv_text_button_1 = 'Publish' iv_icon_button_1 = 'ICON_EXPORT' iv_text_button_2 = 'Cancel' iv_icon_button_2 = 'ICON_CANCEL' iv_default_button = '2' iv_display_cancel_button = abap_false iv_popup_type = 'ICON_MESSAGE_WARNING' ). IF answer = '2'. MESSAGE 'Publish cancelled' TYPE 'S'. RETURN. ENDIF. result = abap_true. ENDMETHOD. METHOD constructor. super->constructor( ). validation_log = NEW #( ). form_data = NEW #( ). form = get_form_schema( ). form_util = /apmg/cl_apm_html_form_utils=>create( form ). pubish_package = package. IF pubish_package IS NOT INITIAL. form_data = read_package( pubish_package ). ENDIF. registry = /apmg/cl_apm_settings=>factory( )->get( )-registry. ENDMETHOD. METHOD create. DATA(component) = NEW /apmg/cl_apm_gui_dlg_publish( package ). result = /apmg/cl_apm_gui_page_hoc=>create( page_title = 'Publish Package' child_component = component ). ENDMETHOD. METHOD get_form_schema. result = /apmg/cl_apm_html_form=>create( iv_form_id = 'publish-package-form' iv_help_page = 'https://docs.abappm.com/' ). " TODO result->text( iv_name = c_id-package iv_side_action = c_action-choose_package iv_required = abap_true iv_upper_case = abap_true iv_label = 'Package' iv_hint = 'SAP package' iv_placeholder = 'Z... / $...' iv_max = 30 )->text( iv_name = c_id-name iv_label = 'Name' iv_readonly = abap_true )->text( iv_name = c_id-version iv_label = 'Version' iv_readonly = abap_true ). result->command( iv_label = 'Publish Package' iv_cmd_type = /apmg/if_apm_html_form=>c_cmd_type-input_main iv_action = c_action-publish_package )->command( iv_label = 'Refresh' iv_action = c_action-refresh )->command( iv_label = 'Back' iv_action = /apmg/if_apm_gui_router=>c_action-go_back ). ENDMETHOD. METHOD get_parameters. form_data->to_struc( CHANGING cs_container = result ). ENDMETHOD. METHOD read_package. DATA(package_json) = /apmg/cl_apm_package_json=>factory( package )->get( ). result = NEW #( ). result->set( iv_key = c_id-package iv_val = package )->set( iv_key = c_id-name iv_val = package_json-name )->set( iv_key = c_id-version iv_val = package_json-version ). ENDMETHOD. METHOD validate_form. result = form_util->validate( form_data ). DATA(package) = CONV devclass( form_data->get( c_id-package ) ). IF package IS NOT INITIAL. TRY. zcl_abapgit_factory=>get_sap_package( package )->validate_name( ). " Check if package owned by SAP is allowed (new packages are ok, since they are created automatically) DATA(username) = zcl_abapgit_factory=>get_sap_package( package )->read_responsible( ). IF sy-subrc = 0 AND username = 'SAP' AND zcl_abapgit_factory=>get_environment( )->is_sap_object_allowed( ) = abap_false. zcx_abapgit_exception=>raise( |Package { package } not allowed, responsible user = 'SAP'| ). ENDIF. CATCH zcx_abapgit_exception INTO DATA(error). result->set( iv_key = c_id-package iv_val = error->get_text( ) ). ENDTRY. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_dlg_undepreca IMPLEMENTATION. METHOD /apmg/if_apm_gui_event_handler~on_event. form_data = form_util->normalize_abapgit( ii_event->form_data( ) ). CASE ii_event->mv_action. WHEN c_action-choose_package. form_data->set( iv_key = c_id-package iv_val = /apmg/cl_apm_gui_factory=>get_popups( )->popup_search_help( 'TDEVC-DEVCLASS' ) ). IF form_data->get( c_id-package ) IS NOT INITIAL. validation_log = validate_form( form_data ). ELSE. form_data = read_package( |{ form_data->get( c_id-package ) }| ). ENDIF. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-undeprecate. validation_log = validate_form( form_data ). IF validation_log->is_empty( ) = abap_true. DATA(params) = get_parameters( form_data ). IF confirm_popup_version( params ) = abap_true. /apmg/cl_apm_command_deprecate=>run( registry = registry name = params-name range = params-version message_text = '' ). ENDIF. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-go_back. ELSE. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. " Display errors ENDIF. ENDCASE. ENDMETHOD. METHOD /apmg/if_apm_gui_menu_provider~get_menu. ro_toolbar = /apmg/cl_apm_gui_menus=>registry( registry ). ENDMETHOD. METHOD /apmg/if_apm_gui_renderable~render. register_handlers( ). DATA(html) = /apmg/cl_apm_html=>create( ). html->add( '
' ). html->add( form->render( io_values = form_data io_validation_log = validation_log ) ). html->add( '
' ). ri_html = html. ENDMETHOD. METHOD confirm_popup_version. DATA(question) = |This will UNDEPRECATE { params-name } { params-version }|. DATA(answer) = /apmg/cl_apm_gui_factory=>get_popups( )->popup_to_confirm( iv_titlebar = 'Undeprecate Version' iv_text_question = question iv_text_button_1 = 'Undeprecate' iv_icon_button_1 = 'ICON_ALLOW' iv_text_button_2 = 'Cancel' iv_icon_button_2 = 'ICON_CANCEL' iv_default_button = '2' iv_display_cancel_button = abap_false iv_popup_type = 'ICON_MESSAGE_WARNING' ). IF answer = '2'. MESSAGE 'Undeprecate cancelled' TYPE 'S'. RETURN. ENDIF. result = abap_true. ENDMETHOD. METHOD constructor. super->constructor( ). validation_log = NEW #( ). form_data = NEW #( ). form = get_form_schema( ). form_util = /apmg/cl_apm_html_form_utils=>create( form ). undeprecate_package = package. IF undeprecate_package IS NOT INITIAL. form_data = read_package( undeprecate_package ). ENDIF. registry = /apmg/cl_apm_settings=>factory( )->get( )-registry. ENDMETHOD. METHOD create. DATA(component) = NEW /apmg/cl_apm_gui_dlg_undepreca( package ). result = /apmg/cl_apm_gui_page_hoc=>create( page_title = 'Undeprecate Package' child_component = component ). ENDMETHOD. METHOD get_form_schema. result = /apmg/cl_apm_html_form=>create( iv_form_id = 'undeprecate-package-form' iv_help_page = 'https://docs.abappm.com/' ). " TODO result->text( iv_name = c_id-name iv_label = 'Name' )->text( iv_name = c_id-version iv_label = 'Version or Range of Versions' ). result->command( iv_label = 'Undeprecate' iv_cmd_type = /apmg/if_apm_html_form=>c_cmd_type-input_main iv_action = c_action-undeprecate )->command( iv_label = 'Back' iv_action = /apmg/if_apm_gui_router=>c_action-go_back ). ENDMETHOD. METHOD get_parameters. form_data->to_struc( CHANGING cs_container = result ). ENDMETHOD. METHOD read_package. DATA(package_json) = /apmg/cl_apm_package_json=>factory( package )->get( ). result = NEW #( ). result->set( iv_key = c_id-package iv_val = package )->set( iv_key = c_id-name iv_val = package_json-name )->set( iv_key = c_id-version iv_val = package_json-version ). ENDMETHOD. METHOD validate_form. result = form_util->validate( form_data ). DATA(package) = CONV devclass( form_data->get( c_id-package ) ). IF package IS NOT INITIAL. TRY. zcl_abapgit_factory=>get_sap_package( package )->validate_name( ). CATCH zcx_abapgit_exception INTO DATA(error). result->set( iv_key = c_id-package iv_val = error->get_text( ) ). ENDTRY. IF /apmg/cl_apm_auth=>is_package_allowed( package ) = abap_false. result->set( iv_key = c_id-package iv_val = 'Package not allowed (responsible user = "SAP")' ). ENDIF. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_name( form_data->get( c_id-name ) ) = abap_false. result->set( iv_key = c_id-name iv_val = 'Invalid name' ). ENDIF. IF /apmg/cl_apm_semver_ranges=>valid_range( form_data->get( c_id-version ) ) = abap_false. result->set( iv_key = c_id-version iv_val = 'Invalid version or range' ). ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_dlg_uninstall IMPLEMENTATION. METHOD /apmg/if_apm_gui_event_handler~on_event. form_data = form_util->normalize_abapgit( ii_event->form_data( ) ). CASE ii_event->mv_action. WHEN c_action-choose_package. form_data->set( iv_key = c_id-package iv_val = /apmg/cl_apm_gui_factory=>get_popups( )->popup_search_help( 'TDEVC-DEVCLASS' ) ). IF form_data->get( c_id-package ) IS NOT INITIAL. validation_log = validate_form( form_data ). ELSE. form_data = read_package( |{ form_data->get( c_id-package ) }| ). ENDIF. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-refresh. form_data = read_package( |{ form_data->get( c_id-package ) }| ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-uninstall_package. validation_log = validate_form( form_data ). IF validation_log->is_empty( ) = abap_true. DATA(params) = get_parameters( form_data ). IF confirm_popup( params ) = abap_true. /apmg/cl_apm_command_uninstall=>run( params-package ). ENDIF. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-go_back. ELSE. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. " Display errors ENDIF. ENDCASE. ENDMETHOD. METHOD /apmg/if_apm_gui_renderable~render. register_handlers( ). DATA(html) = /apmg/cl_apm_html=>create( ). html->add( '
' ). html->add( form->render( io_values = form_data io_validation_log = validation_log ) ). html->add( '
' ). ri_html = html. ENDMETHOD. METHOD confirm_popup. DATA(question) = |This will DELETE all objects in { params-package } | && |including subpackages from the system|. DATA(answer) = /apmg/cl_apm_gui_factory=>get_popups( )->popup_to_confirm( iv_titlebar = 'Uninstall' iv_text_question = question iv_text_button_1 = 'Delete' iv_icon_button_1 = 'ICON_DELETE' iv_text_button_2 = 'Cancel' iv_icon_button_2 = 'ICON_CANCEL' iv_default_button = '2' iv_display_cancel_button = abap_false iv_popup_type = 'ICON_MESSAGE_WARNING' ). IF answer = '2'. MESSAGE 'Uninstall cancelled' TYPE 'S'. RETURN. ENDIF. result = abap_true. ENDMETHOD. METHOD constructor. super->constructor( ). validation_log = NEW #( ). form_data = NEW #( ). form = get_form_schema( ). form_util = /apmg/cl_apm_html_form_utils=>create( form ). unpubish_package = package. IF unpubish_package IS NOT INITIAL. form_data = read_package( unpubish_package ). ENDIF. registry = /apmg/cl_apm_settings=>factory( )->get( )-registry. ENDMETHOD. METHOD create. DATA(component) = NEW /apmg/cl_apm_gui_dlg_uninstall( package ). result = /apmg/cl_apm_gui_page_hoc=>create( page_title = 'Uninstall Package' child_component = component ). ENDMETHOD. METHOD get_form_schema. result = /apmg/cl_apm_html_form=>create( iv_form_id = 'uninstall-package-form' iv_help_page = 'https://docs.abappm.com/' ). " TODO result->text( iv_name = c_id-package iv_side_action = c_action-choose_package iv_required = abap_true iv_upper_case = abap_true iv_label = 'Package' iv_hint = 'SAP package' iv_placeholder = 'Z... / $...' iv_max = 30 )->text( iv_name = c_id-name iv_label = 'Name' iv_readonly = abap_true )->text( iv_name = c_id-version iv_label = 'Version' iv_readonly = abap_true ). result->command( iv_label = 'Uninstall Package' iv_cmd_type = /apmg/if_apm_html_form=>c_cmd_type-input_main iv_action = c_action-uninstall_package )->command( iv_label = 'Refresh' iv_action = c_action-refresh )->command( iv_label = 'Back' iv_action = /apmg/if_apm_gui_router=>c_action-go_back ). ENDMETHOD. METHOD get_parameters. form_data->to_struc( CHANGING cs_container = result ). ENDMETHOD. METHOD read_package. DATA(package_json) = /apmg/cl_apm_package_json=>factory( package )->get( ). result = NEW #( ). result->set( iv_key = c_id-package iv_val = package )->set( iv_key = c_id-name iv_val = package_json-name )->set( iv_key = c_id-version iv_val = package_json-version ). ENDMETHOD. METHOD validate_form. result = form_util->validate( io_form_data ). DATA(package) = CONV devclass( io_form_data->get( c_id-package ) ). IF package IS NOT INITIAL. TRY. zcl_abapgit_factory=>get_sap_package( package )->validate_name( ). CATCH zcx_abapgit_exception INTO DATA(error). result->set( iv_key = c_id-package iv_val = error->get_text( ) ). ENDTRY. IF /apmg/cl_apm_auth=>is_package_allowed( package ) = abap_false. result->set( iv_key = c_id-package iv_val = 'Package not allowed (responsible user = "SAP")' ). ENDIF. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_dlg_unpublish IMPLEMENTATION. METHOD /apmg/if_apm_gui_event_handler~on_event. form_data = form_util->normalize_abapgit( ii_event->form_data( ) ). CASE ii_event->mv_action. WHEN c_action-choose_package. form_data->set( iv_key = c_id-package iv_val = /apmg/cl_apm_gui_factory=>get_popups( )->popup_search_help( 'TDEVC-DEVCLASS' ) ). IF form_data->get( c_id-package ) IS NOT INITIAL. validation_log = validate_form( form_data ). ELSE. form_data = read_package( |{ form_data->get( c_id-package ) }| ). ENDIF. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-refresh. form_data = read_package( |{ form_data->get( c_id-package ) }| ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-unpublish_package. validation_log = validate_form( form_data ). IF validation_log->is_empty( ) = abap_true. DATA(params) = get_parameters( form_data ). IF confirm_popup_package( params ) = abap_true. /apmg/cl_apm_command_unpublish=>run( registry = registry name = params-name ). ENDIF. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-go_back. ELSE. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. " Display errors ENDIF. WHEN c_action-unpublish_version. validation_log = validate_form( form_data ). IF validation_log->is_empty( ) = abap_true. params = get_parameters( form_data ). IF confirm_popup_version( params ) = abap_true. /apmg/cl_apm_command_unpublish=>run( registry = registry name = params-name version = params-version ). ENDIF. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-go_back. ELSE. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. " Display errors ENDIF. ENDCASE. ENDMETHOD. METHOD /apmg/if_apm_gui_menu_provider~get_menu. ro_toolbar = /apmg/cl_apm_gui_menus=>registry( registry ). ENDMETHOD. METHOD /apmg/if_apm_gui_renderable~render. register_handlers( ). DATA(html) = /apmg/cl_apm_html=>create( ). html->add( '
' ). html->add( form->render( io_values = form_data io_validation_log = validation_log ) ). html->add( '
' ). ri_html = html. ENDMETHOD. METHOD confirm_popup_package. DATA(question) = |This will UNPUBLISH the COMPLETE { params-name } package | && |from the registry (Note: Terms will apply)|. DATA(answer) = /apmg/cl_apm_gui_factory=>get_popups( )->popup_to_confirm( iv_titlebar = 'Unpublish Package' iv_text_question = question iv_text_button_1 = 'Unpublish' iv_icon_button_1 = 'ICON_EXPORT' iv_text_button_2 = 'Cancel' iv_icon_button_2 = 'ICON_CANCEL' iv_default_button = '2' iv_display_cancel_button = abap_false iv_popup_type = 'ICON_MESSAGE_WARNING' ). IF answer = '2'. MESSAGE 'Unpublish cancelled' TYPE 'S'. RETURN. ENDIF. result = abap_true. ENDMETHOD. METHOD confirm_popup_version. DATA(question) = |This will UNPUBLISH { params-name } { params-version } | && |from the registry (Note: Terms will apply)|. DATA(answer) = /apmg/cl_apm_gui_factory=>get_popups( )->popup_to_confirm( iv_titlebar = 'Unpublish Version' iv_text_question = question iv_text_button_1 = 'Unpublish' iv_icon_button_1 = 'ICON_EXPORT' iv_text_button_2 = 'Cancel' iv_icon_button_2 = 'ICON_CANCEL' iv_default_button = '2' iv_display_cancel_button = abap_false iv_popup_type = 'ICON_MESSAGE_WARNING' ). IF answer = '2'. MESSAGE 'Unpublish cancelled' TYPE 'S'. RETURN. ENDIF. result = abap_true. ENDMETHOD. METHOD constructor. super->constructor( ). validation_log = NEW #( ). form_data = NEW #( ). form = get_form_schema( ). form_util = /apmg/cl_apm_html_form_utils=>create( form ). unpublish_package = package. IF unpublish_package IS NOT INITIAL. form_data = read_package( unpublish_package ). ENDIF. registry = /apmg/cl_apm_settings=>factory( )->get( )-registry. ENDMETHOD. METHOD create. DATA(component) = NEW /apmg/cl_apm_gui_dlg_unpublish( package ). result = /apmg/cl_apm_gui_page_hoc=>create( page_title = 'Unpublish Package' child_component = component ). ENDMETHOD. METHOD get_form_schema. result = /apmg/cl_apm_html_form=>create( iv_form_id = 'unpublish-package-form' iv_help_page = 'https://docs.abappm.com/' ). " TODO result->text( iv_name = c_id-name iv_label = 'Name' )->text( iv_name = c_id-version iv_label = 'Version' ). result->command( iv_label = 'Unpublish Version' iv_cmd_type = /apmg/if_apm_html_form=>c_cmd_type-input_main iv_action = c_action-unpublish_version )->command( iv_label = 'Unpublish Complete Package' iv_action = c_action-unpublish_package )->command( iv_label = 'Back' iv_action = /apmg/if_apm_gui_router=>c_action-go_back ). ENDMETHOD. METHOD get_parameters. form_data->to_struc( CHANGING cs_container = result ). ENDMETHOD. METHOD read_package. DATA(package_json) = /apmg/cl_apm_package_json=>factory( package )->get( ). result = NEW #( ). result->set( iv_key = c_id-package iv_val = package )->set( iv_key = c_id-name iv_val = package_json-name )->set( iv_key = c_id-version iv_val = package_json-version ). ENDMETHOD. METHOD validate_form. result = form_util->validate( form_data ). DATA(package) = CONV devclass( form_data->get( c_id-package ) ). IF package IS NOT INITIAL. TRY. zcl_abapgit_factory=>get_sap_package( package )->validate_name( ). CATCH zcx_abapgit_exception INTO DATA(error). result->set( iv_key = c_id-package iv_val = error->get_text( ) ). ENDTRY. IF /apmg/cl_apm_auth=>is_package_allowed( package ) = abap_false. result->set( iv_key = c_id-package iv_val = 'Package not allowed (responsible user = "SAP")' ). ENDIF. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_name( form_data->get( c_id-name ) ) = abap_false. result->set( iv_key = c_id-name iv_val = 'Invalid name' ). ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_version( form_data->get( c_id-version ) ) = abap_false. result->set( iv_key = c_id-version iv_val = 'Invalid version' ). ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_router IMPLEMENTATION. METHOD /apmg/if_apm_gui_event_handler~on_event. DATA result TYPE /apmg/if_apm_gui_event_handler=>ty_handling_result. IF result-state IS INITIAL. result = general_page_routing( ii_event ). ENDIF. IF result-state IS INITIAL. result = command_dialogs( ii_event ). ENDIF. IF result-state IS INITIAL. result = utility_actions( ii_event ). ENDIF. IF result-state IS INITIAL. result = sap_gui_actions( ii_event ). ENDIF. IF result-state IS INITIAL. result = browser_actions( ii_event ). ENDIF. IF result-state IS INITIAL. result = other_utilities( ii_event ). ENDIF. IF result-state IS INITIAL. result-state = /apmg/cl_apm_gui=>c_event_state-not_handled. ENDIF. rs_handled = result. ENDMETHOD. METHOD browser_actions. CASE event->mv_action. WHEN /apmg/if_apm_gui_router=>c_action-homepage. call_browser( /apmg/if_apm_constants=>c_website ). result-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. WHEN /apmg/if_apm_gui_router=>c_action-feedback. call_browser( /apmg/if_apm_constants=>c_new_issue ). result-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. WHEN /apmg/if_apm_gui_router=>c_action-registry. TRY. DATA(registry) = /apmg/cl_apm_settings=>factory( )->get( )-registry. call_browser( registry ). CATCH /apmg/cx_apm_error ##NO_HANDLER. ENDTRY. result-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. WHEN /apmg/if_apm_gui_router=>c_action-documentation. call_browser( /apmg/if_apm_constants=>c_documentation ). result-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. WHEN /apmg/if_apm_gui_router=>c_action-changelog. call_browser( /apmg/if_apm_constants=>c_changelog ). result-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. WHEN /apmg/if_apm_gui_router=>c_action-sponsor. call_browser( /apmg/if_apm_constants=>c_sponsor ). result-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. ENDCASE. ENDMETHOD. METHOD call_browser. /apmg/cl_apm_gui_factory=>get_frontend_services( )->execute( iv_document = |{ url }| ). ENDMETHOD. METHOD command_dialogs. DATA(id) = CONV /apmg/if_apm_package_json=>ty_package_id( event->query( )->get( 'KEY' ) ). DATA(package) = /apmg/cl_apm_package_json=>get_package_from_id( id ). CASE event->mv_action. WHEN /apmg/if_apm_gui_router=>c_action-apm_init. result-page = /apmg/cl_apm_gui_dlg_init=>create( ). result-state = /apmg/cl_apm_gui=>c_event_state-new_page. WHEN /apmg/if_apm_gui_router=>c_action-apm_install. result-page = /apmg/cl_apm_gui_dlg_install=>create( ). result-state = /apmg/cl_apm_gui=>c_event_state-new_page. WHEN /apmg/if_apm_gui_router=>c_action-apm_uninstall. result-page = /apmg/cl_apm_gui_dlg_uninstall=>create( package ). result-state = /apmg/cl_apm_gui=>c_event_state-new_page. WHEN /apmg/if_apm_gui_router=>c_action-apm_publish. result-page = /apmg/cl_apm_gui_dlg_publish=>create( package ). result-state = /apmg/cl_apm_gui=>c_event_state-new_page. WHEN /apmg/if_apm_gui_router=>c_action-apm_unpublish. result-page = /apmg/cl_apm_gui_dlg_unpublish=>create( package ). result-state = /apmg/cl_apm_gui=>c_event_state-new_page. WHEN /apmg/if_apm_gui_router=>c_action-apm_deprecate. result-page = /apmg/cl_apm_gui_dlg_deprecate=>create( package ). result-state = /apmg/cl_apm_gui=>c_event_state-new_page. WHEN /apmg/if_apm_gui_router=>c_action-apm_undeprecate. result-page = /apmg/cl_apm_gui_dlg_undepreca=>create( package ). result-state = /apmg/cl_apm_gui=>c_event_state-new_page. ENDCASE. ENDMETHOD. METHOD general_page_routing. CASE event->mv_action. WHEN /apmg/if_apm_gui_router=>c_action-go_home. result-page = /apmg/cl_apm_gui_page_list=>create( ). "TODO main_page( ). result-state = /apmg/cl_apm_gui=>c_event_state-new_page. WHEN /apmg/if_apm_gui_router=>c_action-go_back. result-state = /apmg/cl_apm_gui=>c_event_state-go_back. WHEN /apmg/if_apm_gui_router=>c_action-apm_home. result-page = /apmg/cl_apm_gui_page_list=>create( ). result-state = /apmg/cl_apm_gui=>c_event_state-new_page_replacing. WHEN /apmg/if_apm_gui_router=>c_action-go_settings. DATA(key) = /apmg/cl_apm_settings=>get_setting_key( /apmg/if_apm_settings=>c_global ). result-page = /apmg/cl_apm_gui_page_db_entry=>create( key ). result-state = /apmg/cl_apm_gui=>c_event_state-new_page. WHEN /apmg/if_apm_gui_router=>c_action-go_settings_personal. key = /apmg/cl_apm_settings=>get_setting_key( sy-uname ). result-page = /apmg/cl_apm_gui_page_db_entry=>create( key ). result-state = /apmg/cl_apm_gui=>c_event_state-new_page. * FUTURE * WHEN /apmg/if_apm_gui_router=>c_action-go_tutorial * result-page = /apmg/cl_apm_gui_page_tutorial=>create( ) * result-state = /apmg/cl_apm_gui=>c_event_state-new_page WHEN /apmg/if_apm_gui_router=>c_action-favorite_package. toggle_favorite( event->query( )->get( 'PACKAGE' ) ). result-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN /apmg/if_apm_gui_router=>c_action-show_hotkeys. /apmg/cl_apm_gui_factory=>get_gui_services( )->get_hotkeys_ctl( )->set_visible( abap_true ). result-state = /apmg/cl_apm_gui=>c_event_state-re_render. ENDCASE. ENDMETHOD. METHOD jump_display_transport. TRY. DATA(is_adt_jump_enabled) = /apmg/cl_apm_settings=>factory( )->get( )-gui_settings-adt_jump_enabled. CATCH /apmg/cx_apm_error ##NO_HANDLER. ENDTRY. IF is_adt_jump_enabled = abap_true. TRY. DATA(adt_link) = zcl_abapgit_adt_link=>link_transport( transport ). /apmg/cl_apm_gui_factory=>get_frontend_services( )->execute( iv_document = adt_link ). CATCH /apmg/cx_apm_error. " Fallback if ADT link execution failed or was cancelled CALL FUNCTION 'TR_DISPLAY_REQUEST' EXPORTING i_trkorr = transport. ENDTRY. ELSE. CALL FUNCTION 'TR_DISPLAY_REQUEST' EXPORTING i_trkorr = transport. ENDIF. ENDMETHOD. METHOD jump_display_user. " TODO: user display in ADT CALL FUNCTION 'BAPI_USER_DISPLAY' EXPORTING username = username. ENDMETHOD. METHOD jump_object. DATA(item) = VALUE zif_abapgit_definitions=>ty_item( obj_type = cl_http_utility=>unescape_url( |{ obj_type }| ) obj_name = cl_http_utility=>unescape_url( |{ obj_name }| ) ). DATA(sub_item) = VALUE zif_abapgit_definitions=>ty_item( obj_type = cl_http_utility=>unescape_url( |{ sub_type }| ) obj_name = cl_http_utility=>unescape_url( |{ sub_name }| ) ). IF line CO '0123456789'. DATA(line_number) = CONV i( line ). ENDIF. DATA(is_new_window) = xsdbool( new_window IS NOT INITIAL ). TRY. DATA(html_viewer) = /apmg/cl_apm_gui_factory=>get_html_viewer( ). " Hide HTML Viewer in dummy screen0 for direct CALL SCREEN to work html_viewer->set_visiblity( abap_false ). IF line_number IS INITIAL OR sub_item IS INITIAL. /apmg/cl_apm_abapgit_objects=>jump( is_item = item iv_filename = filename iv_new_window = is_new_window ). ELSE. /apmg/cl_apm_abapgit_objects=>jump( is_item = item is_sub_item = sub_item iv_filename = filename iv_line_number = line_number iv_new_window = is_new_window ). ENDIF. html_viewer->set_visiblity( abap_true ). CATCH cx_root INTO DATA(error). html_viewer->set_visiblity( abap_true ). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD main_page ##CALLED. " Prio 1: Show last viewed package (if it exists) DATA(settings) = /apmg/cl_apm_settings=>factory( )->get( ). IF settings-show_last_package = abap_true AND settings-last_package IS NOT INITIAL. TRY. /apmg/cl_apm_package_json=>factory( settings-last_package )->load( ). result = /apmg/cl_apm_gui_page_package=>create( settings-last_package ). RETURN. CATCH /apmg/cx_apm_error. " Remove inconsistent value from settings CLEAR settings-last_package. /apmg/cl_apm_settings=>factory( )->set( settings )->save( ). ENDTRY. ENDIF. " Prio 2: Show list of packages DATA(package_list) = /apmg/cl_apm_package_json=>list( ). IF package_list IS NOT INITIAL. result = /apmg/cl_apm_gui_page_list=>create( ). ELSE. " TODO: Prio 3: Show tutorial " ri_page = /apmg/cl_apm_gui_page_tutorial=>create( ) /apmg/cl_apm_roadmap=>planned( ). ENDIF. ENDMETHOD. METHOD other_utilities. TYPES ty_char TYPE c LENGTH 1024. DATA clipboard TYPE STANDARD TABLE OF ty_char WITH KEY table_line. CASE event->mv_action. WHEN /apmg/if_apm_gui_router=>c_action-ie_devtools. /apmg/cl_apm_gui_factory=>get_frontend_services( )->open_ie_devtools( ). result-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. WHEN /apmg/if_apm_gui_router=>c_action-clipboard. DATA(clip_content) = event->query( )->get( 'CLIPBOARD' ). APPEND clip_content TO clipboard. /apmg/cl_apm_gui_factory=>get_frontend_services( )->clipboard_export( clipboard ). result-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. MESSAGE 'Successfully exported to clipboard' TYPE 'S'. ENDCASE. ENDMETHOD. METHOD sap_gui_actions. CASE event->mv_action. WHEN /apmg/if_apm_gui_router=>c_action-jump. " Open object editor jump_object( obj_type = event->query( )->get( 'TYPE' ) obj_name = event->query( )->get( 'NAME' ) filename = event->query( )->get( 'FILE' ) sub_type = event->query( )->get( 'SUBTYPE' ) sub_name = event->query( )->get( 'SUBNAME' ) line = event->query( )->get( 'LINE' ) new_window = event->query( )->get( 'NEW_WINDOW' ) ). result-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. WHEN /apmg/if_apm_gui_router=>c_action-jump_transport. jump_display_transport( |{ event->query( )->get( 'TRANSPORT' ) }| ). result-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. WHEN /apmg/if_apm_gui_router=>c_action-jump_user. jump_display_user( |{ event->query( )->get( 'USER' ) }| ). result-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. WHEN /apmg/if_apm_gui_router=>c_action-url. call_browser( event->query( )->get( 'URL' ) ). result-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. ENDCASE. ENDMETHOD. METHOD toggle_favorite. TRY. DATA(settings) = /apmg/cl_apm_settings=>factory( )->get( ). READ TABLE settings-package_settings ASSIGNING FIELD-SYMBOL() WITH KEY package = package. IF sy-subrc = 0. -favorite = xsdbool( -favorite = abap_false ). ELSE. DATA(package_setting) = VALUE /apmg/if_apm_settings=>ty_package_settings( package = package favorite = abap_true ). INSERT package_setting INTO TABLE settings-package_settings. ENDIF. /apmg/cl_apm_settings=>factory( )->set( settings )->save( ). CATCH /apmg/cx_apm_error INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD utility_actions. CASE event->mv_action. WHEN /apmg/if_apm_gui_router=>c_action-go_db. result-page = /apmg/cl_apm_gui_page_db=>create( ). result-state = /apmg/cl_apm_gui=>c_event_state-new_page. WHEN /apmg/if_apm_gui_router=>c_action-go_debuginfo. result-page = /apmg/cl_apm_gui_page_debuginf=>create( ). result-state = /apmg/cl_apm_gui=>c_event_state-new_page. ENDCASE. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_hotkey_ctl IMPLEMENTATION. METHOD constructor. super->constructor( ). TRY. keyboard_settings = /apmg/cl_apm_settings=>factory( )->get( )-keyboard_settings. " apm CATCH /apmg/cx_apm_error ##NO_HANDLER. ENDTRY. ENDMETHOD. METHOD render_scripts. DATA(json) = `{`. LOOP AT hotkeys ASSIGNING FIELD-SYMBOL(). IF sy-tabix > 1. json = json && |,|. ENDIF. json = json && | "{ -hotkey }" : "{ -action }" |. ENDLOOP. json = json && `}`. result = /apmg/cl_apm_html=>create( ). result->set_title( cl_abap_typedescr=>describe_by_object_ref( me )->get_relative_name( ) ). result->add( |setKeyBindings({ json });| ). ENDMETHOD. METHOD should_show_hint. IF was_hint_shown = abap_false. result = abap_true. was_hint_shown = abap_true. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_gui_hotkeys~get_hotkey_actions. DATA hotkey LIKE LINE OF rt_hotkey_actions. hotkey-ui_component = 'Hotkeys'. hotkey-action = c_showhotkeys_action. hotkey-description = 'Show Hotkeys Help'. hotkey-hotkey = '?'. INSERT hotkey INTO TABLE rt_hotkey_actions. ENDMETHOD. METHOD /apmg/if_apm_gui_hotkey_ctl~get_registered_hotkeys. rt_registered_hotkeys = hotkeys. ENDMETHOD. METHOD /apmg/if_apm_gui_hotkey_ctl~register_hotkeys. " Compress duplicates LOOP AT it_hotkeys ASSIGNING FIELD-SYMBOL(). READ TABLE hotkeys WITH KEY hotkey = -hotkey TRANSPORTING NO FIELDS. IF sy-subrc = 0. " If found command with same hotkey DELETE hotkeys INDEX sy-tabix. " Later registered commands enjoys the priority ENDIF. IF keyboard_settings-link_hints_enabled = abap_true AND keyboard_settings-link_hint_key = -hotkey. " Link hint activation key is more important CONTINUE. ENDIF. APPEND TO hotkeys. ENDLOOP. ENDMETHOD. METHOD /apmg/if_apm_gui_hotkey_ctl~reset. CLEAR hotkeys. ENDMETHOD. METHOD /apmg/if_apm_gui_hotkey_ctl~set_visible. is_visible = iv_visible. ENDMETHOD. METHOD /apmg/if_apm_gui_renderable~render. register_handlers( ). DATA(result) = /apmg/cl_apm_html=>create( ). DATA(registered_hotkeys) = /apmg/if_apm_gui_hotkey_ctl~get_registered_hotkeys( ). SORT registered_hotkeys BY ui_component description. register_deferred_script( render_scripts( registered_hotkeys ) ). " Render hotkeys result->add( '
    ' ). LOOP AT registered_hotkeys ASSIGNING FIELD-SYMBOL(). result->add( |
  • | && |{ -hotkey }| && |{ -description }| && |
  • | ). ENDLOOP. " render link hints activation key IF keyboard_settings-link_hints_enabled = abap_true. result->add( |
  • | && |{ keyboard_settings-link_hint_key }| && |Link Hints| && |
  • | ). result->add( |
  • | && |y{ keyboard_settings-link_hint_key }| && |Copy Link Text| && |
  • | ). ENDIF. result->add( '
' ). DATA(hotkey) = ''. READ TABLE registered_hotkeys ASSIGNING WITH KEY action = c_showhotkeys_action. IF sy-subrc = 0. hotkey = -hotkey. ENDIF. DATA(hint) = |Close window with upper right corner 'X'|. IF hotkey IS NOT INITIAL. hint = hint && | or press '{ -hotkey }'|. ENDIF. result = /apmg/cl_apm_gui_chunk_lib=>render_infopanel( iv_div_id = 'hotkeys' iv_title = 'Hotkeys' iv_hint = hint iv_hide = boolc( is_visible = abap_false ) iv_scrollable = abap_false io_content = result ). IF hotkey IS NOT INITIAL AND should_show_hint( ) = abap_true. result->add( |
| && |Press '{ -hotkey }' to get keyboard shortcuts list| && |
| ). ENDIF. " Always reset visibility here. Closing of the popup has to be done by the " user and is handled in JS. is_visible = abap_false. ri_html = result. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_html_viewer IMPLEMENTATION. METHOD /apmg/if_apm_html_viewer~back. mo_html_viewer->go_back( ). ENDMETHOD. METHOD /apmg/if_apm_html_viewer~close_document. mo_html_viewer->close_document( ). ENDMETHOD. METHOD /apmg/if_apm_html_viewer~free. mo_html_viewer->free( ). ENDMETHOD. METHOD /apmg/if_apm_html_viewer~get_url. DATA lv_url TYPE c LENGTH 250. mo_html_viewer->get_current_url( IMPORTING url = lv_url ). cl_gui_cfw=>flush( ). rv_url = lv_url. ENDMETHOD. METHOD /apmg/if_apm_html_viewer~load_data. DATA lv_url TYPE c LENGTH 250. DATA lv_assigned TYPE c LENGTH 250. ASSERT strlen( iv_url ) <= 250. lv_url = iv_url. mo_html_viewer->load_data( EXPORTING url = lv_url type = iv_type subtype = iv_subtype size = iv_size IMPORTING assigned_url = lv_assigned CHANGING data_table = ct_data_table EXCEPTIONS dp_invalid_parameter = 1 dp_error_general = 2 cntl_error = 3 " html_syntax_notcorrect = 4 " not in lower releases OTHERS = 5 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error loading data for HTML viewer'. ENDIF. ev_assigned_url = lv_assigned. ENDMETHOD. METHOD /apmg/if_apm_html_viewer~set_focus. cl_gui_control=>set_focus( EXPORTING control = mo_html_viewer EXCEPTIONS cntl_error = 1 cntl_system_error = 2 OTHERS = 3 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error in: cl_gui_control=>set_focus - SUBRC = { sy-subrc }|. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_html_viewer~set_registered_events. mo_html_viewer->set_registered_events( EXPORTING events = it_events EXCEPTIONS cntl_error = 1 cntl_system_error = 2 illegal_event_combination = 3 OTHERS = 4 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error registering events for HTML viewer'. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_html_viewer~set_visiblity. DATA: lv_visible TYPE c LENGTH 1. IF iv_visible = abap_true. lv_visible = cl_gui_container=>visible_true. ELSE. lv_visible = cl_gui_container=>visible_false. ENDIF. mo_html_viewer->set_visible( lv_visible ). ENDMETHOD. METHOD /apmg/if_apm_html_viewer~show_url. DATA lv_url TYPE c LENGTH 250. lv_url = iv_url. mo_html_viewer->show_url( EXPORTING url = lv_url EXCEPTIONS cntl_error = 1 cnht_error_not_allowed = 2 cnht_error_parameter = 3 dp_error_general = 4 OTHERS = 5 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error showing URL in HTML viewer'. ENDIF. ENDMETHOD. METHOD constructor. DATA: lt_events TYPE cntl_simple_events, ls_event LIKE LINE OF lt_events. CREATE OBJECT mo_html_viewer EXPORTING query_table_disabled = iv_disable_query_table parent = io_container. ls_event-eventid = /apmg/if_apm_html_viewer=>c_id_sapevent. ls_event-appl_event = abap_true. APPEND ls_event TO lt_events. mo_html_viewer->set_registered_events( lt_events ). SET HANDLER on_event FOR mo_html_viewer. ENDMETHOD. METHOD on_event. RAISE EVENT /apmg/if_apm_html_viewer~sapevent EXPORTING action = action frame = frame getdata = getdata postdata = postdata query_table = query_table. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_popups IMPLEMENTATION. METHOD /apmg/if_apm_popups~popup_folder_logic. ENDMETHOD. METHOD /apmg/if_apm_popups~popup_search_help. DATA lt_ret TYPE TABLE OF ddshretval. DATA ls_ret LIKE LINE OF lt_ret. DATA lv_tabname TYPE dfies-tabname. DATA lv_fieldname TYPE dfies-fieldname. SPLIT iv_tab_field AT '-' INTO lv_tabname lv_fieldname. lv_tabname = to_upper( lv_tabname ). lv_fieldname = to_upper( lv_fieldname ). CALL FUNCTION 'F4IF_FIELD_VALUE_REQUEST' EXPORTING tabname = lv_tabname fieldname = lv_fieldname TABLES return_tab = lt_ret EXCEPTIONS OTHERS = 5. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |F4IF_FIELD_VALUE_REQUEST error [{ iv_tab_field }]|. ENDIF. IF lines( lt_ret ) > 0. READ TABLE lt_ret WITH KEY fieldname = lv_fieldname INTO ls_ret. IF sy-subrc = 0. rv_value = ls_ret-fieldval. ELSE. READ TABLE lt_ret INDEX 1 INTO ls_ret. ASSERT sy-subrc = 0. rv_value = ls_ret-fieldval. ENDIF. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_popups~popup_to_confirm. ms_position = center( iv_width = 65 iv_height = 5 ). CALL FUNCTION 'POPUP_TO_CONFIRM' EXPORTING titlebar = iv_titlebar text_question = iv_text_question text_button_1 = iv_text_button_1 icon_button_1 = iv_icon_button_1 text_button_2 = iv_text_button_2 icon_button_2 = iv_icon_button_2 default_button = iv_default_button display_cancel_button = iv_display_cancel_button popup_type = iv_popup_type start_column = ms_position-start_column start_row = ms_position-start_row IMPORTING answer = rv_answer EXCEPTIONS text_not_found = 1 OTHERS = 2. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error from POPUP_TO_CONFIRM'. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_popups~popup_to_create_package. DATA ls_data TYPE scompkdtln. MOVE-CORRESPONDING is_package_data TO ls_data. IF zcl_abapgit_factory=>get_function_module( )->function_exists( 'PB_POPUP_PACKAGE_CREATE' ) = abap_false. " looks like the function module used does not exist on all versions since 702 RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Your system does not support automatic creation of packages.' && ' Please, create the package manually.'. ENDIF. CALL FUNCTION 'PB_POPUP_PACKAGE_CREATE' CHANGING p_object_data = ls_data EXCEPTIONS action_cancelled = 1. ev_create = boolc( sy-subrc = 0 ). MOVE-CORRESPONDING ls_data TO es_package_data. ENDMETHOD. METHOD /apmg/if_apm_popups~popup_to_select_labels. ENDMETHOD. METHOD /apmg/if_apm_popups~popup_to_select_transport. CALL FUNCTION 'TR_F4_REQUESTS' IMPORTING ev_selected_request = rv_trkorr. ENDMETHOD. METHOD center. CONSTANTS: lc_min_size TYPE i VALUE 10, lc_min_pos TYPE i VALUE 5. " Magic math to approximate starting position of popup IF sy-scols > lc_min_size AND iv_width > 0 AND sy-scols > iv_width. rs_position-start_column = nmax( val1 = ( sy-scols - iv_width ) / 2 val2 = lc_min_pos ). ELSE. rs_position-start_column = lc_min_pos. ENDIF. IF sy-srows > lc_min_size AND iv_height > 0 AND sy-srows > iv_height. rs_position-start_row = nmax( val1 = ( sy-srows - iv_height ) / 2 - 1 val2 = lc_min_pos ). ELSE. rs_position-start_row = lc_min_pos. ENDIF. rs_position-end_column = rs_position-start_column + iv_width. rs_position-end_row = rs_position-start_row + iv_height. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_factory IMPLEMENTATION. METHOD get_asset_manager. DATA lo_buf TYPE REF TO zcl_abapgit_string_buffer. DATA li_asset_man TYPE REF TO /apmg/if_apm_gui_asset_manager. CREATE OBJECT lo_buf. li_asset_man = /apmg/cl_apm_gui_asset_manager=>create( ). **************************************************** * abapmerge Pragma - ZABAPGIT_CSS_COMMON **************************************************** lo_buf->add( '/*' ). lo_buf->add( ' * ABAPGIT COMMON CSS' ). lo_buf->add( ' */' ). lo_buf->add( '' ). lo_buf->add( '/* GLOBALS */' ). lo_buf->add( '' ). lo_buf->add( 'body {' ). lo_buf->add( ' overflow-x: hidden;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'body.centered {' ). lo_buf->add( ' max-width: 1280px;' ). lo_buf->add( ' margin: 0 auto;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'body.full_width {' ). lo_buf->add( ' width:100%;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'a, a:visited {' ). lo_buf->add( ' text-decoration: none;' ). lo_buf->add( '}' ). lo_buf->add( 'a:hover, a:active {' ). lo_buf->add( ' cursor: pointer;' ). lo_buf->add( ' text-decoration: underline;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'img {' ). lo_buf->add( ' border-width: 0px;' ). lo_buf->add( ' vertical-align: middle;' ). lo_buf->add( '}' ). lo_buf->add( 'table { border-collapse: collapse; }' ). lo_buf->add( 'pre { display: inline; }' ). lo_buf->add( 'sup {' ). lo_buf->add( ' vertical-align: top;' ). lo_buf->add( ' position: relative;' ). lo_buf->add( ' top: -0.5em;' ). lo_buf->add( ' font-size: 75%;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'input, textarea, select {' ). lo_buf->add( ' padding: 3px 0.5em;' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( '}' ). lo_buf->add( 'input:focus, textarea:focus {' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.cursor-pointer {' ). lo_buf->add( ' cursor: pointer;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'span.separator {' ). lo_buf->add( ' padding-left: 0.5em;' ). lo_buf->add( ' padding-right: 0.5em;' ). lo_buf->add( ' opacity: 0.25;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* MODIFIERS */' ). lo_buf->add( '' ). lo_buf->add( '.emphasis { font-weight: bold !important; }' ). lo_buf->add( '.crossout { text-decoration: line-through !important; }' ). lo_buf->add( '.right { text-align:right; }' ). lo_buf->add( '.center { text-align:center; }' ). lo_buf->add( '.paddings { padding: 0.5em 0.5em; }' ). lo_buf->add( '.pad-sides { padding-left: 0.3em; padding-right: 0.3em; }' ). lo_buf->add( '.pad-1em { padding: 1em 1em; }' ). lo_buf->add( '.margin-v5 { margin-top: 0.5em; margin-bottom: 0.5em; }' ). lo_buf->add( '.margin-v1 { margin-top: 1em; margin-bottom: 1em; }' ). lo_buf->add( '.indent5em { padding-left: 0.5em; }' ). lo_buf->add( '.pad4px { padding: 4px; }' ). lo_buf->add( '.w100 { width: 100%; }' ). lo_buf->add( '.wmin { width: 1%; }' ). lo_buf->add( '.w40 { width: 40%; }' ). lo_buf->add( '.float-right { float: right; }' ). lo_buf->add( '.pad-right { padding-right: 6px; }' ). lo_buf->add( '.no-pad { padding: 0px !important; }' ). lo_buf->add( '.inline { display: inline; }' ). lo_buf->add( '.hidden { visibility: hidden; }' ). lo_buf->add( '.nodisplay { display: none }' ). lo_buf->add( '.m-em5-sides { margin-left: 0.5em; margin-right: 0.5em }' ). lo_buf->add( '.w600px { width: 600px }' ). lo_buf->add( '.w800px { width: 800px }' ). lo_buf->add( '.w1000px { width: 1000px }' ). lo_buf->add( '.wmax600px { max-width: 600px }' ). lo_buf->add( '.auto-center { /* use with max-width */' ). lo_buf->add( ' width: 100%;' ). lo_buf->add( ' margin-left: auto;' ). lo_buf->add( ' margin-right: auto;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'span.boxed {' ). lo_buf->add( ' border-radius: 3px;' ). lo_buf->add( ' padding: 4px 7px;' ). lo_buf->add( ' margin-left: 0.2em;' ). lo_buf->add( ' margin-right: 0.2em;' ). lo_buf->add( ' font-size: smaller;' ). lo_buf->add( '}' ). lo_buf->add( 'span.boxed i.icon {' ). lo_buf->add( ' padding-right: 5px;' ). lo_buf->add( '}' ). lo_buf->add( '.red-filled-set {' ). lo_buf->add( ' border-width: 0px;' ). lo_buf->add( ' color: hsl(0, 78%, 93%);' ). lo_buf->add( ' background-color: hsl(0, 78%, 65%);' ). lo_buf->add( '}' ). lo_buf->add( '.green-filled-set {' ). lo_buf->add( ' border-width: 0px;' ). lo_buf->add( ' color: hsl(120, 45%, 90%);' ). lo_buf->add( ' background-color: hsl(120, 27%, 60%);' ). lo_buf->add( '}' ). lo_buf->add( '.yellow-filled-set {' ). lo_buf->add( ' border-width: 0px;' ). lo_buf->add( ' color: hsl(45, 99%, 90%);' ). lo_buf->add( ' background-color: hsl(45, 100%, 46%);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* PANELS */' ). lo_buf->add( '' ). lo_buf->add( 'div.panel {' ). lo_buf->add( ' border-radius: 3px;' ). lo_buf->add( ' padding: 0.5em 0.5em;' ). lo_buf->add( ' margin: 0.5em 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.dummydiv {' ). lo_buf->add( ' padding: 0.5em 1em;' ). lo_buf->add( ' text-align: center;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'a.close-btn {' ). lo_buf->add( ' text-decoration: none;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* STRUCTURE DIVS, HEADER & FOOTER */' ). lo_buf->add( '' ). lo_buf->add( 'div#header {' ). lo_buf->add( ' padding: 0.5em 0.5em;' ). lo_buf->add( ' border-bottom: 3px double;' ). lo_buf->add( '}' ). lo_buf->add( 'div#header > div { display: inline-block }' ). lo_buf->add( '' ). lo_buf->add( '.logo .icon { display: inline-block }' ). lo_buf->add( '.logo .icon:before { width: auto }' ). lo_buf->add( '' ). lo_buf->add( '/* official logo colors, not vars, redefine in themes directly*/' ). lo_buf->add( '.logo .icon.icon-git-alt { color: #f03c2e }' ). lo_buf->add( '.logo .icon.icon-abapgit {' ). lo_buf->add( ' color: #362701;' ). lo_buf->add( ' vertical-align: bottom;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div#header .logo { font-size: x-large }' ). lo_buf->add( 'div#header .page-title { font-size: x-large }' ). lo_buf->add( 'div#header span.spacer {' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( ' padding-right: 0.25em;' ). lo_buf->add( ' padding-left: 0.25em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div#footer .sponsor a { font-size: smaller; }' ). lo_buf->add( 'div#footer .logo { font-size: large }' ). lo_buf->add( 'div#footer {' ). lo_buf->add( ' padding: 0.5em 0.5em;' ). lo_buf->add( ' border-top: 3px double;' ). lo_buf->add( '}' ). lo_buf->add( 'div#footer .version {' ). lo_buf->add( ' margin-top: 0.5em;' ). lo_buf->add( ' font-size: small;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '#debug-output {' ). lo_buf->add( ' text-align: right;' ). lo_buf->add( ' padding-right: 0.5em;' ). lo_buf->add( ' font-size: smaller;' ). lo_buf->add( '}' ). lo_buf->add( '#debug-output p {' ). lo_buf->add( ' margin-top: 0em;' ). lo_buf->add( ' margin-bottom: 0em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* ERROR LOG */' ). lo_buf->add( '' ). lo_buf->add( 'div.log {' ). lo_buf->add( ' padding: 6px;' ). lo_buf->add( ' margin: 4px;' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( ' border-radius: 4px;' ). lo_buf->add( '}' ). lo_buf->add( 'div.log > span { display:block; }' ). lo_buf->add( 'div.log .icon { padding-right: 6px; }' ). lo_buf->add( '' ). lo_buf->add( '/* REPOSITORY */' ). lo_buf->add( '' ). lo_buf->add( 'div.repo {' ). lo_buf->add( ' padding: 0.5em 1em 0.5em 1em;' ). lo_buf->add( ' position: relative;' ). lo_buf->add( '}' ). lo_buf->add( '.repo_name span.name {' ). lo_buf->add( ' font-weight: bold;' ). lo_buf->add( ' font-size: 14pt;' ). lo_buf->add( '}' ). lo_buf->add( '.repo_name a.url {' ). lo_buf->add( ' font-size: 12pt;' ). lo_buf->add( ' margin-left: 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( '.repo_name span.url {' ). lo_buf->add( ' font-size: 12pt;' ). lo_buf->add( ' margin-left: 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( '.repo_name .icon {' ). lo_buf->add( ' padding-right: 4px;' ). lo_buf->add( '}' ). lo_buf->add( '.repo_attr {' ). lo_buf->add( ' font-size: 12pt;' ). lo_buf->add( '}' ). lo_buf->add( '.repo_attr span {' ). lo_buf->add( ' margin-left: 0.2em;' ). lo_buf->add( ' margin-right: 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( '.repo_attr span.bg_marker {' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( ' border-radius: 3px;' ). lo_buf->add( ' font-size: 8pt;' ). lo_buf->add( ' padding: 4px 2px 3px 2px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* ABAPGIT OBJECTS */' ). lo_buf->add( '' ). lo_buf->add( 'span.branch,' ). lo_buf->add( 'span.user-box,' ). lo_buf->add( 'span.package-box,' ). lo_buf->add( 'span.path-box,' ). lo_buf->add( 'span.transport-box {' ). lo_buf->add( ' padding: 2px 4px;' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( ' border-radius: 4px;' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'span.package-box i.icon {' ). lo_buf->add( ' margin-right: 0.15em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* MISC AND REFACTOR */' ). lo_buf->add( '' ). lo_buf->add( '.hidden-submit {' ). lo_buf->add( ' border: 0 none;' ). lo_buf->add( ' height: 0;' ). lo_buf->add( ' width: 0;' ). lo_buf->add( ' padding: 0;' ). lo_buf->add( ' margin: 0;' ). lo_buf->add( ' position: absolute;' ). lo_buf->add( ' overflow: hidden;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* STATE BLOCK COMMON*/' ). lo_buf->add( '' ). lo_buf->add( 'span.state-block {' ). lo_buf->add( ' margin-left: 1em;' ). lo_buf->add( ' font-family: Consolas, "Lucida Console", Courier, monospace;' ). lo_buf->add( ' font-size: x-small;' ). lo_buf->add( ' vertical-align: 13%;' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( ' text-align: center;' ). lo_buf->add( ' white-space: nowrap;' ). lo_buf->add( '}' ). lo_buf->add( 'span.state-block span {' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( ' padding: 0px 3px;' ). lo_buf->add( ' border-width: 1px;' ). lo_buf->add( ' border-style: solid;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* REPOSITORY TABLE*/' ). lo_buf->add( '' ). lo_buf->add( 'div.repo_container {' ). lo_buf->add( ' position: relative;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.repo_banner {' ). lo_buf->add( ' margin: 0em 1em 1em;' ). lo_buf->add( ' padding: 0.5em 0.5em;' ). lo_buf->add( ' text-align: center;' ). lo_buf->add( ' font-size: 85%;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'table.repo_tab {' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( ' border-radius: 3px;' ). lo_buf->add( ' width: 100%;' ). lo_buf->add( ' line-height: 1.2;' ). lo_buf->add( '}' ). lo_buf->add( '.repo_tab th {' ). lo_buf->add( ' text-align: left;' ). lo_buf->add( ' padding: 0.5em;' ). lo_buf->add( ' border-bottom: 1px solid;' ). lo_buf->add( ' font-weight: normal;' ). lo_buf->add( '}' ). lo_buf->add( '.repo_tab td {' ). lo_buf->add( ' vertical-align: middle;' ). lo_buf->add( ' padding-top: 2px;' ). lo_buf->add( ' padding-bottom: 2px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.repo_tab td.icon {' ). lo_buf->add( ' width: 1px;' ). lo_buf->add( ' text-align: center;' ). lo_buf->add( ' padding-left: 8px;' ). lo_buf->add( ' padding-right: 4px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.repo_tab td.transport {' ). lo_buf->add( ' width: 140px;' ). lo_buf->add( ' text-align: left;' ). lo_buf->add( '}' ). lo_buf->add( '.repo_tab td.type {' ). lo_buf->add( ' width: 4em;' ). lo_buf->add( ' padding-left: 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.repo_tab td.filename{' ). lo_buf->add( ' padding-left: 1em;' ). lo_buf->add( ' word-break: break-all;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.repo_tab td.object {' ). lo_buf->add( ' padding-left: 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.repo_tab td.files {' ). lo_buf->add( ' padding-left: 0.5em;' ). lo_buf->add( ' line-height: 1.5;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.repo_tab tr.object_row{' ). lo_buf->add( ' border-top: 1px solid;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.repo_tab td.cmd, .repo_tab th.cmd {' ). lo_buf->add( ' text-align: right;' ). lo_buf->add( ' padding-left: 0.5em;' ). lo_buf->add( ' padding-right: 0.7em;' ). lo_buf->add( ' min-width: 70px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.repo_tab th.cmd .icon{' ). lo_buf->add( ' padding-right: 8px;' ). lo_buf->add( '}' ). lo_buf->add( '.repo_tab tr:first-child td { border-top: 0px; }' ). lo_buf->add( '' ). lo_buf->add( '.repo_tab tr:hover td {' ). lo_buf->add( ' background-image: linear-gradient(rgba(0, 0, 0, 0.075), rgba(0, 0, 0, 0.075));' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* STAGE */' ). lo_buf->add( '' ). lo_buf->add( 'th.stage-status { width: 30px; }' ). lo_buf->add( 'th.stage-objtype { width: 30px; }' ). lo_buf->add( 'input.stage-filter { width: 18em; }' ). lo_buf->add( '' ). lo_buf->add( '.stage_tab {' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( ' margin-top: 0.2em;' ). lo_buf->add( ' line-height: 1.5;' ). lo_buf->add( '}' ). lo_buf->add( '.stage_tab td {' ). lo_buf->add( ' border-top: 1px solid;' ). lo_buf->add( ' vertical-align: middle;' ). lo_buf->add( ' padding: 2px 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( '.stage_tab th {' ). lo_buf->add( ' text-align: left;' ). lo_buf->add( ' font-weight: normal;' ). lo_buf->add( ' padding: 4px 0.5em;' ). lo_buf->add( ' border-bottom: 1px solid;' ). lo_buf->add( '}' ). lo_buf->add( '.stage_tab td.status {' ). lo_buf->add( ' width: 2em;' ). lo_buf->add( ' text-align: center;' ). lo_buf->add( '}' ). lo_buf->add( '.stage_tab td.highlight {' ). lo_buf->add( ' font-weight: bold;' ). lo_buf->add( '}' ). lo_buf->add( '.stage_tab td.name {' ). lo_buf->add( ' word-break: break-all;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.stage_tab tr:first-child td { border-top: 0px; }' ). lo_buf->add( '' ). lo_buf->add( '.stage_tab tr:hover td {' ). lo_buf->add( ' background-image: linear-gradient(rgba(0, 0, 0, 0.075), rgba(0, 0, 0, 0.075));' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.stage_tab td.cmd { cursor: pointer; }' ). lo_buf->add( '.stage_tab td.cmd a { padding: 0px 4px; }' ). lo_buf->add( '.stage_tab th.cmd a { padding: 0px 4px; }' ). lo_buf->add( '.stage_tab tbody tr:first-child td { padding-top: 0.5em; }' ). lo_buf->add( '.stage_tab tbody tr:last-child td { padding-bottom: 0.5em; }' ). lo_buf->add( '' ). lo_buf->add( '/* COMMIT */' ). lo_buf->add( '' ). lo_buf->add( 'div.form-container {' ). lo_buf->add( ' padding: 1em 1em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'form.aligned-form {' ). lo_buf->add( ' display: table;' ). lo_buf->add( ' border-spacing: 2px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'form.aligned-form label {' ). lo_buf->add( ' padding-right: 1em;' ). lo_buf->add( ' vertical-align: middle;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'form.aligned-form select {' ). lo_buf->add( ' padding-right: 1em;' ). lo_buf->add( ' vertical-align: middle;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'form.aligned-form span.sub-title {' ). lo_buf->add( ' font-size: smaller;' ). lo_buf->add( ' padding-top: 8px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'form.aligned-form div.row { display: table-row; }' ). lo_buf->add( 'form.aligned-form label { display: table-cell; }' ). lo_buf->add( 'form.aligned-form input { display: table-cell; }' ). lo_buf->add( 'form.aligned-form input[type="text"] { width: 25em; }' ). lo_buf->add( 'form.aligned-form span.cell { display: table-cell; }' ). lo_buf->add( '' ). lo_buf->add( '/* SETTINGS STYLES */' ). lo_buf->add( '' ). lo_buf->add( 'div.settings_container {' ). lo_buf->add( ' padding: 0.5em 0.5em 1em;' ). lo_buf->add( ' font-size: 10pt;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.settings_section {' ). lo_buf->add( ' margin-left:50px' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'table.settings td:first-child {' ). lo_buf->add( ' padding-left: 1em;' ). lo_buf->add( ' padding-right: 1em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* DIFF */' ). lo_buf->add( '' ). lo_buf->add( 'div.diff {' ). lo_buf->add( ' padding: 0.7em' ). lo_buf->add( '}' ). lo_buf->add( 'div.diff_head {' ). lo_buf->add( ' padding-bottom: 0.7em;' ). lo_buf->add( '}' ). lo_buf->add( 'span.diff_name {' ). lo_buf->add( ' padding-left: 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( 'span.diff_changed_by {' ). lo_buf->add( ' float: right;' ). lo_buf->add( '}' ). lo_buf->add( 'span.diff_banner {' ). lo_buf->add( ' border-style: solid;' ). lo_buf->add( ' border-width: 1px;' ). lo_buf->add( ' border-radius: 3px;' ). lo_buf->add( ' padding-left: 0.3em;' ). lo_buf->add( ' padding-right: 0.3em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.diff_content {' ). lo_buf->add( ' border-top: 1px solid;' ). lo_buf->add( ' border-bottom: 1px solid;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.diff_content tbody tr td{' ). lo_buf->add( ' width: 50%;' ). lo_buf->add( ' vertical-align: top' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.diff_head span.state-block {' ). lo_buf->add( ' margin-left: 0.5em;' ). lo_buf->add( ' font-size: inherit;' ). lo_buf->add( ' vertical-align: initial;' ). lo_buf->add( '}' ). lo_buf->add( 'div.diff_head span.state-block span {' ). lo_buf->add( ' padding: 0px 4px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* DIFF TABLE */' ). lo_buf->add( '' ). lo_buf->add( 'table.diff_tab {' ). lo_buf->add( ' font-family: Consolas, Courier, monospace;' ). lo_buf->add( ' font-size: 10pt;' ). lo_buf->add( ' width: 100%;' ). lo_buf->add( '}' ). lo_buf->add( 'table.diff_tab td,' ). lo_buf->add( 'table.diff_tab th {' ). lo_buf->add( ' padding-left: 0.5em;' ). lo_buf->add( ' padding-right: 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( 'table.diff_tab th {' ). lo_buf->add( ' text-align: left;' ). lo_buf->add( ' font-weight: normal;' ). lo_buf->add( ' padding-top: 3px;' ). lo_buf->add( ' padding-bottom: 3px;' ). lo_buf->add( '}' ). lo_buf->add( 'table.diff_tab thead.header th {' ). lo_buf->add( ' text-align: left;' ). lo_buf->add( ' font-weight: bold;' ). lo_buf->add( ' padding-left: 0.5em;' ). lo_buf->add( ' font-size: 9pt;' ). lo_buf->add( '}' ). lo_buf->add( 'table.diff_tab td.num, th.num {' ). lo_buf->add( ' width: 1%;' ). lo_buf->add( ' min-width: 2em;' ). lo_buf->add( ' padding-right: 8px;' ). lo_buf->add( ' padding-left: 8px;' ). lo_buf->add( ' text-align: right !important;' ). lo_buf->add( ' border-left: 1px solid;' ). lo_buf->add( ' border-right: 1px solid;' ). lo_buf->add( ' -ms-user-select: none;' ). lo_buf->add( ' user-select: none;' ). lo_buf->add( '}' ). lo_buf->add( 'table.diff_tab td.patch, th.patch {' ). lo_buf->add( ' width: 1%;' ). lo_buf->add( ' min-width: 1.5em;' ). lo_buf->add( ' padding-right: 8px;' ). lo_buf->add( ' padding-left: 8px;' ). lo_buf->add( ' text-align: right !important;' ). lo_buf->add( ' border-left: 1px solid;' ). lo_buf->add( ' border-right: 1px solid;' ). lo_buf->add( ' -ms-user-select: none;' ). lo_buf->add( ' user-select: none;' ). lo_buf->add( ' cursor: pointer;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'table.diff_tab tr.diff_line:hover td {' ). lo_buf->add( ' background-image: linear-gradient(rgba(0, 0, 0, 0.075), rgba(0, 0, 0, 0.075));' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'table.diff_tab td.num::before {' ). lo_buf->add( ' content: attr(line-num);' ). lo_buf->add( '}' ). lo_buf->add( 'table.diff_tab code {' ). lo_buf->add( ' font-family: inherit;' ). lo_buf->add( ' white-space: pre;' ). lo_buf->add( '}' ). lo_buf->add( 'table.diff_tab td.code {' ). lo_buf->add( ' word-wrap: break-word;' ). lo_buf->add( ' white-space: pre-wrap;' ). lo_buf->add( ' overflow: visible;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'table.diff_tab tbody tr:first-child td { padding-top: 0.5em; }' ). lo_buf->add( 'table.diff_tab tbody tr:last-child td { padding-bottom: 0.5em; }' ). lo_buf->add( '' ). lo_buf->add( 'table.diff_tab td.mark, th.mark {' ). lo_buf->add( ' width: 0.1%;' ). lo_buf->add( ' -ms-user-select: none;' ). lo_buf->add( ' user-select: none;' ). lo_buf->add( ' cursor: default;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.diff_select_left td.diff_right,' ). lo_buf->add( '.diff_select_left td.diff_right *,' ). lo_buf->add( '.diff_select_left th.diff_right,' ). lo_buf->add( '.diff_select_left th.diff_right *,' ). lo_buf->add( '.diff_select_right td.diff_left,' ). lo_buf->add( '.diff_select_right td.diff_left *,' ). lo_buf->add( '.diff_select_right th.diff_left,' ). lo_buf->add( '.diff_select_right th.diff_left * {' ). lo_buf->add( ' -ms-user-select: none;' ). lo_buf->add( ' user-select: none;' ). lo_buf->add( ' cursor: text;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.diff_select_left td.diff_left,' ). lo_buf->add( '.diff_select_left td.diff_left *,' ). lo_buf->add( '.diff_select_left th.diff_left,' ). lo_buf->add( '.diff_select_left th.diff_left *,' ). lo_buf->add( '.diff_select_right td.diff_right,' ). lo_buf->add( '.diff_select_right td.diff_right *,' ). lo_buf->add( '.diff_select_right th.diff_right,' ). lo_buf->add( '.diff_select_right th.diff_right * {' ). lo_buf->add( ' -ms-user-select: text;' ). lo_buf->add( ' user-select: text;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'td.diff_others::selection,' ). lo_buf->add( 'td.diff_others *::selection,' ). lo_buf->add( 'th.diff_others::selection,' ). lo_buf->add( 'th.diff_others *::selection {' ). lo_buf->add( ' background-color: transparent;' ). lo_buf->add( ' cursor: default;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.diff_select_left td.diff_right::selection,' ). lo_buf->add( '.diff_select_left td.diff_right *::selection,' ). lo_buf->add( '.diff_select_left th.diff_right::selection,' ). lo_buf->add( '.diff_select_left th.diff_right *::selection,' ). lo_buf->add( '.diff_select_right td.diff_left::selection,' ). lo_buf->add( '.diff_select_right td.diff_left *::selection,' ). lo_buf->add( '.diff_select_right th.diff_left::selection,' ). lo_buf->add( '.diff_select_right th.diff_left *::selection {' ). lo_buf->add( ' background-color: transparent;' ). lo_buf->add( ' cursor: text;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* DEBUG INFO STYLES */' ). lo_buf->add( '' ). lo_buf->add( 'div.debug_container {' ). lo_buf->add( ' padding: 0.5em;' ). lo_buf->add( ' font-size: 10pt;' ). lo_buf->add( ' font-family: Consolas, Courier, monospace;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.debug_container p {' ). lo_buf->add( ' margin: 0px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* *** */' ). lo_buf->add( '' ). lo_buf->add( 'li.action_link.enabled{' ). lo_buf->add( ' visibility: visible;' ). lo_buf->add( ' position: relative;' ). lo_buf->add( ' display: block;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'li.action_link:not(enabled){' ). lo_buf->add( ' visibility: hidden;' ). lo_buf->add( ' position: fixed; /* so it does not take up space when hidden */' ). lo_buf->add( ' display: none;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* TUTORIAL */' ). lo_buf->add( '' ). lo_buf->add( 'div.tutorial {' ). lo_buf->add( ' margin-top: 3px;' ). lo_buf->add( ' padding: 0.5em 1em 0.5em 1em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.tutorial li { margin: 2px 0px }' ). lo_buf->add( 'div.tutorial h1 { font-size: 18pt; }' ). lo_buf->add( 'div.tutorial h2 { font-size: 14pt;}' ). lo_buf->add( '' ). lo_buf->add( '/* MENU */' ). lo_buf->add( '' ). lo_buf->add( '/* Special credits to example at https://codepen.io/philhoyt/pen/ujHzd */' ). lo_buf->add( '/* container div, aligned left, but with .float-right modifier aligns right */' ). lo_buf->add( '' ). lo_buf->add( '.nav-container ul {' ). lo_buf->add( ' list-style: none;' ). lo_buf->add( ' position: relative;' ). lo_buf->add( ' float: left;' ). lo_buf->add( ' margin: 0;' ). lo_buf->add( ' padding: 0;' ). lo_buf->add( ' white-space: nowrap;' ). lo_buf->add( ' text-align: left;' ). lo_buf->add( '}' ). lo_buf->add( '.nav-container.float-right ul { float: right; }' ). lo_buf->add( '' ). lo_buf->add( '.nav-container ul a {' ). lo_buf->add( ' display: block;' ). lo_buf->add( ' text-decoration: none;' ). lo_buf->add( ' line-height: 30px;' ). lo_buf->add( ' padding: 0 12px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* clearfix https://css-tricks.com/snippets/css/clear-fix/ */' ). lo_buf->add( '.nav-container:after {' ). lo_buf->add( ' clear: both;' ). lo_buf->add( ' display: block;' ). lo_buf->add( ' content: "";' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* submenues align to left or right border of the active item' ). lo_buf->add( ' depending on .float-right modifier */' ). lo_buf->add( '.nav-container ul li {' ). lo_buf->add( ' position: relative;' ). lo_buf->add( ' float: left;' ). lo_buf->add( ' margin: 0;' ). lo_buf->add( ' padding: 0;' ). lo_buf->add( '}' ). lo_buf->add( '.nav-container.float-right ul ul { left: auto; right: 0; }' ). lo_buf->add( '.nav-container ul li.current-menu-item { font-weight: 700; }' ). lo_buf->add( '.nav-container ul li.force-nav-hover ul { display: block; }' ). lo_buf->add( '.nav-container ul li:hover > ul { display: block; }' ). lo_buf->add( '' ). lo_buf->add( '/* special selection style for 1st level items (see also .corner below) */' ). lo_buf->add( '' ). lo_buf->add( '.nav-container ul ul {' ). lo_buf->add( ' display: none;' ). lo_buf->add( ' position: absolute;' ). lo_buf->add( ' top: 100%;' ). lo_buf->add( ' left: 0;' ). lo_buf->add( ' z-index: 1;' ). lo_buf->add( ' padding: 0;' ). lo_buf->add( ' box-shadow: 1px 1px 3px 0px #bbb;' ). lo_buf->add( ' max-height: 700px;' ). lo_buf->add( ' overflow: auto;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.nav-container ul ul li {' ). lo_buf->add( ' float: none;' ). lo_buf->add( ' min-width: 160px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.nav-container ul ul a {' ). lo_buf->add( ' line-height: 120%;' ). lo_buf->add( ' padding: 8px 15px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.nav-container ul ul ul {' ). lo_buf->add( ' top: 0;' ). lo_buf->add( ' left: 100%;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.nav-container.float-right ul ul ul {' ). lo_buf->add( ' left: auto;' ). lo_buf->add( ' right: 100%;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* Minizone to extent hover area,' ). lo_buf->add( ' aligned to the left or to the right of the selected item' ). lo_buf->add( ' depending on .float-right modifier */' ). lo_buf->add( '.nav-container > ul > li > div.minizone {' ). lo_buf->add( ' display: none;' ). lo_buf->add( ' z-index: 1;' ). lo_buf->add( ' position: absolute;' ). lo_buf->add( ' padding: 0px;' ). lo_buf->add( ' width: 16px;' ). lo_buf->add( ' height: 100%;' ). lo_buf->add( ' bottom: 0px;' ). lo_buf->add( ' left: 100%;' ). lo_buf->add( '}' ). lo_buf->add( '.nav-container > ul > li:hover div.minizone { display: block; }' ). lo_buf->add( '.nav-container.float-right > ul > li > div.minizone {' ). lo_buf->add( ' left: auto;' ). lo_buf->add( ' right: 100%;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* icons - text-align strictly left - otherwise look ugly' ). lo_buf->add( ' + bite a bit of left padding for nicer look' ). lo_buf->add( ' + forbids item text wrapping (maybe can be done differently) */' ). lo_buf->add( '.nav-container ul ul li a .icon {' ). lo_buf->add( ' padding-right: 10px;' ). lo_buf->add( ' margin-left: -3px;' ). lo_buf->add( '}' ). lo_buf->add( '.nav-container ul.with-icons li {' ). lo_buf->add( ' text-align: left;' ). lo_buf->add( ' white-space: nowrap;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* Special .corner modifier - hangs menu at the top right corner' ). lo_buf->add( ' and cancels 1st level background coloring */' ). lo_buf->add( '.nav-container.corner {' ). lo_buf->add( ' position: absolute;' ). lo_buf->add( ' right: 0px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* Toolbar separator style */' ). lo_buf->add( '.nav-container ul ul li.separator {' ). lo_buf->add( ' font-size: x-small;' ). lo_buf->add( ' text-align: center;' ). lo_buf->add( ' padding: 4px 0;' ). lo_buf->add( ' text-transform: uppercase;' ). lo_buf->add( ' border-bottom: 1px solid;' ). lo_buf->add( ' border-top: 1px solid;' ). lo_buf->add( '}' ). lo_buf->add( '.nav-container ul ul li.separator:first-child { border-top: none; }' ). lo_buf->add( '' ). lo_buf->add( '/* NEWS ANNOUNCEMENT */' ). lo_buf->add( '' ). lo_buf->add( 'div.info-panel {' ). lo_buf->add( ' position: absolute;' ). lo_buf->add( ' z-index: 99;' ). lo_buf->add( ' top: 36px;' ). lo_buf->add( ' left: 50%;' ). lo_buf->add( ' width: 40em;' ). lo_buf->add( ' margin-left: -20em;' ). lo_buf->add( ' box-shadow: 1px 1px 3px 2px #dcdcdc;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.info-panel-fixed {' ). lo_buf->add( ' position: fixed;' ). lo_buf->add( ' top: 15%;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.info-panel div.info-hint {' ). lo_buf->add( ' text-transform: uppercase;' ). lo_buf->add( ' font-size: small;' ). lo_buf->add( ' padding: 8px 6px 0px;' ). lo_buf->add( ' text-align: center;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.info-panel div.info-title {' ). lo_buf->add( ' text-transform: uppercase;' ). lo_buf->add( ' font-size: small;' ). lo_buf->add( ' padding: 6px;' ). lo_buf->add( ' text-align: center;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.info-panel div.info-title a.close-btn {' ). lo_buf->add( ' padding-left: 12px;' ). lo_buf->add( ' padding-right: 2px;' ). lo_buf->add( ' position: relative;' ). lo_buf->add( ' bottom: 1px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.info-panel div.info-list {' ). lo_buf->add( ' padding: 0.8em 0.7em 1em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.info-panel ul {' ). lo_buf->add( ' padding-left: 10px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.info-panel li {' ). lo_buf->add( ' padding-left: 0px;' ). lo_buf->add( ' list-style-type: none;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.info-panel h1:first-child { margin: auto; }' ). lo_buf->add( 'div.info-panel h1 {' ). lo_buf->add( ' font-size: inherit;' ). lo_buf->add( ' padding: 6px 4px;' ). lo_buf->add( ' margin: 4px auto auto;' ). lo_buf->add( ' text-decoration: underline;' ). lo_buf->add( ' font-weight: normal;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.info-panel .version-marker {' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( ' margin-left: 20px;' ). lo_buf->add( ' border-radius: 3px;' ). lo_buf->add( ' padding: 0px 6px;' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.info-panel .update { border: 1px solid; }' ). lo_buf->add( 'div.info-panel div.info-list td { padding-right: 1em }' ). lo_buf->add( '' ). lo_buf->add( '/* ERROR MESSAGE PANEL */' ). lo_buf->add( '' ). lo_buf->add( 'div.message-panel {' ). lo_buf->add( ' z-index: 99;' ). lo_buf->add( ' box-shadow: 2px 2px 4px 0px hsla(0, 0%, 0%, .1);' ). lo_buf->add( ' padding: 0.5em 1em;' ). lo_buf->add( ' position: fixed;' ). lo_buf->add( ' bottom: 12px;' ). lo_buf->add( ' width: 95%;' ). lo_buf->add( ' margin: 0 auto;' ). lo_buf->add( ' max-width: 1248px;' ). lo_buf->add( '' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( ' border-radius: 5px;' ). lo_buf->add( ' border-color: hsl(0, 42%, 64%);' ). lo_buf->add( ' background-color: hsla(0, 42%, 90%, 1);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.message-panel-bar {' ). lo_buf->add( ' position: absolute;' ). lo_buf->add( ' bottom: 10px;' ). lo_buf->add( ' right: 10px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.message-panel-commands {' ). lo_buf->add( ' display: none;' ). lo_buf->add( ' margin-right: 2em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.message-panel-commands a {' ). lo_buf->add( ' padding: 0em 0.5em;' ). lo_buf->add( ' border-left: 1px solid;' ). lo_buf->add( ' border-left-color: #ccc;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.message-panel-commands a:first-child {' ). lo_buf->add( ' padding-left: 0;' ). lo_buf->add( ' border-left: none;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.message-panel:hover .message-panel-commands {' ). lo_buf->add( ' display: block;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* TOOLTIP TEXT */' ). lo_buf->add( '' ). lo_buf->add( '.link-hint {' ). lo_buf->add( ' line-height: 1em;' ). lo_buf->add( ' text-align: center;' ). lo_buf->add( ' padding: 5px 15px;' ). lo_buf->add( ' border-radius: 4px;' ). lo_buf->add( ' position: absolute;' ). lo_buf->add( ' z-index: 1;' ). lo_buf->add( ' margin-top: -30px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.link-hint-a {' ). lo_buf->add( ' margin-left: -60px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.link-hint-input {' ). lo_buf->add( ' margin-left: -30px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.link-hint-i {' ). lo_buf->add( ' margin-left: -30px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.link-hint .pending { color: hsla(0, 0%, 0%, 0.2); }' ). lo_buf->add( '' ). lo_buf->add( '/* Tooltip arrow */' ). lo_buf->add( '.link-hint::after {' ). lo_buf->add( ' content: "";' ). lo_buf->add( ' position: absolute;' ). lo_buf->add( ' top: 100%;' ). lo_buf->add( ' left: 50%;' ). lo_buf->add( ' margin-left: -5px;' ). lo_buf->add( ' border-width: 5px;' ). lo_buf->add( ' border-style: solid;' ). lo_buf->add( ' border-color: transparent;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* HOTKEYS */' ). lo_buf->add( '' ). lo_buf->add( 'ul.hotkeys {' ). lo_buf->add( ' list-style-type: none;' ). lo_buf->add( ' padding: 0;' ). lo_buf->add( ' margin: 0;' ). lo_buf->add( ' font-size: smaller;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'ul.hotkeys span.key-id {' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( ' border-radius: 3px;' ). lo_buf->add( ' padding: 1px 7px;' ). lo_buf->add( ' width: 3em;' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( ' text-align: center;' ). lo_buf->add( ' margin-top: 0.2em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'ul.hotkeys span.key-descr {' ). lo_buf->add( ' margin-left: 1.2em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.corner-hint {' ). lo_buf->add( ' position: fixed;' ). lo_buf->add( ' bottom: 10px;' ). lo_buf->add( ' right: 10px;' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( ' border-radius: 3px;' ). lo_buf->add( ' padding: 4px;' ). lo_buf->add( ' font-size: smaller;' ). lo_buf->add( ' opacity: 0.5;' ). lo_buf->add( ' z-index: 99;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* Commit popup */' ). lo_buf->add( 'table.commit tr .title {' ). lo_buf->add( ' font-weight: bold;' ). lo_buf->add( ' vertical-align: top;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* Repo overview */' ). lo_buf->add( '.repo-overview {' ). lo_buf->add( ' padding: 0.5em 0.7em;' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview table {' ). lo_buf->add( ' font-size: 90%;' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview-toolbar {' ). lo_buf->add( ' padding: 1em 1em;' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview-toolbar label {' ). lo_buf->add( ' margin-right: 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview th {' ). lo_buf->add( ' text-align: left;' ). lo_buf->add( ' font-weight: normal;' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview table {' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview thead tr {' ). lo_buf->add( ' border-bottom: 1px solid;' ). lo_buf->add( ' line-height: 1.5;' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview tfoot tr {' ). lo_buf->add( ' border-top: 1px solid;' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview tr.favorite .icon-star {' ). lo_buf->add( ' color: #5e8dc9 !important;' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview td,' ). lo_buf->add( '.repo-overview th {' ). lo_buf->add( ' padding: 6px 6px; /* maybe use height ? */' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview .ro-detail { display: none; }' ). lo_buf->add( '.repo-overview .ro-go a {' ). lo_buf->add( ' padding: 0px 0.15em;' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview .ro-go a:hover {' ). lo_buf->add( ' color: #ff721e;' ). lo_buf->add( ' text-decoration: none;' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview td.labels {' ). lo_buf->add( ' max-width: 18ch;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* REPO LABELS */' ). lo_buf->add( '' ). lo_buf->add( '.repo-label-catalog {' ). lo_buf->add( ' padding: 1em 1em;' ). lo_buf->add( ' margin-top: -1em;' ). lo_buf->add( '}' ). lo_buf->add( '.repo-label-catalog label {' ). lo_buf->add( ' margin-right: 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( 'ul.repo-labels {' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( ' list-style-type: none;' ). lo_buf->add( ' padding-inline-start: 0px;' ). lo_buf->add( ' padding-left: 0px;' ). lo_buf->add( ' margin-block-start: 0px;' ). lo_buf->add( ' margin-block-end: 0px;' ). lo_buf->add( ' margin-top: 0px;' ). lo_buf->add( ' margin-bottom: 0px;' ). lo_buf->add( '}' ). lo_buf->add( 'ul.repo-labels li {' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( ' padding: 3px 5px;' ). lo_buf->add( ' border-radius: 3px;' ). lo_buf->add( ' border-style: solid;' ). lo_buf->add( ' border-width: 1px;' ). lo_buf->add( ' margin-bottom: 2px;' ). lo_buf->add( '}' ). lo_buf->add( 'ul.repo-labels li a {' ). lo_buf->add( ' color: inherit;' ). lo_buf->add( '}' ). lo_buf->add( 'ul.repo-labels li:not(:last-child) { margin-right: 0.3em; }' ). lo_buf->add( 'table ul.repo-labels li {' ). lo_buf->add( ' font-size: 90%;' ). lo_buf->add( ' padding: 2px 4px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* LABEL COLORS */' ). lo_buf->add( '' ). lo_buf->add( '.rl-white {' ). lo_buf->add( ' color: hsl(0, 0%, 30%);' ). lo_buf->add( ' background-color: hsl(0, 0%, 100%);' ). lo_buf->add( ' border-color: hsl(0, 0%, 80%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-white-b {' ). lo_buf->add( ' color: hsl(214, 100%, 60%);' ). lo_buf->add( ' background-color: hsl(0, 0%, 100%);' ). lo_buf->add( ' border-color: hsl(214, 89%, 86%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-white-r {' ). lo_buf->add( ' color: hsl(0, 100%, 41%);' ). lo_buf->add( ' background-color: hsl(0, 0%, 100%);' ). lo_buf->add( ' border-color: hsl(0, 100%, 85%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-grey {' ). lo_buf->add( ' color: hsl(0, 0%, 100%);' ). lo_buf->add( ' background-color: hsl(0, 0%, 70%);' ). lo_buf->add( ' border-color: hsl(0, 0%, 60%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-dark-w {' ). lo_buf->add( ' color: hsl(0, 0%, 100%);' ). lo_buf->add( ' background-color: hsl(0, 0%, 25%);' ). lo_buf->add( ' border-color: hsl(0, 0%, 25%);;' ). lo_buf->add( '}' ). lo_buf->add( '.rl-dark-y {' ). lo_buf->add( ' color: hsl(43, 95%, 75%);' ). lo_buf->add( ' background-color: hsl(0, 0%, 25%);' ). lo_buf->add( ' border-color: hsl(0, 0%, 25%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-dark-r {' ). lo_buf->add( ' color: hsl(0, 100%, 74%);' ). lo_buf->add( ' background-color: hsl(0, 0%, 25%);' ). lo_buf->add( ' border-color: hsl(0, 0%, 25%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-dark-b {' ). lo_buf->add( ' color: hsl(227, 92%, 80%);' ). lo_buf->add( ' background-color: hsl(0, 0%, 25%);' ). lo_buf->add( ' border-color: hsl(0, 0%, 25%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-lightblue {' ). lo_buf->add( ' color: hsl(217, 80%, 25%);' ). lo_buf->add( ' background-color: hsl(216, 76%, 84%);' ). lo_buf->add( ' border-color: hsl(216, 76%, 73%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-darkblue {' ). lo_buf->add( ' color: hsl(218, 77%, 88%);' ). lo_buf->add( ' background-color: hsl(217, 66%, 32%);' ). lo_buf->add( ' border-color: hsl(217, 66%, 20%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-lightgreen {' ). lo_buf->add( ' color: hsl(153, 76%, 18%);' ). lo_buf->add( ' background-color: hsl(152, 65%, 82%);' ). lo_buf->add( ' border-color: hsl(152, 65%, 65%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-darkgreen {' ). lo_buf->add( ' color: hsl(0, 0%, 100%);' ). lo_buf->add( ' background-color: hsl(153, 77%, 37%);' ). lo_buf->add( ' border-color: hsl(153, 77%, 30%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-lightred {' ). lo_buf->add( ' color: hsl(8, 86%, 29%);' ). lo_buf->add( ' background-color: hsl(8, 74%, 80%);' ). lo_buf->add( ' border-color: hsl(8, 74%, 70%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-darkred {' ). lo_buf->add( ' color: hsl(7, 76%, 85%);' ). lo_buf->add( ' background-color: hsl(8, 77%, 29%);' ). lo_buf->add( ' border-color: hsl(8, 77%, 20%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-yellow {' ). lo_buf->add( ' color: hsl(44, 87%, 22%);' ). lo_buf->add( ' background-color: hsl(44, 94%, 87%);' ). lo_buf->add( ' border-color: hsl(44, 94%, 70%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-darkyellow {' ). lo_buf->add( ' color: hsl(49, 100%, 24%);' ). lo_buf->add( ' background-color: hsl(49, 100%, 64%);' ). lo_buf->add( ' border-color: hsl(49, 100%, 49%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-orange {' ). lo_buf->add( ' color: hsl(0, 0%, 100%);' ). lo_buf->add( ' background-color: hsl(19, 100%, 61%);' ). lo_buf->add( ' border-color: hsl(19, 100%, 50%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-brown {' ). lo_buf->add( ' color: hsl(33, 100%, 89%);' ). lo_buf->add( ' background-color: hsl(33, 66%, 39%);' ). lo_buf->add( ' border-color: hsl(33, 66%, 30%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-pink {' ). lo_buf->add( ' color: hsl(340, 35%, 45%);' ). lo_buf->add( ' background-color: hsl(340, 85%, 77%);' ). lo_buf->add( ' border-color: hsl(340, 85%, 65%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-teal {' ). lo_buf->add( ' color: hsl(0, 0%, 100%);' ). lo_buf->add( ' background-color: hsl(191, 61%, 45%);' ). lo_buf->add( ' border-color: hsl(191, 61%, 37%);' ). lo_buf->add( '}' ). lo_buf->add( '.rl-darkviolet {' ). lo_buf->add( ' color: hsl(0, 0%, 100%);' ). lo_buf->add( ' background-color: hsl(258, 100%, 80%);' ). lo_buf->add( ' border-color: hsl(258, 100%, 72%);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* FORM FIELD HELP TOOLTIP */' ). lo_buf->add( '' ). lo_buf->add( '.form-field-help-tooltip {' ). lo_buf->add( ' position: relative;' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( '}' ). lo_buf->add( '.form-field-help-tooltip .form-field-help-tooltip-text {' ). lo_buf->add( ' visibility: hidden;' ). lo_buf->add( ' width: 40ch;' ). lo_buf->add( ' border-radius: 5px;' ). lo_buf->add( ' position: absolute;' ). lo_buf->add( ' z-index: 1;' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( ' padding: 0.4em 0.6em;' ). lo_buf->add( ' text-align: justify;' ). lo_buf->add( '}' ). lo_buf->add( '.form-field-help-tooltip .form-field-help-tooltip-text p {' ). lo_buf->add( ' margin: 0px;' ). lo_buf->add( '}' ). lo_buf->add( '.form-field-help-tooltip .form-field-help-tooltip-text {' ). lo_buf->add( ' background-color: white;' ). lo_buf->add( ' border-color: #888;' ). lo_buf->add( '}' ). lo_buf->add( '.form-field-help-tooltip:hover .form-field-help-tooltip-text {' ). lo_buf->add( ' visibility: visible;' ). lo_buf->add( '}' ). lo_buf->add( '.form-field-help-tooltip code {' ). lo_buf->add( ' border-radius: 5px;' ). lo_buf->add( ' font-size: 90%;' ). lo_buf->add( ' padding: 0.1em 0.4em;' ). lo_buf->add( ' background-color: #e2e2e2;' ). lo_buf->add( ' word-wrap: break-word;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* TABLE COMPONENT DEFAULT STYLE */' ). lo_buf->add( '' ). lo_buf->add( 'div.default-table-container {' ). lo_buf->add( ' padding: 6px 0.5em;' ). lo_buf->add( ' background-color: white;' ). lo_buf->add( ' border-radius: 6px;' ). lo_buf->add( ' overflow-x: hidden;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'table.default-table {' ). lo_buf->add( ' line-height: 1.5;' ). lo_buf->add( ' width: 100%;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'table.default-table thead tr:last-child {' ). lo_buf->add( ' border-bottom: #efefef solid 1px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'table.default-table td,' ). lo_buf->add( 'table.default-table th {' ). lo_buf->add( ' padding: 6px 8px;' ). lo_buf->add( ' text-align: left;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'table.default-table th {' ). lo_buf->add( ' white-space: nowrap;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'table.default-table th span.sort-arrow {' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( ' color: hsla(0, 0%, 0%, 0.15);' ). lo_buf->add( ' width: 1em; /*for constant width*/' ). lo_buf->add( ' text-align: right;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'table.default-table th span.sort-active {' ). lo_buf->add( ' color: #4078c0;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'table.default-table th {' ). lo_buf->add( ' padding-bottom: 10px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* CODE INSPECTOR */' ). lo_buf->add( '' ). lo_buf->add( 'div.ci {' ). lo_buf->add( ' margin-top: 1px;' ). lo_buf->add( ' margin-bottom: 1px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.ci-msg {' ). lo_buf->add( ' padding: 0.7em 1em 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( 'div.ci-msg span.ci-variant {' ). lo_buf->add( ' font-weight: bold;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.ci-stats {' ). lo_buf->add( ' padding: 0.5em 1em;' ). lo_buf->add( '}' ). lo_buf->add( 'div.ci-stats span.count {' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( ' padding: 2px 6px;' ). lo_buf->add( ' border-radius: 3px;' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.ci-detail {' ). lo_buf->add( ' padding: 6px 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.ci-detail table td,' ). lo_buf->add( 'div.ci-detail table th {' ). lo_buf->add( ' vertical-align: text-top;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.ci-detail table th[data-cid="kind"],' ). lo_buf->add( 'div.ci-detail table th[data-cid="obj_type"] {' ). lo_buf->add( ' white-space: nowrap' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.ci-detail table td[data-cid="kind"] span {' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( ' border-radius: 3px;' ). lo_buf->add( ' width: 1em;' ). lo_buf->add( ' height: 1em;' ). lo_buf->add( ' vertical-align: inherit;' ). lo_buf->add( ' color: transparent;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.ci-detail table td[data-cid="text"] {' ). lo_buf->add( ' font-size: smaller;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* FLOATING BUTTONS */' ). lo_buf->add( '' ). lo_buf->add( '.floating-button {' ). lo_buf->add( ' position: fixed;' ). lo_buf->add( ' top: 8em;' ). lo_buf->add( ' right: 2.8em;' ). lo_buf->add( ' padding: 1em 1.8em;' ). lo_buf->add( ' border-radius: 4px;' ). lo_buf->add( ' border-width: 1px;' ). lo_buf->add( ' border-style: solid;' ). lo_buf->add( ' box-shadow: 2px 2px 6px 0px #ccc;' ). lo_buf->add( ' cursor: pointer;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* COMMAND PALETTE */' ). lo_buf->add( '' ). lo_buf->add( '.cmd-palette {' ). lo_buf->add( ' position: absolute;' ). lo_buf->add( ' z-index: 99;' ). lo_buf->add( ' top: 36px;' ). lo_buf->add( ' left: 50%;' ). lo_buf->add( ' width: 40em;' ). lo_buf->add( ' margin-left: -20em;' ). lo_buf->add( ' box-shadow: 1px 1px 3px 2px #dcdcdc;' ). lo_buf->add( ' background-color: white;' ). lo_buf->add( ' border: solid 2px;' ). lo_buf->add( ' padding: 0px 1px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.cmd-palette input {' ). lo_buf->add( ' width: 100%;' ). lo_buf->add( ' box-sizing: border-box;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.cmd-palette ul {' ). lo_buf->add( ' max-height: 10em;' ). lo_buf->add( ' overflow-y: scroll;' ). lo_buf->add( ' margin: 4px 0;' ). lo_buf->add( ' padding: 2px 4px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.cmd-palette li {' ). lo_buf->add( ' list-style-type: none;' ). lo_buf->add( ' cursor: default;' ). lo_buf->add( ' padding: 4px 6px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.cmd-palette li .icon {' ). lo_buf->add( ' margin-right: 10px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.cmd-palette li .icon:before {' ). lo_buf->add( ' width: 1.1em;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* SETTINGS */' ). lo_buf->add( '' ). lo_buf->add( 'table.settings_tab {' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( ' max-width: 600px;' ). lo_buf->add( ' line-height: 1.5;' ). lo_buf->add( '}' ). lo_buf->add( 'table.settings_tab th {' ). lo_buf->add( ' text-align: left;' ). lo_buf->add( ' padding: 0.5em;' ). lo_buf->add( ' border-bottom: 1px solid;' ). lo_buf->add( '}' ). lo_buf->add( 'table.settings_tab td {' ). lo_buf->add( ' text-align: left;' ). lo_buf->add( ' padding: 0.3em 0.5em;' ). lo_buf->add( ' border-top: 1px solid;' ). lo_buf->add( '}' ). lo_buf->add( 'table.settings_tab input {' ). lo_buf->add( ' border: none;' ). lo_buf->add( ' text-align: center;' ). lo_buf->add( '}' ). lo_buf->add( 'settings_tab tr:first-child td { border-top: 0px; }' ). lo_buf->add( '' ). lo_buf->add( '/* UNIT TESTS */' ). lo_buf->add( '' ). lo_buf->add( 'table.unit_tests {' ). lo_buf->add( ' line-height: 1.5;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* DIALOGS */' ). lo_buf->add( '' ). lo_buf->add( '.dialog {' ). lo_buf->add( ' margin: 0 auto;' ). lo_buf->add( ' margin-top: 1em;' ). lo_buf->add( ' margin-bottom: 1em;' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( ' padding: 1em 1em;' ). lo_buf->add( ' border-radius: 6px;' ). lo_buf->add( ' text-align: left;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog-form {' ). lo_buf->add( ' width: 600px;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog-form-center {' ). lo_buf->add( ' margin: 1em auto 1em;' ). lo_buf->add( ' max-width: 600px;' ). lo_buf->add( ' width: 100%;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog ul {' ). lo_buf->add( ' padding: 0;' ). lo_buf->add( ' margin: 0;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li {' ). lo_buf->add( ' padding: 5px 10px;' ). lo_buf->add( ' display: block;' ). lo_buf->add( ' list-style: none;' ). lo_buf->add( ' position: relative;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.dialog-commands {' ). lo_buf->add( ' text-align: right;' ). lo_buf->add( ' margin-top: 12px;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.dialog-commands a {' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( ' cursor: pointer;' ). lo_buf->add( ' text-decoration: none;' ). lo_buf->add( ' padding: 6px 12px;' ). lo_buf->add( ' border-radius: 3px;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.dialog-commands input[type="button"],' ). lo_buf->add( '.dialog li.dialog-commands input[type="submit"] {' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( ' padding: 6px 12px;' ). lo_buf->add( ' border-radius: 3px;' ). lo_buf->add( ' cursor: pointer;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.dialog-commands a.main,' ). lo_buf->add( '.dialog li.dialog-commands input[type="submit"].main {' ). lo_buf->add( ' border: 1px solid transparent;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog label {' ). lo_buf->add( ' display: block;' ). lo_buf->add( ' font-size: 90%;' ). lo_buf->add( ' margin-top: 6px;' ). lo_buf->add( ' margin-bottom: 6px;' ). lo_buf->add( ' padding-left: 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.error small {' ). lo_buf->add( ' display: block;' ). lo_buf->add( ' font-size: 75%;' ). lo_buf->add( ' margin: 4px 0px;' ). lo_buf->add( ' padding-left: 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.hidden {' ). lo_buf->add( ' padding: 0px 0px;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog .radio-container {' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( ' padding: 4px;' ). lo_buf->add( ' border-radius: 3px;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog input[type="checkbox"] + label {' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog input[type="password"],' ). lo_buf->add( '.dialog input[type="text"] {' ). lo_buf->add( ' width: 100%;' ). lo_buf->add( ' box-sizing: border-box;' ). lo_buf->add( ' height: 2.5em;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog input[type="number"] {' ). lo_buf->add( ' width: 25%;' ). lo_buf->add( ' box-sizing: border-box;' ). lo_buf->add( ' height: 2.5em;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog textarea {' ). lo_buf->add( ' width: 100%;' ). lo_buf->add( ' box-sizing: border-box;' ). lo_buf->add( ' padding: 10px;' ). lo_buf->add( ' font-family: Arial,Helvetica,sans-serif;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog .radio-container input[type="radio"] {' ). lo_buf->add( ' visibility: hidden;' ). lo_buf->add( ' display: none;' ). lo_buf->add( ' height: 0px;' ). lo_buf->add( ' width: 0px;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog .radio-container input[type="radio"] + label {' ). lo_buf->add( ' border: 1px solid transparent;' ). lo_buf->add( ' cursor: pointer;' ). lo_buf->add( ' width: auto;' ). lo_buf->add( ' margin: 0px;' ). lo_buf->add( ' padding: 3px 8px;' ). lo_buf->add( ' border-radius: 2px;' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog .radio-container input[type="radio"]:checked + label {' ). lo_buf->add( ' border: 1px solid transparent;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog table {' ). lo_buf->add( ' width: 100%;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog table thead td {' ). lo_buf->add( ' font-size: 14px;' ). lo_buf->add( ' height: 2.5em;' ). lo_buf->add( ' background-color: #ddd;' ). lo_buf->add( ' border: 1px solid #ccc;' ). lo_buf->add( ' padding: 0px 10px;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog table tbody td {' ). lo_buf->add( ' border: 1px solid #ccc;' ). lo_buf->add( ' background-color: white;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog table td input {' ). lo_buf->add( ' border: 0px;' ). lo_buf->add( ' background: none;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.with-command div.input-container {' ). lo_buf->add( ' display: table-cell;' ). lo_buf->add( ' width: 100%;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.with-command div.command-container {' ). lo_buf->add( ' display: table-cell;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.with-command input[type="button"],' ). lo_buf->add( '.dialog li.with-command input[type="submit"] {' ). lo_buf->add( ' height: 2.5em;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog fieldset {' ). lo_buf->add( ' margin-top: 1em;' ). lo_buf->add( ' border: 1px solid;' ). lo_buf->add( ' border-right: none;' ). lo_buf->add( ' border-left: none;' ). lo_buf->add( ' border-bottom: none;' ). lo_buf->add( ' border-radius: 6px; /* does not work in IE ? */' ). lo_buf->add( ' padding-bottom: 1em;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog fieldset:first-child {' ). lo_buf->add( ' margin-top: 0;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog fieldset legend {' ). lo_buf->add( ' font-size: large;' ). lo_buf->add( ' font-weight: bold;' ). lo_buf->add( ' padding-left: 0.5em;' ). lo_buf->add( ' padding-right: 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog .dialog-help {' ). lo_buf->add( ' float: left;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* STICKY HEADERS */' ). lo_buf->add( '' ). lo_buf->add( '/* https://www.w3schools.com/howto/howto_js_navbar_sticky.asp */' ). lo_buf->add( '/* Note: We have to use JS since IE does not support CSS position:sticky */' ). lo_buf->add( '' ). lo_buf->add( '/* The sticky class is added to the navbar with JS when it reaches its scroll position */' ). lo_buf->add( '.sticky {' ). lo_buf->add( ' position: fixed;' ). lo_buf->add( ' top: 0;' ). lo_buf->add( ' z-index: 10;' ). lo_buf->add( ' width: 100%;' ). lo_buf->add( ' padding: 0.5em;' ). lo_buf->add( ' margin-bottom: 3px;' ). lo_buf->add( ' max-height: 47px;' ). lo_buf->add( ' max-width: 1265px; /* if set to 1280px, then actual width will be 1296px (strange) */' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.sticky_full_width {' ). lo_buf->add( ' position: fixed;' ). lo_buf->add( ' top: 0;' ). lo_buf->add( ' z-index: 10;' ). lo_buf->add( ' width: 100%;' ). lo_buf->add( ' padding: 0.5em 0.5em;' ). lo_buf->add( ' margin-bottom: 3px;' ). lo_buf->add( ' max-height: 47px;' ). lo_buf->add( '}' ). lo_buf->add( '.sticky_full_width .nav-container {' ). lo_buf->add( ' margin-right: 18px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* Add some top padding to the page content to prevent sudden quick movement' ). lo_buf->add( ' as the navigation bar gets a new position at the top of the page */' ). lo_buf->add( '.sticky + .not_sticky {' ). lo_buf->add( ' padding-top: 50px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* Give more padding if browser control warning is present */' ). lo_buf->add( '.sticky:has(.browser-control-warning) + .not_sticky,' ). lo_buf->add( '.sticky_full_width:has(.browser-control-warning) + .not_sticky {' ). lo_buf->add( ' padding-top: 75px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.sticky_full_width + .not_sticky {' ). lo_buf->add( ' padding-top: 50px;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* Light toolbar or blocks with separators */' ). lo_buf->add( '.toolbar-light a {' ). lo_buf->add( ' padding-left: 0.5em;' ). lo_buf->add( ' padding-right: 0.5em;' ). lo_buf->add( ' border-left: 1px solid;' ). lo_buf->add( ' border-left-color: #ccc;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.toolbar-light a:first-child {' ). lo_buf->add( ' padding-left: 0;' ). lo_buf->add( ' border-left: none;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* Warning if wrong browser control is used */' ). lo_buf->add( '.browser-control-warning {' ). lo_buf->add( ' width: 100%;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* MODAL POPUP */' ). lo_buf->add( '/* https://css-tricks.com/considerations-styling-modal/ */' ). lo_buf->add( '' ). lo_buf->add( '.modal {' ). lo_buf->add( ' /* center on screen */' ). lo_buf->add( ' position: fixed;' ). lo_buf->add( ' top: 50%;' ). lo_buf->add( ' left: 50%;' ). lo_buf->add( ' transform: translate(-50%, -50%);' ). lo_buf->add( ' /* size */' ). lo_buf->add( ' max-width: 100%;' ). lo_buf->add( ' max-height: 100%;' ). lo_buf->add( ' /* infront of overlay */' ). lo_buf->add( ' z-index: 1010;' ). lo_buf->add( ' display: block;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.modal-guts {' ). lo_buf->add( ' padding: 6px 6px;' ). lo_buf->add( ' /* let it scroll */' ). lo_buf->add( ' overflow: auto;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.modal-guts .dialog {' ). lo_buf->add( ' box-shadow: 2px 2px 4px 1px rgba(0,0,0,0.3);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.modal-overlay {' ). lo_buf->add( ' /* darken and prevent interactions with background */' ). lo_buf->add( ' z-index: 1000;' ). lo_buf->add( ' position: fixed;' ). lo_buf->add( ' top: 0;' ). lo_buf->add( ' left: 0;' ). lo_buf->add( ' width: 100%;' ). lo_buf->add( ' height: 100%;' ). lo_buf->add( ' background: rgba(0, 0, 0, 0.3);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.modal .radio-container label {' ). lo_buf->add( ' /* hacky, improve later, get rid of !important, hook it to a named style instead */' ). lo_buf->add( ' border-radius: 3px !important;' ). lo_buf->add( ' border: 1px solid rgba(0, 0, 0, 0.3) !important;' ). lo_buf->add( ' margin-bottom: 2px !important;' ). lo_buf->add( '}' ). lo_buf->add( '.modal .radio-container label:hover {' ). lo_buf->add( ' background-color: rgba(0, 0, 0, 0.1);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* WHERE USED PAGE */' ). lo_buf->add( '' ). lo_buf->add( 'div.wu-header {' ). lo_buf->add( ' padding: 8px 0.5em;' ). lo_buf->add( ' margin: 0px 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( 'div.wu {' ). lo_buf->add( ' padding: 8px 0.5em;' ). lo_buf->add( '}' ). lo_buf->add( 'div.wu table thead tr:first-child {' ). lo_buf->add( ' /* TODO: maybe move this to default table style */' ). lo_buf->add( ' color: hsl(0, 0%, 80%);' ). lo_buf->add( ' text-transform: uppercase;' ). lo_buf->add( ' font-size: 90%;' ). lo_buf->add( '}' ). lo_buf->add( 'div.wu table tbody {' ). lo_buf->add( ' font-size: 90%;' ). lo_buf->add( ' vertical-align: baseline; /* for second lines, used type */' ). lo_buf->add( '}' ). lo_buf->add( 'div.wu table td[data-cid="dep_obj_name"] span.used-obj {' ). lo_buf->add( ' display: block;' ). lo_buf->add( ' color: hsl(0, 0%, 70%);' ). lo_buf->add( ' font-size: smaller;' ). lo_buf->add( ' text-transform: lowercase;' ). lo_buf->add( '}' ). lo_buf->add( 'div.wu table td[data-gid="where"],' ). lo_buf->add( 'div.wu table th[data-gid="where"] {' ). lo_buf->add( ' background-color: hsl(0, 0%, 97%);' ). lo_buf->add( '}' ). li_asset_man->register_asset( iv_url = 'css/common.css' iv_type = 'text/css' iv_mime_name = 'ZABAPGIT_CSS_COMMON' iv_inline = lo_buf->join_w_newline_and_flush( ) ). **************************************************** * abapmerge Pragma - ZABAPGIT_CSS_THEME_DEFAULT **************************************************** lo_buf->add( '/*' ). lo_buf->add( ' * ABAPGIT COLOR THEME CSS - DEFAULT' ). lo_buf->add( ' */' ). lo_buf->add( '' ). lo_buf->add( ':root {' ). lo_buf->add( ' --theme-background-color: #E8E8E8;' ). lo_buf->add( ' --theme-container-background-color: #f2f2f2;' ). lo_buf->add( ' --theme-container-border-color: lightgrey;' ). lo_buf->add( ' --theme-table-background-color: white;' ). lo_buf->add( ' --theme-table-head-background-color: #edf2f9;' ). lo_buf->add( ' --theme-table-border-color: #ddd;' ). lo_buf->add( ' --theme-table-cell-border-color: #eee;' ). lo_buf->add( '' ). lo_buf->add( ' --theme-primary-font: "72", Arial, Helvetica, sans-serif;' ). lo_buf->add( ' --theme-primary-font-color: #333333;' ). lo_buf->add( ' --theme-primary-font-color-reduced: #ccc;' ). lo_buf->add( ' --theme-font-size: 12pt;' ). lo_buf->add( ' --theme-link-color: #4078c0;' ). lo_buf->add( '' ). lo_buf->add( ' --theme-greyscale-dark: #808080;' ). lo_buf->add( ' --theme-greyscale-medium: #b3b3b3;' ). lo_buf->add( ' --theme-greyscale-light: #ccc;' ). lo_buf->add( ' --theme-greyscale-lighter: lightgrey;' ). lo_buf->add( ' --theme-linkhint-background: lightgreen;' ). lo_buf->add( ' --theme-debug-color: #aaa;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* GLOBALS */' ). lo_buf->add( '' ). lo_buf->add( 'body {' ). lo_buf->add( ' background-color: var(--theme-background-color);' ). lo_buf->add( ' font-family: var(--theme-primary-font);' ). lo_buf->add( ' color: var(--theme-primary-font-color);' ). lo_buf->add( ' font-size: var(--theme-font-size);' ). lo_buf->add( '}' ). lo_buf->add( 'a, a:visited { color: var(--theme-link-color); }' ). lo_buf->add( '.link {' ). lo_buf->add( ' color: var(--theme-link-color);' ). lo_buf->add( ' cursor: pointer;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'input, textarea, select { border-color: #ddd; }' ). lo_buf->add( 'input:focus, textarea:focus { border-color: #8cadd9; }' ). lo_buf->add( '' ). lo_buf->add( '/* COLOR PALETTE */' ). lo_buf->add( '' ). lo_buf->add( '.grey { color: var(--theme-greyscale-lighter) !important; }' ). lo_buf->add( '.grey70 { color: var(--theme-greyscale-medium) !important; }' ). lo_buf->add( '.grey80 { color: var(--theme-greyscale-light) !important; }' ). lo_buf->add( '.darkgrey { color: var(--theme-greyscale-dark) !important; }' ). lo_buf->add( '.bgorange { background-color: orange; }' ). lo_buf->add( '.attention { color: red !important; }' ). lo_buf->add( '.error { color: #d41919 !important; }' ). lo_buf->add( '.warning { color: #efb301 !important; }' ). lo_buf->add( '.success { color: green !important; }' ). lo_buf->add( '.blue { color: #5e8dc9 !important; }' ). lo_buf->add( '.red { color: red !important; }' ). lo_buf->add( '.white { color: white !important; }' ). lo_buf->add( '.pink { color: pink !important; }' ). lo_buf->add( '' ). lo_buf->add( '/* FLOATING BUTTONS AND COLOR SETS */' ). lo_buf->add( '' ). lo_buf->add( '.blue-set {' ). lo_buf->add( ' border-color: #abc3e3;' ). lo_buf->add( ' color: #5e8dc9;' ). lo_buf->add( ' background-color: #d9e4f2;' ). lo_buf->add( '}' ). lo_buf->add( '.grey-set {' ). lo_buf->add( ' border-color: #c7c7c7;' ). lo_buf->add( ' color: var(--theme-greyscale-dark);' ). lo_buf->add( ' background-color: #e6e6e6;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* ABAPGIT OBJECTS */' ). lo_buf->add( '' ). lo_buf->add( 'span.user-box {' ). lo_buf->add( ' border-color: #c2d4ea;' ). lo_buf->add( ' background-color: #d9e4f2;' ). lo_buf->add( '}' ). lo_buf->add( 'span.package-box {' ). lo_buf->add( ' border-color: #d3ccd2;' ). lo_buf->add( ' background-color: #ebe3ea;' ). lo_buf->add( '}' ). lo_buf->add( 'span.path-box,' ). lo_buf->add( 'span.transport-box {' ). lo_buf->add( ' border-color: #a7e3cf;' ). lo_buf->add( ' background-color: #dbf3eb;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* PANELS */' ). lo_buf->add( '/* TODO: add warning and error colors */' ). lo_buf->add( '' ). lo_buf->add( 'div.panel.success {' ). lo_buf->add( ' color: #589a58 !important;' ). lo_buf->add( ' background-color: #c5eac5;' ). lo_buf->add( '}' ). lo_buf->add( 'div.panel.error {' ). lo_buf->add( ' color: #d41919;' ). lo_buf->add( ' background-color: #fad6d6;' ). lo_buf->add( '}' ). lo_buf->add( '#debug-output { color: var(--theme-debug-color); }' ). lo_buf->add( 'div.dummydiv { background-color: var(--theme-container-background-color); }' ). lo_buf->add( '' ). lo_buf->add( '/* STRUCTURE DIVS, HEADER & FOOTER */' ). lo_buf->add( '' ). lo_buf->add( 'div#header {' ). lo_buf->add( ' background-color: var(--theme-background-color);' ). lo_buf->add( ' border-bottom-color: var(--theme-container-border-color);' ). lo_buf->add( '}' ). lo_buf->add( 'div#header .page-title { color: var(--theme-greyscale-medium); }' ). lo_buf->add( 'div#footer .version { color: var(--theme-greyscale-medium); }' ). lo_buf->add( 'div#footer { border-top-color: var(--theme-container-border-color); }' ). lo_buf->add( '' ). lo_buf->add( '/* ERROR LOG */' ). lo_buf->add( '' ). lo_buf->add( 'div.log {' ). lo_buf->add( ' background-color: #fee6e6;' ). lo_buf->add( ' border-color: #fdcece;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* REPOSITORY */' ). lo_buf->add( '' ). lo_buf->add( 'div.repo { background-color: var(--theme-container-background-color); }' ). lo_buf->add( '.repo_name span.name { color: #333; }' ). lo_buf->add( '.repo_name span.url { color: var(--theme-primary-font-color-reduced); }' ). lo_buf->add( '.repo_name a.url { color: var(--theme-primary-font-color-reduced); }' ). lo_buf->add( '.repo_name a.url:hover { color: var(--theme-link-color); }' ). lo_buf->add( '.repo_attr { color: grey; }' ). lo_buf->add( '' ). lo_buf->add( '.repo_attr span.bg_marker {' ). lo_buf->add( ' border-color: #d2d2d2;' ). lo_buf->add( ' background-color: #d8d8d8;' ). lo_buf->add( ' color: #fff;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.repo_attr span.branch_head {' ). lo_buf->add( ' border-color: #d8dff3;' ). lo_buf->add( ' background-color: #eceff9;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'span.branch {' ). lo_buf->add( ' border-color: #d9d9d9;' ). lo_buf->add( ' background-color: #e2e2e2;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'span.branch_branch {' ). lo_buf->add( ' border-color: #e7d9b1;' ). lo_buf->add( ' background-color: #f8f0d8;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* REPOSITORY TABLE*/' ). lo_buf->add( '' ). lo_buf->add( 'table.repo_tab {' ). lo_buf->add( ' border-color: var(--theme-table-border-color);' ). lo_buf->add( ' background-color: var(--theme-table-background-color);' ). lo_buf->add( '}' ). lo_buf->add( '.repo_tab th {' ). lo_buf->add( ' color: var(--theme-link-color);' ). lo_buf->add( ' background-color: #edf2f9;' ). lo_buf->add( ' border-bottom-color: var(--theme-table-border-color);' ). lo_buf->add( '}' ). lo_buf->add( '.repo_tab td {' ). lo_buf->add( ' color: var(--theme-primary-font-color);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.repo_tab tr.object_row{' ). lo_buf->add( ' border-top-color: var(--theme-table-cell-border-color);' ). lo_buf->add( '}' ). lo_buf->add( '.repo_tab .inactive { color: orange; }' ). lo_buf->add( '.repo_tab tr.unsupported { color: var(--theme-greyscale-lighter); }' ). lo_buf->add( '.repo_tab tr.modified { background-color: #fbf7e9; }' ). lo_buf->add( '.repo_tab td.current_dir { color: var(--theme-primary-font-color-reduced); }' ). lo_buf->add( '' ). lo_buf->add( '/* STAGE */' ). lo_buf->add( '' ). lo_buf->add( '.stage_tab {' ). lo_buf->add( ' border-color: #ddd;' ). lo_buf->add( ' background-color: #fff;' ). lo_buf->add( '}' ). lo_buf->add( '.stage_tab th {' ). lo_buf->add( ' color: var(--theme-greyscale-dark);' ). lo_buf->add( ' background-color: #edf2f9;' ). lo_buf->add( ' border-bottom-color: #ddd;' ). lo_buf->add( '}' ). lo_buf->add( '.stage_tab td {' ). lo_buf->add( ' color: var(--theme-greyscale-medium);' ). lo_buf->add( ' border-top-color: var(--theme-table-cell-border-color);' ). lo_buf->add( '}' ). lo_buf->add( '.stage_tab td.status {' ). lo_buf->add( ' color: var(--theme-primary-font-color-reduced);' ). lo_buf->add( ' background-color: #fafafa;' ). lo_buf->add( '}' ). lo_buf->add( '.stage_tab td.highlight { color: #444 !important; }' ). lo_buf->add( '.stage_tab td.method { font-weight: bold; }' ). lo_buf->add( '.stage_tab mark {' ). lo_buf->add( ' color: white;' ). lo_buf->add( ' background-color: #79a0d2;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* COMMIT */' ). lo_buf->add( '' ). lo_buf->add( 'div.form-container { background-color: #F8F8F8; }' ). lo_buf->add( 'form.aligned-form label { color: var(--theme-greyscale-medium); }' ). lo_buf->add( 'form.aligned-form span.sub-title { color: var(--theme-greyscale-medium); }' ). lo_buf->add( '' ). lo_buf->add( '/* SETTINGS STYLES */' ). lo_buf->add( '' ). lo_buf->add( 'div.settings_container {' ). lo_buf->add( ' color: #444;' ). lo_buf->add( ' background-color: var(--theme-container-background-color);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* DIFF */' ). lo_buf->add( '' ). lo_buf->add( 'div.diff { background-color: var(--theme-container-background-color); }' ). lo_buf->add( 'span.diff_name { color: grey; }' ). lo_buf->add( 'span.diff_name strong { color: #333; }' ). lo_buf->add( 'span.diff_changed_by { color: grey; }' ). lo_buf->add( 'span.diff_changed_by span.user {' ). lo_buf->add( ' border-color: #c2d4ea;' ). lo_buf->add( ' background-color: #d9e4f2;' ). lo_buf->add( '}' ). lo_buf->add( '.diff_ins {' ). lo_buf->add( ' border-color: #abf2ab;' ). lo_buf->add( ' background-color: #e0ffe0;' ). lo_buf->add( '}' ). lo_buf->add( '.diff_del {' ). lo_buf->add( ' border-color: #ff667d;' ). lo_buf->add( ' background-color: #ffccd4;' ). lo_buf->add( '}' ). lo_buf->add( '.diff_upd {' ). lo_buf->add( ' border-color: #dada00;' ). lo_buf->add( ' background-color: #ffffcc;' ). lo_buf->add( '}' ). lo_buf->add( 'div.diff_content {' ). lo_buf->add( ' background-color: #fff;' ). lo_buf->add( ' border-top-color: #ddd;' ). lo_buf->add( ' border-bottom-color: #ddd;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* STATE BLOCK COLORS */' ). lo_buf->add( '' ). lo_buf->add( 'span.state-block span.added {' ). lo_buf->add( ' background-color: #69ad74;' ). lo_buf->add( ' border-color: #579e64;' ). lo_buf->add( ' color: white;' ). lo_buf->add( '}' ). lo_buf->add( 'span.state-block span.changed {' ). lo_buf->add( ' background-color: #e0c150;' ). lo_buf->add( ' border-color: #d4af25;' ). lo_buf->add( ' color: white;' ). lo_buf->add( '}' ). lo_buf->add( 'span.state-block span.mixed {' ). lo_buf->add( ' background-color: #e0c150;' ). lo_buf->add( ' border-color: #579e64;' ). lo_buf->add( ' color: #69ad74;' ). lo_buf->add( '}' ). lo_buf->add( 'span.state-block span.deleted {' ). lo_buf->add( ' background-color: #c76861;' ). lo_buf->add( ' border-color: #b8605a;' ). lo_buf->add( ' color: white;' ). lo_buf->add( '}' ). lo_buf->add( 'span.state-block span.none {' ). lo_buf->add( ' background-color: #e8e8e8;' ). lo_buf->add( ' border-color: #dbdbdb;' ). lo_buf->add( ' color: #c8c8c8;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* DIFF TABLE */' ). lo_buf->add( '' ). lo_buf->add( 'table.diff_tab td,' ). lo_buf->add( 'table.diff_tab th {' ). lo_buf->add( ' color: #444;' ). lo_buf->add( '}' ). lo_buf->add( 'table.diff_tab thead.header th {' ). lo_buf->add( ' color: #eee;' ). lo_buf->add( ' background-color: var(--theme-greyscale-medium);' ). lo_buf->add( '}' ). lo_buf->add( 'table.diff_tab thead.nav_line {' ). lo_buf->add( ' background-color: #edf2f9;' ). lo_buf->add( '}' ). lo_buf->add( 'table.diff_tab thead.nav_line th {' ). lo_buf->add( ' color: var(--theme-greyscale-medium);' ). lo_buf->add( '}' ). lo_buf->add( 'table.diff_tab td.num, th.num {' ). lo_buf->add( ' color: var(--theme-primary-font-color-reduced);' ). lo_buf->add( ' border-left-color: var(--theme-table-cell-border-color);' ). lo_buf->add( ' border-right-color: var(--theme-table-cell-border-color);' ). lo_buf->add( '}' ). lo_buf->add( 'table.diff_tab td.patch, th.patch {' ). lo_buf->add( ' color: var(--theme-primary-font-color-reduced);' ). lo_buf->add( ' border-left-color: var(--theme-table-cell-border-color);' ). lo_buf->add( ' border-right-color: var(--theme-table-cell-border-color);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* STYLES FOR SYNTAX HIGHLIGHTING */' ). lo_buf->add( '' ). lo_buf->add( '/* ABAP */' ). lo_buf->add( '.syntax-hl span.keyword { color: #0a69ce; }' ). lo_buf->add( '.syntax-hl span.text { color: #48ce4f; }' ). lo_buf->add( '.syntax-hl span.comment { color: var(--theme-greyscale-dark); font-style: italic; }' ). lo_buf->add( '/* XML+HTML */' ). lo_buf->add( '.syntax-hl span.xml_tag { color: #457ce3; }' ). lo_buf->add( '.syntax-hl span.attr { color: #b777fb; }' ). lo_buf->add( '.syntax-hl span.attr_val { color: #7a02f9; }' ). lo_buf->add( '/* CSS+JS */' ). lo_buf->add( '.syntax-hl span.properties { color:#0a69ce; }' ). lo_buf->add( '.syntax-hl span.values { color:blue; }' ). lo_buf->add( '.syntax-hl span.units { color:maroon; }' ). lo_buf->add( '.syntax-hl span.selectors { color:purple; }' ). lo_buf->add( '.syntax-hl span.functions { color:purple; }' ). lo_buf->add( '.syntax-hl span.colors { color:purple; }' ). lo_buf->add( '.syntax-hl span.extensions { color:darkblue; }' ). lo_buf->add( '.syntax-hl span.at_rules { color:darkblue; }' ). lo_buf->add( '.syntax-hl span.html { color:green; }' ). lo_buf->add( '.syntax-hl span.variables { color:purple; }' ). lo_buf->add( '' ). lo_buf->add( '/* DEBUG INFO STYLES */' ). lo_buf->add( '' ). lo_buf->add( 'div.debug_container {' ). lo_buf->add( ' color: #444;' ). lo_buf->add( ' background-color: var(--theme-container-background-color);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* Repo overview */' ). lo_buf->add( '' ). lo_buf->add( '.repo-overview { background-color: var(--theme-container-background-color); }' ). lo_buf->add( '.repo-overview table {' ). lo_buf->add( ' background-color: var(--theme-table-background-color);' ). lo_buf->add( ' border-color: var(--theme-table-border-color);' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview th {' ). lo_buf->add( ' color: var(--theme-link-color);' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview thead tr {' ). lo_buf->add( ' background-color: var(--theme-table-head-background-color);' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview thead tr,' ). lo_buf->add( '.repo-overview tfoot tr {' ). lo_buf->add( ' border-color: var(--theme-table-border-color);' ). lo_buf->add( '}' ). lo_buf->add( '.repo-overview a.remote_repo { color: var(--theme-primary-font-color-reduced); }' ). lo_buf->add( '.repo-overview a.remote_repo:hover { color: var(--theme-link-color); }' ). lo_buf->add( '.repo-overview tbody tr:hover td { background-color: hsla(214, 50%, 50%, 0.05); }' ). lo_buf->add( '.repo-overview tbody tr.selected { background-color: hsla(214, 50%, 75%, 0.33); }' ). lo_buf->add( '' ). lo_buf->add( '/* TUTORIAL */' ). lo_buf->add( '' ). lo_buf->add( 'div.tutorial { background-color: var(--theme-container-background-color); }' ). lo_buf->add( 'div.tutorial hr { border-color: var(--theme-greyscale-light); }' ). lo_buf->add( 'div.tutorial h1, h2 { color: #404040; }' ). lo_buf->add( '' ). lo_buf->add( '/* MENU */' ). lo_buf->add( '' ). lo_buf->add( '.nav-container ul li:hover { background-color: #fff; }' ). lo_buf->add( '.nav-container ul ul li:hover { background-color: #f6f6f6; }' ). lo_buf->add( '.nav-container > ul > li:hover > a { background-color: #ffffff80; }' ). lo_buf->add( '.nav-container ul ul { background-color: #fff; }' ). lo_buf->add( '.nav-container.corner > ul > li:hover > a { background-color: inherit; }' ). lo_buf->add( '' ). lo_buf->add( '.nav-container ul ul li.separator {' ). lo_buf->add( ' color: var(--theme-greyscale-medium);' ). lo_buf->add( ' border-bottom-color: #eee;' ). lo_buf->add( ' border-top-color: #eee;' ). lo_buf->add( '}' ). lo_buf->add( '.nav-container ul ul li.separator:hover { background-color: inherit; }' ). lo_buf->add( '' ). lo_buf->add( '/* NEWS ANNOUNCEMENT */' ). lo_buf->add( '' ). lo_buf->add( 'div.info-panel { background-color: white; }' ). lo_buf->add( 'div.info-panel div.info-hint { color: var(--theme-greyscale-light); }' ). lo_buf->add( 'div.info-panel div.info-title {' ). lo_buf->add( ' color: #f8f8f8;' ). lo_buf->add( ' background-color: #888;' ). lo_buf->add( '}' ). lo_buf->add( 'div.info-panel div.info-title a.close-btn { color: #d8d8d8; }' ). lo_buf->add( 'div.info-panel div.info-list { color: #444; }' ). lo_buf->add( 'div.info-panel .version-marker {' ). lo_buf->add( ' color: white;' ). lo_buf->add( ' border-color: #c0c0c0;' ). lo_buf->add( ' background-color: var(--theme-greyscale-light);' ). lo_buf->add( '}' ). lo_buf->add( 'div.info-panel .update {' ). lo_buf->add( ' border-color: #e8ba30;' ). lo_buf->add( ' background-color: #f5c538;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* TOOLTIPS TEXT */' ). lo_buf->add( '' ). lo_buf->add( '.link-hint { color: var(--theme-primary-font-color); }' ). lo_buf->add( '.link-hint { background-color: var(--theme-linkhint-background) }' ). lo_buf->add( '.link-hint::after { border-top-color: var(--theme-linkhint-background) }' ). lo_buf->add( '' ). lo_buf->add( '/* HOTKEYS */' ). lo_buf->add( '' ). lo_buf->add( 'ul.hotkeys span.key-id {' ). lo_buf->add( ' background-color: #f0f0f0;' ). lo_buf->add( ' border-color: #dcdcdc;' ). lo_buf->add( '}' ). lo_buf->add( 'div.corner-hint {' ). lo_buf->add( ' color: var(--theme-greyscale-medium);' ). lo_buf->add( ' border-color: var(--theme-greyscale-light);' ). lo_buf->add( ' background-color: #fff;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* CODE INSPECTOR */' ). lo_buf->add( '' ). lo_buf->add( 'div.ci { background-color: var(--theme-container-background-color); }' ). lo_buf->add( '' ). lo_buf->add( 'div.ci-msg span.ci-variant { color: var(--theme-primary-font-color); }' ). lo_buf->add( '' ). lo_buf->add( 'div.ci-stats span.error-count {' ). lo_buf->add( ' border-color: hsl(350, 100%, 80%);' ). lo_buf->add( ' background-color: hsl(350, 100%, 90%);' ). lo_buf->add( '}' ). lo_buf->add( 'div.ci-stats span.warn-count {' ). lo_buf->add( ' border-color: hsl(60, 100%, 42%);' ). lo_buf->add( ' background-color: hsl(60, 100%, 90%);' ). lo_buf->add( '}' ). lo_buf->add( 'div.ci-stats span.info-count {' ). lo_buf->add( ' border-color: hsl(120, 80%, 80%);' ). lo_buf->add( ' background-color: hsl(120, 80%, 94%);' ). lo_buf->add( '}' ). lo_buf->add( 'div.ci-stats span.all-count {' ). lo_buf->add( ' border-color: hsl(235, 100%, 89%);' ). lo_buf->add( ' background-color: hsl(235, 100%, 93%);' ). lo_buf->add( '}' ). lo_buf->add( 'div.ci-detail table td[data-cid="text"] {' ). lo_buf->add( ' color: hsl(0, 0%, 40%);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'div.ci-detail table tr[data-kind="error"] td[data-cid="kind"] span {' ). lo_buf->add( ' background-color: hsl(0, 100%, 68%);' ). lo_buf->add( '}' ). lo_buf->add( 'div.ci-detail table tr[data-kind="warning"] td[data-cid="kind"] span {' ). lo_buf->add( ' background-color:hsl(52, 100%, 49%);' ). lo_buf->add( '}' ). lo_buf->add( 'div.ci-detail table tr[data-kind="info"] td[data-cid="kind"] span {' ). lo_buf->add( ' background-color: hsl(118, 67%, 47%);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* COMMAND PALETTE */' ). lo_buf->add( '' ). lo_buf->add( '.cmd-palette {' ). lo_buf->add( ' border-color: #ccc;' ). lo_buf->add( '}' ). lo_buf->add( '.cmd-palette li.selected {' ). lo_buf->add( ' background-color: hsla(214, 50%, 90%, 1);' ). lo_buf->add( '}' ). lo_buf->add( '.cmd-palette mark {' ). lo_buf->add( ' color: white;' ). lo_buf->add( ' background-color: #79a0d2;' ). lo_buf->add( ' /* todo merge with stage search */' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* SETTINGS */' ). lo_buf->add( '' ). lo_buf->add( 'table.settings_tab {' ). lo_buf->add( ' background-color: #fff;' ). lo_buf->add( ' border-color: #ddd;' ). lo_buf->add( '}' ). lo_buf->add( 'table.settings_tab th {' ). lo_buf->add( ' color: #888888;' ). lo_buf->add( ' border-bottom-color: #ddd;' ). lo_buf->add( '}' ). lo_buf->add( 'table.settings_tab td {' ). lo_buf->add( ' color: #333;' ). lo_buf->add( ' border-top-color: #eee;' ). lo_buf->add( '}' ). lo_buf->add( 'table.settings_tab input {' ). lo_buf->add( ' background-color: #f8f8f8;' ). lo_buf->add( '}' ). lo_buf->add( 'table.settings_tab input:focus {' ). lo_buf->add( ' background-color: #fff;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* HTML FORMS */' ). lo_buf->add( '' ). lo_buf->add( '.dialog input::placeholder { color: #ccc }' ). lo_buf->add( '.dialog textarea::placeholder { color: #ccc }' ). lo_buf->add( '.dialog input:-ms-input-placeholder { color: #ccc }' ). lo_buf->add( '.dialog textarea:-ms-input-placeholder { color: #ccc }' ). lo_buf->add( '.dialog {' ). lo_buf->add( ' border-color: #ccc;' ). lo_buf->add( ' background-color: #f0f0f0;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.dialog-commands a,' ). lo_buf->add( '.dialog li.dialog-commands input[type="submit"] {' ). lo_buf->add( ' border-color: #ccc;' ). lo_buf->add( ' background-color: #ddd;' ). lo_buf->add( ' color: #000;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.dialog-commands a.main,' ). lo_buf->add( '.dialog li.dialog-commands input[type="submit"].main {' ). lo_buf->add( ' background-color: #64a8ff;' ). lo_buf->add( ' color: #fff;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog label {' ). lo_buf->add( ' color: #444;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog label em {' ). lo_buf->add( ' color: #64a8ff;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.error small {' ). lo_buf->add( ' color: #ff5959;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.error input[type="number"],' ). lo_buf->add( '.dialog li.error input[type="text"] {' ). lo_buf->add( ' border-color: #ff5959;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog .radio-container {' ). lo_buf->add( ' border-color: #ddd;' ). lo_buf->add( ' background-color: #fff;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog .radio-container input[type="radio"]:checked + label {' ). lo_buf->add( ' background-color: #64a8ff;' ). lo_buf->add( ' color: #fff;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.with-command input[type="button"]:hover,' ). lo_buf->add( '.dialog li.with-command input[type="submit"]:hover {' ). lo_buf->add( ' background-color: #64a8ff;' ). lo_buf->add( ' color: #fff;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog fieldset {' ). lo_buf->add( ' border-color: #dfdfdf;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog fieldset legend {' ). lo_buf->add( ' color: #444;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog input:read-only {' ). lo_buf->add( ' background-color: #f4f4f4;' ). lo_buf->add( ' color: var(--theme-greyscale-dark);' ). lo_buf->add( '}' ). lo_buf->add( '/* for IE */' ). lo_buf->add( '.dialog input[readonly] {' ). lo_buf->add( ' background-color: #f4f4f4;' ). lo_buf->add( ' color: var(--theme-greyscale-dark);' ). lo_buf->add( '}' ). li_asset_man->register_asset( iv_url = 'css/theme-default.css' iv_type = 'text/css' iv_cacheable = abap_false iv_mime_name = 'ZABAPGIT_CSS_THEME_DEFAULT' iv_inline = lo_buf->join_w_newline_and_flush( ) ). **************************************************** * abapmerge Pragma - ZABAPGIT_CSS_THEME_DARK **************************************************** lo_buf->add( '/*' ). lo_buf->add( ' * ABAPGIT THEME CSS - DARK' ). lo_buf->add( ' */' ). lo_buf->add( '' ). lo_buf->add( '/* https://experience.sap.com/fiori-design-web/colors/ */' ). lo_buf->add( '' ). lo_buf->add( ':root {' ). lo_buf->add( ' --theme-background-color: #333333;' ). lo_buf->add( ' --theme-container-background-color: #444444;' ). lo_buf->add( ' --theme-primary-font: "72", Arial, Helvetica, sans-serif;' ). lo_buf->add( ' --theme-primary-font-color: #cccccc;' ). lo_buf->add( ' --theme-primary-font-color-reduced: #EEEEEE;' ). lo_buf->add( ' --theme-font-size: 11pt;' ). lo_buf->add( ' --theme-link-color: #d9ffff;' ). lo_buf->add( ' --theme-link-color-hover: #f6f6f6;' ). lo_buf->add( ' --theme-container-border-color: #D1E0EE;' ). lo_buf->add( ' --theme-table-border-color: #E5E5E5; /* ALV border color */' ). lo_buf->add( ' --theme-greyscale-dark: #666666;' ). lo_buf->add( ' --theme-greyscale-medium: #999999;' ). lo_buf->add( ' --theme-greyscale-light: #CCCCCC;' ). lo_buf->add( ' --theme-greyscale-lighter: #E5E5E5;' ). lo_buf->add( ' --theme-list-hover-background-color: black;' ). lo_buf->add( '' ). lo_buf->add( ' --theme-table-background-color: #333333;' ). lo_buf->add( ' --theme-table-head-background-color: #202020;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* GLOBALS */' ). lo_buf->add( '' ). lo_buf->add( 'body {' ). lo_buf->add( ' background-color: var(--theme-background-color);' ). lo_buf->add( ' color: var(--theme-primary-font-color);' ). lo_buf->add( '}' ). lo_buf->add( 'select, input, textarea {' ). lo_buf->add( ' color: var(--theme-primary-font-color);' ). lo_buf->add( ' border-color: #ffffff;' ). lo_buf->add( ' background-color: var(--theme-background-color);' ). lo_buf->add( '}' ). lo_buf->add( 'a:hover { color: var(--theme-link-color-hover); }' ). lo_buf->add( '' ). lo_buf->add( '/* HEADER */' ). lo_buf->add( '' ). lo_buf->add( '#header a, #header a:visited { color: var(--theme-link-color); }' ). lo_buf->add( '' ). lo_buf->add( '/* MENU */' ). lo_buf->add( '' ). lo_buf->add( 'div#toc .favorites a { opacity: 1; }' ). lo_buf->add( '.nav-container ul a:hover { text-decoration: underline; }' ). lo_buf->add( '.nav-container ul ul { background-color: #555555; }' ). lo_buf->add( '.nav-container ul li:hover { background-color: #555555; }' ). lo_buf->add( '.nav-container ul ul li:hover { background-color: var(--theme-list-hover-background-color); }' ). lo_buf->add( 'table.repo_tab {' ). lo_buf->add( ' border-color: var(--theme-container-background-color);' ). lo_buf->add( ' background-color: var(--theme-background-color);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* ABAPGIT OBJECTS */' ). lo_buf->add( '' ). lo_buf->add( 'span.user-box {' ). lo_buf->add( ' background-color: #4c6782;' ). lo_buf->add( ' border-color: #7491b2;' ). lo_buf->add( '}' ). lo_buf->add( 'span.package-box {' ). lo_buf->add( ' background-color: #705a6d;' ). lo_buf->add( ' border-color: #987095;' ). lo_buf->add( '}' ). lo_buf->add( 'span.path-box,' ). lo_buf->add( 'span.transport-box {' ). lo_buf->add( ' background-color: #456d5d;' ). lo_buf->add( ' border-color: #60a087;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* PANELS */' ). lo_buf->add( '' ). lo_buf->add( '#debug-output { color: var(--theme-greyscale-dark); }' ). lo_buf->add( '' ). lo_buf->add( '/* abapGit logo in header and footer */' ). lo_buf->add( '.logo .icon.icon-abapgit {' ). lo_buf->add( ' color: var(--theme-primary-font-color);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* TUTORIAL */' ). lo_buf->add( '' ). lo_buf->add( 'div.tutorial h1, h2 { color: var(--theme-primary-font-color); }' ). lo_buf->add( '' ). lo_buf->add( '/* REPOSITORY */' ). lo_buf->add( '' ). lo_buf->add( 'div.repo { background-color: var(--theme-container-background-color); }' ). lo_buf->add( '.repo_name span.name { color: var(--theme-primary-font-color-reduced); }' ). lo_buf->add( '.repo_name span.url { color: var(--theme-greyscale-medium); }' ). lo_buf->add( '.repo_name a.url { color: var(--theme-greyscale-medium); }' ). lo_buf->add( '.repo_attr { color: var(--theme-primary-font-color); }' ). lo_buf->add( '' ). lo_buf->add( 'span.branch_branch {' ). lo_buf->add( ' border-color: var(--theme-greyscale-medium);' ). lo_buf->add( ' background-color: #777777;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* REPOSITORY TABLE */' ). lo_buf->add( '' ). lo_buf->add( '.repo_tab td { color: var(--theme-primary-font-color); }' ). lo_buf->add( '.repo_tab tr.unsupported { background-color: #555; }' ). lo_buf->add( '.repo_tab tr.modified { background-color: #555; }' ). lo_buf->add( '.repo_tab tr:hover {background-color: var(--theme-list-hover-background-color) !important;}' ). lo_buf->add( '' ). lo_buf->add( '.repo_tab th {' ). lo_buf->add( ' border-top-color: var(--theme-greyscale-dark);' ). lo_buf->add( ' background-color: black;' ). lo_buf->add( '}' ). lo_buf->add( '.repo_tab td {' ). lo_buf->add( ' border-top-color: var(--theme-greyscale-dark);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* STAGE */' ). lo_buf->add( '' ). lo_buf->add( '.stage_tab {' ). lo_buf->add( ' border-color: var(--theme-greyscale-dark);' ). lo_buf->add( ' background-color: var(--theme-background-color);' ). lo_buf->add( '}' ). lo_buf->add( '.stage_tab th {' ). lo_buf->add( ' border-top-color: var(--theme-greyscale-dark);' ). lo_buf->add( ' background-color: black;' ). lo_buf->add( '}' ). lo_buf->add( '.stage_tab td {' ). lo_buf->add( ' color: var(--theme-primary-font-color);' ). lo_buf->add( ' border-top-color: var(--theme-greyscale-dark);' ). lo_buf->add( '}' ). lo_buf->add( '.stage_tab td.status.highlight {' ). lo_buf->add( ' color: var(--theme-primary-font-color) !important;' ). lo_buf->add( ' background-color: var(--theme-background-color);' ). lo_buf->add( '}' ). lo_buf->add( '.stage_tab td.status {' ). lo_buf->add( ' color: #777;' ). lo_buf->add( ' background-color: var(--theme-background-color);' ). lo_buf->add( '}' ). lo_buf->add( '.stage_tab th { background-color: var(--theme-container-background-color); }' ). lo_buf->add( '.stage_tab tr:hover {background-color: var(--theme-list-hover-background-color) !important;}' ). lo_buf->add( '' ). lo_buf->add( '/* COMMIT */' ). lo_buf->add( '' ). lo_buf->add( 'div.form-container { background-color: var(--theme-background-color); }' ). lo_buf->add( '' ). lo_buf->add( '/* SETTINGS STYLES */' ). lo_buf->add( '' ). lo_buf->add( 'div.settings_container { color: var(--theme-primary-font-color); }' ). lo_buf->add( '' ). lo_buf->add( '/* DIFF */' ). lo_buf->add( '' ). lo_buf->add( '.diff_ins { background-color: #352; }' ). lo_buf->add( '.diff_del { background-color: #411; }' ). lo_buf->add( '.diff_upd { background-color: #551; }' ). lo_buf->add( 'div.diff_content { background-color: var(--theme-background-color); }' ). lo_buf->add( '' ). lo_buf->add( '/* DIFF TABLE */' ). lo_buf->add( '' ). lo_buf->add( 'table.diff_tab td,th { color: #fff; }' ). lo_buf->add( 'table.diff_tab thead.nav_line { background-color: var(--theme-container-background-color); }' ). lo_buf->add( '' ). lo_buf->add( '/* STYLES FOR SYNTAX HIGHLIGHTING */' ). lo_buf->add( '' ). lo_buf->add( '/* ABAP */' ). lo_buf->add( '.syntax-hl span.keyword { color: #4af; }' ). lo_buf->add( '.syntax-hl span.text { color: #8f8; }' ). lo_buf->add( '.syntax-hl span.comment { color: #999; }' ). lo_buf->add( '/* XML+HTML */' ). lo_buf->add( '.syntax-hl span.xml_tag { color: #659cff; }' ). lo_buf->add( '.syntax-hl span.attr { color: #bab2f9; }' ). lo_buf->add( '.syntax-hl span.attr_val { color: #b777fb; }' ). lo_buf->add( '/* CSS+JS */' ). lo_buf->add( '.syntax-hl span.properties { color:#0a69ce; }' ). lo_buf->add( '.syntax-hl span.values { color:blue; }' ). lo_buf->add( '.syntax-hl span.units { color:maroon; }' ). lo_buf->add( '.syntax-hl span.selectors { color:purple; }' ). lo_buf->add( '.syntax-hl span.functions { color:purple; }' ). lo_buf->add( '.syntax-hl span.colors { color:purple; }' ). lo_buf->add( '.syntax-hl span.extensions { color:lightblue; }' ). lo_buf->add( '.syntax-hl span.at_rules { color:lightblue; }' ). lo_buf->add( '.syntax-hl span.html { color:green; }' ). lo_buf->add( '.syntax-hl span.variables { color:purple; }' ). lo_buf->add( '' ). lo_buf->add( '/* DEBUG INFO STYLES */' ). lo_buf->add( '' ). lo_buf->add( 'div.debug_container#debug_info { color: var(--theme-primary-font-color); }' ). lo_buf->add( '' ). lo_buf->add( '/* DB ENTRIES */' ). lo_buf->add( '' ). lo_buf->add( 'div.db_list { background-color: var(--theme-container-background-color); }' ). lo_buf->add( 'table.db_tab td { color: var(--theme-primary-font-color); }' ). lo_buf->add( 'table.db_tab td.data { opacity: 0.5; }' ). lo_buf->add( 'table.db_tab tbody tr:hover, tr:active { background-color: var(--theme-list-hover-background-color); }' ). lo_buf->add( 'table.db_tab th {' ). lo_buf->add( ' color: var(--theme-primary-font-color);' ). lo_buf->add( ' border-bottom-color: #333;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'table.db_tab tr.selected {' ). lo_buf->add( ' background: rgba(92, 92, 92, 1) !important;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/* ERROR LOGS */' ). lo_buf->add( '' ). lo_buf->add( 'div.log { color: var(--theme-greyscale-dark); }' ). lo_buf->add( '.close-btn, .message-panel, .message-panel-commands a { color: var(--theme-greyscale-dark); }' ). lo_buf->add( '.message-panel-commands a:hover { color: var(--theme-greyscale-dark); }' ). lo_buf->add( '' ). lo_buf->add( '/* DIALOGS */' ). lo_buf->add( '' ). lo_buf->add( '.dialog {' ). lo_buf->add( ' color: var(--theme-primary-font-color-reduced);' ). lo_buf->add( ' background-color: var(--theme-container-background-color);' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.dialog-commands a,' ). lo_buf->add( '.dialog li.dialog-commands input[type="submit"] {' ). lo_buf->add( ' border-color: #ccc;' ). lo_buf->add( ' background-color: var(--theme-greyscale-dark);' ). lo_buf->add( ' color: #fff;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.dialog-commands a.main,' ). lo_buf->add( '.dialog li.dialog-commands input[type="submit"].main {' ). lo_buf->add( ' background-color: #64a8ff;' ). lo_buf->add( ' color: #fff;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog label {' ). lo_buf->add( ' color: var(--theme-primary-font-color);' ). lo_buf->add( '}' ). lo_buf->add( '.dialog label em {' ). lo_buf->add( ' color: #64a8ff;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.error small {' ). lo_buf->add( ' color: #ff5959;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.error input[type="number"],' ). lo_buf->add( '.dialog li.error input[type="text"] {' ). lo_buf->add( ' border-color: #ff5959;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog .radio-container {' ). lo_buf->add( ' border-color: #ddd;' ). lo_buf->add( ' background-color: var(--theme-greyscale-dark);' ). lo_buf->add( '}' ). lo_buf->add( '.dialog .radio-container input[type="radio"] + label {' ). lo_buf->add( ' color: #fff;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog .radio-container input[type="radio"]:checked + label {' ). lo_buf->add( ' background-color: #64a8ff;' ). lo_buf->add( ' color: #fff;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog li.with-command input[type="button"]:hover,' ). lo_buf->add( '.dialog li.with-command input[type="submit"]:hover {' ). lo_buf->add( ' background-color: #64a8ff;' ). lo_buf->add( ' color: #fff;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog fieldset {' ). lo_buf->add( ' border-color: #dfdfdf;' ). lo_buf->add( '}' ). lo_buf->add( '.dialog fieldset legend {' ). lo_buf->add( ' color: var(--theme-primary-font-color);' ). lo_buf->add( '}' ). lo_buf->add( '.dialog input:read-only {' ). lo_buf->add( ' background-color: var(--theme-greyscale-dark);' ). lo_buf->add( ' color: var(--theme-greyscale-medium);' ). lo_buf->add( '}' ). lo_buf->add( '/* for IE */' ). lo_buf->add( '.dialog input[readonly] {' ). lo_buf->add( ' background-color: var(--theme-greyscale-dark);' ). lo_buf->add( ' color: var(--theme-greyscale-medium);' ). lo_buf->add( '}' ). li_asset_man->register_asset( iv_url = 'css/theme-dark.css' iv_type = 'text/css' iv_cacheable = abap_false iv_mime_name = 'ZABAPGIT_CSS_THEME_DARK' iv_inline = lo_buf->join_w_newline_and_flush( ) ). **************************************************** * abapmerge Pragma - ZABAPGIT_CSS_THEME_BELIZE_BLUE **************************************************** lo_buf->add( '/*' ). lo_buf->add( ' * ABAPGIT THEME CSS - BELIZE BLUE' ). lo_buf->add( ' */' ). lo_buf->add( '' ). lo_buf->add( '/* https://experience.sap.com/fiori-design-web/colors/ */' ). lo_buf->add( '' ). lo_buf->add( ':root {' ). lo_buf->add( ' --fiori-color-global-light-base: #EFF4F9; /* Background in SAP GUI */' ). lo_buf->add( ' --fiori-color-gui-tab-background: #FCFDFE; /* Tabstrip background */' ). lo_buf->add( ' --fiori-color-gui-container-border: #D1E0EE;' ). lo_buf->add( ' --fiori-color-gui-uneditable-background: #F2F2F2; /* Textbox not editable */' ). lo_buf->add( ' --fiori-color-gui-editable-background: #FFFFFF; /* Textbox editable */' ). lo_buf->add( ' --fiori-color-font-primary: #333333; /* Grayscale 1 */' ). lo_buf->add( ' --fiori-color-font-secondary: #666666; /* Grayscale 2 */' ). lo_buf->add( ' --fiori-color-font-highlighted: #003D84;' ). lo_buf->add( ' --fiori-color-message-box-background: #2F3C48; /* Bottom message container */' ). lo_buf->add( '' ). lo_buf->add( ' --theme-background-color: var(--fiori-color-global-light-base);' ). lo_buf->add( ' --theme-container-background-color: var(--fiori-color-gui-tab-background);' ). lo_buf->add( ' --theme-primary-font: "72", Arial, Helvetica, sans-serif;' ). lo_buf->add( ' --theme-primary-font-color: var(--fiori-color-font-primary);' ). lo_buf->add( ' --theme-primary-font-color-reduced: var(--fiori-color-font-secondary);' ). lo_buf->add( ' --theme-font-size: 11pt;' ). lo_buf->add( ' --theme-link-color: var(--fiori-color-font-highlighted);' ). lo_buf->add( ' --theme-container-border-color: var(--fiori-color-gui-container-border);' ). lo_buf->add( ' --theme-table-border-color: #E5E5E5; /* ALV border color */' ). lo_buf->add( ' --theme-greyscale-dark: #666666;' ). lo_buf->add( ' --theme-greyscale-medium: #BFBFBF;' ). lo_buf->add( ' --theme-greyscale-light: #CCCCCC;' ). lo_buf->add( ' --theme-greyscale-lighter: #E5E5E5;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '#header a, #header a:visited {' ). lo_buf->add( ' color: #346187;' ). lo_buf->add( '}' ). li_asset_man->register_asset( iv_url = 'css/theme-belize-blue.css' iv_type = 'text/css' iv_cacheable = abap_false iv_mime_name = 'ZABAPGIT_CSS_THEME_BELIZE_BLUE' iv_inline = lo_buf->join_w_newline_and_flush( ) ). **************************************************** * abapmerge Pragma - ZABAPGIT_JS_COMMON **************************************************** lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * abapGit JavaScript Function Library' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' Global variables used from outside' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( '/* exported setInitialFocus */' ). lo_buf->add( '/* exported setInitialFocusWithQuerySelector */' ). lo_buf->add( '/* exported submitFormById */' ). lo_buf->add( '/* exported errorStub */' ). lo_buf->add( '/* exported confirmInitialized */' ). lo_buf->add( '/* exported perfOut */' ). lo_buf->add( '/* exported perfLog */' ). lo_buf->add( '/* exported perfClear */' ). lo_buf->add( '/* exported enableArrowListNavigation */' ). lo_buf->add( '/* exported activateLinkHints */' ). lo_buf->add( '/* exported setKeyBindings */' ). lo_buf->add( '/* exported preparePatch */' ). lo_buf->add( '/* exported registerStagePatch */' ). lo_buf->add( '/* exported toggleRepoListDetail */' ). lo_buf->add( '/* exported onTagTypeChange */' ). lo_buf->add( '/* exported getIndocStyleSheet */' ). lo_buf->add( '/* exported addMarginBottom */' ). lo_buf->add( '/* exported enumerateJumpAllFiles */' ). lo_buf->add( '/* exported createRepoCatalogEnumerator */' ). lo_buf->add( '/* exported enumerateUiActions */' ). lo_buf->add( '/* exported onDiffCollapse */' ). lo_buf->add( '/* exported restoreScrollPosition */' ). lo_buf->add( '/* exported toggleBrowserControlWarning */' ). lo_buf->add( '/* exported displayBrowserControlFooter */' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Polyfills' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( '// Bind polyfill (for IE7), taken from https://developer.mozilla.org/' ). lo_buf->add( 'if (!Function.prototype.bind) {' ). lo_buf->add( ' Function.prototype.bind = function(oThis) {' ). lo_buf->add( ' if (typeof this !== "function") {' ). lo_buf->add( ' throw new TypeError("Function.prototype.bind - subject is not callable");' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' var aArgs = Array.prototype.slice.call(arguments, 1);' ). lo_buf->add( ' var fToBind = this;' ). lo_buf->add( ' var fNOP = function() { };' ). lo_buf->add( ' var fBound = function() {' ). lo_buf->add( ' return fToBind.apply(' ). lo_buf->add( ' this instanceof fNOP ? this : oThis,' ). lo_buf->add( ' aArgs.concat(Array.prototype.slice.call(arguments))' ). lo_buf->add( ' );' ). lo_buf->add( ' };' ). lo_buf->add( '' ). lo_buf->add( ' if (this.prototype) {' ). lo_buf->add( ' fNOP.prototype = this.prototype;' ). lo_buf->add( ' }' ). lo_buf->add( ' fBound.prototype = new fNOP();' ). lo_buf->add( '' ). lo_buf->add( ' return fBound;' ). lo_buf->add( ' };' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '// String includes polyfill, taken from https://developer.mozilla.org' ). lo_buf->add( 'if (!String.prototype.includes) {' ). lo_buf->add( ' String.prototype.includes = function(search, start) {' ). lo_buf->add( ' "use strict";' ). lo_buf->add( ' if (typeof start !== "number") {' ). lo_buf->add( ' start = 0;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' if (start + search.length > this.length) {' ). lo_buf->add( ' return false;' ). lo_buf->add( ' } else {' ). lo_buf->add( ' return this.indexOf(search, start) !== -1;' ). lo_buf->add( ' }' ). lo_buf->add( ' };' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '// String startsWith polyfill, taken from https://developer.mozilla.org' ). lo_buf->add( 'if (!String.prototype.startsWith) {' ). lo_buf->add( ' Object.defineProperty(String.prototype, "startsWith", {' ). lo_buf->add( ' value: function(search, pos) {' ). lo_buf->add( ' pos = !pos || pos < 0 ? 0 : +pos;' ). lo_buf->add( '' ). lo_buf->add( ' return this.substring(pos, pos + search.length) === search;' ). lo_buf->add( ' }' ). lo_buf->add( ' });' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '// forEach polyfill, taken from https://developer.mozilla.org' ). lo_buf->add( '// used for querySelectorAll results' ). lo_buf->add( 'if (window.NodeList && !NodeList.prototype.forEach) {' ). lo_buf->add( ' NodeList.prototype.forEach = Array.prototype.forEach;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Common functions' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( '// Output text to the debug div' ). lo_buf->add( 'function debugOutput(text, dstID) {' ). lo_buf->add( ' var stdout = document.getElementById(dstID || "debug-output");' ). lo_buf->add( ' var wrapped = "

" + text + "

";' ). lo_buf->add( '' ). lo_buf->add( ' stdout.innerHTML = stdout.innerHTML + wrapped;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '// Use a supplied form, a pre-created form or create a hidden form' ). lo_buf->add( '// and submit with sapevent' ). lo_buf->add( 'function submitSapeventForm(params, action, method, form) {' ). lo_buf->add( '' ). lo_buf->add( ' function getSapeventPrefix() {' ). lo_buf->add( ' // Depending on the used browser control and its version, different URL schemes' ). lo_buf->add( ' // are used which we distinguish here' ). lo_buf->add( ' if (document.querySelector(''a[href*="file:///SAPEVENT:"]'')) {' ). lo_buf->add( ' // Prefix for old (SAPGUI <= 8.00 PL3) chromium based browser control' ). lo_buf->add( ' return "file:///";' ). lo_buf->add( ' } else if (document.querySelector(''a[href^="sap-cust"]'')) {' ). lo_buf->add( ' // Prefix for new (SAPGUI >= 8.00 PL3 Hotfix 1) chromium based browser control' ). lo_buf->add( ' return "sap-cust://sap-place-holder/";' ). lo_buf->add( ' } else {' ). lo_buf->add( ' return ""; // No prefix for old IE control' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' var stub_form_id = "form_" + action;' ). lo_buf->add( '' ). lo_buf->add( ' form = form' ). lo_buf->add( ' || document.getElementById(stub_form_id)' ). lo_buf->add( ' || document.createElement("form");' ). lo_buf->add( '' ). lo_buf->add( ' form.setAttribute("method", method || "post");' ). lo_buf->add( ' if (/sapevent/i.test(action)) {' ). lo_buf->add( ' form.setAttribute("action", action);' ). lo_buf->add( ' } else {' ). lo_buf->add( ' form.setAttribute("action", getSapeventPrefix() + "SAPEVENT:" + action);' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' for (var key in params) {' ). lo_buf->add( ' var hiddenField = document.createElement("input");' ). lo_buf->add( ' hiddenField.setAttribute("type", "hidden");' ). lo_buf->add( ' hiddenField.setAttribute("name", key);' ). lo_buf->add( ' hiddenField.setAttribute("value", params[key]);' ). lo_buf->add( ' form.appendChild(hiddenField);' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' var formExistsInDOM = form.id && Boolean(document.querySelector("#" + form.id));' ). lo_buf->add( '' ). lo_buf->add( ' if (form.id !== stub_form_id && !formExistsInDOM) {' ). lo_buf->add( ' document.body.appendChild(form);' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' form.submit();' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '// Set focus to a control' ). lo_buf->add( 'function setInitialFocus(id) {' ). lo_buf->add( ' document.getElementById(id).focus();' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '// Set focus to an element with query selector' ). lo_buf->add( 'function setInitialFocusWithQuerySelector(sSelector, bFocusParent) {' ). lo_buf->add( ' var oSelected = document.querySelector(sSelector);' ). lo_buf->add( '' ). lo_buf->add( ' if (oSelected) {' ). lo_buf->add( ' if (bFocusParent) {' ). lo_buf->add( ' oSelected.parentElement.focus();' ). lo_buf->add( ' } else {' ). lo_buf->add( ' oSelected.focus();' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '// Submit an existing form' ). lo_buf->add( 'function submitFormById(id) {' ). lo_buf->add( ' document.getElementById(id).submit();' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '// JS error stub' ). lo_buf->add( 'function errorStub(event) {' ). lo_buf->add( ' var element = event.target || event.srcElement;' ). lo_buf->add( ' var targetName = element.id || element.name || "???";' ). lo_buf->add( ' alert("JS Error, please log an issue (@" + targetName + ")");' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '// Confirm JS initialization' ). lo_buf->add( 'function confirmInitialized() {' ). lo_buf->add( ' var errorBanner = document.getElementById("js-error-banner");' ). lo_buf->add( ' if (errorBanner) {' ). lo_buf->add( ' errorBanner.style.display = "none";' ). lo_buf->add( ' }' ). lo_buf->add( ' debugOutput("js: OK"); // Final final confirmation :)' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Performance utils (for debugging)' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( 'var gPerf = [];' ). lo_buf->add( '' ). lo_buf->add( 'function perfOut(prefix) {' ). lo_buf->add( ' var totals = {};' ). lo_buf->add( ' for (var i = gPerf.length - 1; i >= 0; i--) {' ). lo_buf->add( ' if (!totals[gPerf[i].name]) totals[gPerf[i].name] = { count: 0, time: 0 };' ). lo_buf->add( '' ). lo_buf->add( ' totals[gPerf[i].name].time += gPerf[i].time;' ). lo_buf->add( ' totals[gPerf[i].name].count += 1;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' var keys = Object.keys(totals);' ). lo_buf->add( ' for (var j = keys.length - 1; j >= 0; j--) {' ). lo_buf->add( ' console.log(prefix' ). lo_buf->add( ' + " " + keys[j] + ": "' ). lo_buf->add( ' + totals[keys[j]].time.toFixed(3) + "ms"' ). lo_buf->add( ' + " (" + totals[keys[j]].count.toFixed() + ")");' ). lo_buf->add( ' }' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'function perfLog(name, startTime) {' ). lo_buf->add( ' gPerf.push({ name: name, time: window.performance.now() - startTime });' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'function perfClear() {' ). lo_buf->add( ' gPerf = [];' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Repo Overview Logic' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( 'function findStyleSheetByName(name) {' ). lo_buf->add( ' for (var s = 0; s < document.styleSheets.length; s++) {' ). lo_buf->add( ' var styleSheet = document.styleSheets[s];' ). lo_buf->add( ' var classes = styleSheet.cssRules || styleSheet.rules;' ). lo_buf->add( ' for (var i = 0; i < classes.length; i++) {' ). lo_buf->add( ' if (classes[i].selectorText === name) return classes[i];' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'function getIndocStyleSheet() {' ). lo_buf->add( ' for (var s = 0; s < document.styleSheets.length; s++) {' ). lo_buf->add( ' if (!document.styleSheets[s].href) return document.styleSheets[s]; // One with empty href' ). lo_buf->add( ' }' ). lo_buf->add( ' // None found ? create one' ). lo_buf->add( ' var style = document.createElement("style");' ). lo_buf->add( ' document.head.appendChild(style);' ). lo_buf->add( ' return style.sheet;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'function RepoOverViewHelper(opts) {' ). lo_buf->add( ' if (opts && opts.focusFilterKey) {' ). lo_buf->add( ' this.focusFilterKey = opts.focusFilterKey;' ). lo_buf->add( ' }' ). lo_buf->add( ' this.setHooks();' ). lo_buf->add( ' this.pageId = (opts && opts.pageId) ? opts.pageId : "RepoOverViewHelperState";' ). lo_buf->add( ' this.isDetailsDisplayed = false;' ). lo_buf->add( ' this.isOnlyFavoritesDisplayed = false;' ). lo_buf->add( ' this.detailCssClass = findStyleSheetByName(".repo-overview .ro-detail");' ). lo_buf->add( '' ). lo_buf->add( ' var icon = document.getElementById("icon-filter-detail");' ). lo_buf->add( ' this.toggleFilterIcon(icon, this.isDetailsDisplayed);' ). lo_buf->add( ' this.registerRowSelection();' ). lo_buf->add( ' this.registerKeyboardShortcuts();' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'RepoOverViewHelper.prototype.setHooks = function() {' ). lo_buf->add( ' window.onload = this.onPageLoad.bind(this);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'RepoOverViewHelper.prototype.onPageLoad = function() {' ). lo_buf->add( ' var data = window.localStorage && JSON.parse(window.localStorage.getItem(this.pageId));' ). lo_buf->add( ' if (data) {' ). lo_buf->add( ' if (data.isDetailsDisplayed) {' ). lo_buf->add( ' this.toggleItemsDetail(true);' ). lo_buf->add( ' }' ). lo_buf->add( ' if (data.selectedRepoKey) {' ). lo_buf->add( ' this.selectRowByRepoKey(data.selectedRepoKey);' ). lo_buf->add( ' } else {' ). lo_buf->add( ' this.selectRowByIndex(0);' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'RepoOverViewHelper.prototype.registerKeyboardShortcuts = function() {' ). lo_buf->add( ' var self = this;' ). lo_buf->add( ' document.addEventListener("keypress", function(event) {' ). lo_buf->add( ' if (document.activeElement.id === "filter") {' ). lo_buf->add( ' return;' ). lo_buf->add( ' }' ). lo_buf->add( ' if (self.focusFilterKey && event.key === self.focusFilterKey && !CommandPalette.isVisible()) {' ). lo_buf->add( ' var filterInput = document.getElementById("filter");' ). lo_buf->add( ' if (filterInput) filterInput.focus();' ). lo_buf->add( ' event.preventDefault();' ). lo_buf->add( ' return;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' var keycode = event.keyCode;' ). lo_buf->add( ' var rows = Array.prototype.slice.call(self.getVisibleRows());' ). lo_buf->add( ' var selected = document.querySelector(".repo-overview tr.selected");' ). lo_buf->add( ' var indexOfSelected = rows.indexOf(selected);' ). lo_buf->add( ' var lastRow = rows.length - 1;' ). lo_buf->add( '' ). lo_buf->add( ' if (keycode == 13 && document.activeElement.tagName.toLowerCase() != "input") {' ). lo_buf->add( ' // "enter" to open, unless command field has focus' ). lo_buf->add( ' self.openSelectedRepo();' ). lo_buf->add( ' } else if ((keycode == 52 || keycode == 56) && indexOfSelected > 0) {' ). lo_buf->add( ' // "4,8" for previous, digits are the numlock keys' ). lo_buf->add( ' // NB: numpad must be activated, keypress does not detect arrows' ). lo_buf->add( ' // if we need arrows it will be keydown. But then mind the keycodes, they may change !' ). lo_buf->add( ' // e.g. 100 is ''d'' with keypress (and conflicts with diff hotkey), and also it is arrow-left keydown' ). lo_buf->add( ' self.selectRowByIndex(indexOfSelected - 1);' ). lo_buf->add( ' } else if ((keycode == 54 || keycode == 50) && indexOfSelected < lastRow) {' ). lo_buf->add( ' // "6,2" for next' ). lo_buf->add( ' self.selectRowByIndex(indexOfSelected + 1);' ). lo_buf->add( ' }' ). lo_buf->add( ' });' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'RepoOverViewHelper.prototype.openSelectedRepo = function() {' ). lo_buf->add( ' this.selectedRepoKey = document.querySelector(".repo-overview tr.selected").dataset.key;' ). lo_buf->add( ' this.saveLocalStorage();' ). lo_buf->add( ' document.querySelector(".repo-overview tr.selected td.ro-go a").click();' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'RepoOverViewHelper.prototype.selectRowByIndex = function(index) {' ). lo_buf->add( ' var rows = this.getVisibleRows();' ). lo_buf->add( ' if (rows.length >= index) {' ). lo_buf->add( ' var selectedRow = rows[index];' ). lo_buf->add( ' if (selectedRow.classList.contains("selected")) {' ). lo_buf->add( ' return;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' this.deselectAllRows();' ). lo_buf->add( ' rows[index].classList.add("selected");' ). lo_buf->add( ' this.selectedRepoKey = selectedRow.dataset.key;' ). lo_buf->add( ' this.updateActionLinks(selectedRow);' ). lo_buf->add( ' this.saveLocalStorage();' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'RepoOverViewHelper.prototype.selectRowByRepoKey = function(key) {' ). lo_buf->add( ' var attributeQuery = "[data-key=''" + key + "'']";' ). lo_buf->add( ' var row = document.querySelector(".repo-overview tbody tr" + attributeQuery);' ). lo_buf->add( ' // navigation to already selected repo' ). lo_buf->add( ' if (row.dataset.key === key && row.classList.contains("selected")) {' ). lo_buf->add( ' return;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' this.deselectAllRows();' ). lo_buf->add( ' row.classList.add("selected");' ). lo_buf->add( ' this.selectedRepoKey = key;' ). lo_buf->add( ' this.updateActionLinks(row);' ). lo_buf->add( ' this.saveLocalStorage();' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'RepoOverViewHelper.prototype.updateActionLinks = function(selectedRow) {' ). lo_buf->add( ' // now we have a repo selected, determine which action buttons are relevant' ). lo_buf->add( ' var selectedRepoKey = selectedRow.dataset.key;' ). lo_buf->add( ' var selectedRepoIsOffline = selectedRow.dataset.offline === "X";' ). lo_buf->add( '' ). lo_buf->add( ' var actionLinks = document.querySelectorAll("a.action_link");' ). lo_buf->add( ' actionLinks.forEach(function(link) {' ). lo_buf->add( ' // adjust repo key in urls' ). lo_buf->add( ' link.href = link.href.replace(/\?key=(#|\d+)/, "?key=" + selectedRepoKey);' ). lo_buf->add( '' ). lo_buf->add( ' // toggle button visibility' ). lo_buf->add( ' if (link.classList.contains("action_offline_repo")) {' ). lo_buf->add( ' if (selectedRepoIsOffline) {' ). lo_buf->add( ' link.parentElement.classList.add("enabled");' ). lo_buf->add( ' } else {' ). lo_buf->add( ' link.parentElement.classList.remove("enabled");' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( ' else if (link.classList.contains("action_online_repo")) {' ). lo_buf->add( ' if (!selectedRepoIsOffline) {' ). lo_buf->add( ' link.parentElement.classList.add("enabled");' ). lo_buf->add( ' } else {' ). lo_buf->add( ' link.parentElement.classList.remove("enabled");' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( ' else {' ). lo_buf->add( ' // if the action is for both repository types, it will only have the .action_link class' ). lo_buf->add( ' // it still needs to be toggled as we want to hide everything if no repo is selected' ). lo_buf->add( ' link.parentElement.classList.add("enabled");' ). lo_buf->add( ' }' ). lo_buf->add( ' });' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'RepoOverViewHelper.prototype.deselectAllRows = function() {' ). lo_buf->add( ' document.querySelectorAll(".repo-overview tbody tr").forEach(function(x) {' ). lo_buf->add( ' x.classList.remove("selected");' ). lo_buf->add( ' });' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'RepoOverViewHelper.prototype.getVisibleRows = function() {' ). lo_buf->add( ' return document.querySelectorAll(".repo-overview tbody tr:not(.nodisplay)");' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'RepoOverViewHelper.prototype.registerRowSelection = function() {' ). lo_buf->add( ' var self = this;' ). lo_buf->add( ' document.querySelectorAll(".repo-overview tr td:not(.ro-go)").forEach(function(repoListRowCell) {' ). lo_buf->add( ' repoListRowCell.addEventListener("click", function() {' ). lo_buf->add( ' self.selectRowByRepoKey(this.parentElement.dataset.key);' ). lo_buf->add( ' });' ). lo_buf->add( ' });' ). lo_buf->add( '' ). lo_buf->add( ' document.querySelectorAll(".repo-overview tr td.ro-go").forEach(function(openRepoIcon) {' ). lo_buf->add( ' openRepoIcon.addEventListener("click", function() {' ). lo_buf->add( ' var selectedRow = this.parentElement;' ). lo_buf->add( ' self.selectRowByRepoKey(selectedRow.dataset.key);' ). lo_buf->add( ' self.openSelectedRepo();' ). lo_buf->add( ' });' ). lo_buf->add( ' });' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'RepoOverViewHelper.prototype.toggleRepoListDetail = function(forceDisplay) {' ). lo_buf->add( ' if (this.detailCssClass) {' ). lo_buf->add( ' this.toggleItemsDetail(forceDisplay);' ). lo_buf->add( ' this.saveLocalStorage();' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'RepoOverViewHelper.prototype.toggleItemsDetail = function(forceDisplay) {' ). lo_buf->add( ' if (this.detailCssClass) {' ). lo_buf->add( ' this.isDetailsDisplayed = forceDisplay || !this.isDetailsDisplayed;' ). lo_buf->add( '' ). lo_buf->add( ' // change layout to wide if details are displayed' ). lo_buf->add( ' if (this.isDetailsDisplayed) {' ). lo_buf->add( ' document.body.classList.remove("centered");' ). lo_buf->add( ' document.body.classList.add("full_width");' ). lo_buf->add( ' } else {' ). lo_buf->add( ' document.body.classList.add("centered");' ). lo_buf->add( ' document.body.classList.remove("full_width");' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' this.detailCssClass.style.display = this.isDetailsDisplayed ? "" : "none";' ). lo_buf->add( '' ). lo_buf->add( ' var icon = document.getElementById("icon-filter-detail");' ). lo_buf->add( ' this.toggleFilterIcon(icon, this.isDetailsDisplayed);' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'RepoOverViewHelper.prototype.toggleFilterIcon = function(icon, isEnabled) {' ). lo_buf->add( ' if (isEnabled) {' ). lo_buf->add( ' icon.classList.remove("grey");' ). lo_buf->add( ' icon.classList.add("blue");' ). lo_buf->add( ' } else {' ). lo_buf->add( ' icon.classList.remove("blue");' ). lo_buf->add( ' icon.classList.add("grey");' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'RepoOverViewHelper.prototype.saveLocalStorage = function() {' ). lo_buf->add( ' if (!window.localStorage) return;' ). lo_buf->add( ' var data = {' ). lo_buf->add( ' isDetailsDisplayed : this.isDetailsDisplayed,' ). lo_buf->add( ' isOnlyFavoritesDisplayed: this.isOnlyFavoritesDisplayed,' ). lo_buf->add( ' selectedRepoKey : this.selectedRepoKey,' ). lo_buf->add( ' };' ). lo_buf->add( ' window.localStorage.setItem(this.pageId, JSON.stringify(data));' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Staging Logic' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( '// Stage helper constructor' ). lo_buf->add( 'function StageHelper(params) {' ). lo_buf->add( ' this.pageSeed = params.seed;' ). lo_buf->add( ' this.formAction = params.formAction;' ). lo_buf->add( ' this.patchAction = params.patchAction;' ). lo_buf->add( ' this.user = params.user;' ). lo_buf->add( ' this.ids = params.ids;' ). lo_buf->add( ' this.selectedCount = 0;' ). lo_buf->add( ' this.filteredCount = 0;' ). lo_buf->add( ' this.lastFilterValue = "";' ). lo_buf->add( ' this.focusFilterKey = params.focusFilterKey;' ). lo_buf->add( '' ). lo_buf->add( ' // DOM nodes' ). lo_buf->add( ' this.dom = {' ). lo_buf->add( ' stageTab : document.getElementById(params.ids.stageTab),' ). lo_buf->add( ' commitAllBtn : document.getElementById(params.ids.commitAllBtn),' ). lo_buf->add( ' commitSelectedBtn: document.getElementById(params.ids.commitSelectedBtn),' ). lo_buf->add( ' commitFilteredBtn: document.getElementById(params.ids.commitFilteredBtn),' ). lo_buf->add( ' patchBtn : document.getElementById(params.ids.patchBtn),' ). lo_buf->add( ' objectSearch : document.getElementById(params.ids.objectSearch),' ). lo_buf->add( ' selectedCounter : null,' ). lo_buf->add( ' filteredCounter : null,' ). lo_buf->add( ' };' ). lo_buf->add( ' this.findCounters();' ). lo_buf->add( '' ). lo_buf->add( ' // Table columns (autodetection)' ). lo_buf->add( ' this.colIndex = this.detectColumns();' ). lo_buf->add( ' this.filterTargets = ["name", "user", "transport"];' ). lo_buf->add( '' ). lo_buf->add( ' // Constants' ). lo_buf->add( ' this.HIGHLIGHT_STYLE = "highlight";' ). lo_buf->add( ' this.STATUS = {' ). lo_buf->add( ' add : "A",' ). lo_buf->add( ' remove : "R",' ). lo_buf->add( ' ignore : "I",' ). lo_buf->add( ' reset : "?",' ). lo_buf->add( ' isValid: function(status) { return "ARI?".indexOf(status) == -1 }' ). lo_buf->add( ' };' ). lo_buf->add( '' ). lo_buf->add( ' this.TEMPLATES = {' ). lo_buf->add( ' cmdReset : "reset",' ). lo_buf->add( ' cmdLocal : "add",' ). lo_buf->add( ' cmdRemote: "ignoreremove"' ). lo_buf->add( ' };' ). lo_buf->add( '' ). lo_buf->add( ' this.setHooks();' ). lo_buf->add( ' if (this.user) this.injectFilterMe();' ). lo_buf->add( ' Hotkeys.addHotkeyToHelpSheet("^Enter", "Commit");' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'StageHelper.prototype.findCounters = function() {' ). lo_buf->add( ' this.dom.selectedCounter = this.dom.commitSelectedBtn.querySelector("span.counter");' ). lo_buf->add( ' this.dom.filteredCounter = this.dom.commitFilteredBtn.querySelector("span.counter");' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'StageHelper.prototype.injectFilterMe = function() {' ). lo_buf->add( ' var tabFirstHead = this.dom.stageTab.tHead.rows[0];' ). lo_buf->add( ' if (!tabFirstHead || tabFirstHead.className !== "local") {' ). lo_buf->add( ' return; // for the case only "remove part" is displayed' ). lo_buf->add( ' }' ). lo_buf->add( ' var changedByHead = tabFirstHead.cells[this.colIndex.user];' ). lo_buf->add( '' ). lo_buf->add( ' changedByHead.innerText = changedByHead.innerText + " (";' ). lo_buf->add( '' ). lo_buf->add( ' var a = document.createElement("A");' ). lo_buf->add( ' a.appendChild(document.createTextNode("me"));' ). lo_buf->add( ' a.onclick = this.onFilterMe.bind(this);' ). lo_buf->add( ' a.href = "#";' ). lo_buf->add( ' changedByHead.appendChild(a);' ). lo_buf->add( ' changedByHead.appendChild(document.createTextNode(")"));' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'StageHelper.prototype.onFilterMe = function() {' ). lo_buf->add( ' this.dom.objectSearch.value = this.user;' ). lo_buf->add( ' this.onFilter({ type: "keypress", which: 13, target: this.dom.objectSearch });' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Hook global click listener on table, load/unload actions' ). lo_buf->add( 'StageHelper.prototype.setHooks = function() {' ). lo_buf->add( ' window.onkeypress = this.onCtrlEnter.bind(this);' ). lo_buf->add( ' this.dom.stageTab.onclick = this.onTableClick.bind(this);' ). lo_buf->add( ' this.dom.commitSelectedBtn.onclick = this.submit.bind(this);' ). lo_buf->add( ' this.dom.commitFilteredBtn.onclick = this.submitVisible.bind(this);' ). lo_buf->add( ' this.dom.patchBtn.onclick = this.submitPatch.bind(this);' ). lo_buf->add( ' this.dom.objectSearch.oninput = this.onFilter.bind(this);' ). lo_buf->add( ' this.dom.objectSearch.onkeypress = this.onFilter.bind(this);' ). lo_buf->add( ' window.onbeforeunload = this.onPageUnload.bind(this);' ). lo_buf->add( ' window.onload = this.onPageLoad.bind(this);' ). lo_buf->add( '' ). lo_buf->add( ' var self = this;' ). lo_buf->add( ' document.addEventListener("keypress", function(event) {' ). lo_buf->add( ' if (document.activeElement.id !== self.ids.objectSearch' ). lo_buf->add( ' && self.focusFilterKey && event.key === self.focusFilterKey' ). lo_buf->add( ' && !CommandPalette.isVisible()) {' ). lo_buf->add( '' ). lo_buf->add( ' self.dom.objectSearch.focus();' ). lo_buf->add( ' event.preventDefault();' ). lo_buf->add( ' }' ). lo_buf->add( ' });' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Detect column index' ). lo_buf->add( 'StageHelper.prototype.detectColumns = function() {' ). lo_buf->add( ' var dataRow = this.dom.stageTab.tBodies[0].rows[0];' ). lo_buf->add( ' var colIndex = {};' ). lo_buf->add( '' ). lo_buf->add( ' for (var i = dataRow.cells.length - 1; i >= 0; i--) {' ). lo_buf->add( ' if (dataRow.cells[i].className) colIndex[dataRow.cells[i].className] = i;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' return colIndex;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Store table state on leaving the page' ). lo_buf->add( 'StageHelper.prototype.onPageUnload = function() {' ). lo_buf->add( ' if (!window.sessionStorage) return;' ). lo_buf->add( '' ). lo_buf->add( ' var data = this.collectData();' ). lo_buf->add( ' window.sessionStorage.setItem(this.pageSeed, JSON.stringify(data));' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Re-store table state on entering the page' ). lo_buf->add( 'StageHelper.prototype.onPageLoad = function() {' ). lo_buf->add( ' var data = window.sessionStorage && JSON.parse(window.sessionStorage.getItem(this.pageSeed));' ). lo_buf->add( '' ). lo_buf->add( ' this.iterateStageTab(true, function(row) {' ). lo_buf->add( ' var status = data && data[row.cells[this.colIndex["name"]].innerText];' ). lo_buf->add( ' this.updateRow(row, status || this.STATUS.reset);' ). lo_buf->add( ' });' ). lo_buf->add( '' ). lo_buf->add( ' this.updateMenu();' ). lo_buf->add( ' if (this.dom.objectSearch.value) {' ). lo_buf->add( ' this.applyFilterValue(this.dom.objectSearch.value);' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Table event handler, change status' ). lo_buf->add( 'StageHelper.prototype.onTableClick = function(event) {' ). lo_buf->add( ' var target = event.target || event.srcElement;' ). lo_buf->add( ' if (!target) return;' ). lo_buf->add( '' ). lo_buf->add( ' var td;' ). lo_buf->add( ' if (target.tagName === "A") {' ). lo_buf->add( ' td = target.parentNode;' ). lo_buf->add( ' } else if (target.tagName === "TD") {' ). lo_buf->add( ' td = target;' ). lo_buf->add( ' if (td.children.length === 1 && td.children[0].tagName === "A") {' ). lo_buf->add( ' target = td.children[0];' ). lo_buf->add( ' } else return;' ). lo_buf->add( ' } else return;' ). lo_buf->add( '' ). lo_buf->add( ' if (["TD", "TH"].indexOf(td.tagName) == -1 || td.className != "cmd") return;' ). lo_buf->add( '' ). lo_buf->add( ' var status = this.STATUS[target.innerText]; // Convert anchor text to status' ). lo_buf->add( ' var targetRow = td.parentNode;' ). lo_buf->add( '' ). lo_buf->add( ' if (td.tagName === "TD") {' ). lo_buf->add( ' this.updateRow(targetRow, status);' ). lo_buf->add( ' } else { // TH' ). lo_buf->add( ' this.iterateStageTab(true, function(row) {' ). lo_buf->add( ' if (row.style.display !== "none" // Not filtered out' ). lo_buf->add( ' && row.className === targetRow.className // Same context as header' ). lo_buf->add( ' ) {' ). lo_buf->add( ' this.updateRow(row, status);' ). lo_buf->add( ' }' ). lo_buf->add( ' });' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' this.updateMenu();' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'StageHelper.prototype.onCtrlEnter = function(e) {' ). lo_buf->add( ' if (e.ctrlKey && (e.which === 10 || e.key === "Enter")) {' ). lo_buf->add( ' var clickMap = {' ). lo_buf->add( ' "default" : this.dom.commitAllBtn,' ). lo_buf->add( ' "selected": this.dom.commitSelectedBtn,' ). lo_buf->add( ' "filtered": this.dom.commitFilteredBtn' ). lo_buf->add( ' };' ). lo_buf->add( ' clickMap[this.calculateActiveCommitCommand()].click();' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Search object' ). lo_buf->add( 'StageHelper.prototype.onFilter = function(e) {' ). lo_buf->add( ' if ( // Enter hit or clear, IE SUCKS !' ). lo_buf->add( ' e.type === "input" && !e.target.value && this.lastFilterValue' ). lo_buf->add( ' || e.type === "keypress" && (e.which === 13 || e.key === "Enter") && !e.ctrlKey) {' ). lo_buf->add( '' ). lo_buf->add( ' this.applyFilterValue(e.target.value);' ). lo_buf->add( ' submitSapeventForm({ filterValue: e.target.value }, "stage_filter", "post");' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'StageHelper.prototype.applyFilterValue = function(sFilterValue) {' ). lo_buf->add( ' this.lastFilterValue = sFilterValue;' ). lo_buf->add( ' this.filteredCount = this.iterateStageTab(true, this.applyFilterToRow, sFilterValue);' ). lo_buf->add( ' this.updateMenu();' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Apply filter to a single stage line - hide or show' ). lo_buf->add( 'StageHelper.prototype.applyFilterToRow = function(row, filter) {' ). lo_buf->add( ' // Collect data cells' ). lo_buf->add( ' var targets = this.filterTargets.map(function(attr) {' ). lo_buf->add( ' // Get the innermost tag with the text we want to filter' ). lo_buf->add( ' // text: elem = td-tag' ). lo_buf->add( ' // text: elem = a-tag' ). lo_buf->add( ' var elem = row.cells[this.colIndex[attr]];' ). lo_buf->add( ' var elemA = elem.getElementsByTagName("A")[0];' ). lo_buf->add( '' ). lo_buf->add( ' if (elemA) elem = elemA;' ). lo_buf->add( ' return {' ). lo_buf->add( ' elem : elem,' ). lo_buf->add( ' plainText: elem.innerText.replace(/ /g, "\u00a0"), // without tags, with encoded spaces' ). lo_buf->add( ' curHtml : elem.innerHTML' ). lo_buf->add( ' };' ). lo_buf->add( ' }, this);' ). lo_buf->add( '' ). lo_buf->add( ' var isVisible = false;' ). lo_buf->add( '' ). lo_buf->add( ' // Apply filter to cells, mark filtered text' ). lo_buf->add( ' for (var i = targets.length - 1; i >= 0; i--) {' ). lo_buf->add( ' var target = targets[i];' ). lo_buf->add( ' // Ignore case of filter' ). lo_buf->add( ' var regFilter = new RegExp("(" + filter + ")", "gi");' ). lo_buf->add( '' ). lo_buf->add( ' target.newHtml = (filter)' ). lo_buf->add( ' ? target.plainText.replace(regFilter, "$1")' ). lo_buf->add( ' : target.plainText;' ). lo_buf->add( ' target.isChanged = target.newHtml !== target.curHtml;' ). lo_buf->add( ' isVisible = isVisible || !filter || target.newHtml !== target.plainText;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' // Update DOM' ). lo_buf->add( ' row.style.display = isVisible ? "" : "none";' ). lo_buf->add( ' for (var j = targets.length - 1; j >= 0; j--) {' ). lo_buf->add( ' if (targets[j].isChanged) targets[j].elem.innerHTML = targets[j].newHtml;' ). lo_buf->add( ' }' ). lo_buf->add( ' return isVisible ? 1 : 0;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Get how status should affect object counter' ). lo_buf->add( 'StageHelper.prototype.getStatusImpact = function(status) {' ). lo_buf->add( ' if (typeof status !== "string"' ). lo_buf->add( ' || status.length !== 1' ). lo_buf->add( ' || this.STATUS.isValid(status)) {' ). lo_buf->add( ' alert("Unknown status");' ). lo_buf->add( ' } else {' ). lo_buf->add( ' return (status !== this.STATUS.reset) ? 1: 0;' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Update table line' ). lo_buf->add( 'StageHelper.prototype.updateRow = function(row, newStatus) {' ). lo_buf->add( ' var oldStatus = row.cells[this.colIndex["status"]].innerText;' ). lo_buf->add( '' ). lo_buf->add( ' if (oldStatus !== newStatus) {' ). lo_buf->add( ' this.updateRowStatus(row, newStatus);' ). lo_buf->add( ' this.updateRowCommand(row, newStatus);' ). lo_buf->add( ' } else if (!row.cells[this.colIndex["cmd"]].children.length) {' ). lo_buf->add( ' this.updateRowCommand(row, newStatus); // For initial run' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' this.selectedCount += this.getStatusImpact(newStatus) - this.getStatusImpact(oldStatus);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Update Status cell (render set of commands)' ). lo_buf->add( 'StageHelper.prototype.updateRowStatus = function(row, status) {' ). lo_buf->add( ' row.cells[this.colIndex["status"]].innerText = status;' ). lo_buf->add( ' if (status === this.STATUS.reset) {' ). lo_buf->add( ' row.cells[this.colIndex["status"]].classList.remove(this.HIGHLIGHT_STYLE);' ). lo_buf->add( ' } else {' ). lo_buf->add( ' row.cells[this.colIndex["status"]].classList.add(this.HIGHLIGHT_STYLE);' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Update Command cell (render set of commands)' ). lo_buf->add( 'StageHelper.prototype.updateRowCommand = function(row, status) {' ). lo_buf->add( ' var cell = row.cells[this.colIndex["cmd"]];' ). lo_buf->add( ' if (status === this.STATUS.reset) {' ). lo_buf->add( ' cell.innerHTML = (row.className == "local")' ). lo_buf->add( ' ? this.TEMPLATES.cmdLocal' ). lo_buf->add( ' : this.TEMPLATES.cmdRemote;' ). lo_buf->add( ' } else {' ). lo_buf->add( ' cell.innerHTML = this.TEMPLATES.cmdReset;' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'StageHelper.prototype.calculateActiveCommitCommand = function() {' ). lo_buf->add( ' var active;' ). lo_buf->add( ' if (this.selectedCount > 0) {' ). lo_buf->add( ' active = "selected";' ). lo_buf->add( ' } else if (this.lastFilterValue) {' ). lo_buf->add( ' active = "filtered";' ). lo_buf->add( ' } else {' ). lo_buf->add( ' active = "default";' ). lo_buf->add( ' }' ). lo_buf->add( ' return active;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Update menu items visibility' ). lo_buf->add( 'StageHelper.prototype.updateMenu = function() {' ). lo_buf->add( ' var display = this.calculateActiveCommitCommand();' ). lo_buf->add( '' ). lo_buf->add( ' if (display === "selected") this.dom.selectedCounter.innerText = this.selectedCount.toString();' ). lo_buf->add( ' if (display === "filtered") this.dom.filteredCounter.innerText = this.filteredCount.toString();' ). lo_buf->add( '' ). lo_buf->add( ' this.dom.commitAllBtn.style.display = display === "default" ? "" : "none";' ). lo_buf->add( ' this.dom.commitSelectedBtn.style.display = display === "selected" ? "" : "none";' ). lo_buf->add( ' this.dom.commitFilteredBtn.style.display = display === "filtered" ? "" : "none";' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Submit stage state to the server' ). lo_buf->add( 'StageHelper.prototype.submit = function() {' ). lo_buf->add( ' submitSapeventForm(this.collectData(), this.formAction);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'StageHelper.prototype.submitVisible = function() {' ). lo_buf->add( ' this.markVisiblesAsAdded();' ). lo_buf->add( ' submitSapeventForm(this.collectData(), this.formAction);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'StageHelper.prototype.submitPatch = function() {' ). lo_buf->add( ' submitSapeventForm(this.collectData(), this.patchAction);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Extract data from the table' ). lo_buf->add( 'StageHelper.prototype.collectData = function() {' ). lo_buf->add( ' var data = {};' ). lo_buf->add( ' this.iterateStageTab(false, function(row) {' ). lo_buf->add( ' data[row.cells[this.colIndex["name"]].innerText] = row.cells[this.colIndex["status"]].innerText;' ). lo_buf->add( ' });' ). lo_buf->add( ' return data;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'StageHelper.prototype.markVisiblesAsAdded = function() {' ). lo_buf->add( ' this.iterateStageTab(false, function(row) {' ). lo_buf->add( ' // TODO refactor, unify updateRow logic' ). lo_buf->add( ' if (row.style.display === "" && row.className === "local") { // visible' ). lo_buf->add( ' this.updateRow(row, this.STATUS.add);' ). lo_buf->add( ' } else {' ). lo_buf->add( ' this.updateRow(row, this.STATUS.reset);' ). lo_buf->add( ' }' ). lo_buf->add( ' });' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Table iteration helper' ). lo_buf->add( 'StageHelper.prototype.iterateStageTab = function(changeMode, cb /*, ...*/) {' ). lo_buf->add( ' var restArgs = Array.prototype.slice.call(arguments, 2);' ). lo_buf->add( ' var table = this.dom.stageTab;' ). lo_buf->add( ' var retTotal = 0;' ). lo_buf->add( '' ). lo_buf->add( ' if (changeMode) {' ). lo_buf->add( ' var scrollOffset = window.pageYOffset;' ). lo_buf->add( '' ). lo_buf->add( ' this.dom.stageTab.style.display = "none";' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' for (var b = 0, bN = table.tBodies.length; b < bN; b++) {' ). lo_buf->add( ' var tbody = table.tBodies[b];' ). lo_buf->add( ' for (var r = 0, rN = tbody.rows.length; r < rN; r++) {' ). lo_buf->add( ' var args = [tbody.rows[r]].concat(restArgs);' ). lo_buf->add( ' var retVal = cb.apply(this, args); // callback' ). lo_buf->add( '' ). lo_buf->add( ' if (typeof retVal === "number") retTotal += retVal;' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' if (changeMode) {' ). lo_buf->add( ' this.dom.stageTab.style.display = "";' ). lo_buf->add( ' window.scrollTo(0, scrollOffset);' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' return retTotal;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Check List Wrapper' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( 'function CheckListWrapper(id, cbAction, cbActionOnlyMyChanges) {' ). lo_buf->add( ' this.id = document.getElementById(id);' ). lo_buf->add( ' this.cbAction = cbAction;' ). lo_buf->add( ' this.cbActionOnlyMyChanges = cbActionOnlyMyChanges;' ). lo_buf->add( ' this.id.onclick = this.onClick.bind(this);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'CheckListWrapper.prototype.onClick = function(e) { // eslint-disable-line no-unused-vars' ). lo_buf->add( ' // Get nodes' ). lo_buf->add( ' var target = event.target || event.srcElement;' ). lo_buf->add( ' if (!target) return;' ). lo_buf->add( ' if (target.tagName !== "A") { target = target.parentNode } // icon clicked' ). lo_buf->add( ' if (target.tagName !== "A") return;' ). lo_buf->add( ' if (target.parentNode.tagName !== "LI") return;' ). lo_buf->add( '' ). lo_buf->add( ' var nodeA = target;' ). lo_buf->add( ' var nodeLi = target.parentNode;' ). lo_buf->add( ' var nodeIcon = target.children[0];' ). lo_buf->add( ' if (!nodeIcon.classList.contains("icon")) return;' ). lo_buf->add( '' ). lo_buf->add( ' // Node updates' ). lo_buf->add( ' var option = nodeA.innerText;' ). lo_buf->add( ' var oldState = nodeLi.getAttribute("data-check");' ). lo_buf->add( ' if (oldState === null) return; // no data-check attribute - non-checkbox' ). lo_buf->add( ' var newState = oldState === "X" ? false : true;' ). lo_buf->add( '' ). lo_buf->add( ' if (newState) {' ). lo_buf->add( ' nodeIcon.classList.remove("grey");' ). lo_buf->add( ' nodeIcon.classList.add("blue");' ). lo_buf->add( ' nodeLi.setAttribute("data-check", "X");' ). lo_buf->add( ' } else {' ). lo_buf->add( ' nodeIcon.classList.remove("blue");' ). lo_buf->add( ' nodeIcon.classList.add("grey");' ). lo_buf->add( ' nodeLi.setAttribute("data-check", "");' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' // Action callback, special handling for "Only My Changes"' ). lo_buf->add( ' if (option === "Only my changes") {' ). lo_buf->add( ' this.cbActionOnlyMyChanges(nodeLi.getAttribute("data-aux"), newState);' ). lo_buf->add( '' ). lo_buf->add( ' // hide "Changed By" menu' ). lo_buf->add( ' } else {' ). lo_buf->add( ' this.cbAction(nodeLi.getAttribute("data-aux"), option, newState);' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Diff Page Logic' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( '// Diff helper constructor' ). lo_buf->add( 'function DiffHelper(params) {' ). lo_buf->add( ' this.pageSeed = params.seed;' ). lo_buf->add( ' this.counter = 0;' ). lo_buf->add( ' this.stageAction = params.stageAction;' ). lo_buf->add( '' ). lo_buf->add( ' // DOM nodes' ). lo_buf->add( ' this.dom = {' ). lo_buf->add( ' diffList : document.getElementById(params.ids.diffList),' ). lo_buf->add( ' stageButton: document.getElementById(params.ids.stageButton)' ). lo_buf->add( ' };' ). lo_buf->add( '' ). lo_buf->add( ' this.repoKey = this.dom.diffList.getAttribute("data-repo-key");' ). lo_buf->add( ' if (!this.repoKey) return; // Unexpected' ). lo_buf->add( '' ). lo_buf->add( ' this.dom.jump = document.getElementById(params.ids.jump);' ). lo_buf->add( ' this.dom.jump.onclick = this.onJump.bind(this);' ). lo_buf->add( '' ). lo_buf->add( ' // Checklist wrapper' ). lo_buf->add( ' if (document.getElementById(params.ids.filterMenu)) {' ). lo_buf->add( ' this.checkList = new CheckListWrapper(params.ids.filterMenu, this.onFilter.bind(this), this.onFilterOnlyMyChanges.bind(this));' ). lo_buf->add( ' this.dom.filterButton = document.getElementById(params.ids.filterMenu).parentNode;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' // Hijack stage command' ). lo_buf->add( ' if (this.dom.stageButton) {' ). lo_buf->add( ' this.dom.stageButton.href = "#";' ). lo_buf->add( ' this.dom.stageButton.onclick = this.onStage.bind(this);' ). lo_buf->add( ' }' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '// Action on jump click' ). lo_buf->add( 'DiffHelper.prototype.onJump = function(e) {' ). lo_buf->add( ' var text = ((e.target && e.target.text) || e);' ). lo_buf->add( ' if (!text) return;' ). lo_buf->add( '' ). lo_buf->add( ' var elFile = document.querySelector("[data-file*=''" + text + "'']");' ). lo_buf->add( ' if (!elFile) return;' ). lo_buf->add( '' ). lo_buf->add( ' setTimeout(function() {' ). lo_buf->add( ' elFile.scrollIntoView();' ). lo_buf->add( ' }, 100);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Action on filter click' ). lo_buf->add( 'DiffHelper.prototype.onFilter = function(attr, target, state) {' ). lo_buf->add( ' this.applyFilter(attr, target, state);' ). lo_buf->add( ' this.highlightButton(state);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'DiffHelper.prototype.onFilterOnlyMyChanges = function(username, state) {' ). lo_buf->add( ' this.applyOnlyMyChangesFilter(username, state);' ). lo_buf->add( ' this.counter = 0;' ). lo_buf->add( '' ). lo_buf->add( ' if (state) {' ). lo_buf->add( ' this.dom.filterButton.classList.add("bgorange");' ). lo_buf->add( ' } else {' ). lo_buf->add( ' this.dom.filterButton.classList.remove("bgorange");' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' // apply logic on Changed By list items' ). lo_buf->add( ' var changedByListItems = Array.prototype.slice.call(document.querySelectorAll("[data-aux*=changed-by]"));' ). lo_buf->add( '' ). lo_buf->add( ' changedByListItems' ). lo_buf->add( ' .map(function(item) {' ). lo_buf->add( ' var nodeIcon = item.children[0].children[0];' ). lo_buf->add( '' ). lo_buf->add( ' if (state === true) {' ). lo_buf->add( ' if (item.innerText === username) { // current user' ). lo_buf->add( ' item.style.display = "";' ). lo_buf->add( ' item.setAttribute("data-check", "X");' ). lo_buf->add( '' ). lo_buf->add( ' if (nodeIcon) {' ). lo_buf->add( ' nodeIcon.classList.remove("grey");' ). lo_buf->add( ' nodeIcon.classList.add("blue");' ). lo_buf->add( ' }' ). lo_buf->add( ' } else { // other users' ). lo_buf->add( ' item.style.display = "none";' ). lo_buf->add( ' item.setAttribute("data-check", "");' ). lo_buf->add( ' }' ). lo_buf->add( ' } else {' ). lo_buf->add( ' item.style.display = "";' ). lo_buf->add( ' item.setAttribute("data-check", "X");' ). lo_buf->add( '' ). lo_buf->add( ' if (nodeIcon) {' ). lo_buf->add( ' nodeIcon.classList.remove("grey");' ). lo_buf->add( ' nodeIcon.classList.add("blue");' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( ' });' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'DiffHelper.prototype.applyOnlyMyChangesFilter = function(username, state) {' ). lo_buf->add( ' var jumpListItems = Array.prototype.slice.call(document.querySelectorAll("[id*=li_jump]"));' ). lo_buf->add( '' ). lo_buf->add( ' this.iterateDiffList(function(div) {' ). lo_buf->add( ' if (state === true) { // switching on "Only my changes" filter' ). lo_buf->add( ' if (div.getAttribute("data-changed-by") === username) {' ). lo_buf->add( ' div.style.display = state ? "" : "none";' ). lo_buf->add( ' } else {' ). lo_buf->add( ' div.style.display = state ? "none" : "";' ). lo_buf->add( ' }' ). lo_buf->add( ' } else { // disabling' ). lo_buf->add( ' div.style.display = "";' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' // hide the file in the jump list' ). lo_buf->add( ' var dataFile = div.getAttribute("data-file");' ). lo_buf->add( ' jumpListItems' ). lo_buf->add( ' .filter(function(item) { return dataFile.includes(item.text) })' ). lo_buf->add( ' .map(function(item) { item.style.display = div.style.display });' ). lo_buf->add( ' });' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Hide/show diff based on params' ). lo_buf->add( 'DiffHelper.prototype.applyFilter = function(attr, target, state) {' ). lo_buf->add( ' var jumpListItems = Array.prototype.slice.call(document.querySelectorAll("[id*=li_jump]"));' ). lo_buf->add( '' ). lo_buf->add( ' this.iterateDiffList(function(div) {' ). lo_buf->add( ' if (div.getAttribute("data-" + attr) === target) {' ). lo_buf->add( ' div.style.display = state ? "" : "none";' ). lo_buf->add( '' ). lo_buf->add( ' // hide the file in the jump list' ). lo_buf->add( ' var dataFile = div.getAttribute("data-file");' ). lo_buf->add( ' jumpListItems' ). lo_buf->add( ' .filter(function(item) { return dataFile.includes(item.text) })' ). lo_buf->add( ' .map(function(item) { item.style.display = div.style.display });' ). lo_buf->add( ' }' ). lo_buf->add( ' });' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Action on stage -> save visible diffs as state for stage page' ). lo_buf->add( 'DiffHelper.prototype.onStage = function(e) { // eslint-disable-line no-unused-vars' ). lo_buf->add( ' if (window.sessionStorage) {' ). lo_buf->add( ' var data = this.buildStageCache();' ). lo_buf->add( ' window.sessionStorage.setItem(this.pageSeed, JSON.stringify(data));' ). lo_buf->add( ' }' ). lo_buf->add( ' var getParams = { key: this.repoKey, seed: this.pageSeed };' ). lo_buf->add( ' submitSapeventForm(getParams, this.stageAction, "get");' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Collect visible diffs' ). lo_buf->add( 'DiffHelper.prototype.buildStageCache = function() {' ). lo_buf->add( ' var list = {};' ). lo_buf->add( ' this.iterateDiffList(function(div) {' ). lo_buf->add( ' var filename = div.getAttribute("data-file");' ). lo_buf->add( ' if (!div.style.display && filename) { // No display override - visible !!' ). lo_buf->add( ' list[filename] = "A"; // Add' ). lo_buf->add( ' }' ). lo_buf->add( ' });' ). lo_buf->add( ' return list;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Table iterator' ). lo_buf->add( 'DiffHelper.prototype.iterateDiffList = function(cb /*, ...*/) {' ). lo_buf->add( ' var restArgs = Array.prototype.slice.call(arguments, 1);' ). lo_buf->add( ' var diffList = this.dom.diffList;' ). lo_buf->add( '' ). lo_buf->add( ' for (var i = 0, iN = diffList.children.length; i < iN; i++) {' ). lo_buf->add( ' var div = diffList.children[i];' ). lo_buf->add( ' if (div.className !== "diff") continue;' ). lo_buf->add( ' var args = [div].concat(restArgs);' ). lo_buf->add( ' cb.apply(this, args);// callback' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Highlight filter button if filter is activated' ). lo_buf->add( 'DiffHelper.prototype.highlightButton = function(state) {' ). lo_buf->add( ' this.counter += state ? -1 : 1;' ). lo_buf->add( ' if (this.counter > 0) {' ). lo_buf->add( ' this.dom.filterButton.classList.add("bgorange");' ). lo_buf->add( ' } else {' ). lo_buf->add( ' this.dom.filterButton.classList.remove("bgorange");' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Collapse or expand diffs' ). lo_buf->add( 'function onDiffCollapse(event) {' ). lo_buf->add( ' var source = event.target || event.srcElement;' ). lo_buf->add( ' var nextDiffContent = source.parentElement.nextElementSibling;' ). lo_buf->add( ' var hide;' ). lo_buf->add( '' ). lo_buf->add( ' if (source.classList.contains("icon-chevron-down")) {' ). lo_buf->add( ' source.classList.remove("icon-chevron-down");' ). lo_buf->add( ' source.classList.add("icon-chevron-right");' ). lo_buf->add( ' hide = true;' ). lo_buf->add( ' } else {' ). lo_buf->add( ' source.classList.remove("icon-chevron-right");' ). lo_buf->add( ' source.classList.add("icon-chevron-down");' ). lo_buf->add( ' hide = false;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' hide ? nextDiffContent.classList.add("nodisplay"): nextDiffContent.classList.remove("nodisplay");' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '// Add bottom margin, so that we can scroll to the top of the last file' ). lo_buf->add( 'function addMarginBottom() {' ). lo_buf->add( ' document.getElementsByTagName("body")[0].style.marginBottom = screen.height + "px";' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Diff Page Column Selection' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( 'function DiffColumnSelection() {' ). lo_buf->add( ' this.selectedColumnIdx = -1;' ). lo_buf->add( ' this.lineNumColumnIdx = -1;' ). lo_buf->add( ' //https://stackoverflow.com/questions/2749244/javascript-setinterval-and-this-solution' ). lo_buf->add( ' document.addEventListener("mousedown", this.mousedownEventListener.bind(this));' ). lo_buf->add( ' document.addEventListener("copy", this.copyEventListener.bind(this));' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'DiffColumnSelection.prototype.mousedownEventListener = function(e) {' ). lo_buf->add( ' // Select text in a column of an HTML table and copy to clipboard (in DIFF view)' ). lo_buf->add( ' // (https://stackoverflow.com/questions/6619805/select-text-in-a-column-of-an-html-table)' ). lo_buf->add( ' // Process mousedown event for all TD elements -> apply CSS class at TABLE level.' ). lo_buf->add( ' // (https://stackoverflow.com/questions/40956717/how-to-addeventlistener-to-multiple-elements-in-a-single-line)' ). lo_buf->add( ' var unifiedLineNumColumnIdx = 0;' ). lo_buf->add( ' var unifiedCodeColumnIdx = 3;' ). lo_buf->add( ' var splitLineNumLeftColumnIdx = 0;' ). lo_buf->add( ' var splitCodeLeftColumnIdx = 2;' ). lo_buf->add( ' var splitLineNumRightColumnIdx = 3;' ). lo_buf->add( ' var splitCodeRightColumnIdx = 5;' ). lo_buf->add( '' ). lo_buf->add( ' if (e.button !== 0) return; // function is only valid for left button, not right button' ). lo_buf->add( '' ). lo_buf->add( ' var td = e.target;' ). lo_buf->add( '' ). lo_buf->add( ' while (td != undefined && td.tagName != "TD" && td.tagName != "TBODY") td = td.parentElement;' ). lo_buf->add( ' if (td == undefined) return;' ). lo_buf->add( ' var table = td.parentElement.parentElement;' ). lo_buf->add( '' ). lo_buf->add( ' var patchColumnCount = 0;' ). lo_buf->add( ' if (td.parentElement.cells[0].classList.contains("patch")) {' ). lo_buf->add( ' patchColumnCount = 1;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' if (td.classList.contains("diff_left")) {' ). lo_buf->add( ' table.classList.remove("diff_select_right");' ). lo_buf->add( ' table.classList.add("diff_select_left");' ). lo_buf->add( ' if (window.getSelection() && this.selectedColumnIdx != splitCodeLeftColumnIdx + patchColumnCount) {' ). lo_buf->add( ' // De-select to avoid effect of dragging selection in case the right column was first selected' ). lo_buf->add( ' if (document.body.createTextRange) { // All IE but Edge' ). lo_buf->add( ' // document.getSelection().removeAllRanges() may trigger error' ). lo_buf->add( ' // so use this code which is equivalent but does not fail' ). lo_buf->add( ' // (https://stackoverflow.com/questions/22914075/javascript-error-800a025e-using-range-selector)' ). lo_buf->add( ' range = document.body.createTextRange();' ). lo_buf->add( ' range.collapse();' ). lo_buf->add( ' range.select();' ). lo_buf->add( ' } else {' ). lo_buf->add( ' document.getSelection().removeAllRanges();' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( ' this.selectedColumnIdx = splitCodeLeftColumnIdx + patchColumnCount;' ). lo_buf->add( ' this.lineNumColumnIdx = splitLineNumLeftColumnIdx + patchColumnCount;' ). lo_buf->add( '' ). lo_buf->add( ' } else if (td.classList.contains("diff_right")) {' ). lo_buf->add( ' table.classList.remove("diff_select_left");' ). lo_buf->add( ' table.classList.add("diff_select_right");' ). lo_buf->add( ' if (window.getSelection() && this.selectedColumnIdx != splitCodeRightColumnIdx + patchColumnCount) {' ). lo_buf->add( ' if (document.body.createTextRange) { // All IE but Edge' ). lo_buf->add( ' // document.getSelection().removeAllRanges() may trigger error' ). lo_buf->add( ' // so use this code which is equivalent but does not fail' ). lo_buf->add( ' // (https://stackoverflow.com/questions/22914075/javascript-error-800a025e-using-range-selector)' ). lo_buf->add( ' var range = document.body.createTextRange();' ). lo_buf->add( ' range.collapse();' ). lo_buf->add( ' range.select();' ). lo_buf->add( ' } else {' ). lo_buf->add( ' document.getSelection().removeAllRanges();' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( ' this.selectedColumnIdx = splitCodeRightColumnIdx + patchColumnCount;' ). lo_buf->add( ' this.lineNumColumnIdx = splitLineNumRightColumnIdx + patchColumnCount;' ). lo_buf->add( '' ). lo_buf->add( ' } else if (td.classList.contains("diff_unified")) {' ). lo_buf->add( ' this.selectedColumnIdx = unifiedCodeColumnIdx;' ). lo_buf->add( ' this.lineNumColumnIdx = unifiedLineNumColumnIdx;' ). lo_buf->add( '' ). lo_buf->add( ' } else {' ). lo_buf->add( ' this.selectedColumnIdx = -1;' ). lo_buf->add( ' this.lineNumColumnIdx = -1;' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'DiffColumnSelection.prototype.copyEventListener = function(e) {' ). lo_buf->add( ' // Select text in a column of an HTML table and copy to clipboard (in DIFF view)' ). lo_buf->add( ' // (https://stackoverflow.com/questions/6619805/select-text-in-a-column-of-an-html-table)' ). lo_buf->add( ' var td = e.target;' ). lo_buf->add( '' ). lo_buf->add( ' while (td != undefined && td.tagName != "TD" && td.tagName != "TBODY") td = td.parentElement;' ). lo_buf->add( ' if (td != undefined) {' ). lo_buf->add( ' // Use window.clipboardData instead of e.clipboardData' ). lo_buf->add( ' // (https://stackoverflow.com/questions/23470958/ie-10-copy-paste-issue)' ). lo_buf->add( ' var clipboardData = (e.clipboardData == undefined ? window.clipboardData : e.clipboardData);' ). lo_buf->add( ' var text = this.getSelectedText();' ). lo_buf->add( ' clipboardData.setData("text", text);' ). lo_buf->add( ' e.preventDefault();' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'DiffColumnSelection.prototype.getSelectedText = function() {' ). lo_buf->add( ' // Select text in a column of an HTML table and copy to clipboard (in DIFF view)' ). lo_buf->add( ' // (https://stackoverflow.com/questions/6619805/select-text-in-a-column-of-an-html-table)' ). lo_buf->add( ' var sel = window.getSelection();' ). lo_buf->add( ' var range = sel.getRangeAt(0);' ). lo_buf->add( ' var doc = range.cloneContents();' ). lo_buf->add( ' var nodes = doc.querySelectorAll("tr");' ). lo_buf->add( ' var text = "";' ). lo_buf->add( ' if (nodes.length === 0) {' ). lo_buf->add( ' text = doc.textContent;' ). lo_buf->add( ' } else {' ). lo_buf->add( ' var newline = "";' ). lo_buf->add( ' var realThis = this;' ). lo_buf->add( ' var copySide = "";' ). lo_buf->add( ' [].forEach.call(nodes, function(tr, i) {' ). lo_buf->add( ' var cellIdx = (i == 0 ? 0 : realThis.selectedColumnIdx);' ). lo_buf->add( ' if (tr.cells.length > cellIdx) {' ). lo_buf->add( ' var tdSelected = tr.cells[cellIdx];' ). lo_buf->add( ' // decide which side to copy based on first line of selection' ). lo_buf->add( ' if (i == 0) {' ). lo_buf->add( ' copySide = (tdSelected.classList.contains("new") ? "new" : "old" );' ). lo_buf->add( ' }' ). lo_buf->add( ' // copy is interesting only for one side of code, do not copy lines which exist on other side' ). lo_buf->add( ' if (i == 0 || copySide == "new" && !tdSelected.classList.contains("old") || copySide == "old" && !tdSelected.classList.contains("new")) {' ). lo_buf->add( ' text += newline + tdSelected.textContent;' ). lo_buf->add( ' // special processing for TD tag which sometimes contains newline' ). lo_buf->add( ' // (expl: /src/ui/zabapgit_js_common.w3mi.data.js) so do not add newline again in that case.' ). lo_buf->add( ' var lastChar = tdSelected.textContent[tdSelected.textContent.length - 1];' ). lo_buf->add( ' if (lastChar == "\n") newline = "";' ). lo_buf->add( ' else newline = "\n";' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( ' });' ). lo_buf->add( ' }' ). lo_buf->add( ' return text;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Display Helper' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( '// Toggle display of changelog (news) and message popups' ). lo_buf->add( 'function toggleDisplay(divId) {' ). lo_buf->add( ' var div = document.getElementById(divId);' ). lo_buf->add( '' ). lo_buf->add( ' if (div) div.style.display = (div.style.display) ? "" : "none";' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Keyboard Navigation' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( 'function KeyNavigation() { }' ). lo_buf->add( '' ). lo_buf->add( 'KeyNavigation.prototype.onkeydown = function(event) {' ). lo_buf->add( ' if (event.defaultPrevented) return;' ). lo_buf->add( '' ). lo_buf->add( ' // navigate with arrows through list items and support pressing links with enter and space' ). lo_buf->add( ' var isHandled = false;' ). lo_buf->add( ' if (event.key === "Enter" || event.key === "") {' ). lo_buf->add( ' isHandled = this.onEnterOrSpace();' ). lo_buf->add( ' } else if (/Down$/.test(event.key)) {' ). lo_buf->add( ' isHandled = this.onArrowDown();' ). lo_buf->add( ' } else if (/Up$/.test(event.key)) {' ). lo_buf->add( ' isHandled = this.onArrowUp();' ). lo_buf->add( ' } else if (event.key === "Backspace") {' ). lo_buf->add( ' isHandled = this.onBackspace();' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' if (isHandled) event.preventDefault();' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'KeyNavigation.prototype.onEnterOrSpace = function() {' ). lo_buf->add( ' if (document.activeElement.nodeName !== "A") return;' ). lo_buf->add( ' var anchor = document.activeElement;' ). lo_buf->add( '' ). lo_buf->add( ' if (anchor.href.replace(/#$/, "") === document.location.href.replace(/#$/, "")' ). lo_buf->add( ' && !anchor.onclick' ). lo_buf->add( ' && anchor.parentElement' ). lo_buf->add( ' && anchor.parentElement.nodeName === "LI") {' ). lo_buf->add( ' anchor.parentElement.classList.toggle("force-nav-hover");' ). lo_buf->add( ' } else {' ). lo_buf->add( ' anchor.click();' ). lo_buf->add( ' }' ). lo_buf->add( ' return true;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'KeyNavigation.prototype.focusListItem = function(li) {' ). lo_buf->add( ' var anchor = li.firstElementChild;' ). lo_buf->add( ' if (!anchor || anchor.nodeName !== "A") return false;' ). lo_buf->add( ' anchor.focus();' ). lo_buf->add( ' return true;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'KeyNavigation.prototype.closeDropdown = function(dropdownLi) {' ). lo_buf->add( ' dropdownLi.classList.remove("force-nav-hover");' ). lo_buf->add( ' if (dropdownLi.firstElementChild.nodeName === "A") dropdownLi.firstElementChild.focus();' ). lo_buf->add( ' return true;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'KeyNavigation.prototype.onBackspace = function() {' ). lo_buf->add( ' var activeElement = document.activeElement;' ). lo_buf->add( '' ). lo_buf->add( ' // Detect opened subsequent dropdown' ). lo_buf->add( ' if (activeElement.nodeName === "A"' ). lo_buf->add( ' && activeElement.parentElement' ). lo_buf->add( ' && activeElement.parentElement.nodeName === "LI"' ). lo_buf->add( ' && activeElement.parentElement.classList.contains("force-nav-hover")) {' ). lo_buf->add( ' return this.closeDropdown(activeElement.parentElement);' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' // Detect opened parent dropdown' ). lo_buf->add( ' if (activeElement.nodeName === "A"' ). lo_buf->add( ' && activeElement.parentElement' ). lo_buf->add( ' && activeElement.parentElement.nodeName === "LI"' ). lo_buf->add( ' && activeElement.parentElement.parentElement' ). lo_buf->add( ' && activeElement.parentElement.parentElement.nodeName === "UL"' ). lo_buf->add( ' && activeElement.parentElement.parentElement.parentElement' ). lo_buf->add( ' && activeElement.parentElement.parentElement.parentElement.nodeName === "LI"' ). lo_buf->add( ' && activeElement.parentElement.parentElement.parentElement.classList.contains("force-nav-hover")) {' ). lo_buf->add( ' return this.closeDropdown(activeElement.parentElement.parentElement.parentElement);' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'KeyNavigation.prototype.onArrowDown = function() {' ). lo_buf->add( ' var activeElement = document.activeElement;' ). lo_buf->add( '' ). lo_buf->add( ' // Start of dropdown list: LI > selected A :: UL > LI > A' ). lo_buf->add( ' if (activeElement.nodeName === "A"' ). lo_buf->add( ' && activeElement.parentElement' ). lo_buf->add( ' && activeElement.parentElement.nodeName === "LI"' ). lo_buf->add( ' && activeElement.parentElement.classList.contains("force-nav-hover") // opened dropdown' ). lo_buf->add( ' && activeElement.nextElementSibling' ). lo_buf->add( ' && activeElement.nextElementSibling.nodeName === "UL"' ). lo_buf->add( ' && activeElement.nextElementSibling.firstElementChild' ). lo_buf->add( ' && activeElement.nextElementSibling.firstElementChild.nodeName === "LI") {' ). lo_buf->add( ' return this.focusListItem(activeElement.nextElementSibling.firstElementChild);' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' // Next item of dropdown list: ( LI > selected A ) :: LI > A' ). lo_buf->add( ' if (activeElement.nodeName === "A"' ). lo_buf->add( ' && activeElement.parentElement' ). lo_buf->add( ' && activeElement.parentElement.nodeName === "LI"' ). lo_buf->add( ' && activeElement.parentElement.nextElementSibling' ). lo_buf->add( ' && activeElement.parentElement.nextElementSibling.nodeName === "LI") {' ). lo_buf->add( ' return this.focusListItem(activeElement.parentElement.nextElementSibling);' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'KeyNavigation.prototype.onArrowUp = function() {' ). lo_buf->add( ' var activeElement = document.activeElement;' ). lo_buf->add( '' ). lo_buf->add( ' // Prev item of dropdown list: ( LI > selected A ) <:: LI > A' ). lo_buf->add( ' if (activeElement.nodeName === "A"' ). lo_buf->add( ' && activeElement.parentElement' ). lo_buf->add( ' && activeElement.parentElement.nodeName === "LI"' ). lo_buf->add( ' && activeElement.parentElement.previousElementSibling' ). lo_buf->add( ' && activeElement.parentElement.previousElementSibling.nodeName === "LI") {' ). lo_buf->add( ' return this.focusListItem(activeElement.parentElement.previousElementSibling);' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'KeyNavigation.prototype.getHandler = function() {' ). lo_buf->add( ' return this.onkeydown.bind(this);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// this function enables the navigation with arrows through list items (li)' ). lo_buf->add( '// e.g. in dropdown menus' ). lo_buf->add( 'function enableArrowListNavigation() {' ). lo_buf->add( ' document.addEventListener("keydown", new KeyNavigation().getHandler());' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Link Hints (Vimium-like)' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( 'function LinkHints(linkHintHotKey) {' ). lo_buf->add( ' this.linkHintHotKey = linkHintHotKey;' ). lo_buf->add( ' this.areHintsDisplayed = false;' ). lo_buf->add( ' this.pendingPath = ""; // already typed code prefix' ). lo_buf->add( ' this.hintsMap = this.deployHintContainers();' ). lo_buf->add( ' this.activatedDropdown = null;' ). lo_buf->add( ' this.yankModeActive = false;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'LinkHints.prototype.getHintStartValue = function(targetsCount) {' ). lo_buf->add( ' // e.g. if we have 89 tooltips we start from 10' ). lo_buf->add( ' // if we have 90 tooltips we start from 100' ). lo_buf->add( ' // if we have 900 tooltips we start from 1000' ). lo_buf->add( ' var' ). lo_buf->add( ' baseLength = Math.pow(10, targetsCount.toString().length - 1),' ). lo_buf->add( ' maxHintStringLength = (targetsCount + baseLength).toString().length;' ). lo_buf->add( ' return Math.pow(10, maxHintStringLength - 1);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'LinkHints.prototype.deployHintContainers = function() {' ). lo_buf->add( '' ). lo_buf->add( ' var hintTargets = document.querySelectorAll("a, input, textarea, i");' ). lo_buf->add( ' var codeCounter = this.getHintStartValue(hintTargets.length);' ). lo_buf->add( ' var hintsMap = { first: codeCounter };' ). lo_buf->add( '' ). lo_buf->add( ' // ' ). lo_buf->add( ' // 123' ). lo_buf->add( ' // ' ). lo_buf->add( ' for (var i = 0, N = hintTargets.length; i < N; i++) {' ). lo_buf->add( ' // skip hidden fields' ). lo_buf->add( ' if (hintTargets[i].type === "HIDDEN") {' ). lo_buf->add( ' continue;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' var hint = {};' ). lo_buf->add( '' ). lo_buf->add( ' hint.container = document.createElement("span");' ). lo_buf->add( ' hint.pendingSpan = document.createElement("span");' ). lo_buf->add( ' hint.remainingSpan = document.createElement("span");' ). lo_buf->add( ' hint.parent = hintTargets[i];' ). lo_buf->add( ' hint.code = codeCounter.toString();' ). lo_buf->add( '' ). lo_buf->add( ' hint.container.appendChild(hint.pendingSpan);' ). lo_buf->add( ' hint.container.appendChild(hint.remainingSpan);' ). lo_buf->add( '' ). lo_buf->add( ' hint.pendingSpan.classList.add("pending");' ). lo_buf->add( ' hint.container.classList.add("link-hint");' ). lo_buf->add( ' if (hint.parent.nodeName === "INPUT" || hint.parent.nodeName === "TEXTAREA") {' ). lo_buf->add( ' hint.container.classList.add("link-hint-input");' ). lo_buf->add( ' } else if (hint.parent.nodeName === "A") {' ). lo_buf->add( ' hint.container.classList.add("link-hint-a");' ). lo_buf->add( ' } else if (hint.parent.nodeName === "I" && hint.parent.classList.contains("cursor-pointer")) {' ). lo_buf->add( ' hint.container.classList.add("link-hint-i");' ). lo_buf->add( ' } else {' ). lo_buf->add( ' continue;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' hint.container.classList.add("nodisplay"); // hide by default' ). lo_buf->add( ' hint.container.dataset.code = codeCounter.toString(); // not really needed, more for debug' ). lo_buf->add( '' ). lo_buf->add( ' if (hintTargets[i].nodeName === "INPUT" || hintTargets[i].nodeName === "TEXTAREA") {' ). lo_buf->add( ' // does not work if inside the input node' ). lo_buf->add( ' if (hintTargets[i].type === "checkbox" || hintTargets[i].type === "radio") {' ). lo_buf->add( ' if (hintTargets[i].nextElementSibling && hintTargets[i].nextElementSibling.nodeName === "LABEL") {' ). lo_buf->add( ' // insert at end of label' ). lo_buf->add( ' hintTargets[i].nextElementSibling.appendChild(hint.container);' ). lo_buf->add( ' } else {' ). lo_buf->add( ' // inserting right after' ). lo_buf->add( ' hintTargets[i].insertAdjacentElement("afterend", hint.container);' ). lo_buf->add( ' }' ). lo_buf->add( ' } else {' ). lo_buf->add( ' // inserting right after' ). lo_buf->add( ' hintTargets[i].insertAdjacentElement("afterend", hint.container);' ). lo_buf->add( ' }' ). lo_buf->add( ' } else {' ). lo_buf->add( ' hintTargets[i].appendChild(hint.container);' ). lo_buf->add( ' }' ). lo_buf->add( ' hintsMap[codeCounter++] = hint;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' hintsMap.last = codeCounter - 1;' ). lo_buf->add( ' return hintsMap;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'LinkHints.prototype.getHandler = function() {' ). lo_buf->add( ' return this.handleKey.bind(this);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'LinkHints.prototype.handleKey = function(event) {' ). lo_buf->add( ' if (event.defaultPrevented) {' ). lo_buf->add( ' return;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' if (event.key === "y") {' ). lo_buf->add( ' this.yankModeActive = !this.yankModeActive;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' if (event.key === this.linkHintHotKey && Hotkeys.isHotkeyCallPossible()) {' ). lo_buf->add( '' ). lo_buf->add( ' // on user hide hints, close an opened dropdown too' ). lo_buf->add( ' if (this.areHintsDisplayed && this.activatedDropdown) this.closeActivatedDropdown();' ). lo_buf->add( ' if (this.areHintsDisplayed) this.yankModeActive = false;' ). lo_buf->add( '' ). lo_buf->add( ' this.pendingPath = "";' ). lo_buf->add( ' this.displayHints(!this.areHintsDisplayed);' ). lo_buf->add( '' ). lo_buf->add( ' } else if (this.areHintsDisplayed) {' ). lo_buf->add( '' ). lo_buf->add( ' // the user tries to reach a hint' ). lo_buf->add( ' this.pendingPath += event.key;' ). lo_buf->add( '' ). lo_buf->add( ' var hint = this.hintsMap[this.pendingPath];' ). lo_buf->add( '' ). lo_buf->add( ' if (hint) { // we are there, we have a fully specified tooltip. Let us activate or yank it' ). lo_buf->add( ' this.displayHints(false);' ). lo_buf->add( ' event.preventDefault();' ). lo_buf->add( ' if (this.yankModeActive) {' ). lo_buf->add( ' submitSapeventForm({ clipboard: hint.parent.firstChild.textContent }, "clipboard");' ). lo_buf->add( ' this.yankModeActive = false;' ). lo_buf->add( ' } else {' ). lo_buf->add( ' this.hintActivate(hint);' ). lo_buf->add( ' }' ). lo_buf->add( ' } else {' ). lo_buf->add( ' // we are not there yet, but let us filter the link so that only' ). lo_buf->add( ' // the partially matched are shown' ). lo_buf->add( ' var visibleHints = this.filterHints();' ). lo_buf->add( ' if (!visibleHints) {' ). lo_buf->add( ' this.displayHints(false);' ). lo_buf->add( ' if (this.activatedDropdown) this.closeActivatedDropdown();' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'LinkHints.prototype.closeActivatedDropdown = function() {' ). lo_buf->add( ' if (!this.activatedDropdown) return;' ). lo_buf->add( ' this.activatedDropdown.classList.remove("force-nav-hover");' ). lo_buf->add( ' this.activatedDropdown = null;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'LinkHints.prototype.displayHints = function(isActivate) {' ). lo_buf->add( ' this.areHintsDisplayed = isActivate;' ). lo_buf->add( ' for (var i = this.hintsMap.first; i <= this.hintsMap.last; i++) {' ). lo_buf->add( ' var hint = this.hintsMap[i];' ). lo_buf->add( ' if (isActivate) {' ). lo_buf->add( ' hint.container.classList.remove("nodisplay");' ). lo_buf->add( ' hint.pendingSpan.innerText = "";' ). lo_buf->add( ' hint.remainingSpan.innerText = hint.code;' ). lo_buf->add( ' } else {' ). lo_buf->add( ' hint.container.classList.add("nodisplay");' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'LinkHints.prototype.hintActivate = function(hint) {' ). lo_buf->add( ' if (hint.parent.nodeName === "A"' ). lo_buf->add( ' // hint.parent.href doesn`t have a # at the end while accessing dropdowns the first time.' ). lo_buf->add( ' // Seems like a idiosyncrasy of SAP GUI`s IE. So let`s ignore the last character.' ). lo_buf->add( ' && (hint.parent.href.substr(0, hint.parent.href.length - 1) === document.location.href)// href is #' ). lo_buf->add( ' && !hint.parent.onclick // no handler' ). lo_buf->add( ' && hint.parent.parentElement && hint.parent.parentElement.nodeName === "LI") {' ). lo_buf->add( ' // probably it is a dropdown ...' ). lo_buf->add( ' this.activatedDropdown = hint.parent.parentElement;' ). lo_buf->add( ' this.activatedDropdown.classList.toggle("force-nav-hover");' ). lo_buf->add( ' hint.parent.focus();' ). lo_buf->add( ' } else if (hint.parent.type === "checkbox") {' ). lo_buf->add( ' this.toggleCheckbox(hint);' ). lo_buf->add( ' } else if (hint.parent.type === "radio") {' ). lo_buf->add( ' this.toggleRadioButton(hint);' ). lo_buf->add( ' } else if (hint.parent.type === "submit") {' ). lo_buf->add( ' hint.parent.click();' ). lo_buf->add( ' } else if (hint.parent.nodeName === "INPUT" || hint.parent.nodeName === "TEXTAREA") {' ). lo_buf->add( ' hint.parent.focus();' ). lo_buf->add( ' } else {' ). lo_buf->add( ' hint.parent.click();' ). lo_buf->add( ' if (this.activatedDropdown) this.closeActivatedDropdown();' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'LinkHints.prototype.toggleCheckbox = function(hint) {' ). lo_buf->add( ' var checked = hint.parent.checked;' ). lo_buf->add( ' this.triggerClickHandler(hint.parent.parentElement);' ). lo_buf->add( ' if (checked === hint.parent.checked) {' ). lo_buf->add( ' // fallback if no handler is registered' ). lo_buf->add( ' hint.parent.checked = !hint.parent.checked;' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'LinkHints.prototype.toggleRadioButton = function(hint) {' ). lo_buf->add( ' this.triggerClickHandler(hint.parent);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'LinkHints.prototype.triggerClickHandler = function(el) {' ). lo_buf->add( ' // ensures that onclick handler is executed' ). lo_buf->add( ' // https://stackoverflow.com/questions/41981509/trigger-an-event-when-a-checkbox-is-changed-programmatically-via-javascript' ). lo_buf->add( ' var event = document.createEvent("HTMLEvents");' ). lo_buf->add( ' event.initEvent("click", false, true);' ). lo_buf->add( ' el.dispatchEvent(event);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'LinkHints.prototype.filterHints = function() {' ). lo_buf->add( ' var visibleHints = 0;' ). lo_buf->add( ' for (var i = this.hintsMap.first; i <= this.hintsMap.last; i++) {' ). lo_buf->add( ' var hint = this.hintsMap[i];' ). lo_buf->add( ' if (i.toString().startsWith(this.pendingPath)) {' ). lo_buf->add( ' hint.pendingSpan.innerText = this.pendingPath;' ). lo_buf->add( ' hint.remainingSpan.innerText = hint.code.substring(this.pendingPath.length);' ). lo_buf->add( ' // hint.container.classList.remove("nodisplay"); // for backspace' ). lo_buf->add( ' visibleHints++;' ). lo_buf->add( ' } else {' ). lo_buf->add( ' hint.container.classList.add("nodisplay");' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( ' return visibleHints;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'function activateLinkHints(linkHintHotKey) {' ). lo_buf->add( ' if (!linkHintHotKey) return;' ). lo_buf->add( ' var oLinkHint = new LinkHints(linkHintHotKey);' ). lo_buf->add( ' document.addEventListener("keypress", oLinkHint.getHandler());' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Hotkeys' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( 'function Hotkeys(oKeyMap) {' ). lo_buf->add( ' this.oKeyMap = oKeyMap || {};' ). lo_buf->add( '' ). lo_buf->add( ' // these are the hotkeys provided by the backend' ). lo_buf->add( ' Object.keys(this.oKeyMap).forEach(function(sKey) {' ). lo_buf->add( '' ). lo_buf->add( ' var action = this.oKeyMap[sKey];' ). lo_buf->add( '' ). lo_buf->add( ' // add a tooltip/title with the hotkey, currently only sapevents are supported' ). lo_buf->add( ' this.getAllSapEventsForSapEventName(action).forEach(function(elAnchor) {' ). lo_buf->add( ' elAnchor.title = elAnchor.title + " [" + sKey + "]";' ). lo_buf->add( ' });' ). lo_buf->add( '' ). lo_buf->add( ' // We replace the actions with callback functions to unify' ). lo_buf->add( ' // the hotkey execution' ). lo_buf->add( ' this.oKeyMap[sKey] = function(oEvent) {' ). lo_buf->add( '' ). lo_buf->add( ' // gHelper is only valid for diff page' ). lo_buf->add( ' var diffHelper = (window.gHelper || {});' ). lo_buf->add( '' ). lo_buf->add( ' // We have either a js function on this' ). lo_buf->add( ' if (this[action]) {' ). lo_buf->add( ' this[action].call(this);' ). lo_buf->add( ' return;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' // Or a method of the helper object for the diff page' ). lo_buf->add( ' if (diffHelper[action]) {' ). lo_buf->add( ' diffHelper[action].call(diffHelper);' ). lo_buf->add( ' return;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' // Or a global function' ). lo_buf->add( ' if (window[action] && typeof (window[action]) === "function") {' ). lo_buf->add( ' window[action].call(this);' ). lo_buf->add( ' return;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' // Or a SAP event link' ). lo_buf->add( ' var sUiSapEventHref = this.getSapEventHref(action);' ). lo_buf->add( ' if (sUiSapEventHref) {' ). lo_buf->add( ' submitSapeventForm({}, sUiSapEventHref, "post");' ). lo_buf->add( ' oEvent.preventDefault();' ). lo_buf->add( ' return;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' // Or an SAP event input' ). lo_buf->add( ' var sUiSapEventInputAction = this.getSapEventInputAction(action);' ). lo_buf->add( ' if (sUiSapEventInputAction) {' ). lo_buf->add( ' submitSapeventForm({}, sUiSapEventInputAction, "post");' ). lo_buf->add( ' oEvent.preventDefault();' ). lo_buf->add( ' return;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' // Or an SAP event main form' ). lo_buf->add( ' var elForm = this.getSapEventForm(action);' ). lo_buf->add( ' if (elForm) {' ). lo_buf->add( ' elForm.submit();' ). lo_buf->add( ' oEvent.preventDefault();' ). lo_buf->add( ' return;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' };' ). lo_buf->add( '' ). lo_buf->add( ' }.bind(this));' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'Hotkeys.prototype.showHotkeys = function() {' ). lo_buf->add( ' var elHotkeys = document.querySelector("#hotkeys");' ). lo_buf->add( '' ). lo_buf->add( ' if (elHotkeys) {' ). lo_buf->add( ' elHotkeys.style.display = (elHotkeys.style.display) ? "" : "none";' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Hotkeys.prototype.getAllSapEventsForSapEventName = function (sSapEvent) {' ). lo_buf->add( ' if (/^#+$/.test(sSapEvent)){' ). lo_buf->add( ' // sSapEvent contains only #. Nothing sensible can be done here' ). lo_buf->add( ' return [];' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' var includesSapEvent = function(text){' ). lo_buf->add( ' return (text.includes("sapevent") || text.includes("SAPEVENT"));' ). lo_buf->add( ' };' ). lo_buf->add( '' ). lo_buf->add( ' return [].slice' ). lo_buf->add( ' .call(document.querySelectorAll("a[href*="+ sSapEvent +"], input[formaction*="+ sSapEvent+"]"))' ). lo_buf->add( ' .filter(function (elem) {' ). lo_buf->add( ' return (elem.nodeName === "A" && includesSapEvent(elem.href)' ). lo_buf->add( ' || (elem.nodeName === "INPUT" && includesSapEvent(elem.formAction)));' ). lo_buf->add( ' });' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Hotkeys.prototype.getSapEventHref = function(sSapEvent) {' ). lo_buf->add( ' return this.getAllSapEventsForSapEventName(sSapEvent)' ). lo_buf->add( ' .filter(function(el) {' ). lo_buf->add( ' // only anchors' ). lo_buf->add( ' return (!!el.href);' ). lo_buf->add( ' })' ). lo_buf->add( ' .map(function(oSapEvent) {' ). lo_buf->add( ' return oSapEvent.href;' ). lo_buf->add( ' })' ). lo_buf->add( ' .filter(this.eliminateSapEventFalsePositives(sSapEvent))' ). lo_buf->add( ' .pop();' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Hotkeys.prototype.getSapEventInputAction = function(sSapEvent) {' ). lo_buf->add( ' return this.getAllSapEventsForSapEventName(sSapEvent)' ). lo_buf->add( ' .filter(function(el) {' ). lo_buf->add( ' // input forms' ). lo_buf->add( ' return (el.type === "submit");' ). lo_buf->add( ' })' ). lo_buf->add( ' .map(function(oSapEvent) {' ). lo_buf->add( ' return oSapEvent.formAction;' ). lo_buf->add( ' })' ). lo_buf->add( ' .filter(this.eliminateSapEventFalsePositives(sSapEvent))' ). lo_buf->add( ' .pop();' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Hotkeys.prototype.getSapEventForm = function(sSapEvent) {' ). lo_buf->add( ' return this.getAllSapEventsForSapEventName(sSapEvent)' ). lo_buf->add( ' .filter(function(el) {' ). lo_buf->add( ' // forms' ). lo_buf->add( ' var parentForm = el.parentNode.parentNode.parentNode;' ). lo_buf->add( ' return (el.type === "submit" && parentForm.nodeName === "FORM");' ). lo_buf->add( ' })' ). lo_buf->add( ' .map(function(oSapEvent) {' ). lo_buf->add( ' return oSapEvent.parentNode.parentNode.parentNode;' ). lo_buf->add( ' })' ). lo_buf->add( ' .pop();' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Hotkeys.prototype.eliminateSapEventFalsePositives = function(sapEvent) {' ). lo_buf->add( ' return function(sapEventAttr) {' ). lo_buf->add( ' return sapEventAttr.match(new RegExp("\\b" + sapEvent + "\\b"));' ). lo_buf->add( ' };' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Hotkeys.prototype.onkeydown = function(oEvent) {' ). lo_buf->add( ' if (oEvent.defaultPrevented) {' ). lo_buf->add( ' return;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' if (!Hotkeys.isHotkeyCallPossible()) {' ). lo_buf->add( ' return;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' var' ). lo_buf->add( ' sKey = oEvent.key || String.fromCharCode(oEvent.keyCode),' ). lo_buf->add( ' fnHotkey = this.oKeyMap[sKey];' ). lo_buf->add( '' ). lo_buf->add( ' if (fnHotkey) {' ). lo_buf->add( ' fnHotkey.call(this, oEvent);' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Hotkeys.isHotkeyCallPossible = function() {' ). lo_buf->add( ' var activeElementType = ((document.activeElement && document.activeElement.nodeName) || "");' ). lo_buf->add( ' var activeElementReadOnly = ((document.activeElement && document.activeElement.readOnly) || false);' ). lo_buf->add( '' ). lo_buf->add( ' return (activeElementReadOnly || (activeElementType !== "INPUT" && activeElementType !== "TEXTAREA"));' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Hotkeys.addHotkeyToHelpSheet = function(key, description) {' ). lo_buf->add( ' var hotkeysUl = document.querySelector("#hotkeys ul.hotkeys");' ). lo_buf->add( ' if (!hotkeysUl) return;' ). lo_buf->add( '' ). lo_buf->add( ' var li = document.createElement("li");' ). lo_buf->add( ' var spanId = document.createElement("span");' ). lo_buf->add( ' var spanDescr = document.createElement("span");' ). lo_buf->add( '' ). lo_buf->add( ' spanId.className = "key-id";' ). lo_buf->add( ' spanId.innerText = key;' ). lo_buf->add( ' spanDescr.className = "key-descr";' ). lo_buf->add( ' spanDescr.innerText = description;' ). lo_buf->add( ' li.appendChild(spanId);' ). lo_buf->add( ' li.appendChild(spanDescr);' ). lo_buf->add( '' ). lo_buf->add( ' hotkeysUl.appendChild(li);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'function setKeyBindings(oKeyMap) {' ). lo_buf->add( ' var oHotkeys = new Hotkeys(oKeyMap);' ). lo_buf->add( '' ). lo_buf->add( ' document.addEventListener("keypress", oHotkeys.onkeydown.bind(oHotkeys));' ). lo_buf->add( ' setTimeout(function() {' ). lo_buf->add( ' var div = document.getElementById("hotkeys-hint");' ). lo_buf->add( ' if (div) div.style.opacity = 0.2;' ). lo_buf->add( ' }, 4900);' ). lo_buf->add( ' setTimeout(function() { toggleDisplay("hotkeys-hint") }, 5000);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Patch Logic (git add -p)' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( '/*' ). lo_buf->add( ' We have three type of cascading checkboxes.' ). lo_buf->add( ' Which means that by clicking a file or section checkbox all corresponding line checkboxes are checked.' ). lo_buf->add( '' ). lo_buf->add( ' The id of the checkbox indicates its semantics and its membership.' ). lo_buf->add( '*/' ). lo_buf->add( '' ). lo_buf->add( '/*' ). lo_buf->add( ' 1) file links' ). lo_buf->add( '' ). lo_buf->add( ' example id of file link' ). lo_buf->add( '' ). lo_buf->add( ' patch_file_zcl_abapgit_user_exit.clas.abap' ). lo_buf->add( ' \________/ \_____________________________/' ). lo_buf->add( ' | |' ). lo_buf->add( ' | |____ file name' ). lo_buf->add( ' |' ). lo_buf->add( ' |' ). lo_buf->add( ' |' ). lo_buf->add( ' constant prefix' ). lo_buf->add( '*/' ). lo_buf->add( '' ). lo_buf->add( 'function PatchFile(sId) {' ). lo_buf->add( ' var oRegex = new RegExp("(" + this.ID + ")_(.*$)");' ). lo_buf->add( ' var oMatch = sId.match(oRegex);' ). lo_buf->add( '' ). lo_buf->add( ' this.id = sId;' ). lo_buf->add( ' this.prefix = oMatch[1];' ). lo_buf->add( ' this.file_name = oMatch[2];' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'PatchFile.prototype.ID = "patch_file";' ). lo_buf->add( '' ). lo_buf->add( '/*' ). lo_buf->add( ' 2) section links within a file' ). lo_buf->add( '' ). lo_buf->add( ' example id of section link' ). lo_buf->add( '' ). lo_buf->add( ' patch_section_zcl_abapgit_user_exit.clas.abap_1' ). lo_buf->add( ' \___________/ \_____________________________/ ^' ). lo_buf->add( ' | | |' ). lo_buf->add( ' | file name |' ). lo_buf->add( ' | |' ). lo_buf->add( ' | ------ section' ). lo_buf->add( ' |' ). lo_buf->add( ' constant prefix' ). lo_buf->add( '*/' ). lo_buf->add( '' ). lo_buf->add( 'function PatchSection(sId) {' ). lo_buf->add( ' var oRegex = new RegExp("(" + this.ID + ")_(.*)_(\\d+$)");' ). lo_buf->add( ' var oMatch = sId.match(oRegex);' ). lo_buf->add( '' ). lo_buf->add( ' this.id = sId;' ). lo_buf->add( ' this.prefix = oMatch[1];' ). lo_buf->add( ' this.file_name = oMatch[2];' ). lo_buf->add( ' this.section = oMatch[3];' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'PatchSection.prototype.ID = "patch_section";' ). lo_buf->add( '' ). lo_buf->add( '/*' ). lo_buf->add( ' 3) line links within a section' ). lo_buf->add( '' ). lo_buf->add( ' example id of line link' ). lo_buf->add( '' ). lo_buf->add( ' patch_line_zcl_abapgit_user_exit.clas.abap_1_25' ). lo_buf->add( ' \________/ \_____________________________/ ^ ^' ). lo_buf->add( ' ^ ^ | |' ). lo_buf->add( ' | | | ------- line number' ). lo_buf->add( ' | file name |' ). lo_buf->add( ' | section' ). lo_buf->add( ' |' ). lo_buf->add( ' |' ). lo_buf->add( ' constant prefix' ). lo_buf->add( '*/' ). lo_buf->add( '' ). lo_buf->add( 'function PatchLine() { }' ). lo_buf->add( '' ). lo_buf->add( 'PatchLine.prototype.ID = "patch_line";' ). lo_buf->add( '' ). lo_buf->add( 'function Patch() { }' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.ID = {' ). lo_buf->add( ' STAGE: "stage"' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.ACTION = {' ). lo_buf->add( ' PATCH_STAGE : "patch_stage",' ). lo_buf->add( ' REFRESH_LOCAL: "refresh_local",' ). lo_buf->add( ' REFRESH_ALL : "refresh_all"' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.escape = function(sFileName) {' ). lo_buf->add( ' return sFileName' ). lo_buf->add( ' .replace(/\./g, "\\.")' ). lo_buf->add( ' .replace(/#/g, "\\#");' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.preparePatch = function() {' ). lo_buf->add( ' this.registerClickHandlerForFiles();' ). lo_buf->add( ' this.registerClickHandlerForSections();' ). lo_buf->add( ' this.registerClickHandlerForLines();' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.buildSelectorInputStartsWithId = function(sId) {' ). lo_buf->add( ' return "input[id^=''" + sId + "'']";' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.registerClickHandlerForFiles = function() {' ). lo_buf->add( ' this.registerClickHandlerForSelectorParent(this.buildSelectorInputStartsWithId(PatchFile.prototype.ID), this.onClickFileCheckbox);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.registerClickHandlerForSections = function() {' ). lo_buf->add( ' this.registerClickHandlerForSelectorParent(this.buildSelectorInputStartsWithId(PatchSection.prototype.ID), this.onClickSectionCheckbox);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.registerClickHandlerForLines = function() {' ). lo_buf->add( ' this.registerClickHandlerForSelectorParent(this.buildSelectorInputStartsWithId(PatchLine.prototype.ID), this.onClickLineCheckbox);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.registerClickHandlerForSelectorParent = function(sSelector, fnCallback) {' ). lo_buf->add( ' var elAll = document.querySelectorAll(sSelector);' ). lo_buf->add( '' ). lo_buf->add( ' [].forEach.call(elAll, function(elem) {' ). lo_buf->add( ' elem.parentElement.addEventListener("click", fnCallback.bind(this));' ). lo_buf->add( ' }.bind(this));' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.getAllLineCheckboxesForFile = function(oFile) {' ). lo_buf->add( ' return this.getAllLineCheckboxesForId(oFile.id, PatchFile.prototype.ID);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.getAllSectionCheckboxesForFile = function(oFile) {' ). lo_buf->add( ' return this.getAllSectionCheckboxesForId(oFile.id, PatchFile.prototype.ID);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.getAllLineCheckboxesForSection = function(oSection) {' ). lo_buf->add( ' return this.getAllLineCheckboxesForId(oSection.id, PatchSection.prototype.ID);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.getAllLineCheckboxesForId = function(sId, sIdPrefix) {' ). lo_buf->add( ' return this.getAllCheckboxesForId(sId, sIdPrefix, PatchLine.prototype.ID);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.getAllSectionCheckboxesForId = function(sId, sIdPrefix) {' ). lo_buf->add( ' return this.getAllCheckboxesForId(sId, sIdPrefix, PatchSection.prototype.ID);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.getAllCheckboxesForId = function(sId, sIdPrefix, sNewIdPrefix) {' ). lo_buf->add( ' var oRegex = new RegExp("^" + sIdPrefix);' ). lo_buf->add( '' ). lo_buf->add( ' sId = sId.replace(oRegex, sNewIdPrefix);' ). lo_buf->add( ' return document.querySelectorAll(this.buildSelectorInputStartsWithId(this.escape(sId)));' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.getToggledCheckbox = function(oEvent) {' ). lo_buf->add( ' var elCheckbox = null;' ). lo_buf->add( '' ). lo_buf->add( ' // We have either an input element or any element with input child' ). lo_buf->add( ' // in the latter case we have to toggle the checkbox manually' ). lo_buf->add( ' if (oEvent.srcElement.nodeName === "INPUT") {' ). lo_buf->add( ' elCheckbox = oEvent.srcElement;' ). lo_buf->add( ' } else {' ). lo_buf->add( ' elCheckbox = this.toggleCheckbox(oEvent.srcElement.querySelector("INPUT"));' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' return elCheckbox;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.toggleCheckbox = function(elCheckbox) {' ). lo_buf->add( ' elCheckbox.checked = !elCheckbox.checked;' ). lo_buf->add( ' return elCheckbox;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.onClickFileCheckbox = function(oEvent) {' ). lo_buf->add( ' var elCheckbox = this.getToggledCheckbox(oEvent);' ). lo_buf->add( ' var oFile = new PatchFile(elCheckbox.id);' ). lo_buf->add( ' var elAllLineCheckboxesOfFile = this.getAllLineCheckboxesForFile(oFile);' ). lo_buf->add( ' var elAllSectionCheckboxesOfFile = this.getAllSectionCheckboxesForFile(oFile);' ). lo_buf->add( '' ). lo_buf->add( ' [].forEach.call(elAllLineCheckboxesOfFile, function(elem) {' ). lo_buf->add( ' elem.checked = elCheckbox.checked;' ). lo_buf->add( ' }.bind(this));' ). lo_buf->add( '' ). lo_buf->add( ' [].forEach.call(elAllSectionCheckboxesOfFile, function(elem) {' ). lo_buf->add( ' elem.checked = elCheckbox.checked;' ). lo_buf->add( ' }.bind(this));' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.onClickSectionCheckbox = function(oEvent) {' ). lo_buf->add( ' var elSrcElement = this.getToggledCheckbox(oEvent);' ). lo_buf->add( ' var oSection = new PatchSection(elSrcElement.id);' ). lo_buf->add( ' this.clickAllLineCheckboxesInSection(oSection, elSrcElement.checked);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.onClickLineCheckbox = function(oEvent) {' ). lo_buf->add( ' this.getToggledCheckbox(oEvent);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.clickAllLineCheckboxesInSection = function(oSection, bChecked) {' ). lo_buf->add( ' var elAllLineCheckboxesOfSection = this.getAllLineCheckboxesForSection(oSection);' ). lo_buf->add( '' ). lo_buf->add( ' [].forEach.call(elAllLineCheckboxesOfSection, function(elem) {' ). lo_buf->add( ' elem.checked = bChecked;' ). lo_buf->add( ' }.bind(this));' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.registerStagePatch = function() {' ). lo_buf->add( ' var elStage = document.querySelector("#" + this.ID.STAGE);' ). lo_buf->add( ' var REFRESH_PREFIX = "refresh";' ). lo_buf->add( '' ). lo_buf->add( ' elStage.addEventListener("click", this.submitPatch.bind(this, this.ACTION.PATCH_STAGE));' ). lo_buf->add( '' ). lo_buf->add( ' var aRefresh = document.querySelectorAll("[id*=" + REFRESH_PREFIX + "]");' ). lo_buf->add( ' [].forEach.call(aRefresh, function(el) {' ). lo_buf->add( ' el.addEventListener("click", memorizeScrollPosition(this.submitPatch.bind(this, el.id)).bind(this));' ). lo_buf->add( ' }.bind(this));' ). lo_buf->add( '' ). lo_buf->add( ' // for hotkeys' ). lo_buf->add( ' window.stagePatch = function() {' ). lo_buf->add( ' this.submitPatch(this.ACTION.PATCH_STAGE);' ). lo_buf->add( ' }.bind(this);' ). lo_buf->add( '' ). lo_buf->add( ' window.refreshLocal = memorizeScrollPosition(function() {' ). lo_buf->add( ' this.submitPatch(this.ACTION.REFRESH_LOCAL);' ). lo_buf->add( ' }.bind(this));' ). lo_buf->add( '' ). lo_buf->add( ' window.refreshAll = memorizeScrollPosition(function() {' ). lo_buf->add( ' this.submitPatch(this.ACTION.REFRESH_ALL);' ). lo_buf->add( ' }.bind(this));' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.submitPatch = function(action) {' ). lo_buf->add( ' // Collect add and remove info and submit to backend' ). lo_buf->add( ' var aAddPatch = this.collectElementsForCheckboxId(PatchLine.prototype.ID, true);' ). lo_buf->add( ' var aRemovePatch = this.collectElementsForCheckboxId(PatchLine.prototype.ID, false);' ). lo_buf->add( '' ). lo_buf->add( ' submitSapeventForm({ add: aAddPatch, remove: aRemovePatch }, action, "post");' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'Patch.prototype.collectElementsForCheckboxId = function(sId, bChecked) {' ). lo_buf->add( ' var sSelector = this.buildSelectorInputStartsWithId(sId);' ). lo_buf->add( '' ). lo_buf->add( ' return [].slice.call(document.querySelectorAll(sSelector))' ). lo_buf->add( ' .filter(function(elem) {' ). lo_buf->add( ' return (elem.checked === bChecked);' ). lo_buf->add( ' }).map(function(elem) {' ). lo_buf->add( ' return elem.id;' ). lo_buf->add( ' });' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'function preparePatch() {' ). lo_buf->add( ' var oPatch = new Patch();' ). lo_buf->add( ' oPatch.preparePatch();' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'function registerStagePatch() {' ). lo_buf->add( ' var oPatch = new Patch();' ). lo_buf->add( ' oPatch.registerStagePatch();' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Command Palette (F1)' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( '// fuzzy match helper' ). lo_buf->add( '// return non empty marked string in case it fits the filter' ). lo_buf->add( '// abc + b = abc' ). lo_buf->add( 'function fuzzyMatchAndMark(str, filter) {' ). lo_buf->add( ' var markedStr = "";' ). lo_buf->add( ' var filterLower = filter.toLowerCase();' ). lo_buf->add( ' var strLower = str.toLowerCase();' ). lo_buf->add( ' var cur = 0;' ). lo_buf->add( '' ). lo_buf->add( ' for (var i = 0; i < filter.length; i++) {' ). lo_buf->add( ' while (filterLower[i] !== strLower[cur] && cur < str.length) {' ). lo_buf->add( ' markedStr += str[cur++];' ). lo_buf->add( ' }' ). lo_buf->add( ' if (cur === str.length) break;' ). lo_buf->add( ' markedStr += "" + str[cur++] + "";' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' var matched = i === filter.length;' ). lo_buf->add( '' ). lo_buf->add( ' if (matched && cur < str.length) markedStr += str.substring(cur);' ). lo_buf->add( ' return matched ? markedStr: null;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'function CommandPalette(commandEnumerator, opts) {' ). lo_buf->add( ' if (typeof commandEnumerator !== "function") throw Error("commandEnumerator must be a function");' ). lo_buf->add( ' if (typeof opts !== "object") throw Error("opts must be an object");' ). lo_buf->add( ' if (typeof opts.toggleKey !== "string" || !opts.toggleKey) throw Error("toggleKey must be a string");' ). lo_buf->add( ' this.commands = commandEnumerator();' ). lo_buf->add( ' if (!this.commands) return;' ). lo_buf->add( ' // this.commands = [{' ). lo_buf->add( ' // action: "sap_event_action_code_with_params"' ). lo_buf->add( ' // iconClass: "icon icon_x ..."' ). lo_buf->add( ' // title: "my command X"' ). lo_buf->add( ' // }, ...];' ). lo_buf->add( '' ). lo_buf->add( ' if (opts.toggleKey[0] === "^") {' ). lo_buf->add( ' this.toggleKeyCtrl = true;' ). lo_buf->add( ' this.toggleKey = opts.toggleKey.substring(1);' ). lo_buf->add( ' if (!this.toggleKey) throw Error("Incorrect toggleKey");' ). lo_buf->add( ' } else {' ). lo_buf->add( ' this.toggleKeyCtrl = false;' ). lo_buf->add( ' this.toggleKey = opts.toggleKey;' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' this.hotkeyDescription = opts.hotkeyDescription;' ). lo_buf->add( ' this.elements = {' ). lo_buf->add( ' palette: null,' ). lo_buf->add( ' ul : null,' ). lo_buf->add( ' input : null' ). lo_buf->add( ' };' ). lo_buf->add( ' this.selectIndex = -1; // not selected' ). lo_buf->add( ' this.filter = "";' ). lo_buf->add( ' this.renderAndBindElements();' ). lo_buf->add( ' this.hookEvents();' ). lo_buf->add( ' Hotkeys.addHotkeyToHelpSheet(opts.toggleKey, opts.hotkeyDescription);' ). lo_buf->add( '' ). lo_buf->add( ' if (!CommandPalette.instances) {' ). lo_buf->add( ' CommandPalette.instances = [];' ). lo_buf->add( ' }' ). lo_buf->add( ' CommandPalette.instances.push(this);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.hookEvents = function() {' ). lo_buf->add( ' document.addEventListener("keydown", this.handleToggleKey.bind(this));' ). lo_buf->add( ' this.elements.input.addEventListener("keyup", this.handleInputKey.bind(this));' ). lo_buf->add( ' this.elements.ul.addEventListener("click", this.handleUlClick.bind(this));' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.renderCommandItem = function(cmd) {' ). lo_buf->add( ' var li = document.createElement("li");' ). lo_buf->add( ' if (cmd.iconClass) {' ). lo_buf->add( ' var icon = document.createElement("i");' ). lo_buf->add( '' ). lo_buf->add( ' icon.className = cmd.iconClass;' ). lo_buf->add( ' li.appendChild(icon);' ). lo_buf->add( ' }' ). lo_buf->add( ' var titleSpan = document.createElement("span");' ). lo_buf->add( ' li.appendChild(titleSpan);' ). lo_buf->add( ' cmd.element = li;' ). lo_buf->add( ' cmd.titleSpan = titleSpan;' ). lo_buf->add( ' return li;' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.renderAndBindElements = function() {' ). lo_buf->add( ' var div = document.createElement("div");' ). lo_buf->add( ' var input = document.createElement("input");' ). lo_buf->add( ' var ul = document.createElement("ul");' ). lo_buf->add( '' ). lo_buf->add( ' div.className = "cmd-palette";' ). lo_buf->add( ' div.style.display = "none";' ). lo_buf->add( ' input.placeholder = this.hotkeyDescription;' ). lo_buf->add( ' for (var i = 0; i < this.commands.length; i++) ul.appendChild(this.renderCommandItem(this.commands[i]));' ). lo_buf->add( ' div.appendChild(input);' ). lo_buf->add( ' div.appendChild(ul);' ). lo_buf->add( '' ). lo_buf->add( ' this.elements.palette = div;' ). lo_buf->add( ' this.elements.input = input;' ). lo_buf->add( ' this.elements.ul = ul;' ). lo_buf->add( ' document.body.appendChild(div);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.handleToggleKey = function(event) {' ). lo_buf->add( ' if (event.key !== this.toggleKey) return;' ). lo_buf->add( ' if (this.toggleKeyCtrl && !event.ctrlKey) return;' ). lo_buf->add( ' this.toggleDisplay();' ). lo_buf->add( ' event.preventDefault();' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.handleInputKey = function(event) {' ). lo_buf->add( ' if (event.key === "ArrowUp" || event.key === "Up") {' ). lo_buf->add( ' this.selectPrev();' ). lo_buf->add( ' } else if (event.key === "ArrowDown" || event.key === "Down") {' ). lo_buf->add( ' this.selectNext();' ). lo_buf->add( ' } else if (event.key === "Enter") {' ). lo_buf->add( ' this.exec(this.getSelected());' ). lo_buf->add( ' } else if (event.key === "Backspace" && !this.filter) {' ). lo_buf->add( ' this.toggleDisplay(false);' ). lo_buf->add( ' } else if (this.filter !== this.elements.input.value) {' ). lo_buf->add( ' this.filter = this.elements.input.value;' ). lo_buf->add( ' this.applyFilter();' ). lo_buf->add( ' this.selectFirst();' ). lo_buf->add( ' }' ). lo_buf->add( ' event.preventDefault();' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.applyFilter = function() {' ). lo_buf->add( ' for (var i = 0; i < this.commands.length; i++) {' ). lo_buf->add( ' var cmd = this.commands[i];' ). lo_buf->add( ' if (!this.filter) {' ). lo_buf->add( ' cmd.element.style.display = "";' ). lo_buf->add( ' cmd.titleSpan.innerText = cmd.title;' ). lo_buf->add( ' } else {' ). lo_buf->add( ' var matchedTitle = fuzzyMatchAndMark(cmd.title, this.filter);' ). lo_buf->add( ' if (matchedTitle) {' ). lo_buf->add( ' cmd.titleSpan.innerHTML = matchedTitle;' ). lo_buf->add( ' cmd.element.style.display = "";' ). lo_buf->add( ' } else {' ). lo_buf->add( ' cmd.element.style.display = "none";' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.applySelectIndex = function(newIndex) {' ). lo_buf->add( ' if (newIndex !== this.selectIndex) {' ). lo_buf->add( ' if (this.selectIndex >= 0) this.commands[this.selectIndex].element.classList.remove("selected");' ). lo_buf->add( ' var newCmd = this.commands[newIndex];' ). lo_buf->add( ' newCmd.element.classList.add("selected");' ). lo_buf->add( ' this.selectIndex = newIndex;' ). lo_buf->add( ' this.adjustScrollPosition(newCmd.element);' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.selectFirst = function() {' ). lo_buf->add( ' for (var i = 0; i < this.commands.length; i++) {' ). lo_buf->add( ' if (this.commands[i].element.style.display === "none") continue; // skip hidden' ). lo_buf->add( ' this.applySelectIndex(i);' ). lo_buf->add( ' break;' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.selectNext = function() {' ). lo_buf->add( ' for (var i = this.selectIndex + 1; i < this.commands.length; i++) {' ). lo_buf->add( ' if (this.commands[i].element.style.display === "none") continue; // skip hidden' ). lo_buf->add( ' this.applySelectIndex(i);' ). lo_buf->add( ' break;' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.selectPrev = function() {' ). lo_buf->add( ' for (var i = this.selectIndex - 1; i >= 0; i--) {' ). lo_buf->add( ' if (this.commands[i].element.style.display === "none") continue; // skip hidden' ). lo_buf->add( ' this.applySelectIndex(i);' ). lo_buf->add( ' break;' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.getSelected = function() {' ). lo_buf->add( ' return this.commands[this.selectIndex];' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.adjustScrollPosition = function(itemElement) {' ). lo_buf->add( ' var bItem = itemElement.getBoundingClientRect();' ). lo_buf->add( ' var bContainer = this.elements.ul.getBoundingClientRect();' ). lo_buf->add( '' ). lo_buf->add( ' bItem.top = Math.round(bItem.top);' ). lo_buf->add( ' bItem.bottom = Math.round(bItem.bottom);' ). lo_buf->add( ' bItem.height = Math.round(bItem.height);' ). lo_buf->add( ' bItem.mid = Math.round(bItem.top + bItem.height / 2);' ). lo_buf->add( ' bContainer.top = Math.round(bContainer.top);' ). lo_buf->add( ' bContainer.bottom = Math.round(bContainer.bottom);' ). lo_buf->add( '' ). lo_buf->add( ' if (bItem.mid > bContainer.bottom - 2) {' ). lo_buf->add( ' this.elements.ul.scrollTop += bItem.bottom - bContainer.bottom;' ). lo_buf->add( ' } else if (bItem.mid < bContainer.top + 2) {' ). lo_buf->add( ' this.elements.ul.scrollTop += bItem.top - bContainer.top;' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.toggleDisplay = function(forceState) {' ). lo_buf->add( ' var isDisplayed = (this.elements.palette.style.display !== "none");' ). lo_buf->add( ' var tobeDisplayed = (forceState !== undefined) ? forceState : !isDisplayed;' ). lo_buf->add( '' ). lo_buf->add( ' if (tobeDisplayed) {' ). lo_buf->add( ' // auto close other command palettes' ). lo_buf->add( ' CommandPalette.instances.forEach(function(instance) {' ). lo_buf->add( ' instance.elements.palette.style.display = "none";' ). lo_buf->add( ' });' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' this.elements.palette.style.display = tobeDisplayed ? "" : "none";' ). lo_buf->add( ' if (tobeDisplayed) {' ). lo_buf->add( ' this.elements.input.value = "";' ). lo_buf->add( ' this.elements.input.focus();' ). lo_buf->add( ' this.applyFilter();' ). lo_buf->add( ' this.selectFirst();' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.getCommandByElement = function(element) {' ). lo_buf->add( ' for (var i = 0; i < this.commands.length; i++) {' ). lo_buf->add( ' if (this.commands[i].element === element) return this.commands[i];' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.handleUlClick = function(event) {' ). lo_buf->add( ' var element = event.target || event.srcElement;' ). lo_buf->add( ' if (!element) return;' ). lo_buf->add( ' if (element.nodeName === "SPAN") element = element.parentNode;' ). lo_buf->add( '' ). lo_buf->add( ' if (element.nodeName === "I") element = element.parentNode;' ). lo_buf->add( '' ). lo_buf->add( ' if (element.nodeName !== "LI") return;' ). lo_buf->add( ' this.exec(this.getCommandByElement(element));' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( 'CommandPalette.prototype.exec = function(cmd) {' ). lo_buf->add( ' if (!cmd) return;' ). lo_buf->add( ' this.toggleDisplay(false);' ). lo_buf->add( ' if (typeof cmd.action === "function") {' ). lo_buf->add( ' cmd.action();' ). lo_buf->add( ' } else {' ). lo_buf->add( ' submitSapeventForm(null, cmd.action);' ). lo_buf->add( ' }' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '// Is any command palette visible?' ). lo_buf->add( 'CommandPalette.isVisible = function() {' ). lo_buf->add( ' return CommandPalette.instances.reduce(function(result, instance) { return result || instance.elements.palette.style.display !== "none" }, false);' ). lo_buf->add( '};' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Command Enumerators' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( 'function createRepoCatalogEnumerator(catalog, action) {' ). lo_buf->add( ' // expecting [{ key, isOffline, displayName }]' ). lo_buf->add( ' return function() {' ). lo_buf->add( ' return catalog.map(function(i) {' ). lo_buf->add( ' return {' ). lo_buf->add( ' action : action + "?key=" + i.key,' ). lo_buf->add( ' iconClass: i.isOffline ? "icon icon-plug darkgrey" : "icon icon-cloud-upload-alt blue",' ). lo_buf->add( ' title : i.displayName' ). lo_buf->add( ' };' ). lo_buf->add( ' });' ). lo_buf->add( ' };' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'function enumerateUiActions() {' ). lo_buf->add( ' var items = [];' ). lo_buf->add( ' function processUL(ulNode, prefix) {' ). lo_buf->add( ' for (var i = 0; i < ulNode.children.length; i++) {' ). lo_buf->add( ' var item = ulNode.children[i];' ). lo_buf->add( ' if (item.nodeName !== "LI") continue; // unexpected node' ). lo_buf->add( ' if (item.children.length >= 2 && item.children[1].nodeName === "UL") {' ). lo_buf->add( ' // submenu detected' ). lo_buf->add( ' var menutext = item.children[0].innerText;' ). lo_buf->add( ' // special treatment for menus without text' ). lo_buf->add( ' if (!menutext) {' ). lo_buf->add( ' menutext = item.children[0].getAttribute("title");' ). lo_buf->add( ' }' ). lo_buf->add( ' processUL(item.children[1], menutext);' ). lo_buf->add( ' } else if (item.firstElementChild && item.firstElementChild.nodeName === "A") {' ). lo_buf->add( ' var anchor = item.firstElementChild;' ). lo_buf->add( ' if (anchor.href && anchor.href !== "#") items.push([anchor, prefix]);' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' // toolbars and actionbars' ). lo_buf->add( ' [].slice.call(document.querySelectorAll(".nav-container > ul[id*=toolbar], .nav-container > ul[id*=actionbar]"))' ). lo_buf->add( ' .filter(function(toolbar) {' ). lo_buf->add( ' return (toolbar && toolbar.nodeName === "UL");' ). lo_buf->add( ' }).forEach(function(toolbar) {' ). lo_buf->add( ' processUL(toolbar);' ). lo_buf->add( ' });' ). lo_buf->add( '' ). lo_buf->add( ' items = items.map(function(item) {' ). lo_buf->add( ' var action = "";' ). lo_buf->add( ' var anchor = item[0];' ). lo_buf->add( ' if (anchor.href.includes("#")) {' ). lo_buf->add( ' action = function() {' ). lo_buf->add( ' anchor.click();' ). lo_buf->add( ' };' ). lo_buf->add( ' } else {' ). lo_buf->add( ' action = anchor.href.replace("sapevent:", "");' ). lo_buf->add( ' }' ). lo_buf->add( ' var prefix = item[1];' ). lo_buf->add( ' return {' ). lo_buf->add( ' action: action,' ). lo_buf->add( ' title : (prefix ? prefix + ": " : "") + anchor.innerText.trim()' ). lo_buf->add( ' };' ). lo_buf->add( ' });' ). lo_buf->add( '' ). lo_buf->add( ' // forms' ). lo_buf->add( ' [].slice.call(document.querySelectorAll("input[type=''submit'']"))' ). lo_buf->add( ' .forEach(function(input) {' ). lo_buf->add( ' items.push({' ). lo_buf->add( ' action: function() {' ). lo_buf->add( ' if (input.form.action.includes(input.formAction) || input.classList.contains("main")) {' ). lo_buf->add( ' input.form.submit();' ). lo_buf->add( ' } else {' ). lo_buf->add( ' submitSapeventForm({}, input.formAction, "post", input.form);' ). lo_buf->add( ' }' ). lo_buf->add( ' },' ). lo_buf->add( ' title: (input.value === "Submit Query" ? input.title : input.value + " " + input.title.replace(/\[.*\]/, ""))' ). lo_buf->add( ' });' ). lo_buf->add( ' });' ). lo_buf->add( '' ). lo_buf->add( ' // radio buttons' ). lo_buf->add( ' [].slice.call(document.querySelectorAll("input[type=''radio'']"))' ). lo_buf->add( ' .forEach(function(input) {' ). lo_buf->add( ' items.push({' ). lo_buf->add( ' action: function() {' ). lo_buf->add( ' input.click();' ). lo_buf->add( ' },' ). lo_buf->add( ' title: document.querySelector("label[for=''" + input.id + "'']").textContent' ). lo_buf->add( ' });' ). lo_buf->add( ' });' ). lo_buf->add( '' ). lo_buf->add( ' // others:' ). lo_buf->add( ' // - links inside forms' ). lo_buf->add( ' // - label links' ). lo_buf->add( ' // - command links' ). lo_buf->add( ' // - other header links' ). lo_buf->add( ' [].slice.call(document.querySelectorAll("form a, a.command:not(.unlisted), #header ul:not([id*=''toolbar'']) a"))' ). lo_buf->add( ' .filter(function(anchor) {' ). lo_buf->add( ' return !!anchor.title || !!anchor.text;' ). lo_buf->add( ' }).forEach(function(anchor) {' ). lo_buf->add( ' items.push({' ). lo_buf->add( ' action: function() {' ). lo_buf->add( ' anchor.click();' ). lo_buf->add( ' },' ). lo_buf->add( ' title: (function() {' ). lo_buf->add( ' var result = anchor.title + anchor.text;' ). lo_buf->add( ' if (anchor.href.includes("label")) {' ). lo_buf->add( ' result = "Label: " + result;' ). lo_buf->add( ' }' ). lo_buf->add( ' return result.trim();' ). lo_buf->add( ' })()' ). lo_buf->add( ' });' ). lo_buf->add( ' });' ). lo_buf->add( '' ). lo_buf->add( ' return items;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'function enumerateJumpAllFiles() {' ). lo_buf->add( ' var root = document.getElementById("jump");' ). lo_buf->add( ' if (!root || root.nodeName !== "UL") return null;' ). lo_buf->add( '' ). lo_buf->add( ' return Array' ). lo_buf->add( ' .prototype.slice.call(root.children)' ). lo_buf->add( ' .filter(function(elem) { return elem.nodeName === "LI" })' ). lo_buf->add( ' .map(function(listItem) {' ). lo_buf->add( ' var title = listItem.children[0].childNodes[0].textContent;' ). lo_buf->add( ' return {' ). lo_buf->add( ' action: root.onclick.bind(null, title),' ). lo_buf->add( ' title : title' ). lo_buf->add( ' };' ). lo_buf->add( ' });' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Save Scroll Position' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( 'function saveScrollPosition() {' ). lo_buf->add( ' // Not supported by Java GUI' ). lo_buf->add( ' try { if (!window.sessionStorage) { return } }' ). lo_buf->add( ' catch (err) { return err }' ). lo_buf->add( '' ). lo_buf->add( ' window.sessionStorage.setItem("scrollTop", document.querySelector("html").scrollTop);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'function restoreScrollPosition() {' ). lo_buf->add( ' // Not supported by Java GUI' ). lo_buf->add( ' try { if (!window.sessionStorage) { return } }' ). lo_buf->add( ' catch (err) { return err }' ). lo_buf->add( '' ). lo_buf->add( ' var scrollTop = window.sessionStorage.getItem("scrollTop");' ). lo_buf->add( ' if (scrollTop) {' ). lo_buf->add( ' document.querySelector("html").scrollTop = scrollTop;' ). lo_buf->add( ' }' ). lo_buf->add( ' window.sessionStorage.setItem("scrollTop", 0);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( 'function memorizeScrollPosition(fn) {' ). lo_buf->add( ' return function() {' ). lo_buf->add( ' saveScrollPosition();' ). lo_buf->add( ' return fn.call(this, fn.args);' ). lo_buf->add( ' }.bind(this);' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Sticky Header' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( '/* https://www.w3schools.com/howto/howto_js_navbar_sticky.asp */' ). lo_buf->add( '/* Note: We have to use JS since IE does not support CSS position:sticky */' ). lo_buf->add( '' ). lo_buf->add( '// When the user scrolls the page, execute toggleSticky' ). lo_buf->add( 'window.onscroll = function() { toggleSticky() };' ). lo_buf->add( '' ). lo_buf->add( '// Add the sticky class to the navbar when you reach its scroll position.' ). lo_buf->add( '// Remove "sticky" when you leave the scroll position' ). lo_buf->add( 'function toggleSticky() {' ). lo_buf->add( ' var body = document.getElementsByTagName("body")[0];' ). lo_buf->add( ' var header = document.getElementById("header");' ). lo_buf->add( ' var sticky = header.offsetTop;' ). lo_buf->add( '' ). lo_buf->add( ' var stickyClass = "sticky";' ). lo_buf->add( ' if (body.classList.contains("full_width")) {' ). lo_buf->add( ' stickyClass = "sticky_full_width";' ). lo_buf->add( ' }' ). lo_buf->add( '' ). lo_buf->add( ' if (window.pageYOffset >= sticky) {' ). lo_buf->add( ' header.classList.add(stickyClass);' ). lo_buf->add( ' } else {' ). lo_buf->add( ' header.classList.remove(stickyClass);' ). lo_buf->add( ' }' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '/**********************************************************' ). lo_buf->add( ' * Browser Control' ). lo_buf->add( ' **********************************************************/' ). lo_buf->add( '' ). lo_buf->add( '// Toggle display of warning message when using Edge (based on Chromium) browser control' ). lo_buf->add( '// Todo: Remove once https://github.com/abapGit/abapGit/issues/4841 is fixed' ). lo_buf->add( 'function toggleBrowserControlWarning(){' ). lo_buf->add( ' if (!navigator.userAgent.includes("Edg")){' ). lo_buf->add( ' var elBrowserControlWarning = document.getElementById("browser-control-warning");' ). lo_buf->add( ' if (elBrowserControlWarning) {' ). lo_buf->add( ' elBrowserControlWarning.style.display = "none";' ). lo_buf->add( ' }' ). lo_buf->add( ' }' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '// Output type of HTML control in the abapGit footer' ). lo_buf->add( 'function displayBrowserControlFooter() {' ). lo_buf->add( ' var out = document.getElementById("browser-control-footer");' ). lo_buf->add( ' out.innerHTML = " - " + ( navigator.userAgent.includes("Edg") ? "Edge" : "IE" );' ). lo_buf->add( '}' ). li_asset_man->register_asset( iv_url = 'js/common.js' iv_type = 'text/javascript' iv_mime_name = 'ZABAPGIT_JS_COMMON' iv_inline = lo_buf->join_w_newline_and_flush( ) ). **************************************************** * abapmerge Pragma - ZABAPGIT_ICON_FONT_CSS **************************************************** lo_buf->add( '@font-face {' ). lo_buf->add( ' font-family: "ag-icons";' ). lo_buf->add( ' font-weight: normal;' ). lo_buf->add( ' font-style: normal;' ). lo_buf->add( ' src: url("../font/ag-icons.woff") format("woff");' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.icon {' ). lo_buf->add( ' line-height: 1;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.icon:before {' ). lo_buf->add( ' font-family: ag-icons !important;' ). lo_buf->add( ' font-style: normal;' ). lo_buf->add( ' font-weight: normal !important;' ). lo_buf->add( '' ). lo_buf->add( ' display: inline-block;' ). lo_buf->add( ' text-decoration: none;' ). lo_buf->add( ' text-align: center;' ). lo_buf->add( ' vertical-align: text-top;' ). lo_buf->add( ' /* width: 1.1em; */' ). lo_buf->add( ' /* padding-right: 0.2em */' ). lo_buf->add( '' ). lo_buf->add( ' /* For safety - reset parent styles, that can break glyph codes*/' ). lo_buf->add( ' font-variant: normal;' ). lo_buf->add( ' text-transform: none;' ). lo_buf->add( '}' ). lo_buf->add( '' ). lo_buf->add( '.icon.large { font-size: 200%; }' ). lo_buf->add( '' ). lo_buf->add( '.icon-abapgit:before { content: "\f101"; }' ). lo_buf->add( '.icon-abaplint:before { content: "\f102"; }' ). lo_buf->add( '.icon-arrow-circle-up:before { content: "\f103"; }' ). lo_buf->add( '.icon-bars:before { content: "\f104"; }' ). lo_buf->add( '.icon-bolt:before { content: "\f105"; }' ). lo_buf->add( '.icon-bookmark-solid:before { content: "\f106"; }' ). lo_buf->add( '.icon-box:before { content: "\f107"; }' ). lo_buf->add( '.icon-briefcase:before { content: "\f108"; }' ). lo_buf->add( '.icon-bug-solid:before { content: "\f109"; }' ). lo_buf->add( '.icon-check:before { content: "\f10a"; }' ). lo_buf->add( '.icon-chevron-down:before { content: "\f10b"; }' ). lo_buf->add( '.icon-chevron-left:before { content: "\f10c"; }' ). lo_buf->add( '.icon-chevron-right:before { content: "\f10d"; }' ). lo_buf->add( '.icon-chevron-up:before { content: "\f10e"; }' ). lo_buf->add( '.icon-circle-dot-regular:before { content: "\f10f"; }' ). lo_buf->add( '.icon-circle-play-regular:before { content: "\f110"; }' ). lo_buf->add( '.icon-circle-solid:before { content: "\f111"; }' ). lo_buf->add( '.icon-cloud-commit:before { content: "\f112"; }' ). lo_buf->add( '.icon-cloud-solid:before { content: "\f113"; }' ). lo_buf->add( '.icon-cloud-upload-alt:before { content: "\f114"; }' ). lo_buf->add( '.icon-code-branch:before { content: "\f115"; }' ). lo_buf->add( '.icon-code-commit:before { content: "\f116"; }' ). lo_buf->add( '.icon-code-fork-solid:before { content: "\f117"; }' ). lo_buf->add( '.icon-code-pull-request-solid:before { content: "\f118"; }' ). lo_buf->add( '.icon-code-solid:before { content: "\f119"; }' ). lo_buf->add( '.icon-cog:before { content: "\f11a"; }' ). lo_buf->add( '.icon-copy-solid:before { content: "\f11b"; }' ). lo_buf->add( '.icon-download-solid:before { content: "\f11c"; }' ). lo_buf->add( '.icon-edit-solid:before { content: "\f11d"; }' ). lo_buf->add( '.icon-exclamation-circle:before { content: "\f11e"; }' ). lo_buf->add( '.icon-exclamation-triangle:before { content: "\f11f"; }' ). lo_buf->add( '.icon-eye-solid:before { content: "\f120"; }' ). lo_buf->add( '.icon-file-alt:before { content: "\f121"; }' ). lo_buf->add( '.icon-file-code:before { content: "\f122"; }' ). lo_buf->add( '.icon-file-image:before { content: "\f123"; }' ). lo_buf->add( '.icon-file-zipper-regular:before { content: "\f124"; }' ). lo_buf->add( '.icon-file:before { content: "\f125"; }' ). lo_buf->add( '.icon-fire-alt:before { content: "\f126"; }' ). lo_buf->add( '.icon-flow:before { content: "\f127"; }' ). lo_buf->add( '.icon-folder:before { content: "\f128"; }' ). lo_buf->add( '.icon-git-alt:before { content: "\f129"; }' ). lo_buf->add( '.icon-github:before { content: "\f12a"; }' ). lo_buf->add( '.icon-heart-regular:before { content: "\f12b"; }' ). lo_buf->add( '.icon-info-circle-solid:before { content: "\f12c"; }' ). lo_buf->add( '.icon-language-solid:before { content: "\f12d"; }' ). lo_buf->add( '.icon-link-solid:before { content: "\f12e"; }' ). lo_buf->add( '.icon-lock:before { content: "\f12f"; }' ). lo_buf->add( '.icon-magnifying-glass-solid:before { content: "\f130"; }' ). lo_buf->add( '.icon-markdown:before { content: "\f131"; }' ). lo_buf->add( '.icon-paste-solid:before { content: "\f132"; }' ). lo_buf->add( '.icon-plug:before { content: "\f133"; }' ). lo_buf->add( '.icon-question-circle-solid:before { content: "\f134"; }' ). lo_buf->add( '.icon-redo-alt-solid:before { content: "\f135"; }' ). lo_buf->add( '.icon-server-solid:before { content: "\f136"; }' ). lo_buf->add( '.icon-sliders-h:before { content: "\f137"; }' ). lo_buf->add( '.icon-snowflake:before { content: "\f138"; }' ). lo_buf->add( '.icon-star:before { content: "\f139"; }' ). lo_buf->add( '.icon-tag-solid:before { content: "\f13a"; }' ). lo_buf->add( '.icon-times-solid:before { content: "\f13b"; }' ). lo_buf->add( '.icon-tools-solid:before { content: "\f13c"; }' ). lo_buf->add( '.icon-truck-solid:before { content: "\f13d"; }' ). lo_buf->add( '.icon-upload-solid:before { content: "\f13e"; }' ). lo_buf->add( '.icon-user-cog-solid:before { content: "\f13f"; }' ). lo_buf->add( '.icon-user-solid:before { content: "\f140"; }' ). lo_buf->add( '.icon-vial-solid:before { content: "\f141"; }' ). li_asset_man->register_asset( iv_url = 'css/ag-icons.css' iv_type = 'text/css' iv_mime_name = 'ZABAPGIT_ICON_FONT_CSS' iv_inline = lo_buf->join_w_newline_and_flush( ) ). **************************************************** * abapmerge Pragma - ZABAPGIT_ICON_FONT **************************************************** lo_buf->add( 'd09GRgABAAAAACEsAAsAAAAAOygAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABH' ). lo_buf->add( 'U1VCAAABCAAAADsAAABUIIslek9TLzIAAAFEAAAAPwAAAFZAtU4xY21hcAAA' ). lo_buf->add( 'AYQAAAFYAAAE8PbieW1nbHlmAAAC3AAAGbEAACz898pmW2hlYWQAAByQAAAA' ). lo_buf->add( 'MAAAADYu14YpaGhlYQAAHMAAAAAeAAAAJAmVB9NobXR4AAAc4AAAAGcAAAEI' ). lo_buf->add( 'ggn/6GxvY2EAAB1IAAAAhgAAAIZvkWSUbWF4cAAAHdAAAAAfAAAAIAFnARxu' ). lo_buf->add( 'YW1lAAAd8AAAASgAAAIWQeF35nBvc3QAAB8YAAACEgAAA67zKOCfeJxjYGRg' ). lo_buf->add( 'YOBiMGCwY2BycfMJYeDLSSzJY5BiYGGAAJA8MpsxJzM9kYEDxgPKsYBpDiBm' ). lo_buf->add( 'g4gCACY7BUgAeJxjYGT8wTiBgZWBgXEaYxoDA4M7lP7KIMnQwsDAxMDKzIAV' ). lo_buf->add( 'BKS5pjA4fGT86MgE4uoxsTKAVDKCOADYRgluAHic7dNnbtwwAAXhWa+8buve' ). lo_buf->add( '7XXvvfeqq+ZA+ZWL6AQORy/HiICPAxJqACVgGOgW50UFnT908PhdVjvtepfx' ). lo_buf->add( 'dr3iV3tO5XpT//yUseNY5lU7DpVzq3LHHiOMMlaum6DPJFNMM8Msc8yzwCJL' ). lo_buf->add( 'LLPCKmusM2CDTbbYZodd9tjngEOOOOaEU87K8y+45IprbrjljnseeOSJZ154' ). lo_buf->add( '5Y13Pvjki2/q8go9/h99h+7g36x2l6Ldt6HAdsP9b6pwJ5vh8NtoeoEdCexo' ). lo_buf->add( 'YMcCOx5+M81EYPvh2zWTgZ0K7HRgZwI7G9i5wM4HdiGwi4FdCuxyYFcCuxrY' ). lo_buf->add( 'tcCuB3YQ2I3AbgZ2K7Dbgd0J7G5g9wK7H9iDwB4G9iiwx4E9CexpYM8Cex7+' ). lo_buf->add( '581FYC8DexXY68DeBPY2sHeBvQ/sQ2AfA/sU2OfAvgT2NbBvgX0P7EdgPwP7' ). lo_buf->add( 'FdjvwNZB/RfMwaYceJytWnmQHFd576+P97p7unuO7p6e+9ztnp2Vdlc7p7TS' ). lo_buf->add( 'aq1dSStZNusjkbAdLMoGC8sFi8EEsA1rbIwTTEqxC8qBgqiKw7gqpFQVF7gq' ). lo_buf->add( 'JAjbJKRyqZwDjKGy4Q+oFIktF05hJ2E233vdM7u6gBy7M6/f6/fe967v+H3f' ). lo_buf->add( 'G8HYGAgC/ZQoCBVhQlgSrhPuFu4VPis8Jfyd8APhPMQFYbwTTMEe6PrUJ9Rr' ). lo_buf->add( 'd1sloC61wCVpbx56ZQgsqPnt7l7w9kIJvNluZwpojdB6p069EhTBAtqbB+zn' ). lo_buf->add( 'OqRe8wMlpNj+hRSnACnylr1OCVrzYkR1GjhVN4m0qGsj3W46JDuO47VwCKSE' ). lo_buf->add( 'xQ7SnU1jyzLrWbfENM6sx9722n6NOGks2eFEomk6JA7uOO+8B1od/qjj3F2k' ). lo_buf->add( 'g7XpFs4NtAyRoD3nH5AO2bZnwNyOGSjOlJqWpFhaJp+w0/UfSJLh52Z2Q28y' ). lo_buf->add( '4yfEk6QoSyBLv6bnrFw6m7WyGqxnidje5R8UD9kpz4TdMzNiabocEsnmEmB7' ). lo_buf->add( 'dWh0caTEuDM5lYLuNm/KluSbSUGSZMW9Sy0mnIyH9FSx8zm6U5Y1L9bv9meN' ). lo_buf->add( 'nC7JoO7MLBTfelw8eqJ6sCTLn8psz+AHqjkinVQKsigpOTiupXU3fs2yJO6Q' ). lo_buf->add( 'KrUxCYIpb1vynQ0ZZHJ/dwKg1CK4vHQKJ+/7uR0i2HEo7KeGWbZAelcWrBTs' ). lo_buf->add( 'Xzk4D3HzVFMHIv+X7aeeIwZMlhvbDPXwr0joBxOs3WHTgpmlClJLJsQcklOU' ). lo_buf->add( 'wX8qijwBM0hx7NrtBt03Mb2oyADiwav2HJBF7DQlvzVhwcmn7hJj1rtk6Qdp' ). lo_buf->add( 'z8N1eoPP4NAS+Q6f1BPYRW7MSYrj3dsWpUPNQJqSxI9ta4ChCnkB/2AD/kyQ' ). lo_buf->add( 'BCrEhLhgCx6+LQt1IRAmhWmhJfSEOWGvsCgcFK4W3iTcIBwVbhZuFU4I7xY+' ). lo_buf->add( 'iH095Ebq9eyWWw+ib+8KeSUqe9GTfemWfHC5p0OYYCEXDzPBMAOTaddd9NLu' ). lo_buf->add( 'G8Viq9lsVqtH0um5mZm6511VKk0Ui365PPjTmZlitVquVs163c3n1zwvXS7v' ). lo_buf->add( 'HhvzC4WnX3zx9z/+8ZdXV1/4xjfuzVQ9r5p5Ar+YuzYswX1Vo6bWYtXByxkv' ). lo_buf->add( 'k83sybC/OcxlMrP4jOF3PjOD6R6s343vF7LZ7EqS9c3EE4yS93ZW8jy2zSLf' ). lo_buf->add( '69fhdcEQmoJgt+dhtgQOl/O9qBfSczDb3QNeenYvdNvT4NfiQJwypGfVxbsf' ). lo_buf->add( 'vnuRJ6eWLT2hW8uoLGKUfpQ6+MWcC8Ji1GLxbv/I13TL0r92hNU49CGKHwez' ). lo_buf->add( 'OLzE53AWnseTLuPpCnaVjdn269UaYQLu/ZJyAk5TTaOD4yy9Qh7u02iDlTA5' ). lo_buf->add( 'fdksshzqXFjCLcGtgXqv609CDXUPwU2gJN2a7dldHxrvaUqWcUqzoAJW7Den' ). lo_buf->add( 'sGDRM6jbjn8kZhva+VjiWSNlwIyR4cviyQNI0xGELFNWuJEept1OMlyApavk' ). lo_buf->add( '058mqm4VnMG6U4Bzg2Xdku+6S7Z0eMYpFML9EQURUBriQhbpoXacBLc1Xsfp' ). lo_buf->add( 'iS2Kx9TiWwKDlpJNfOKVwaufSGSVWfGV8wUHTiPNg0/EU2fPpuJP0Mp5JOmc' ). lo_buf->add( 'F4b7LgqwjvLF5EoYdwiumGnXEcV6r95p830oov7FpMqLnRYsaBT39WzRhtN2' ). lo_buf->add( '8eyplaKNzxW7KArFz6+tnWGbOnPKLhbtU6dn2GOmaK9gdQWH3MA/EXBPFoRD' ). lo_buf->add( '0aiuQz00F7VpIJsD1gLKxGovsCkx+ZqDTsDErAytHgqbg3YBbYBUQxPVak8h' ). lo_buf->add( 'RcfsWXs0O6d3Gy2qZBTaanT1nK3tsXqmY5v9aV73I/5uum/aG6V2ab3U/icz' ). lo_buf->add( 'lagUd7O3vcprCqXKa5Uea7q7WEmk8vbcFK/ir6bmbLCxG36G54t7+A12vr2A' ). lo_buf->add( 'TZZNr8eStEOB1P7wSUrHqauunlddzNDBizR96Es0TcdU9k4dw+zge+qIV/4Y' ). lo_buf->add( 'PrpJax4YrTSjhXxY+9tnKbZ26Gc+g0ma0mep8+CzPAvsHfDKZzdpjcNLjJbN' ). lo_buf->add( 'p1OCOHIe42fcZb9dv0xHSF46wEW0xinrzpRD4HfDxaYdKF6G2CtXphWt0fbY' ). lo_buf->add( 'sixAZU18NidG79VLu8H9l12ktLHB5UJA3ZUVakgXeQB5wSnB7DyMb1Vl47Pp' ). lo_buf->add( 'UEPDuf6td9/a58lgZv89j96znycgNPr9lX4fhJ1Rbf/WnQeiyv33fInVrfQv' ). lo_buf->add( 'HtP/RWP2utGxIfe2rzTu103jVCx2ysheYeA/ouoxmsOvZZ2xRuca6mthc2Cc' ). lo_buf->add( 'xFZlC69vKudIx4sPwD+jjLfDXgz7udgVBWoKOsxwMXyGwjSPJT/oYlKv0Snw' ). lo_buf->add( 'QkDW9kGYPDzZ7Ab7gkczlenFqXL26fpcbdc20m8UM+5hlei6iUp2HVup8cPT' ). lo_buf->add( '2O59ajNfnZ6u5pvq+2pz9akDWjLV6CfGtv0ba2nqQ9l5APVPAQujMbdMD3UQ' ). lo_buf->add( 'n5CYi8Y5Gk0CVtnoUIxIbo7KRxI218xoz/4S6hTV3VDx1fyIp8kVxvyTq1Cv' ). lo_buf->add( 'Ue2qmErvlC35TqpeaRbtVaYDVx1sh82Q3WWm92AN9XiPa77rGfcg5EVl6yGc' ). lo_buf->add( 'Jfy/w6cUvnTZPHH72ZEgxu32Wt0e+5/f5Gc7hB0OsUccvja9b7qSAzcTHytY' ). lo_buf->add( 'XiJbxnI5yx8FT3HsWBYm3brtQqY0OIdTtHCW4ePF8AGr0bllEJjin0xP0eFZ' ). lo_buf->add( '0m9H2eyEpuoI6GSie7mm2ryT9bS0wVr4RPPAnxHGQKn5B1x3Rqii1DPVbjEz' ). lo_buf->add( 'zhe3B5Ucro+dw3AJd5p2pdms2Dm7igjKNjXTTJqmKMSt+2P9A339fiuRsO7X' ). lo_buf->add( 'MRu730z8BM0tfnB/2TjwTRxHFeYRGa7gWIx3GQ/3GHsP0cxWlufGh3tA3DXp' ). lo_buf->add( 'he5F1MIbdpeGrskKghjHsm7KpW3ztGmnc7iruXS/MRPlZhp9njsXtnsnS1Ad' ). lo_buf->add( 'PBE+M5mGFpi2bQZaI8N2kZUb/YObpYP9RlS3M+wyeCakhPuocLvJcEAgzAjL' ). lo_buf->add( '6BPehBzEPKTWLEu3roKZUBcHjtTqPNDhCrwLVru5FaMlKsNFwyxRE8jz0dL8' ). lo_buf->add( 'XEI1LWVFVVcU67lwdmpUFz5K0WqXwynDQDJoA2Xhyc31PZnzG9QgorKManxZ' ). lo_buf->add( 'Ea8Lm+79m80mm9nBelhrhXQjzLLxc3FNlFD/lXAXBEBXVSIl6DJw5jFoStkq' ). lo_buf->add( 'hmhV2QSs7EWv24bfMj15TTJyPF3UtJtuYmZ9VdNW9dzgeX2YtVkFbEiGOVgw' ). lo_buf->add( 'PXSL4KzpvRer3/xmVrkaNtSijB2+j/D0K7AR8t940A1QXAMfEVscOJcHPmUq' ). lo_buf->add( 'iNDA5xKPxp14+MpjPvFe4FLhpbETuvY9L80l3qdDMYcfNwlpot+a9CVVbuZr' ). lo_buf->add( 'itzryUot35RVyU+iPF5cr3R5/aQU1v8Et3bf9PTz2Xo9K6pyZ0ISxaxn7JAp' ). lo_buf->add( 'GAZQeYfhZUVRmujIqnhxG4WwNkThbUBmbVKM2L7pCMN/E5efRC9tG66cxRSq' ). lo_buf->add( 'yGVJBtuSiCdtFC3iJkNk2UminCHGYTC204YFSx+cZ/7BctX78pdZBtChOKFb' ). lo_buf->add( 'H9hHdXJ8ocFfrerW4LteFY6dwOan+CvVmthHjxN9C55F+fCE7UI30q5F3P1a' ). lo_buf->add( 'yAGMKZAfxNBraEVexCRwSKOMFChTPIfYGa/h0a6xoz20nkMXGdZY+rYpu5Pa' ). lo_buf->add( 'DnrE4+tMnH96mLVa01gPWzt8jZ+rsKaYbLft7f2IfcP5XYV7ZApFYRfzUxEf' ). lo_buf->add( 'iMTfK3ZncS9GnMrmKbnVJPMVutxJGG4bmy+2gsdvkqSXbtFN0F+S1C/uMCqx' ). lo_buf->add( 'MUm6CY94LHZ2cPZHitQgRB38kEF9OO4U4gr63l9h9S/pYOq3vCQ5O2LYRcU+' ). lo_buf->add( '2KUyePntsCBNKHF0QU6zTp9TCWlII9/sdXgD5c0VxjEfgpsQ6NAhPAi8ULd2' ). lo_buf->add( 'eiF23wpAXrfzeT+f36tSZUahmC4odCtYuYfV+vkvPiJTKj9CKOVnufFz3KtF' ). lo_buf->add( 'lHMLTzNAfkpbgDuBsMqeRyGRrjSyuEsludSgksoR9bxWrWrNywy/nGC+NLqN' ). lo_buf->add( 'iVgsMfjqZSYQjY+YKYejzzPUFBpoFFP8n+V2uItyzeT5AtTHYl4o2q6D4jzP' ). lo_buf->add( 'PLWghp2gcufRSi5tEGKkc5Wjf88KpqKYrPAfwb5r9wU8OeXndF2hkr7/moau' ). lo_buf->add( '7SjLiijB+nUlv1I2jHLFL123Nb8eRN2CfXfkfElU5JjuN9yloBwAVbhMKCP/' ). lo_buf->add( 'k/nWOxD/7cHyCO8w2ZxN03ooEaF1bPu95FbZZWgocFt2Mgy8JaFClS8zHwkT' ). lo_buf->add( 'Y5ihd4Tea8H5K8e60Vo7IqPwnga1wOpZck34YMnXB09zxkROw6a7jwzOQMXS' ). lo_buf->add( 'TwyO41QJny/DSC7ue1NoCYvCm9Eb2sHU6NbwA5MRrj+26pR24NVtlBtu6anP' ). lo_buf->add( 'fXdszPwUP3B3MDU77Duk9alqFV3RlETFXZK0S6RSUnrxRgtXwWZYcQprR46s' ). lo_buf->add( 'DNZP4/w+JBPooYLNRE/WEVtTaaeEH9bx88WiqIq2JHXx0RMlpDq4kbn1xzm1' ). lo_buf->add( 'Iw5f62ncGR/7fx3p0Oh54NKew7Nje5FEb6nIdwN9/4vXTOsdKVpycrzV4XqE' ). lo_buf->add( 'J70ReL9764raR86x5WwI656sS8cCWZeDGaxsFArwuQummzgyuJ1Pd7A+s5aR' ). lo_buf->add( 'pGMNSWqcYQ0bhdFZMd7KomfRF/YLR4Xb8KyGjBMJZjQ1ZKkL540nUKdhm1B0' ). lo_buf->add( '3a0FPJsI+zJvk73xWkxDT/KY9gIPL2i0QrUZ21zR6IbANTSm9k127n285qKE' ). lo_buf->add( 'ZEQfXYmMmIpZsaQ9qkMysEY1Vlwx7cE61eCcnxtwZQ9+Dumthrj4YPh40zFV' ). lo_buf->add( 'zY+P51X1WMw0b9vEudJF5/VLzuqyp3LlExjG6piNTXFvl1A/CD2ZyG3zJcID' ). lo_buf->add( '7KiavB5qKQJLpXxz56Fbd9/xwTt2V/Olbm2yf3Uq3rhtFyFOCv66UNnVWTrd' ). lo_buf->add( 'nb9zHj/5G5tTg7/M7O/ny9mZ+xdjWuMY91l4fOM8ao+9HHMKdhh5CYPvhGsR' ). lo_buf->add( 'XGYUvfHbvWGwguHOULGQoarhwRsvRDI1f3wIat5gVjP3eKOPhtc2H88xY7qi' ). lo_buf->add( 'aW/B70pYhUY2Z/cbvOrB8e44fgZn8+wxDj9mzbP9RoVZYkyyrAv208+wZIXX' ). lo_buf->add( '8gpMGn1Wuxr2fCkkFPmKuLOCzWLPUL/w2Nj6JFQqHh4anDvb08zHh+qLndSj' ). lo_buf->add( '+OLPYa2nDRUgJoPzUOlpYRh7g50Z+qHbOD+gWHpdNJwsIWgtAkTjBO0Fh+K9' ). lo_buf->add( 'gG4qN3jmeTWjNVpmJqbMJvJKtpqk0ynRLegmTcY1ZfZjqvq85mnPqeA8r6oT' ). lo_buf->add( 'bSWWMWdJqpJVCsmpD2p1VzfH6L0kb7QexmbY5DnNE1Q+qVfRnhNEIY6wKtwj' ). lo_buf->add( 'fFB4SPgdlFuHtPFQA8LsFkJpdJC7vl3jPiji0D0Ik5i1wzPmSbcVmkLEqt0e' ). lo_buf->add( 't4PMofA7fq/rodLFc2YW0kuj0cQ2rsPNJ7+v6TFuRcMokTSqZZYgbmdkpsSA' ). lo_buf->add( 'oNVEuzoFX4opklRG9SiDPqkAGjfYs3914S1HVM0wjaRhKbKMplpRIIZGXSdW' ). lo_buf->add( 'LpGUzLFUwUrEDFnUtJSZMEtXlc2EkdJVANlScNsKqfGkpl79G7Xqe78rEpmY' ). lo_buf->add( 'QEVSBhEBMw5lioh8QcLPe2RQWVYUFUlEvCUS+PDC6tINDyIeJnsALQZV4yrq' ). lo_buf->add( 'DFmWmQ6nMQTNatpAb9jNbrezcc1KpyWqaPwPDXo6belx3bK35xydyFblRoLw' ). lo_buf->add( '+sEblnb2FgZPspkQUVakcZBFficFJihsFBmVScSfAN9Dj6fLoma4gaOLsTAa' ). lo_buf->add( 'x8Sr1017tdYODzePexgOngJ3I5hmIPS1mJHdV28tZb2njYrxtJddatUWMwCx' ). lo_buf->add( '7YltXm17Mn7mTDy5veZtS8AfGEYmTbSVNinvdJ+KxZ5yd5ZJe0Uj6czKdMKU' ). lo_buf->add( 'SKdO3MD6wheswCX1DpHMxKb+G2LF9kXxqZF3720FH6G7HcIQ5semL7g0WExm' ). lo_buf->add( 's7Vs9hBVjjHsYBgsRfyGz60I852sUS07YLUZhS7wlCq3UyW0oeIDcJbfIjFE' ). lo_buf->add( 'cSu6KO0uCnbaHcIcSFaTylRodErserPFsds0ePXxaFpszsi/+N6CaZE15Peg' ). lo_buf->add( '3V4Uj+72hgHqdtfPidwaDTCFBRAGd4u6oYsLMoknJO2wJiXiRL7e9qGlGrtV' ). lo_buf->add( 'w1BF0cy5qqwlqG0mUjJNEEJlXTWIkSBxO3aStWkbofsxNHQbAggzBP9O0QQl' ). lo_buf->add( 'quOoBDNHJ1cNVTYShjxeNOWEJcsxzVCBjywqikESBok7WdZANUb3P+J7Ub9P' ). lo_buf->add( 'CDcgykbJ566qBcwZRY4KdXnAAr7dKeCIymPe9rgFocM6D6w98aPYt8fCzOEu' ). lo_buf->add( 'dhkpQsWlgm0X/Hdsg5gcz8XEzJyT8d7tLWZcOQmq4uYplFW/cPjqwuBE2HJS' ). lo_buf->add( '3GyZWb1My/xrhasPF8a1Mqg5V1EhKbuZRaTpuXMZUc/H5Zg4+Q6fEXvkV204' ). lo_buf->add( 'ugs7y/FMldmBTjuKF9Yv9hjDoAmcykwcuP7ARGboZxXuO9Xc3YTK8sSBCfws' ). lo_buf->add( 'F5yzrAqT5VKzWRrGwriP6vAxtlx7bHpVocCEwgOnGx9mJuxDvaVhgPjcJuyH' ). lo_buf->add( 'maXeh5i9+3Ajih3/xaYzsekTPwDfQv4voGcuQPUCQOLjKmng1jstr9dyvaCF' ). lo_buf->add( 'cK0lXv/z76PXZItjmA5ent8zP4/fPY/cggl8K28PXmC1MGXnBy+8/8iR979w' ). lo_buf->add( 'zTUvSHe94x0Cj8FxjMJ88IowK8yxO0UuFrhrTMRoFASYg1HE0mtF/kgIG9Hc' ). lo_buf->add( '9NiEmN+xVvUesfTr1FKtpLJQkX6iEvrdz56wdB4weMS6G91/uo/Csldd1q14' ). lo_buf->add( 'Mhm39MEZ3YL3h37369/mkYIZHk/wyXFsO9RVDLtS3JVJzPMjLYJXHfpCPALb' ). lo_buf->add( '6jAN2grPPeiEAQUQEI3MDPjlYuJQb6F3KIHZn+FLWF1lOGM1waFlZdex2G23' ). lo_buf->add( 'xY7tqvBiIqy7QE/uRtR8oU8d1KKoUBj6YSoRbSfKFN3qo4X+J2PQkQ++VSO+' ). lo_buf->add( '2ijmXUmU65IuJTQrlSxqJClm9RRV+ugEK1YuZte69dA13qpxb0vGK7Im5dAI' ). lo_buf->add( '2VS3rZiq6GDF7JzMNCqoRFYThlspTOdfD73mLXcSb7C7W9TjXq8bYkOO1ffi' ). lo_buf->add( 'sddDXsZtjC6z+E9B+IpB2HGsNkbxpPSHdYs2vbnK9muntl8zVd7p4eQb6Gf9' ). lo_buf->add( '+vRieKkxVh2jvJnl0Kab2X7tzddsd1000w0pJpVq0XWHoEWYdR15nkWcdwi7' ). lo_buf->add( '0Iu8QbhFeBvnf25wmAFgcwyG21cfZrz/hxawPlhghw1nMd0Tsuzh8PH2/0UN' ). lo_buf->add( 'HLdNXofJsZCrI97+H74f3l/h3tSFg8JJ1PjVC+93I84P2SzSd8HmlWx1qAlH' ). lo_buf->add( 'bVqXtLmUTtQGzg/OcElY4c4Wz8KW/IDfGsPKZptzPHvuwuxaggteKFOb2dP/' ). lo_buf->add( 'o9ccmw82NuBZ1AFfYZFaHmBgtixgYVdmvqZh9K7HSj6Ji53eMHYwWu8ocueG' ). lo_buf->add( 'bVmQIRgGH3jIgdlJj176LpLzvaI7Av6X/uSiE7bleCScFzyDHEcRHMbIyvLy' ). lo_buf->add( 'ColhlqKRJpKkSqbS0FFZxPGtu7xLUWKKKWdx1UTLYs6Q5V0H04AogBJRC4iJ' ). lo_buf->add( 'PSTyi8lpYkgufXAX4omt5JSYLM8tc3JIAMkhrJCkz3qaGKM4AKSbTZd1pjFR' ). lo_buf->add( '82SDWEjSq0cDXNe8cbdssRnlpti5TGX5ZJW5G5vXhdOhNQ8nZ+EkhxRFt9lM' ). lo_buf->add( 'w/+dohH9NmPj5+I2UeK+Ojs3djaEH1aabTeevj8tIoYB6arHzDhod2ZFdMce' ). lo_buf->add( 'fBA9LTF7pwZx87GriOfByw+5opPR7njMcAlZWiLENR67Q8s4kH7IUNXN39l8' ). lo_buf->add( 'FeUuKeQQgbrOlkAUj26MbvTOpDr2w6nUGTv/Saewyj1fG858Mm+fsfF9Bx+F' ). lo_buf->add( 'NnuXs0e69zicYbbdHvHmiCVHfBWxU/DT2zUtree022/XcnpaY88LyiBcoWJY' ). lo_buf->add( 'ju4xRUVUEOXPcN2K0oOGKOgE1Gt5nKfDwTgw5kHQONvOLndEGPN3aRBd02y5' ). lo_buf->add( '2vj3e5LT7sm1hePzJ2Nq8p5YWe/Ent5WtrN23tFEU7l6KXZYkej4obzrwvXG' ). lo_buf->add( 'zTufsMc79n3GZ0Ml+d57kmrs5PzxhbWT7jR2j3Vipa+W7Vgqf3iMSsrh2NLV' ). lo_buf->add( 'siVqTt7tFq+Xrt/5e3Zn3L638vDocoljF3ENj4rdr+1kUd/xeic8oE57FIdn' ). lo_buf->add( 'gS0W1grTULMpw0VUh5keg1TiasK63XK2FZwBB4KJ/kp/Db8zyJ2DiSjmti18' ). lo_buf->add( '7j617fbTdznYfoYHFrBDo99vsC9XWKth3C38nFu9ffO+Qxjdd7hhqCX61Uz4' ). lo_buf->add( '2wwUZGU4V26t6pH12rznu/TCY/CdNb/tc4eDB8LgtyND9PhPL73veIo127zy' ). lo_buf->add( 'ODHc0DB29BHEgnP8F6wfwP3sBmHUPLwMC3wGathVGMM8/CKMB9jDazAWEggv' ). lo_buf->add( 'wXisnTKXqySS8a3w2KsFdcoj7vznKB3uqAR1XHS9FjrBaKR7fke8TZJzCugZ' ). lo_buf->add( 'Wcmi21etEieZU2RPByUry1ki6p6s5IY1WUXOYM2jTgGKDfwOvl+fq9XmDrDk' ). lo_buf->add( 'X0VJpLqeNAqGrKN0G7osxZL1I/Xk2PwYcwPihvhx141LpJKVJcuiJQKUoktE' ). lo_buf->add( 'rbgoZytEil++1pJ4rYFDFqHQKP5uNCAmP5RkYpYDM0sVVaY5U4NEAkdrOgWN' ). lo_buf->add( 'KOz3VNxvAcSTNXbnvXV/LtoKxq1TsL65nPu3Tt0pjM2DsDluZThMwWliZuQv' ). lo_buf->add( 'Auc5FisLr9ZgCE17TM+g4Cs9j8JRxhz64F8K/QYJCuBprDz4x8dbx+FnjH80' ). lo_buf->add( 'rGoojT5W6ax8FGuQ+n8DjoRJEQAAAHicY2BkYGAA4r+lycXx/DZfGbiZGEDg' ). lo_buf->add( 'idCiqTD6/9//f9mnMbECuRwMYGkAXp8M93icY2BkYGBiAAH2qf///v/NPo2B' ). lo_buf->add( 'kQEVOAEAdO8FdgAAeJxlj9ERgDAIQ6E/rtVRHIUl3IchHMQFbKS0PVA/3nE0' ). lo_buf->add( 'QFIiou1AK1Y7rEZFYxn9AAhtout9aEUCFoDONCNx22frqrg77DsfNPsCfKU8' ). lo_buf->add( 'y0ej92y/zLan9reZl/d3niKuu8cDcFI4PQAAAAAAAX4CNAJ2AroC5AMGAzID' ). lo_buf->add( 'bgPUA/oEIARGBGwEkgTMBQwFJgVyBaAF6gZmBpoHCgeMB9wIWAiaCOoJRAmA' ). lo_buf->add( 'CcAKJAp8CwILUgvaDAoMRAy2DOQNLA4GDloOpA80D7oP7BAkEFwQsBDuEVYR' ). lo_buf->add( 'nhIsErITshPoFBQUThS6FRIVYhYWFkwWfgAAeJxjYGRgYHBiFGCQZgABJiDm' ). lo_buf->add( 'AkIGhv9gPgMAE1cBhQB4nGWNS27CMBRFb/j0A1KLVKkd1qMOqAifIQuAOQNm' ). lo_buf->add( 'HYTghKAkjhyDxKwr6BK6hK6i6gq6oF7cxwRsye+8865tAD38IMBxBbjx53E1' ). lo_buf->add( 'cM3un5uke+EW+Um4jS6eha/o+8IdvGIi3MUD3vhC0Lql6aESbuAO78JN+g/h' ). lo_buf->add( 'FvlTuI1HfAlf0X8Ld7DEr3AXL0E/SgdZbMp6odNdHtlTe6pLbevMlGocjk5q' ). lo_buf->add( 'rkttI6fXanVQ9T6dOJeoxJpCzUzpdJ4bVVmz1bELN85V0+EwER/GpkCEFANk' ). lo_buf->add( 'iGFQosYCmmaHnBN7MT3vl0xb1sz3CmOEGF2k5kyVPhnBsa6ZXOHAs8ae2Qmt' ). lo_buf->add( 'Q8I+YcagIM383WM65zY0lZ9taWL6EBt/q8IUQ+7kLB/634s/3IhcMHicbVJn' ). lo_buf->add( 'e9owGOQSEzAzgYzuvVt375U2/SWyLRs9CMvVgNBfX1kSNB/qD9bdya/vvVdq' ). lo_buf->add( '7bT80279/znDDnYRoY09dNBFjB76GGCIEcbYxwEmmOIQRzjGCS7hMq7gKq7h' ). lo_buf->add( 'Om7gJm7hNu7gLu7hPh7gIR7hMZ7gKZ4hwXO8wEu8wmu8wVu8w3t8wEd8wmd8' ). lo_buf->add( 'wVd8w3ec4gd+4gy/Wh2SkrpkutusnFV6TKQUqyRjMuM0MXWUEqmiVHA9SoWY' ). lo_buf->add( 'L4icJ0pwlu+m4jxOJaNFRhSNU1N6vZ3NaDYf2PdSiirJxaraEk4LPdwQycqZ' ). lo_buf->add( '7m2YqSfBMxc6kbQ0nMhpkGpO1httEDRnNsi4MHmSicWC6b4nbmPfY1NzQfKE' ). lo_buf->add( 'cLspcpqkklTZzGNfNHa4EJtYJ47XhnNr+NtQpb3ec7pPnonS0nrt6aiJ6Gz8' ). lo_buf->add( 'hzRnoWZCzzNOFkQzG9C3fXhR0pKRquQ0puvw627BbDTbbuxA49lziC1ISacO' ). lo_buf->add( '/mF1TeVmHFGj2TLpyqKCi9VeIXhOZccea6Pt2XVm0uGMErmd7AGrCpFcHOWI' ). lo_buf->add( '216MdQkx7GUII4m4yObHtoGKFWtWlUnJiVKh4eZCNAPo10TpUBvV3JRHbnj/' ). lo_buf->add( 'kgcXSXPRdBXOT1G5tGEciZV9UamSWawqsSo4mdNIaSJjTcLl6mu2oGqDheBb' ). lo_buf->add( 'LE0W2h2EQ/d+xjrYOYb6nqMeLhnhHrZafwFnhCXLAAA=' ). li_asset_man->register_asset( iv_url = 'font/ag-icons.woff' iv_type = 'font/woff' iv_mime_name = 'ZABAPGIT_ICON_FONT' iv_base64 = lo_buf->join_and_flush( ) ). result = li_asset_man. ENDMETHOD. METHOD get_frontend_services. IF frontend_services IS INITIAL. CREATE OBJECT frontend_services TYPE /apmg/cl_apm_frontend_services. ENDIF. result = frontend_services. ENDMETHOD. METHOD get_gui. DATA: hotkey_controller TYPE REF TO /apmg/if_apm_gui_hotkey_ctl, router TYPE REF TO /apmg/if_apm_gui_event_handler. IF gui IS INITIAL. DATA(asset_mananager) = get_asset_manager( ). DATA(html_preprocessor) = NEW /apmg/cl_apm_gui_html_processo( ii_asset_man = asset_mananager ). html_preprocessor->preserve_css( 'css/ag-icons.css' ). html_preprocessor->preserve_css( 'css/common.css' ). CREATE OBJECT router TYPE /apmg/cl_apm_gui_router. CREATE OBJECT hotkey_controller TYPE /apmg/cl_apm_gui_hotkey_ctl. gui = NEW #( io_component = router ii_hotkey_ctl = hotkey_controller ii_html_processor = html_preprocessor ii_asset_man = asset_mananager ). ENDIF. result = gui. ENDMETHOD. METHOD get_gui_services. IF gui_service IS NOT BOUND. gui_service ?= get_gui( ). ENDIF. result = gui_service. ENDMETHOD. METHOD get_html_viewer. IF html_viewer IS NOT BOUND. CREATE OBJECT html_viewer TYPE /apmg/cl_apm_gui_html_viewer EXPORTING io_container = container iv_disable_query_table = disable_query_table. ENDIF. result = html_viewer. ENDMETHOD. METHOD get_popups. IF popups IS INITIAL. CREATE OBJECT popups TYPE /apmg/cl_apm_popups. ENDIF. result = popups. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_html_processo IMPLEMENTATION. METHOD /apmg/if_apm_gui_html_processo~process. DATA lo_css_processor TYPE REF TO /apmg/cl_apm_gui_css_processor. DATA lt_css_urls TYPE string_table. DATA lv_css_build TYPE string. FIELD-SYMBOLS LIKE LINE OF lt_css_urls. patch_html( EXPORTING iv_html = iv_html IMPORTING ev_html = rv_html et_css_urls = lt_css_urls ). IF lines( lt_css_urls ) > 0. CREATE OBJECT lo_css_processor EXPORTING ii_asset_manager = mi_asset_man. LOOP AT lt_css_urls ASSIGNING . lo_css_processor->add_file( ). ENDLOOP. lv_css_build = lo_css_processor->process( ). ii_gui_services->cache_asset( iv_url = |{ c_css_build_name }| iv_type = 'text' iv_subtype = 'css' iv_text = lv_css_build ). ENDIF. ENDMETHOD. METHOD constructor. mi_asset_man = ii_asset_man. ENDMETHOD. METHOD find_head_offset. rv_head_end = find( val = iv_html regex = |{ cl_abap_char_utilities=>newline }?\\s*| case = abap_false ) ##REGEX_POSIX. IF rv_head_end <= 0. rv_head_end = find( val = iv_html sub = '' case = abap_false ). IF rv_head_end <= 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'HTML preprocessor: not found'. ENDIF. ENDIF. ENDMETHOD. METHOD is_preserved. READ TABLE mt_preserve_css TRANSPORTING NO FIELDS WITH KEY table_line = iv_css_url. rv_yes = boolc( sy-subrc = 0 ). ENDMETHOD. METHOD patch_html. CONSTANTS lc_css_re TYPE string VALUE ``. DATA lv_head_end TYPE i. DATA lo_css_re TYPE REF TO cl_abap_regex. DATA lo_matcher TYPE REF TO cl_abap_matcher. DATA lv_css_path TYPE string. DATA lv_marker TYPE string. DATA lv_off TYPE i. DATA lv_len TYPE i. DATA lv_cur TYPE i. DATA lv_css_build TYPE string VALUE ''. REPLACE FIRST OCCURRENCE OF '$BUILD_NAME' IN lv_css_build WITH c_css_build_name. " Mmmm CLEAR: ev_html, et_css_urls. lv_head_end = find_head_offset( iv_html ). CREATE OBJECT lo_css_re EXPORTING ignore_case = abap_true pattern = lc_css_re ##REGEX_POSIX. lo_matcher = lo_css_re->create_matcher( text = substring( val = iv_html len = lv_head_end ) ). WHILE lo_matcher->find_next( ) = abap_true. lv_css_path = lo_matcher->get_submatch( 1 ). IF abap_false = is_preserved( lv_css_path ). lv_off = lo_matcher->get_offset( ). lv_len = lo_matcher->get_length( ). ev_html = ev_html && substring( val = iv_html off = lv_cur len = lv_off - lv_cur ). ev_html = ev_html && c_comment_start && substring( val = iv_html off = lv_off len = lv_len ) && c_comment_end. lv_cur = lv_off + lv_len. APPEND lv_css_path TO et_css_urls. ENDIF. ENDWHILE. ev_html = ev_html && substring( val = iv_html off = lv_cur len = lv_head_end - lv_cur ). IF lines( et_css_urls ) > 0. lv_marker = cl_abap_char_utilities=>newline && ` ` " Assume 4 space indent, maybe improve and detect ? && c_preprocess_marker && cl_abap_char_utilities=>newline && ` `. ev_html = ev_html && lv_marker && lv_css_build. ENDIF. ev_html = ev_html && substring( val = iv_html off = lv_head_end ). ENDMETHOD. METHOD preserve_css. APPEND iv_css_url TO mt_preserve_css. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_menus IMPLEMENTATION. METHOD advanced. result = /apmg/cl_apm_html_toolbar=>create( 'apm-toolbar-advanced' ). result->add( iv_txt = 'Database Utility' iv_act = /apmg/if_apm_gui_router=>c_action-go_db )->add( iv_txt = 'Debug Info' iv_act = /apmg/if_apm_gui_router=>c_action-go_debuginfo ). IF /apmg/cl_apm_gui_factory=>get_frontend_services( )->is_sapgui_for_windows( ) = abap_true. result->add( iv_txt = 'Open IE DevTools' iv_act = /apmg/if_apm_gui_router=>c_action-ie_devtools ). ENDIF. ENDMETHOD. METHOD back. result = /apmg/cl_apm_html_toolbar=>create( 'apm-toolbar-back' ). result->add( iv_txt = 'Back' iv_act = /apmg/if_apm_gui_router=>c_action-go_back ). ENDMETHOD. METHOD experimental. TRY. IF /apmg/cl_apm_settings=>factory( )->get( )-experimental_features IS NOT INITIAL. "apm menu->add( iv_txt = /apmg/cl_apm_gui_buttons=>experimental( ) iv_act = /apmg/if_apm_gui_router=>c_action-go_settings ). ENDIF. CATCH /apmg/cx_apm_error ##NO_HANDLER. ENDTRY. ENDMETHOD. METHOD help. result = /apmg/cl_apm_html_toolbar=>create( 'apm-toolbar-help' ). result->add( iv_txt = 'Feedback' iv_class = 'red' iv_act = /apmg/if_apm_gui_router=>c_action-feedback )->add( iv_txt = 'Registry' iv_act = /apmg/if_apm_gui_router=>c_action-registry * FUTURE * )->add( * iv_txt = 'Tutorial' * iv_act = /apmg/if_apm_gui_router=>c_action-go_tutorial )->add( iv_txt = 'Documentation' iv_act = /apmg/if_apm_gui_router=>c_action-documentation )->add( iv_txt = 'Changelog' iv_act = /apmg/if_apm_gui_router=>c_action-changelog )->add( iv_txt = 'Hotkeys' iv_act = /apmg/if_apm_gui_router=>c_action-show_hotkeys ). ENDMETHOD. METHOD registry. IF registry = /apmg/if_apm_settings=>c_registry. DATA(fav_class) = `transport-box`. " green ELSE. fav_class = `user-box`. " blue ENDIF. result = /apmg/cl_apm_html_toolbar=>create( 'apm-toolbar-registry' )->add( iv_title = 'Registry' iv_txt = registry iv_class = fav_class iv_act = |{ /apmg/if_apm_gui_router=>c_action-url }?url={ registry }| ). ENDMETHOD. METHOD settings. result = /apmg/cl_apm_html_toolbar=>create( 'apm-toolbar-settings' ). result->add( iv_txt = 'Global' iv_act = /apmg/if_apm_gui_router=>c_action-go_settings )->add( iv_txt = 'Personal' iv_act = /apmg/if_apm_gui_router=>c_action-go_settings_personal ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_page IMPLEMENTATION. METHOD /apmg/if_apm_gui_error_handler~handle_error. error = ix_error. rv_handled = abap_true. ENDMETHOD. METHOD /apmg/if_apm_gui_event_handler~on_event. CASE ii_event->mv_action. WHEN /apmg/if_apm_gui_router=>c_action-goto_source. IF exception_viewer IS BOUND. exception_viewer->goto_source( ). ENDIF. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. WHEN /apmg/if_apm_gui_router=>c_action-show_callstack. IF exception_viewer IS BOUND. exception_viewer->show_callstack( ). ENDIF. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. WHEN /apmg/if_apm_gui_router=>c_action-goto_message. IF exception_viewer IS BOUND. exception_viewer->goto_message( ). ENDIF. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. ENDCASE. ENDMETHOD. METHOD /apmg/if_apm_gui_modal~is_modal. rv_yes = xsdbool( page_control-show_as_modal = abap_true ). ENDMETHOD. METHOD /apmg/if_apm_gui_renderable~render. register_handlers( ). " Real page DATA(html) = /apmg/cl_apm_html=>create( ). html->add( '' ). html->add( '' ). html->add( html_head( ) ). html->add( || ). html->add( title( ) ). html->add( '
' ). DATA(timer) = zcl_abapgit_timer=>create( )->start( ). html->add( render_content( ) ). " TODO -> render child DATA(render_content_time) = timer->end( ). html->add( render_hotkey_overview( ) ). html->add( render_error_message_box( ) ). html->add( render_deferred_parts( c_html_parts-hidden_forms ) ). html->add( footer( render_content_time ) ). html->add( '
' ). DATA(scripts) = scripts( ). IF scripts IS BOUND AND scripts->is_empty( ) = abap_false. html->add( '' ). ENDIF. html->add( '' ). html->add( '' ). ri_html = html. ENDMETHOD. METHOD constructor. super->constructor( ). page_control-page_layout = c_page_layout-centered. ENDMETHOD. METHOD footer. DATA(html) = /apmg/cl_apm_html=>create( ). html->add( '' ). result = html. ENDMETHOD. METHOD get_version_details. result = /apmg/if_apm_version=>c_version. " apm IF zcl_abapgit_factory=>get_environment( )->is_merged( ) = abap_true. result = result && ` - Standalone Version`. ELSE. result = result && ` - Developer Version`. ENDIF. DATA(frontend_services) = /apmg/cl_apm_gui_factory=>get_frontend_services( ). CASE abap_true. WHEN frontend_services->is_webgui( ). result = result && ` - Web`. WHEN frontend_services->is_sapgui_for_windows( ). result = result && ` - Win`. WHEN frontend_services->is_sapgui_for_java( ). result = result && ` - Java`. WHEN OTHERS. " eg. open-abap? result = result && ` - Unknown`. ENDCASE. " Will be filled by JS method displayBrowserControlFooter result = result && ''. ENDMETHOD. METHOD header_script_links. html->add( '' ). IF page_control-extra_js_url IS NOT INITIAL. html->add( || ). ENDIF. ENDMETHOD. METHOD header_stylesheet_links. html->add( '' ). html->add( '' ). " Themes html->add( '' ). " Theme basis CASE settings-gui_settings-ui_theme. WHEN c_ui_theme-dark. html->add( '' ). WHEN c_ui_theme-belize. html->add( '' ). ENDCASE. " Page stylesheets IF page_control-extra_css_url IS NOT INITIAL. html->add( || ). ENDIF. ENDMETHOD. METHOD html_head. DATA(html) = /apmg/cl_apm_html=>create( ). html->add( '' ). html->add( '' ). html->add( '' ). html->add( 'apm' ). header_stylesheet_links( html ). header_script_links( html ). " Overwrite the automatic icon scaling done in /apmg/cl_apm_html=>icon CASE settings-gui_settings-icon_scaling. WHEN 'large'. "mo_settings->c_icon_scaling-large. html->add( '' ). WHEN 'small'. "mo_settings->c_icon_scaling-small. html->add( '' ). ENDCASE. html->add( '' ). result = html. ENDMETHOD. METHOD is_edge_control_warning_needed. DATA: gui_release TYPE /apmg/if_apm_frontend_services=>ty_gui_release, gui_sp TYPE /apmg/if_apm_frontend_services=>ty_gui_sp, gui_patch TYPE /apmg/if_apm_frontend_services=>ty_gui_patch. " With SAP GUI 8.00 PL3 and 7.70 PL13 Edge browser control is basically working. " For lower releases we render the browser control warning " and toggle it via JS function toggleBrowserControlWarning. result = abap_true. TRY. DATA(frontend_services) = /apmg/cl_apm_gui_factory=>get_frontend_services( ). frontend_services->get_gui_version( IMPORTING ev_gui_release = gui_release ev_gui_sp = gui_sp ev_gui_patch = gui_patch ). CATCH /apmg/cx_apm_error. RETURN. ENDTRY. IF gui_release >= '7700' AND gui_sp >= '1' AND gui_patch >= '13' OR gui_release >= '8000' AND gui_sp >= '1' AND gui_patch >= '3'. result = abap_false. ENDIF. ENDMETHOD. METHOD render_browser_control_warning. DATA(html) = /apmg/cl_apm_html=>create( ). DATA(link) = /apmg/cl_apm_html=>create( )->add_a( iv_txt = 'Documentation' iv_typ = /apmg/if_apm_html=>c_action_type-url iv_act = 'https://docs.abapgit.org/guide-sapgui.html#sap-gui-for-windows' ). html->add( '
' ). html->add( /apmg/cl_apm_gui_chunk_lib=>render_warning_banner( |Attention: You use Edge browser control. | && |There are several known malfunctions. See | && link->render( ) ) ). html->add( '
' ). result = html. ENDMETHOD. METHOD render_command_palettes. DATA(html) = /apmg/cl_apm_html=>create( ). html->add( 'var gCommandPalette = new CommandPalette(enumerateUiActions, {' ). html->add( ' toggleKey: "F1",' ). html->add( ' hotkeyDescription: "Command Palette"' ). html->add( '});' ). result = html. ENDMETHOD. METHOD render_deferred_parts. DATA(html) = /apmg/cl_apm_html=>create( ). DATA(parts) = gui_services( )->get_html_parts( )->get_parts( part_category ). LOOP AT parts INTO DATA(part). html->add( part ). ENDLOOP. result = html. ENDMETHOD. METHOD render_error_message_box. " You should remember that the we have to instantiate ro_html even " it's overwritten further down. Because ADD checks whether it's " bound. result = /apmg/cl_apm_html=>create( ). " You should remember that we render the message panel only " if we have an error. IF error IS NOT BOUND. RETURN. ENDIF. result = /apmg/cl_apm_gui_chunk_lib=>render_error_message_box( error ). " You should remember that the exception viewer dispatches the events of " error message panel exception_viewer = NEW #( error ). " You should remember that we render the message panel just once " for each exception/error text. CLEAR error. ENDMETHOD. METHOD render_hotkey_overview. DATA hotkeys_component TYPE REF TO /apmg/if_apm_gui_renderable. hotkeys_component ?= gui_services( )->get_hotkeys_ctl( ). " Mmmm... result = hotkeys_component->render( ). ENDMETHOD. METHOD render_link_hints. DATA(html) = /apmg/cl_apm_html=>create( ). DATA(link_hint_key) = settings-keyboard_settings-link_hint_key. IF settings-keyboard_settings-link_hints_enabled = abap_true AND link_hint_key IS NOT INITIAL. html->add( |activateLinkHints("{ link_hint_key }");| ). html->add( |setInitialFocusWithQuerySelector('#header', false);| ). html->add( |enableArrowListNavigation();| ). ENDIF. result = html. ENDMETHOD. METHOD scripts. DATA(html) = /apmg/cl_apm_html=>create( ). html->add( render_deferred_parts( c_html_parts-scripts ) ). html->add( render_link_hints( ) ). html->add( render_command_palettes( ) ). html->add( |toggleBrowserControlWarning();| ). html->add( |displayBrowserControlFooter();| ). result = html. ENDMETHOD. METHOD title. DATA(page_menu) = page_control-page_menu. IF page_menu IS NOT BOUND AND page_control-page_menu_provider IS BOUND. page_menu = page_control-page_menu_provider->get_menu( ). ENDIF. DATA(page_title) = page_control-page_title. IF page_control-page_title_provider IS BOUND. page_title = page_control-page_title_provider->get_page_title( ). ENDIF. DATA(html) = /apmg/cl_apm_html=>create( ). html->add( '' ). result = html. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_page_db IMPLEMENTATION. METHOD /apmg/if_apm_gui_event_handler~on_event. DATA(key) = CONV /apmg/if_apm_persist_apm=>ty_key( ii_event->query( )->get( 'KEY' ) ). CASE ii_event->mv_action. WHEN c_action-db_display. rs_handled-page = /apmg/cl_apm_gui_page_db_entry=>create( key = key edit_mode = abap_false ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-new_page. WHEN c_action-db_edit. rs_handled-page = /apmg/cl_apm_gui_page_db_entry=>create( key = key edit_mode = abap_true ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-new_page. WHEN c_action-delete. do_delete_entry( key ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-backup. do_backup_db( ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-restore. do_restore_db( ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. ENDCASE. ENDMETHOD. METHOD /apmg/if_apm_gui_menu_provider~get_menu. DATA(toolbar) = /apmg/cl_apm_html_toolbar=>create( 'apm-database-utility' ). toolbar->add( iv_txt = 'Backup' iv_act = c_action-backup )->add( iv_txt = 'Restore' iv_act = c_action-restore )->add( iv_txt = 'Back' iv_act = /apmg/if_apm_gui_router=>c_action-go_back ). ro_toolbar = toolbar. ENDMETHOD. METHOD /apmg/if_apm_gui_renderable~render. register_handlers( ). db_entries = db_persist->list( ). prepare_list( ). DATA(html) = /apmg/cl_apm_html=>create( ). html->add( '
' ). render_stats( html ). html->add( '
' ). html->add( '
' ). render_table( html ). html->add( '
' ). ri_html = html. ENDMETHOD. METHOD /apmg/if_apm_html_table~get_row_attrs. ENDMETHOD. METHOD /apmg/if_apm_html_table~render_cell. ASSIGN COMPONENT 'KEYS' OF STRUCTURE is_row TO FIELD-SYMBOL(). ASSERT sy-subrc = 0. ASSIGN COMPONENT 'KEY_TYPE' OF STRUCTURE is_row TO FIELD-SYMBOL(). ASSERT sy-subrc = 0. ASSIGN COMPONENT 'KEY_NAME' OF STRUCTURE is_row TO FIELD-SYMBOL(). ASSERT sy-subrc = 0. ASSIGN COMPONENT 'KEY_EXTRA' OF STRUCTURE is_row TO FIELD-SYMBOL(). ASSERT sy-subrc = 0. CASE iv_column_id. WHEN 'show_key'. rs_render-content = |{ iv_value }|. WHEN 'value'. IF IS INITIAL AND IS INITIAL. rs_render-content = '' && /apmg/cl_apm_persist_apm=>explain_key( )-key_type && ''. ELSEIF IS INITIAL. rs_render-content = '' && /apmg/cl_apm_persist_apm=>explain_key( |{ }:{ }| )-description && ''. ELSE. rs_render-content = /apmg/cl_apm_persist_apm=>explain_key( )-extra. ENDIF. rs_render-css_class = 'data'. WHEN 'user'. IF IS NOT INITIAL. DATA(user) = CONV syuname( iv_value ). rs_render-content = /apmg/cl_apm_gui_chunk_lib=>render_user_name( user )->render( ). ENDIF. WHEN 'timestamp'. IF IS NOT INITIAL. DATA(timestamp) = CONV timestampl( iv_value ). rs_render-content = /apmg/cl_apm_gui_chunk_lib=>render_timestamp( timestamp ). rs_render-css_class = 'data'. ENDIF. WHEN 'cmd'. IF IS NOT INITIAL. DATA(action) = |key={ escape( val = |{ }| format = cl_abap_format=>e_url ) }|. DATA(toolbar) = /apmg/cl_apm_html_toolbar=>create( )->add( iv_txt = 'Display' iv_act = |{ c_action-db_display }?{ action }| )->add( iv_txt = 'Edit' iv_act = |{ c_action-db_edit }?{ action }| )->add( iv_txt = 'Delete' iv_act = |{ c_action-delete }?{ action }| ). rs_render-html = toolbar->render( ). ENDIF. ENDCASE. ENDMETHOD. METHOD class_constructor. db_persist = /apmg/cl_apm_persist_apm=>get_instance( ). ENDMETHOD. METHOD constructor. super->constructor( ). register_stylesheet( ). ENDMETHOD. METHOD create. DATA(component) = NEW /apmg/cl_apm_gui_page_db( ). result = /apmg/cl_apm_gui_page_hoc=>create( page_title = 'Database Utility' extra_css_url = c_css_url page_menu_provider = component child_component = component ). ENDMETHOD. METHOD do_backup_db. DATA table_of_contents TYPE string_table. DATA(db_entries) = db_persist->list( ). INSERT |Table of Content\n| INTO TABLE table_of_contents. INSERT |================\n| INTO TABLE table_of_contents. INSERT |\n| INTO TABLE table_of_contents. DATA(zip) = NEW cl_abap_zip( ). LOOP AT db_entries INTO DATA(db_entry). DATA(filename) = |{ db_entry-keys }|. TRANSLATE filename USING '/#'. IF filename CS 'JSON'. filename = filename && '.json'. ELSEIF filename CS 'README'. filename = filename && '.md'. ELSE. filename = filename && '.txt'. ENDIF. TRY. DATA(content) = zcl_abapgit_convert=>string_to_xstring_utf8( db_entry-value ). CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. zip->add( name = filename content = content ). INSERT explain_key( db_entry-keys ) INTO TABLE table_of_contents. ENDLOOP. TRY. content = zcl_abapgit_convert=>string_to_xstring_utf8( concat_lines_of( table_of_contents ) ). CATCH zcx_abapgit_exception INTO error. RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. zip->add( name = c_toc_filename content = content ). DATA(zip_content) = zip->save( ). CONCATENATE 'apm_Backup_' sy-datlo '_' sy-timlo '.zip' INTO filename. DATA(frontend_service) = /apmg/cl_apm_gui_factory=>get_frontend_services( ). DATA(path) = frontend_service->show_file_save_dialog( iv_title = 'apm Backup' iv_extension = 'zip' iv_default_filename = filename ). frontend_service->file_download( iv_path = path iv_xstr = zip_content ). MESSAGE 'apm Backup successfully saved' TYPE 'S'. ENDMETHOD. METHOD do_delete_entry. ASSERT key IS NOT INITIAL. DATA(answer) = /apmg/cl_apm_gui_factory=>get_popups( )->popup_to_confirm( iv_titlebar = 'Warning' iv_text_question = |Are you sure you want to delete entry { key }?| iv_text_button_1 = 'Yes' iv_icon_button_1 = 'ICON_DELETE' iv_text_button_2 = 'No' iv_icon_button_2 = 'ICON_CANCEL' iv_default_button = '2' iv_display_cancel_button = abap_false ). IF answer = '2'. RAISE EXCEPTION TYPE /apmg/cx_apm_cancel. ENDIF. db_persist->delete( key ). COMMIT WORK. MESSAGE 'Entry successfully deleted' TYPE 'S'. ENDMETHOD. METHOD do_restore_db. DATA: file_data TYPE xstring, db_entries TYPE /apmg/if_apm_persist_apm=>ty_list, db_entries_old TYPE /apmg/if_apm_persist_apm=>ty_list, db_entry TYPE /apmg/if_apm_persist_apm=>ty_list_item. DATA(frontend_service) = /apmg/cl_apm_gui_factory=>get_frontend_services( ). DATA(path) = frontend_service->show_file_open_dialog( iv_title = 'Restore apm Backup' iv_extension = 'zip' iv_default_filename = 'apm_Backup_*.zip' ). DATA(zip_data) = frontend_service->file_upload( path ). DATA(zip) = NEW cl_abap_zip( ). zip->load( EXPORTING zip = zip_data EXCEPTIONS zip_parse_error = 1 OTHERS = 2 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error loading ZIP file'. ENDIF. LOOP AT zip->files ASSIGNING FIELD-SYMBOL() WHERE name <> c_toc_filename. CLEAR db_entry. " Remove extension SPLIT -name AT '.' INTO db_entry-keys DATA(rest). TRANSLATE db_entry-keys USING '#/'. " Validate DB key IF /apmg/cl_apm_persist_apm=>validate_key( db_entry-keys ) = abap_false. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid DB entry type. This is not an apm Backup'. ENDIF. zip->get( EXPORTING name = -name IMPORTING content = file_data EXCEPTIONS zip_index_error = 1 zip_decompression_error = 2 OTHERS = 3 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error getting file { -name } from ZIP|. ENDIF. TRY. db_entry-value = zcl_abapgit_convert=>xstring_to_string_utf8( file_data ). CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. INSERT db_entry INTO TABLE db_entries. ENDLOOP. DATA(answer) = /apmg/cl_apm_gui_factory=>get_popups( )->popup_to_confirm( iv_titlebar = 'Warning' iv_text_question = 'All existing packages and settings will be deleted and overwritten! Continue?' iv_text_button_1 = 'Restore' iv_icon_button_1 = 'ICON_IMPORT' iv_text_button_2 = 'Cancel' iv_icon_button_2 = 'ICON_CANCEL' iv_default_button = '2' iv_display_cancel_button = abap_false ). IF answer <> '1'. RAISE EXCEPTION TYPE /apmg/cx_apm_cancel. ENDIF. db_persist->lock( db_entry-keys ). db_entries_old = db_persist->list( ). LOOP AT db_entries_old INTO db_entry. db_persist->delete( db_entry-keys ). ENDLOOP. LOOP AT db_entries INTO db_entry. db_persist->save( key = db_entry-keys value = db_entry-value ). ENDLOOP. COMMIT WORK. MESSAGE 'apm Backup successfully restored' TYPE 'S'. ENDMETHOD. METHOD explain_key. DATA(explained) = /apmg/cl_apm_persist_apm=>explain_key( key ). result = |{ key } - { explained-key_type }: { explained-description } ({ explained-extra })\n|. ENDMETHOD. METHOD prepare_list. CONSTANTS indent TYPE string VALUE '   '. DATA sorted_list TYPE ty_list. CLEAR list. " Different field order for AT processing LOOP AT db_entries ASSIGNING FIELD-SYMBOL(). DATA(list_entry) = CORRESPONDING ty_list_item( ). INSERT list_entry INTO TABLE sorted_list. ENDLOOP. DATA(icon) = /apmg/cl_apm_html=>icon( 'folder' ). LOOP AT sorted_list INTO list_entry. AT NEW key_type. list_entry-show_key = |{ icon } { list_entry-key_type }|. INSERT list_entry INTO TABLE list. ENDAT. AT NEW key_name. list_entry-show_key = |{ indent }{ icon } { list_entry-key_name }|. INSERT list_entry INTO TABLE list. ENDAT. CASE list_entry-key_extra. WHEN /apmg/if_apm_persist_apm=>c_key_extra-package_json. list_entry-show_key = 'package.abap.json'. WHEN /apmg/if_apm_persist_apm=>c_key_extra-package_readme. list_entry-show_key = 'readme.md'. WHEN OTHERS. list_entry-show_key = to_lower( list_entry-key_extra ). ENDCASE. list_entry-show_key = |{ indent }{ indent }{ list_entry-show_key }|. INSERT list_entry INTO TABLE list. ENDLOOP. ENDMETHOD. METHOD register_stylesheet. DATA(buffer) = NEW zcl_abapgit_string_buffer( ). **************************************************** * abapmerge Pragma - ZABAPGIT_CSS_PAGE_DB **************************************************** buffer->add( '/*' ). buffer->add( ' * PAGE DB CSS' ). buffer->add( ' */' ). buffer->add( '' ). buffer->add( '/* LAYOUT */' ). buffer->add( '' ). buffer->add( '.db-list {' ). buffer->add( ' padding: 0.5em;' ). buffer->add( ' overflow-x: auto;' ). buffer->add( '}' ). buffer->add( '.db-list table { table-layout: fixed; }' ). buffer->add( '.db-list table pre {' ). buffer->add( ' display: inline-block;' ). buffer->add( ' overflow: hidden;' ). buffer->add( ' word-wrap:break-word;' ). buffer->add( ' white-space: pre-wrap;' ). buffer->add( ' margin: 0px;' ). buffer->add( ' width: 30em;' ). buffer->add( '}' ). buffer->add( '.db-list table th {' ). buffer->add( ' text-align: left;' ). buffer->add( ' padding: 0.5em;' ). buffer->add( '}' ). buffer->add( '.db-list table thead tr { border-bottom: 1px solid; }' ). buffer->add( '.db-list table td {' ). buffer->add( ' padding: 4px 0.5em;' ). buffer->add( ' vertical-align: middle;' ). buffer->add( ' word-break: break-all;' ). buffer->add( '}' ). buffer->add( '.db-list table td.data { font-style: italic; }' ). buffer->add( '' ). buffer->add( '/* COLORS */' ). buffer->add( '' ). buffer->add( '.db-list { background-color: var(--theme-table-background-color); }' ). buffer->add( '.db-list table td { color: var(--theme-primary-font-color); }' ). buffer->add( '.db-list table td.data { color: var(--theme-greyscale-dark); }' ). buffer->add( '.db-list table tbody tr:hover td { background-color: rgba(0, 0, 0, 0.075); }' ). buffer->add( '.db-list table tbody tr:active td { background-color: #f4f4f4; } /* Needed? */' ). buffer->add( '.db-list table th { color: var(--theme-link-color); }' ). buffer->add( '.db-list table thead tr { border-color: var(--theme-table-border-color); }' ). gui_services( )->register_page_asset( iv_url = c_css_url iv_type = 'text/css' iv_mime_name = 'ZABAPGIT_CSS_PAGE_DB' iv_inline = buffer->join_w_newline_and_flush( ) ). ENDMETHOD. METHOD render_stats. DATA(package_count) = 0. DATA(user_count) = 0. LOOP AT db_entries ASSIGNING FIELD-SYMBOL(). CASE -key_type. WHEN /apmg/if_apm_persist_apm=>c_key_type-package. IF -key_extra = /apmg/if_apm_persist_apm=>c_key_extra-package_json. package_count = package_count + 1. ENDIF. WHEN /apmg/if_apm_persist_apm=>c_key_type-settings. IF -key_name <> /apmg/if_apm_persist_apm=>c_key_name-global_settings. user_count = user_count + 1. ENDIF. ENDCASE. ENDLOOP. html->add( |Packages: { package_count }, Users: { user_count }| ). ENDMETHOD. METHOD render_table. html->add( /apmg/cl_apm_html_table=>create( me )->define_column( iv_column_id = 'show_key' iv_column_title = 'Key' )->define_column( iv_column_id = 'value' iv_column_title = 'Description' )->define_column( iv_column_id = 'user' iv_column_title = 'Last Changed By' )->define_column( iv_column_id = 'timestamp' iv_column_title = 'Last Changed At' )->define_column( iv_column_id = 'cmd' iv_column_title = 'Commands' )->render( list ) ). ENDMETHOD. ENDCLASS. CLASS lcl_json_editor DEFINITION. PUBLIC SECTION. CLASS-METHODS get_javascript RETURNING VALUE(result) TYPE string. ENDCLASS. CLASS lcl_json_editor IMPLEMENTATION. METHOD get_javascript. result = |class JSON_Editor extends HTMLElement \{\n| && | constructor() \{\n| && | super()\n| && | const template = document.createElement('template')\n| && | template.innerHTML = `\n| && | \n| && |
\n| && | `\n| && |\n| && | this.last_string_content = ''\n| && | this.attachShadow(\{ mode: 'open' \})\n| && | this.shadowRoot.appendChild( template.content.cloneNode(true) )\n| && | this.editor = this.shadowRoot.getElementById('editor')\n| && | this.addEventListener('keyup', _ => this.format() )\n| && | \}\n| && |\n| && | connectedCallback() \{\n| && | this.indent = Number(this.getAttribute('indent')) \|\| 3\n| && | this.value = this.getAttribute('value')\n| && | \}\n| && |\n| && | //===[ Caret Control ]=================================================\n| && |\n| && | get_selection() \{\n| && | if( this.shadowRoot.getSelection )\n| && | return this.shadowRoot.getSelection()\n| && | return document.getSelection()\n| && | \}\n| && |\n| && | // return a "pointer" with relevant information about the caret position\n| && | get_caret_pointer() \{\n| && | const selection = this.get_selection()\n| && | if (selection.rangeCount > 0) \{\n| && | const range = selection.getRangeAt(0)\n| && | const caret_range = range.cloneRange()\n| && | caret_range.selectNodeContents(this.editor)\n| && | caret_range.setEnd(range.endContainer, range.endOffset)\n| && | const section = caret_range.toString()\n| && | const character = section[section.length-1]\n| && | const occurrence = this.get_number_of_occurrences(section, character)\n| && | return \{ character, occurrence, section \}\n| && | \}\n| && | return null\n| && | \}\n| && |\n| && | // set the caret position based on pointer information\n| && | set_caret_from_pointer(pointer) \{\n| && | const selection = window.getSelection()\n| && | const range = document.createRange()\n| && | let nodes_to_explore = this.get_text_nodes(this.editor)\n| && | let occurrence = pointer.occurrence\n| && | let fount_at = 0\n| && | let i=0\n| && |\n| && | for(i=0; i= 0 )\n| && | break\n| && | occurrence -= this.get_number_of_occurrences(node.textContent, pointer.character)\n| && | \}\n| && |\n| && | fount_at++\n| && | range.setStart(nodes_to_explore[i], fount_at)\n| && | range.setEnd(nodes_to_explore[i], fount_at)\n| && | selection.removeAllRanges()\n| && | selection.addRange(range)\n| && | \}\n| && |\n| && | //===[ Utils ]=========================================================\n| && |\n| && | // escape string special characters used in regular expressions\n| && | escape_regex_string(string) \{\n| " /[.*+?^${}()|[\]\\]/g, '\\$&' && | return string.replace(/[.*+?^$\{\}()\|[\\]\\\\]/g, '\\\\$&')\n| && | \}\n| && |\n| && | // return the position of the occurrence-th sub_string occurrence\n| && | get_position_of_occurrence(string, sub_string, occurrence) \{\n| && | const position = string.split(sub_string, occurrence).join(sub_string).length\n| && | return position === string.length ? -1 : position\n| && | \}\n| && |\n| && | // return the number of sub_string occurrences\n| && | get_number_of_occurrences(string, sub_string) \{\n| && | return sub_string ? string.replace(new RegExp(`[^$\{this.escape_regex_string| && |(sub_string)\}]`, 'g'), '').length : 0\n| && | \}\n| && |\n| && | // return the element's children text nodes\n| && | get_text_nodes(element) \{\n| && | let node, list=[], walk=document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false)\n| && | while(node=walk.nextNode())\n| && | list.push(node)\n| && | return list\n| && | \}\n| && |\n| && | //===[ Formatting ]====================================================\n| && |\n| && | escape_html(input) \{\n| && | const replace = [ ['&', '&'], ['<', '<'], ['>', '>'], ['"', '"'], ["'", '''] ]\n| && | return replace.reduce( ( escaped, replacement) => escaped.replaceAll( ...replacement ), input)\n| && | \}\n| && | \n| && | // format a json object\n| && | format_object(input, offset=0) \{\n| && | // in JS typeof null returns "object" (legacy bug), for null input we just return null\n| && | if( input === null )\n| && | return 'null'\n| && | let output = ''\n| && | output += `\{
\n`\n| && | output += Object.keys(input).map((key, index, list) => \{\n| && | return `$\{' '.repeat(offset+this.indent)\}| && |\\"| && |$\{this.escape_html(key)\}\\":| && |$\{this.format_input(input[key], offset+this.indent)\}| && |$\{index < list.length-1 ? ',' : ''\}
\n`\n| && | \}).join('')\n| && | output += ' '.repeat(offset)\n| && | output += `\}`\n| && | return output\n| && | \}\n| && |\n| && | // format a json array\n| && | format_array(input, offset=0) \{\n| && | let output = ''\n| && | output += `[
\n`\n| && | output += input.map((value, index, list) => \{\n| && | return `$\{' '.repeat(offset+this.indent)\}$\{this.format_input(value, | && |offset+this.indent)\}| && |$\{index < list.length-1 ? ',' : ''\}
\n`\n| && | \}).join('')\n| && | output += ' '.repeat(offset)\n| && | output += `]`\n| && | return output\n| && | \}\n| && |\n| && | // format a json string\n| && | format_string(input) \{\n| && | return `\\"$\{this.escape_html(input)\}| && |\\"`;\n| && | \}\n| && |\n| && | // format a boolean\n| && | format_boolean(input) \{\n| && | return `$\{input\}`;\n| && | \}\n| && |\n| && | // format a number\n| && | format_number(input) \{\n| && | return `$\{input\}`;\n| && | \}\n| && |\n| && | // format a json input\n| && | format_input(input, offset=0) \{\n| && | const type = Array.isArray(input) ? 'array' : typeof input\n| && | switch (type) \{\n| && | case 'object':\n| && | return this.format_object(input, offset)\n| && | case 'array':\n| && | return this.format_array(input, offset)\n| && | case 'string':\n| && | return this.format_string(input)\n| && | case 'boolean':\n| && | return this.format_boolean(input)\n| && | case 'number':\n| && | return this.format_number(input)\n| && | default:\n| && | return input\n| && | \}\n| && | \}\n| && |\n| && | format() \{\n| && | const editor = this.editor\n| && | const pointer = this.get_caret_pointer()\n| && | let content = ''\n| && | try \{\n| && | content = JSON.parse(this.raw_string)\n| && | \}\n| && | catch(exception) \{\n| && | return\n| && | \}\n| && |\n| && | // prevent unnecesary render\n| && | const current_string_content = JSON.stringify(content)\n| && | if(!content \|\| current_string_content == this.last_string_content)\n| && | return\n| && |\n| && | editor.innerHTML = this.format_input(content)\n| && | this.last_string_content = current_string_content\n| && | if(pointer && focus)\n| && | this.set_caret_from_pointer(pointer)\n| && | \}\n| && |\n| && | //===[ Getters / Setters ]=============================================\n| && |\n| && | get raw_string() \{\n| && | // remove %A0 (NBSP) characters, which are no valid in JSON\n| && | return this.editor.innerText?.replaceAll('\\xa0', '') \|\| ''\n| && | \}\n| && |\n| && | set raw_string( input ) \{\n| && | this.string_value = input\n| && | \}\n| && |\n| && | get string_value() \{\n| && | return this.last_string_content\n| && | \}\n| && |\n| && | set string_value( input ) \{\n| && | this.editor.innerText = input\n| && | this.format()\n| && | \}\n| && |\n| && | get value() \{\n| && | return this.string_value\n| && | \}\n| && |\n| && | set value( input ) \{\n| && | return this.string_value = input\n| && | \}\n| && |\n| && | get json_value() \{\n| && | return JSON.parse( this.string_value )\n| && | \}\n| && |\n| && | set json_value( input ) \{\n| && | this.string_value = JSON.stringify( input )\n| && | \}\n| && |\n| && | is_valid() \{\n| && | try \{\n| && | JSON.parse( this.raw_string )\n| && | return true\n| && | \}\n| && | catch(e) \{\n| && | return false\n| && | \}\n| && | \}\n| && |\}\n| && |\n| && |customElements.define('json-editor', JSON_Editor)\n|. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_page_db_entry IMPLEMENTATION. METHOD /apmg/if_apm_gui_event_handler~on_event. CASE ii_event->mv_action. WHEN c_action-switch_mode. edit_mode = xsdbool( edit_mode = abap_false ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-update. do_update( key = ii_event->form_data( )->get( 'KEYS' ) value = ii_event->form_data( )->get( 'VALUE' ) ). edit_mode = abap_false. IF back_on_save = abap_true. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-go_back_to_bookmark. ELSE. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. ENDIF. ENDCASE. ENDMETHOD. METHOD /apmg/if_apm_gui_menu_provider~get_menu. DATA(toolbar) = /apmg/cl_apm_html_toolbar=>create( 'apm-database-entry' ). IF edit_mode = abap_true. toolbar->add( iv_txt = 'Save' iv_act = |submitFormById('{ c_edit_form_id }');| iv_typ = /apmg/if_apm_html=>c_action_type-onclick iv_opt = /apmg/if_apm_html=>c_html_opt-strong ). ELSE. IF edit_mode = abap_true. DATA(txt) = `Display`. ELSE. txt = `Edit`. ENDIF. toolbar->add( iv_txt = txt iv_act = |{ c_action-switch_mode }| ). ENDIF. toolbar->add( iv_txt = 'Back' iv_act = /apmg/if_apm_gui_router=>c_action-go_back ). ro_toolbar = toolbar. ENDMETHOD. METHOD /apmg/if_apm_gui_page_title~get_page_title. IF edit_mode = abap_true. rv_title = 'Config Edit'. ELSE. rv_title = 'Config Display'. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_gui_renderable~render. register_handlers( ). DATA(html) = /apmg/cl_apm_html=>create( ). html->add( '
' ). render_header( html ). IF edit_mode = abap_true. TRY. db_persist->lock( db_entry-keys ). CATCH /apmg/cx_apm_error INTO DATA(error). edit_mode = abap_false. RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. render_edit( html ). ELSE. render_view( html ). ENDIF. html->add( '
' ). register_deferred_script( get_scripts( ) ). ri_html = html. ENDMETHOD. METHOD class_constructor. db_persist = /apmg/cl_apm_persist_apm=>get_instance( ). ENDMETHOD. METHOD constructor. super->constructor( ). register_stylesheet( ). me->edit_mode = edit_mode. me->back_on_save = back_on_save. content_type = /apmg/cl_apm_persist_apm=>explain_key( key )-content_type. db_entry = load_entry( key ). ENDMETHOD. METHOD create. DATA(component) = NEW /apmg/cl_apm_gui_page_db_entry( key = key edit_mode = edit_mode back_on_save = back_on_save ). result = /apmg/cl_apm_gui_page_hoc=>create( extra_css_url = c_css_url page_title_provider = component child_component = component ). ENDMETHOD. METHOD do_update. ASSERT key IS NOT INITIAL AND strlen( key ) < /apmg/if_apm_persist_apm=>c_max_key_len. " Validation might raise exception but we want to keep the edited (inconsistent) value db_entry-value = escape_percent_sign( value ). IF content_type = /apmg/if_apm_persist_apm=>c_content_type-json. db_entry-value = /apmg/cl_apm_json=>validate_and_prettify( value ). ENDIF. db_persist->save( key = db_entry-keys value = db_entry-value ). COMMIT WORK. MESSAGE 'Entry successfully saved' TYPE 'S'. ENDMETHOD. METHOD escape_percent_sign. " cl_apm_gui_event->unescape removes '%25' but it's valid in URLs that can be part of the content. " This is no general solution but we add it back here for /namespaces/ which are encoded as " #namespace#. result = replace( val = value regex = '%23([a-zA-Z0-9]{3,8})%23' with = '%2523$1%2523' occ = 0 ) ##REGEX_POSIX. ENDMETHOD. METHOD get_entry_tag. result = ||. ENDMETHOD. METHOD get_scripts. DATA(html) = /apmg/cl_apm_html=>create( ). html->set_title( cl_abap_typedescr=>describe_by_object_ref( me )->get_relative_name( ) ). " TODO: Replace with " html->add( lcl_json_editor=>get_javascript( ) ) result = html. ENDMETHOD. METHOD load_entry. result = db_persist->load( key ). ENDMETHOD. METHOD register_stylesheet. DATA(buffer) = NEW zcl_abapgit_string_buffer( ). **************************************************** * abapmerge Pragma - ZABAPGIT_CSS_PAGE_DB_ENTRY **************************************************** buffer->add( '/*' ). buffer->add( ' * PAGE DB ENTRY CSS' ). buffer->add( ' */' ). buffer->add( '' ). buffer->add( '/* LAYOUT */' ). buffer->add( '' ). buffer->add( '.db-entry {' ). buffer->add( ' padding: 0.5em;' ). buffer->add( '}' ). buffer->add( '.db-entry pre {' ). buffer->add( ' display: block;' ). buffer->add( ' font-size: 10pt;' ). buffer->add( ' overflow: hidden;' ). buffer->add( ' word-wrap:break-word;' ). buffer->add( ' white-space: pre-wrap;' ). buffer->add( ' border: 1px solid;' ). buffer->add( ' border-radius: 3px;' ). buffer->add( ' padding: 0.5em;' ). buffer->add( ' margin: 0.5em 0em;' ). buffer->add( ' width: 98%;' ). buffer->add( '}' ). buffer->add( '.db-entry textarea {' ). buffer->add( ' margin: 0.5em 0em;' ). buffer->add( ' width: 98%;' ). buffer->add( '}' ). buffer->add( '.db-entry .toolbar {' ). buffer->add( ' padding-left: 0.5em;' ). buffer->add( ' padding-right: 0.5em;' ). buffer->add( '}' ). buffer->add( '.db-entry dl.entry-tag div {' ). buffer->add( ' display: inline-block;' ). buffer->add( ' border: 1px solid;' ). buffer->add( ' border-radius: 3px;' ). buffer->add( ' margin-right: 0.5em;' ). buffer->add( '}' ). buffer->add( '.db-entry dl.entry-tag div:last-child {' ). buffer->add( ' margin-right: 0px;' ). buffer->add( '}' ). buffer->add( '.db-entry dt, .db-entry dd {' ). buffer->add( ' display: inline-block;' ). buffer->add( ' margin-left: 0px;' ). buffer->add( ' padding: 2px 5px;' ). buffer->add( '}' ). buffer->add( '.db-entry dt::after { content: ":" }' ). buffer->add( '' ). buffer->add( '/* COLORS */' ). buffer->add( '' ). buffer->add( '.db-entry {' ). buffer->add( ' background-color: var(--theme-container-background-color);' ). buffer->add( '}' ). buffer->add( '.db-entry pre {' ). buffer->add( ' background-color: #f4f4f4;' ). buffer->add( ' border-color: var(--theme-container-border-color);' ). buffer->add( '}' ). buffer->add( '.db-entry textarea {' ). buffer->add( ' background-color: var(--theme-table-background-color);' ). buffer->add( ' border-color: var(--theme-container-border-color);' ). buffer->add( '}' ). buffer->add( '.db-entry dl.entry-tag div {' ). buffer->add( ' border-color: hsl(206, 20%, 75%);' ). buffer->add( ' background-color: hsl(206, 20%, 90%);' ). buffer->add( '}' ). buffer->add( '.db-entry dt {' ). buffer->add( ' background-color: hsl(206, 20%, 75%);' ). buffer->add( '}' ). gui_services( )->register_page_asset( iv_url = c_css_url iv_type = 'text/css' iv_mime_name = 'ZABAPGIT_CSS_PAGE_DB_ENTRY' iv_inline = buffer->join_w_newline_and_flush( ) ). ENDMETHOD. METHOD render_edit. html->add( |
| ). html->add( || ). DATA(value) = escape( val = db_entry-value format = cl_abap_format=>e_html_text ). IF content_type = /apmg/if_apm_persist_apm=>c_content_type-json. html->add( || ). " TODO: Replace with " ii_html->add( || ) ELSE. html->add( || ). ENDIF. html->add( '
' ). ENDMETHOD. METHOD render_header. html->add( '
' ). html->add( get_entry_tag( ) ). html->add( '
' ). ENDMETHOD. METHOD render_view. " Better not to use syntax highlighter so we see the actual, unmodified data html->add( |
{ db_entry-value }
| ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_page_debuginf IMPLEMENTATION. METHOD /apmg/if_apm_gui_event_handler~on_event. CASE ii_event->mv_action. WHEN c_action-save. DATA(filename) = |apm_Debug_Info_{ sy-datlo }_{ sy-timlo }.html|. DATA(frontend_services) = /apmg/cl_apm_gui_factory=>get_frontend_services( ). DATA(path) = frontend_services->show_file_save_dialog( iv_title = 'apm - Debug Info' iv_extension = 'html' iv_default_filename = filename ). TRY. DATA(content) = zcl_abapgit_convert=>string_to_xstring_utf8( html_for_download ). CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. frontend_services->file_download( iv_path = path iv_xstr = content ). MESSAGE 'apm debug info successfully saved' TYPE 'S'. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN OTHERS. ASSERT 1 = 1. ENDCASE. ENDMETHOD. METHOD /apmg/if_apm_gui_menu_provider~get_menu. DATA(toolbar) = /apmg/cl_apm_html_toolbar=>create( 'apm-debug-info' ). toolbar->add( iv_txt = 'Save' iv_act = c_action-save ). toolbar->add( iv_txt = 'Back' iv_act = /apmg/if_apm_gui_router=>c_action-go_back ). ro_toolbar = toolbar. ENDMETHOD. METHOD /apmg/if_apm_gui_renderable~render. register_handlers( ). DATA(html) = /apmg/cl_apm_html=>create( ). html->add( '
' ). render_debug_info( html ). html->add( '
' ). html_for_download = |\n| && |\n| && | apm - Debug Info\n| && |\n| && |\n| && | { html->render( ) }\n| && |\n| && ||. register_deferred_script( get_scripts( ) ). ri_html = html. ENDMETHOD. METHOD create. DATA(component) = NEW /apmg/cl_apm_gui_page_debuginf( ). result = /apmg/cl_apm_gui_page_hoc=>create( page_title = 'Debug Info' page_menu_provider = component child_component = component ). ENDMETHOD. METHOD get_scripts. DATA(html) = /apmg/cl_apm_html=>create( ). html->set_title( cl_abap_typedescr=>describe_by_object_ref( me )->get_relative_name( ) ). html->add( 'debugOutput("
Browser:" + navigator.userAgent + ' && '"
Frontend time:" + new Date() + "
", "debug_info");' ). result = html. ENDMETHOD. METHOD render_debug_info. DATA gui_version TYPE string. DATA(frontend_service) = /apmg/cl_apm_gui_factory=>get_frontend_services( ). TRY. frontend_service->get_gui_version( IMPORTING ev_gui_version_string = gui_version ). CATCH /apmg/cx_apm_error ##NO_HANDLER. " Continue rendering even if this fails ENDTRY. IF zcl_abapgit_factory=>get_environment( )->is_merged( ) = abap_true. html->add( '

apm - Standalone Version

' ). html->add( '
To keep apm up-to-date (or also to contribute) you need to' ). html->add( |install it as a repository ({ html->a( iv_txt = 'Developer Version' iv_act = /apmg/if_apm_constants=>c_repository iv_typ = /apmg/if_apm_html=>c_action_type-url ) }).
| ). ELSE. TRY. DATA(package) = zcl_abapgit_factory=>get_tadir( )->get_object_package( iv_object = 'PROG' iv_obj_name = '/APMG/APM' ). CATCH cx_root ##NO_HANDLER. package = 'UNKNOWN'. ENDTRY. html->add( '

apm - Developer Version

' ). html->add( |
apm is installed in package { package }
| ). ENDIF. data(action) = |{ /apmg/if_apm_gui_router=>c_action-url }?url={ /apmg/if_apm_constants=>c_repository }/blob/main/CONTRIBUTING.md|. html->add( '
' ). html->add_a( iv_txt = 'Contribution guidelines for apm' iv_act = action iv_class = |url| ). html->add( '
' ). DATA(release) = zcl_abapgit_factory=>get_environment( )->get_basis_release( ). html->add( '

Environment

' ). html->add( || ). html->add( || ). html->add( || ). html->add( || ). html->add( || ). html->add( |
apm version: { /apmg/if_apm_version=>c_version }
GUI version: { gui_version }
SY time: { sy-datum } { sy-uzeit } { sy-tzone }
SY release: { release-release } SP { release-sp }
| ). html->add( |
| ). " TODO: List support objects for bundling and IMPORT (see package /APMG/APM_OBJECTS) ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_page_hoc IMPLEMENTATION. METHOD constructor. super->constructor( ). me->child_component = child_component. page_control = page_controller. IF page_control-show_as_modal = abap_false. page_control-show_as_modal = detect_modal( ). ENDIF. IF page_control-page_menu_provider IS NOT BOUND. page_control-page_menu_provider = detect_menu_provider( ). ENDIF. IF page_control-page_title_provider IS NOT BOUND. page_control-page_title_provider = detect_title_provider( ). ENDIF. ENDMETHOD. METHOD create. DATA(page_control) = VALUE /apmg/cl_apm_gui_page=>ty_control( page_title = page_title page_layout = page_layout page_menu = page_menu page_menu_provider = page_menu_provider page_title_provider = page_title_provider extra_css_url = extra_css_url extra_js_url = extra_js_url show_as_modal = show_as_modal ). IF page_control-page_menu_provider IS NOT BOUND. " try component itself TRY. page_control-page_menu_provider ?= child_component. CATCH cx_sy_move_cast_error. ENDTRY. ENDIF. IF page_control-page_title_provider IS NOT BOUND. " try component itself TRY. page_control-page_title_provider ?= child_component. CATCH cx_sy_move_cast_error. ENDTRY. ENDIF. result = NEW /apmg/cl_apm_gui_page_hoc( child_component = child_component page_controller = page_control ). ENDMETHOD. METHOD detect_menu_provider. TRY. result ?= child_component. CATCH cx_sy_move_cast_error. ENDTRY. ENDMETHOD. METHOD detect_modal. DATA modal TYPE REF TO /apmg/if_apm_gui_modal. TRY. modal ?= child_component. result = modal->is_modal( ). CATCH cx_sy_move_cast_error. ENDTRY. ENDMETHOD. METHOD detect_title_provider. TRY. result ?= child_component. CATCH cx_sy_move_cast_error. ENDTRY. ENDMETHOD. METHOD get_child. result = child_component. ENDMETHOD. METHOD render_content. IF child_component IS BOUND. result = child_component->render( ). ENDIF. ENDMETHOD. ENDCLASS. CLASS lcl_table_scheme DEFINITION FINAL. " TODO: move to a global class, when table is separated as a component PUBLIC SECTION. DATA columns TYPE /apmg/cl_apm_gui_chunk_lib=>ty_col_spec_tt READ-ONLY. METHODS add_column IMPORTING tech_name TYPE string OPTIONAL display_name TYPE string OPTIONAL css_class TYPE string OPTIONAL add_tz TYPE abap_bool OPTIONAL title TYPE string OPTIONAL allow_order_by TYPE any OPTIONAL RETURNING VALUE(result) TYPE REF TO lcl_table_scheme. ENDCLASS. CLASS lcl_table_scheme IMPLEMENTATION. METHOD add_column. APPEND INITIAL LINE TO columns ASSIGNING FIELD-SYMBOL(). -display_name = display_name. -tech_name = tech_name. -title = title. -css_class = css_class. -add_tz = add_tz. -allow_order_by = allow_order_by. result = me. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_page_list IMPLEMENTATION. METHOD /apmg/if_apm_gui_event_handler~on_event. DATA(package) = CONV devclass( ii_event->query( )->get( 'KEY' ) ). CASE ii_event->mv_action. WHEN c_action-refresh. load_package_list( ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-select. settings-last_package = package. save_settings( ). rs_handled-page = /apmg/cl_apm_gui_page_package=>create( package ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-new_page. WHEN c_action-change_order_by. set_order_by( ii_event->query( )->get( 'ORDERBY' ) ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-toggle_favorites. IF ii_event->query( )->has( 'FORCE_STATE' ) = abap_true. settings-list_settings-only_favorites = ii_event->query( )->get( 'FORCE_STATE' ). ELSE. settings-list_settings-only_favorites = xsdbool( settings-list_settings-only_favorites = abap_false ). ENDIF. save_settings( ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-direction. set_order_direction( xsdbool( ii_event->query( )->get( 'DIRECTION' ) = 'DESCENDING' ) ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-apply_filter. set_filter( ii_event->mt_postdata ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-label_filter. IF ii_event->mv_getdata IS NOT INITIAL. settings-list_settings-filter = c_label_filter_prefix && ii_event->mv_getdata. ELSE. CLEAR settings-list_settings-filter. " Unexpected request ENDIF. save_settings( ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. ENDCASE. ENDMETHOD. METHOD /apmg/if_apm_gui_hotkeys~get_hotkey_actions. DATA hotkey_action LIKE LINE OF rt_hotkey_actions. hotkey_action-ui_component = 'Package List'. hotkey_action-description = |Global Settings|. hotkey_action-action = /apmg/if_apm_gui_router=>c_action-go_settings. hotkey_action-hotkey = |x|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. hotkey_action-description = |Personal Settings|. hotkey_action-action = /apmg/if_apm_gui_router=>c_action-go_settings_personal. hotkey_action-hotkey = |p|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. hotkey_action-description = |Refresh|. hotkey_action-action = c_action-refresh. hotkey_action-hotkey = |r|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. hotkey_action-description = |Init|. hotkey_action-action = /apmg/if_apm_gui_router=>c_action-apm_init. hotkey_action-hotkey = |t|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. hotkey_action-description = |Install|. hotkey_action-action = /apmg/if_apm_gui_router=>c_action-apm_install. hotkey_action-hotkey = |i|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. hotkey_action-description = |Uninstall|. hotkey_action-action = /apmg/if_apm_gui_router=>c_action-apm_uninstall. hotkey_action-hotkey = |u|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. hotkey_action-description = |Publish|. hotkey_action-action = /apmg/if_apm_gui_router=>c_action-apm_publish. hotkey_action-hotkey = |p|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. hotkey_action-description = |Unpublish|. hotkey_action-action = /apmg/if_apm_gui_router=>c_action-apm_unpublish. hotkey_action-hotkey = |q|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. " registered/handled in js hotkey_action-description = |Previous Package|. hotkey_action-action = `#`. hotkey_action-hotkey = |4|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. hotkey_action-description = |Next Package|. hotkey_action-action = `##`. hotkey_action-hotkey = |6|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. hotkey_action-description = |Show Package|. hotkey_action-action = `###`. hotkey_action-hotkey = |Enter|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. hotkey_action-description = |Focus Filter|. hotkey_action-action = `####`. hotkey_action-hotkey = |f|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. ENDMETHOD. METHOD /apmg/if_apm_gui_menu_provider~get_menu. CONSTANTS: c_dummy_key TYPE string VALUE `?key=#`, c_action_class TYPE string VALUE `action_link`. DATA(commands) = /apmg/cl_apm_html_toolbar=>create( 'apm-package-list-commands' ). commands->add( iv_txt = 'Deprecate' iv_act = |{ /apmg/if_apm_gui_router=>c_action-apm_deprecate }{ c_dummy_key }| iv_class = c_action_class iv_li_class = c_action_class )->add( iv_txt = 'Undeprecate' iv_act = |{ /apmg/if_apm_gui_router=>c_action-apm_undeprecate }{ c_dummy_key }| iv_class = c_action_class iv_li_class = c_action_class )->add( iv_txt = 'Danger' iv_typ = /apmg/if_apm_html=>c_action_type-separator )->add( iv_txt = 'Unpublish' iv_act = |{ /apmg/if_apm_gui_router=>c_action-apm_unpublish }{ c_dummy_key }| iv_class = |{ c_action_class } red| iv_li_class = c_action_class )->add( iv_txt = 'Uninstall' iv_act = |{ /apmg/if_apm_gui_router=>c_action-apm_uninstall }{ c_dummy_key }| iv_class = |{ c_action_class } red| iv_li_class = c_action_class ). DATA(toolbar) = /apmg/cl_apm_html_toolbar=>create( 'apm-package-list' ). toolbar->add( iv_txt = /apmg/cl_apm_html=>icon( 'file' ) && ' Init' iv_act = /apmg/if_apm_gui_router=>c_action-apm_init )->add( iv_txt = /apmg/cl_apm_html=>icon( 'download-solid' ) && ' Install' iv_act = /apmg/if_apm_gui_router=>c_action-apm_install )->add( iv_txt = /apmg/cl_apm_html=>icon( 'upload-solid' ) && ' Publish' iv_act = |{ /apmg/if_apm_gui_router=>c_action-apm_publish }{ c_dummy_key }| iv_class = c_action_class iv_li_class = c_action_class )->add( iv_txt = /apmg/cl_apm_html=>icon( 'chevron-right' ) && ' Commands' io_sub = commands )->add( iv_txt = /apmg/cl_apm_gui_buttons=>settings( ) io_sub = /apmg/cl_apm_gui_menus=>settings( ) )->add( iv_txt = /apmg/cl_apm_html=>icon( 'redo-alt-solid' ) iv_act = c_action-refresh )->add( iv_txt = /apmg/cl_apm_gui_buttons=>advanced( ) io_sub = /apmg/cl_apm_gui_menus=>advanced( ) )->add( iv_txt = /apmg/cl_apm_gui_buttons=>help( ) io_sub = /apmg/cl_apm_gui_menus=>help( ) ). ro_toolbar = toolbar. ENDMETHOD. METHOD /apmg/if_apm_gui_renderable~render. register_handlers( ). " FUTURE " label_colors = zcl_abapgit_repo_labels=>split_colors_into_map( settings-gui_settings-label_colors ) load_settings( ). load_package_list( ). DATA(html) = /apmg/cl_apm_html=>create( ). render_styles( html ). html->add( |
| ). render_header_bar( html ). render_header_label_list( html ). render_package_list( html ). html->add( |
| ). register_deferred_script( get_scripts( ) ). register_deferred_script( get_palette( c_action-select ) ). ri_html = html. ENDMETHOD. METHOD apply_filter. IF settings-list_settings-filter IS INITIAL. RETURN. ENDIF. DATA(prefix_length) = strlen( c_label_filter_prefix ). IF strlen( settings-list_settings-filter ) > prefix_length AND settings-list_settings-filter+0(prefix_length) = c_label_filter_prefix. DATA(filter_label) = settings-list_settings-filter+prefix_length. CASE filter_label. WHEN 'all'. DELETE packages WHERE labels IS INITIAL. WHEN 'none'. DELETE packages WHERE labels IS NOT INITIAL. WHEN OTHERS. LOOP AT packages ASSIGNING FIELD-SYMBOL(). IF line_exists( -labels[ filter_label ] ). DELETE packages INDEX sy-tabix. ENDIF. ENDLOOP. ENDCASE. ELSE. " Regular filter DELETE packages WHERE package NS settings-list_settings-filter AND name NS settings-list_settings-filter AND version NS settings-list_settings-filter AND changed_by NS settings-list_settings-filter. ENDIF. ENDMETHOD. METHOD apply_order_by. DATA: sort_order TYPE abap_sortorder_tab, sort_order_item LIKE LINE OF sort_order. sort_order_item-name = 'FAVORITE'. sort_order_item-descending = abap_true. sort_order_item-astext = abap_true. INSERT sort_order_item INTO TABLE sort_order. IF settings-list_settings-order_by IS NOT INITIAL. CLEAR sort_order_item. IF settings-list_settings-order_by = 'CHANGED_AT'. sort_order_item-name = settings-list_settings-order_by && c_raw_field_suffix. ELSE. sort_order_item-name = settings-list_settings-order_by. sort_order_item-astext = abap_true. ENDIF. sort_order_item-descending = settings-list_settings-order_descending. INSERT sort_order_item INTO TABLE sort_order. ENDIF. SORT packages BY (sort_order). ENDMETHOD. METHOD build_table_scheme. DATA(table_schema) = NEW lcl_table_scheme( ). table_schema->add_column( tech_name = 'FAVORITE' css_class = 'wmin' allow_order_by = abap_false )->add_column( tech_name = 'PACKAGE' display_name = 'Package' css_class = 'package' allow_order_by = abap_true ). IF all_labels IS NOT INITIAL. table_schema->add_column( tech_name = 'LABELS' display_name = 'Labels' allow_order_by = abap_false ). ENDIF. table_schema->add_column( tech_name = 'NAME' display_name = 'Name' css_class = 'name' allow_order_by = abap_true )->add_column( tech_name = 'VERSION' display_name = 'Version' css_class = 'version' allow_order_by = abap_true )->add_column( tech_name = 'DESCRIPTION' display_name = 'Description' css_class = 'description' allow_order_by = abap_true )->add_column( tech_name = 'CHANGED_BY' display_name = 'Changed by' css_class = 'ro-detail' allow_order_by = abap_true )->add_column( tech_name = 'CHANGED_AT' display_name = 'Changed at' css_class = 'ro-detail' add_tz = abap_true allow_order_by = abap_true )->add_column( tech_name = 'KEY' display_name = 'Key' css_class = 'ro-detail nodisplay' allow_order_by = abap_true )->add_column( tech_name = 'GO' css_class = 'ro-go wmin' allow_order_by = abap_false ). result = table_schema->columns. ENDMETHOD. METHOD collect_all_labels. LOOP AT packages ASSIGNING FIELD-SYMBOL(). APPEND LINES OF -labels TO result. ENDLOOP. SORT result. DELETE result WHERE table_line IS INITIAL. DELETE ADJACENT DUPLICATES FROM result. ENDMETHOD. METHOD constructor. super->constructor( ). load_settings( ). " Overwrite setting IF only_favorites = abap_true. settings-list_settings-only_favorites = abap_true. ENDIF. ENDMETHOD. METHOD create. DATA(component) = NEW /apmg/cl_apm_gui_page_list( only_favorites ). result = /apmg/cl_apm_gui_page_hoc=>create( page_title = 'Package List' page_menu_provider = component child_component = component ). ENDMETHOD. METHOD get_palette. DATA(package_list) = packages. DATA(package_count) = lines( package_list ). SORT package_list BY description AS TEXT. DATA(html) = /apmg/cl_apm_html=>create( ). html->add( 'var repoCatalog = [' ). LOOP AT package_list ASSIGNING FIELD-SYMBOL(). DATA(display_name) = escape( val = -description format = cl_abap_format=>e_html_js ) && | ({ -package }, { -name }@{ -version })|. DATA(json) = |\{| && | key: "{ -package }",| && | isOffline: "",| && | displayName: "{ display_name }"| && | \}|. IF sy-tabix < package_count. json = json && ','. ENDIF. html->add( json ). ENDLOOP. html->add( '];' ). html->add( 'var gGoRepoPalette = new CommandPalette(' ). html->add( | createRepoCatalogEnumerator(repoCatalog, "{ action }"), \{| ). html->add( ' toggleKey: "F2",' ). html->add( ' hotkeyDescription: "Go to Package"' ). html->add( '});' ). result = html. ENDMETHOD. METHOD get_scripts. DATA(html) = /apmg/cl_apm_html=>create( ). html->set_title( cl_abap_typedescr=>describe_by_object_ref( me )->get_relative_name( ) ). html->add( 'var gHelper = new RepoOverViewHelper({ focusFilterKey: "f", pageId: "apm-list" });' ). result = html. ENDMETHOD. METHOD load_package_list. packages = /apmg/cl_apm_package_json=>list( instanciate = abap_true is_bundle = abap_false ). ENDMETHOD. METHOD load_settings. TRY. settings = /apmg/cl_apm_settings=>factory( )->get( ). CATCH /apmg/cx_apm_error. " Settings didn't exist, so save the defaults /apmg/cl_apm_settings=>factory( )->set( settings )->save( ). ENDTRY. ENDMETHOD. METHOD prepare_packages. result = packages. LOOP AT result ASSIGNING FIELD-SYMBOL(). READ TABLE settings-package_settings ASSIGNING FIELD-SYMBOL() WITH TABLE KEY package = -package. IF sy-subrc = 0. -favorite = -favorite. -labels = -labels. -write_protected = -write_protected. ENDIF. ENDLOOP. IF settings-list_settings-only_favorites = abap_true. DELETE result WHERE favorite = abap_false. ENDIF. " Hmmm, side effect, not ideal, but we need label list before filter applied all_labels = collect_all_labels( result ). apply_order_by( CHANGING packages = result ). apply_filter( CHANGING packages = result ). ENDMETHOD. METHOD render_action_toolbar. " FUTURE html->add( '' ). ENDMETHOD. METHOD render_filter_bar. html->add( |
| ). html->add( /apmg/cl_apm_gui_chunk_lib=>render_text_input( iv_name = |filter| iv_label = |Filter: { render_filter_help_hint( ) }| iv_value = settings-list_settings-filter ) ). html->add( || ). html->add( |
| ). IF settings-list_settings-only_favorites = abap_true. DATA(icon_class) = `blue`. ELSE. icon_class = `grey`. ENDIF. html->add( '' ). html->add( html->a( iv_txt = | Only Favorites| iv_class = 'command' iv_act = |{ c_action-toggle_favorites }| ) ). html->add( html->a( iv_txt = ' Detail' iv_act = |gHelper.toggleRepoListDetail()| iv_class = 'command' iv_typ = /apmg/if_apm_html=>c_action_type-onclick ) ). html->add( '' ). ENDMETHOD. METHOD render_filter_help_hint. DATA fragments TYPE string_table. APPEND `Filter is applied to all text fields in the below table.` TO fragments. APPEND ` Search works for any portion of the text (so can be a mid part as well).` TO fragments. APPEND `
Starting query from label:xxx will filter appropriate label.` TO fragments. APPEND `Two "special" label queries are available:` TO fragments. APPEND ` all (to select all packages that have at least one label)` TO fragments. APPEND ` and none (to select unlabeled packages).` TO fragments. result = /apmg/cl_apm_gui_chunk_lib=>render_help_hint( concat_lines_of( table = fragments ) ). ENDMETHOD. METHOD render_header_bar. html->add( |
| ). render_filter_bar( html ). render_registry( html ). render_action_toolbar( html ). html->add( |
| ). ENDMETHOD. METHOD render_header_label_list. IF all_labels IS INITIAL. RETURN. ENDIF. html->add( |
| ). html->add( '' ). html->add( /apmg/cl_apm_gui_chunk_lib=>render_label_list( it_labels = all_labels io_label_colors = label_colors iv_clickable_action = c_action-label_filter ) ). html->add( |
| ). ENDMETHOD. METHOD render_package_list. html->add( || ). render_table_header( html ). render_table_body( html ). render_table_footer( html ). html->add( |
| ). ENDMETHOD. METHOD render_registry. IF settings-registry = /apmg/if_apm_settings=>c_registry. DATA(fav_class) = 'transport-box'. " green ELSE. fav_class = 'user-box'. " blue ENDIF. html->add( '' ). html->add( || ). html->add_a( iv_title = 'Registry' iv_txt = settings-registry iv_act = |{ /apmg/if_apm_gui_router=>c_action-url }?url={ settings-registry }| ). html->add( '' ). html->add( '' ). ENDMETHOD. METHOD render_styles. " Emoji Styles DATA(emoji_styles) = concat_lines_of( table = /apmg/cl_apm_emoji=>create( )->get_emoji_css( ) sep = cl_abap_char_utilities=>newline ). html->add( '' ). ENDMETHOD. METHOD render_table_body. html->add( '' ). DATA(list) = prepare_packages( ). LOOP AT list ASSIGNING FIELD-SYMBOL(). render_table_item( html = html package = ). ENDLOOP. html->add( || ). ENDMETHOD. METHOD render_table_footer. IF settings-list_settings-only_favorites = abap_true. html->add( `` ). html->add( `` ). html->add( |(Only favorites are shown. { html->a( iv_txt = |Show All| iv_act = |{ c_action-toggle_favorites }?force_state={ abap_false }| ) })| ). html->add( `` ). html->add( `` ). ENDIF. ENDMETHOD. METHOD render_table_header. html->add( /apmg/cl_apm_gui_chunk_lib=>render_table_header( it_col_spec = build_table_scheme( ) iv_order_by = settings-list_settings-order_by iv_order_descending = settings-list_settings-order_descending ) ). ENDMETHOD. METHOD render_table_item. " Start of row IF package-favorite = abap_true. DATA(fav_class) = ' class="favorite"'. DATA(fav_color) = 'blue'. ELSE. fav_class = ''. fav_color = 'grey'. ENDIF. html->add( || ). " Favorite DATA(favorite_icon) = html->icon( iv_name = |star/{ fav_color }| iv_class = 'pad-sides' iv_hint = 'Click to toggle favorite' ). html->td( iv_class = 'wmin' iv_content = html->a( iv_act = |{ /apmg/if_apm_gui_router=>c_action-favorite_package }?package={ package-package }| iv_txt = favorite_icon ) ). " Package IF package-write_protected = abap_true. DATA(lock_icon) = html->icon( iv_name = 'lock/grey70' iv_class = 'm-em5-sides' iv_hint = 'Locked against changes by apm' ). ENDIF. html->td( ii_content = /apmg/cl_apm_gui_chunk_lib=>render_package_name( iv_package = package-package iv_suppress_title = xsdbool( settings-list_settings-only_favorites = abap_false ) ) ). " Labels IF all_labels IS NOT INITIAL. html->td( iv_content = /apmg/cl_apm_gui_chunk_lib=>render_label_list( it_labels = package-labels io_label_colors = label_colors ) iv_class = 'labels' ). ENDIF. " Name html->td( html->a( iv_txt = package-name iv_act = |{ c_action-select }?key={ package-package }| ) ). " Version html->td( html->a( iv_txt = package-version iv_act = |{ c_action-select }?key={ package-package }| ) && lock_icon ). " Description html->td( html->a( iv_txt = /apmg/cl_apm_emoji=>create( )->format_emoji( package-description ) iv_act = |{ c_action-select }?key={ package-package }| ) ). " Details: changed by html->td( iv_class = 'ro-detail' ii_content = /apmg/cl_apm_gui_chunk_lib=>render_user_name( iv_username = package-changed_by iv_suppress_title = xsdbool( settings-list_settings-only_favorites = abap_false ) ) ). " Details: changed at html->td( iv_class = 'ro-detail' iv_content = |{ package-changed_at }| ). " Details: key for navigation html->td( iv_class = 'ro-detail nodisplay' iv_content = |{ package-package }| ). " Go-to action html->td( iv_class = 'ro-go wmin' iv_content = html->a( iv_title = 'Open' iv_txt = '▸' iv_act = |{ c_action-select }?key={ package-package }| ) ). html->add( `` ). ENDMETHOD. METHOD save_settings. /apmg/cl_apm_settings=>factory( )->set( settings )->save( ). ENDMETHOD. METHOD set_filter. READ TABLE postdata ASSIGNING FIELD-SYMBOL() INDEX 1. IF sy-subrc = 0. FIND FIRST OCCURRENCE OF REGEX 'filter=(.*)' IN SUBMATCHES settings-list_settings-filter ##SUBRC_OK ##REGEX_POSIX. ENDIF. settings-list_settings-filter = condense( settings-list_settings-filter ). save_settings( ). ENDMETHOD. METHOD set_order_by. IF settings-list_settings-order_by <> order_by. set_order_direction( abap_false ). " Reset ordering ENDIF. settings-list_settings-order_by = order_by. save_settings( ). ENDMETHOD. METHOD set_order_direction. settings-list_settings-order_descending = order_descending. save_settings( ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_page_package IMPLEMENTATION. METHOD /apmg/if_apm_gui_event_handler~on_event. CASE ii_event->mv_action. WHEN c_action-view_readme OR c_action-view_readme_code OR c_action-view_readme_raw. markdown-data = get_markdown( ). view = ii_event->mv_action. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-view_dependencies OR c_action-view_json. package_json = get_package_json( ). view = ii_event->mv_action. rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-edit_readme. rs_handled-page = /apmg/cl_apm_gui_page_db_entry=>create( key = /apmg/cl_apm_readme=>get_package_key( package ) edit_mode = abap_true back_on_save = abap_true ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-new_page_w_bookmark. WHEN c_action-copy_readme. copy_to_clipboard( get_markdown( ) ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. WHEN c_action-download_readme. download_to_file( filename = 'README.md' extension = 'md' data = get_markdown( ) ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. WHEN c_action-edit_json. rs_handled-page = /apmg/cl_apm_gui_page_db_entry=>create( key = /apmg/cl_apm_package_json=>get_package_key( package ) edit_mode = abap_true back_on_save = abap_true ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-new_page_w_bookmark. WHEN c_action-copy_json. copy_to_clipboard( get_json_data( ) ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. WHEN c_action-download_json. download_to_file( filename = 'package.abap.json' extension = 'json' data = get_json_data( ) ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. WHEN c_action-update_dependencies. DATA(registry) = /apmg/cl_apm_settings=>factory( )->get( )-registry. /apmg/cl_apm_command_update=>run( registry = registry package = package ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-re_render. WHEN c_action-add_dependency OR c_action-remove_dependency. /apmg/cl_apm_roadmap=>planned( `The feature has not been implemented yet. ` && `Edit the manifest and then update the dependencies.` ). rs_handled-state = /apmg/cl_apm_gui=>c_event_state-no_more_act. ENDCASE. ENDMETHOD. METHOD /apmg/if_apm_gui_hotkeys~get_hotkey_actions. DATA hotkey_action LIKE LINE OF rt_hotkey_actions. hotkey_action-ui_component = 'Package View'. hotkey_action-description = |Readme|. hotkey_action-action = c_action-view_readme. hotkey_action-hotkey = |r|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. hotkey_action-description = |Dependencies|. hotkey_action-action = c_action-view_dependencies. hotkey_action-hotkey = |d|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. hotkey_action-description = |Manifest|. hotkey_action-action = c_action-view_json. hotkey_action-hotkey = |m|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. " Commands hotkey_action-description = |Publish|. hotkey_action-action = /apmg/if_apm_gui_router=>c_action-apm_publish. hotkey_action-hotkey = |p|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. hotkey_action-description = |Unpublish|. hotkey_action-action = /apmg/if_apm_gui_router=>c_action-apm_unpublish. hotkey_action-hotkey = |q|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. hotkey_action-description = |Uninstall|. hotkey_action-action = /apmg/if_apm_gui_router=>c_action-apm_uninstall. hotkey_action-hotkey = |u|. INSERT hotkey_action INTO TABLE rt_hotkey_actions. ENDMETHOD. METHOD /apmg/if_apm_gui_menu_provider~get_menu. CONSTANTS: c_key TYPE string VALUE `?key=`, c_action_class TYPE string VALUE `action_link`. DATA(commands) = /apmg/cl_apm_html_toolbar=>create( 'apm-package-view-commands' ). DATA(id) = /apmg/cl_apm_package_json=>get_id_from_package( package ). commands->add( iv_txt = 'Publish' iv_act = |{ /apmg/if_apm_gui_router=>c_action-apm_publish }{ c_key }{ id }| )->add( iv_txt = 'Deprecate' iv_act = |{ /apmg/if_apm_gui_router=>c_action-apm_deprecate }{ c_key }{ id }| )->add( iv_txt = 'Undeprecate' iv_act = |{ /apmg/if_apm_gui_router=>c_action-apm_undeprecate }{ c_key }{ id }| )->add( iv_txt = 'Danger' iv_typ = /apmg/if_apm_html=>c_action_type-separator )->add( iv_txt = 'Unpublish' iv_act = |{ /apmg/if_apm_gui_router=>c_action-apm_unpublish }{ c_key }{ id }| iv_class = 'red' )->add( iv_txt = 'Uninstall' iv_act = |{ /apmg/if_apm_gui_router=>c_action-apm_uninstall }{ c_key }{ id }| iv_class = 'red' ). DATA(toolbar) = /apmg/cl_apm_html_toolbar=>create( 'apm-package-view' )->add( iv_txt = /apmg/cl_apm_html=>icon( 'markdown' ) && ' Readme' iv_act = c_action-view_readme )->add( " TODO: Replace with dependencies icon iv_txt = /apmg/cl_apm_html=>icon( 'code-fork-solid' ) && ' Dependencies' iv_act = c_action-view_dependencies )->add( iv_txt = /apmg/cl_apm_html=>icon( 'code-solid' ) && ' Manifest' iv_act = c_action-view_json )->add( iv_txt = /apmg/cl_apm_html=>icon( 'chevron-right' ) && ' Commands' io_sub = commands )->add( iv_txt = 'Back' iv_act = /apmg/if_apm_gui_router=>c_action-go_back ). ro_toolbar = toolbar. ENDMETHOD. METHOD /apmg/if_apm_gui_renderable~render. register_handlers( ). DATA(html) = /apmg/cl_apm_html=>create( ). markdown-data = get_markdown( ). package_json = get_package_json( ). render_styles( html ). render_top( html ). html->add( '
' ). render_header( html ). render_content( html ). render_footer( html ). html->add( '
' ). ri_html = html. ENDMETHOD. METHOD constructor. super->constructor( ). IF package IS INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Missing package'. ENDIF. me->package = package. view = c_default-view. markdown-path = c_default-path. markdown-filename = c_default-filename. markdown-data = get_markdown( ). package_json = get_package_json( ). gui_services( )->cache_asset( iv_type = 'image' iv_subtype = 'png' iv_url = |{ c_markdown-logo }| iv_xdata = get_mime( c_markdown-mime ) ). ENDMETHOD. METHOD copy_to_clipboard. " Note: export is buggy when using table of raw. Use table of char! DATA clip_table TYPE STANDARD TABLE OF char1024 WITH EMPTY KEY. SPLIT data AT cl_abap_char_utilities=>newline INTO TABLE clip_table. DATA(frontend_services) = /apmg/cl_apm_gui_factory=>get_frontend_services( ). frontend_services->clipboard_export( clip_table ). MESSAGE 'Copied to clipboard' TYPE 'S'. ENDMETHOD. METHOD create. DATA(component) = NEW /apmg/cl_apm_gui_page_package( package ). result = /apmg/cl_apm_gui_page_hoc=>create( page_title = 'Package View' page_menu_provider = component child_component = component ). ENDMETHOD. METHOD download_to_file. TRY. DATA(bin_data) = zcl_abapgit_convert=>string_to_xstring_utf8( data ). CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = error->get_text( ). ENDTRY. DATA(frontend_services) = /apmg/cl_apm_gui_factory=>get_frontend_services( ). DATA(path) = frontend_services->show_file_save_dialog( iv_title = 'apm - File Download' iv_extension = extension iv_default_filename = filename ). frontend_services->file_download( iv_path = path iv_xstr = bin_data ). MESSAGE 'Saved to file' TYPE 'S'. ENDMETHOD. METHOD get_json_data. " Always load data since it can be edited in another page result = /apmg/cl_apm_package_json=>factory( package )->load( )->get_json( ). ENDMETHOD. METHOD get_markdown. " Always load data since it can be edited in another page result = /apmg/cl_apm_readme=>factory( package )->load( )->get( ). ENDMETHOD. METHOD get_mime. DATA: size_value TYPE wwwparams-value, mime_data TYPE STANDARD TABLE OF w3mime WITH KEY line. DATA(key) = VALUE wwwdatatab( relid = 'MI' objid = mime_name ). " Get exact file size CALL FUNCTION 'WWWPARAMS_READ' EXPORTING relid = key-relid objid = key-objid name = 'filesize' IMPORTING value = size_value EXCEPTIONS entry_not_exists = 1. IF sy-subrc IS NOT INITIAL. RETURN. ENDIF. DATA(size) = size_value. " Get binary data CALL FUNCTION 'WWWDATA_IMPORT' EXPORTING key = key TABLES mime = mime_data EXCEPTIONS wrong_object_type = 1 import_error = 2. IF sy-subrc IS NOT INITIAL. RETURN. ENDIF. LOOP AT mime_data INTO DATA(mime_line). CONCATENATE result mime_line-line INTO result IN BYTE MODE. ENDLOOP. result = result(size). ENDMETHOD. METHOD get_package_boxed. IF value IS INITIAL. result = |{ name }|. ELSE. result = |{ name }: { value }|. ENDIF. ENDMETHOD. METHOD get_package_json. " Always load data since it can be edited in another page result = /apmg/cl_apm_package_json=>factory( package )->load( )->get( ). ENDMETHOD. METHOD get_root_href. IF url CS 'github.com'. result = |{ url }/blob/{ branch }|. ELSEIF url CS 'gitlab.com'. result = |{ url }/-/blob/{ branch }|. ELSE. " TODO: Add other git hosts result = |{ url }/blob/{ branch }|. ENDIF. ENDMETHOD. METHOD get_root_img. IF url CS 'github.com'. result = |{ url }/raw/{ branch }|. ELSEIF url CS 'gitlab.com'. result = |{ url }/-/raw/{ branch }|. ELSE. " TODO: Add other git hosts result = |{ url }/raw/{ branch }|. ENDIF. ENDMETHOD. METHOD get_toolbar. CASE view. WHEN c_action-view_readme OR c_action-view_readme_code OR c_action-view_readme_raw. DATA(readme_menu) = /apmg/cl_apm_html_toolbar=>create( 'apm-package-readme' )->add( iv_txt = 'Rendered' iv_chk = boolc( view = c_action-view_readme OR view IS INITIAL ) iv_act = c_action-view_readme )->add( iv_txt = 'Markdown' iv_chk = boolc( view = c_action-view_readme_code ) iv_act = c_action-view_readme_code )->add( iv_txt = 'Raw' iv_chk = boolc( view = c_action-view_readme_raw ) iv_act = c_action-view_readme_raw ). result = /apmg/cl_apm_html_toolbar=>create( 'apm-package-readme-actions' )->add( iv_txt = 'View' io_sub = readme_menu )->add( iv_txt = 'Copy' iv_act = c_action-copy_readme )->add( iv_txt = 'Download' iv_act = c_action-download_readme )->add( iv_txt = 'Edit' iv_act = |{ c_action-edit_readme }?key={ package }| ). WHEN c_action-view_dependencies. result = /apmg/cl_apm_html_toolbar=>create( 'apm-package-dependencies-actions' )->add( iv_txt = 'Add' iv_act = |{ c_action-add_dependency }?key={ package }| )->add( iv_txt = 'Remove' iv_act = |{ c_action-remove_dependency }?key={ package }| )->add( iv_txt = 'Update' iv_act = |{ c_action-update_dependencies }?key={ package }| ). WHEN c_action-view_json. result = /apmg/cl_apm_html_toolbar=>create( 'apm-package-manifest-actions' )->add( iv_txt = 'Copy' iv_act = c_action-copy_json )->add( iv_txt = 'Download' iv_act = c_action-download_json )->add( iv_txt = 'Edit' iv_act = |{ c_action-edit_json }?key={ package }| ). WHEN OTHERS. ASSERT 0 = 1. ENDCASE. ENDMETHOD. METHOD render_content. html->add( '
' ). CASE view. WHEN c_action-view_readme. render_markdown( html ). WHEN c_action-view_readme_code. render_markdown_source( html = html raw = abap_false ). WHEN c_action-view_readme_raw. render_markdown_source( html = html raw = abap_true ). WHEN c_action-view_dependencies. render_dependencies( html ). WHEN c_action-view_json. render_json( html ). WHEN OTHERS. ASSERT 0 = 1. ENDCASE. html->add( '
' ). ENDMETHOD. METHOD render_dependencies. html->add( '
' ). DATA(none) = abap_true. IF package_json-dependencies IS NOT INITIAL. html->add( |

Dependencies ({ lines( package_json-dependencies ) })

| ). render_dependencies_table( html = html dependencies = package_json-dependencies bundle_dependencies = package_json-bundle_dependencies ). none = abap_false. ENDIF. IF package_json-dev_dependencies IS NOT INITIAL. html->add( |

Dev Dependencies ({ lines( package_json-dev_dependencies ) })

| ). render_dependencies_table( html = html dependencies = package_json-dev_dependencies ). none = abap_false. ENDIF. IF package_json-peer_dependencies IS NOT INITIAL. html->add( |

Peer Dependencies ({ lines( package_json-peer_dependencies ) })

| ). render_dependencies_table( html = html dependencies = package_json-peer_dependencies ). none = abap_false. ENDIF. IF package_json-optional_dependencies IS NOT INITIAL. html->add( |

Optional Dependencies ({ lines( package_json-optional_dependencies ) })

| ). render_dependencies_table( html = html dependencies = package_json-optional_dependencies ). none = abap_false. ENDIF. IF none = abap_true. html->add( '

This package has no dependencies

' ). ENDIF. html->add( '
' ). ENDMETHOD. METHOD render_dependencies_table. DATA installed_package TYPE /apmg/if_apm_package_json=>ty_package. DATA(list) = /apmg/cl_apm_package_json=>list( instanciate = abap_true ). html->add( '' ). html->add( '' ). html->add( '' ). html->add( '' ). html->add( '' ). html->add( '' ). html->add( '' ). html->add( '' ). LOOP AT dependencies ASSIGNING FIELD-SYMBOL(). html->add( '' ). html->td( get_package_boxed( name = -key value = -range ) ). CLEAR installed_package. IF line_exists( bundle_dependencies[ table_line = -key ] ). IF line_exists( list[ name = -key parent = package bundle = abap_true ] ) ##PRIMKEY[NAME]. installed_package = list[ name = -key parent = package bundle = abap_true ] ##PRIMKEY[NAME]. ENDIF. html->td( 'Bundled' ). ELSEIF line_exists( list[ KEY name COMPONENTS name = -key ] ). installed_package = list[ KEY name COMPONENTS name = -key ]. html->td( 'Global' ). ELSE. html->td( '' ). ENDIF. IF installed_package IS INITIAL. html->td( '' ). html->td( '' ). ELSE. html->td( ii_content = /apmg/cl_apm_gui_chunk_lib=>render_package_name( installed_package-package ) ). html->td( installed_package-version ). ENDIF. DATA(satisfies) = /apmg/cl_apm_semver_functions=>satisfies( version = installed_package-version range = -range ). IF satisfies = abap_true. html->td( html->icon( 'check/success' ) ). ELSE. html->td( html->icon( 'bolt/warning' ) ). ENDIF. html->add( '' ). ENDLOOP. html->add( '
RangeTypePackageVersionStatus
' ). ENDMETHOD. METHOD render_footer. " Someday... html->add( '' ). ENDMETHOD. METHOD render_header. CASE view. WHEN c_action-view_readme OR c_action-view_readme_code OR c_action-view_readme_raw. IF markdown-data IS INITIAL. html->add( '
' ). html->add( 'Readme not found' ). html->add( '
' ). ELSE. render_header_content( html = html image = /apmg/cl_apm_html=>icon( 'markdown' ) text = |{ markdown-path+1 }{ markdown-filename }| ). ENDIF. WHEN c_action-view_dependencies. " TODO: Replace with dependencies icon render_header_content( html = html image = /apmg/cl_apm_html=>icon( 'code-fork-solid' ) text = 'Dependencies' ). WHEN c_action-view_json. render_header_content( html = html image = /apmg/cl_apm_html=>icon( 'code-solid' ) text = 'package.abap.json' ). WHEN OTHERS. ASSERT 0 = 1. ENDCASE. ENDMETHOD. METHOD render_header_content. html->add( '
' ). html->add( image ). html->add( '' ). html->add( text ). html->add( '' ). html->add( '
' ). ENDMETHOD. METHOD render_json. DATA(highlighter) = /apmg/cl_apm_highlighter_facto=>create( '*.json' ). DATA(json_data) = get_json_data( ). SPLIT json_data AT |\n| INTO TABLE DATA(json). html->add( '
' ). html->add( '
' ).
    LOOP AT json INTO json_data.
      html->add( highlighter->process_line( json_data ) ).
    ENDLOOP.
    html->add( '
' ). html->add( '
' ). ENDMETHOD. METHOD render_markdown. DATA(url) = package_json-repository-url. " Heuristic to determine which branch was used for README urls and images IF markdown-data CS '/master/'. DATA(branch) = `master`. ELSE. branch = `main`. ENDIF. DATA(markdown_service) = NEW /apmg/cl_apm_markdown( root_href = get_root_href( url = url branch = branch ) root_img = get_root_img( url = url branch = branch ) path = markdown-path sapevent = abap_true ). DATA(markdown_text) = markdown_service->text( markdown-data ). " Output with Emoji html->add( '
' ). html->add( /apmg/cl_apm_emoji=>create( )->format_emoji( markdown_text ) ). html->add( '
' ). ENDMETHOD. METHOD render_markdown_source. DATA(markdown_text) = markdown-data. IF raw = abap_true. markdown_text = escape( val = markdown_text format = cl_abap_format=>e_html_text ). ENDIF. " Same style as diffs html->add( '
' ). html->add( || ). SPLIT markdown_text AT cl_abap_char_utilities=>newline INTO TABLE DATA(lines). DATA(code_block_language) = `markdown`. LOOP AT lines ASSIGNING FIELD-SYMBOL(). DATA(line_number) = sy-tabix. IF raw = abap_true. DATA(markup) = . ELSE. " Detect code block FIND REGEX '^```\s*(.*)' IN SUBMATCHES DATA(codeblock) ##REGEX_POSIX. IF sy-subrc = 0. code_block_language = 'markdown'. " falls back to txt ENDIF. markup = /apmg/cl_apm_markdown_syn=>process( source = language = code_block_language ). IF codeblock IS NOT INITIAL. code_block_language = codeblock. ELSE. code_block_language = 'markdown'. ENDIF. ENDIF. html->add( '' ). html->add( || ). html->add( || ). html->add( '' ). ENDLOOP. html->add( '
{ markup }
' ). html->add( '
' ). ENDMETHOD. METHOD render_styles. " Emoji Styles DATA(emoji_styles) = concat_lines_of( table = /apmg/cl_apm_emoji=>create( )->get_emoji_css( ) sep = cl_abap_char_utilities=>newline ). html->add( '' ). " Markdown Styles html->add( '' ). ENDMETHOD. METHOD render_top. html->add( '
' ). html->add( '' ). html->add( '' ). html->add( '' ). html->add( '
' ). html->add( /apmg/cl_apm_gui_chunk_lib=>render_package_name( package ) ). html->add( '' ). html->add( get_package_boxed( name = package_json-name value = package_json-version ) ). html->add( '' ). " TODO: link to apm registry * html->add( '' ) * html->add( /apmg/cl_apm_gui_chunk_lib=>render_registry_link( * iv_name = package_json-name * iv_version = package_json-version ) ) * html->add( '' ) html->add( '' ). html->add( get_toolbar( )->render( iv_right = abap_true ) ). html->add( '
' ). html->add( '
' ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_gui_utils IMPLEMENTATION. METHOD is_event_handler. DATA li_event_handler TYPE REF TO /apmg/if_apm_gui_event_handler. TRY. li_event_handler ?= io_obj. rv_yes = abap_true. CATCH cx_sy_move_cast_error. rv_yes = abap_false. ENDTRY. ENDMETHOD. METHOD is_renderable. DATA li_renderable TYPE REF TO /apmg/if_apm_gui_renderable. TRY. li_renderable ?= io_obj. rv_yes = abap_true. CATCH cx_sy_move_cast_error. rv_yes = abap_false. ENDTRY. ENDMETHOD. ENDCLASS. CLASS lcl_out DEFINITION. PUBLIC SECTION. CLASS-METHODS convert IMPORTING !string TYPE string RETURNING VALUE(result) TYPE xstring RAISING /apmg/cx_apm_error. PRIVATE SECTION. CLASS-DATA conv_new TYPE REF TO object. CLASS-DATA conv_old TYPE REF TO object. ENDCLASS. CLASS lcl_out IMPLEMENTATION. METHOD convert. IF conv_new IS INITIAL AND conv_old IS INITIAL. TRY. CALL METHOD ('CL_ABAP_CONV_CODEPAGE')=>create_out RECEIVING instance = conv_new. CATCH cx_sy_dyn_call_illegal_class. DATA(class) = 'CL_ABAP_CONV_OUT_CE'. CALL METHOD (class)=>create EXPORTING encoding = 'UTF-8' RECEIVING conv = conv_old. ENDTRY. ENDIF. TRY. IF conv_new IS NOT INITIAL. CALL METHOD conv_new->('IF_ABAP_CONV_OUT~CONVERT') EXPORTING source = string RECEIVING result = result. ELSE. CALL METHOD conv_old->('CONVERT') EXPORTING data = string IMPORTING buffer = result. ENDIF. CATCH cx_parameter_invalid_range cx_sy_codepage_converter_init cx_sy_conversion_codepage cx_parameter_invalid_type INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_highlighter IMPLEMENTATION. METHOD add_rule. DATA(rule) = VALUE ty_rule( token = token style = style relevant_submatch = submatch ). IF regex IS NOT INITIAL. rule-regex = NEW #( pattern = regex ignore_case = abap_true ) ##REGEX_POSIX. ENDIF. APPEND rule TO rules. ENDMETHOD. METHOD apply_style. DATA(escaped) = escape( val = line format = cl_abap_format=>e_html_text ). escaped = show_hidden_chars( escaped ). IF class IS NOT INITIAL. result = |{ escaped }|. ELSE. result = escaped. ENDIF. ENDMETHOD. METHOD extend_matches. DATA(line_len) = strlen( line ). SORT matches BY offset. " Add entries referring to parts of text that should not be formatted DATA(last_pos) = 0. DATA(length) = 0. LOOP AT matches ASSIGNING FIELD-SYMBOL(). IF -offset > last_pos. length = -offset - last_pos. DATA(match) = VALUE ty_match( token = c_token_none offset = last_pos length = length ). INSERT match INTO matches INDEX sy-tabix. ENDIF. last_pos = -offset + -length. ENDLOOP. " Add remainder of the string IF line_len > last_pos. match = VALUE ty_match( token = c_token_none offset = last_pos length = line_len - last_pos ). APPEND match TO matches. ENDIF. ENDMETHOD. METHOD format_line. TRY. LOOP AT matches ASSIGNING FIELD-SYMBOL(). DATA(chunk) = substring( val = line off = -offset len = -length ). " Failed read equals no style READ TABLE rules INTO DATA(rule) WITH KEY token = -token. IF sy-subrc = 0. chunk = apply_style( line = chunk class = rule-style ). ELSE. chunk = apply_style( chunk ). ENDIF. result = result && chunk. ENDLOOP. CATCH cx_sy_range_out_of_bounds. " If issue with invalid substring, then return unformatted line result = line. ENDTRY. ENDMETHOD. METHOD is_whitespace. "/^\s+$/ DATA(whitespace) = ` ` && cl_abap_char_utilities=>horizontal_tab && cl_abap_char_utilities=>cr_lf. rv_result = xsdbool( string CO whitespace ). ENDMETHOD. METHOD order_matches. ENDMETHOD. METHOD parse_line. " Process syntax-dependent regex table and find all matches LOOP AT rules ASSIGNING FIELD-SYMBOL() WHERE regex IS BOUND. DATA(regex) = -regex. DATA(matcher) = regex->create_matcher( text = line ). DATA(matches) = matcher->find_all( ). " Save matches into custom table with predefined tokens LOOP AT matches ASSIGNING FIELD-SYMBOL(). IF -relevant_submatch = 0. DATA(match) = VALUE ty_match( token = -token offset = -offset length = -length ). APPEND match TO result. ELSE. READ TABLE -submatches ASSIGNING FIELD-SYMBOL() INDEX -relevant_submatch. "submatch might be empty if only discarded parts matched IF sy-subrc = 0 AND -offset >= 0 AND -length > 0. match = VALUE ty_match( token = -token offset = -offset length = -length ). APPEND match TO result. ENDIF. ENDIF. ENDLOOP. ENDLOOP. ENDMETHOD. METHOD process_line. IF line IS INITIAL OR is_whitespace( line ) = abap_true. result = show_hidden_chars( line ). RETURN. ENDIF. DATA(matches) = parse_line( line ). order_matches( EXPORTING line = line CHANGING matches = matches ). extend_matches( EXPORTING line = line CHANGING matches = matches ). result = format_line( line = line matches = matches ). ENDMETHOD. METHOD set_hidden_chars. me->hidden_chars = hidden_chars. ENDMETHOD. METHOD show_hidden_chars. TYPES ty_bom TYPE x LENGTH 3. result = line. IF hidden_chars = abap_true. " The order matters :-) REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>horizontal_tab IN result WITH ' → '. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf IN result WITH '¶'. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>newline IN result WITH '↵'. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf(1) IN result WITH '¶'. REPLACE ALL OCCURRENCES OF ` ` IN result WITH '·'. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>form_feed IN result WITH ''. IF strlen( result ) BETWEEN 1 AND 2. TRY. DATA(bom) = CONV ty_bom( lcl_out=>convert( result ) ). CATCH /apmg/cx_apm_error ##NO_HANDLER. ENDTRY. IF bom(2) = cl_abap_char_utilities=>byte_order_mark_big. result = ''. " UTF-16 big-endian (FE FF) ENDIF. IF bom(2) = cl_abap_char_utilities=>byte_order_mark_little. result = ''. " UTF-16 little-endian (FF FE) ENDIF. IF bom(3) = cl_abap_char_utilities=>byte_order_mark_utf8. result = '¤'. " UTF-8 (EF BB BF) ENDIF. ENDIF. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_highlighter_abap IMPLEMENTATION. METHOD class_constructor. init_keywords( ). ENDMETHOD. METHOD constructor. super->constructor( ). " Initialize instances of regular expression add_rule( regex = c_regex-keyword token = c_token-keyword style = c_css-keyword ). add_rule( regex = c_regex-comment token = c_token-comment style = c_css-comment ). add_rule( regex = c_regex-text token = c_token-text style = c_css-text ). ENDMETHOD. METHOD init_keywords. DATA(list) = '&&|?TO|ABAP-SOURCE|ABBREVIATED|ABS|ABSTRACT|ACCEPT|ACCEPTING' && '|ACCORDING|ACOS|ACTIVATION|ACTUAL|ADD|ADD-CORRESPONDING|ADJACENT|AFTER|ALIAS' && '|ALIASES|ALIGN|ALL|ALLOCATE|ALPHA|ANALYSIS|ANALYZER|AND|ANY|APPEND|APPENDAGE' && '|APPENDING|APPLICATION|ARCHIVE|AREA|ARITHMETIC|AS|ASCENDING|ASIN|ASPECT|ASSERT' && '|ASSIGN|ASSIGNED|ASSIGNING|ASSOCIATION|ASYNCHRONOUS|AT|ATAN|ATTRIBUTES|AUTHORITY' && '|AUTHORITY-CHECK|AVG|BACK|BACKGROUND|BACKUP|BACKWARD|BADI|BASE|BEFORE|BEGIN' && '|BETWEEN|BIG|BINARY|BIT|BIT-AND|BIT-NOT|BIT-OR|BIT-XOR|BLACK|BLANK' && '|BLANKS|BLOB|BLOCK|BLOCKS|BLUE|BOUND|BOUNDARIES|BOUNDS|BOXED|BREAK-POINT|BT' && '|BUFFER|BY|BYPASSING|BYTE|BYTE-CA|BYTE-CN|BYTE-CO|BYTE-CS|BYTE-NA|BYTE-NS' && '|BYTE-ORDER|C|CA|CALL|CALLING|CASE|CAST|CASTING|CATCH|CEIL|CENTER|CENTERED' && '|CHAIN|CHAIN-INPUT|CHAIN-REQUEST|CHANGE|CHANGING|CHANNELS|CHARACTER|CHARLEN' && '|CHAR-TO-HEX|CHECK|CHECKBOX|CI_|CIRCULAR|CLASS|CLASS-CODING|CLASS-DATA' && '|CLASS-EVENTS|CLASS-METHODS|CLASS-POOL|CLEANUP|CLEAR|CLIENT|CLOB|CLOCK|CLOSE' && '|CN|CO|COALESCE|CODE|CODING|COL_BACKGROUND|COL_GROUP|COL_HEADING|COL_KEY' && '|COL_NEGATIVE|COL_NORMAL|COL_POSITIVE|COL_TOTAL|COLLECT|COLOR|COLUMN|COLUMNS' && '|COMMENT|COMMENTS|COMMIT|COMMON|COMMUNICATION|COMPARING|COMPONENT|COMPONENTS' && '|COMPRESSION|COMPUTE|CONCAT|CONCATENATE|COND|CONDENSE|CONDITION|CONNECT' && '|CONNECTION|CONSTANTS|CONTEXT|CONTEXTS|CONTINUE|CONTROL|CONTROLS|CONV|CONVERSION' && '|CONVERT|COPIES|COPY|CORRESPONDING|COS|COSH|COUNT|COUNTRY|COVER|CP|CPI|CREATE' && '|CREATING|CRITICAL|CS|CURRENCY|CURRENCY_CONVERSION|CURRENT|CURSOR|CURSOR-SELECTION' && '|CUSTOMER|CUSTOMER-FUNCTION|DANGEROUS|DATA|DATABASE|DATAINFO|DATASET|DATE' && '|DAYLIGHT|DBMAXLEN|DD/MM/YY|DD/MM/YYYY|DDMMYY|DEALLOCATE|DECIMAL_SHIFT|DECIMALS' && '|DECLARATIONS|DEEP|DEFAULT|DEFERRED|DEFINE|DEFINING|DEFINITION|DELETE|DELETING' && '|DEMAND|DEPARTMENT|DESCENDING|DESCRIBE|DESTINATION|DETAIL|DIALOG|DIRECTORY' && '|DISCONNECT|DISPLAY|DISPLAY-MODE|DISTANCE|DISTINCT|DIV|DIVIDE|DIVIDE-CORRESPONDING' && '|DIVISION|DO|DUMMY|DUPLICATE|DUPLICATES|DURATION|DURING|DYNAMIC|DYNPRO' && '|EDIT|EDITOR-CALL|ELSE|ELSEIF|EMPTY|ENABLED|ENABLING|ENCODING|END|ENDAT|ENDCASE' && '|ENDCATCH|ENDCHAIN|ENDCLASS|ENDDO|ENDENHANCEMENT|END-ENHANCEMENT-SECTION' && '|ENDEXEC|ENDFORM|ENDFUNCTION|ENDIAN|ENDIF|ENDING|ENDINTERFACE' && '|END-LINES|ENDLOOP|ENDMETHOD|ENDMODULE|END-OF-DEFINITION|END-OF-FILE' && '|END-OF-PAGE|END-OF-SELECTION|ENDON|ENDPROVIDE|ENDSELECT|ENDTRY|ENDWHILE' && '|ENGINEERING|ENHANCEMENT|ENHANCEMENT-POINT|ENHANCEMENTS|ENHANCEMENT-SECTION' && '|ENTRIES|ENTRY|ENVIRONMENT|EQ|EQUIV|ERRORMESSAGE|ERRORS|ESCAPE|ESCAPING' && '|EVENT|EVENTS|EXACT|EXCEPT|EXCEPTION|EXCEPTIONS|EXCEPTION-TABLE|EXCLUDE|EXCLUDING' && '|EXEC|EXECUTE|EXISTS|EXIT|EXIT-COMMAND|EXP|EXPAND|EXPANDING|EXPIRATION|EXPLICIT' && '|EXPONENT|EXPORT|EXPORTING|EXTEND|EXTENDED|EXTENSION|EXTRACT|FAIL|FETCH|FIELD' && '|FIELD-GROUPS|FIELDS|FIELD-SYMBOL|FIELD-SYMBOLS|FILE|FILTER|FILTERS|FILTER-TABLE' && '|FINAL|FIND|FIRST|FIRST-LINE|FIXED-POINT|FKEQ|FKGE|FLOOR|FLUSH|FONT|FOR|FORM' && '|FORMAT|FORWARD|FOUND|FRAC|FRAME|FRAMES|FREE|FRIENDS|FROM|FUNCTION|FUNCTIONALITY' && '|FUNCTION-POOL|FURTHER|GAPS|GE|GENERATE|GET|GIVING|GKEQ|GKGE|GLOBAL|GRANT' && '|GREEN|GROUP|GROUPS|GT|HANDLE|HANDLER|HARMLESS|HASHED|HAVING|HDB|HEADER|HEADERS' && '|HEADING|HEAD-LINES|HELP-ID|HELP-REQUEST|HIDE|HIGH|HINT|HOLD|HOTSPOT|I|ICON|ID' && '|IDENTIFICATION|IDENTIFIER|IDS|IF|IGNORE|IGNORING|IMMEDIATELY|IMPLEMENTATION' && '|IMPLEMENTATIONS|IMPLEMENTED|IMPLICIT|IMPORT|IMPORTING|IN|INACTIVE|INCL|INCLUDE' && '|INCLUDES|INCLUDING|INCREMENT|INDEX|INDEX-LINE|INFOTYPES|INHERITING|INIT|INITIAL' && '|INITIALIZATION|INNER|INOUT|INPUT|INSERT|INSTANCES|INTENSIFIED|INTERFACE' && '|INTERFACE-POOL|INTERFACES|INTERNAL|INTERVALS|INTO|INVERSE|INVERTED-DATE|IS' && '|ISO|JOB|JOIN|KEEP|KEEPING|KERNEL|KEY|KEYS|KEYWORDS|KIND' && '|LANGUAGE|LAST|LATE|LAYOUT|LE|LEADING|LEAVE|LEFT|LEFT-JUSTIFIED|LEFTPLUS' && '|LEFTSPACE|LEGACY|LENGTH|LET|LEVEL|LEVELS|LIKE|LINE|LINE-COUNT|LINEFEED' && '|LINES|LINE-SELECTION|LINE-SIZE|LIST|LISTBOX|LIST-PROCESSING|LITTLE|LLANG' && '|LOAD|LOAD-OF-PROGRAM|LOB|LOCAL|LOCALE|LOCATOR|LOG|LOG10|LOGFILE|LOGICAL' && '|LOG-POINT|LONG|LOOP|LOW|LOWER|LPAD|LPI|LT|M|MAIL|MAIN|MAJOR-ID|MAPPING|MARGIN' && '|MARK|MASK|MATCH|MATCHCODE|MAX|MAXIMUM|MEDIUM|MEMBERS|MEMORY|MESH|MESSAGE' && '|MESSAGE-ID|MESSAGES|MESSAGING|METHOD|METHODS|MIN|MINIMUM|MINOR-ID|MM/DD/YY' && '|MM/DD/YYYY|MMDDYY|MOD|MODE|MODIF|MODIFIER|MODIFY|MODULE|MOVE|MOVE-CORRESPONDING' && '|MULTIPLY|MULTIPLY-CORRESPONDING|NA|NAME|NAMETAB|NATIVE|NB|NE|NESTED|NESTING' && '|NEW|NEW-LINE|NEW-PAGE|NEW-SECTION|NEXT|NO|NODE|NODES|NO-DISPLAY' && '|NO-EXTENSION|NO-GAP|NO-GAPS|NO-GROUPING|NO-HEADING|NON-UNICODE|NON-UNIQUE' && '|NO-SCROLLING|NO-SIGN|NOT|NO-TITLE|NO-TOPOFPAGE|NO-ZERO|NP|NS|NULL|NUMBER' && '|NUMOFCHAR|O|OBJECT|OBJECTS|OBLIGATORY|OCCURRENCE|OCCURRENCES|OCCURS|OF|OFF' && '|OFFSET|OLE|ON|ONLY|OPEN|OPTION|OPTIONAL|OPTIONS|OR|ORDER|OTHER|OTHERS|OUT' && '|OUTER|OUTPUT|OUTPUT-LENGTH|OVERFLOW|OVERLAY|PACK|PACKAGE|PAD|PADDING|PAGE' && '|PAGES|PARAMETER|PARAMETERS|PARAMETER-TABLE|PART|PARTIALLY|PATTERN|PERCENTAGE' && '|PERFORM|PERFORMING|PERSON|PF1|PF2|PF3|PF4|PF5|PF6|PF7|PF8|PF9|PF10|PF11|PF12' && '|PF13|PF14|PF15|PF-STATUS|PINK|PLACES|POOL|POS_HIGH|POS_LOW' && '|POSITION|PRAGMAS|PRECOMPILED|PREFERRED|PRESERVING|PRIMARY|PRINT|PRINT-CONTROL' && '|PRIORITY|PRIVATE|PROCEDURE|PROCESS|PROGRAM|PROPERTY|PROTECTED|PROVIDE|PUBLIC' && '|PUSHBUTTON|PUT|QUEUE-ONLY|QUICKINFO|RADIOBUTTON|RAISE|RAISING|RANGE|RANGES' && '|RAW|READ|READER|READ-ONLY|RECEIVE|RECEIVED|RECEIVER|RECEIVING|RED|REDEFINITION' && '|REDUCE|REDUCED|REF|REFERENCE|REFRESH|REGEX|REJECT|REMOTE|RENAMING|REPLACE' && '|REPLACEMENT|REPLACING|REPORT|REQUEST|REQUESTED|RESERVE|RESET|RESOLUTION' && '|RESPECTING|RESPONSIBLE|RESULT|RESULTS|RESUMABLE|RESUME|RETRY|RETURN|RETURNCODE' && '|RETURNING|RIGHT|RIGHT-JUSTIFIED|RIGHTPLUS|RIGHTSPACE|RISK|RMC_COMMUNICATION_FAILURE' && '|RMC_INVALID_STATUS|RMC_SYSTEM_FAILURE|ROLE|ROLLBACK|ROUND|ROWS|RUN|SAP' && '|SAP-SPOOL|SAVING|SCALE_PRESERVING|SCALE_PRESERVING_SCIENTIFIC|SCAN|SCIENTIFIC' && '|SCIENTIFIC_WITH_LEADING_ZERO|SCREEN|SCROLL|SCROLL-BOUNDARY|SCROLLING|SEARCH' && '|SECONDARY|SECONDS|SECTION|SELECT|SELECTION|SELECTIONS|SELECTION-SCREEN|SELECTION-SET' && '|SELECTION-SETS|SELECTION-TABLE|SELECT-OPTIONS|SEND|SEPARATE|SEPARATED|SET' && '|SHARED|SHIFT|SHORT|SHORTDUMP-ID|SIGN|SIGN_AS_POSTFIX|SIMPLE|SIN|SINGLE|SINH|SIZE' && '|SKIP|SKIPPING|SMART|SOME|SORT|SORTABLE|SORTED|SOURCE|SPACE|SPECIFIED|SPLIT|SPOOL' && '|SPOTS|SQL|SQLSCRIPT|SQRT|STABLE|STAMP|STANDARD|STARTING|START-OF-SELECTION|STATE' && '|STATEMENT|STATEMENTS|STATIC|STATICS|STATUSINFO|STEP-LOOP|STOP|STRLEN|STRUCTURE' && '|STRUCTURES|STYLE|SUBKEY|SUBMATCHES|SUBMIT|SUBROUTINE|SUBSCREEN|SUBSTRING|SUBTRACT' && '|SUBTRACT-CORRESPONDING|SUFFIX|SUM|SUMMARY|SUMMING|SUPPLIED|SUPPLY|SUPPRESS|SWITCH' && '|SWITCHSTATES|SYMBOL|SYNCPOINTS|SYNTAX|SYNTAX-CHECK|SYNTAX-TRACE' && '|SYSTEM-CALL|SYSTEM-EXCEPTIONS|SYSTEM-EXIT|TAB|TABBED|TABLE|TABLES|TABLEVIEW|TABSTRIP' && '|TAN|TANH|TARGET|TASK|TASKS|TEST|TESTING|TEXT|TEXTPOOL|THEN|THROW|TIME|TIMES|TIMESTAMP' && '|TIMEZONE|TITLE|TITLEBAR|TITLE-LINES|TO|TOKENIZATION|TOKENS|TOP-LINES|TOP-OF-PAGE' && '|TRACE-FILE|TRACE-TABLE|TRAILING|TRANSACTION|TRANSFER|TRANSFORMATION|TRANSLATE' && '|TRANSPORTING|TRMAC|TRUNC|TRUNCATE|TRUNCATION|TRY|TYPE|TYPE-POOL|TYPE-POOLS|TYPES' && '|ULINE|UNASSIGN|UNDER|UNICODE|UNION|UNIQUE|UNIT|UNIT_CONVERSION|UNIX|UNPACK|UNTIL' && '|UNWIND|UP|UPDATE|UPPER|USER|USER-COMMAND|USING|UTF-8|VALID|VALUE|VALUE-REQUEST|VALUES' && '|VARY|VARYING|VERIFICATION-MESSAGE|VERSION|VIA|VIEW|VISIBLE|WAIT|WARNING|WHEN|WHENEVER' && '|WHERE|WHILE|WIDTH|WINDOW|WINDOWS|WITH|WITH-HEADING|WITHOUT|WITH-TITLE|WORD|WORK' && '|WRITE|WRITER|X|XML|XSD|XSTRLEN|YELLOW|YES|YYMMDD|Z|ZERO|ZONE' && '|BINTOHEX|CHAR|CLNT|CONCAT_WITH_SPACE|CURR|DATS|DATS_ADD_DAYS|DATS_ADD_MONTHS' && '|DATS_DAYS_BETWEEN|DATS_IS_VALID|DEC|END-OF-EDITING|END-TEST-INJECTION|END-TEST-SEAM' && '|ENDWITH|ENUM|HEXTOBIN|INSTANCE|INSTR|LANG|LTRIM|NUMC|PUSH' && '|QUAN|RETURNS|RPAD|RTRIM|SSTRING|START-OF-EDITING|TEST-INJECTION|TEST-SEAM|TIMS' && '|TIMS_IS_VALID|TSTMP_ADD_SECONDS|TSTMP_CURRENT_UTCTIMESTAMP|TSTMP_IS_VALID' && '|TSTMP_SECONDS_BETWEEN|B|D|DECFLOAT16|DECFLOAT34|F|INT8|N|P|S|STRING|T|UTCLONG|XSTRING' && '|ABAP_BOOL|ACCP|CUKY|DF16_DEC|DF16_RAW|DF34_DEC|DF34_RAW|FLTP' && '|INT1|INT2|INT4|LCHR|LRAW|RAWSTRING|DF16_SCL|DF34_SCL' && '|PREC|VARC|CLIKE|CSEQUENCE|DECFLOAT|NUMERIC|XSEQUENCE|ME|SYST|SY' && '|BIT-SET|BOOLC|BOOLX|CHAR_OFF|CMAX|CMIN|CONCAT_LINES_OF|CONTAINS|CONTAINS_ANY_NOT_OF' && '|CONTAINS_ANY_OF|COUNT_ANY_NOT_OF|COUNT_ANY_OF|FIND_ANY_NOT_OF|FIND_ANY_OF|FIND_END' && '|FROM_MIXED|IPOW|LINE_EXISTS|LINE_INDEX|MATCHES|NMAX|NMIN|REPEAT|RESCALE|REVERSE' && '|SEGMENT|SHIFT_LEFT|SHIFT_RIGHT|SUBSTRING_AFTER|SUBSTRING_BEFORE|SUBSTRING_FROM|SUBSTRING_TO' && '|TO_LOWER|TO_MIXED|TO_UPPER|UTCLONG_ADD|UTCLONG_CURRENT|UTCLONG_DIFF|XSDBOOL'. SPLIT list AT '|' INTO TABLE DATA(keyword_list). " remove duplicates to avoid dumps when converting to a hash table SORT keyword_list BY table_line ASCENDING. DELETE ADJACENT DUPLICATES FROM keyword_list. keywords = keyword_list. " Hash table ENDMETHOD. METHOD is_keyword. result = xsdbool( line_exists( keywords[ table_line = to_upper( chunk ) ] ) ). ENDMETHOD. METHOD order_matches. FIELD-SYMBOLS TYPE ty_match. SORT matches BY offset. DATA(line_len) = strlen( line ). DATA(prev_token) = ''. LOOP AT matches ASSIGNING FIELD-SYMBOL(). DATA(index) = sy-tabix. " Delete matches after open text match IF prev_token = c_token-text AND -token <> c_token-text. DELETE matches INDEX index. CONTINUE. ENDIF. CASE -token. WHEN c_token-keyword. IF -offset > 0 AND substring( val = line off = ( -offset - 1 ) len = 1 ) CA '-<'. " Delete match if keyword is part of structure or field symbol DELETE matches INDEX index. CONTINUE. ENDIF. WHEN c_token-comment. -length = line_len - -offset. DELETE matches FROM index + 1. CONTINUE. WHEN c_token-text. -text_tag = substring( val = line off = -offset len = -length ). IF prev_token = c_token-text. IF -text_tag = -text_tag. -length = -offset + -length - -offset. CLEAR prev_token. ELSEIF -text_tag = '}' AND -text_tag = '{'. -length = -offset - -offset - 1. " Shift } out of scope -offset = -offset + 1. " Shift { out of scope CLEAR prev_token. ELSEIF -text_tag = '{'. -length = -offset - -offset. CLEAR prev_token. ELSEIF -text_tag = '}'. -length = -offset - -offset. -offset = -offset + 1. " Shift } out of scope CLEAR prev_token. ENDIF. DELETE matches INDEX index. CONTINUE. ENDIF. ENDCASE. prev_token = -token. ASSIGN TO . ENDLOOP. ENDMETHOD. METHOD parse_line. "REDEFINITION result = super->parse_line( line ). " Remove non-keywords LOOP AT result ASSIGNING FIELD-SYMBOL() WHERE token = c_token-keyword. DATA(tabix) = sy-tabix. IF NOT is_keyword( substring( val = line off = -offset len = -length ) ). DELETE result INDEX tabix. ENDIF. ENDLOOP. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_highlighter_css IMPLEMENTATION. METHOD class_constructor. init_keywords( ). ENDMETHOD. METHOD constructor. super->constructor( ). " Reset indicator for multi-line comments CLEAR comment. " Initialize instances of regular expression add_rule( regex = c_regex-keyword token = c_token-keyword style = c_css-keyword ). add_rule( regex = c_regex-comment token = c_token-comment style = c_css-comment ). add_rule( regex = c_regex-text token = c_token-text style = c_css-text ). add_rule( regex = c_regex-selectors token = c_token-selectors style = c_css-selectors ). add_rule( regex = c_regex-units token = c_token-units style = c_css-units ). " Styles for keywords add_rule( regex = '' token = c_token-html style = c_css-html ). add_rule( regex = '' token = c_token-properties style = c_css-properties ). add_rule( regex = '' token = c_token-values style = c_css-values ). add_rule( regex = '' token = c_token-functions style = c_css-functions ). add_rule( regex = '' token = c_token-colors style = c_css-colors ). add_rule( regex = '' token = c_token-extensions style = c_css-extensions ). add_rule( regex = '' token = c_token-at_rules style = c_css-at_rules ). ENDMETHOD. METHOD init_keywords. CLEAR keywords. " 1) CSS Properties DATA(keyword_list) = 'align-content|align-items|align-self|animation|animation-delay|animation-direction|animation-duration|' && 'animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|' && 'backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|' && 'background-image|background-origin|background-position|background-repeat|background-size|border|' && 'border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|' && 'border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|' && 'border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|' && 'border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|' && 'border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|' && 'border-top-style|border-top-width|border-width|box-decoration-break|box-shadow|box-sizing|caption-side|' && 'caret-color|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|' && 'column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|' && 'counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|' && 'flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-kerning|font-size|font-size-adjust|' && 'font-stretch|font-style|font-variant|font-weight|grid|grid-area|grid-auto-columns|grid-auto-flow|' && 'grid-auto-rows|grid-column|grid-column-end|grid-column-gap|grid-column-start|grid-gap|grid-row|' && 'grid-row-end|grid-row-gap|grid-row-start|grid-template|grid-template-areas|grid-template-columns|' && 'grid-template-rows|hanging-punctuation|height|hyphens|isolation|justify-content|' && 'letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|' && 'margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|media|min-height|min-width|' && 'mix-blend-mode|object-fit|object-position|opacity|order|outline|outline-color|outline-offset|' && 'outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|' && 'padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|' && 'perspective-origin|pointer-events|position|quotes|resize|scroll-behavior|tab-size|table-layout|' && 'text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|' && 'text-decoration-style|text-indent|text-justify|text-overflow|text-rendering|text-shadow|text-transform|' && 'transform|transform-origin|transform-style|transition|transition-delay|transition-duration|' && 'transition-property|transition-timing-function|unicode-bidi|user-select|vertical-align|visibility|' && 'white-space|width|word-break|word-spacing|word-wrap|writing-mode|z-index'. insert_keywords( list = keyword_list token = c_token-properties ). " 2) CSS Values keyword_list = 'absolute|all|auto|block|bold|border-box|both|bottom|center|counter|cover|dashed|fixed|hidden|important|' && 'inherit|initial|inline-block|italic|left|max-content|middle|min-content|no-repeat|none|normal|pointer|' && 'relative|rem|right|solid|table-cell|text|top|transparent|underline|url'. insert_keywords( list = keyword_list token = c_token-values ). " 3) CSS Selectors keyword_list = ':active|::after|::before|:checked|:disabled|:empty|:enabled|:first-child|::first-letter|::first-line|' && ':first-of-type|:focus|:hover|:lang|:last-child|:last-of-type|:link|:not|:nth-child|:nth-last-child|' && ':nth-last-of-type|:nth-of-type|:only-child|:only-of-type|:root|:target|:visited'. insert_keywords( list = keyword_list token = c_token-selectors ). " 4) CSS Functions keyword_list = 'attr|calc|cubic-bezier|hsl|hsla|linear-gradient|math|radial-gradient|repeating-linear-gradient|' && 'repeating-radial-gradient|rgb|rgba|rotate|scale|theme|translateX|translateY|var'. insert_keywords( list = keyword_list token = c_token-functions ). " 5) CSS Colors keyword_list = '#|aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|' && 'burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|' && 'darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|' && 'darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|' && 'deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|' && 'ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|' && 'lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|' && 'lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|' && 'lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|' && 'mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|' && 'mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|' && 'orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|' && 'peru|pink|plum|powderblue|purple|rebeccapurple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|' && 'seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|' && 'tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen'. insert_keywords( list = keyword_list token = c_token-colors ). " 6) CSS Extensions keyword_list = 'moz|moz-binding|moz-border-bottom-colors|moz-border-left-colors|moz-border-right-colors|' && 'moz-border-top-colors|moz-box-align|moz-box-direction|moz-box-flex|moz-box-ordinal-group|' && 'moz-box-orient|moz-box-pack|moz-box-shadow|moz-context-properties|moz-float-edge|' && 'moz-force-broken-image-icon|moz-image-region|moz-orient|moz-osx-font-smoothing|' && 'moz-outline-radius|moz-outline-radius-bottomleft|moz-outline-radius-bottomright|' && 'moz-outline-radius-topleft|moz-outline-radius-topright|moz-stack-sizing|moz-system-metric|' && 'moz-transform|moz-transform-origin|moz-transition|moz-transition-delay|moz-user-focus|' && 'moz-user-input|moz-user-modify|moz-window-dragging|moz-window-shadow|ms|ms-accelerator|' && 'ms-block-progression|ms-content-zoom-chaining|ms-content-zoom-limit|' && 'ms-content-zoom-limit-max|ms-content-zoom-limit-min|ms-content-zoom-snap|' && 'ms-content-zoom-snap-points|ms-content-zoom-snap-type|ms-content-zooming|ms-filter|' && 'ms-flow-from|ms-flow-into|ms-high-contrast-adjust|ms-hyphenate-limit-chars|' && 'ms-hyphenate-limit-lines|ms-hyphenate-limit-zone|ms-ime-align|ms-overflow-style|' && 'ms-scroll-chaining|ms-scroll-limit|ms-scroll-limit-x-max|ms-scroll-limit-x-min|' && 'ms-scroll-limit-y-max|ms-scroll-limit-y-min|ms-scroll-rails|ms-scroll-snap-points-x|' && 'ms-scroll-snap-points-y|ms-scroll-snap-x|ms-scroll-snap-y|ms-scroll-translation|' && 'ms-scrollbar-3dlight-color|ms-scrollbar-arrow-color|ms-scrollbar-base-color|' && 'ms-scrollbar-darkshadow-color|ms-scrollbar-face-color|ms-scrollbar-highlight-color|' && 'ms-scrollbar-shadow-color|ms-scrollbar-track-color|ms-transform|ms-text-autospace|' && 'ms-touch-select|ms-wrap-flow|ms-wrap-margin|ms-wrap-through|o|o-transform|webkit|' && 'webkit-animation-trigger|webkit-app-region|webkit-appearance|webkit-aspect-ratio|' && 'webkit-backdrop-filter|webkit-background-composite|webkit-border-after|' && 'webkit-border-after-color|webkit-border-after-style|webkit-border-after-width|' && 'webkit-border-before|webkit-border-before-color|webkit-border-before-style|' && 'webkit-border-before-width|webkit-border-end|webkit-border-end-color|' && 'webkit-border-end-style|webkit-border-end-width|webkit-border-fit|' && 'webkit-border-horizontal-spacing|webkit-border-radius|webkit-border-start|' && 'webkit-border-start-color|webkit-border-start-style|webkit-border-start-width|' && 'webkit-border-vertical-spacing|webkit-box-align|webkit-box-direction|webkit-box-flex|' && 'webkit-box-flex-group|webkit-box-lines|webkit-box-ordinal-group|webkit-box-orient|' && 'webkit-box-pack|webkit-box-reflect|webkit-box-shadow|webkit-column-axis|' && 'webkit-column-break-after|webkit-column-break-before|webkit-column-break-inside|' && 'webkit-column-progression|webkit-cursor-visibility|webkit-dashboard-region|' && 'webkit-font-size-delta|webkit-font-smoothing|webkit-highlight|webkit-hyphenate-character|' && 'webkit-hyphenate-limit-after|webkit-hyphenate-limit-before|webkit-hyphenate-limit-lines|' && 'webkit-initial-letter|webkit-line-align|webkit-line-box-contain|webkit-line-clamp|' && 'webkit-line-grid|webkit-line-snap|webkit-locale|webkit-logical-height|' && 'webkit-logical-width|webkit-margin-after|webkit-margin-after-collapse|' && 'webkit-margin-before|webkit-margin-before-collapse|webkit-margin-bottom-collapse|' && 'webkit-margin-collapse|webkit-margin-end|webkit-margin-start|webkit-margin-top-collapse|' && 'webkit-marquee|webkit-marquee-direction|webkit-marquee-increment|' && 'webkit-marquee-repetition|webkit-marquee-speed|webkit-marquee-style|webkit-mask-box-image|' && 'webkit-mask-box-image-outset|webkit-mask-box-image-repeat|webkit-mask-box-image-slice|' && 'webkit-mask-box-image-source|webkit-mask-box-image-width|webkit-mask-repeat-x|' && 'webkit-mask-repeat-y|webkit-mask-source-type|webkit-max-logical-height|' && 'webkit-max-logical-width|webkit-min-logical-height|webkit-min-logical-width|' && 'webkit-nbsp-mode|webkit-padding-after|webkit-padding-before|webkit-padding-end|' && 'webkit-padding-start|webkit-perspective-origin-x|webkit-perspective-origin-y|' && 'webkit-print-color-adjust|webkit-rtl-ordering|webkit-svg-shadow|' && 'webkit-tap-highlight-color|webkit-text-combine|webkit-text-decoration-skip|' && 'webkit-text-decorations-in-effect|webkit-text-fill-color|webkit-text-security|' && 'webkit-text-stroke|webkit-text-stroke-color|webkit-text-stroke-width|webkit-text-zoom|' && 'webkit-transform|webkit-transform-origin|webkit-transform-origin-x|' && 'webkit-transform-origin-y|webkit-transform-origin-z|webkit-transition|' && 'webkit-transition-delay|webkit-user-drag|webkit-user-modify|overflow-clip-box|' && 'overflow-clip-box-block|overflow-clip-box-inline|zoom'. insert_keywords( list = keyword_list token = c_token-extensions ). " 6) CSS At-Rules (SASS/SCSS) keyword_list = '@|charset|counter-style|font-face|import|keyframes|@use|@mixin|@include|@extend'. insert_keywords( list = keyword_list token = c_token-at_rules ). " 7) HTML tag keyword_list = 'doctyype|a|abbr|acronym|address|applet|area|b|base|basefont|bdo|bgsound|big|blink|blockquote|' && 'body|br|button|caption|center|cite|code|col|colgroup|dd|del|dfn|dir|div|dl|dt|em|embed|fieldset|' && 'font|form|frame|frameset|h1|h2|h3|h4|h5|h6|head|hr|html|i|iframe|ilayer|img|input|ins|isindex|' && 'kbd|keygen|label|layer|legend|li|link|listing|map|menu|meta|multicol|nobr|noembed|noframes|' && 'nolayer|noscript|object|ol|optgroup|option|p|param|plaintext|pre|q|s|samp|script|select|server|' && 'small|sound|spacer|span|strike|strong|style|sub|sup|tbody|textarea|title|tt|u|ul|var|wbr|xmp|' && 'xsl|xml|accesskey|action|align|alink|alt|background|balance|behavior|bgcolor|bgproperties|' && 'border|bordercolor|bordercolordark|bordercolorlight|bottommargin|checked|class|classid|clear|' && 'code|codebase|codetype|color|cols|colspan|compact|content|controls|coords|data|datafld|' && 'dataformatas|datasrc|direction|disabled|dynsrc|enctype|event|face|for|frame|frameborder|' && 'framespacing|height|hidden|href|hspace|http-equiv|id|ismap|lang|language|leftmargin|link|loop|' && 'lowsrc|marginheight|marginwidth|maxlength|mayscript|method|methods|multiple|name|nohref|' && 'noresize|noshade|nowrap|palette|pluginspage|public|readonly|rel|rev|rightmargin|rows|rowspan|' && 'rules|scroll|scrollamount|scrolldelay|scrolling|selected|shape|size|span|src|start|style|' && 'tabindex|target|text|title|topmargin|truespeed|type|url|urn|usemap|valign|value|vlink|volume|' && 'vrml|vspace|width|wrap|apply-templates|attribute|choose|comment|define-template-set|' && 'entity-ref|eval|expr|for-each|if|match|no-entities|node-name|order-by|otherwise|select|' && 'stylesheet|template|test|value-of|version|when|xmlns|xsl|cellpadding|cellspacing|table|td|' && 'tfoot|th|thead|tr'. insert_keywords( list = keyword_list token = c_token-html ). ENDMETHOD. METHOD insert_keywords. SPLIT list AT '|' INTO TABLE DATA(keyword_list). LOOP AT keyword_list ASSIGNING FIELD-SYMBOL(). DATA(keyword) = VALUE ty_keyword( keyword = token = token ). INSERT keyword INTO TABLE keywords. ENDLOOP. ENDMETHOD. METHOD is_keyword. result = xsdbool( line_exists( keywords[ keyword = to_lower( chunk ) ] ) ). ENDMETHOD. METHOD order_matches. FIELD-SYMBOLS TYPE ty_match. " Longest matches SORT matches BY offset length DESCENDING. DATA(line_len) = strlen( line ). DATA(prev_token) = ''. DATA(prev_end) = ''. " Check if this is part of multi-line comment and mark it accordingly IF comment = abap_true. IF NOT line_exists( matches[ token = c_token-comment ] ). CLEAR matches. APPEND INITIAL LINE TO matches ASSIGNING FIELD-SYMBOL(). -token = c_token-comment. -offset = 0. -length = line_len. RETURN. ENDIF. ENDIF. LOOP AT matches ASSIGNING . " Delete matches after open text match IF prev_token = c_token-text AND -token <> c_token-text. CLEAR -token. CONTINUE. ENDIF. DATA(match) = substring( val = line off = -offset len = -length ). CASE -token. WHEN c_token-keyword. " Skip keyword that's part of previous (longer) keyword IF -offset < prev_end. CLEAR -token. CONTINUE. ENDIF. " Map generic keyword to specific CSS token match = to_lower( match ). READ TABLE keywords ASSIGNING FIELD-SYMBOL() WITH TABLE KEY keyword = match. IF sy-subrc = 0. -token = -token. ENDIF. WHEN c_token-comment. CASE match. WHEN '/*'. DELETE matches WHERE offset > -offset. -length = line_len - -offset. comment = abap_true. WHEN '*/'. DELETE matches WHERE offset < -offset. -length = -offset + 2. -offset = 0. comment = abap_false. WHEN OTHERS. DATA(cmmt_end) = -offset + -length. DELETE matches WHERE offset > -offset AND offset <= cmmt_end. ENDCASE. WHEN c_token-text. -text_tag = match. IF prev_token = c_token-text. IF -text_tag = -text_tag. -length = -offset + -length - -offset. CLEAR prev_token. ENDIF. CLEAR -token. CONTINUE. ENDIF. ENDCASE. prev_token = -token. prev_end = -offset + -length. ASSIGN TO . ENDLOOP. DELETE matches WHERE token IS INITIAL. ENDMETHOD. METHOD parse_line. "REDEFINITION result = super->parse_line( line ). " Remove non-keywords LOOP AT result ASSIGNING FIELD-SYMBOL() WHERE token = c_token-keyword. IF NOT is_keyword( substring( val = line off = -offset len = -length ) ). CLEAR -token. ENDIF. ENDLOOP. DELETE result WHERE token IS INITIAL. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_highlighter_diff IMPLEMENTATION. METHOD constructor. super->constructor( ). " Initialize instances of regular expressions add_rule( regex = c_regex-ins token = c_token-ins style = c_css-ins ). add_rule( regex = c_regex-del token = c_token-del style = c_css-del ). add_rule( regex = c_regex-test token = c_token-test style = c_css-test ). add_rule( regex = c_regex-comment token = c_token-comment style = c_css-comment ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_highlighter_facto IMPLEMENTATION. METHOD create. " Create instance of highlighter dynamically dependent on syntax type IF filename CP '*.abap'. result = NEW /apmg/cl_apm_highlighter_abap( ). ELSEIF filename CP '*.xml' OR filename CP '*.html'. result = NEW /apmg/cl_apm_highlighter_xml( ). ELSEIF filename CP '*.css' OR filename CP '*.scss' OR filename CP '*.sass'. result = NEW /apmg/cl_apm_highlighter_css( ). ELSEIF filename CP '*.js'. result = NEW /apmg/cl_apm_highlighter_js( ). ELSEIF filename CP '*.json' OR filename CP '*.jsonc' OR filename CP '*.json5'. result = NEW /apmg/cl_apm_highlighter_json( ). ELSEIF filename CP '*.txt' OR filename CP '*.ini' OR filename CP '*.text'. result = NEW /apmg/cl_apm_highlighter_txt( ). ELSEIF filename CP '*.md' OR filename CP '*.markdown'. result = NEW /apmg/cl_apm_highlighter_md( ). ELSEIF filename CP '*.diff'. result = NEW /apmg/cl_apm_highlighter_diff( ). ELSE. CLEAR result. ENDIF. IF result IS BOUND. result->set_hidden_chars( hidden_chars ). ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_highlighter_js IMPLEMENTATION. METHOD class_constructor. init_keywords( ). ENDMETHOD. METHOD constructor. super->constructor( ). " Reset indicator for multi-line comments CLEAR comment. " Initialize instances of regular expression add_rule( regex = c_regex-keyword token = c_token-keyword style = c_css-keyword ). add_rule( regex = c_regex-comment token = c_token-comment style = c_css-comment ). add_rule( regex = c_regex-text token = c_token-text style = c_css-text ). " Styles for keywords add_rule( regex = '' token = c_token-variables style = c_css-variables ). ENDMETHOD. METHOD init_keywords. CLEAR keywords. " 1) General keywords DATA(list) = 'alert|all|body|break|bytetostring|case|continue|default|delete|do|document|else|event|export|for|function|if|' && 'import|in|innerhtml|isnan|item|mimetypes|navigator|new|onabort|onblur|onchange|onclick|ondblclick|ondragdrop|' && 'onerror|onfocus|onkeydown|onkeypress|onkeyup|onload|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|' && 'onmove|onreset|onselect|onsubmit|onunload|onresize|options|parsefloat|parseint|prototype|return|screen|switch|' && 'unit|var|void|while|window|with|anchor|applet|area|button|checkbox|fileupload|form|frame|hidden|link|mimetype|' && 'password|plugin|radio|reset|select|submit|text|textarea|abs|acos|alert|anchor|asin|atan|atan2|back|big|blink|' && 'blur|bold|captureevents|ceil|charat|charcodeat|clearinterval|cleartimeout|click|close|concat|confirm|cos|' && 'disableexternalcapture|enableexternalcapture|eval|exp|find|fixed|floor|focus|fontcolor|fontsize|forward|' && 'fromcharcode|getdate|getday|getelementbyid|gethours|getminutes|getmonth|getoptionvalue|getoptionvaluecount|' && 'getseconds|getselection|gettime|gettimezoneoffset|getyear|go|handleevent|home|indexof|italics|javaenabled|join|' && 'lastindexof|link|load|log|match|max|min|moveabove|movebelow|moveby|moveto|movetoabsolute|open|parse|plugins|' && 'pop|pow|preference|print|prompt|push|random|refresh|releaseevents|reload|replace|reset|resizeby|resizeto|' && 'reverse|round|routeevent|scroll|scrollby|scrollto|search|select|setdate|sethours|setinterval|setminutes|' && 'setmonth|setseconds|settime|settimeout|setyear|shift|sin|slice|small|sort|splice|split|sqrt|stop|strike|sub|' && 'submit|substr|substring|sup|taintenabled|tan|togmtstring|tolocalestring|tolowercase|tostring|touppercase|' && 'unshift|unwatch|utc|valueof|watch|write|writeln|e|ln10|ln2|log10e|log2e|max_value|min_value|negative_infinity|' && 'nan|pi|positive_infinity|url|above|action|alinkcolor|anchors|appcodename|appname|appversion|applets|arguments|' && 'arity|availheight|availwidth|background|backgroundcolor|below|bgcolor|border|bottom|caller|cancelbubble|' && 'checked|clientheight|clientwidth|clientx|clienty|clip|closed|color|colordepth|complete|constructor|cookie|' && 'count|current|defaultchecked|defaultselected|defaultstatus|defaultvalue|description|display|document|domain|' && 'elements|embeds|enabledplugin|encoding|false|fgcolor|filename|form|formname|forms|frames|hash|height|history|' && 'host|hostname|href|hspace|images|innerheight|innerwidth|language|lastmodified|layers|left|length|linkcolor|' && 'links|location|locationbar|lowsrc|menubar|method|mimetypes|name|next|null|offsetheight|offsetleft|offsetparent|' && 'offsetwidth|opener|outerheight|outerwidth|pagex|pagexoffset|pagey|pageyoffset|parent|parentlayer|pathname|' && 'personalbar|pixeldepth|platform|plugins|port|poswidth|previous|protocol|prototype|referrer|right|scrolltop|' && 'scrollbars|search|selected|selectedindex|self|siblingabove|siblingbelow|src|srcelement|status|statusbar|style|' && 'suffixes|tags|target|text|this|title|toolbar|top|true|type|useragent|value|visibility|vlinkcolor|vspace|width|' && 'window|zindex'. insert_keywords( list = list token = c_token-keyword ). " 2) Variable types list = 'array|boolean|date|function|image|layer|math|number|object|option|regexp|string'. insert_keywords( list = list token = c_token-variables ). ENDMETHOD. METHOD insert_keywords. SPLIT list AT '|' INTO TABLE DATA(keyword_list). LOOP AT keyword_list ASSIGNING FIELD-SYMBOL(). DATA(keyword) = VALUE ty_keyword( keyword = token = token ). INSERT keyword INTO TABLE keywords. ENDLOOP. ENDMETHOD. METHOD is_keyword. result = xsdbool( line_exists( keywords[ keyword = to_lower( chunk ) ] ) ). ENDMETHOD. METHOD order_matches. FIELD-SYMBOLS TYPE ty_match. " Longest matches SORT matches BY offset length DESCENDING. DATA(line_len) = strlen( line ). DATA(prev_token) = ''. DATA(prev_end) = ''. " Check if this is part of multi-line comment and mark it accordingly IF comment = abap_true. IF NOT line_exists( matches[ token = c_token-comment ] ). CLEAR matches. APPEND INITIAL LINE TO matches ASSIGNING FIELD-SYMBOL(). -token = c_token-comment. -offset = 0. -length = line_len. RETURN. ENDIF. ENDIF. LOOP AT matches ASSIGNING . " Delete matches after open text match IF prev_token = c_token-text AND -token <> c_token-text. CLEAR -token. CONTINUE. ENDIF. DATA(match) = substring( val = line off = -offset len = -length ). CASE -token. WHEN c_token-keyword. " Skip keyword that's part of previous (longer) keyword IF -offset < prev_end. CLEAR -token. CONTINUE. ENDIF. " Map generic keyword to specific token match = to_lower( match ). READ TABLE keywords ASSIGNING FIELD-SYMBOL() WITH TABLE KEY keyword = match. IF sy-subrc = 0. -token = -token. ENDIF. WHEN c_token-comment. CASE match. WHEN '/*'. DELETE matches WHERE offset > -offset. -length = line_len - -offset. comment = abap_true. WHEN '//'. DELETE matches WHERE offset > -offset. -length = line_len - -offset. WHEN '*/'. DELETE matches WHERE offset < -offset. -length = -offset + 2. -offset = 0. comment = abap_false. WHEN OTHERS. DATA(cmmt_end) = -offset + -length. DELETE matches WHERE offset > -offset AND offset <= cmmt_end. ENDCASE. WHEN c_token-text. -text_tag = match. IF prev_token = c_token-text. IF -text_tag = -text_tag. -length = -offset + -length - -offset. CLEAR prev_token. ENDIF. CLEAR -token. CONTINUE. ENDIF. ENDCASE. prev_token = -token. prev_end = -offset + -length. ASSIGN TO . ENDLOOP. DELETE matches WHERE token IS INITIAL. ENDMETHOD. METHOD parse_line. result = super->parse_line( line ). " Remove non-keywords LOOP AT result ASSIGNING FIELD-SYMBOL() WHERE token = c_token-keyword. IF NOT is_keyword( substring( val = line off = -offset len = -length ) ). CLEAR -token. ENDIF. ENDLOOP. DELETE result WHERE token IS INITIAL. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_highlighter_json IMPLEMENTATION. METHOD constructor. super->constructor( ). " Initialize instances of regular expression add_rule( regex = c_regex-keyword token = c_token-keyword style = c_css-keyword ). " Style for keys add_rule( regex = c_regex-text token = c_token-text style = c_css-text ). " Style for values add_rule( regex = '' token = c_token-values style = c_css-values ). " JSONC comments add_rule( regex = c_regex-comment token = c_token-comment style = c_css-comment ). ENDMETHOD. METHOD order_matches. FIELD-SYMBOLS TYPE ty_match. " Longest matches SORT matches BY offset length DESCENDING. DATA(prev_token) = ''. LOOP AT matches ASSIGNING FIELD-SYMBOL(). " Delete matches after open text match IF prev_token = c_token-text AND -token <> c_token-text. CLEAR -token. CONTINUE. ENDIF. DATA(match) = substring( val = line off = -offset len = -length ). IF -token = c_token-text. -text_tag = match. IF prev_token = c_token-text. IF -text_tag = -text_tag. -length = -offset + -length - -offset. CLEAR prev_token. ENDIF. CLEAR -token. CONTINUE. ENDIF. ENDIF. prev_token = -token. ASSIGN TO . ENDLOOP. DELETE matches WHERE token IS INITIAL. " Switch style of second text match to values DATA(count) = 0. LOOP AT matches ASSIGNING WHERE token = c_token-text. count = count + 1. IF count >= 2. -token = c_token-values. ENDIF. ENDLOOP. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_highlighter_md IMPLEMENTATION. METHOD constructor. super->constructor( ). " Initialize instances of regular expressions add_rule( regex = c_regex-xml_tag token = c_token-xml_tag style = c_css-xml_tag submatch = 1 ). add_rule( regex = c_regex-attr token = c_token-attr style = c_css-attr ). add_rule( regex = c_regex-attr_val token = c_token-attr_val style = c_css-attr_val ). add_rule( regex = c_regex-heading token = c_token-heading style = c_css-heading ). add_rule( regex = c_regex-link token = c_token-link style = c_css-link ). add_rule( regex = c_regex-url token = c_token-url style = c_css-url ). " TODO: Rules for strong and emphasis conflict with others " add_rule( regex = c_regex-strong " token = c_token-strong " style = c_css-strong ) " add_rule( regex = c_regex-emphasis " token = c_token-emphasis " style = c_css-emphasis ) add_rule( regex = c_regex-comment token = c_token-comment style = c_css-comment ). ENDMETHOD. METHOD order_matches. FIELD-SYMBOLS TYPE ty_match. SORT matches BY offset. DATA(line_len) = strlen( line ). DATA(prev_token) = ''. DATA(state) = 'O'. " O - for open tag; C - for closed tag; " Check if this is part of multi-line comment and mark it accordingly IF comment = abap_true. IF NOT line_exists( matches[ token = c_token-comment ] ). CLEAR matches. APPEND INITIAL LINE TO matches ASSIGNING FIELD-SYMBOL(). -token = c_token-comment. -offset = 0. -length = line_len. RETURN. ENDIF. ENDIF. LOOP AT matches ASSIGNING . DATA(index) = sy-tabix. DATA(match) = substring( val = line off = -offset len = -length ). CASE -token. WHEN c_token-xml_tag. -text_tag = match. " No other matches between two tags IF -text_tag = '>' AND prev_token = c_token-xml_tag. state = 'C'. -length = -offset - -offset + -length. DELETE matches INDEX index. CONTINUE. " Adjust length and offset of closing tag ELSEIF -text_tag = '>' AND prev_token <> c_token-xml_tag. state = 'C'. IF IS ASSIGNED. DATA(new_len) = -offset - -offset - -length + -length. IF new_len < 0. " Something went wrong. Ignore the match DELETE matches INDEX index. CONTINUE. ENDIF. -length = new_len. -offset = -offset + -length. ENDIF. ELSE. state = 'O'. ENDIF. WHEN c_token-comment. CASE match. WHEN ''. DELETE matches WHERE offset < -offset. -length = -offset + 3. -offset = 0. comment = abap_false. WHEN OTHERS. DATA(cmmt_end) = -offset + -length. DELETE matches WHERE offset > -offset AND offset <= cmmt_end. DELETE matches WHERE offset = -offset AND token = c_token-xml_tag. ENDCASE. WHEN OTHERS. IF prev_token = c_token-xml_tag. -length = -offset - -offset. " Extend length of the opening tag ENDIF. IF state = 'C'. " Delete all matches between tags DELETE matches INDEX index. CONTINUE. ENDIF. ENDCASE. prev_token = -token. ASSIGN TO . CHECK sy-subrc >= 0. "abaplint false positive ENDLOOP. "if the last XML tag is not closed, extend it to the end of the tag IF prev_token = c_token-xml_tag AND IS ASSIGNED AND -length = 1 AND -text_tag = '<'. FIND REGEX '<\s*[^\s]*' IN line+-offset MATCH LENGTH -length ##REGEX_POSIX. IF sy-subrc <> 0. -length = 1. ENDIF. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_highlighter_po IMPLEMENTATION. METHOD constructor. super->constructor( ). add_rule( regex = c_regex-msgid token = c_token-msgid style = c_style-msgid ). add_rule( regex = c_regex-msgstr token = c_token-msgstr style = c_style-msgstr ). add_rule( regex = c_regex-comment token = c_token-comment style = c_style-comment ). " TODO maybe add rule to highlight empty msgstr with red ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_highlighter_txt IMPLEMENTATION. METHOD process_line. result = apply_style( line = line class = '' ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_highlighter_xml IMPLEMENTATION. METHOD constructor. super->constructor( ). " Reset indicator for multi-line comments CLEAR comment. " Initialize instances of regular expressions add_rule( regex = c_regex-xml_tag token = c_token-xml_tag style = c_css-xml_tag submatch = 1 ). add_rule( regex = c_regex-attr token = c_token-attr style = c_css-attr ). add_rule( regex = c_regex-attr_val token = c_token-attr_val style = c_css-attr_val ). add_rule( regex = c_regex-comment token = c_token-comment style = c_css-comment ). ENDMETHOD. METHOD order_matches. FIELD-SYMBOLS TYPE ty_match. " Longest matches SORT matches BY offset length DESCENDING. DATA(line_len) = strlen( line ). DATA(prev_token) = ''. DATA(state) = 'O'. " O - for open tag; C - for closed tag; " Check if this is part of multi-line comment and mark it accordingly IF comment = abap_true. IF NOT line_exists( matches[ token = c_token-comment ] ). CLEAR matches. APPEND INITIAL LINE TO matches ASSIGNING FIELD-SYMBOL(). -token = c_token-comment. -offset = 0. -length = line_len. RETURN. ENDIF. ENDIF. LOOP AT matches ASSIGNING . DATA(index) = sy-tabix. DATA(match) = substring( val = line off = -offset len = -length ). CASE -token. WHEN c_token-xml_tag. -text_tag = match. " No other matches between two tags IF -text_tag = '>' AND prev_token = c_token-xml_tag. state = 'C'. -length = -offset - -offset + -length. DELETE matches INDEX index. CONTINUE. " Adjust length and offset of closing tag ELSEIF -text_tag = '>' AND prev_token <> c_token-xml_tag. state = 'C'. IF IS ASSIGNED. -length = -offset - -offset - -length + -length. -offset = -offset + -length. ENDIF. ELSE. state = 'O'. ENDIF. WHEN c_token-comment. CASE match. WHEN ''. DELETE matches WHERE offset < -offset. -length = -offset + 3. -offset = 0. comment = abap_false. WHEN OTHERS. DATA(cmmt_end) = -offset + -length. DELETE matches WHERE offset > -offset AND offset <= cmmt_end. DELETE matches WHERE offset = -offset AND token = c_token-xml_tag. ENDCASE. WHEN OTHERS. IF prev_token = c_token-xml_tag. -length = -offset - -offset. " Extend length of the opening tag ENDIF. IF state = 'C'. " Delete all matches between tags DELETE matches INDEX index. CONTINUE. ENDIF. ENDCASE. prev_token = -token. ASSIGN TO . ENDLOOP. "if the last XML tag is not closed, extend it to the end of the tag IF prev_token = c_token-xml_tag AND IS ASSIGNED AND -length = 1 AND -text_tag = '<'. FIND REGEX '<\s*[^\s]*' IN line+-offset MATCH LENGTH -length ##REGEX_POSIX. IF sy-subrc <> 0. -length = 1. ENDIF. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_highlighter_yaml IMPLEMENTATION. METHOD constructor. super->constructor( ). " Initialize instances of regular expression " Comments add_rule( regex = c_regex-comment token = c_token-comment style = c_css-comment ). " Keywords add_rule( regex = c_regex-keyword token = c_token-keyword style = c_css-keyword ). " Style for keys add_rule( regex = c_regex-text token = c_token-text style = c_css-text ). " Style for values add_rule( regex = c_regex-values token = c_token-values style = c_css-values ). " YAML collections, structures, scalars, tags add_rule( regex = c_regex-attr token = c_token-attr style = c_css-attr ). ENDMETHOD. METHOD order_matches. FIELD-SYMBOLS TYPE ty_match. " Longest matches SORT matches BY offset length DESCENDING. DATA(prev_token) = ''. LOOP AT matches ASSIGNING FIELD-SYMBOL(). " Delete matches after open text match IF prev_token = c_token-text AND -token <> c_token-text. CLEAR -token. CONTINUE. ENDIF. DATA(match) = substring( val = line off = -offset len = -length ). IF -token = c_token-text. -text_tag = match. IF prev_token = c_token-text. IF -text_tag = -text_tag. -length = -offset + -length - -offset. CLEAR prev_token. ENDIF. CLEAR -token. CONTINUE. ENDIF. ENDIF. prev_token = -token. ASSIGN TO . ENDLOOP. DELETE matches WHERE token IS INITIAL. " Switch style of second text match to values DATA(count) = 0. LOOP AT matches ASSIGNING WHERE token = c_token-text. count = count + 1. IF count >= 2. -token = c_token-values. ENDIF. ENDLOOP. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_html IMPLEMENTATION. METHOD /apmg/if_apm_html~a. DATA: lv_class TYPE string, lv_href TYPE string, lv_click TYPE string, lv_id TYPE string, lv_act TYPE string, lv_style TYPE string, lv_title TYPE string. lv_class = iv_class. IF iv_opt CA /apmg/if_apm_html=>c_html_opt-strong. lv_class = lv_class && ' emphasis'. ENDIF. IF iv_opt CA /apmg/if_apm_html=>c_html_opt-cancel. lv_class = lv_class && ' attention'. ENDIF. IF iv_opt CA /apmg/if_apm_html=>c_html_opt-crossout. lv_class = lv_class && ' crossout grey'. ENDIF. IF lv_class IS NOT INITIAL. SHIFT lv_class LEFT DELETING LEADING space. lv_class = | class="{ lv_class }"|. ENDIF. lv_href = ' href="#"'. " Default, dummy lv_act = iv_act. IF ( iv_act IS NOT INITIAL OR iv_typ = /apmg/if_apm_html=>c_action_type-dummy ) AND iv_opt NA /apmg/if_apm_html=>c_html_opt-crossout. CASE iv_typ. WHEN /apmg/if_apm_html=>c_action_type-url. IF iv_query IS NOT INITIAL. lv_act = lv_act && `?` && iv_query. ENDIF. lv_href = | href="{ lv_act }"|. WHEN /apmg/if_apm_html=>c_action_type-sapevent. IF iv_query IS NOT INITIAL. lv_act = lv_act && `?` && iv_query. ENDIF. lv_href = | href="sapevent:{ lv_act }"|. WHEN /apmg/if_apm_html=>c_action_type-onclick. lv_href = ' href="#"'. lv_click = | onclick="{ iv_act }"|. WHEN /apmg/if_apm_html=>c_action_type-dummy. lv_href = ' href="#"'. ENDCASE. ENDIF. IF iv_id IS NOT INITIAL. lv_id = | id="{ iv_id }"|. ENDIF. IF iv_style IS NOT INITIAL. lv_style = | style="{ iv_style }"|. ENDIF. IF iv_title IS NOT INITIAL. lv_title = | title="{ iv_title }"|. ENDIF. " Debug option to display href-link on hover IF gv_debug_mode = abap_true. lv_title = | title="{ escape( val = lv_href format = cl_abap_format=>e_html_attr ) }"|. ENDIF. rv_str = || && |{ iv_txt }|. ENDMETHOD. METHOD /apmg/if_apm_html~add. DATA: lv_type TYPE c, li_renderable TYPE REF TO /apmg/if_apm_gui_renderable, lx_error TYPE REF TO /apmg/cx_apm_error, lo_html TYPE REF TO /apmg/cl_apm_html. FIELD-SYMBOLS: TYPE string_table. lv_type = cl_abap_typedescr=>describe_by_data( ig_chunk )->type_kind. CASE lv_type. WHEN 'C' OR 'g'. " Char or string APPEND ig_chunk TO mt_buffer. WHEN 'h'. " Table ASSIGN ig_chunk TO . " Assuming table of strings ! Will dump otherwise APPEND LINES OF TO mt_buffer. WHEN 'r'. " Object ref ASSERT ig_chunk IS BOUND. " Dev mistake TRY. lo_html ?= ig_chunk. CATCH cx_sy_move_cast_error. TRY. li_renderable ?= ig_chunk. lo_html ?= li_renderable->render( ). CATCH cx_sy_move_cast_error. ASSERT 1 = 0. " Dev mistake CATCH /apmg/cx_apm_error INTO lx_error. lo_html ?= create( |Render error: { lx_error->get_text( ) }| ). ENDTRY. ENDTRY. APPEND LINES OF lo_html->mt_buffer TO mt_buffer. WHEN OTHERS. ASSERT 1 = 0. " Dev mistake ENDCASE. ri_self = me. ENDMETHOD. METHOD /apmg/if_apm_html~add_a. /apmg/if_apm_html~add( /apmg/if_apm_html~a( iv_txt = iv_txt iv_act = iv_act iv_query = iv_query iv_typ = iv_typ iv_opt = iv_opt iv_class = iv_class iv_id = iv_id iv_style = iv_style iv_title = iv_title ) ). ri_self = me. ENDMETHOD. METHOD /apmg/if_apm_html~add_checkbox. /apmg/if_apm_html~add( checkbox( iv_id = iv_id iv_checked = iv_checked ) ). ri_self = me. ENDMETHOD. METHOD /apmg/if_apm_html~add_icon. /apmg/if_apm_html~add( icon( iv_name = iv_name iv_class = iv_class iv_hint = iv_hint iv_onclick = iv_onclick ) ). ri_self = me. ENDMETHOD. METHOD /apmg/if_apm_html~div. /apmg/if_apm_html~wrap( iv_tag = 'div' iv_content = iv_content ii_content = ii_content is_data_attr = is_data_attr it_data_attrs = it_data_attrs iv_id = iv_id iv_class = iv_class ). ri_self = me. ENDMETHOD. METHOD /apmg/if_apm_html~icon. rv_str = icon( iv_name = iv_name iv_hint = iv_hint iv_class = iv_class iv_onclick = iv_onclick ). ENDMETHOD. METHOD /apmg/if_apm_html~is_empty. rv_yes = boolc( lines( mt_buffer ) = 0 ). ENDMETHOD. METHOD /apmg/if_apm_html~render. DATA: ls_context TYPE ty_indent_context, lt_temp TYPE string_table. FIELD-SYMBOLS: LIKE LINE OF lt_temp, LIKE LINE OF lt_temp. IF iv_no_line_breaks = abap_true. CONCATENATE LINES OF mt_buffer INTO rv_html. ELSE. ls_context-no_indent_jscss = iv_no_indent_jscss. LOOP AT mt_buffer ASSIGNING . APPEND TO lt_temp ASSIGNING . indent_line( CHANGING cs_context = ls_context cv_line = ). ENDLOOP. CONCATENATE LINES OF lt_temp INTO rv_html SEPARATED BY cl_abap_char_utilities=>newline. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_html~set_title. /apmg/if_apm_html~mv_chunk_title = iv_title. ri_self = me. ENDMETHOD. METHOD /apmg/if_apm_html~td. /apmg/if_apm_html~wrap( iv_format_single_line = iv_format_single_line iv_tag = 'td' iv_content = iv_content ii_content = ii_content iv_id = iv_id iv_class = iv_class is_data_attr = is_data_attr it_data_attrs = it_data_attrs iv_hint = iv_hint ). ri_self = me. ENDMETHOD. METHOD /apmg/if_apm_html~th. /apmg/if_apm_html~wrap( iv_format_single_line = iv_format_single_line iv_tag = 'th' iv_content = iv_content ii_content = ii_content iv_id = iv_id iv_class = iv_class is_data_attr = is_data_attr it_data_attrs = it_data_attrs iv_hint = iv_hint ). ri_self = me. ENDMETHOD. METHOD /apmg/if_apm_html~wrap. DATA lv_open_tag TYPE string. DATA lv_close_tag TYPE string. DATA ls_data_attr LIKE LINE OF it_data_attrs. DATA: lv_class TYPE string, lv_id TYPE string, lv_data_attr TYPE string, lv_title TYPE string. IF iv_id IS NOT INITIAL. lv_id = | id="{ iv_id }"|. ENDIF. IF iv_class IS NOT INITIAL. lv_class = | class="{ iv_class }"|. ENDIF. IF iv_hint IS NOT INITIAL. lv_title = | title="{ iv_hint }"|. ENDIF. IF is_data_attr IS NOT INITIAL. lv_data_attr = | data-{ is_data_attr-name }="{ is_data_attr-value }"|. ENDIF. LOOP AT it_data_attrs INTO ls_data_attr. lv_data_attr = lv_data_attr && | data-{ ls_data_attr-name }="{ ls_data_attr-value }"|. ENDLOOP. lv_open_tag = |<{ iv_tag }{ lv_id }{ lv_class }{ lv_data_attr }{ lv_title }>|. lv_close_tag = ||. IF ii_content IS NOT BOUND AND iv_content IS INITIAL. lv_open_tag = lv_open_tag && lv_close_tag. CLEAR lv_close_tag. ENDIF. IF iv_format_single_line = abap_true AND iv_content IS NOT INITIAL. /apmg/if_apm_html~add( lv_open_tag && iv_content && lv_close_tag ). ELSE. /apmg/if_apm_html~add( lv_open_tag ). IF ii_content IS BOUND. /apmg/if_apm_html~add( ii_content ). ELSEIF iv_content IS NOT INITIAL. /apmg/if_apm_html~add( iv_content ). ENDIF. IF lv_close_tag IS NOT INITIAL. /apmg/if_apm_html~add( lv_close_tag ). ENDIF. ENDIF. ri_self = me. ENDMETHOD. METHOD checkbox. DATA: lv_checked TYPE string. IF iv_checked = abap_true. lv_checked = |checked|. ENDIF. rv_html = |`. ENDMETHOD. METHOD class_constructor. CREATE OBJECT go_single_tags_re EXPORTING pattern = '<(AREA|BASE|BR|COL|COMMAND|EMBED|HR|IMG|INPUT|LINK|META|PARAM|SOURCE|!)' ignore_case = abap_false ##REGEX_POSIX. gv_spaces = repeat( val = ` ` occ = c_max_indent ). ENDMETHOD. METHOD create. CREATE OBJECT ri_instance TYPE /apmg/cl_apm_html. IF iv_initial_chunk IS NOT INITIAL. ri_instance->add( iv_initial_chunk ). ENDIF. ENDMETHOD. METHOD icon. DATA: lv_hint TYPE string, lv_name TYPE string, lv_color TYPE string, lv_class TYPE string, lv_onclick TYPE string. SPLIT iv_name AT '/' INTO lv_name lv_color. IF iv_hint IS NOT INITIAL. lv_hint = | title="{ iv_hint }"|. ENDIF. IF iv_onclick IS NOT INITIAL. lv_onclick = | onclick="{ iv_onclick }"|. ENDIF. IF iv_class IS NOT INITIAL. lv_class = | { iv_class }|. ENDIF. IF lv_color IS NOT INITIAL. lv_color = | { lv_color }|. ENDIF. rv_str = ||. ENDMETHOD. METHOD indent_line. DATA: ls_study TYPE ty_study_result, lv_spaces TYPE i. ls_study = study_line( is_context = cs_context iv_line = cv_line ). " No indent for textarea tags IF ls_study-textarea_open = abap_true. cs_context-within_textarea = abap_true. RETURN. ELSEIF ls_study-textarea_close = abap_true. cs_context-within_textarea = abap_false. RETURN. ELSEIF cs_context-within_textarea = abap_true. RETURN. ENDIF. " No indent for pre tags IF ls_study-pre_open = abap_true. cs_context-within_pre = abap_true. RETURN. ELSEIF ls_study-pre_close = abap_true. cs_context-within_pre = abap_false. RETURN. ELSEIF cs_context-within_pre = abap_true. RETURN. ENDIF. " First closing tag - shift back exceptionally IF ( ls_study-script_close = abap_true OR ls_study-style_close = abap_true OR ls_study-curly_close = abap_true OR ls_study-tag_close = abap_true ) AND cs_context-indent > 0. lv_spaces = ( cs_context-indent - 1 ) * c_indent_size. IF lv_spaces <= c_max_indent. cv_line = gv_spaces(lv_spaces) && cv_line. ELSE. cv_line = gv_spaces && cv_line. ENDIF. ELSE. cv_line = cs_context-indent_str && cv_line. ENDIF. " Context status update CASE abap_true. WHEN ls_study-script_open. cs_context-within_js = abap_true. cs_context-within_style = abap_false. WHEN ls_study-style_open. cs_context-within_js = abap_false. cs_context-within_style = abap_true. WHEN ls_study-script_close OR ls_study-style_close. cs_context-within_js = abap_false. cs_context-within_style = abap_false. ls_study-closings = ls_study-closings + 1. ENDCASE. " More-less logic chosen due to possible double tags in a line '' IF ls_study-openings <> ls_study-closings. IF ls_study-openings > ls_study-closings. cs_context-indent = cs_context-indent + 1. ELSEIF cs_context-indent > 0. " AND ls_study-openings < ls_study-closings cs_context-indent = cs_context-indent - 1. ENDIF. lv_spaces = cs_context-indent * c_indent_size. IF lv_spaces <= c_max_indent. cs_context-indent_str = gv_spaces(lv_spaces). ELSE. cv_line = gv_spaces && cv_line. ENDIF. ENDIF. ENDMETHOD. METHOD parse_data_attr. SPLIT iv_str AT '=' INTO rs_data_attr-name rs_data_attr-value. IF rs_data_attr-name IS INITIAL. CLEAR rs_data_attr. ENDIF. ENDMETHOD. METHOD set_debug_mode. gv_debug_mode = iv_mode. ENDMETHOD. METHOD study_line. DATA: lv_line TYPE string, lv_len TYPE i. lv_line = to_upper( shift_left( val = iv_line sub = ` ` ) ). lv_len = strlen( lv_line ). " Some assumptions for simplification and speed " - style & scripts tag should be opened/closed in a separate line " - style & scripts opening and closing in one line is possible but only once " TODO & Issues " - What if the string IS a well formed html already not just single line ? IF is_context-within_js = abap_true OR is_context-within_style = abap_true. IF is_context-within_js = abap_true AND lv_len >= 8 AND lv_line(8) = '= 7 AND lv_line(7) = '= 1 AND lv_line(1) = '}'. rs_result-curly_close = abap_true. ENDIF. FIND ALL OCCURRENCES OF '{' IN lv_line MATCH COUNT rs_result-openings. FIND ALL OCCURRENCES OF '}' IN lv_line MATCH COUNT rs_result-closings. ENDIF. ELSE. IF lv_len >= 7 AND lv_line(7) = ' 0. " Not found rs_result-script_open = abap_true. ENDIF. ENDIF. IF lv_len >= 6 AND lv_line(6) = ' 0. " Not found rs_result-style_open = abap_true. ENDIF. ENDIF. IF lv_len >= 2 AND lv_line(2) = ' rs_result-openings. * if everything is closings, there are no single tags FIND ALL OCCURRENCES OF REGEX go_single_tags_re IN lv_line MATCH COUNT rs_result-singles. ENDIF. rs_result-openings = rs_result-openings - rs_result-closings - rs_result-singles. ENDIF. " Textarea (same assumptions as above) IF is_context-within_textarea = abap_true AND lv_len >= 10 AND lv_line(10) = '= 9 AND lv_line(9) = ' 0. " Not found rs_result-textarea_open = abap_true. ENDIF. ENDIF. " Pre (same assumptions as above) IF is_context-within_pre = abap_true AND lv_len >= 5 AND lv_line(5) = '= 4 AND lv_line(4) = ' 0. " Not found rs_result-pre_open = abap_true. ENDIF. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_html_action_utils IMPLEMENTATION. METHOD add_field. DATA ls_field LIKE LINE OF ct_field. FIELD-SYMBOLS TYPE any. ls_field-name = iv_name. CASE cl_abap_typedescr=>describe_by_data( ig_field )->kind. WHEN cl_abap_typedescr=>kind_elem. ls_field-value = ig_field. WHEN cl_abap_typedescr=>kind_struct. ASSIGN COMPONENT iv_name OF STRUCTURE ig_field TO . ASSERT IS ASSIGNED. ls_field-value = . WHEN OTHERS. ASSERT 0 = 1. ENDCASE. APPEND ls_field TO ct_field. ENDMETHOD. METHOD fields_to_string. * There is no equivalent to cl_http_utility=>fields_to_string released in ABAP Cloud, * see cl_web_http_utility DATA lt_tab TYPE STANDARD TABLE OF string WITH DEFAULT KEY. DATA lv_str TYPE string. DATA ls_field LIKE LINE OF it_fields. LOOP AT it_fields INTO ls_field. ls_field-value = escape( val = ls_field-value format = cl_abap_format=>e_url ). lv_str = ls_field-name && '=' && ls_field-value. APPEND lv_str TO lt_tab. ENDLOOP. rv_string = concat_lines_of( table = lt_tab sep = '&' ). ENDMETHOD. METHOD jump_encode. DATA lt_fields TYPE ty_name_value_tt. add_field( EXPORTING iv_name = 'TYPE' ig_field = iv_obj_type CHANGING ct_field = lt_fields ). add_field( EXPORTING iv_name = 'NAME' ig_field = iv_obj_name CHANGING ct_field = lt_fields ). IF iv_filename IS NOT INITIAL. add_field( EXPORTING iv_name = 'FILE' ig_field = iv_filename CHANGING ct_field = lt_fields ). ENDIF. rv_string = fields_to_string( lt_fields ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_html_form IMPLEMENTATION. METHOD /apmg/if_apm_gui_hotkeys~get_hotkey_actions. DATA: ls_hotkey_action LIKE LINE OF rt_hotkey_actions. FIELD-SYMBOLS: TYPE /apmg/if_apm_html_form=>ty_command. ls_hotkey_action-ui_component = |Form-{ mv_form_id }|. READ TABLE mt_commands ASSIGNING WITH KEY cmd_type = /apmg/if_apm_html_form=>c_cmd_type-input_main. IF sy-subrc = 0. ls_hotkey_action-description = -label. ls_hotkey_action-action = -action. ls_hotkey_action-hotkey = |Enter|. INSERT ls_hotkey_action INTO TABLE rt_hotkey_actions. ENDIF. READ TABLE mt_commands ASSIGNING WITH KEY action = /apmg/if_apm_gui_router=>c_action-go_back. IF sy-subrc = 0. ls_hotkey_action-description = -label. ls_hotkey_action-action = -action. ls_hotkey_action-hotkey = |F3|. INSERT ls_hotkey_action INTO TABLE rt_hotkey_actions. ENDIF. ENDMETHOD. METHOD checkbox. DATA ls_field LIKE LINE OF mt_fields. ls_field-type = /apmg/if_apm_html_form=>c_field_type-checkbox. ls_field-name = iv_name. ls_field-label = iv_label. ls_field-hint = iv_hint. ls_field-readonly = iv_readonly. APPEND ls_field TO mt_fields. ro_self = me. ENDMETHOD. METHOD column. FIELD-SYMBOLS LIKE LINE OF mt_fields. DATA ls_column LIKE LINE OF -subitems. DATA lv_size TYPE i. lv_size = lines( mt_fields ). ASSERT lv_size > 0. " Exception ? Maybe add zcx_no_check ? READ TABLE mt_fields INDEX lv_size ASSIGNING . ASSERT sy-subrc = 0. ASSERT -type = /apmg/if_apm_html_form=>c_field_type-table. ls_column-label = iv_label. ls_column-value = iv_width. ls_column-readonly = iv_readonly. APPEND ls_column TO -subitems. ro_self = me. ENDMETHOD. METHOD command. DATA ls_cmd LIKE LINE OF mt_commands. ASSERT iv_cmd_type BETWEEN 1 AND 4. ls_cmd-label = iv_label. ls_cmd-action = iv_action. ls_cmd-cmd_type = iv_cmd_type. APPEND ls_cmd TO mt_commands. ro_self = me. ENDMETHOD. METHOD create. DATA lv_ts TYPE timestampl. CREATE OBJECT ro_form. ro_form->mv_form_id = iv_form_id. ro_form->mv_help_page = iv_help_page. IF ro_form->mv_form_id IS INITIAL. GET TIME STAMP FIELD lv_ts. ro_form->mv_form_id = |form_{ lv_ts }|. ENDIF. ro_form->mv_webgui = /apmg/cl_apm_gui_factory=>get_frontend_services( )->is_webgui( ). ENDMETHOD. METHOD get_fields. rt_fields = mt_fields. ENDMETHOD. METHOD hidden. DATA ls_field LIKE LINE OF mt_fields. ls_field-type = /apmg/if_apm_html_form=>c_field_type-hidden. ls_field-name = iv_name. APPEND ls_field TO mt_fields. ro_self = me. ENDMETHOD. METHOD number. DATA ls_field LIKE LINE OF mt_fields. ls_field-type = /apmg/if_apm_html_form=>c_field_type-number. ls_field-name = iv_name. ls_field-label = iv_label. ls_field-readonly = iv_readonly. ls_field-min = iv_min. ls_field-max = iv_max. ls_field-hint = iv_hint. ls_field-required = iv_required. APPEND ls_field TO mt_fields. ro_self = me. ENDMETHOD. METHOD option. FIELD-SYMBOLS LIKE LINE OF mt_fields. DATA ls_option LIKE LINE OF -subitems. DATA lv_size TYPE i. lv_size = lines( mt_fields ). ASSERT lv_size > 0. " Exception ? Maybe add zcx_no_check ? READ TABLE mt_fields INDEX lv_size ASSIGNING . ASSERT sy-subrc = 0. ASSERT -type = /apmg/if_apm_html_form=>c_field_type-radio. " Or dropdown - TODO in future ls_option-label = iv_label. ls_option-value = iv_value. APPEND ls_option TO -subitems. ro_self = me. ENDMETHOD. METHOD radio. DATA ls_field LIKE LINE OF mt_fields. ls_field-type = /apmg/if_apm_html_form=>c_field_type-radio. ls_field-name = iv_name. ls_field-label = iv_label. ls_field-default_value = iv_default_value. ls_field-hint = iv_hint. ls_field-click = iv_action. " put options into one column instead of side-by-side ls_field-condense = iv_condense. APPEND ls_field TO mt_fields. ro_self = me. ENDMETHOD. METHOD render. FIELD-SYMBOLS LIKE LINE OF mt_fields. FIELD-SYMBOLS LIKE LINE OF mt_commands. DATA lv_hint TYPE string. DATA ls_form_id TYPE string. DATA ls_form_action TYPE string. DATA lv_cur_group TYPE string. DATA lv_url TYPE string. DATA lv_autofocus TYPE abap_bool. IF mv_form_id IS NOT INITIAL. ls_form_id = | id="{ mv_form_id }"|. ENDIF. LOOP AT mt_commands ASSIGNING WHERE cmd_type = /apmg/if_apm_html_form=>c_cmd_type-input_main. ls_form_action = | action="sapevent:{ -action }"|. EXIT. ENDLOOP. ri_html = /apmg/cl_apm_html=>create( ). ri_html->add( |
| ). " to center use 'dialog-form-center' ri_html->add( |
| ). " Add hidden button that triggers main command when pressing enter LOOP AT mt_commands ASSIGNING WHERE cmd_type = /apmg/if_apm_html_form=>c_cmd_type-input_main. ri_html->add( || ). EXIT. ENDLOOP. lv_autofocus = abap_true. LOOP AT mt_fields ASSIGNING . AT FIRST. IF -type <> /apmg/if_apm_html_form=>c_field_type-field_group. ri_html->add( |
    | ). ENDIF. ENDAT. IF -type = /apmg/if_apm_html_form=>c_field_type-field_group. IF lv_cur_group IS NOT INITIAL AND lv_cur_group <> -name. ri_html->add( |
| ). ri_html->add( || ). ENDIF. IF -hint IS NOT INITIAL. lv_hint = | title="{ -hint }"|. ELSE. lv_hint = ''. ENDIF. lv_cur_group = -name. ri_html->add( |
| ). ri_html->add( |{ -label }| ). ri_html->add( |
    | ). CONTINUE. ENDIF. render_field( ii_html = ri_html io_values = io_values io_validation_log = io_validation_log is_field = iv_autofocus = lv_autofocus ). lv_autofocus = abap_false. AT LAST. ri_html->add( |
| ). IF lv_cur_group IS NOT INITIAL. ri_html->add( |
| ). ENDIF. ENDAT. ENDLOOP. ri_html->add( |
    | ). ri_html->add( |
  • | ). IF mv_help_page IS NOT INITIAL. lv_url = escape( val = mv_help_page format = cl_abap_format=>e_url ). ri_html->add_a( iv_txt = /apmg/cl_apm_gui_buttons=>help( ) iv_act = |{ /apmg/if_apm_gui_router=>c_action-url }?url={ lv_url }| iv_class = 'dialog-help' iv_title = 'Help' ). ENDIF. LOOP AT mt_commands ASSIGNING . render_command( ii_html = ri_html is_cmd = ). ENDLOOP. ri_html->add( |
  • | ). ri_html->add( |
| ). ri_html->add( || ). ri_html->add( |
| ). register_handlers( ). ENDMETHOD. METHOD render_command. " HTML GUI supports only links for submitting forms IF mv_webgui = abap_true. render_command_link( is_cmd = is_cmd ii_html = ii_html ). RETURN. ENDIF. CASE is_cmd-cmd_type. WHEN /apmg/if_apm_html_form=>c_cmd_type-link. render_command_link( is_cmd = is_cmd ii_html = ii_html ). WHEN /apmg/if_apm_html_form=>c_cmd_type-button. ii_html->add( || ). WHEN /apmg/if_apm_html_form=>c_cmd_type-input. ii_html->add( || ). WHEN /apmg/if_apm_html_form=>c_cmd_type-input_main. ii_html->add( || ). WHEN OTHERS. ASSERT 0 = 1. ENDCASE. ENDMETHOD. METHOD render_command_link. DATA lv_class TYPE string VALUE 'dialog-commands'. IF is_cmd-cmd_type = /apmg/if_apm_html_form=>c_cmd_type-input_main. lv_class = lv_class && ' main'. ENDIF. ii_html->add_a( iv_txt = is_cmd-label iv_act = is_cmd-action iv_class = lv_class ). ENDMETHOD. METHOD render_field. DATA: ls_attr TYPE ty_attr, lv_item_class TYPE string. " Get value and validation error ls_attr-value = io_values->get( is_field-name ). IF is_field-type <> /apmg/if_apm_html_form=>c_field_type-textarea. ls_attr-value = escape( val = ls_attr-value format = cl_abap_format=>e_html_attr ). ENDIF. IF io_validation_log IS BOUND. ls_attr-error = io_validation_log->get( is_field-name ). IF ls_attr-error IS NOT INITIAL. ls_attr-error = escape( val = ls_attr-error format = cl_abap_format=>e_html_text ). ls_attr-error = |{ ls_attr-error }|. ENDIF. ENDIF. " Prepare field attributes IF is_field-required = abap_true. ls_attr-required = ' *'. ENDIF. IF is_field-hint IS NOT INITIAL. ls_attr-hint = escape( val = is_field-hint format = cl_abap_format=>e_html_attr ). ls_attr-hint = | title="{ ls_attr-hint }"|. ENDIF. IF is_field-placeholder IS NOT INITIAL. ls_attr-placeholder = escape( val = is_field-placeholder format = cl_abap_format=>e_html_attr ). ls_attr-placeholder = | placeholder="{ ls_attr-placeholder }"|. ENDIF. IF is_field-readonly = abap_true. ls_attr-readonly = ' readonly'. ENDIF. IF iv_autofocus = abap_true. ls_attr-autofocus = ' autofocus'. ENDIF. " Prepare item class lv_item_class = is_field-item_class. IF ls_attr-error IS NOT INITIAL. lv_item_class = condense( lv_item_class && ' error' ). ENDIF. IF is_field-type = /apmg/if_apm_html_form=>c_field_type-text AND is_field-max BETWEEN 1 AND 20. " Reduced width for short fields lv_item_class = lv_item_class && ' w40'. ENDIF. IF is_field-type = /apmg/if_apm_html_form=>c_field_type-hidden. lv_item_class = lv_item_class && ' hidden'. ENDIF. IF lv_item_class IS NOT INITIAL. lv_item_class = | class="{ lv_item_class }"|. ENDIF. " Render field ii_html->add( || ). CASE is_field-type. WHEN /apmg/if_apm_html_form=>c_field_type-text OR /apmg/if_apm_html_form=>c_field_type-number. render_field_text( ii_html = ii_html is_field = is_field is_attr = ls_attr ). WHEN /apmg/if_apm_html_form=>c_field_type-textarea. render_field_textarea( ii_html = ii_html is_field = is_field is_attr = ls_attr ). WHEN /apmg/if_apm_html_form=>c_field_type-checkbox. render_field_checkbox( ii_html = ii_html is_field = is_field is_attr = ls_attr ). WHEN /apmg/if_apm_html_form=>c_field_type-radio. render_field_radio( ii_html = ii_html is_field = is_field is_attr = ls_attr ). WHEN /apmg/if_apm_html_form=>c_field_type-table. render_field_table( ii_html = ii_html is_field = is_field is_attr = ls_attr io_values = io_values ). WHEN /apmg/if_apm_html_form=>c_field_type-hidden. render_field_hidden( ii_html = ii_html is_field = is_field is_attr = ls_attr ). WHEN OTHERS. ASSERT 1 = 0. ENDCASE. ii_html->add( '' ). ENDMETHOD. METHOD render_field_checkbox. DATA lv_checked TYPE string. DATA lv_disabled TYPE string. IF is_attr-error IS NOT INITIAL. ii_html->add( is_attr-error ). ENDIF. IF is_attr-value = abap_true OR is_attr-value = 'on'. " boolc return ` ` which is not initial -> bug after 1st validation lv_checked = ' checked'. ENDIF. IF is_attr-readonly IS NOT INITIAL. lv_disabled = ' disabled'. ENDIF. ii_html->add( || ). ii_html->add( || ). ENDMETHOD. METHOD render_field_hidden. ii_html->add( || ). ENDMETHOD. METHOD render_field_radio. DATA: lv_checked TYPE string, lv_opt_id TYPE string, lv_opt_value TYPE string, lv_onclick TYPE string. FIELD-SYMBOLS LIKE LINE OF is_field-subitems. ii_html->add( |{ is_field-label }| ). IF is_attr-error IS NOT INITIAL. ii_html->add( is_attr-error ). ENDIF. ii_html->add( |
| ). LOOP AT is_field-subitems ASSIGNING . lv_opt_id = |{ is_field-name }{ sy-tabix }|. lv_opt_value = escape( val = -value format = cl_abap_format=>e_html_attr ). CLEAR lv_checked. IF is_attr-value = lv_opt_value OR ( is_attr-value IS INITIAL AND lv_opt_value = is_field-default_value ). lv_checked = ' checked'. ENDIF. " With edge browser control radio buttons aren't checked automatically when " activated with link hints. Therefore we need to check them manually. IF is_field-click IS NOT INITIAL. lv_onclick = |onclick="| && |var form = document.getElementById('{ mv_form_id }');| && |document.getElementById('{ lv_opt_id }').checked = true;| && |form.action = 'sapevent:{ is_field-click }';| && |form.submit();"|. ELSE. lv_onclick = |onclick="document.getElementById('{ lv_opt_id }').checked = true;"|. ENDIF. IF is_field-condense = abap_true. ii_html->add( '
' ). ENDIF. ii_html->add( || ). ii_html->add( || ). IF is_field-condense = abap_true. ii_html->add( '
' ). ENDIF. ENDLOOP. ii_html->add( '
' ). ENDMETHOD. METHOD render_field_table. DATA: lv_value TYPE string, lv_readonly TYPE string, lv_rows TYPE i, lv_cell_id TYPE string. FIELD-SYMBOLS LIKE LINE OF is_field-subitems. ii_html->add( || ). IF is_attr-error IS NOT INITIAL. ii_html->add( is_attr-error ). ENDIF. lv_rows = io_values->get( |{ is_field-name }-{ /apmg/if_apm_html_form=>c_rows }| ). " Render table only if there are some data rows IF lv_rows > 0. ii_html->add( || ). ii_html->add( || ). ii_html->add( || ). LOOP AT is_field-subitems ASSIGNING . CLEAR lv_value. IF -value IS NOT INITIAL. lv_value = escape( val = -value format = cl_abap_format=>e_html_attr ). lv_value = | width="{ lv_value }"|. ENDIF. ii_html->add( |{ -label }| ). ENDLOOP. ii_html->add( || ). ii_html->add( || ). ii_html->add( || ). DO lv_rows TIMES. lv_rows = sy-index. ii_html->add( || ). LOOP AT is_field-subitems ASSIGNING . lv_cell_id = |{ is_field-name }-{ lv_rows }-{ sy-tabix }|. lv_value = escape( val = io_values->get( lv_cell_id ) format = cl_abap_format=>e_html_attr ). CLEAR lv_readonly. IF -readonly = abap_true. lv_readonly = | readonly|. ENDIF. ii_html->add( || ). ENDLOOP. ii_html->add( || ). ENDDO. ii_html->add( || ). ii_html->add( |
| ). ELSE. ii_html->add( || ). ENDIF. " Hidden field with number of rows to simplify getting values from form lv_value = |{ is_field-name }-{ /apmg/if_apm_html_form=>c_rows }|. ii_html->add( || ). ENDMETHOD. METHOD render_field_text. DATA: lv_type TYPE string, lv_minlength TYPE string, lv_maxlength TYPE string. ii_html->add( || ). IF is_attr-error IS NOT INITIAL. ii_html->add( is_attr-error ). ENDIF. IF is_field-side_action IS NOT INITIAL. ii_html->add( '
' ). " Ugly :( ENDIF. IF is_field-type = /apmg/if_apm_html_form=>c_field_type-number. lv_type = 'number'. ELSEIF is_field-password = abap_true. lv_type = 'password'. ELSE. lv_type = 'text'. ENDIF. IF is_field-min > 0. lv_minlength = | minlength={ is_field-min }|. ENDIF. IF is_field-max > 0 AND is_field-max < cl_abap_math=>max_int4. lv_maxlength = | maxlength={ is_field-max }|. ENDIF. ii_html->add( || ). IF is_field-side_action IS NOT INITIAL. ii_html->add( '
' ). ii_html->add( '
' ). ii_html->add( || ). ii_html->add( '
' ). ENDIF. ENDMETHOD. METHOD render_field_textarea. DATA lv_rows TYPE string. DATA lv_cols TYPE string. DATA lv_html TYPE string. ii_html->add( || ). IF is_attr-error IS NOT INITIAL. ii_html->add( is_attr-error ). ENDIF. IF is_field-rows > 0. lv_rows = | rows="{ is_field-rows }"|. ELSEIF is_attr-value IS NOT INITIAL. lv_rows = | rows="{ lines( zcl_abapgit_convert=>split_string( is_attr-value ) ) + 1 }"|. ENDIF. IF is_field-cols > 0. lv_cols = | cols="{ is_field-cols }"|. ENDIF. " Avoid adding line-breaks inside textarea tag (except for the actual value) lv_html = ||. ii_html->add( lv_html ). ENDMETHOD. METHOD start_group. DATA ls_field LIKE LINE OF mt_fields. ls_field-type = /apmg/if_apm_html_form=>c_field_type-field_group. ls_field-label = iv_label. ls_field-name = iv_name. ls_field-hint = iv_hint. APPEND ls_field TO mt_fields. ro_self = me. ENDMETHOD. METHOD table. DATA ls_field LIKE LINE OF mt_fields. ls_field-type = /apmg/if_apm_html_form=>c_field_type-table. ls_field-name = iv_name. ls_field-label = iv_label. ls_field-hint = iv_hint. APPEND ls_field TO mt_fields. ro_self = me. ENDMETHOD. METHOD text. DATA ls_field LIKE LINE OF mt_fields. ls_field-type = /apmg/if_apm_html_form=>c_field_type-text. ls_field-name = iv_name. ls_field-label = iv_label. ls_field-upper_case = iv_upper_case. ls_field-readonly = iv_readonly. ls_field-min = iv_min. ls_field-max = iv_max. ls_field-password = iv_password. ls_field-condense = iv_condense. ls_field-hint = iv_hint. ls_field-required = iv_required. ls_field-placeholder = iv_placeholder. IF iv_side_action IS NOT INITIAL AND mv_form_id IS NOT INITIAL. ls_field-item_class = 'with-command'. ls_field-side_action = iv_side_action. ls_field-dblclick = | ondblclick="document.getElementById('{ mv_form_id }').action = 'sapevent:| && |{ iv_side_action }'; document.getElementById('{ mv_form_id }').submit()"|. ENDIF. APPEND ls_field TO mt_fields. ro_self = me. ENDMETHOD. METHOD textarea. DATA ls_field LIKE LINE OF mt_fields. ls_field-type = /apmg/if_apm_html_form=>c_field_type-textarea. ls_field-name = iv_name. ls_field-label = iv_label. ls_field-readonly = iv_readonly. ls_field-hint = iv_hint. ls_field-required = iv_required. ls_field-placeholder = iv_placeholder. ls_field-rows = iv_rows. ls_field-cols = iv_cols. ls_field-upper_case = iv_upper_case. APPEND ls_field TO mt_fields. ro_self = me. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_html_form_utils IMPLEMENTATION. METHOD constructor. mo_form = io_form. ENDMETHOD. METHOD create. CREATE OBJECT ro_form_util EXPORTING io_form = io_form. ENDMETHOD. METHOD exit. DATA lv_answer TYPE c LENGTH 1. IF is_dirty( io_form_data = io_form_data io_compare_with = io_compare_with ) = abap_true. lv_answer = /apmg/cl_apm_gui_factory=>get_popups( )->popup_to_confirm( iv_display_cancel_button = abap_false iv_titlebar = 'abapGit - Unsaved Changes' iv_text_question = 'There are unsaved changes. Do you want to exit the form?' iv_default_button = '2' ). IF lv_answer = '1'. rv_state = /apmg/cl_apm_gui=>c_event_state-go_back_to_bookmark. ELSE. rv_state = /apmg/cl_apm_gui=>c_event_state-no_more_act. ENDIF. ELSE. rv_state = /apmg/cl_apm_gui=>c_event_state-go_back_to_bookmark. ENDIF. ENDMETHOD. METHOD is_dirty. rv_dirty = boolc( io_form_data->mt_entries <> io_compare_with->mt_entries ). ENDMETHOD. METHOD is_empty. DATA: lt_fields TYPE /apmg/if_apm_html_form=>ty_fields, lv_value TYPE string, lv_rows TYPE i, lv_row TYPE i. FIELD-SYMBOLS LIKE LINE OF lt_fields. rv_empty = abap_true. lt_fields = mo_form->get_fields( ). LOOP AT lt_fields ASSIGNING WHERE type <> /apmg/if_apm_html_form=>c_field_type-field_group. lv_value = condense( val = io_form_data->get( -name ) del = ` ` ). IF -type = /apmg/if_apm_html_form=>c_field_type-number. rv_empty = boolc( lv_value IS INITIAL OR lv_value = '0' ). ELSEIF -type = /apmg/if_apm_html_form=>c_field_type-table. lv_rows = io_form_data->get( |{ -name }-{ /apmg/if_apm_html_form=>c_rows }| ). DO lv_rows TIMES. lv_row = sy-index. DO lines( -subitems ) TIMES. lv_value = io_form_data->get( |{ -name }-{ lv_row }-{ sy-index }| ). rv_empty = boolc( lv_value IS INITIAL ). IF rv_empty <> abap_true. RETURN. ENDIF. ENDDO. ENDDO. ELSEIF -type = /apmg/if_apm_html_form=>c_field_type-textarea. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf IN lv_value WITH ''. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>newline IN lv_value WITH ''. rv_empty = boolc( lv_value IS INITIAL ). ELSE. rv_empty = boolc( lv_value IS INITIAL ). ENDIF. IF rv_empty <> abap_true. RETURN. ENDIF. ENDLOOP. ENDMETHOD. METHOD normalize. DATA: lt_fields TYPE /apmg/if_apm_html_form=>ty_fields, lv_value TYPE string, lv_rows TYPE i, lv_row TYPE i, lv_len TYPE i. FIELD-SYMBOLS LIKE LINE OF lt_fields. CREATE OBJECT ro_form_data. IF io_form_data->is_empty( ) = abap_true. RETURN. ENDIF. lt_fields = mo_form->get_fields( ). LOOP AT lt_fields ASSIGNING WHERE type <> /apmg/if_apm_html_form=>c_field_type-field_group AND type <> /apmg/if_apm_html_form=>c_field_type-hidden. CLEAR lv_value. lv_value = io_form_data->get( -name ). IF -condense = abap_true. lv_value = condense( val = lv_value del = ` ` ). ENDIF. IF -type = /apmg/if_apm_html_form=>c_field_type-checkbox. ro_form_data->set( iv_key = -name iv_val = boolc( lv_value = 'on' ) ) ##TYPE. ELSEIF ( -type = /apmg/if_apm_html_form=>c_field_type-text OR -type = /apmg/if_apm_html_form=>c_field_type-textarea ) AND -upper_case = abap_true. ro_form_data->set( iv_key = -name iv_val = to_upper( lv_value ) ). ELSEIF -type = /apmg/if_apm_html_form=>c_field_type-number. " Numeric value is checked in validation ro_form_data->set( iv_key = -name iv_val = condense( val = lv_value del = ` ` ) ). ELSEIF -type = /apmg/if_apm_html_form=>c_field_type-table. lv_rows = io_form_data->get( |{ -name }-{ /apmg/if_apm_html_form=>c_rows }| ). DO lv_rows TIMES. lv_row = sy-index. DO lines( -subitems ) TIMES. lv_value = io_form_data->get( |{ -name }-{ lv_row }-{ sy-index }| ). ro_form_data->set( iv_key = |{ -name }-{ lv_row }-{ sy-index }| iv_val = lv_value ). ENDDO. ENDDO. ro_form_data->set( iv_key = |{ -name }-{ /apmg/if_apm_html_form=>c_rows }| iv_val = |{ lv_rows }| ). ELSEIF -type = /apmg/if_apm_html_form=>c_field_type-textarea. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf IN lv_value WITH cl_abap_char_utilities=>newline. " Remove last line if empty (ie 2x newline) lv_len = strlen( lv_value ) - 2. IF lv_len >= 0 AND lv_value+lv_len(1) = cl_abap_char_utilities=>newline. lv_len = lv_len + 1. lv_value = lv_value(lv_len). ENDIF. ro_form_data->set( iv_key = -name iv_val = lv_value ). ELSE. ro_form_data->set( iv_key = -name iv_val = lv_value ). ENDIF. ENDLOOP. ENDMETHOD. METHOD normalize_abapgit. " TODO: replace with normalize DATA: lt_fields TYPE /apmg/if_apm_html_form=>ty_fields, lv_value TYPE string, lv_rows TYPE i, lv_row TYPE i, lv_len TYPE i. FIELD-SYMBOLS LIKE LINE OF lt_fields. CREATE OBJECT ro_form_data. IF io_form_data->is_empty( ) = abap_true. RETURN. ENDIF. lt_fields = mo_form->get_fields( ). LOOP AT lt_fields ASSIGNING WHERE type <> /apmg/if_apm_html_form=>c_field_type-field_group AND type <> /apmg/if_apm_html_form=>c_field_type-hidden. CLEAR lv_value. lv_value = io_form_data->get( -name ). IF -condense = abap_true. lv_value = condense( val = lv_value del = ` ` ). ENDIF. IF -type = /apmg/if_apm_html_form=>c_field_type-checkbox. ro_form_data->set( iv_key = -name iv_val = boolc( lv_value = 'on' ) ) ##TYPE. ELSEIF ( -type = /apmg/if_apm_html_form=>c_field_type-text OR -type = /apmg/if_apm_html_form=>c_field_type-textarea ) AND -upper_case = abap_true. ro_form_data->set( iv_key = -name iv_val = to_upper( lv_value ) ). ELSEIF -type = /apmg/if_apm_html_form=>c_field_type-number. " Numeric value is checked in validation ro_form_data->set( iv_key = -name iv_val = condense( val = lv_value del = ` ` ) ). ELSEIF -type = /apmg/if_apm_html_form=>c_field_type-table. lv_rows = io_form_data->get( |{ -name }-{ /apmg/if_apm_html_form=>c_rows }| ). DO lv_rows TIMES. lv_row = sy-index. DO lines( -subitems ) TIMES. lv_value = io_form_data->get( |{ -name }-{ lv_row }-{ sy-index }| ). ro_form_data->set( iv_key = |{ -name }-{ lv_row }-{ sy-index }| iv_val = lv_value ). ENDDO. ENDDO. ro_form_data->set( iv_key = |{ -name }-{ /apmg/if_apm_html_form=>c_rows }| iv_val = |{ lv_rows }| ). ELSEIF -type = /apmg/if_apm_html_form=>c_field_type-textarea. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf IN lv_value WITH cl_abap_char_utilities=>newline. " Remove last line if empty (ie 2x newline) lv_len = strlen( lv_value ) - 2. IF lv_len >= 0 AND lv_value+lv_len(1) = cl_abap_char_utilities=>newline. lv_len = lv_len + 1. lv_value = lv_value(lv_len). ENDIF. ro_form_data->set( iv_key = -name iv_val = lv_value ). ELSE. ro_form_data->set( iv_key = -name iv_val = lv_value ). ENDIF. ENDLOOP. ENDMETHOD. METHOD set_data. mo_form_data = io_form_data. ENDMETHOD. METHOD validate. DATA: lt_fields TYPE /apmg/if_apm_html_form=>ty_fields, lv_value TYPE string, lv_number TYPE p LENGTH 16 DECIMALS 0. FIELD-SYMBOLS LIKE LINE OF lt_fields. CREATE OBJECT ro_validation_log. lt_fields = mo_form->get_fields( ). LOOP AT lt_fields ASSIGNING . lv_value = io_form_data->get( -name ). IF -condense = abap_true. lv_value = condense( val = lv_value del = ` ` ). ENDIF. IF -required IS NOT INITIAL AND lv_value IS INITIAL. ro_validation_log->set( iv_key = -name iv_val = |{ -label } cannot be empty| ). ENDIF. CASE -type. WHEN /apmg/if_apm_html_form=>c_field_type-text. IF -min = -max AND strlen( lv_value ) <> -min. ro_validation_log->set( iv_key = -name iv_val = |{ -label } must be exactly { -min } characters long| ). ELSE. IF -min <> cl_abap_math=>min_int4 AND strlen( lv_value ) < -min. ro_validation_log->set( iv_key = -name iv_val = |{ -label } must not be shorter than { -min } characters| ). ENDIF. IF -max <> cl_abap_math=>max_int4 AND strlen( lv_value ) > -max. ro_validation_log->set( iv_key = -name iv_val = |{ -label } must not be longer than { -max } characters| ). ENDIF. ENDIF. WHEN /apmg/if_apm_html_form=>c_field_type-number. TRY. lv_number = lv_value. CATCH cx_root. ro_validation_log->set( iv_key = -name iv_val = |{ -label } is not numeric| ). CONTINUE. ENDTRY. IF -min <> cl_abap_math=>min_int4 AND lv_number < -min. ro_validation_log->set( iv_key = -name iv_val = |{ -label } must not be lower than { -min }| ). ENDIF. IF -max <> cl_abap_math=>max_int4 AND lv_number > -max. ro_validation_log->set( iv_key = -name iv_val = |{ -label } must not be higher than { -max }| ). ENDIF. ENDCASE. ENDLOOP. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_html_parts IMPLEMENTATION. METHOD add_part. DATA lr_collection TYPE REF TO ty_named_collection. lr_collection = get_collection( iv_collection = iv_collection iv_create_if_missing = abap_true ). APPEND ii_part TO lr_collection->pile. ENDMETHOD. METHOD clear. CLEAR mt_part_collections. ENDMETHOD. METHOD get_collection. READ TABLE mt_part_collections REFERENCE INTO rr_collection WITH KEY name = iv_collection. IF sy-subrc <> 0 AND iv_create_if_missing = abap_true. APPEND INITIAL LINE TO mt_part_collections REFERENCE INTO rr_collection. rr_collection->name = iv_collection. ENDIF. ENDMETHOD. METHOD get_collection_names. FIELD-SYMBOLS LIKE LINE OF mt_part_collections. LOOP AT mt_part_collections ASSIGNING . APPEND -name TO rt_list. ENDLOOP. ENDMETHOD. METHOD get_collection_size. DATA lr_collection TYPE REF TO ty_named_collection. lr_collection = get_collection( iv_collection ). IF lr_collection IS BOUND. rv_size = lines( lr_collection->pile ). ENDIF. ENDMETHOD. METHOD get_parts. DATA lr_collection TYPE REF TO ty_named_collection. lr_collection = get_collection( iv_collection ). IF lr_collection IS BOUND. rt_parts = lr_collection->pile. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_html_table IMPLEMENTATION. METHOD apply_sorting. DATA lv_field TYPE abap_compname. DATA ls_col LIKE LINE OF mt_columns. IF ms_sorting_state-column_id IS INITIAL. RETURN. ENDIF. READ TABLE mt_columns INTO ls_col WITH KEY column_id = ms_sorting_state-column_id. IF sy-subrc <> 0. RETURN. " ??? but let's not throw errors here ENDIF. IF ls_col-from_field IS NOT INITIAL. lv_field = to_upper( ls_col-from_field ). ELSE. lv_field = to_upper( ms_sorting_state-column_id ). ENDIF. " What to do if column_id is not a table field ? " Well... then it is a complex case for an external sorting, don't use the simple one IF ms_sorting_state-descending = abap_true. SORT ct_data BY (lv_field) DESCENDING. ELSE. SORT ct_data BY (lv_field) ASCENDING. ENDIF. ENDMETHOD. METHOD cid_attr. rs_data_attr-name = 'cid'. rs_data_attr-value = iv_column_id. ENDMETHOD. METHOD create. CREATE OBJECT ro_instance. ro_instance->mi_renderer = ii_renderer. ro_instance->ms_sorting_state = is_initial_sorting_state. ENDMETHOD. METHOD define_column. FIELD-SYMBOLS LIKE LINE OF mt_columns. ASSERT iv_column_id IS NOT INITIAL. ro_self = me. APPEND INITIAL LINE TO mt_columns ASSIGNING . -column_id = iv_column_id. -column_title = iv_column_title. -from_field = to_upper( iv_from_field ). -sortable = iv_sortable. IF mr_last_grp IS NOT INITIAL. mr_last_grp->group_span = mr_last_grp->group_span + 1. ENDIF. ENDMETHOD. METHOD define_column_group. IF lines( mt_columns ) > 0 AND mr_last_grp IS INITIAL. " Groups should cover all columns " you can create a group with empty title if groups start later VISUALLY RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Start groups from the beginning'. ENDIF. ro_self = me. APPEND INITIAL LINE TO mt_columns REFERENCE INTO mr_last_grp. mr_last_grp->is_group = abap_true. mr_last_grp->column_id = iv_group_id. mr_last_grp->column_title = iv_group_title. ENDMETHOD. METHOD detect_sorting_request. DATA lv_req TYPE string. IF find( val = iv_event regex = c_sort_by_event_regex ) = 0 ##REGEX_POSIX. lv_req = replace( val = iv_event sub = c_sort_by_event_prefix with = '' ). SPLIT lv_req AT ':' INTO rs_sorting_request-column_id lv_req. rs_sorting_request-descending = boolc( lv_req = 'dsc' ). ENDIF. ENDMETHOD. METHOD gid_attr. rs_data_attr-name = 'gid'. rs_data_attr-value = iv_column_id. ENDMETHOD. METHOD process_sorting_request. DATA ls_sorting_req LIKE ms_sorting_state. ls_sorting_req = detect_sorting_request( iv_event ). IF ls_sorting_req IS NOT INITIAL. ms_sorting_state = ls_sorting_req. rv_processed = abap_true. ENDIF. ENDMETHOD. METHOD render. DATA lv_attrs TYPE string. DATA lr_data_copy TYPE REF TO data. FIELD-SYMBOLS TYPE ANY TABLE. IF ii_renderer IS BOUND. mi_renderer = ii_renderer. ENDIF. ASSERT mi_renderer IS BOUND. mv_with_cids = iv_with_cids. mv_table_id = iv_id. ASSIGN it_data TO . IF is_sorting_state IS NOT INITIAL. ms_sorting_state = is_sorting_state. ELSEIF ms_sorting_state IS NOT INITIAL. " If sorting state is not passed, " but there is non empty sort state then suppose simple sorting mode " so that table sorts the data itself before rendering " TODO not efficient, maybe bind changing data from outside CREATE DATA lr_data_copy LIKE it_data. ASSIGN lr_data_copy->* TO . = it_data. apply_sorting( CHANGING ct_data = ). ENDIF. IF iv_id IS NOT INITIAL. lv_attrs = lv_attrs && | id="{ iv_id }"|. ENDIF. IF iv_css_class IS NOT INITIAL. lv_attrs = lv_attrs && | class="{ iv_css_class }"|. ENDIF. mi_html = /apmg/cl_apm_html=>create( ). ri_html = mi_html. IF iv_wrap_in_div IS NOT INITIAL. mi_html->add( |
| ). ENDIF. mi_html->add( || ). render_thead( ). render_tbody( ). mi_html->add( '' ). IF iv_wrap_in_div IS NOT INITIAL. mi_html->add( '
' ). ENDIF. ENDMETHOD. METHOD render_column_title. DATA lv_direction TYPE string. DATA lv_arrow TYPE string. DATA lv_sort_active TYPE string. IF is_col-sortable = abap_true AND ms_sorting_state IS NOT INITIAL. IF is_col-column_id = ms_sorting_state-column_id AND ms_sorting_state-descending = abap_false. lv_direction = 'dsc'. ELSE. lv_direction = 'asc'. ENDIF. IF is_col-column_id = ms_sorting_state-column_id AND ms_sorting_state-descending = abap_true. lv_arrow = '▴'. " arrow up ELSE. lv_arrow = '▾'. " arrow down ENDIF. IF is_col-column_id = ms_sorting_state-column_id. lv_sort_active = | { mv_sort_active_class }|. ENDIF. rv_text = mi_html->a( iv_txt = is_col-column_title iv_act = |{ c_sort_by_event_prefix }{ is_col-column_id }:{ lv_direction }| ). rv_text = rv_text && |{ lv_arrow }|. ELSE. rv_text = is_col-column_title. ENDIF. ENDMETHOD. METHOD render_row. DATA ls_render TYPE /apmg/if_apm_html_table=>ty_cell_render. DATA lv_dummy TYPE string. DATA lt_attrs TYPE /apmg/if_apm_html=>ty_data_attrs. FIELD-SYMBOLS LIKE LINE OF mt_columns. FIELD-SYMBOLS LIKE LINE OF mt_columns. FIELD-SYMBOLS TYPE any. LOOP AT mt_columns ASSIGNING . IF -is_group = abap_true. ASSIGN TO . CONTINUE. ENDIF. IF -from_field IS NOT INITIAL AND -from_field <> '-'. ASSIGN COMPONENT -from_field OF STRUCTURE is_row TO . IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |html_table: cannot assign field [{ -from_field }]|. ENDIF. ELSEIF -from_field <> '-'. -from_field = to_upper( -column_id ). " Try column_id ASSIGN COMPONENT -from_field OF STRUCTURE is_row TO . IF sy-subrc <> 0. -from_field = '-'. " Don't try assignments anymore ASSIGN lv_dummy TO . ENDIF. ELSE. ASSIGN lv_dummy TO . ENDIF. ls_render = mi_renderer->render_cell( iv_table_id = mv_table_id iv_row_index = iv_row_index is_row = is_row iv_column_id = -column_id iv_value = |{ }| ). IF mv_with_cids = abap_true. CLEAR lt_attrs. APPEND cid_attr( -column_id ) TO lt_attrs. IF IS ASSIGNED AND -column_id IS NOT INITIAL. APPEND gid_attr( -column_id ) TO lt_attrs. ENDIF. ENDIF. mi_html->td( iv_content = ls_render-content ii_content = ls_render-html it_data_attrs = lt_attrs iv_class = ls_render-css_class ). ENDLOOP. ENDMETHOD. METHOD render_tbody. DATA ls_row_attrs TYPE /apmg/if_apm_html_table=>ty_row_attrs. DATA lv_row_attrs TYPE string. DATA lv_index TYPE i. FIELD-SYMBOLS TYPE any. mi_html->add( '' ). LOOP AT it_data ASSIGNING . lv_index = sy-tabix. ls_row_attrs = mi_renderer->get_row_attrs( iv_table_id = mv_table_id iv_row_index = lv_index is_row = ). CLEAR lv_row_attrs. IF ls_row_attrs-css_class IS NOT INITIAL. lv_row_attrs = lv_row_attrs && | class="{ ls_row_attrs-css_class }"|. ENDIF. IF ls_row_attrs-data IS NOT INITIAL. lv_row_attrs = lv_row_attrs && | data-{ ls_row_attrs-data-name }="{ ls_row_attrs-data-value }"|. ENDIF. mi_html->add( || ). render_row( iv_row_index = lv_index is_row = ). mi_html->add( '' ). ENDLOOP. mi_html->add( '' ). ENDMETHOD. METHOD render_thead. FIELD-SYMBOLS LIKE LINE OF mt_columns. FIELD-SYMBOLS LIKE LINE OF mt_columns. DATA lt_attrs TYPE /apmg/if_apm_html=>ty_data_attrs. DATA ls_grp_span TYPE string. DATA lv_grp_data TYPE string. mi_html->add( '' ). " Group headers IF mr_last_grp IS NOT INITIAL. " Has groups mi_html->add( '' ). LOOP AT mt_columns ASSIGNING WHERE is_group = abap_true. IF mv_with_cids = abap_true AND -column_id IS NOT INITIAL. lv_grp_data = | data-gid="{ -column_id }"|. ELSE. CLEAR lv_grp_data. ENDIF. IF -group_span > 1. ls_grp_span = | colspan="{ -group_span }"|. ELSE. CLEAR ls_grp_span. ENDIF. mi_html->add( |{ -column_title }| ). ENDLOOP. mi_html->add( '' ). ENDIF. " Regular headers mi_html->add( '' ). LOOP AT mt_columns ASSIGNING . IF -is_group = abap_true. ASSIGN TO . CONTINUE. ENDIF. IF mv_with_cids = abap_true. CLEAR lt_attrs. APPEND cid_attr( -column_id ) TO lt_attrs. IF IS ASSIGNED AND -column_id IS NOT INITIAL. APPEND gid_attr( -column_id ) TO lt_attrs. ENDIF. ENDIF. mi_html->th( iv_content = render_column_title( ) it_data_attrs = lt_attrs ). ENDLOOP. mi_html->add( '' ). mi_html->add( '' ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_html_toolbar IMPLEMENTATION. METHOD add. DATA ls_item TYPE ty_item. ASSERT iv_typ = /apmg/if_apm_html=>c_action_type-separator " sep doesn't have action OR iv_typ = /apmg/if_apm_html=>c_action_type-onclick " click may have no action (assigned in JS) OR iv_typ = /apmg/if_apm_html=>c_action_type-dummy " dummy may have no action OR iv_act IS INITIAL AND io_sub IS NOT INITIAL OR iv_act IS NOT INITIAL AND io_sub IS INITIAL. " Only one supplied ASSERT NOT ( iv_chk <> abap_undefined AND io_sub IS NOT INITIAL ). ASSERT iv_hotkey IS INITIAL OR strlen( iv_hotkey ) = 1. ls_item-txt = iv_txt. ls_item-act = iv_act. ls_item-ico = iv_ico. ls_item-sub = io_sub. ls_item-opt = iv_opt. ls_item-typ = iv_typ. ls_item-cur = iv_cur. ls_item-chk = iv_chk. ls_item-aux = iv_aux. ls_item-id = iv_id. ls_item-title = iv_title. ls_item-class = iv_class. ls_item-li_class = iv_li_class. ls_item-hotkey = to_lower( iv_hotkey ). APPEND ls_item TO mt_items. ro_self = me. ENDMETHOD. METHOD constructor. mv_id = iv_id. ENDMETHOD. METHOD count_items. rv_count = lines( mt_items ). ENDMETHOD. METHOD create. CREATE OBJECT ro_instance EXPORTING iv_id = iv_id. ENDMETHOD. METHOD get_hotkeys. DATA ls_hotkey_action LIKE LINE OF rt_hotkeys. FIELD-SYMBOLS LIKE LINE OF mt_items. ls_hotkey_action-ui_component = iv_component_name. LOOP AT mt_items ASSIGNING WHERE hotkey IS NOT INITIAL. ls_hotkey_action-description = -txt. ls_hotkey_action-action = -act. ls_hotkey_action-hotkey = -hotkey. INSERT ls_hotkey_action INTO TABLE rt_hotkeys. ENDLOOP. ENDMETHOD. METHOD render. DATA: lv_class TYPE string. ri_html = /apmg/cl_apm_html=>create( ). lv_class = 'nav-container'. IF iv_right = abap_true. lv_class = lv_class && ' float-right'. ENDIF. ri_html->add( |
| ). ri_html->add( render_items( iv_sort = iv_sort ) ). ri_html->add( '
' ). ENDMETHOD. METHOD render_as_droplist. DATA: lv_class TYPE string. ri_html = /apmg/cl_apm_html=>create( ). lv_class = 'nav-container'. IF iv_right = abap_true. lv_class = lv_class && ' float-right'. ENDIF. IF iv_corner = abap_true. lv_class = lv_class && ' corner'. ENDIF. ri_html->add( |
| ). ri_html->add( '
  • ' ). ri_html->add_a( iv_txt = iv_label iv_typ = /apmg/if_apm_html=>c_action_type-sapevent iv_act = iv_action ). ri_html->add( '
    ' ). ri_html->add( render_items( iv_sort = iv_sort ) ). ri_html->add( '
' ). ri_html->add( '
' ). ENDMETHOD. METHOD render_items. DATA: lv_class TYPE string, lv_class_value TYPE string, lv_icon TYPE string, lv_id TYPE string, lv_check TYPE string, lv_aux TYPE string, lv_txt TYPE string, lv_hkidx TYPE i, lv_has_icons TYPE abap_bool. FIELD-SYMBOLS LIKE LINE OF mt_items. ri_html = /apmg/cl_apm_html=>create( ). IF iv_sort = abap_true. SORT mt_items BY txt ASCENDING AS TEXT. ENDIF. " Check has icons or check boxes LOOP AT mt_items ASSIGNING WHERE ico IS NOT INITIAL OR chk <> abap_undefined. lv_has_icons = abap_true. lv_class = ' class="with-icons"'. EXIT. ENDLOOP. IF mv_id IS NOT INITIAL. lv_id = | id="{ mv_id }"|. ENDIF. ri_html->add( || ). " Render items LOOP AT mt_items ASSIGNING . CLEAR: lv_class, lv_class_value, lv_icon. lv_txt = -txt. IF -hotkey IS NOT INITIAL. ASSERT strlen( -hotkey ) = 1. lv_hkidx = find( val = lv_txt sub = to_upper( -hotkey ) ). IF lv_hkidx < 0. lv_hkidx = find( val = lv_txt sub = -hotkey ). ENDIF. IF lv_hkidx >= 0. lv_txt = replace( val = lv_txt off = lv_hkidx len = 1 with = |{ lv_txt+lv_hkidx(1) }| ). ENDIF. ENDIF. IF -typ = /apmg/if_apm_html=>c_action_type-separator. ri_html->add( |
  • { lv_txt }
  • | ). CONTINUE. ENDIF. IF lv_has_icons = abap_true. IF -chk = abap_true. lv_icon = ri_html->icon( 'check/blue' ). lv_check = ' data-check="X"'. ELSEIF -chk = abap_false. lv_icon = ri_html->icon( 'check/grey' ). lv_check = ' data-check=""'. ELSE. " abap_undefined -> not a check box lv_icon = ri_html->icon( -ico ). ENDIF. ENDIF. IF -cur = abap_true. IF -li_class IS INITIAL. lv_class_value = 'current-menu-item'. ELSE. lv_class_value = |current-menu-item { -li_class }|. ENDIF. ELSE. lv_class_value = -li_class. ENDIF. IF lv_class_value IS NOT INITIAL. lv_class = | class="{ lv_class_value }"|. ENDIF. IF -aux IS NOT INITIAL. lv_aux = | data-aux="{ -aux }"|. ENDIF. ri_html->add( || ). IF -sub IS INITIAL. ri_html->add_a( iv_txt = lv_icon && lv_txt iv_typ = -typ iv_act = -act iv_id = -id iv_opt = -opt iv_title = -title iv_class = -class ). ELSE. ri_html->add_a( iv_txt = lv_icon && lv_txt iv_typ = /apmg/if_apm_html=>c_action_type-dummy iv_act = '' iv_id = -id iv_opt = -opt iv_title = -title iv_class = -class ). ri_html->add( -sub->render_items( iv_sort ) ). ENDIF. ri_html->add( '' ). ENDLOOP. ri_html->add( '' ). ENDMETHOD. ENDCLASS. CLASS lcl_http_response DEFINITION FINAL. PUBLIC SECTION. INTERFACES /apmg/if_apm_http_response. CLASS-METHODS create IMPORTING http_client TYPE REF TO if_http_client RETURNING VALUE(result) TYPE REF TO /apmg/if_apm_http_response. PRIVATE SECTION. DATA http_client TYPE REF TO if_http_client. DATA http_response TYPE REF TO if_http_response. ENDCLASS. CLASS lcl_http_response IMPLEMENTATION. METHOD create. DATA(response) = NEW lcl_http_response( ). response->http_client = http_client. response->http_response = http_client->response. result ?= response. ENDMETHOD. METHOD /apmg/if_apm_http_response~close. http_client->close( ). ENDMETHOD. METHOD /apmg/if_apm_http_response~is_ok. DATA(status_code) = /apmg/if_apm_http_response~code( ). result = xsdbool( status_code >= 200 AND status_code < 300 ). ENDMETHOD. METHOD /apmg/if_apm_http_response~data. result = http_response->get_data( ). ENDMETHOD. METHOD /apmg/if_apm_http_response~cdata. result = http_response->get_cdata( ). ENDMETHOD. METHOD /apmg/if_apm_http_response~code. DATA msg TYPE string ##NEEDED. http_response->get_status( IMPORTING reason = msg " for debug code = result ). ENDMETHOD. METHOD /apmg/if_apm_http_response~json. TRY. result = /apmg/cl_apm_ajson=>parse( /apmg/if_apm_http_response~cdata( ) ). CATCH /apmg/cx_apm_ajson_error INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD /apmg/if_apm_http_response~error. result = http_response->get_cdata( ). ENDMETHOD. METHOD /apmg/if_apm_http_response~headers. DATA headers TYPE tihttpnvp. " HTTP headers are not case sensitive and can have multiple entries (i.e. for cookies) result = NEW #( iv_case_insensitive = abap_true iv_list_mode = abap_true ). http_response->get_header_fields( CHANGING fields = headers ). LOOP AT headers ASSIGNING FIELD-SYMBOL(
    ). result->set( iv_key =
    -name iv_val =
    -value ). ENDLOOP. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_http_agent IMPLEMENTATION. METHOD /apmg/if_apm_http_agent~global_headers. result = global_headers. ENDMETHOD. METHOD /apmg/if_apm_http_agent~request. DATA: http_client TYPE REF TO if_http_client, status_code TYPE i, message TYPE string. cl_http_client=>create_by_url( EXPORTING url = url ssl_id = ssl_id proxy_host = proxy_host proxy_service = proxy_service proxy_user = proxy_user proxy_passwd = proxy_passwd IMPORTING client = http_client EXCEPTIONS argument_not_found = 1 plugin_not_active = 2 internal_error = 3 pse_not_found = 4 pse_not_distrib = 5 pse_errors = 6 OTHERS = 7 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. http_client->request->set_version( if_http_request=>co_protocol_version_1_1 ). http_client->request->set_method( method ). IF query IS BOUND. LOOP AT query->mt_entries ASSIGNING FIELD-SYMBOL(). http_client->request->set_form_field( name = -k value = -v ). ENDLOOP. ENDIF. LOOP AT global_headers->mt_entries ASSIGNING . http_client->request->set_header_field( name = -k value = -v ). ENDLOOP. IF headers IS BOUND. LOOP AT headers->mt_entries ASSIGNING . http_client->request->set_header_field( name = -k value = -v ). ENDLOOP. ENDIF. IF method = /apmg/if_apm_http_agent=>c_method-post OR method = /apmg/if_apm_http_agent=>c_method-put OR method = /apmg/if_apm_http_agent=>c_method-patch. attach_payload( request = http_client->request payload = payload ). ENDIF. http_client->send( EXCEPTIONS http_communication_failure = 1 http_invalid_state = 2 http_processing_failed = 3 http_invalid_timeout = 4 OTHERS = 5 ). IF sy-subrc = 0. http_client->receive( EXCEPTIONS http_communication_failure = 1 http_invalid_state = 2 http_processing_failed = 3 OTHERS = 4 ). ENDIF. IF sy-subrc <> 0. http_client->get_last_error( IMPORTING code = status_code message = message ). RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |HTTP error: [{ status_code }] { message }|. ENDIF. result = lcl_http_response=>create( http_client ). ENDMETHOD. METHOD attach_payload. DATA(payload_type) = cl_abap_typedescr=>describe_by_data( payload ). CASE payload_type->type_kind. WHEN cl_abap_typedescr=>typekind_xstring. request->set_data( payload ). WHEN cl_abap_typedescr=>typekind_string. request->set_cdata( payload ). WHEN cl_abap_typedescr=>typekind_char. request->set_cdata( |{ payload }| ). WHEN OTHERS. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Unexpected payload type { payload_type->absolute_name }|. ENDCASE. ENDMETHOD. METHOD constructor. me->proxy_host = proxy_host. me->proxy_service = proxy_service. me->proxy_user = proxy_user. me->proxy_passwd = proxy_passwd. global_headers = NEW #( ). ENDMETHOD. METHOD create. result = NEW /apmg/cl_apm_http_agent( proxy_host = proxy_host proxy_service = proxy_service proxy_user = proxy_user proxy_passwd = proxy_passwd ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_http_login_manage IMPLEMENTATION. METHOD append. DATA(hostname) = get_host( host ). IF NOT line_exists( auths[ host = hostname ] ). APPEND INITIAL LINE TO auths ASSIGNING FIELD-SYMBOL(). -host = hostname. -auth = auth. ENDIF. ENDMETHOD. METHOD clear. CLEAR auths. ENDMETHOD. METHOD get. READ TABLE auths INTO DATA(auth) WITH KEY host = get_host( host ). IF sy-subrc = 0. result = auth-auth. ENDIF. ENDMETHOD. METHOD get_host. " If it's a URL, use host:port, otherwise just take the input TRY. DATA(url) = /apmg/cl_apm_url=>parse( host )->components. result = url-host. IF url-port IS NOT INITIAL. result = |{ result }:{ url-port }|. ENDIF. CATCH /apmg/cx_apm_error. result = host. ENDTRY. ENDMETHOD. METHOD save. IF auth IS NOT INITIAL. append( host = host auth = auth ). ENDIF. ENDMETHOD. METHOD set. ASSERT host IS NOT INITIAL. IF username IS INITIAL OR password IS INITIAL. RETURN. ENDIF. IF is_basic = abap_true. result = |Basic { cl_http_utility=>encode_base64( |{ username }:{ password }| ) }|. ELSE. result = |Bearer { password }|. ENDIF. append( host = host auth = result ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_importer IMPLEMENTATION. METHOD create_packages. TRY. LOOP AT packages ASSIGNING FIELD-SYMBOL(). DATA(source) = zcl_abapgit_factory=>get_sap_package( -source_package ). DATA(target) = zcl_abapgit_factory=>get_sap_package( -target_package ). IF NOT target->exists( ). DATA(package) = source->get( ). package-parentcl = -parent_package. package-devclass = -target_package. package-as4user = sy-uname. IF -target_package(1) = '$'. package-dlvunit = 'LOCAL'. ENDIF. IF is_dry_run = abap_false. target->create( package ). ENDIF. ENDIF. ENDLOOP. CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD get_map. IF is_log = abap_true. FORMAT COLOR COL_HEADING. WRITE: / 'Object Mapping:', AT c_width space. SKIP. FORMAT COLOR OFF. ENDIF. LOOP AT packages ASSIGNING FIELD-SYMBOL(). DATA(map) = /apmg/cl_apm_code_mapper=>get( source_package = -source_package target_package = -target_package rules = rules object_types = object_types object_names = object_names is_logging = is_log is_production = is_production ). INSERT LINES OF map INTO TABLE result. ENDLOOP. ENDMETHOD. METHOD get_packages. " XXX: Switch for self-update DATA(list) = /apmg/cl_apm_package_json=>list( "DATA(list) = /apmg/cl_package_json=>list( instanciate = abap_true is_bundle = abap_false ). IF is_log = abap_true. FORMAT COLOR COL_HEADING. WRITE: / 'Packages:', AT c_width space. SKIP. FORMAT COLOR OFF. ENDIF. LOOP AT rules ASSIGNING FIELD-SYMBOL(). IF dependencies IS NOT INITIAL. READ TABLE dependencies ASSIGNING FIELD-SYMBOL() WITH KEY name = -name. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Package { -name } used in IMPORT statement but not a dependency|. ENDIF. ENDIF. " TODO!: Instead of using the globally installed package as a source, " this needs to be retrieved from the registry DATA(found) = abap_false. LOOP AT list ASSIGNING FIELD-SYMBOL() USING KEY name WHERE name = -name. IF zcl_abapgit_factory=>get_sap_package( -package )->exists( ) = abap_true. found = abap_true. EXIT. ENDIF. ENDLOOP. IF sy-subrc = 0 AND found = abap_true. DATA(package) = VALUE /apmg/if_apm_importer=>ty_package( name = -name version = -version source_package = -package target_package = -target_package parent_package = -parent_package ). CASE -version. WHEN 'latest'. IF dependencies IS NOT INITIAL. package-version = -version. ELSE. package-version = -version. ENDIF. WHEN -version. " TODO: better error RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Version mismatch'. ENDCASE. INSERT package INTO TABLE result. IF is_log = abap_true. FORMAT COLOR COL_POSITIVE. WRITE: / package-name, AT 30 package-version, AT 40 package-source_package, AT 72 package-target_package, AT c_width space. ENDIF. ELSE. IF is_log = abap_true. FORMAT COLOR COL_NEGATIVE. WRITE: / -name, 'not found in global namespace', AT c_width space. ENDIF. ENDIF. ENDLOOP. IF is_log = abap_true. FORMAT COLOR OFF. SKIP. ENDIF. SORT result. DELETE ADJACENT DUPLICATES FROM result. ENDMETHOD. METHOD get_programs. DATA programs TYPE /apmg/if_apm_importer=>ty_programs. DATA(packages) = zcl_abapgit_factory=>get_sap_package( package )->list_subpackages( ). IF package <> '/APMG/APM'. INSERT package INTO TABLE packages. ENDIF. IF is_log = abap_true. FORMAT COLOR COL_HEADING. WRITE: / 'Searching for IMPORTs:', AT c_width space. FORMAT COLOR OFF. SKIP. ENDIF. LOOP AT packages INTO DATA(sub_package). " Find INCLUDEs containing IMPORT statements " FUTURE: includes are not available in BTP so this could be the source of an interface CLEAR programs. SELECT a~obj_name AS program, a~devclass AS package INTO CORRESPONDING FIELDS OF TABLE @programs FROM tadir AS a JOIN trdir AS b ON a~obj_name = b~name WHERE a~pgmid = 'R3TR' AND a~object = 'PROG' AND a~devclass = @sub_package AND b~subc = 'I' ORDER BY program ##SUBRC_OK. "#EC CI_BUFFJOIN IF is_log = abap_true AND programs IS NOT INITIAL. WRITE: / 'PACKAGE', sub_package, AT c_width space. ENDIF. LOOP AT programs ASSIGNING FIELD-SYMBOL(). "#EC CI_NOORDER IF is_log = abap_true. WRITE: AT /5 'INCLUDE', -program, AT c_width space. ENDIF. DATA(source_code) = /apmg/cl_apm_code_importer=>read( -program ). DATA(found) = abap_false. LOOP AT source_code ASSIGNING FIELD-SYMBOL(). " TODO?: Check for multi-line statements FIND REGEX 'IMPORT\s+.*\s+TO\s+.*\s+FROM\s+''.*''\s*\.' IN IGNORING CASE ##REGEX_POSIX. IF sy-subrc = 0. IF is_log = abap_true. FORMAT COLOR COL_POSITIVE. WRITE: AT /10 , AT c_width space. FORMAT COLOR OFF. ENDIF. found = abap_true. ENDIF. ENDLOOP. IF found = abap_true. INSERT INTO TABLE result. ELSEIF is_log = abap_true. FORMAT COLOR COL_NORMAL. WRITE: AT /10 'No IMPORT statements found', AT c_width space. FORMAT COLOR OFF. ENDIF. ENDLOOP. " programs ENDLOOP. " packages IF is_log = abap_true. FORMAT COLOR COL_TOTAL. IF result IS INITIAL. WRITE: / 'No includes with IMPORT statements found', AT c_width space. ENDIF. FORMAT COLOR OFF. SKIP. ENDIF. SORT result. ENDMETHOD. METHOD get_rules. result = /apmg/cl_apm_code_import_rules=>get( programs = programs is_logging = is_log default_rule = default_rule ). ENDMETHOD. METHOD import_objects. DATA handler TYPE REF TO /apmg/if_apm_object. DATA(log) = NEW zcl_abapgit_log( ). DATA(progress) = /apmg/cl_apm_progress_bar=>get_instance( lines( map ) ). IF is_log = abap_true. FORMAT COLOR COL_HEADING. WRITE: / 'Importing:', AT c_width space. FORMAT COLOR OFF. SKIP. ENDIF. TRY. zcl_abapgit_objects_activation=>clear( ). IF transport IS NOT INITIAL. zcl_abapgit_factory=>get_default_transport( )->set( transport ). ENDIF. CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. LOOP AT map INTO DATA(mapping). progress->show( current = sy-tabix text = |Importing { mapping-new_object }| ). IF is_log = abap_true. WRITE: / 'IMPORT', mapping-object_type, mapping-old_object, 'TO', mapping-new_object, 'IN PACKAGE', mapping-target_package. ENDIF. " TODO: i18n DATA(old_item) = VALUE /apmg/if_apm_object=>ty_item( obj_type = mapping-object_type obj_name = mapping-old_object package = mapping-source_package language = sy-langu ). DATA(new_item) = VALUE /apmg/if_apm_object=>ty_item( obj_type = mapping-object_type obj_name = map[ old_object = mapping-old_object ]-new_object package = mapping-target_package language = sy-langu ). ASSERT new_item-obj_name IS NOT INITIAL AND new_item-obj_name <> old_item-obj_name. " XXX: Switch for apm self-update " Some modules are used by the importer and don't support self-update " If these items are changed, it would dump, so we skip them (requires manual update) IF new_item-package CP '/APMG/APM*' AND ( old_item-obj_name CP '/APMG/CX_ERROR*' OR old_item-obj_name CP 'Z++_AJSON*' OR old_item-obj_name CP '/APMG/++_PACKAGE_JSON*' ). IF is_log = abap_true. WRITE 'Skipped' COLOR COL_TOTAL INTENSIFIED OFF. ENDIF. CONTINUE. ENDIF. TRY. DATA(handler_class) = |/APMG/CL_APM_OBJECT_{ old_item-obj_type }|. " TODO: pass FILES CREATE OBJECT handler TYPE (handler_class) EXPORTING item = old_item. CATCH cx_sy_create_object_error. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Unsupported object type { old_item-obj_type }|. ENDTRY. TRY. IF is_dry_run = abap_false. zcl_abapgit_factory=>get_cts_api( )->insert_transport_object( iv_transport = transport iv_object = new_item-obj_type iv_obj_name = new_item-obj_name iv_package = new_item-package iv_language = new_item-language ). zcl_abapgit_factory=>get_tadir( )->insert_single( iv_object = new_item-obj_type iv_obj_name = new_item-obj_name iv_package = new_item-package iv_language = new_item-language ). ENDIF. CATCH zcx_abapgit_exception INTO error. RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. TRY. handler->import( new_object = new_item-obj_name new_package = new_item-package language = new_item-language map = map is_dry_run = is_dry_run is_production = is_production ). IF is_log = abap_true. IF is_dry_run = abap_true. WRITE 'Dry run' COLOR COL_TOTAL. ELSE. WRITE 'Success' COLOR COL_POSITIVE. ENDIF. ENDIF. CATCH cx_root INTO DATA(any_error). IF is_log = abap_true. DATA(msg) = any_error->get_text( ). WRITE msg COLOR COL_NEGATIVE. ENDIF. ENDTRY. ENDLOOP. TRY. " TODO: support DDIC (eventually) zcl_abapgit_objects_activation=>activate( log ). IF transport IS NOT INITIAL. zcl_abapgit_factory=>get_default_transport( )->reset( ). ENDIF. CATCH zcx_abapgit_exception INTO error. RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. progress->off( ). IF is_log = abap_true. FORMAT COLOR OFF. SKIP. ENDIF. ENDMETHOD. METHOD run. is_log = is_logging. " 1. Get all programs that contain IMPORT statements DATA(programs) = get_programs( package ). " 2. Get the import rules from the programs DATA(rules) = get_rules( programs = programs default_rule = default_rule ). " 3. Get name/version from the rules DATA(packages) = get_packages( rules = rules dependencies = dependencies ). " TODO: 4. Download the tarballs for all packages " 5. Get the object mapping DATA(map) = get_map( rules = rules packages = packages object_types = object_types object_names = object_names ). " 6. Create packages (if necessary) create_packages( packages = packages is_dry_run = is_dry_run ). " 7. Import the tarballs using the mapping import_objects( map = map transport = transport is_dry_run = is_dry_run is_production = is_production ). " 8. Save packages to apm save_packages( packages = packages dependencies = dependencies ). ENDMETHOD. METHOD save_packages. " FUTURE: Avoid saving bundled deps individually but store it with the parent package " Add or update dependencies " TODO: This should be using the complete manifest of the dependencies (and not just name/version) LOOP AT packages ASSIGNING FIELD-SYMBOL(). " XXX: Switch for apm self-update DATA(package_json_service) = /apmg/cl_apm_package_json=>factory( -target_package ). " DATA(package_json_service) = /apmg/cl_package_json=>factory( -target_package ) IF package_json_service->exists( ). DATA(package_json) = package_json_service->load( )->get( ). IF package_json-name <> -name. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Package inconsistency: Expected { -name }, found { package_json-name }|. ENDIF. package_json-version = -version. ELSE. "XXX: Switch for apm self-update package_json = VALUE /apmg/if_apm_types=>ty_package_json( " package_json = VALUE /apmg/if_types=>ty_package_json( name = -name version = -version ). ENDIF. package_json_service->set( package_json )->save( ). ENDLOOP. " Remove dependencies LOOP AT dependencies ASSIGNING FIELD-SYMBOL() WHERE action = /apmg/if_apm_importer=>c_action-remove. " XXX: Switch for apm self-update package_json_service = /apmg/cl_apm_package_json=>factory( -package ). " package_json_service = /apmg/cl_package_json=>factory( -package ) IF package_json_service->exists( ). package_json_service->delete( ). ENDIF. ENDLOOP. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_installer IMPLEMENTATION. METHOD install. TRY. _log_start( title = 'Install' name = name version = version ). _system_check( ). _files( enum_source = enum_source name = name data = data ). _packaging( ). _folder_logic( enum_folder_logic ). _transport_check( package = package transport = transport ). _confirm_messages( ). _namespaces( package = package transport = transport main_language = main_language ). _deserialize_objects( package = package transport = transport main_language = main_language ). CATCH cx_root INTO DATA(error). _transport_reset( ). log->add_exception( error ). ENDTRY. TRY. _log_end( ). _restore_messages( ). CATCH cx_root INTO error. log->add_exception( error ). ENDTRY. ENDMETHOD. METHOD uninstall. TRY. _log_start( title = 'Uninstall' name = name version = version ). _system_check( ). " TODO: needs to work for apm " _transport( c_enum_transport-prompt ) _confirm_messages( ). " A few tries to tackle dependencies DO 3 TIMES. DATA(tadir) = zcl_abapgit_factory=>get_tadir( )->read( package ). DELETE tadir WHERE object = 'NSPC'. IF tadir IS NOT INITIAL. _uninstall_sotr( tadir = tadir transport = transport ). _uninstall_sots( tadir = tadir transport = transport ). " Bridge to abapGit /apmg/cl_apm_abapgit_objects=>delete( it_tadir = tadir iv_transport = transport ii_log = log ). ENDIF. ENDDO. CATCH cx_root INTO DATA(error). _transport_reset( ). log->add_exception( error ). ENDTRY. TRY. _log_end( ). _check_uninstalled( package = package tadir = tadir ). _restore_messages( ). CATCH cx_root INTO error. log->add_exception( error ). ENDTRY. ENDMETHOD. METHOD _check_uninstalled. DATA tadir_list LIKE tadir. CHECK tadir IS NOT INITIAL. SELECT pgmid, object, obj_name FROM tadir INTO TABLE @tadir_list FOR ALL ENTRIES IN @tadir WHERE pgmid = @tadir-pgmid AND object = @tadir-object AND obj_name = @tadir-obj_name ##TOO_MANY_ITAB_FIELDS. IF sy-subrc = 0. LOOP AT tadir_list TRANSPORTING NO FIELDS WHERE object <> 'DEVC' AND object <> 'NSPC'. EXIT. ENDLOOP. IF sy-subrc = 0. DATA(msg) = |Some objects could not be uninstalled. Uninstall the remaining objects | && |of pacakge { package } manually|. ELSE. msg = |Release the transport and deleted the remaining pacakge { package } manually|. ENDIF. MESSAGE msg TYPE 'I'. ENDIF. ENDMETHOD. METHOD _confirm_messages. " Temporarily suppress certain messages that are not relevant for installation CONSTANTS c_toolflag_set TYPE funcname VALUE 'SCWG_TOOLFLAG_SET'. " Set tool flag to avoid messages TRY. CALL FUNCTION c_toolflag_set. CATCH cx_root ##NO_HANDLER. ENDTRY. " Confirm message about modification mode (DT, CLM_INFORMATION) " and backup old state (see _restore_messages) SELECT * FROM clmcus INTO TABLE @clmcus_backup WHERE username = @sy-uname ##SUBRC_OK. "#EC CI_ALL_FIELDS_NEEDED DATA(customizing) = VALUE clmcus( username = sy-uname obj_type = 'CLAS' ). INSERT clmcus FROM @customizing ##SUBRC_OK. customizing-obj_type = 'INTF'. INSERT clmcus FROM @customizing ##SUBRC_OK. customizing-obj_type = 'METH'. INSERT clmcus FROM @customizing ##SUBRC_OK. ENDMETHOD. METHOD _deserialize_objects. " Bridge to abapGit TRY. /apmg/cl_apm_abapgit_objects=>deserialize( iv_package = package iv_language = main_language iv_transport = transport it_remote = remote_files io_dot = dot_abapgit ii_log = log ). CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD _files. DATA(progress) = /apmg/cl_apm_progress_bar=>get_instance( 100 ). progress->show( text = 'Uploading package' current = 5 ). " Load ZIP File CASE enum_source. WHEN c_enum_source-internet. DATA(package_data) = /apmg/cl_apm_installer_files=>load_internet( name ). WHEN c_enum_source-local. package_data = /apmg/cl_apm_installer_files=>load_local( name ). WHEN c_enum_source-server. package_data = /apmg/cl_apm_installer_files=>load_server( name ). WHEN c_enum_source-data. package_data = data. WHEN c_enum_source-registry. package_data = data. WHEN OTHERS. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Unknown source for package'. ENDCASE. progress->show( text = 'Unpacking files from package' current = 10 ). CASE enum_source. WHEN c_enum_source-internet OR c_enum_source-local OR c_enum_source-server OR c_enum_source-data. remote_files = /apmg/cl_apm_installer_files=>unzip( package_data ). WHEN c_enum_source-registry. remote_files = /apmg/cl_apm_installer_files=>untar( package_data ). WHEN OTHERS. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Unknown source for package'. ENDCASE. " Scan for viruses and unzip progress->show( text = 'Scanning files for viruses' current = 20 ). /apmg/cl_apm_installer_files=>virus_scan( package_data ). ENDMETHOD. METHOD _find_remote_dot_abapgit. READ TABLE remote ASSIGNING FIELD-SYMBOL() WITH TABLE KEY file_path COMPONENTS path = zif_abapgit_definitions=>c_root_dir filename = zif_abapgit_definitions=>c_dot_abapgit. IF sy-subrc = 0. TRY. result = zcl_abapgit_dot_abapgit=>deserialize( -data ). CATCH zcx_abapgit_exception. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error decoding .abapgit.xml'. ENDTRY. ELSE. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error finding .abapgit.xml - Is this an apm package?'. ENDIF. ENDMETHOD. METHOD _find_remote_namespaces. LOOP AT remote_files ASSIGNING FIELD-SYMBOL() WHERE filename CP '*.nspc.xml'. INSERT INTO TABLE result. ENDLOOP. ENDMETHOD. METHOD _folder_logic. CASE enum_folder_logic. WHEN c_enum_folder_logic-default. folder_logic = dot_abapgit->get_folder_logic( ). WHEN c_enum_folder_logic-prefix. folder_logic = zif_abapgit_dot_abapgit=>c_folder_logic-prefix. WHEN c_enum_folder_logic-mixed. folder_logic = zif_abapgit_dot_abapgit=>c_folder_logic-mixed. WHEN c_enum_folder_logic-full. folder_logic = zif_abapgit_dot_abapgit=>c_folder_logic-full. WHEN OTHERS. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Unknown folder logic'. ENDCASE. ENDMETHOD. METHOD _log_end. result = log->get_status( ). ENDMETHOD. METHOD _log_start. log = NEW zcl_abapgit_log( ). log->set_title( |{ title } Log for { name }@{ version }| ). ENDMETHOD. METHOD _namespaces. " Namespaces must be created upfront, " otherwise folder_logic->path_to_package will fail DATA(remote_files) = _find_remote_namespaces( ). IF lines( remote_files ) > 0. TRY. /apmg/cl_apm_abapgit_objects=>deserialize( iv_package = package iv_language = main_language iv_transport = transport it_remote = remote_files io_dot = dot_abapgit ii_log = log ). COMMIT WORK. CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDIF. ENDMETHOD. METHOD _packaging. dot_abapgit = _find_remote_dot_abapgit( remote_files ). " Check language main_language = dot_abapgit->get_main_language( ). IF main_language <> sy-langu. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Unable to install. Logon in main language of package which is { main_language }|. ENDIF. ENDMETHOD. METHOD _restore_messages. CONSTANTS c_toolflag_reset TYPE funcname VALUE 'SCWG_TOOLFLAG_RESET'. " Reset tool flag TRY. CALL FUNCTION c_toolflag_reset. CATCH cx_root ##NO_HANDLER. ENDTRY. DELETE FROM clmcus WHERE username = @sy-uname ##SUBRC_OK. INSERT clmcus FROM TABLE @clmcus_backup ##SUBRC_OK. ENDMETHOD. METHOD _system_check. DATA: systemedit TYPE tadir-edtflag, sys_cliinddep_edit TYPE t000-ccnocliind. CALL FUNCTION 'TR_SYS_PARAMS' IMPORTING systemedit = systemedit sys_cliinddep_edit = sys_cliinddep_edit EXCEPTIONS no_systemname = 1 no_systemtype = 2 OTHERS = 3. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. IF systemedit = 'N'. MESSAGE e102(tk) INTO /apmg/cx_apm_error=>null. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. IF sys_cliinddep_edit CA '23'. MESSAGE e729(tk) INTO /apmg/cx_apm_error=>null. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. ENDMETHOD. METHOD _transport_check. DATA: request_header TYPE trwbo_request_header, request_headers TYPE trwbo_request_headers. DATA(text) = CONV as4text( |apm: Package { package }| ). CALL FUNCTION 'TR_READ_REQUEST_WITH_TASKS' EXPORTING iv_trkorr = transport IMPORTING et_request_headers = request_headers EXCEPTIONS invalid_input = 1 OTHERS = 2. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. " Request Type: Workbench READ TABLE request_headers INTO request_header WITH KEY trfunction = 'K' trstatus = 'D' korrdev = 'SYST'. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Transport { transport } is not a changeable workbench request|. ENDIF. " Task Type: Unclassified (ok) IF line_exists( request_headers[ trfunction = 'X' trstatus = 'D' korrdev = 'SYST' ] ). RETURN. ENDIF. " Task Type: Development IF NOT line_exists( request_headers[ trfunction = 'S' trstatus = 'D' korrdev = 'SYST' ] ). CALL FUNCTION 'TRINT_INSERT_NEW_COMM' EXPORTING wi_kurztext = text wi_trfunction = 'S' wi_strkorr = request_header-trkorr EXCEPTIONS OTHERS = 1. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. ENDIF. " Task Type: Repair (for namespaced projects) IF package(1) = '/'. IF NOT line_exists( request_headers[ trfunction = 'R' trstatus = 'D' korrdev = 'SYST' ] ). CALL FUNCTION 'TRINT_INSERT_NEW_COMM' EXPORTING wi_kurztext = text wi_trfunction = 'R' wi_strkorr = request_header-trkorr EXCEPTIONS OTHERS = 1. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. ENDIF. ENDIF. ENDMETHOD. METHOD _transport_reset. TRY. zcl_abapgit_factory=>get_default_transport( )->reset( ). CATCH zcx_abapgit_exception ##NO_HANDLER. ENDTRY. ENDMETHOD. METHOD _uninstall_sotr. " Necessary since older releases do not delete SOTR when package is deleted DATA(use_korr) = xsdbool( transport IS NOT INITIAL ). LOOP AT tadir ASSIGNING FIELD-SYMBOL() WHERE object = 'DEVC'. SELECT concept FROM sotr_head INTO TABLE @DATA(sotr_head) WHERE paket = @-obj_name. "#EC CI_SGLSELECT IF sy-subrc = 0. LOOP AT sotr_head ASSIGNING FIELD-SYMBOL(). DELETE FROM sotr_use WHERE concept = @-concept ##SUBRC_OK. CALL FUNCTION 'BTFR_DELETE_SINGLE_TEXT' EXPORTING concept = -concept corr_num = transport use_korrnum_immediatedly = use_korr flag_string = abap_false EXCEPTIONS text_not_found = 1 invalid_package = 2 text_not_changeable = 3 text_enqueued = 4 no_correction = 5 parameter_error = 6 OTHERS = 7. IF sy-subrc <> 0. CONTINUE. ENDIF. ENDLOOP. ENDIF. TRY. zcl_abapgit_factory=>get_tadir( )->delete_single( iv_object = 'SOTR' iv_obj_name = -obj_name ). CATCH zcx_abapgit_exception ##NO_HANDLER. ENDTRY. ENDLOOP. ENDMETHOD. METHOD _uninstall_sots. " Necessary since older releases do not delete SOTS when package is deleted DATA(use_korr) = xsdbool( transport IS NOT INITIAL ). LOOP AT tadir ASSIGNING FIELD-SYMBOL() WHERE object = 'DEVC'. SELECT concept FROM sotr_headu INTO TABLE @DATA(sotr_head) WHERE paket = @-obj_name. "#EC CI_SGLSELECT IF sy-subrc = 0. LOOP AT sotr_head ASSIGNING FIELD-SYMBOL(). DELETE FROM sotr_useu WHERE concept = @-concept ##SUBRC_OK. CALL FUNCTION 'BTFR_DELETE_SINGLE_TEXT' EXPORTING concept = -concept corr_num = transport use_korrnum_immediatedly = use_korr flag_string = abap_true EXCEPTIONS text_not_found = 1 invalid_package = 2 text_not_changeable = 3 text_enqueued = 4 no_correction = 5 parameter_error = 6 OTHERS = 7. IF sy-subrc <> 0. CONTINUE. ENDIF. ENDLOOP. ENDIF. TRY. zcl_abapgit_factory=>get_tadir( )->delete_single( iv_object = 'SOTS' iv_obj_name = -obj_name ). CATCH zcx_abapgit_exception ##NO_HANDLER. ENDTRY. ENDLOOP. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_installer_files IMPLEMENTATION. METHOD load_internet. DATA: client TYPE REF TO if_http_client, code TYPE i, message TYPE string, reason TYPE string. DATA(url_components) = /apmg/cl_apm_url=>parse( url )->components. IF proxy_host IS NOT INITIAL AND proxy_port IS NOT INITIAL. cl_http_client=>create_by_url( EXPORTING url = url_components-host ssl_id = 'ANONYM' proxy_host = proxy_host proxy_service = proxy_port IMPORTING client = client EXCEPTIONS argument_not_found = 1 plugin_not_active = 2 internal_error = 3 OTHERS = 4 ). ELSE. cl_http_client=>create_by_url( EXPORTING url = url_components-host ssl_id = 'ANONYM' IMPORTING client = client EXCEPTIONS argument_not_found = 1 plugin_not_active = 2 internal_error = 3 OTHERS = 4 ). ENDIF. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error creating HTTP client (check certificates in STRUST)'. ENDIF. IF proxy_user IS NOT INITIAL AND proxy_password IS NOT INITIAL. client->authenticate( proxy_authentication = abap_true username = proxy_user password = proxy_password ). ENDIF. IF user IS NOT INITIAL AND password IS NOT INITIAL. client->authenticate( username = user password = password ). ENDIF. cl_http_utility=>set_request_uri( request = client->request uri = url ). client->request->set_method( 'GET' ). client->request->set_compression( ). client->request->set_header_field( name = 'content-type' value = 'application/zip' ). client->send( EXPORTING timeout = '6000' EXCEPTIONS http_communication_failure = 1 http_invalid_state = 2 http_processing_failed = 3 http_invalid_timeout = 4 OTHERS = 5 ). IF sy-subrc = 0. client->receive( EXCEPTIONS http_communication_failure = 1 http_invalid_state = 2 http_processing_failed = 3 OTHERS = 4 ). ENDIF. IF sy-subrc <> 0. client->get_last_error( IMPORTING code = code message = message ). RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |{ code } { message }|. ENDIF. client->response->get_status( IMPORTING code = code reason = reason ). IF code <> 200. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |{ code } { reason }|. ENDIF. result = client->response->get_data( ). IF result IS INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error downloading file. No data returned.'. ENDIF. client->close( ). ENDMETHOD. METHOD load_local. DATA: file_size TYPE i, data_table TYPE STANDARD TABLE OF ty_hex WITH EMPTY KEY. cl_gui_frontend_services=>gui_upload( EXPORTING filename = |{ filename }| filetype = 'BIN' IMPORTING filelength = file_size CHANGING data_tab = data_table EXCEPTIONS file_open_error = 1 file_read_error = 2 no_batch = 3 gui_refuse_filetransfer = 4 invalid_type = 5 no_authority = 6 unknown_error = 7 bad_data_format = 8 header_not_allowed = 9 separator_not_allowed = 10 header_too_long = 11 unknown_dp_error = 12 access_denied = 13 dp_out_of_memory = 14 disk_full = 15 dp_timeout = 16 not_supported_by_gui = 17 error_no_gui = 18 OTHERS = 19 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. CONCATENATE LINES OF data_table INTO result IN BYTE MODE. result = result(file_size). ENDMETHOD. METHOD load_server. DATA: eps_inbox TYPE eps2path, file_name TYPE file_name, file_size TYPE i, data_table TYPE STANDARD TABLE OF ty_hex WITH EMPTY KEY. CALL FUNCTION 'EPS_GET_DIRECTORY_PATH' EXPORTING eps_subdir = 'in' IMPORTING ev_long_dir_name = eps_inbox EXCEPTIONS invalid_eps_subdir = 1 sapgparam_failed = 2 build_directory_failed = 3 OTHERS = 4. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error getting EPS directory from server'. ENDIF. IF eps_inbox CA '\'. file_name = eps_inbox && '\' && filename. ELSE. file_name = eps_inbox && '/' && filename. ENDIF. CALL FUNCTION 'SCMS_UPLOAD' EXPORTING filename = file_name binary = abap_true frontend = abap_false IMPORTING filesize = file_size TABLES data = data_table EXCEPTIONS error = 1 OTHERS = 2. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error loading file from server: { file_name }|. ENDIF. CONCATENATE LINES OF data_table INTO result IN BYTE MODE. result = result(file_size). ENDMETHOD. METHOD untar. DATA(tar) = /apmg/cl_apm_tar=>new( )->load( /apmg/cl_apm_tar=>new( )->gunzip( xstr ) ). DATA(files) = tar->list( ). LOOP AT files ASSIGNING FIELD-SYMBOL() WHERE typeflag = '0'. DATA(file) = VALUE zif_abapgit_git_definitions=>ty_file( ). IF -name CA '/'. FIND REGEX '(.*[\\/])?([^\\/]+)' IN -name SUBMATCHES file-path file-filename ##REGEX_POSIX. ASSERT sy-subrc = 0. ELSE. file-filename = -name. ENDIF. file-path = '/' && file-path. file-path = replace( val = file-path sub = '/package/' with = '/' ). " packaged with npm file-data = tar->get( -name ). TRY. file-sha1 = zcl_abapgit_hash=>sha1_raw( file-data ). CATCH zcx_abapgit_exception ##NO_HANDLER. ENDTRY. INSERT file INTO TABLE result. ENDLOOP. ENDMETHOD. METHOD unzip. DATA file_data TYPE xstring. DATA(zip) = NEW cl_abap_zip( ). zip->load( EXPORTING zip = xstr EXCEPTIONS zip_parse_error = 1 OTHERS = 2 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error loading ZIP'. ENDIF. LOOP AT zip->files ASSIGNING FIELD-SYMBOL(). zip->get( EXPORTING name = -name IMPORTING content = file_data EXCEPTIONS zip_index_error = 1 zip_decompression_error = 2 OTHERS = 3 ). IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error getting file from ZIP'. ENDIF. APPEND INITIAL LINE TO result ASSIGNING FIELD-SYMBOL(). _filename( EXPORTING str = -name IMPORTING path = -path filename = -filename ). -data = file_data. TRY. -sha1 = zcl_abapgit_hash=>sha1_blob( -data ). CATCH zcx_abapgit_exception. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error during hashing'. ENDTRY. ENDLOOP. DELETE result WHERE filename IS INITIAL. _normalize_path( CHANGING files = result ). ENDMETHOD. METHOD virus_scan. DATA: scanner TYPE REF TO cl_vsi, scanrc TYPE vscan_scanrc, bapi_msg TYPE bapiret2, bapi_msgs TYPE vscan_bapiret2_t. " Data was download from Internet and uploaded here " so we will use the HTTP_UPLOAD profile cl_vsi=>get_instance( EXPORTING if_profile = '/SIHTTP/HTTP_UPLOAD' IMPORTING eo_instance = scanner EXCEPTIONS profile_not_active = 1 OTHERS = 2 ). CASE sy-subrc. WHEN 0. " Perform virus scan scanner->if_vscan_instance~scan_bytes( EXPORTING if_data = data IMPORTING ef_scanrc = scanrc et_bapiret = bapi_msgs EXCEPTIONS not_available = 1 configuration_error = 2 internal_error = 3 OTHERS = 4 ). " Severe errors of the scanner (NOT: Virus found) are reported " as exceptions and must be reported as technical errors IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. " Result of virus scan " Any scan error or virus infection will be reported there IF scanrc <> 0. LOOP AT bapi_msgs INTO bapi_msg WHERE type = 'E'. MESSAGE ID bapi_msg-id TYPE 'E' NUMBER bapi_msg-number WITH bapi_msg-message_v1 bapi_msg-message_v2 bapi_msg-message_v3 bapi_msg-message_v4 INTO /apmg/cx_apm_error=>null. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDLOOP. ENDIF. WHEN 1. " No Virus Scan active --> nothing to do WHEN 2. " Error getting scanner. Reporting needed RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDCASE. ENDMETHOD. METHOD _filename. IF str CA '/'. FIND REGEX '(.*/)(.*)' IN str SUBMATCHES path filename ##REGEX_POSIX. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Malformed path'. ENDIF. IF path <> '/'. CONCATENATE '/' path INTO path. ENDIF. ELSE. path = '/'. filename = str. ENDIF. TRANSLATE filename TO LOWER CASE. ENDMETHOD. METHOD _normalize_path. " removes first folder from path if needed READ TABLE files INDEX 1 ASSIGNING FIELD-SYMBOL(). IF sy-subrc <> 0. RETURN. ENDIF. SPLIT -path AT '/' INTO TABLE DATA(parts). IF sy-subrc <> 0. RETURN. ENDIF. READ TABLE parts INDEX 2 INTO DATA(part). IF sy-subrc <> 0 OR strlen( part ) = 0. RETURN. ENDIF. CONCATENATE '/' part '/*' INTO part. DATA(needed) = abap_true. LOOP AT files ASSIGNING . IF -path NP part. needed = abap_false. EXIT. " current loop ENDIF. ENDLOOP. IF needed = abap_true. DATA(length) = strlen( part ) - 2. LOOP AT files ASSIGNING . -path = -path+length. ENDLOOP. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_json IMPLEMENTATION. METHOD get. TRY. result = /apmg/cl_apm_ajson=>parse( json )->get_string( path ). CATCH /apmg/cx_apm_ajson_error INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD to_abap. TRY. DATA(ajson) = /apmg/cl_apm_ajson=>parse( json )->map( /apmg/cl_apm_ajson_extensions=>from_camel_case_underscore( ) )->to_abap_corresponding_only( ). ajson->to_abap( IMPORTING ev_container = result ). CATCH /apmg/cx_apm_ajson_error INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD to_string. TRY. result = /apmg/cl_apm_ajson=>new( )->set( iv_path = '/' iv_val = value )->stringify( ). CATCH /apmg/cx_apm_ajson_error INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD validate_and_prettify. TRY. result = /apmg/cl_apm_ajson=>parse( iv_json = json iv_keep_item_order = abap_true )->stringify( 2 ). CATCH /apmg/cx_apm_ajson_error INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_logo IMPLEMENTATION. METHOD replace_width_height. DATA(width) = CONV ty_ratio( height * ratio ). result = replace( val = svg sub = '$' with = |{ width }| ). result = replace( val = result sub = '$' with = |{ height }| ). ENDMETHOD. METHOD svg_cube. result = |\n| && || && || && || && || && || && ||. result = replace_width_height( svg = result ratio = '0.92' height = height ). ENDMETHOD. METHOD svg_logo. result = |\n| && |\n| && |\n| && |\n| && |\n| && |\n| && |\n| && |\n| && |\n| && |\n| && |\n| && |\n| && ||. result = replace_width_height( svg = result ratio = '1.0' height = height ). ENDMETHOD. METHOD svg_logo_with_text. " TODO: Replace gap with styles to avoid underline on hover result = svg_logo( height ) && | | && svg_text( height - 5 ). ENDMETHOD. METHOD svg_text. result = |\n| && |\n| && |\n| && |\n| && |\n| && |\n| && ||. result = replace_width_height( svg = result ratio = '2.43' height = height ). ENDMETHOD. METHOD xml. result = |\n{ svg }\n|. ENDMETHOD. ENDCLASS. *! CLASS lcl_string IMPLEMENTATION. METHOD get_data. result = data. ENDMETHOD. METHOD set_data. me->data = data. ENDMETHOD. METHOD lif_value_type~copy. " Copies the value of the source object to itself DATA string TYPE REF TO lcl_string. string ?= source. data = string->data. ENDMETHOD. ENDCLASS. *! CLASS lcl_string_array IMPLEMENTATION. METHOD get_data. result = data. ENDMETHOD. METHOD set_data. me->data = data. ENDMETHOD. METHOD append. " Append a value to the end of the array APPEND value TO me->data. ENDMETHOD. METHOD append_array. " Append the items of an array to this array FIELD-SYMBOLS TYPE string. LOOP AT array->data ASSIGNING . append( ). ENDLOOP. ENDMETHOD. METHOD delete. " Deletes a value from the array DELETE data WHERE table_line = value. ENDMETHOD. METHOD find_val. " Returns the index of the first occurrence of a value in the array, " or 0 if not found. result = line_index( data[ table_line = value ] ). ENDMETHOD. METHOD lif_value_type~copy. " Copies the value of the source object to itself DATA sa TYPE REF TO lcl_string_array. sa ?= source. data = sa->data. ENDMETHOD. ENDCLASS. *! CLASS lcl_hashmap IMPLEMENTATION. METHOD constructor. " Hashmap constructor " @parameter value_type The value part class name. This must be a valid " ABAP class name, or a composition of valid ABAP " class names separated by a colon. IF value_type CS ':'. FIND REGEX '^([^\s:]+)(?::(.+))?$' IN value_type SUBMATCHES me->value_type me->subsequent_hashmap_value_type ##REGEX_POSIX. IF sy-subrc <> 0. me->value_type = value_type. ENDIF. ELSE. me->value_type = value_type. ENDIF. TRANSLATE me->value_type TO UPPER CASE. TRANSLATE me->subsequent_hashmap_value_type TO UPPER CASE. ENDMETHOD. METHOD new. " Adds a new item to the hashmap " The value part in the new item will be created dynamically with " the type passed to the constructor (sorta like a template based hashmap). " @return The instance of the created item's value part, or empty if the item already exists. DATA new_item TYPE ty_item. FIELD-SYMBOLS TYPE ty_item. new_item-key = key. INSERT new_item INTO TABLE data ASSIGNING . CHECK sy-subrc = 0. IF me->value_type = 'LCL_HASHMAP' AND me->subsequent_hashmap_value_type IS NOT INITIAL. -value = NEW lcl_hashmap( value_type = me->subsequent_hashmap_value_type ). ELSE. CREATE OBJECT -value TYPE (me->value_type). ENDIF. result = -value. ENDMETHOD. METHOD exists. " Checks if a item exists in the hashmap. " @return A flag indicating if the item exists. IF line_exists( data[ key = key ] ). result = abap_true. ENDIF. ENDMETHOD. METHOD get. " Gets an item reference from the hashmap. " If the item is not found, a new item is created, as if using the method new. " @return The reference to the value part of the item. FIELD-SYMBOLS TYPE ty_item. READ TABLE data ASSIGNING WITH KEY key = key. IF sy-subrc = 0. result = -value. ELSE. result = new( key ). ENDIF. ENDMETHOD. METHOD set. " Sets the value of an item in the hashmap. " If the item does not yet exist, an item is created with the passed key/value pair. " If the item already exists, its value is replaced with the passed value. DATA item TYPE REF TO lif_value_type. item = get( key ). item->copy( value ). ENDMETHOD. METHOD delete. " Deletes an item from the hashmap. DELETE data WHERE key = key. ENDMETHOD. METHOD get_data. result = data. ENDMETHOD. METHOD set_data. me->data = data. ENDMETHOD. METHOD lif_value_type~copy. " Copies the contents of another hashmap to this hashmap " @parameter hashmap The other (source) hashmap DATA: hashmap TYPE REF TO lcl_hashmap, value TYPE REF TO lif_value_type. FIELD-SYMBOLS TYPE ty_item. hashmap ?= source. LOOP AT hashmap->data ASSIGNING . value = new( -key ). value->copy( -value ). ENDLOOP. ENDMETHOD. ENDCLASS. *! CLASS lcl_alerts IMPLEMENTATION. METHOD get. IF line CS '[!NOTE]'. result-tag = '[!NOTE!]'. result-class = 'alert-note'. result-color = '#4493F8'. result-icon = note( ). result-text = 'Note'. ELSEIF line CS '[!TIP]'. result-tag = '[!TIP!]'. result-class = 'alert-tip'. result-color = '#3FB950'. result-icon = tip( ). result-text = 'Tip'. ELSEIF line CS '[!IMPORTANT]'. result-tag = '[!IMPORTANT!]'. result-class = 'alert-important'. result-color = '#AB7DF8'. result-icon = important( ). result-text = 'Important'. ELSEIF line CS '[!WARNING]'. result-tag = '[!WARNING!]'. result-class = 'alert-warning'. result-color = '#D29922'. result-icon = warning( ). result-text = 'Warning'. ELSEIF line CS '[!CAUTION]'. result-tag = '[!CAUTION!]'. result-class = 'alert-caution'. result-color = '#F85149'. result-icon = caution( ). result-text = 'Caution'. ENDIF. ENDMETHOD. METHOD note. result = ''. ENDMETHOD. METHOD tip. result = ''. ENDMETHOD. METHOD important. result = ''. ENDMETHOD. METHOD warning. result = ''. ENDMETHOD. METHOD caution. result = ''. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_markdown IMPLEMENTATION. METHOD block_code. IF block IS NOT INITIAL AND block-type IS INITIAL AND block-interrupted IS INITIAL. RETURN. ENDIF. IF line-indent >= 4. result-element-name = 'pre'. result-element-handler = 'element'. result-element-text-name = 'code'. result-element-text-text = line-body+4. ENDIF. ENDMETHOD. "block_code METHOD block_code_complete. result = block. result-element-text-text = result-element-text-text. ENDMETHOD. "block_code_complete METHOD block_code_continue. DATA text TYPE string. IF line-indent >= 4. result = block. IF block-interrupted IS NOT INITIAL. CONCATENATE result-element-text-text %_newline INTO result-element-text-text RESPECTING BLANKS. CLEAR result-interrupted. ENDIF. text = line-body+4. CONCATENATE result-element-text-text %_newline text INTO result-element-text-text RESPECTING BLANKS. ENDIF. ENDMETHOD. "block_code_continue METHOD block_comment. CHECK markup_escaped IS INITIAL. IF strlen( line-text ) >= 3 AND line-text+3(1) = '-' AND line-text+2(1) = '-' AND line-text+1(1) = '!'. result-markup = line-body. FIND REGEX '-->$' IN line-text ##REGEX_POSIX. IF sy-subrc = 0. result-closed = abap_true. ENDIF. ENDIF. ENDMETHOD. "block_Comment METHOD block_comment_continue. CHECK block-closed IS INITIAL. result = block. CONCATENATE result-markup %_newline line-body INTO result-markup. FIND REGEX '-->\s*$' IN line-text ##REGEX_POSIX. " apm IF sy-subrc = 0. result-closed = abap_true. ENDIF. ENDMETHOD. "block_Comment_Continue METHOD block_fencedcode. DATA: regex TYPE string, m1 TYPE string. FIELD-SYMBOLS LIKE LINE OF result-element-text-attributes. regex = '^[' && line-text(1) && ']{3,}[ ]*([^`]+)?[ ]*$'. FIND REGEX regex IN line-text SUBMATCHES m1 ##REGEX_POSIX. IF sy-subrc = 0. IF m1 IS NOT INITIAL. APPEND INITIAL LINE TO result-element-text-attributes ASSIGNING . -name = 'class'. CONCATENATE 'language-' m1 INTO -value. ENDIF. result-char = line-text(1). result-element-name = 'pre'. result-element-handler = 'element'. result-element-text-name = 'code'. result-element-text-handler = 'syntax_highlighter'. " apm ENDIF. ENDMETHOD. "block_Fenced_Code METHOD block_fencedcode_complete. result = block. result-element-text-text = result-element-text-text. ENDMETHOD. "block_Fenced_Code_Complete METHOD block_fencedcode_continue. DATA regex TYPE string. CHECK block-complete IS INITIAL. result = block. IF result-interrupted IS NOT INITIAL. CONCATENATE result-element-text-text %_newline INTO result-element-text-text. CLEAR result-interrupted. ENDIF. CONCATENATE '^' block-char '{3,}[ ]*$' INTO regex. FIND REGEX regex IN line-text ##REGEX_POSIX. IF sy-subrc = 0. result-element-text-text = result-element-text-text+1. result-complete = abap_true. RETURN. ENDIF. CONCATENATE result-element-text-text %_newline line-body INTO result-element-text-text. ENDMETHOD. "block_Fenced_Code_Continue METHOD block_header. DATA: level TYPE i VALUE 1, h_level TYPE n, pos TYPE i, id TYPE string. FIELD-SYMBOLS LIKE LINE OF result-element-attributes. CHECK strlen( line-text ) > 1 AND line-text+1(1) IS NOT INITIAL. WHILE strlen( line-text ) > level AND line-text+level(1) = '#'. level = level + 1. ENDWHILE. CHECK level <= 6. h_level = level. CONCATENATE 'h' h_level INTO result-element-name. result-element-text-text = line-text. result-element-text-text = trim( str = result-element-text-text mask = ' #' ). CONDENSE result-element-text-text. result-element-handler = 'line'. ">>> apm FIND REGEX ' \{(#[\w-]*)\}' IN result-element-text-text IGNORING CASE MATCH OFFSET pos SUBMATCHES id ##REGEX_POSIX. IF sy-subrc = 0. " # Heading {#custom-id} APPEND INITIAL LINE TO result-element-attributes ASSIGNING . -name = 'id'. -value = id. result-element-text-text = result-element-text-text(pos). ELSE. id = replace( val = to_lower( result-element-text-text ) regex = `[^\w\s\-]` with = `` occ = 0 ) ##REGEX_POSIX. id = replace( val = id regex = `\s` with = `-` occ = 0 ) ##REGEX_POSIX. " If there are HTML tags, no id IF id CA '<>'. id = '#'. ENDIF. APPEND INITIAL LINE TO result-element-attributes ASSIGNING . -name = 'id'. -value = id. ENDIF. "<<< apm ENDMETHOD. "block_Header METHOD block_list. DATA: name TYPE string, pattern TYPE string, regex TYPE string, m1 TYPE string, m2 TYPE string, list_start TYPE string. FIELD-SYMBOLS LIKE LINE OF result-element-attributes. IF line-text(1) <= '-'. name = 'ul'. pattern = '[*+-]'. ELSE. name = 'ol'. pattern = '[0-9]+[.]'. ENDIF. regex = '^(' && pattern && '[ ]+)(.*)'. FIND REGEX regex IN line-text SUBMATCHES m1 m2 ##REGEX_POSIX. IF sy-subrc = 0. result-indent = line-indent. ">>> apm " We could distinguish between *,+,- lists "IF name = 'ul' " result-pattern = |[{ m1(1) }]| "ELSE result-pattern = pattern. "ENDIF "<<< apm result-element-name = name. result-element-handler = 'elements'. IF result-element-name = 'ol'. list_start = substring_before( val = line-text sub = '.' case = abap_false ). IF list_start <> '1'. APPEND INITIAL LINE TO result-element-attributes ASSIGNING . -name = 'start'. -value = list_start. ENDIF. ENDIF. result-li-name = 'li'. result-li-handler = 'li'. APPEND m2 TO result-li-lines. ENDIF. ENDMETHOD. "block_List METHOD block_list_complete. FIELD-SYMBOLS:
  • LIKE LINE OF result-element-texts, LIKE LINE OF
  • -lines. result = block. APPEND result-li TO result-element-texts. IF result-loose IS NOT INITIAL. LOOP AT result-element-texts ASSIGNING
  • . READ TABLE
  • -lines INDEX lines( result-li-lines ) ASSIGNING . IF sy-subrc = 0 AND IS NOT INITIAL. APPEND INITIAL LINE TO
  • -lines. ENDIF. ENDLOOP. ENDIF. ENDMETHOD. "block_List_complete METHOD block_list_continue. DATA: regex TYPE string, m1 TYPE string, text TYPE string, ref_block TYPE ty_block. result = block. regex = '^' && block-pattern && '(?:[ ]+(.*)|$)'. IF block-indent = line-indent. FIND REGEX regex IN line-text SUBMATCHES m1 ##REGEX_POSIX. IF sy-subrc = 0. IF result-interrupted IS NOT INITIAL. APPEND INITIAL LINE TO result-li-lines. result-loose = abap_true. CLEAR result-interrupted. ENDIF. APPEND result-li TO result-element-texts. CLEAR result-li. result-li-name = 'li'. result-li-handler = 'li'. APPEND m1 TO result-li-lines. RETURN. ENDIF. ENDIF. IF line-text(1) = '['. ref_block = block_reference( line ). IF ref_block IS NOT INITIAL. RETURN. ENDIF. ENDIF. IF result-interrupted IS INITIAL. text = line-body. REPLACE ALL OCCURRENCES OF REGEX '^[ ]{0,4}' IN text WITH '' ##REGEX_POSIX. APPEND text TO result-li-lines. RETURN. ENDIF. IF line-indent > 0. APPEND INITIAL LINE TO result-li-lines. text = line-body. REPLACE ALL OCCURRENCES OF REGEX '^[ ]{0,4}' IN text WITH '' ##REGEX_POSIX. APPEND text TO result-li-lines. CLEAR result-interrupted. RETURN. ENDIF. CLEAR result. ENDMETHOD. "block_List_Continue METHOD block_markup. DATA: regex TYPE string, m1 TYPE string, m2 TYPE string, length TYPE i, index TYPE i, remainder TYPE string, remainder_trimmed TYPE string. CHECK: markup_escaped IS INITIAL, safe_mode IS INITIAL. regex = '^<(\w*)(?:[ ]*' && regex_html_attribute && ')*[ ]*(/)?>'. FIND FIRST OCCURRENCE OF REGEX regex IN line-text SUBMATCHES m1 m2 MATCH LENGTH length ##REGEX_POSIX. IF sy-subrc = 0. index = text_level_elements->find_val( m1 ). CHECK index = 0. result-name = m1. result-depth = 0. result-markup = _adjust_markup( line-text ). " apm remainder = line-text+length. remainder_trimmed = trim( remainder ). index = void_elements->find_val( m1 ). IF remainder_trimmed IS INITIAL. IF m2 IS NOT INITIAL OR index <> 0. result-closed = abap_true. result-void = abap_true. ENDIF. ELSE. IF m2 IS NOT INITIAL OR index <> 0. CLEAR result. RETURN. ENDIF. CONCATENATE '[ ]*$' INTO regex. FIND FIRST OCCURRENCE OF REGEX regex IN remainder IGNORING CASE ##REGEX_POSIX. IF sy-subrc = 0. result-closed = abap_true. ENDIF. ENDIF. ENDIF. "regex sy-subrc = 0 ENDMETHOD. "block_Markup METHOD block_markup_continue. DATA: regex TYPE string, body TYPE string. CHECK block-closed IS INITIAL. result = block. CONCATENATE '^<' result-name '(?:[ ]*' regex_html_attribute ')*[ ]*>' INTO regex. FIND REGEX regex IN line-text IGNORING CASE ##REGEX_POSIX. "open IF sy-subrc = 0. result-depth = result-depth + 1. ENDIF. CONCATENATE '[ ]*$' INTO regex. FIND REGEX regex IN line-text IGNORING CASE ##REGEX_POSIX. "close IF sy-subrc = 0. IF result-depth > 0. result-depth = result-depth - 1. ELSE. result-closed = abap_true. ENDIF. ENDIF. IF result-interrupted IS NOT INITIAL. CONCATENATE result-markup %_newline INTO result-markup. CLEAR result-interrupted. ENDIF. ">>> apm body = _adjust_markup( line-body ). CONCATENATE result-markup %_newline body INTO result-markup. "<<< apm ENDMETHOD. "block_Markup_Continue METHOD block_quote. DATA m1 TYPE string. FIELD-SYMBOLS LIKE LINE OF result-element-attributes. FIND REGEX '^>[ ]?(.*)' IN line-text SUBMATCHES m1 ##REGEX_POSIX. IF sy-subrc = 0. SHIFT m1 LEFT DELETING LEADING space. result-element-name = 'blockquote'. result-element-handler = '_lines'. " >>> apm IF lcl_alerts=>get( m1 ) IS NOT INITIAL. APPEND INITIAL LINE TO result-element-attributes ASSIGNING . -name = 'class'. -value = lcl_alerts=>get( m1 )-class. m1 = lcl_alerts=>get( m1 )-tag. ENDIF. " <<< apm APPEND m1 TO result-element-lines. ENDIF. ENDMETHOD. "block_Quote METHOD block_quote_complete. result = block. ENDMETHOD. METHOD block_quote_continue. DATA m1 TYPE string. IF line-text(1) = '>'. result = block. FIND REGEX '^>[ ]?(.*)' IN line-text SUBMATCHES m1 ##REGEX_POSIX. IF sy-subrc = 0. " >>> apm IF lcl_alerts=>get( m1 ) IS NOT INITIAL. CLEAR result. RETURN. ENDIF. " <<< apm SHIFT m1 LEFT DELETING LEADING space. IF result-interrupted IS NOT INITIAL. APPEND INITIAL LINE TO result-element-lines. CLEAR result-interrupted. ENDIF. APPEND m1 TO result-element-lines. RETURN. ENDIF. ENDIF. IF block-interrupted IS INITIAL. result = block. APPEND line-text TO result-element-lines. ENDIF. ENDMETHOD. "block_Quote_Continue METHOD block_reference. DATA: m1 TYPE string, m2 TYPE string, m3 TYPE string, m4 TYPE string, id TYPE string, ref_map TYPE REF TO lcl_hashmap, ref_item TYPE REF TO lcl_hashmap, ref_val TYPE REF TO lcl_string. FIND REGEX '^\[(.+)\]:[ ]*?([ ]+["''(](.+)["'')])?[ ]*$' IN line-text SUBMATCHES m1 m2 m3 m4 ##REGEX_POSIX. IF sy-subrc = 0. id = m1. TRANSLATE id TO LOWER CASE. ref_map ?= definition_data->get( 'Reference' ). ref_item ?= ref_map->get( id ). ref_val ?= ref_item->get( 'url' ). ref_val->set_data( m2 ). IF m3 IS NOT INITIAL. ref_val ?= ref_item->get( 'title' ). ref_val->set_data( m4 ). ENDIF. result-hidden = abap_true. ENDIF. ENDMETHOD. "block_Reference METHOD block_rule. DATA regex TYPE string. CONCATENATE '^([' line-text(1) '])([ ]*\1){2,}[ ]*$' INTO regex. FIND REGEX regex IN line-text ##REGEX_POSIX. IF sy-subrc = 0. result-element-name = 'hr'. ENDIF. ENDMETHOD. "block_Rule METHOD block_setextheader. CHECK block IS NOT INITIAL AND block-type IS INITIAL AND block-interrupted IS INITIAL. IF line-text CO line-text(1). result = block. IF line-text(1) = '='. result-element-name = 'h1'. ELSE. result-element-name = 'h2'. ENDIF. ENDIF. ENDMETHOD. "block_SetextHeader METHOD block_table. DATA: divider TYPE string, divider_cells TYPE string_table, len TYPE i, header TYPE string, header_cells TYPE string_table, index TYPE i, headeresults TYPE ty_t_element2. FIELD-SYMBOLS: LIKE LINE OF header_cells, LIKE LINE OF headeresults, LIKE LINE OF -attributes, LIKE LINE OF divider_cells, LIKE LINE OF result-alignments, LIKE LINE OF result-element-texts, LIKE LINE OF -texts. CHECK NOT ( block IS INITIAL OR block-type IS NOT INITIAL OR block-interrupted IS NOT INITIAL ). FIND '|' IN block-element-text-text. IF sy-subrc = 0 AND line-text CO ' -:|'. result = block. divider = trim( line-text ). divider = trim( str = divider mask = '|' ). " >>> apm " Replace escaped \| divider = replace( val = divider sub = '\|' with = '%bar%' occ = 0 ). " <<< apm SPLIT divider AT '|' INTO TABLE divider_cells. LOOP AT divider_cells ASSIGNING . = trim( ). = replace( val = sub = '%bar%' with = '|' occ = 0 ). " apm CHECK IS NOT INITIAL. APPEND INITIAL LINE TO result-alignments ASSIGNING . IF (1) = ':'. = 'left'. ENDIF. len = strlen( ) - 1. IF +len(1) = ':'. IF = 'left'. = 'center'. ELSE. = 'right'. ENDIF. ENDIF. ENDLOOP. "lt_divider_cells " ~ header = trim( result-element-text-text ). header = trim( str = header mask = '|' ). " >>> apm " Replace escaped \| header = replace( val = header sub = '\|' with = '%bar%' occ = 0 ). " <<< apm SPLIT header AT '|' INTO TABLE header_cells. LOOP AT header_cells ASSIGNING . index = sy-tabix. = trim( ). = replace( val = sub = '%bar%' with = '|' occ = 0 ). " apm APPEND INITIAL LINE TO headeresults ASSIGNING . -name = 'th'. -text = . -handler = 'line'. READ TABLE result-alignments ASSIGNING INDEX index. IF sy-subrc = 0 AND IS NOT INITIAL. APPEND INITIAL LINE TO -attributes ASSIGNING . -name = 'style'. CONCATENATE 'text-align: ' ';' INTO -value RESPECTING BLANKS. ENDIF. ENDLOOP. " ~ result-identified = abap_true. result-element-name = 'table'. result-element-handler = 'elements'. APPEND INITIAL LINE TO result-element-texts ASSIGNING . -name = 'thead'. -handler = 'elements'. APPEND INITIAL LINE TO -texts ASSIGNING . -name = 'tr'. -handler = 'elements'. -texts = headeresults. APPEND INITIAL LINE TO result-element-texts ASSIGNING . -name = 'tbody'. -handler = 'elements'. ENDIF. "sy-subrc = 0 and line-text na ' -:|'. ENDMETHOD. "block_Table METHOD block_table_continue. DATA: row TYPE string, matches TYPE match_result_tab, index TYPE i, cell TYPE string. FIELD-SYMBOLS: LIKE LINE OF matches, LIKE LINE OF result-element-texts, LIKE LINE OF -texts, LIKE LINE OF -texts, LIKE LINE OF result-alignments, LIKE LINE OF -attributes. CHECK block-interrupted IS INITIAL. IF line-text CS '|'. result = block. row = trim( line-text ). row = trim( str = row mask = '|' ). READ TABLE result-element-texts ASSIGNING INDEX 2. CHECK sy-subrc = 0. APPEND INITIAL LINE TO -texts ASSIGNING . -name = 'tr'. -handler = 'elements'. " >>> apm " Replace escaped \| row = replace( val = row sub = '\|' with = '%bar%' occ = 0 ). " REGEX '(?:(\\[|])|[^|`]|`[^`]+`|`)+' is too greedy FIND ALL OCCURRENCES OF REGEX '(?:(\\[|])|[^|])+' IN row RESULTS matches ##SUBRC_OK ##REGEX_POSIX. " <<< apm LOOP AT matches ASSIGNING . index = sy-tabix. cell = row+-offset(-length). cell = trim( cell ). cell = replace( val = cell sub = '%bar%' with = '|' occ = 0 ). " apm APPEND INITIAL LINE TO -texts ASSIGNING . -name = 'td'. -handler = 'line'. -text = cell. READ TABLE result-alignments ASSIGNING INDEX index. IF sy-subrc = 0 AND IS NOT INITIAL. APPEND INITIAL LINE TO -attributes ASSIGNING . -name = 'style'. CONCATENATE 'text-align: ' ';' INTO -value RESPECTING BLANKS. ENDIF. ENDLOOP. "lt_matches ENDIF. "line-text cs '|' ENDMETHOD. "block_Table_Continue METHOD chop. DATA regex TYPE string. result = str. regex = mask. REPLACE ALL OCCURRENCES OF REGEX '([\.\?\*\+\|])' IN regex WITH '\\$1' ##REGEX_POSIX. CONCATENATE '[' regex ']*\Z' INTO regex. REPLACE ALL OCCURRENCES OF REGEX regex IN result WITH '' ##REGEX_POSIX. ENDMETHOD. "trim METHOD constructor. " Constuctor method " Initializes the instance constants DATA: ref_sa TYPE REF TO lcl_string_array, string TYPE REF TO lcl_string, objdescr TYPE REF TO cl_abap_objectdescr. FIELD-SYMBOLS LIKE LINE OF objdescr->methods. ">>> apm config-root_href = root_href. config-root_img = root_img. config-path = path. config-sapevent = sapevent. config-path_util = NEW #( ). "<<< apm " " Lines " block_types = NEW #( value_type = 'lcl_string_array' ). ref_sa ?= block_types->new( '#' ). ref_sa->append( 'Header' ). ref_sa ?= block_types->new( '*' ). ref_sa->append( 'Rule' ). ref_sa->append( 'List' ). ref_sa ?= block_types->new( '+' ). ref_sa->append( 'List' ). ref_sa ?= block_types->new( '-' ). ref_sa->append( 'SetextHeader' ). ref_sa->append( 'Table' ). ref_sa->append( 'Rule' ). ref_sa->append( 'List' ). ref_sa ?= block_types->new( '0' ). ref_sa->append( 'List' ). ref_sa ?= block_types->new( '1' ). ref_sa->append( 'List' ). ref_sa ?= block_types->new( '2' ). ref_sa->append( 'List' ). ref_sa ?= block_types->new( '3' ). ref_sa->append( 'List' ). ref_sa ?= block_types->new( '4' ). ref_sa->append( 'List' ). ref_sa ?= block_types->new( '5' ). ref_sa->append( 'List' ). ref_sa ?= block_types->new( '6' ). ref_sa->append( 'List' ). ref_sa ?= block_types->new( '7' ). ref_sa->append( 'List' ). ref_sa ?= block_types->new( '8' ). ref_sa->append( 'List' ). ref_sa ?= block_types->new( '9' ). ref_sa->append( 'List' ). ref_sa ?= block_types->new( ':' ). ref_sa->append( 'Table' ). ref_sa ?= block_types->new( '<' ). ref_sa->append( 'Comment' ). ref_sa->append( 'Markup' ). ref_sa ?= block_types->new( '=' ). ref_sa->append( 'SetextHeader' ). ref_sa ?= block_types->new( '>' ). ref_sa->append( 'Quote' ). ref_sa ?= block_types->new( '[' ). ref_sa->append( 'Reference' ). ref_sa ?= block_types->new( '_' ). ref_sa->append( 'Rule' ). ref_sa ?= block_types->new( '`' ). ref_sa->append( 'FencedCode' ). ref_sa ?= block_types->new( '|' ). ref_sa->append( 'Table' ). ref_sa ?= block_types->new( '~' ). ref_sa->append( 'FencedCode' ). unmarked_block_types = NEW #( ). unmarked_block_types->append( 'Code' ). " " Inline Elements " inline_types = NEW #( value_type = 'lcl_string_array' ). ref_sa ?= inline_types->new( '"' ). ref_sa->append( 'SpecialCharacter' ). ref_sa ?= inline_types->new( '!' ). ref_sa->append( 'Image' ). ref_sa ?= inline_types->new( '&' ). ref_sa->append( 'SpecialCharacter' ). ref_sa ?= inline_types->new( '*' ). ref_sa->append( 'Emphasis' ). ref_sa ?= inline_types->new( ':' ). ref_sa->append( 'Url' ). ref_sa ?= inline_types->new( '<' ). ref_sa->append( 'UrlTag' ). ref_sa->append( 'EmailTag' ). ref_sa->append( 'Markup' ). ref_sa->append( 'SpecialCharacter' ). ref_sa ?= inline_types->new( '>' ). ref_sa->append( 'SpecialCharacter' ). ref_sa ?= inline_types->new( '[' ). ref_sa->append( 'Link' ). ref_sa ?= inline_types->new( '_' ). ref_sa->append( 'Emphasis' ). ref_sa ?= inline_types->new( '`' ). ref_sa->append( 'Code' ). ">>> apm ref_sa ?= inline_types->new( '~' ). ref_sa->append( 'Strikethrough' ). ref_sa->append( 'Emphasis' ). "subscript ref_sa ?= inline_types->new( '^' ). ref_sa->append( 'Emphasis' ). "superscript ref_sa ?= inline_types->new( '=' ). ref_sa->append( 'Highlight' ). "<<< apm ref_sa ?= inline_types->new( '\' ). ref_sa->append( 'EscapeSequence' ). " " Read-Only " special_characters = NEW #( ). ref_sa = special_characters. ref_sa->append( '\' ). ref_sa->append( '`' ). ref_sa->append( '*' ). ref_sa->append( '_' ). ref_sa->append( '{' ). ref_sa->append( '}' ). ref_sa->append( '[' ). ref_sa->append( ']' ). ref_sa->append( '(' ). ref_sa->append( ')' ). ref_sa->append( '>' ). ref_sa->append( '#' ). ref_sa->append( '+' ). ref_sa->append( '-' ). ref_sa->append( '.' ). ref_sa->append( '!' ). ref_sa->append( '|' ). strong_regex = NEW #( ). string ?= strong_regex->new( '*' ). string->set_data( '(^[*][*]((?:\\[*]|[^*]|[*][^*]*[*])+)[*][*](?![*]))' ). string ?= strong_regex->new( '_' ). string->set_data( '(^__((?:\\_|[^_]|_[^_]*_)+)__(?!_))' ). em_regex = NEW #( ). string ?= em_regex->new( '*' ). string->set_data( '(^[*]((?:\\[*]|[^*]|[*][*][^*]+[*][*])+)[*](?![*]))' ). string ?= em_regex->new( '_' ). string->set_data( '(^_((?:\\_|[^_]|__[^_]*__)+)_(?!_)\b)' ). string ?= em_regex->new( '^' ). string->set_data( '(^[\^]((?:\\[\^]|[^\^]|[\^][\^][^\^]+[\^][\^])+)[\^](?![\^]))' ). string ?= em_regex->new( '~' ). string->set_data( '(^~((?:\\~|[^~]|~~[^~]*~~)+)~(?!~)\b)' ). regex_html_attribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"''=<>`\s]+|"[^"]*"|''[^'']*''))?'. void_elements = NEW #( ). ref_sa = void_elements. ref_sa->append( 'area' ). ref_sa->append( 'base' ). ref_sa->append( 'br' ). ref_sa->append( 'col' ). ref_sa->append( 'command' ). ref_sa->append( 'embed' ). ref_sa->append( 'hr' ). ref_sa->append( 'img' ). ref_sa->append( 'input' ). ref_sa->append( 'link' ). ref_sa->append( 'meta' ). ref_sa->append( 'param' ). ref_sa->append( 'source' ). text_level_elements = NEW #( ). ref_sa = text_level_elements. ref_sa->append( 'a' ). ref_sa->append( 'b' ). ref_sa->append( 'i' ). ref_sa->append( 'q' ). ref_sa->append( 's' ). ref_sa->append( 'u' ). ref_sa->append( 'br' ). ref_sa->append( 'em' ). ref_sa->append( 'rp' ). ref_sa->append( 'rt' ). ref_sa->append( 'tt' ). ref_sa->append( 'xm' ). ref_sa->append( 'bdo' ). ref_sa->append( 'big' ). ref_sa->append( 'del' ). ref_sa->append( 'ins' ). ref_sa->append( 'sub' ). ref_sa->append( 'sup' ). ref_sa->append( 'var' ). ref_sa->append( 'wbr' ). ref_sa->append( 'abbr' ). ref_sa->append( 'cite' ). ref_sa->append( 'code' ). ref_sa->append( 'font' ). ref_sa->append( 'mark' ). ref_sa->append( 'nobr' ). ref_sa->append( 'ruby' ). ref_sa->append( 'span' ). ref_sa->append( 'time' ). ref_sa->append( 'blink' ). ref_sa->append( 'small' ). ref_sa->append( 'nextid' ). ref_sa->append( 'spacer' ). ref_sa->append( 'strike' ). ref_sa->append( 'strong' ). ref_sa->append( 'acronym' ). ref_sa->append( 'listing' ). ref_sa->append( 'marquee' ). ref_sa->append( 'basefont' ). safe_links_whitelist = NEW #( ). ref_sa = safe_links_whitelist. ref_sa->append( 'http://' ). ref_sa->append( 'https://' ). ref_sa->append( 'sapevent:' ). " apm ref_sa->append( 'ftp://' ). ref_sa->append( 'ftps://' ). ref_sa->append( 'mailto:' ). ref_sa->append( 'data:image/png;base64,' ). ref_sa->append( 'data:image/gif;base64,' ). ref_sa->append( 'data:image/jpeg;base64,' ). ref_sa->append( 'irc:' ). ref_sa->append( 'ircs:' ). ref_sa->append( 'git:' ). ref_sa->append( 'ssh:' ). ref_sa->append( 'news:' ). ref_sa->append( 'steam:' ). "// Method names objdescr ?= cl_abap_objectdescr=>describe_by_object_ref( me ). LOOP AT objdescr->methods ASSIGNING . APPEND -name TO methods. ENDLOOP. ENDMETHOD. "constructor METHOD element. DATA: current_element TYPE ty_element, method_name TYPE string, content TYPE string. FIELD-SYMBOLS: LIKE current_element-text, LIKE LINE OF current_element-attributes, LIKE -text, LIKE -attributes. magic_move( EXPORTING from = element CHANGING to = current_element ). ">>> apm " Always sanitise " IF safe_mode IS NOT INITIAL current_element = sanitise_element( current_element ). " ENDIF "<<< apm ASSIGN COMPONENT 'TEXT' OF STRUCTURE current_element TO . ASSERT sy-subrc = 0. result = |<{ current_element-name }|. IF current_element-attributes IS NOT INITIAL. LOOP AT current_element-attributes ASSIGNING . result = |{ result } { -name }="{ _escape( -value ) }"|. ENDLOOP. ENDIF. IF IS NOT INITIAL OR current_element-texts IS NOT INITIAL OR current_element-lines IS NOT INITIAL. result = |{ result }>|. ">>> apm IF current_element-handler = 'syntax_highlighter'. ASSIGN COMPONENT 'ATTRIBUTES' OF STRUCTURE TO . ASSERT sy-subrc = 0. = current_element-attributes. ENDIF. "<<< apm IF current_element-handler IS NOT INITIAL. method_name = current_element-handler. TRANSLATE method_name TO UPPER CASE. IF current_element-texts IS NOT INITIAL. "// for array of elements CALL METHOD (method_name) EXPORTING elements = current_element-texts RECEIVING result = content. ELSEIF current_element-lines IS NOT INITIAL. "// for array of lines CALL METHOD (method_name) EXPORTING lines = current_element-lines RECEIVING result = content. ELSE. "// for simple text CALL METHOD (method_name) EXPORTING element = RECEIVING result = content. ENDIF. ELSE. IF current_element-lines IS NOT INITIAL. CONCATENATE LINES OF current_element-lines INTO content SEPARATED BY %_newline. ELSE. ASSIGN COMPONENT 'TEXT' OF STRUCTURE TO . ASSERT sy-subrc = 0. content = . content = _escape( text = content allow_quotes = abap_true ). ENDIF. ENDIF. result = |{ result }{ content }|. ELSE. result = |{ result } />|. ENDIF. ENDMETHOD. "element METHOD elements. DATA markup TYPE string_table. FIELD-SYMBOLS: TYPE any, TYPE string. LOOP AT elements ASSIGNING . APPEND INITIAL LINE TO markup ASSIGNING . = element( ). ENDLOOP. CONCATENATE LINES OF markup INTO result SEPARATED BY %_newline. CONCATENATE %_newline result %_newline INTO result. ENDMETHOD. "elements METHOD filter_unsafe_url_in_attribute. FIELD-SYMBOLS: LIKE LINE OF result-attributes, TYPE string. "safe_links_whitelist->data. result = element. READ TABLE result-attributes WITH KEY name = attribute ASSIGNING . CHECK sy-subrc = 0. ">>> apm CASE attribute. WHEN 'href'. -value = _adjust_a_href( -value ). WHEN 'src'. -value = _adjust_img_src( -value ). ENDCASE. "<<< apm " Check for allowed protocols LOOP AT safe_links_whitelist->get_data( ) ASSIGNING . IF string_at_start( haystack = -value needle = ) = abap_true. RETURN. ENDIF. ENDLOOP. " Effectively disable URL REPLACE FIRST OCCURRENCE OF ':' IN -value WITH '%3A'. ENDMETHOD. METHOD htmlspecialchars. result = input. REPLACE ALL OCCURRENCES OF '&' IN result WITH '&'. REPLACE ALL OCCURRENCES OF '<' IN result WITH '<'. REPLACE ALL OCCURRENCES OF '>' IN result WITH '>'. IF ent_noquotes IS INITIAL. REPLACE ALL OCCURRENCES OF '"' IN result WITH '"'. IF ent_quotes IS NOT INITIAL. IF ent_html401 IS NOT INITIAL. REPLACE ALL OCCURRENCES OF '''' IN result WITH '''. ELSE. REPLACE ALL OCCURRENCES OF '''' IN result WITH '''. ENDIF. ENDIF. ENDIF. ENDMETHOD. "htmlspecialchars METHOD inline_code. DATA: marker TYPE c LENGTH 1, marker_comb TYPE string, m0 TYPE string, m1 TYPE string, text TYPE string, not_found TYPE abap_bool. marker = excerpt-text(1). "// Deal with the different repetitions (from 5 markers to 1) marker_comb = '&&&&&'. REPLACE ALL OCCURRENCES OF '&' IN marker_comb WITH marker. WHILE marker_comb IS NOT INITIAL. match_marked_string( EXPORTING marker = marker_comb subject = excerpt-text IMPORTING m0 = m0 m1 = m1 not_found = not_found ). IF not_found IS INITIAL. text = m1. CONDENSE text. text = text. REPLACE ALL OCCURRENCES OF REGEX '[ ]*\n' IN text WITH ' ' ##REGEX_POSIX. result-extent = strlen( m0 ). result-element-name = 'code'. result-element-text-text = text. EXIT. ENDIF. SHIFT marker_comb LEFT. ENDWHILE. ENDMETHOD. "inline_code METHOD inline_emailtag. DATA: hostname_label TYPE string, common_mark_email TYPE string, regex TYPE string, m0 TYPE string, m1 TYPE string, m2 TYPE string, url TYPE string. FIELD-SYMBOLS LIKE LINE OF result-element-attributes. CHECK excerpt-text CS '>'. hostname_label = '[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?'. common_mark_email = '[a-zA-Z0-9.!#$%&\''*+\/=?^_`{|}~-]+@' && hostname_label && '(?:\.' && hostname_label && ')*'. regex = '(^<((mailto:)?' && common_mark_email && ')>)'. FIND REGEX regex IN excerpt-text IGNORING CASE SUBMATCHES m0 m1 m2 ##REGEX_POSIX. IF sy-subrc = 0. url = m1. IF m2 IS INITIAL. CONCATENATE 'mailto:' url INTO url. ENDIF. result-extent = strlen( m0 ). result-element-name = 'a'. result-element-text-text = m1. APPEND INITIAL LINE TO result-element-attributes ASSIGNING . -name = 'href'. -value = url. ENDIF. ENDMETHOD. "inline_EmailTag METHOD inline_emphasis. DATA: marker TYPE c, emphasis TYPE string, m0 TYPE string, m1 TYPE string, regex TYPE REF TO lcl_string, regex_delim TYPE string, offset TYPE i. CHECK excerpt-text IS NOT INITIAL. marker = excerpt-text(1). regex ?= strong_regex->get( marker ). IF strlen( excerpt-text ) > 1 AND excerpt-text+1(1) = marker AND regex->get_data( ) IS NOT INITIAL. FIND REGEX regex->get_data( ) IN excerpt-text SUBMATCHES m0 m1 ##REGEX_POSIX. IF sy-subrc = 0. emphasis = 'strong'. "// get the (ungreedy) end marker regex_delim = '[^&][&]{2}(?![&])'. REPLACE ALL OCCURRENCES OF '&' IN regex_delim WITH marker ##REGEX_POSIX. FIND REGEX regex_delim IN m1 MATCH OFFSET offset ##REGEX_POSIX. IF sy-subrc = 0. offset = offset + 1. m1 = m1(offset). offset = strlen( m1 ) + 4. m0 = m0(offset). ENDIF. ENDIF. ENDIF. regex ?= em_regex->get( marker ). IF emphasis IS INITIAL AND regex->get_data( ) IS NOT INITIAL. FIND REGEX regex->get_data( ) IN excerpt-text SUBMATCHES m0 m1 ##REGEX_POSIX. IF sy-subrc = 0. ">>> apm CASE marker. WHEN '~'. IF m0+1(1) = ` `. CLEAR emphasis. ELSE. emphasis = 'sub'. ENDIF. WHEN '^'. IF m0+1(1) = ` `. CLEAR emphasis. ELSE. emphasis = 'sup'. ENDIF. WHEN OTHERS. emphasis = 'em'. ENDCASE. "<<< apm ENDIF. ENDIF. CHECK emphasis IS NOT INITIAL. result-extent = strlen( m0 ). result-element-name = emphasis. result-element-handler = 'line'. result-element-text-text = m1. ENDMETHOD. "inline_Emphasis METHOD inline_escapesequence. DATA ch TYPE c. CHECK strlen( excerpt-text ) > 1. ch = excerpt-text+1(1). IF special_characters->find_val( ch ) > 0. result-markup = excerpt-text+1(1). result-extent = 2. ENDIF. ENDMETHOD. "inline_EscapeSequence METHOD inline_highlight. DATA: m0 TYPE string, m1 TYPE string. CHECK strlen( excerpt-text ) > 1 AND excerpt-text+1(1) = '='. FIND REGEX '(^==(?=\S)([^(?:==)]+)(?=\S)==)' IN excerpt-text SUBMATCHES m0 m1 ##REGEX_POSIX. IF sy-subrc = 0. result-extent = strlen( m0 ). result-element-name = 'mark'. result-element-text-text = m1. result-element-handler = 'line'. ENDIF. ENDMETHOD. METHOD inline_image. DATA excerpt_ LIKE excerpt. DATA link LIKE result. FIELD-SYMBOLS: LIKE LINE OF result-element-attributes, LIKE LINE OF link-element-attributes. CHECK strlen( excerpt-text ) > 1 AND excerpt-text+1(1) = '['. excerpt_ = excerpt. excerpt_-text = excerpt_-text+1. link = inline_link( excerpt_ ). CHECK link IS NOT INITIAL. result-extent = link-extent + 1. result-element-name = 'img'. APPEND INITIAL LINE TO result-element-attributes ASSIGNING . -name = 'src'. READ TABLE link-element-attributes ASSIGNING WITH KEY name = 'href'. IF sy-subrc = 0. -value = _adjust_img_src( -value ). " apm DELETE link-element-attributes WHERE name = 'href'. ENDIF. APPEND INITIAL LINE TO result-element-attributes ASSIGNING . -name = 'alt'. -value = link-element-text-text. APPEND LINES OF link-element-attributes TO result-element-attributes. ENDMETHOD. "inline_Image METHOD inline_link. CONSTANTS c_regex_template TYPE string VALUE '\[((?:[^\]\[]|(?R))*)\]'. DATA: len TYPE i, regex TYPE string, remainder TYPE string, m0 TYPE string, m1 TYPE string, m2 TYPE string, definition TYPE string, ref_map TYPE REF TO lcl_hashmap, def_map TYPE REF TO lcl_hashmap, def_val TYPE REF TO lcl_string, exists TYPE abap_bool. FIELD-SYMBOLS LIKE LINE OF result-element-attributes. result-element-name = 'a'. result-element-handler = 'line'. remainder = excerpt-text. regex = |({ c_regex_template })|. DO 5 TIMES. "// regex recursion REPLACE '(?R)' IN regex WITH c_regex_template. ENDDO. REPLACE '(?R)' IN regex WITH '$'. FIND REGEX regex IN remainder SUBMATCHES m0 m1 ##REGEX_POSIX. IF sy-subrc = 0. result-element-text-text = m1. result-extent = strlen( m0 ). remainder = remainder+result-extent. ELSE. CLEAR result. RETURN. ENDIF. *^[(]\s*((?:[^ ()]+|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|''[^'']*''))?\s*[)] *^[(]\s*((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|''[^'']*''))?\s*[)] *^[(]((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|''[^'']*''))?[)] "FIND REGEX '(^[(]\s*((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|''[^\'']*''))?\s*[)])' FIND REGEX '(^[(]\s*((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|''[^\'']*''|\([^\)]*\)))?\s*[)])' IN remainder SUBMATCHES m0 m1 m2 ##REGEX_POSIX. " apm IF sy-subrc = 0. APPEND INITIAL LINE TO result-element-attributes ASSIGNING . -name = 'href'. -value = m1. IF m2 IS NOT INITIAL. APPEND INITIAL LINE TO result-element-attributes ASSIGNING . -name = 'title'. len = strlen( m2 ) - 2. -value = m2+1(len). ENDIF. len = strlen( m0 ). result-extent = result-extent + len. ELSE. FIND REGEX '(^\s*\[([^\]]*)\])' IN remainder SUBMATCHES m0 m1 ##REGEX_POSIX. IF sy-subrc = 0. IF m1 IS NOT INITIAL. definition = m1. ELSE. definition = result-element-text-text. ENDIF. len = strlen( m0 ). result-extent = result-extent + len. ELSE. definition = result-element-text-text. ENDIF. TRANSLATE definition TO LOWER CASE. ref_map ?= definition_data->get( 'Reference' ). exists = ref_map->exists( definition ). IF exists IS INITIAL. CLEAR result. RETURN. ENDIF. def_map ?= ref_map->get( definition ). def_val ?= def_map->get( 'url' ). APPEND INITIAL LINE TO result-element-attributes ASSIGNING . -name = 'href'. -value = _adjust_a_href( def_val->get_data( ) ). " apm exists = def_map->exists( 'title' ). IF exists IS NOT INITIAL. def_val ?= def_map->get( 'title' ). APPEND INITIAL LINE TO result-element-attributes ASSIGNING . -name = 'title'. -value = def_val->get_data( ). ENDIF. ENDIF. ENDMETHOD. "inline_Link METHOD inline_markup. DATA: m0 TYPE string, regex TYPE string. CHECK markup_escaped IS INITIAL AND safe_mode IS INITIAL AND excerpt-text CS '>' AND strlen( excerpt-text ) > 1. FIND REGEX '(^<\/\w*[ ]*>)' IN excerpt-text SUBMATCHES m0 ##REGEX_POSIX. IF sy-subrc <> 0. FIND REGEX '(^)' IN excerpt-text SUBMATCHES m0 ##REGEX_POSIX. IF sy-subrc <> 0. regex = '(^<\w*(?:[ ]*' && regex_html_attribute && ')*[ ]*\/?>)'. FIND REGEX regex IN excerpt-text SUBMATCHES m0 ##SUBRC_OK ##REGEX_POSIX. ENDIF. ENDIF. IF m0 IS NOT INITIAL. result-extent = strlen( m0 ). result-markup = _adjust_markup( m0 ). " apm ENDIF. ENDMETHOD. "inline_Markup METHOD inline_specialcharacter. DATA special TYPE string. CHECK excerpt-text IS NOT INITIAL. IF excerpt-text(1) = '&'. FIND REGEX '^&#?\w+;' IN excerpt-text ##REGEX_POSIX. IF sy-subrc <> 0. result-markup = '&'. result-extent = 1. RETURN. ENDIF. ENDIF. CASE excerpt-text(1). WHEN '>'. special = 'gt'. WHEN '<'. special = 'lt'. WHEN '"'. special = 'quot'. ENDCASE. IF special IS NOT INITIAL. CONCATENATE '&' special ';' INTO result-markup. result-extent = 1. ENDIF. ENDMETHOD. "inline_SpecialCharacter METHOD inline_strikethrough. DATA: m0 TYPE string, m1 TYPE string. CHECK strlen( excerpt-text ) > 1 AND excerpt-text+1(1) = '~'. FIND REGEX '(^~~(?=\S)([^(?:~~)]+)(?=\S)~~)' IN excerpt-text SUBMATCHES m0 m1 ##REGEX_POSIX. IF sy-subrc = 0. result-extent = strlen( m0 ). result-element-name = 'del'. result-element-text-text = m1. result-element-handler = 'line'. ENDIF. ENDMETHOD. "inline_Strikethrough METHOD inline_url. DATA: m0 TYPE string, offset TYPE i. FIELD-SYMBOLS LIKE LINE OF result-element-attributes. CHECK urls_linked IS NOT INITIAL AND strlen( excerpt-text ) > 2 AND excerpt-text+2(1) = '/'. FIND REGEX '(\bhttps?:[\/]{2}[^\s<]+\b\/*)' IN excerpt-context IGNORING CASE SUBMATCHES m0 MATCH OFFSET offset ##REGEX_POSIX. IF sy-subrc = 0. result-extent = strlen( m0 ). result-position = offset + 1. "// set to +1 so 0 is not initial result-element-name = 'a'. result-element-text-text = m0. APPEND INITIAL LINE TO result-element-attributes ASSIGNING . -name = 'href'. -value = m0. ENDIF. ENDMETHOD. "inline_Url METHOD inline_urltag. DATA: m0 TYPE string, m1 TYPE string, url TYPE string. FIELD-SYMBOLS LIKE LINE OF result-element-attributes. CHECK excerpt-text CS '>'. FIND REGEX '(^<(\w+:\/{2}[^ >]+)>)' IN excerpt-text SUBMATCHES m0 m1 ##REGEX_POSIX. IF sy-subrc = 0. url = m1. result-extent = strlen( m0 ). result-element-name = 'a'. result-element-text-text = url. APPEND INITIAL LINE TO result-element-attributes ASSIGNING . -name = 'href'. -value = url. ENDIF. ENDMETHOD. "inline_UrlTag METHOD li. DATA: trimmed_markup TYPE string, fdpos TYPE i, pos_to TYPE i. result = _lines( lines ). trimmed_markup = trim( result ). IF NOT line_exists( lines[ table_line = '' ] ) AND strlen( trimmed_markup ) >= 3 AND trimmed_markup(3) = '

    '. result = trimmed_markup+3. FIND '

    ' IN result MATCH OFFSET fdpos ##SUBRC_OK. pos_to = fdpos + 4. CONCATENATE result(fdpos) result+pos_to INTO result. ENDIF. ">>> apm " Task lists IF result CP '[ ]*'. result = |{ result+3 }|. ELSEIF result CP '[X]*'. result = |{ result+3 }|. ENDIF. "<<< apm ENDMETHOD. "li METHOD line. DATA: text TYPE string, unmarked_text TYPE string, marker_position TYPE i, pos TYPE i, excerpt TYPE ty_excerpt, inline TYPE ty_inline, marker TYPE c, inline_types_sa TYPE REF TO lcl_string_array, method_name TYPE string, markup_part TYPE string, continue_loop TYPE abap_bool. FIELD-SYMBOLS TYPE string. "inline_types_sa->data. " text contains the unexamined text " excerpt-text is based on the first occurrence of a marker text = element-text. WHILE text IS NOT INITIAL. IF text NA inline_marker_list. EXIT. ENDIF. excerpt-text = text+sy-fdpos. marker = excerpt-text(1). FIND marker IN text MATCH OFFSET marker_position ##SUBRC_OK. excerpt-context = text. inline_types_sa ?= inline_types->get( marker ). CLEAR continue_loop. LOOP AT inline_types_sa->get_data( ) ASSIGNING . CONCATENATE 'inline_' INTO method_name. TRANSLATE method_name TO UPPER CASE. CALL METHOD (method_name) EXPORTING excerpt = excerpt RECEIVING result = inline. " makes sure that the inline belongs to "our" marker CHECK inline IS NOT INITIAL. CHECK inline-position <= marker_position. " sets a default inline position IF inline-position IS INITIAL. inline-position = marker_position. ELSE. inline-position = inline-position - 1. ENDIF. " the text that comes before the inline IF strlen( text ) >= inline-position. unmarked_text = text(inline-position). ELSE. unmarked_text = text. ENDIF. " compile the unmarked text markup_part = unmarked_text( unmarked_text ). CONCATENATE result markup_part INTO result. " compile the inline IF inline-markup IS NOT INITIAL. CONCATENATE result inline-markup INTO result. ELSE. markup_part = element( inline-element ). CONCATENATE result markup_part INTO result. ENDIF. " remove the examined text pos = inline-position + inline-extent. IF strlen( text ) >= pos. text = text+pos. ELSE. CLEAR text. ENDIF. continue_loop = abap_true. EXIT. ENDLOOP. "inline_types->data CHECK continue_loop IS INITIAL. " the marker does not belong to an inline marker_position = marker_position + 1. IF strlen( text ) >= marker_position. unmarked_text = text(marker_position). ELSE. unmarked_text = text. ENDIF. markup_part = unmarked_text( unmarked_text ). CONCATENATE result markup_part INTO result. IF strlen( text ) >= marker_position. text = text+marker_position. ELSE. CLEAR text. ENDIF. ENDWHILE. markup_part = unmarked_text( text ). CONCATENATE result markup_part INTO result. ENDMETHOD. "line METHOD magic_move. "! " Magic move-corresponding " Recursively handles any kind of structures "! DATA: td_from TYPE REF TO cl_abap_typedescr, td_to TYPE REF TO cl_abap_typedescr, sd_from TYPE REF TO cl_abap_structdescr, sd_to TYPE REF TO cl_abap_structdescr. FIELD-SYMBOLS: TYPE table, TYPE table, TYPE any, TYPE any, LIKE LINE OF sd_from->components, LIKE LINE OF sd_to->components. td_from = cl_abap_typedescr=>describe_by_data( from ). td_to = cl_abap_typedescr=>describe_by_data( to ). IF td_from->absolute_name = td_to->absolute_name. to = from. RETURN. ENDIF. "// Scenario 1 => simple to simple IF td_from->kind = td_to->kind AND td_from->kind = cl_abap_typedescr=>kind_elem. to = from. "// Scenario 2 => struct to struct ELSEIF td_from->kind = td_to->kind AND td_from->kind = cl_abap_typedescr=>kind_struct. sd_from ?= td_from. sd_to ?= td_to. LOOP AT sd_from->components ASSIGNING . READ TABLE sd_to->components ASSIGNING WITH KEY name = -name. CHECK sy-subrc = 0. IF -type_kind = cl_abap_typedescr=>typekind_table. ASSIGN COMPONENT -name OF STRUCTURE from TO . ASSERT sy-subrc = 0. ASSIGN COMPONENT -name OF STRUCTURE to TO . ASSERT sy-subrc = 0. LOOP AT ASSIGNING . APPEND INITIAL LINE TO ASSIGNING . magic_move( EXPORTING from = name = -name CHANGING to = ). ENDLOOP. ELSE. ASSIGN COMPONENT -name OF STRUCTURE from TO . ASSERT sy-subrc = 0. ASSIGN COMPONENT -name OF STRUCTURE to TO . ASSERT sy-subrc = 0. magic_move( EXPORTING from = name = -name CHANGING to = ). ENDIF. ENDLOOP. "// Scenario 3 => simple to struct ELSEIF td_from->kind = cl_abap_typedescr=>kind_elem AND td_to->kind = cl_abap_typedescr=>kind_struct AND name IS NOT INITIAL. sd_to ?= td_to. READ TABLE sd_to->components ASSIGNING WITH KEY name = name. IF sy-subrc = 0 AND -type_kind <> cl_abap_typedescr=>typekind_table. ASSIGN COMPONENT -name OF STRUCTURE to TO . ASSERT sy-subrc = 0. magic_move( EXPORTING from = from name = -name CHANGING to = ). ENDIF. "// Scenario 4 => struct to simple ELSEIF td_from->kind = cl_abap_typedescr=>kind_struct AND td_to->kind = cl_abap_typedescr=>kind_elem AND name IS NOT INITIAL. sd_from ?= td_from. READ TABLE sd_from->components ASSIGNING WITH KEY name = name. IF sy-subrc = 0 AND -type_kind <> cl_abap_typedescr=>typekind_table. ASSIGN COMPONENT -name OF STRUCTURE to TO . ASSERT sy-subrc = 0. magic_move( EXPORTING from = name = -name CHANGING to = to ). ENDIF. ENDIF. ENDMETHOD. "magic_move METHOD match_marked_string. "! " Workaround for an ungreedy regex match " Specific for regex matches with a delimiting marker "! CONSTANTS: c_regex TYPE string VALUE '(^{&X}[ ]*(.+)[ ]*{&X}(?!{&1}))', c_regex_delim TYPE string VALUE '[^{&1}]{&X}(?!{&1})'. DATA: marker_ptn TYPE string, submarker_ptn TYPE string, regex TYPE string, regex_delim TYPE string, offset TYPE i. CLEAR: m0, m1, not_found. marker_ptn = marker. REPLACE ALL OCCURRENCES OF REGEX '([*?!+])' IN marker_ptn WITH '[$1]' ##REGEX_POSIX. submarker_ptn = marker(1). REPLACE ALL OCCURRENCES OF REGEX '([*?!+])' IN submarker_ptn WITH '[$1]' ##REGEX_POSIX. regex = c_regex. REPLACE ALL OCCURRENCES OF '{&1}' IN regex WITH submarker_ptn ##REGEX_POSIX. REPLACE ALL OCCURRENCES OF '{&X}' IN regex WITH marker_ptn ##REGEX_POSIX. regex_delim = c_regex_delim. REPLACE ALL OCCURRENCES OF '{&1}' IN regex_delim WITH submarker_ptn ##REGEX_POSIX. REPLACE ALL OCCURRENCES OF '{&X}' IN regex_delim WITH marker_ptn ##REGEX_POSIX. FIND REGEX regex IN subject SUBMATCHES m0 m1 ##REGEX_POSIX. IF sy-subrc = 0. FIND REGEX regex_delim IN m1 MATCH OFFSET offset ##REGEX_POSIX. IF sy-subrc = 0. offset = offset + 1. m1 = m1(offset). offset = strlen( m1 ) + ( strlen( marker ) * 2 ). m0 = m0(offset). ENDIF. not_found = abap_false. ELSE. not_found = abap_true. ENDIF. ENDMETHOD. METHOD paragraph. result-element-name = 'p'. result-element-text-text = line-text. result-element-handler = 'line'. ENDMETHOD. "paragraph METHOD sanitise_element. CONSTANTS c_good_attribute TYPE string VALUE '^[a-zA-Z0-9][a-zA-Z0-9_-]*$'. FIELD-SYMBOLS LIKE LINE OF result-attributes. result = element. CASE result-name. WHEN 'a'. result = filter_unsafe_url_in_attribute( element = result attribute = 'href' ). WHEN 'img'. result = filter_unsafe_url_in_attribute( element = result attribute = 'src' ). ENDCASE. LOOP AT result-attributes ASSIGNING . " filter out badly parsed attribute FIND REGEX c_good_attribute IN -name ##REGEX_POSIX. IF sy-subrc <> 0. DELETE TABLE result-attributes FROM . CONTINUE. ENDIF. " dump onevent attribute IF string_at_start( haystack = -name needle = 'on' ) = abap_true. DELETE TABLE result-attributes FROM . CONTINUE. ENDIF. ENDLOOP. ENDMETHOD. METHOD set_breaks_enabled. breaks_enabled = breaks_enabled. result = me. ENDMETHOD. "set_breaks_enabled METHOD set_markup_escaped. markup_escaped = markup_escaped. result = me. ENDMETHOD. "set_markup_escaped METHOD set_safe_mode. safe_mode = iv_safe_mode. result = me. ENDMETHOD. METHOD set_urls_linked. urls_linked = urls_linked. result = me. ENDMETHOD. "set_urls_linked METHOD string_at_start. DATA len TYPE i. len = strlen( needle ). IF strlen( haystack ) < len. result = abap_false. ELSE. result = xsdbool( to_lower( haystack+0(len) ) = needle ). ENDIF. ENDMETHOD. METHOD styles. result = |.markdown\n| && |\{ background-color: #f2f2f2; padding: 15px; \}\n| && |.markdown .logo\n| && |\{ width: 36px; height: 22px; margin-top: -4px; \}\n| && |.markdown .header,\n| && |.markdown .content\n| && |\{ background-color: #ffffff; border: 1px solid #d8dee4; display: block; \}\n| && |.markdown .header\n| && |\{ font-size: larger; margin-bottom: 15px; padding: 15px; \}\n| && |.markdown .content\n| && |\{ padding: 25px; \}\n| && |.markdown .html\n| && |\{ max-width: 1024px; margin: 0 auto; padding: 25px; \}\n| " Markdown View && |.markdown .source\n| && |\{ font-family: Consolas,Courier,monospace; font-size: 12pt; padding: 25px;\n| && | max-width: 1024px; margin: 0 auto; \}\n| && |.markdown .source table\n| && |\{ border: 1px solid #d8dee4; \}\n| && |.markdown .source td\n| && |\{ border-top: 0px; border-bottom: 0px; padding-top: 0; padding-bottom: 0;\n| && | line-height: 20px; vertical-align: top; \}\n| " Syntax Highlight && |.markdown .syntax-hl .heading\n| && |\{ color: blue; \}\n| && |.markdown .syntax-hl .link\n| && |\{ color: purple; \}\n| && |.markdown .syntax-hl .url\n| && |\{ color: green; \}\n| && |.markdown .syntax-hl .html\n| && |\{ padding: 0; \}\n| && |.markdown .syntax-hl .bold\n| && |\{ font-weight: bold; \}\n| " HTML Tags && |.markdown h1,\n| && |.markdown h2\n| && |\{ border-bottom: 1px solid #d8dee4; box-sizing: border-box; \}\n| && |.markdown img\n| && |\{ border: 0; box-sizing: border-box; max-width: 100%; vertical-align: middle; \}\n| && |.markdown p,\n| && |.markdown li\n| && |\{ line-height: 1.5; \}\n| && |.markdown table\n| && |\{ border: 1px solid #ddd; border-radius: 3px; \}\n| && |.markdown th,\n| && |\{ color: #4078c0; background-color: #edf2f9; border-bottom-color: #ddd; \}\n| && |.markdown th,\n| && |.markdown td\n| && |\{ border: 1px solid #ddd; padding: 6px 13px; \}\n| && |.markdown tr:first-child td\n| && |\{ border-top: 0; \}\n| && |.markdown hr\n| && |\{ background-color: #eee; margin: 24px 0; overflow: hidden; padding: 0; \}\n| && |.markdown mark\n| && |\{ background-color: #fff8e0; border-radius: 6px; margin: 0; padding: .2em .4em; \}\n| && |.markdown blockquote\n| && |\{ background-color: #eee; border-left: 3px solid #303d36; border-radius: 6px;\n| && | margin: 0 0 16px; padding: 1px 1em; \}\n| " GitHub Alerts && |.markdown blockquote.alert-note\n| && |\{ border-left: 3px solid #4493f8; \}\n| && |.markdown blockquote.alert-tip\n| && |\{ border-left: 3px solid #3fb950; \}\n| && |.markdown blockquote.alert-important\n| && |\{ border-left: 3px solid #ab7df8; \}\n| && |.markdown blockquote.alert-warning\n| && |\{ border-left: 3px solid #d29922; \}\n| && |.markdown blockquote.alert-caution\n| && |\{ border-left: 3px solid #f85149; \}\n| " Code blocks && |.markdown pre\n| && |\{ background-color: #eee; border-radius: 6px; display: block;\n| && | margin-bottom: 16px; margin-top: 0; overflow: auto; overflow-wrap: normal;\n| && | padding: 16px; word-break: normal; box-sizing: border-box;\n| && | font-family: Consolas, Courier, monospace; font-size: 14px; \}\n| && |.markdown p code\n| && |\{ background-color: #eee; border-radius: 6px; margin: 0; padding: .2em .4em;\n| && | font-family: Consolas, Courier, monospace; font-size: 14px; \}\n| && |.markdown pre code\n| && |\{ background-color: transparent; border-style: initial;\n| && | border-width: 0; box-sizing: border-box; display: inline; margin: 0;\n| && | overflow: visible; word-break: normal; overflow-wrap: normal;\n| && | padding: 0; white-space: pre;\n| && | font-family: Consolas, Courier, monospace; font-size: 14px; \}\n| && |kbd \{\n| && | border: 1px solid rgba(61, 68, 77, .7);\n| && | border-radius: 6px;\n| && | box-shadow: inset 0 -1px 0 var(--borderColor-neutral-muted, var(--color-neutral-muted));\n| && | display: inline-block;\n| && | font-family: Consolas, Courier, monospace;\n| && | font-kerning: auto;\n| && | font-optical-sizing: auto;\n| && | font-size: 11px;\n| && | font-size-adjust: none;\n| && | font-variant: normal;\n| && | font-variant-emoji: normal;\n| && | font-weight: 400;\n| && | line-height: 10px;\n| && | padding: 4px;\n| && | vertical-align: middle;\}\n|. ENDMETHOD. METHOD syntax_highlighter. ">>> apm DATA: language TYPE string, current_element TYPE ty_element. FIELD-SYMBOLS LIKE LINE OF current_element-attributes. magic_move( EXPORTING from = element CHANGING to = current_element ). READ TABLE current_element-attributes ASSIGNING WITH TABLE KEY name = 'class'. IF sy-subrc = 0 AND -value CP 'language-*'. language = -value+9(*). result = /apmg/cl_apm_markdown_syn=>process( source = current_element-text-text language = language ). ELSE. IF current_element-lines IS NOT INITIAL. CONCATENATE LINES OF current_element-lines INTO result SEPARATED BY %_newline. ELSE. result = current_element-text-text. result = _escape( text = result allow_quotes = abap_true ). ENDIF. ENDIF. "<<< apm ENDMETHOD. METHOD text. " Parses the markdown text and returns the markup DATA: lines TYPE string_table, alert TYPE lcl_alerts=>ty_alert, alert_html TYPE string. " make sure no definitions are set definition_data = NEW #( value_type = 'lcl_hashmap:lcl_hashmap' ). " standardize line breaks REPLACE ALL OCCURRENCES OF REGEX '\r?\n' IN text WITH %_newline ##REGEX_POSIX. " remove surrounding line breaks text = trim( str = text mask = '\n' ). " split text into lines SPLIT text AT %_newline INTO TABLE lines. " iterate through lines to identify blocks markup = _lines( lines ). " trim line breaks markup = trim( str = markup mask = '\n' ). " >>> apm DO 5 TIMES. CASE sy-index. WHEN 1. alert = lcl_alerts=>get( '[!NOTE]' ). WHEN 2. alert = lcl_alerts=>get( '[!TIP]' ). WHEN 3. alert = lcl_alerts=>get( '[!IMPORTANT]' ). WHEN 4. alert = lcl_alerts=>get( '[!WARNING]' ). WHEN 5. alert = lcl_alerts=>get( '[!CAUTION]' ). ENDCASE. alert_html = || && |{ alert-icon }  | && |{ alert-text }

    |. markup = replace( val = markup sub = alert-tag with = alert_html occ = 0 ). ENDDO. " <<< apm ENDMETHOD. "text METHOD trim. DATA regex TYPE string. result = str. regex = mask. REPLACE ALL OCCURRENCES OF REGEX '([\.\?\*\+\|])' IN regex WITH '\\$1' ##REGEX_POSIX. CONCATENATE '(\A[' regex ']*)|([' regex ']*\Z)' INTO regex. REPLACE ALL OCCURRENCES OF REGEX regex IN result WITH '' ##REGEX_POSIX. ENDMETHOD. "trim METHOD unmarked_text. DATA break TYPE string. CONCATENATE '
    ' %_newline INTO break. result = text. IF breaks_enabled IS NOT INITIAL. REPLACE ALL OCCURRENCES OF REGEX '[ ]*\n' IN result WITH break ##REGEX_POSIX. ELSE. REPLACE ALL OCCURRENCES OF REGEX '(?:[ ][ ]+|[ ]*\\)\n' IN result WITH break ##REGEX_POSIX. REPLACE ALL OCCURRENCES OF REGEX ' \n' IN result WITH %_newline ##REGEX_POSIX. ENDIF. ENDMETHOD. "unmarked_text METHOD _adjust_a_href. ">>> apm result = _adjust_link( root = config-root_href source = source ). " Open external links in new browser window IF config-sapevent = abap_true AND result CP 'http*'. result = 'sapevent:url?url=' && result. ENDIF. "<<< apm ENDMETHOD. METHOD _adjust_img_src. ">>> apm result = _adjust_link( root = config-root_img source = source ). "<<< apm ENDMETHOD. METHOD _adjust_link. ">>> apm result = source. CHECK root IS NOT INITIAL AND source IS NOT INITIAL AND source NP 'http*' AND source(1) <> '#'. IF result CP '/*'. result = source. ELSEIF result CP './*'. result = config-path && source+2. ELSE. result = config-path && source. ENDIF. result = root && config-path_util->normalize( result ). "<<< apm ENDMETHOD. METHOD _adjust_markup. ">>> apm DATA: matches TYPE match_result_tab, href TYPE string, url TYPE string. FIELD-SYMBOLS: LIKE LINE OF matches, LIKE LINE OF -submatches. result = source. FIND ALL OCCURRENCES OF REGEX 'href\s*=\s*"([^"]*)"' IN result RESULTS matches ##SUBRC_OK ##REGEX_POSIX. SORT matches DESCENDING BY line DESCENDING offset. LOOP AT matches ASSIGNING . READ TABLE -submatches ASSIGNING INDEX 1. ASSERT sy-subrc = 0. href = result+-offset(-length). url = _adjust_a_href( href ). REPLACE href IN result WITH url. ENDLOOP. CLEAR matches. FIND ALL OCCURRENCES OF REGEX 'src\s*=\s*"([^"]*)"' IN result RESULTS matches ##SUBRC_OK ##REGEX_POSIX. SORT matches DESCENDING BY line DESCENDING offset. LOOP AT matches ASSIGNING . READ TABLE -submatches ASSIGNING INDEX 1. ASSERT sy-subrc = 0. href = result+-offset(-length). url = _adjust_img_src( href ). REPLACE href IN result WITH url. ENDLOOP. "<<< apm ENDMETHOD. METHOD _escape. result = htmlspecialchars( input = text ent_html401 = abap_true ent_noquotes = allow_quotes ent_quotes = boolc( allow_quotes IS INITIAL ) ). ENDMETHOD. METHOD _lines. DATA: current_block TYPE ty_block, line TYPE string, chopped_line TYPE string, parts TYPE string_table, shortage TYPE i, spaces TYPE string, indent TYPE i, text TYPE string, continue_to_next_line TYPE abap_bool, current_line TYPE ty_line, method_name TYPE string, block TYPE ty_block, marker TYPE string, ref_block_types TYPE REF TO lcl_string_array, ref_sa TYPE REF TO lcl_string_array, blocks TYPE STANDARD TABLE OF ty_block WITH EMPTY KEY, block_markup TYPE string. FIELD-SYMBOLS: LIKE LINE OF blocks, TYPE string, TYPE lcl_hashmap=>ty_item, LIKE LINE OF parts. LOOP AT lines INTO line. chopped_line = line. REPLACE REGEX '\s+$' IN chopped_line WITH '' ##REGEX_POSIX. IF strlen( chopped_line ) = 0. current_block-interrupted = abap_true. CONTINUE. ENDIF. IF line CS %_horizontal_tab. SPLIT line AT %_horizontal_tab INTO TABLE parts. LOOP AT parts ASSIGNING . AT FIRST. line = . CONTINUE. ENDAT. shortage = 4 - ( strlen( line ) MOD 4 ). CLEAR spaces. DO shortage TIMES. CONCATENATE spaces space INTO spaces RESPECTING BLANKS. ENDDO. CONCATENATE line spaces INTO line RESPECTING BLANKS. ENDLOOP. "lt_parts ENDIF. CLEAR spaces. FIND REGEX '^(\s+)' IN line SUBMATCHES spaces ##SUBRC_OK ##REGEX_POSIX. indent = strlen( spaces ). IF indent > 0. text = line+indent. ELSE. text = line. ENDIF. " ~ CLEAR current_line. current_line-body = line. current_line-indent = indent. current_line-text = text. " ~ IF current_block-continuable IS NOT INITIAL. CLEAR block. CONCATENATE 'block_' current_block-type '_continue' INTO method_name. TRANSLATE method_name TO UPPER CASE. CALL METHOD (method_name) EXPORTING line = current_line block = current_block RECEIVING result = block. IF block IS NOT INITIAL. current_block = block. CONTINUE. ELSE. CONCATENATE 'block_' current_block-type '_complete' INTO method_name. TRANSLATE method_name TO UPPER CASE. IF line_exists( methods[ table_line = method_name ] ). CALL METHOD (method_name) EXPORTING block = current_block RECEIVING result = current_block. ENDIF. ENDIF. "ls_block is not initial. CLEAR current_block-continuable. ENDIF. "ls_current_block-continuable is not initial. " ~ marker = text(1). " ~ ref_block_types = NEW #( ). ref_block_types->lif_value_type~copy( unmarked_block_types ). DATA(block_types_data) = block_types->get_data( ). READ TABLE block_types_data ASSIGNING WITH KEY key = marker. IF sy-subrc = 0. ref_sa ?= -value. ref_block_types->append_array( ref_sa ). ENDIF. " ~ LOOP AT ref_block_types->get_data( ) ASSIGNING . CLEAR block. CONCATENATE 'block_' INTO method_name. TRANSLATE method_name TO UPPER CASE. CALL METHOD (method_name) EXPORTING line = current_line block = current_block RECEIVING result = block. IF block IS NOT INITIAL. block-type = . IF block-identified IS INITIAL. APPEND current_block TO blocks. block-identified = abap_true. ENDIF. CONCATENATE 'block_' '_continue' INTO method_name. TRANSLATE method_name TO UPPER CASE. IF line_exists( methods[ table_line = method_name ] ). block-continuable = abap_true. ENDIF. current_block = block. continue_to_next_line = abap_true. EXIT. ENDIF. ENDLOOP. "ref_block_types->data IF continue_to_next_line IS NOT INITIAL. CLEAR continue_to_next_line. CONTINUE. ENDIF. " ~ IF current_block IS NOT INITIAL AND current_block-type IS INITIAL AND current_block-interrupted IS INITIAL. CONCATENATE current_block-element-text-text %_newline text INTO current_block-element-text-text. ELSE. APPEND current_block TO blocks. current_block = paragraph( current_line ). current_block-identified = abap_true. ENDIF. ENDLOOP. "lines " ~ IF current_block-continuable IS NOT INITIAL. CONCATENATE 'block_' current_block-type '_complete' INTO method_name. TRANSLATE method_name TO UPPER CASE. IF line_exists( methods[ table_line = method_name ] ). CALL METHOD (method_name) EXPORTING block = current_block RECEIVING result = current_block. ENDIF. ENDIF. APPEND current_block TO blocks. DELETE blocks INDEX 1. " ~ LOOP AT blocks ASSIGNING . CHECK -hidden IS INITIAL. IF -markup IS NOT INITIAL. block_markup = -markup. ELSE. block_markup = element( -element ). ENDIF. CONCATENATE result %_newline block_markup INTO result RESPECTING BLANKS. ENDLOOP. CONCATENATE result %_newline INTO result RESPECTING BLANKS. ENDMETHOD. "lines ENDCLASS. CLASS /apmg/cl_apm_markdown_path IMPLEMENTATION. METHOD char_at. result = substring( val = val off = off len = 1 ). ENDMETHOD. METHOD last_index_of. result = find( val = val sub = sub occ = -1 ). ENDMETHOD. METHOD normalize. DATA: is_absolute TYPE abap_bool, trailing_separator TYPE abap_bool, allow_above_root TYPE abap_bool. result = path. IF result IS INITIAL. result = '.'. RETURN. ENDIF. is_absolute = xsdbool( substring( val = result len = 1 ) = c_slash ). trailing_separator = xsdbool( substring( val = reverse( result ) len = 1 ) = c_slash ). allow_above_root = xsdbool( is_absolute = abap_false ). result = posix_normalize( path = result allow_above_root = allow_above_root ). IF result IS INITIAL AND is_absolute = abap_false. result = '.'. ENDIF. IF result IS NOT INITIAL AND trailing_separator = abap_true. result = result && '/'. ENDIF. IF is_absolute = abap_true. result = '/' && result. ENDIF. ENDMETHOD. METHOD posix_normalize. DATA: out TYPE string, out_tab TYPE string_table, last_segment_length TYPE i, last_slash_index TYPE i, last_slash TYPE i VALUE -1, dots TYPE i, code TYPE c LENGTH 1, i TYPE i. DO strlen( path ) + 1 TIMES. IF strlen( path ) > i. code = char_at( val = path off = i ). ELSEIF code = c_slash. EXIT. ELSE. code = c_slash. ENDIF. out = |{ i } { result }|. INSERT out INTO TABLE out_tab. IF code = c_slash. IF i - 1 = last_slash OR dots = 1. ASSERT 0 = 0. " NOP ELSEIF i - 1 <> last_slash AND dots = 2. IF strlen( result ) < 2 OR last_segment_length <> 2 OR char_at( val = result off = strlen( result ) - 1 ) <> c_dot OR char_at( val = result off = strlen( result ) - 2 ) <> c_dot. IF strlen( result ) > 2. last_slash_index = last_index_of( val = result sub = c_slash ). IF strlen( result ) - 1 <> last_slash_index. out = |{ i } { result } #1|. INSERT out INTO TABLE out_tab. IF last_slash_index = -1. result = ''. last_segment_length = 0. ELSE. result = slice( val = result start = 0 end = last_slash_index ). last_segment_length = strlen( result ) - 1 - last_index_of( val = result sub = c_slash ). ENDIF. last_slash = i. dots = 0. i = i + 1. CONTINUE. ENDIF. ELSEIF strlen( result ) = 2 OR strlen( result ) = 1. result = ''. last_segment_length = 0. last_slash = i. dots = 0. i = i + 1. CONTINUE. ENDIF. ENDIF. IF allow_above_root = abap_true. out = |{ i } { result } #3|. INSERT out INTO TABLE out_tab. IF strlen( result ) > 0. result = result && c_slash && c_dot && c_dot. ELSE. result = c_dot && c_dot. ENDIF. last_segment_length = 2. ENDIF. ELSE. out = |{ i } { result } #2|. INSERT out INTO TABLE out_tab. IF strlen( result ) > 0. result = result && c_slash && slice( val = path start = last_slash + 1 end = i ). ELSE. result = slice( val = path start = last_slash + 1 end = i ). ENDIF. last_segment_length = i - last_slash - 1. ENDIF. last_slash = i. dots = 0. ELSEIF code = c_dot AND dots <> -1. dots = dots + 1. ELSE. dots = -1. ENDIF. i = i + 1. ENDDO. ENDMETHOD. METHOD slice. IF end <= start. result = ''. ELSEIF end >= 0. result = substring( val = val off = start len = end - start ). ELSE. result = substring( val = val off = start len = strlen( val ) - end - start ). ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_markdown_syn IMPLEMENTATION. METHOD create. result = /apmg/cl_apm_highlighter_facto=>create( |.{ language }| ). IF result IS INITIAL. result = /apmg/cl_apm_highlighter_facto=>create( |.txt| ). ENDIF. ENDMETHOD. METHOD process. DATA: line TYPE string, lines TYPE string_table. highlighter = create( language ). SPLIT source AT cl_abap_char_utilities=>newline INTO TABLE lines. LOOP AT lines INTO line. IF result IS NOT INITIAL. result = result && cl_abap_char_utilities=>newline. ENDIF. result = result && highlighter->process_line( line ). ENDLOOP. ENDMETHOD. METHOD process_line. IF highlighter IS INITIAL OR language <> current_language. current_language = language. highlighter = create( language ). ENDIF. IF highlighter IS BOUND. result = highlighter->process_line( line ). ENDIF. ENDMETHOD. ENDCLASS. CLASS ZCL_ABAPGIT_OO_BASE IMPLEMENTATION. METHOD convert_attrib_to_vseoattrib. FIELD-SYMBOLS: LIKE LINE OF it_attributes, LIKE LINE OF rt_vseoattrib. LOOP AT it_attributes ASSIGNING . INSERT INITIAL LINE INTO TABLE rt_vseoattrib ASSIGNING . MOVE-CORRESPONDING TO . -clsname = iv_clsname. -state = seoc_state_implemented. -exposure = -exposure. UNASSIGN . ENDLOOP. UNASSIGN . ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~add_to_activation_list. zcl_abapgit_objects_activation=>add_item( is_item ). ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~create. ASSERT 0 = 1. "Subclass responsibility ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~create_documentation. CALL FUNCTION 'DOCU_UPD' EXPORTING id = iv_id langu = iv_language object = iv_object_name no_masterlang = iv_no_masterlang state = c_docu_state_active TABLES line = it_lines EXCEPTIONS ret_code = 1 OTHERS = 2. IF sy-subrc <> 0. zcx_abapgit_exception=>raise_t100( ). ENDIF. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~create_sotr. ASSERT 0 = 1. "Subclass responsibility ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~delete. ASSERT 0 = 1. "Subclass responsibility ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~delete_documentation. CALL FUNCTION 'DOCU_DEL' EXPORTING id = iv_id langu = iv_language object = iv_object_name typ = 'E' EXCEPTIONS ret_code = 1 OTHERS = 2. IF sy-subrc <> 0. zcx_abapgit_exception=>raise( 'Error from DOCU_DEL' ). ENDIF. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~deserialize_source. ASSERT 0 = 1. "Subclass responsibility ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~exists. ASSERT 0 = 1. "Subclass responsibility ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~generate_locals. ASSERT 0 = 1. "Subclass responsibility ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~get_class_properties. ASSERT 0 = 1. "Subclass responsibility ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~get_includes. ASSERT 0 = 1. "Subclass responsibility ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~get_interface_properties. ASSERT 0 = 1. "Subclass responsibility ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~get_skip_test_classes. rv_skip = mv_skip_test_classes. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~insert_text_pool. ASSERT 0 = 1. "Subclass responsibility ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~read_attributes. SELECT cmpname attbusobj attkeyfld exposure FROM seocompodf INTO CORRESPONDING FIELDS OF TABLE rt_attributes WHERE clsname = iv_object_name AND ( attbusobj <> space OR attkeyfld <> space ) AND version = '1' ORDER BY PRIMARY KEY. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~read_descriptions_class. FIELD-SYMBOLS LIKE LINE OF rt_descriptions. " Only translations i.e. not the main language SELECT * FROM seoclasstx INTO TABLE rt_descriptions WHERE clsname = iv_object_name AND langu <> iv_language AND descript <> '' ORDER BY PRIMARY KEY. "#EC CI_SUBRC LOOP AT rt_descriptions ASSIGNING . CLEAR -clsname. ENDLOOP. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~read_descriptions_compo. FIELD-SYMBOLS LIKE LINE OF rt_descriptions. IF iv_language IS INITIAL. " load all languages SELECT * FROM seocompotx INTO TABLE rt_descriptions WHERE clsname = iv_object_name AND descript <> '' ORDER BY PRIMARY KEY. "#EC CI_SUBRC ELSE. " load main language SELECT * FROM seocompotx INTO TABLE rt_descriptions WHERE clsname = iv_object_name AND langu = iv_language AND descript <> '' ORDER BY PRIMARY KEY. "#EC CI_SUBRC ENDIF. LOOP AT rt_descriptions ASSIGNING . CLEAR -clsname. ENDLOOP. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~read_descriptions_subco. FIELD-SYMBOLS LIKE LINE OF rt_descriptions. IF iv_language IS INITIAL. " load all languages SELECT * FROM seosubcotx INTO TABLE rt_descriptions WHERE clsname = iv_object_name AND descript <> '' ORDER BY PRIMARY KEY. "#EC CI_SUBRC ELSE. " load main language SELECT * FROM seosubcotx INTO TABLE rt_descriptions WHERE clsname = iv_object_name AND langu = iv_language AND descript <> '' ORDER BY PRIMARY KEY. "#EC CI_SUBRC ENDIF. LOOP AT rt_descriptions ASSIGNING . CLEAR -clsname. ENDLOOP. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~read_documentation. DATA: lv_state TYPE dokstate, lt_lines TYPE tlinetab. CALL FUNCTION 'DOCU_GET' EXPORTING id = iv_id langu = iv_language object = iv_object_name version_active_or_last = space " retrieve active version IMPORTING dokstate = lv_state TABLES line = lt_lines EXCEPTIONS no_docu_on_screen = 1 no_docu_self_def = 2 no_docu_temp = 3 ret_code = 4 OTHERS = 5. IF sy-subrc = 0 AND lv_state = c_docu_state_active. rt_lines = lt_lines. ELSE. CLEAR rt_lines. ENDIF. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~read_sotr. ASSERT 0 = 1. "Subclass responsibility ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~read_superclass. SELECT SINGLE refclsname FROM vseoextend INTO rv_superclass WHERE clsname = iv_classname. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~read_text_pool. ASSERT 0 = 1. "Subclass responsibility ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~serialize_abap. DATA lo_oo_serializer TYPE REF TO zcl_abapgit_oo_serializer. CREATE OBJECT lo_oo_serializer. CASE iv_type. WHEN seop_ext_class_locals_def. rt_source = lo_oo_serializer->serialize_locals_def( is_class_key ). WHEN seop_ext_class_locals_imp. rt_source = lo_oo_serializer->serialize_locals_imp( is_class_key ). WHEN seop_ext_class_macros. rt_source = lo_oo_serializer->serialize_macros( is_class_key ). WHEN seop_ext_class_testclasses. rt_source = lo_oo_serializer->serialize_testclasses( is_class_key ). mv_skip_test_classes = lo_oo_serializer->are_test_classes_skipped( ). WHEN OTHERS. rt_source = lo_oo_serializer->serialize_abap_clif_source( is_class_key ). ENDCASE. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~syntax_check. ASSERT 0 = 1. "Subclass responsibility ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~update_descriptions_class. DATA lt_descriptions LIKE it_descriptions. DATA ls_description LIKE LINE OF it_descriptions. " Make sure we keep main language SELECT * FROM seoclasstx INTO TABLE lt_descriptions WHERE clsname = is_key-clsname AND langu = iv_language ORDER BY PRIMARY KEY. LOOP AT it_descriptions INTO ls_description WHERE langu <> iv_language. ls_description-clsname = is_key-clsname. INSERT ls_description INTO TABLE lt_descriptions. ENDLOOP. DELETE FROM seoclasstx WHERE clsname = is_key-clsname. "#EC CI_SUBRC INSERT seoclasstx FROM TABLE lt_descriptions. "#EC CI_SUBRC ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~update_descriptions_compo. DATA lt_descriptions LIKE it_descriptions. DATA lt_components TYPE seo_components. DATA ls_description LIKE LINE OF it_descriptions. DATA lv_lang TYPE tadir-masterlang. FIELD-SYMBOLS LIKE LINE OF it_descriptions. FIELD-SYMBOLS TYPE vseocompdf. lt_descriptions = it_descriptions. LOOP AT lt_descriptions ASSIGNING . -clsname = is_key-clsname. ENDLOOP. " make sure to not damage VSEO* views by deleting texts of all components - an empty text must be kept!! SELECT * FROM vseocompdf INTO TABLE lt_components WHERE clsname = is_key-clsname AND version <> seoc_version_deleted AND state = seoc_state_implemented AND alias = seox_false ORDER BY clsname cmpname version. IF lt_components IS NOT INITIAL. SELECT SINGLE masterlang FROM tadir INTO lv_lang WHERE pgmid = 'R3TR' AND ( object = 'CLAS' OR object = 'INTF' ) AND obj_name = is_key-clsname. "#EC CI_GENBUFF IF sy-subrc <> 0. lv_lang = sy-langu. ENDIF. LOOP AT lt_components ASSIGNING . READ TABLE lt_descriptions TRANSPORTING NO FIELDS WITH KEY clsname = is_key-clsname cmpname = -cmpname. IF sy-subrc <> 0. ls_description-clsname = is_key-clsname. ls_description-cmpname = -cmpname. ls_description-langu = lv_lang. ls_description-descript = space. APPEND ls_description TO lt_descriptions. ENDIF. ENDLOOP. ENDIF. DELETE FROM seocompotx WHERE clsname = is_key-clsname. "#EC CI_SUBRC INSERT seocompotx FROM TABLE lt_descriptions. "#EC CI_SUBRC ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~update_descriptions_subco. DATA lt_descriptions LIKE it_descriptions. DATA lt_subcomponents TYPE seo_subcomponents. DATA ls_description LIKE LINE OF it_descriptions. DATA lv_lang TYPE tadir-masterlang. FIELD-SYMBOLS LIKE LINE OF it_descriptions. FIELD-SYMBOLS TYPE vseosubcdf. lt_descriptions = it_descriptions. LOOP AT lt_descriptions ASSIGNING . -clsname = is_key-clsname. ENDLOOP. " make sure to not damage VSEO* views by deleting texts of all subcomponents - an empty text must be kept!! SELECT * FROM vseosubcdf INTO TABLE lt_subcomponents WHERE clsname = is_key-clsname AND version <> seoc_version_deleted ORDER BY clsname cmpname sconame version. IF lt_subcomponents IS NOT INITIAL. SELECT SINGLE masterlang FROM tadir INTO lv_lang WHERE pgmid = 'R3TR' AND ( object = 'CLAS' OR object = 'INTF' ) AND obj_name = is_key-clsname. "#EC CI_GENBUFF IF sy-subrc <> 0. lv_lang = sy-langu. ENDIF. LOOP AT lt_subcomponents ASSIGNING . READ TABLE lt_descriptions TRANSPORTING NO FIELDS WITH KEY clsname = is_key-clsname cmpname = -cmpname sconame = -sconame. IF sy-subrc <> 0. ls_description-clsname = is_key-clsname. ls_description-cmpname = -cmpname. ls_description-sconame = -sconame. ls_description-langu = lv_lang. ls_description-descript = space. APPEND ls_description TO lt_descriptions. ENDIF. ENDLOOP. ENDIF. DELETE FROM seosubcotx WHERE clsname = is_key-clsname. "#EC CI_SUBRC INSERT seosubcotx FROM TABLE lt_descriptions. "#EC CI_SUBRC ENDMETHOD. ENDCLASS. CLASS zcl_abapgit_oo_class IMPLEMENTATION. METHOD create_report. zcl_abapgit_factory=>get_sap_report( )->insert_report( iv_name = iv_program iv_package = iv_package it_source = it_source iv_state = iv_state iv_version = iv_version iv_program_type = iv_program_type iv_extension_type = iv_extension ). ENDMETHOD. METHOD delete_report. zcl_abapgit_factory=>get_sap_report( )->delete_report( iv_program ). ENDMETHOD. METHOD determine_method_include. DATA: ls_mtdkey TYPE seocpdkey. ls_mtdkey-clsname = iv_name. ls_mtdkey-cpdname = iv_method. cl_oo_classname_service=>get_method_include( EXPORTING mtdkey = ls_mtdkey RECEIVING result = rv_program EXCEPTIONS method_not_existing = 1 ). IF sy-subrc = 0. RETURN. ENDIF. CALL FUNCTION 'SEO_METHOD_GENERATE_INCLUDE' EXPORTING suppress_mtdkey_check = abap_true mtdkey = ls_mtdkey EXCEPTIONS not_existing = 1 model_only = 2 include_existing = 3 method_imp_not_generated = 4 method_imp_not_initialised = 5 _internal_class_not_existing = 6 _internal_method_overflow = 7 cancelled = 8 method_is_abstract_implemented = 9 method_is_final_implemented = 10 internal_error_insert_report = 11 OTHERS = 12. IF sy-subrc <> 0. zcx_abapgit_exception=>raise_t100( ). ENDIF. rv_program = cl_oo_classname_service=>get_method_include( ls_mtdkey ). ENDMETHOD. METHOD generate_classpool. DATA: ls_clskey TYPE seoclskey. ls_clskey-clsname = iv_name. CALL FUNCTION 'SEO_CLASS_GENERATE_CLASSPOOL' EXPORTING clskey = ls_clskey suppress_corr = abap_true EXCEPTIONS not_existing = 1 model_only = 2 class_pool_not_generated = 3 class_stment_not_generated = 4 locals_not_generated = 5 macros_not_generated = 6 public_sec_not_generated = 7 protected_sec_not_generated = 8 private_sec_not_generated = 9 typeref_not_generated = 10 class_pool_not_initialised = 11 class_stment_not_initialised = 12 locals_not_initialised = 13 macros_not_initialised = 14 public_sec_not_initialised = 15 protected_sec_not_initialised = 16 private_sec_not_initialised = 17 typeref_not_initialised = 18 _internal_class_overflow = 19 OTHERS = 20. IF sy-subrc <> 0. zcx_abapgit_exception=>raise_t100( ). ENDIF. ENDMETHOD. METHOD get_method_includes. " get method includes for implemented interfaces " this will contain also leftover includes for deleted interface methods rt_includes = cl_oo_classname_service=>get_all_method_includes( iv_classname ). ENDMETHOD. METHOD init_scanner. DATA: lx_exc TYPE REF TO cx_root, lv_message TYPE string, lv_classname TYPE abap_abstypename. FIELD-SYMBOLS: TYPE i. TRY. ro_scanner = cl_oo_source_scanner_class=>create_class_scanner( clif_name = iv_name source = it_source ). ro_scanner->scan( ). CATCH cx_clif_scan_error. zcx_abapgit_exception=>raise( 'error initializing CLAS scanner' ). CATCH cx_root INTO lx_exc. lv_classname = cl_abap_classdescr=>get_class_name( lx_exc ). IF lv_classname = '\CLASS=CX_OO_CLIF_SCAN_ERROR_DETAIL'. ASSIGN lx_exc->('SOURCE_POSITION-LINE') TO . ASSERT sy-subrc = 0. lv_message = |{ lx_exc->get_text( ) }, line { }|. ELSE. lv_message = lx_exc->get_text( ). ENDIF. zcx_abapgit_exception=>raise( lv_message ). ENDTRY. ENDMETHOD. METHOD repair_classpool. CALL FUNCTION 'SEO_CLASS_REPAIR_CLASSPOOL' EXPORTING clskey = is_key EXCEPTIONS not_existing = 1 OTHERS = 2. IF sy-subrc <> 0. zcx_abapgit_exception=>raise( |Error repairing class { is_key-clsname }| ). ENDIF. ENDMETHOD. METHOD repair_redefinitions. " Same logic as SE24 > Utilities > Clean-up > Redefinitions (LSEODCCO) DATA: lt_inheritance TYPE vseoextend, lt_redefinitions TYPE seor_redefinitions_r, ls_cpdkey TYPE seocpdkey, lv_tabix TYPE sy-tabix, lv_exposure TYPE n LENGTH 1, lv_update TYPE abap_bool, lv_local_component TYPE abap_bool. FIELD-SYMBOLS TYPE seoredef. CALL FUNCTION 'SEO_CLASS_TYPEINFO_GET' EXPORTING clskey = is_key version = seoc_version_active IMPORTING inheritance = lt_inheritance redefinitions = lt_redefinitions EXCEPTIONS not_existing = 1 is_interface = 2 model_only = 3 OTHERS = 4. IF sy-subrc <> 0. RETURN. ENDIF. " check redefinitions validity LOOP AT lt_redefinitions ASSIGNING . lv_tabix = sy-tabix. ls_cpdkey-clsname = is_key-clsname. ls_cpdkey-cpdname = -mtdname. CALL FUNCTION 'SEO_COMPONENT_BY_INHERITANCE' EXPORTING cpdkey = ls_cpdkey version = seoc_version_active IMPORTING exposure = lv_exposure is_local_component = lv_local_component EXCEPTIONS not_existing = 1 model_only = 2 OTHERS = 3. IF sy-subrc <> 0. DELETE lt_redefinitions INDEX lv_tabix. lv_update = abap_true. ELSEIF -exposure <> lv_exposure. -exposure = lv_exposure. lv_update = abap_true. ELSEIF lv_local_component = abap_true AND -attvalue IS INITIAL AND -mtdabstrct IS INITIAL AND -mtdfinal IS INITIAL. DELETE lt_redefinitions INDEX lv_tabix. lv_update = abap_true. ENDIF. ENDLOOP. IF lv_update = abap_true. CALL FUNCTION 'SEO_INHERITANC_CHANGE_F_DATA' EXPORTING save = abap_false CHANGING inheritance = lt_inheritance redefinitions = lt_redefinitions EXCEPTIONS not_existing = 1 deleted = 2 is_comprising = 3 is_implementing = 4 not_changed = 5 db_error = 6 OTHERS = 7. IF sy-subrc <> 0. zcx_abapgit_exception=>raise( |Error repairing redefinitions for { is_key-clsname }| ). ENDIF. CALL FUNCTION 'SEO_CLIF_SAVE_ALL' EXPORTING cifkey = is_key EXCEPTIONS not_existing = 1 nothing_to_do = 2 access_error = 3 db_error = 4 error_in_code_generation = 5 OTHERS = 6. IF sy-subrc <> 0. zcx_abapgit_exception=>raise( |Error repairing redefinitions for { is_key-clsname }| ). ENDIF. ENDIF. ENDMETHOD. METHOD update_cs_number_of_methods. " Indirect access to keep downward compatibility DATA lr_cache_entry TYPE REF TO data. FIELD-SYMBOLS: TYPE any, TYPE any. TRY. CREATE DATA lr_cache_entry TYPE ('SEO_CS_CACHE'). CATCH cx_sy_create_data_error. * does not exist in some older systems RETURN. ENDTRY. ASSIGN lr_cache_entry->* TO . ASSERT sy-subrc = 0. ASSIGN COMPONENT 'CLSNAME' OF STRUCTURE TO . ASSERT sy-subrc = 0. = iv_classname. ASSIGN COMPONENT 'NO_OF_METHOD_IMPLS' OF STRUCTURE TO . ASSERT sy-subrc = 0. = iv_number_of_impl_methods. MODIFY ('SEO_CS_CACHE') FROM . ENDMETHOD. METHOD update_full_class_include. CONSTANTS: lc_class_source_extension TYPE c LENGTH 2 VALUE 'CS', lc_include_program_type TYPE c LENGTH 1 VALUE 'I', lc_active_version TYPE r3state VALUE 'A'. create_report( iv_program = cl_oo_classname_service=>get_cs_name( iv_classname ) iv_package = iv_package it_source = it_source iv_extension = lc_class_source_extension iv_program_type = lc_include_program_type iv_state = lc_active_version iv_version = iv_version ). " Assuming that all methods that were scanned are implemented update_cs_number_of_methods( iv_classname = iv_classname iv_number_of_impl_methods = lines( it_methods ) ). ENDMETHOD. METHOD update_meta. DATA: lo_update TYPE REF TO cl_oo_class_section_source, lx_error TYPE REF TO cx_oo_source_save_failure, ls_clskey TYPE seoclskey, lv_scan_error TYPE abap_bool. ls_clskey-clsname = iv_name. TRY. CALL FUNCTION 'SEO_BUFFER_REFRESH' EXPORTING cifkey = ls_clskey version = seoc_version_active. CREATE OBJECT lo_update TYPE ('CL_OO_CLASS_SECTION_SOURCE') EXPORTING clskey = ls_clskey exposure = iv_exposure state = 'A' source = it_source suppress_constrctr_generation = abap_true EXCEPTIONS class_not_existing = 1 read_source_error = 2 OTHERS = 3 ##SUBRC_OK. CATCH cx_sy_dyn_call_param_not_found. * downport to 702, see https://github.com/abapGit/abapGit/issues/933 * this will READ REPORT instead of using it_source, which should be okay CREATE OBJECT lo_update TYPE cl_oo_class_section_source EXPORTING clskey = ls_clskey exposure = iv_exposure state = 'A' EXCEPTIONS class_not_existing = 1 read_source_error = 2 OTHERS = 3. ENDTRY. IF sy-subrc <> 0. zcx_abapgit_exception=>raise_t100( ). ENDIF. lo_update->set_dark_mode( abap_true ). TRY. CALL METHOD lo_update->('SET_AMDP_SUPPORT') EXPORTING enabled = abap_true. CATCH cx_sy_dyn_call_illegal_method ##NO_HANDLER. * AMDP not supported in this system, ignore error ENDTRY. lo_update->scan_section_source( RECEIVING scan_error = lv_scan_error EXCEPTIONS scan_abap_source_error = 1 OTHERS = 2 ). IF sy-subrc <> 0 OR lv_scan_error = abap_true. zcx_abapgit_exception=>raise( |CLAS, error while scanning source. Subrc = { sy-subrc }| ). ENDIF. * this will update the SEO* database tables TRY. lo_update->revert_scan_result( ). CATCH cx_oo_source_save_failure INTO lx_error. zcx_abapgit_exception=>raise_with_text( lx_error ). ENDTRY. IF iv_exposure = seoc_exposure_public. generate_classpool( iv_name ). ENDIF. ENDMETHOD. METHOD update_report. DATA lv_type TYPE c LENGTH 1. lv_type = zcl_abapgit_oo_base=>c_include_program_type. IF iv_program+30 = srext_ext_class_pool. lv_type = zcl_abapgit_oo_base=>c_cp_program_type. ENDIF. rv_updated = zcl_abapgit_factory=>get_sap_report( )->update_report( iv_name = iv_program iv_package = iv_package iv_version = iv_version it_source = it_source iv_program_type = lv_type ). ENDMETHOD. METHOD update_source_index. CONSTANTS: lc_version_active TYPE r3state VALUE 'A', lc_version_inactive TYPE r3state VALUE 'I'. " dynamic invocation, IF_OO_SOURCE_POS_INDEX_HELPER doesn't exist in 702. DATA lo_index_helper TYPE REF TO object. TRY. CREATE OBJECT lo_index_helper TYPE ('CL_OO_SOURCE_POS_INDEX_HELPER'). CALL METHOD lo_index_helper->('IF_OO_SOURCE_POS_INDEX_HELPER~CREATE_INDEX_WITH_SCANNER') EXPORTING class_name = iv_clsname version = lc_version_active scanner = io_scanner. CALL METHOD lo_index_helper->('IF_OO_SOURCE_POS_INDEX_HELPER~DELETE_INDEX') EXPORTING class_name = iv_clsname version = lc_version_inactive. CATCH cx_root. " it's probably okay to no update the index RETURN. ENDTRY. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~create. DATA: lt_vseoattrib TYPE seoo_attributes_r, ls_class_key TYPE seoclskey, ls_properties TYPE vseoclass, lt_attributes TYPE zif_abapgit_oo_object_fnc=>ty_obj_attribute_tt. FIELD-SYMBOLS: TYPE seoclsname. ASSIGN COMPONENT 'CLSNAME' OF STRUCTURE cg_properties TO . ASSERT sy-subrc = 0. " Get existing class properties and attributes and check if the class " needs to be created/updated (or is the same) IF iv_check = abap_true. ls_class_key-clsname = . ls_properties = zif_abapgit_oo_object_fnc~get_class_properties( ls_class_key ). lt_attributes = zif_abapgit_oo_object_fnc~read_attributes( ). IF ls_properties = cg_properties AND lt_attributes = it_attributes. RETURN. ENDIF. ENDIF. lt_vseoattrib = convert_attrib_to_vseoattrib( iv_clsname = it_attributes = it_attributes ). " Hardcode STATE (#2612) ls_properties = cg_properties. ls_properties-state = seoc_state_implemented. TRY. CALL FUNCTION 'SEO_CLASS_CREATE_COMPLETE' EXPORTING devclass = iv_package overwrite = abap_true version = seoc_version_active suppress_dialog = abap_true " Parameter missing in 702 CHANGING class = ls_properties attributes = lt_vseoattrib EXCEPTIONS existing = 1 is_interface = 2 db_error = 3 component_error = 4 no_access = 5 other = 6 OTHERS = 7 ##FM_SUBRC_OK. CATCH cx_sy_dyn_call_param_not_found. CALL FUNCTION 'SEO_CLASS_CREATE_COMPLETE' EXPORTING devclass = iv_package overwrite = abap_true version = seoc_version_active CHANGING class = ls_properties attributes = lt_vseoattrib EXCEPTIONS existing = 1 is_interface = 2 db_error = 3 component_error = 4 no_access = 5 other = 6 OTHERS = 7 ##FM_SUBRC_OK. ENDTRY. IF sy-subrc <> 0. zcx_abapgit_exception=>raise_t100( ). ENDIF. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~create_sotr. zcl_abapgit_sotr_handler=>create_sotr( iv_package = iv_package io_xml = ii_xml ). zcl_abapgit_sots_handler=>create_sots( iv_package = iv_package io_xml = ii_xml ). ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~delete. " SEO_CLASS_DELETE_COMPLETE deletes OTR usage, only " Use handler to also delete OTR header and texts zcl_abapgit_sotr_handler=>delete_sotr( iv_pgmid = 'LIMU' iv_object = 'CPUB' iv_obj_name = is_deletion_key-clsname ). zcl_abapgit_sots_handler=>delete_sots( iv_pgmid = 'LIMU' iv_object = 'CPUB' iv_obj_name = is_deletion_key-clsname ). CALL FUNCTION 'SEO_CLASS_DELETE_COMPLETE' EXPORTING clskey = is_deletion_key EXCEPTIONS not_existing = 1 is_interface = 2 db_error = 3 no_access = 4 other = 5 OTHERS = 6. IF sy-subrc = 1. * ignore deletion of objects that does not exist * this can happen when the SXCI object is deleted before the implementing CLAS RETURN. ELSEIF sy-subrc <> 0. zcx_abapgit_exception=>raise_t100( ). ENDIF. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~deserialize_source. DATA: lv_updated TYPE abap_bool, lv_program TYPE program, lo_scanner TYPE REF TO cl_oo_source_scanner_class, lt_methods TYPE cl_oo_source_scanner_class=>type_method_implementations, lt_incls TYPE seop_methods_w_include, lv_method LIKE LINE OF lt_methods, lt_public TYPE seop_source_string, lt_source TYPE seop_source_string. "Buffer needs to be refreshed, "otherwise standard SAP CLIF_SOURCE reorder methods alphabetically CALL FUNCTION 'SEO_BUFFER_INIT'. CALL FUNCTION 'SEO_BUFFER_REFRESH' EXPORTING cifkey = is_key version = seoc_version_inactive. lo_scanner = init_scanner( it_source = it_source iv_name = is_key-clsname ). * public lt_public = lo_scanner->get_public_section_source( ). IF lt_public IS NOT INITIAL. lv_program = cl_oo_classname_service=>get_pubsec_name( is_key-clsname ). lv_updated = update_report( iv_program = lv_program iv_package = iv_package iv_version = iv_version it_source = lt_public ). IF lv_updated = abap_true. update_meta( iv_name = is_key-clsname iv_exposure = seoc_exposure_public it_source = lt_public ). ENDIF. ENDIF. * protected lt_source = lo_scanner->get_protected_section_source( ). IF lt_source IS NOT INITIAL. lv_program = cl_oo_classname_service=>get_prosec_name( is_key-clsname ). lv_updated = update_report( iv_program = lv_program iv_package = iv_package iv_version = iv_version it_source = lt_source ). IF lv_updated = abap_true. update_meta( iv_name = is_key-clsname iv_exposure = seoc_exposure_protected it_source = lt_source ). ENDIF. ENDIF. * private lt_source = lo_scanner->get_private_section_source( ). IF lt_source IS NOT INITIAL. lv_program = cl_oo_classname_service=>get_prisec_name( is_key-clsname ). lv_updated = update_report( iv_program = lv_program iv_package = iv_package iv_version = iv_version it_source = lt_source ). IF lv_updated = abap_true. update_meta( iv_name = is_key-clsname iv_exposure = seoc_exposure_private it_source = lt_source ). ENDIF. ENDIF. * methods lt_methods = lo_scanner->get_method_implementations( ). lt_incls = get_method_includes( is_key-clsname ). LOOP AT lt_methods INTO lv_method. TRY. lt_source = lo_scanner->get_method_impl_source( lv_method ). CATCH cx_oo_clif_component. zcx_abapgit_exception=>raise( 'error from GET_METHOD_IMPL_SOURCE' ). ENDTRY. lv_program = determine_method_include( iv_name = is_key-clsname iv_method = lv_method ). update_report( iv_program = lv_program iv_package = iv_package iv_version = iv_version it_source = lt_source ). " If method was implemented before, remove from list DELETE lt_incls WHERE cpdkey-clsname = is_key-clsname AND cpdkey-cpdname = lv_method. ENDLOOP. * full class include update_full_class_include( iv_classname = is_key-clsname iv_package = iv_package iv_version = iv_version it_source = it_source it_methods = lt_methods ). " If there are leftover method includes, then class needs to be repaired " which will delete the obsolete includes IF lt_incls IS NOT INITIAL. repair_classpool( is_key ). repair_redefinitions( is_key ). ENDIF. update_source_index( iv_clsname = is_key-clsname io_scanner = lo_scanner ). ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~exists. DATA ls_object_name TYPE seoclskey. ls_object_name = iv_object_name. CALL FUNCTION 'SEO_CLASS_EXISTENCE_CHECK' EXPORTING clskey = ls_object_name EXCEPTIONS not_specified = 1 not_existing = 2 is_interface = 3 no_text = 4 inconsistent = 5 OTHERS = 6. rv_exists = boolc( sy-subrc = 0 OR sy-subrc = 4 ). ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~generate_locals. DATA: lv_program TYPE syrepid. IF lines( it_local_definitions ) > 0. lv_program = cl_oo_classname_service=>get_ccdef_name( is_key-clsname ). update_report( iv_program = lv_program iv_package = iv_package iv_version = iv_version it_source = it_local_definitions ). ENDIF. IF lines( it_local_implementations ) > 0. lv_program = cl_oo_classname_service=>get_ccimp_name( is_key-clsname ). update_report( iv_program = lv_program iv_package = iv_package iv_version = iv_version it_source = it_local_implementations ). ENDIF. IF lines( it_local_macros ) > 0. lv_program = cl_oo_classname_service=>get_ccmac_name( is_key-clsname ). update_report( iv_program = lv_program iv_package = iv_package iv_version = iv_version it_source = it_local_macros ). ENDIF. lv_program = cl_oo_classname_service=>get_ccau_name( is_key-clsname ). IF lines( it_local_test_classes ) > 0. update_report( iv_program = lv_program iv_package = iv_package iv_version = iv_version it_source = it_local_test_classes ). ELSE. " Drop the include to remove left-over test classes delete_report( lv_program ). ENDIF. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~get_class_properties. CALL FUNCTION 'SEO_CLIF_GET' EXPORTING cifkey = is_class_key version = seoc_version_active IMPORTING class = rs_class_properties EXCEPTIONS not_existing = 1 deleted = 2 model_only = 3 OTHERS = 4. IF sy-subrc = 1. RETURN. " in case only inactive version exists ELSEIF sy-subrc <> 0. zcx_abapgit_exception=>raise_t100( ). ENDIF. CLEAR: " TODO 2023-08-01: Clear rs_class_properties-state (#2612) rs_class_properties-uuid, rs_class_properties-author, rs_class_properties-createdon, rs_class_properties-changedby, rs_class_properties-changedon, rs_class_properties-r3release, rs_class_properties-chgdanyby, rs_class_properties-chgdanyon, rs_class_properties-clsfinal, rs_class_properties-clsabstrct, rs_class_properties-exposure, rs_class_properties-version. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~get_includes. * note: includes returned might not exist * method cl_oo_classname_service=>GET_ALL_CLASS_INCLUDES does not exist in 702 DATA: lv_class_name TYPE seoclsname, lt_methods TYPE seop_methods_w_include. FIELD-SYMBOLS: LIKE LINE OF lt_methods. lv_class_name = iv_object_name. APPEND cl_oo_classname_service=>get_ccdef_name( lv_class_name ) TO rt_includes. APPEND cl_oo_classname_service=>get_ccmac_name( lv_class_name ) TO rt_includes. APPEND cl_oo_classname_service=>get_ccimp_name( lv_class_name ) TO rt_includes. APPEND cl_oo_classname_service=>get_cl_name( lv_class_name ) TO rt_includes. APPEND cl_oo_classname_service=>get_ccau_name( lv_class_name ) TO rt_includes. APPEND cl_oo_classname_service=>get_pubsec_name( lv_class_name ) TO rt_includes. APPEND cl_oo_classname_service=>get_prosec_name( lv_class_name ) TO rt_includes. APPEND cl_oo_classname_service=>get_prisec_name( lv_class_name ) TO rt_includes. APPEND cl_oo_classname_service=>get_classpool_name( lv_class_name ) TO rt_includes. APPEND cl_oo_classname_service=>get_ct_name( lv_class_name ) TO rt_includes. * skip the CS include, as it is sometimes generated on the fly instead of * when the methods are changed cl_oo_classname_service=>get_all_method_includes( EXPORTING clsname = lv_class_name RECEIVING result = lt_methods EXCEPTIONS class_not_existing = 1 ). IF sy-subrc <> 0. zcx_abapgit_exception=>raise( |Class { lv_class_name } not existing| ). ENDIF. LOOP AT lt_methods ASSIGNING . APPEND -incname TO rt_includes. ENDLOOP. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~insert_text_pool. DATA: lv_cp TYPE program. lv_cp = cl_oo_classname_service=>get_classpool_name( iv_class_name ). INSERT TEXTPOOL lv_cp FROM it_text_pool LANGUAGE iv_language STATE iv_state. IF sy-subrc <> 0. zcx_abapgit_exception=>raise( 'error from INSERT TEXTPOOL' ). ENDIF. zcl_abapgit_objects_activation=>add( iv_type = 'REPT' iv_name = lv_cp ). ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~read_sotr. zcl_abapgit_sotr_handler=>read_sotr( iv_pgmid = 'LIMU' iv_object = 'CPUB' iv_obj_name = iv_object_name io_i18n_params = io_i18n_params io_xml = ii_xml ). zcl_abapgit_sots_handler=>read_sots( iv_pgmid = 'LIMU' iv_object = 'CPUB' iv_obj_name = iv_object_name io_i18n_params = io_i18n_params io_xml = ii_xml ). ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~read_text_pool. DATA: lv_cp TYPE program. lv_cp = cl_oo_classname_service=>get_classpool_name( iv_class_name ). READ TEXTPOOL lv_cp INTO rt_text_pool LANGUAGE iv_language. "#EC CI_READ_REP ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~syntax_check. DATA: ls_clskey TYPE seoclskey, lv_syntaxerror TYPE abap_bool. ls_clskey-clsname = to_upper( iv_object_name ). CALL FUNCTION 'SEO_CLASS_CHECK_CLASSPOOL' EXPORTING clskey = ls_clskey suppress_error_popup = abap_true IMPORTING syntaxerror = lv_syntaxerror EXCEPTIONS _internal_class_not_existing = 1 error_message = 2 " suppress S-message OTHERS = 3. IF sy-subrc <> 0. zcx_abapgit_exception=>raise_t100( ). ENDIF. IF lv_syntaxerror = abap_true. zcx_abapgit_exception=>raise( |Class { ls_clskey-clsname } has syntax errors | ). ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_object_clas IMPLEMENTATION. METHOD /apmg/if_apm_object~import. DATA(is_pretty) = xsdbool( is_dry_run = abap_false ). TRY. DATA(class_key) = VALUE seoclskey( clsname = class_name ). " TODO: Make files mandatory IF files IS INITIAL. " Copy globally installed interface DATA(class_metadata) = zif_abapgit_oo_object_fnc~get_class_properties( class_key ). DATA(class_attributes) = zif_abapgit_oo_object_fnc~read_attributes( class_name ). DATA(class_text_pool) = zif_abapgit_oo_object_fnc~read_text_pool( iv_class_name = class_name iv_language = sy-langu ). IF class_metadata IS INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Not found'. ENDIF. DATA(orig_class_code) = source( ). ELSE. orig_class_code = files->get_abap( ). DATA(xml) = files->get_xml_parsed( ). xml->read( EXPORTING iv_name = 'VSEOCLASS' CHANGING cg_data = class_metadata ). xml->read( EXPORTING iv_name = 'ATTRIBUTES' CHANGING cg_data = class_attributes ). xml->read( EXPORTING iv_name = 'TPOOL' CHANGING cg_data = class_text_pool ). ENDIF. " Rename and create new class class_key-clsname = new_object. class_metadata-clsname = new_object. IF is_production = abap_true. CLEAR class_metadata-with_unit_tests. ENDIF. DATA(class_code) = /apmg/cl_apm_code_importer=>import( program_name = cl_oo_classname_service=>get_classpool_name( class_name ) program_source = orig_class_code map = map is_pretty = is_pretty ). IF is_dry_run IS INITIAL AND class_code <> orig_class_code. zif_abapgit_oo_object_fnc~create( EXPORTING iv_check = abap_false iv_package = new_package it_attributes = class_attributes CHANGING cg_properties = class_metadata ). zif_abapgit_oo_object_fnc~deserialize_source( iv_package = new_package iv_version = class_metadata-unicode is_key = class_key it_source = class_code ). ENDIF. " TODO: Make files mandatory IF files IS NOT INITIAL. DATA(local_definitions) = files->get_abap( zif_abapgit_oo_object_fnc=>c_parts-locals_def ). DATA(local_implementations) = files->get_abap( zif_abapgit_oo_object_fnc=>c_parts-locals_imp ). DATA(local_macros) = files->get_abap( zif_abapgit_oo_object_fnc=>c_parts-macros ). DATA(test_classes) = files->get_abap( zif_abapgit_oo_object_fnc=>c_parts-testclasses ). ENDIF. local_definitions = /apmg/cl_apm_code_importer=>import( program_name = cl_oo_classname_service=>get_ccdef_name( class_name ) program_source = local_definitions map = map is_pretty = is_pretty ). local_implementations = /apmg/cl_apm_code_importer=>import( program_name = cl_oo_classname_service=>get_ccimp_name( class_name ) program_source = local_implementations map = map is_pretty = is_pretty ). local_macros = /apmg/cl_apm_code_importer=>import( program_name = cl_oo_classname_service=>get_ccmac_name( class_name ) program_source = local_macros map = map is_pretty = is_pretty ). IF is_production = abap_true. CLEAR test_classes. ELSE. test_classes = /apmg/cl_apm_code_importer=>import( program_name = cl_oo_classname_service=>get_ccau_name( class_name ) program_source = test_classes map = map is_pretty = is_pretty ). ENDIF. IF is_dry_run IS INITIAL. zif_abapgit_oo_object_fnc~generate_locals( iv_package = new_package iv_version = class_metadata-unicode is_key = class_key it_local_definitions = local_definitions it_local_implementations = local_implementations it_local_macros = local_macros it_local_test_classes = test_classes ). zif_abapgit_oo_object_fnc~insert_text_pool( iv_class_name = class_name it_text_pool = class_text_pool iv_language = sy-langu ). ENDIF. CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD constructor. super->constructor( ). class_name = item-obj_name. ENDMETHOD. METHOD source. TRY. DATA(instance) = cl_oo_factory=>create_instance( ). DATA(source_handler) = instance->create_clif_source( clif_name = class_name version = 'A' ). source_handler->get_source( IMPORTING source = result ). CATCH cx_root INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. ENDCLASS. CLASS zcl_abapgit_oo_interface IMPLEMENTATION. METHOD init_scanner. DATA: lx_exc TYPE REF TO cx_root, lv_message TYPE string, lv_classname TYPE abap_abstypename. FIELD-SYMBOLS: TYPE i. TRY. ro_scanner = cl_oo_source_scanner_interface=>create_interface_scanner( clif_name = iv_name source = it_source ). ro_scanner->scan( ). CATCH cx_clif_scan_error. zcx_abapgit_exception=>raise( 'error initializing INTF scanner' ). CATCH cx_root INTO lx_exc. lv_classname = cl_abap_classdescr=>get_class_name( lx_exc ). IF lv_classname = '\CLASS=CX_OO_CLIF_SCAN_ERROR_DETAIL'. ASSIGN lx_exc->('SOURCE_POSITION-LINE') TO . ASSERT sy-subrc = 0. lv_message = |{ lx_exc->get_text( ) }, line { }|. ELSE. lv_message = lx_exc->get_text( ). ENDIF. zcx_abapgit_exception=>raise( lv_message ). ENDTRY. ENDMETHOD. METHOD update_meta. DATA: lo_update TYPE REF TO cl_oo_interface_section_source, lx_error TYPE REF TO cx_oo_source_save_failure, ls_clskey TYPE seoclskey, lv_scan_error TYPE abap_bool. ls_clskey-clsname = iv_name. TRY. CALL FUNCTION 'SEO_BUFFER_REFRESH' EXPORTING cifkey = ls_clskey version = seoc_version_active. CREATE OBJECT lo_update TYPE ('CL_OO_INTERFACE_SECTION_SOURCE') EXPORTING intkey = ls_clskey state = 'A' source = it_source EXCEPTIONS interface_not_existing = 1 read_source_error = 2 OTHERS = 3 ##SUBRC_OK. CATCH cx_sy_dyn_call_param_not_found. * downport to 702, see https://github.com/abapGit/abapGit/issues/933 * this will READ REPORT instead of using it_source, which should be okay CREATE OBJECT lo_update TYPE cl_oo_interface_section_source EXPORTING intkey = ls_clskey state = 'A' EXCEPTIONS interface_not_existing = 1 read_source_error = 2 OTHERS = 3. ENDTRY. IF sy-subrc <> 0. zcx_abapgit_exception=>raise_t100( ). ENDIF. lo_update->set_dark_mode( abap_true ). lo_update->scan_section_source( RECEIVING scan_error = lv_scan_error EXCEPTIONS scan_abap_source_error = 1 OTHERS = 2 ). IF sy-subrc <> 0 OR lv_scan_error = abap_true. zcx_abapgit_exception=>raise( |INTF, error while scanning source. Subrc = { sy-subrc }| ). ENDIF. * this will update the SEO* database tables TRY. lo_update->revert_scan_result( ). CATCH cx_oo_source_save_failure INTO lx_error. zcx_abapgit_exception=>raise_with_text( lx_error ). ENDTRY. ENDMETHOD. METHOD update_report. DATA lv_type TYPE c LENGTH 1. lv_type = zcl_abapgit_oo_base=>c_include_program_type. IF iv_program+30 = srext_ext_interface_pool. lv_type = zcl_abapgit_oo_base=>c_ip_program_type. ENDIF. rv_updated = zcl_abapgit_factory=>get_sap_report( )->update_report( iv_name = iv_program iv_package = iv_package iv_version = iv_version it_source = it_source iv_program_type = lv_type ). ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~create. DATA: lt_vseoattrib TYPE seoo_attributes_r, ls_interface_key TYPE seoclskey, ls_properties TYPE vseointerf. FIELD-SYMBOLS: TYPE seoclsname. ASSIGN COMPONENT 'CLSNAME' OF STRUCTURE cg_properties TO . ASSERT sy-subrc = 0. " Get existing interface properties and check if the interface " needs to be created/updated (or is the same) IF iv_check = abap_true. ls_interface_key-clsname = . ls_properties = zif_abapgit_oo_object_fnc~get_interface_properties( ls_interface_key ). IF ls_properties = cg_properties. RETURN. ENDIF. ENDIF. lt_vseoattrib = convert_attrib_to_vseoattrib( iv_clsname = it_attributes = it_attributes ). " Hardcode STATE (#2612) ls_properties = cg_properties. ls_properties-state = seoc_state_implemented. TRY. CALL FUNCTION 'SEO_INTERFACE_CREATE_COMPLETE' EXPORTING devclass = iv_package overwrite = abap_true version = seoc_version_active suppress_dialog = abap_true " Parameter missing in 702 CHANGING interface = ls_properties attributes = lt_vseoattrib EXCEPTIONS existing = 1 is_class = 2 db_error = 3 component_error = 4 no_access = 5 other = 6 OTHERS = 7 ##FM_SUBRC_OK. CATCH cx_sy_dyn_call_param_not_found. CALL FUNCTION 'SEO_INTERFACE_CREATE_COMPLETE' EXPORTING devclass = iv_package overwrite = abap_true version = seoc_version_active CHANGING interface = ls_properties attributes = lt_vseoattrib EXCEPTIONS existing = 1 is_class = 2 db_error = 3 component_error = 4 no_access = 5 other = 6 OTHERS = 7 ##FM_SUBRC_OK. ENDTRY. IF sy-subrc <> 0. zcx_abapgit_exception=>raise_t100( ). ENDIF. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~delete. CALL FUNCTION 'SEO_INTERFACE_DELETE_COMPLETE' EXPORTING intkey = is_deletion_key EXCEPTIONS not_existing = 1 is_class = 2 db_error = 3 no_access = 4 other = 5 OTHERS = 6. IF sy-subrc <> 0. zcx_abapgit_exception=>raise_t100( ). ENDIF. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~deserialize_source. DATA: lv_updated TYPE abap_bool, lv_program TYPE program, lo_scanner TYPE REF TO cl_oo_source_scanner_interface, lt_public TYPE seop_source_string. "Buffer needs to be refreshed, "otherwise standard SAP CLIF_SOURCE reorder methods alphabetically CALL FUNCTION 'SEO_BUFFER_INIT'. CALL FUNCTION 'SEO_BUFFER_REFRESH' EXPORTING cifkey = is_key version = seoc_version_inactive. lo_scanner = init_scanner( it_source = it_source iv_name = is_key-clsname ). lt_public = lo_scanner->get_interface_section_source( ). IF lt_public IS NOT INITIAL. lv_program = cl_oo_classname_service=>get_intfsec_name( is_key-clsname ). lv_updated = update_report( iv_program = lv_program iv_package = iv_package iv_version = iv_version it_source = lt_public ). IF lv_updated = abap_true. update_meta( iv_name = is_key-clsname it_source = lt_public ). ENDIF. ENDIF. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~exists. DATA ls_object_name TYPE seoclskey. ls_object_name = iv_object_name. CALL FUNCTION 'SEO_INTERFACE_EXISTENCE_CHECK' EXPORTING intkey = ls_object_name EXCEPTIONS not_specified = 1 not_existing = 2 is_class = 3 no_text = 4 inconsistent = 5 OTHERS = 6. rv_exists = boolc( sy-subrc = 0 OR sy-subrc = 4 ). ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~get_includes. DATA lv_interface_name TYPE seoclsname. lv_interface_name = iv_object_name. APPEND cl_oo_classname_service=>get_interfacepool_name( lv_interface_name ) TO rt_includes. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~get_interface_properties. CALL FUNCTION 'SEO_CLIF_GET' EXPORTING cifkey = is_interface_key version = seoc_version_active IMPORTING interface = rs_interface_properties EXCEPTIONS not_existing = 1 deleted = 2 model_only = 3 OTHERS = 4. IF sy-subrc = 1. RETURN. " in case only inactive version exists ELSEIF sy-subrc <> 0. zcx_abapgit_exception=>raise_t100( ). ENDIF. CLEAR: " TODO 2023-08-01: Clear rs_interface_properties-state (#2612) rs_interface_properties-uuid, rs_interface_properties-author, rs_interface_properties-createdon, rs_interface_properties-changedby, rs_interface_properties-changedon, rs_interface_properties-chgdanyby, rs_interface_properties-chgdanyon, rs_interface_properties-r3release, rs_interface_properties-version. ENDMETHOD. METHOD zif_abapgit_oo_object_fnc~syntax_check. DATA: ls_intkey TYPE seoclskey, lv_syntaxerror TYPE abap_bool. ls_intkey-clsname = to_upper( iv_object_name ). CALL FUNCTION 'SEO_INTERFACE_CHECK_POOL' EXPORTING intkey = ls_intkey suppress_error_popup = abap_true IMPORTING syntaxerror = lv_syntaxerror EXCEPTIONS error_message = 1 " suppress S-message OTHERS = 2. IF sy-subrc <> 0. zcx_abapgit_exception=>raise_t100( ). ENDIF. IF lv_syntaxerror = abap_true. zcx_abapgit_exception=>raise( |Interface { ls_intkey-clsname } has syntax errors | ). ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_object_intf IMPLEMENTATION. METHOD /apmg/if_apm_object~import. DATA(is_pretty) = xsdbool( is_dry_run = abap_false ). TRY. " Copy globally installed interface DATA(interface_key) = VALUE seoclskey( clsname = interface_name ). " TODO: Make files mandatory IF files IS INITIAL. DATA(interface_metadata) = zif_abapgit_oo_object_fnc~get_interface_properties( interface_key ). IF interface_metadata IS INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Not found'. ENDIF. DATA(orig_interface_code) = source( ). ELSE. orig_interface_code = files->get_abap( ). DATA(xml) = files->get_xml_parsed( ). xml->read( EXPORTING iv_name = 'VSEOINTERF' CHANGING cg_data = interface_metadata ). ENDIF. " Rename and create new interface interface_key-clsname = new_object. interface_metadata-clsname = new_object. DATA(interface_code) = /apmg/cl_apm_code_importer=>import( program_name = cl_oo_classname_service=>get_intfsec_name( interface_name ) program_source = orig_interface_code map = map is_pretty = is_pretty ). IF is_dry_run IS INITIAL AND interface_code <> orig_interface_code. zif_abapgit_oo_object_fnc~create( EXPORTING iv_check = abap_false iv_package = new_package CHANGING cg_properties = interface_metadata ). zif_abapgit_oo_object_fnc~deserialize_source( iv_package = new_package iv_version = interface_metadata-unicode is_key = interface_key it_source = interface_code ). ENDIF. CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD constructor. super->constructor( ). interface_name = item-obj_name. ENDMETHOD. METHOD source. " or /apmg/cl_apm_code_importer=>read( cl_oo_classname_service=>get_intfsec_name( interface_name ) ) ? TRY. DATA(instance) = cl_oo_factory=>create_instance( ). DATA(source_handler) = instance->create_clif_source( clif_name = interface_name version = 'A' ). source_handler->get_source( IMPORTING source = result ). CATCH cx_root INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_object_prog IMPLEMENTATION. METHOD /apmg/if_apm_object~import. DATA program_texts TYPE textpool_table. DATA(is_pretty) = xsdbool( is_dry_run = abap_false ). TRY. " Get old program DATA(program_dir) = zcl_abapgit_factory=>get_sap_report( )->read_progdir( program_name ). " TODO: Make files mandatory IF files IS INITIAL. DATA(orig_program_code) = /apmg/cl_apm_code_importer=>read( program_name ). ELSE. orig_program_code = files->get_abap( ). ENDIF. DATA(program_code) = /apmg/cl_apm_code_importer=>import( program_name = program_name program_source = orig_program_code map = map is_pretty = is_pretty ). READ TEXTPOOL program_name INTO program_texts. IF is_dry_run IS INITIAL AND program_code <> orig_program_code. program_dir-name = new_object. deserialize_program( progdir = program_dir source = program_code tpool = program_texts package = new_package ). deserialize_textpool( program = new_object tpool = program_texts ). ENDIF. CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD constructor. program_name = item-obj_name. ENDMETHOD. METHOD deserialize_program. DATA: progname TYPE reposrc-progname, title TYPE rglif-title. TRY. zcl_abapgit_factory=>get_cts_api( )->insert_transport_object( iv_object = 'ABAP' iv_obj_name = progdir-name iv_package = package iv_language = sy-langu ). title = get_program_title( tpool ). " Check if program already exists SELECT SINGLE progname FROM reposrc INTO @progname WHERE progname = @progdir-name AND r3state = @c_state-active. IF sy-subrc = 0. update_program( progdir = progdir source = source title = title ). ELSE. insert_program( progdir = progdir source = source title = title package = package ). ENDIF. zcl_abapgit_factory=>get_sap_report( )->update_progdir( is_progdir = progdir iv_package = package ). zcl_abapgit_objects_activation=>add( iv_type = 'REPS' iv_name = progdir-name ). CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD deserialize_textpool. IF language IS INITIAL. DATA(_language) = sy-langu. ELSE. _language = language. ENDIF. IF _language = sy-langu. DATA(state) = c_state-inactive. "Textpool in main language needs to be activated ELSE. state = c_state-active. "Translations are always active ENDIF. IF tpool IS INITIAL. IF include = abap_false OR state = c_state-active. DELETE TEXTPOOL program "Remove initial description from textpool if LANGUAGE _language "original program does not have a textpool STATE state. DATA(delete) = abap_true. ELSE. INSERT TEXTPOOL program "In case of includes: Deletion of textpool in FROM tpool "main language cannot be activated because LANGUAGE _language "this would activate the deletion of the textpool STATE state. "of the mail program -> insert empty textpool ENDIF. ELSE. INSERT TEXTPOOL program FROM tpool LANGUAGE _language STATE state. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error from INSERT TEXTPOOL'. ENDIF. ENDIF. " Textpool in main language needs to be activated IF state = c_state-inactive. TRY. zcl_abapgit_objects_activation=>add( iv_type = 'REPT' iv_name = program iv_delete = delete ). CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDIF. ENDMETHOD. METHOD get_program_title. FIELD-SYMBOLS TYPE any. READ TABLE tpool INTO DATA(tpool_line) WITH KEY id = 'R'. IF sy-subrc = 0. " there is a bug in RPY_PROGRAM_UPDATE, the header line of TTAB is not " cleared, so the title length might be inherited from a different program. ASSIGN ('(SAPLSIFP)TTAB') TO . IF sy-subrc = 0. CLEAR . ENDIF. result = tpool_line-entry. ENDIF. ENDMETHOD. METHOD insert_program. DATA _source TYPE abaptxt255_tab. _source = source. TRY. CALL FUNCTION 'RPY_PROGRAM_INSERT' EXPORTING development_class = package program_name = progdir-name program_type = progdir-subc title_string = title save_inactive = state suppress_dialog = abap_true uccheck = progdir-uccheck " does not exist on lower releases TABLES source_extended = _source EXCEPTIONS already_exists = 1 cancelled = 2 name_not_allowed = 3 permission_error = 4 OTHERS = 5 ##FM_SUBRC_OK. CATCH cx_sy_dyn_call_param_not_found. CALL FUNCTION 'RPY_PROGRAM_INSERT' EXPORTING development_class = package program_name = progdir-name program_type = progdir-subc title_string = title save_inactive = state suppress_dialog = abap_true TABLES source_extended = _source EXCEPTIONS already_exists = 1 cancelled = 2 name_not_allowed = 3 permission_error = 4 OTHERS = 5 ##FM_SUBRC_OK. ENDTRY. IF sy-subrc = 3. TRY. " For cases that standard function does not handle (like FUGR), " we save active and inactive version of source with the given PROGRAM TYPE. " Without the active version, the code will not be visible in case of activation errors. zcl_abapgit_factory=>get_sap_report( )->insert_report( iv_name = progdir-name iv_package = package it_source = source iv_state = c_state-active iv_version = progdir-uccheck iv_program_type = progdir-subc ). zcl_abapgit_factory=>get_sap_report( )->insert_report( iv_name = progdir-name iv_package = package it_source = source iv_state = c_state-inactive iv_version = progdir-uccheck iv_program_type = progdir-subc ). CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ELSEIF sy-subrc > 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. ENDMETHOD. METHOD update_program. DATA _source TYPE abaptxt255_tab. _source = source. zcl_abapgit_language=>set_current_language( sy-langu ). CALL FUNCTION 'RPY_PROGRAM_UPDATE' EXPORTING program_name = progdir-name title_string = title save_inactive = state TABLES source_extended = _source EXCEPTIONS cancelled = 1 permission_error = 2 not_found = 3 OTHERS = 4. IF sy-subrc <> 0. zcl_abapgit_language=>restore_login_language( ). IF sy-msgid = 'EU' AND sy-msgno = '510'. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'User is currently editing program'. ELSEIF sy-msgid = 'EU' AND sy-msgno = '522'. " for generated table maintenance function groups, the author is set to SAP* instead of the user which " generates the function group. This hits some standard checks, pulling new code again sets the author " to the current user which avoids the check ELSE. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. ENDIF. zcl_abapgit_language=>restore_login_language( ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_package_json IMPLEMENTATION. METHOD /apmg/if_apm_package_json~delete. db_persist->delete( key ). ENDMETHOD. METHOD /apmg/if_apm_package_json~exists. TRY. db_persist->load( key ). result = abap_true. CATCH /apmg/cx_apm_error. result = abap_false. ENDTRY. ENDMETHOD. METHOD /apmg/if_apm_package_json~get. result = CORRESPONDING #( manifest ). ENDMETHOD. METHOD /apmg/if_apm_package_json~get_json. result = convert_manifest_to_json( manifest = manifest is_package_json = abap_true is_complete = is_complete ). ENDMETHOD. METHOD /apmg/if_apm_package_json~is_valid. TRY. result = xsdbool( /apmg/cl_apm_package_json_vali=>check( manifest ) IS INITIAL ). CATCH /apmg/cx_apm_error. result = abap_false. ENDTRY. ENDMETHOD. METHOD /apmg/if_apm_package_json~load. /apmg/if_apm_package_json~set_json( db_persist->load( key )-value ). result = me. ENDMETHOD. METHOD /apmg/if_apm_package_json~save. check_manifest( manifest ). db_persist->save( key = key value = /apmg/if_apm_package_json~get_json( ) ). ENDMETHOD. METHOD /apmg/if_apm_package_json~set. manifest = CORRESPONDING #( package_json ). check_manifest( manifest ). manifest = sort_manifest( manifest ). result = me. ENDMETHOD. METHOD /apmg/if_apm_package_json~set_json. manifest = convert_json_to_manifest( json ). result = me. ENDMETHOD. METHOD check_manifest. DATA(issues) = /apmg/cl_apm_package_json_vali=>check( manifest ). IF issues IS NOT INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid package manifest (see longtext)' longtext = concat_lines_of( table = issues sep = |\n| ). ENDIF. ENDMETHOD. METHOD class_constructor. db_persist = /apmg/cl_apm_persist_apm=>get_instance( ). ENDMETHOD. METHOD constructor. IF /apmg/cl_apm_package_json_vali=>is_valid_sap_package( package ) = abap_false. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Invalid package: { package }|. ENDIF. me->package = package. manifest-name = name. manifest-version = version. manifest-private = private. key = get_package_key( package ). TRY. /apmg/if_apm_package_json~load( ). CATCH /apmg/cx_apm_error ##NO_HANDLER. ENDTRY. ENDMETHOD. METHOD convert_json_to_manifest. " TODO: AJSON does not allow for mapping of ABAP to JSON objects like { "user1", "user2", ... } " A table would map to an array [ "user1", "user2", ... ] TYPES: " Copy of schema but without dependencies (instead of array) BEGIN OF ty_manifest_partial, name TYPE string, version TYPE string, description TYPE string, keywords TYPE string_table, homepage TYPE string, icon TYPE string, bugs TYPE /apmg/if_apm_types=>ty_bugs, license TYPE string, author TYPE /apmg/if_apm_types=>ty_person, contributors TYPE /apmg/if_apm_types=>ty_persons, maintainers TYPE /apmg/if_apm_types=>ty_persons, main TYPE string, man TYPE string_table, type TYPE string, repository TYPE /apmg/if_apm_types=>ty_repository, funding TYPE /apmg/if_apm_types=>ty_funding, os TYPE string_table, cpu TYPE string_table, db TYPE string_table, private TYPE abap_bool, deprecated TYPE string, dist TYPE /apmg/if_apm_types=>ty_dist, readme TYPE string, sap_package TYPE /apmg/if_apm_types=>ty_sap_package, _id TYPE string, _abap_version TYPE string, _apm_version TYPE string, END OF ty_manifest_partial. DATA: manifest_partial TYPE ty_manifest_partial, dependency TYPE /apmg/if_apm_types=>ty_dependency. TRY. DATA(ajson) = /apmg/cl_apm_ajson=>parse( json )->to_abap_corresponding_only( )->map( /apmg/cl_apm_ajson_extensions=>from_camel_case_underscore( ) ). ajson->to_abap( IMPORTING ev_container = manifest_partial ). DATA(manifest) = CORRESPONDING /apmg/if_apm_types=>ty_manifest( manifest_partial ). " Transpose dependencies LOOP AT ajson->members( '/dependencies' ) INTO dependency-key. dependency-range = ajson->get( '/dependencies/' && replace_slash( dependency-key ) ). INSERT dependency INTO TABLE manifest-dependencies. ENDLOOP. LOOP AT ajson->members( '/dev_Dependencies' ) INTO dependency-key. dependency-range = ajson->get( '/dev_Dependencies/' && replace_slash( dependency-key ) ). INSERT dependency INTO TABLE manifest-dev_dependencies. ENDLOOP. LOOP AT ajson->members( '/optional_Dependencies' ) INTO dependency-key. dependency-range = ajson->get( '/optional_Dependencies/' && replace_slash( dependency-key ) ). INSERT dependency INTO TABLE manifest-optional_dependencies. ENDLOOP. LOOP AT ajson->members( '/peer_Dependencies' ) INTO dependency-key. dependency-range = ajson->get( '/peer_Dependencies/' && replace_slash( dependency-key ) ). INSERT dependency INTO TABLE manifest-peer_dependencies. ENDLOOP. LOOP AT ajson->members( '/bundle_Dependencies' ) INTO dependency-key. dependency-range = ajson->get( '/bundle_Dependencies/' && replace_slash( dependency-key ) ). " store just the range, which is the name of the bundle dependency INSERT dependency-range INTO TABLE manifest-bundle_dependencies. ENDLOOP. LOOP AT ajson->members( '/engines' ) INTO dependency-key. dependency-range = ajson->get( '/engines/' && replace_slash( dependency-key ) ). INSERT dependency INTO TABLE manifest-engines. ENDLOOP. check_manifest( manifest ). result = sort_manifest( manifest ). CATCH /apmg/cx_apm_ajson_error INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD convert_json_to_manifest_abbr. DATA(full_manifest) = convert_json_to_manifest( json ). result = CORRESPONDING #( full_manifest ). ENDMETHOD. METHOD convert_manifest_to_json. DATA skip_paths TYPE string_table. TRY. DATA(ajson) = /apmg/cl_apm_ajson=>new( )->keep_item_order( )->set( iv_path = '/' iv_val = manifest )->map( /apmg/cl_apm_ajson_extensions=>to_camel_case_underscore( ) ). " Transpose dependencies ajson->setx( '/dependencies:{ }' ). LOOP AT manifest-dependencies INTO DATA(dependency). ajson->set( iv_path = '/dependencies/' && replace_slash( dependency-key ) iv_val = dependency-range ). ENDLOOP. ajson->setx( '/devDependencies:{ }' ). LOOP AT manifest-dev_dependencies INTO dependency. ajson->set( iv_path = '/devDependencies/' && replace_slash( dependency-key ) iv_val = dependency-range ). ENDLOOP. ajson->setx( '/optionalDependencies:{ }' ). LOOP AT manifest-optional_dependencies INTO dependency. ajson->set( iv_path = '/optionalDependencies/' && replace_slash( dependency-key ) iv_val = dependency-range ). ENDLOOP. ajson->setx( 'peerDependencies:{ }' ). LOOP AT manifest-peer_dependencies INTO dependency. ajson->set( iv_path = '/peerDependencies/' && replace_slash( dependency-key ) iv_val = dependency-range ). ENDLOOP. ajson->setx( '/engines:{ }' ). LOOP AT manifest-engines INTO dependency. ajson->set( iv_path = '/engines/' && replace_slash( dependency-key ) iv_val = dependency-range ). ENDLOOP. IF is_deprecated = abap_true. ajson = ajson->filter( /apmg/cl_apm_ajson_extensions=>filter_deprecated( ) ). ELSEIF is_complete = abap_false. ajson = ajson->filter( /apmg/cl_apm_ajson_extensions=>filter_empty_zero_null( ) ). IF manifest-private = abap_false. INSERT `/private` INTO TABLE skip_paths. ENDIF. ENDIF. IF is_package_json = abap_true. " Remove the manifest fields that are not in package.json INSERT `/deprecated` INTO TABLE skip_paths. INSERT `/dist` INTO TABLE skip_paths. INSERT `/_id` INTO TABLE skip_paths. INSERT `/_abapVersion` INTO TABLE skip_paths. INSERT `/_apmVersion` INTO TABLE skip_paths. ENDIF. IF skip_paths IS NOT INITIAL. DATA(skip_path) = concat_lines_of( table = skip_paths sep = ',' ). ajson = ajson->filter( /apmg/cl_apm_ajson_filter_lib=>create_path_filter( iv_skip_paths = skip_path ) ). ENDIF. " Stringify with final newline result = ajson->stringify( 2 ) && |\n|. CATCH /apmg/cx_apm_ajson_error INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD factory. READ TABLE instances ASSIGNING FIELD-SYMBOL() WITH TABLE KEY package = package. IF sy-subrc = 0. result = -instance. ELSE. result = NEW /apmg/cl_apm_package_json( package = package name = name version = version private = private ). DATA(instance) = VALUE ty_instance( package = package instance = result ). INSERT instance INTO TABLE instances. ENDIF. ENDMETHOD. METHOD get_id_from_package. CONSTANTS c_initial_key TYPE xstring VALUE ''. " Get a numeric hash for package name (used in package list, action_link) TRY. cl_abap_hmac=>calculate_hmac_for_char( EXPORTING if_algorithm = 'SHA1' if_key = c_initial_key if_data = |{ package }| IMPORTING ef_hmacstring = DATA(sha1) ). TRANSLATE sha1 USING 'A0B1C2D3E4F5'. result = sha1. CATCH cx_abap_message_digest. ASSERT 0 = 1. " open an issue ENDTRY. ENDMETHOD. METHOD get_package_from_id. DATA(list) = list( ). READ TABLE list ASSIGNING FIELD-SYMBOL() WITH KEY id = id. IF sy-subrc = 0. result = -package. ENDIF. ENDMETHOD. METHOD get_package_from_key. SPLIT key AT ':' INTO DATA(prefix) result DATA(suffix) ##NEEDED. result = to_upper( result ). ENDMETHOD. METHOD get_package_key. result = |{ /apmg/if_apm_persist_apm=>c_key_type-package }:{ package }:| && |{ /apmg/if_apm_persist_apm=>c_key_extra-package_json }|. ENDMETHOD. METHOD get_super_packages. DATA(devclass) = package. DO. INSERT devclass INTO TABLE result. SELECT SINGLE parentcl FROM tdevc INTO @DATA(parent) WHERE devclass = @devclass. IF sy-subrc <> 0 OR parent IS INITIAL. EXIT. ENDIF. devclass = parent. ENDDO. ENDMETHOD. METHOD injector. READ TABLE instances ASSIGNING FIELD-SYMBOL() WITH TABLE KEY package = package. IF sy-subrc = 0. -instance = mock. ELSE. DATA(instance) = VALUE ty_instance( package = package instance = mock ). INSERT instance INTO TABLE instances. ENDIF. ENDMETHOD. METHOD list. DATA(list) = db_persist->list( /apmg/if_apm_persist_apm=>c_key_type-package && |:{ filter }%:| && /apmg/if_apm_persist_apm=>c_key_extra-package_json ). LOOP AT list ASSIGNING FIELD-SYMBOL(). DATA(list_package) = get_package_from_key( -keys ). CONVERT TIME STAMP -timestamp TIME ZONE 'UTC' INTO DATE DATA(date) TIME DATA(time). DATA(changed_at) = |{ date DATE = ISO } { time TIME = ISO }|. DATA(result_item) = VALUE /apmg/if_apm_package_json=>ty_package( key = -keys package = list_package changed_by = -user changed_at_raw = -timestamp changed_at = changed_at id = get_id_from_package( list_package ) ). IF instanciate = abap_true. TRY. result_item-instance = factory( result_item-package )->load( ). DATA(package_json) = result_item-instance->get( ). result_item-name = package_json-name. result_item-version = package_json-version. result_item-description = package_json-description. result_item-type = package_json-type. result_item-private = package_json-private. CATCH /apmg/cx_apm_error ##NO_HANDLER. ENDTRY. ENDIF. INSERT result_item INTO TABLE result. ENDLOOP. " Check package hierarchy to determine which packages are bundled LOOP AT result ASSIGNING FIELD-SYMBOL(). DATA(super_packages) = get_super_packages( -package ). LOOP AT super_packages ASSIGNING FIELD-SYMBOL() WHERE table_line <> -package. IF line_exists( result[ KEY package COMPONENTS package = ] ). -bundle = abap_true. -parent = . EXIT. ENDIF. ENDLOOP. ENDLOOP. CASE is_bundle. WHEN abap_true. DELETE result WHERE bundle = abap_false. WHEN abap_false. DELETE result WHERE bundle = abap_true. ENDCASE. SORT result. ENDMETHOD. METHOD replace_slash. result = replace( val = value sub = '/' with = cl_abap_char_utilities=>horizontal_tab occ = 0 ). ENDMETHOD. METHOD sort_manifest. result = manifest. " Keeping things in order avoid unnecessary diffs SORT: result-dependencies BY key, result-dev_dependencies BY key, result-optional_dependencies BY key, result-peer_dependencies BY key, result-bundle_dependencies, result-engines BY key, result-contributors BY name, result-maintainers BY name, result-keywords, result-man, result-os, result-cpu, result-db. ENDMETHOD. ENDCLASS. CLASS lcl_validate DEFINITION. PUBLIC SECTION. CLASS-METHODS validate_single_values IMPORTING !manifest TYPE /apmg/if_apm_types=>ty_manifest RETURNING VALUE(result) TYPE string_table. CLASS-METHODS validate_persons IMPORTING !manifest TYPE /apmg/if_apm_types=>ty_manifest RETURNING VALUE(result) TYPE string_table. CLASS-METHODS validate_arrays IMPORTING !manifest TYPE /apmg/if_apm_types=>ty_manifest RETURNING VALUE(result) TYPE string_table. CLASS-METHODS validate_engines IMPORTING !manifest TYPE /apmg/if_apm_types=>ty_manifest RETURNING VALUE(result) TYPE string_table. CLASS-METHODS validate_dependencies IMPORTING !manifest TYPE /apmg/if_apm_types=>ty_manifest RETURNING VALUE(result) TYPE string_table. CLASS-METHODS validate_sap_package IMPORTING !manifest TYPE /apmg/if_apm_types=>ty_manifest RETURNING VALUE(result) TYPE string_table. ENDCLASS. CLASS lcl_validate IMPLEMENTATION. METHOD validate_single_values. IF /apmg/cl_apm_package_json_vali=>is_valid_name( manifest-name ) = abap_false. INSERT |Invalid name: { manifest-name }| INTO TABLE result. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_version( manifest-version ) = abap_false. INSERT |Invalid version: { manifest-version }| INTO TABLE result. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_package_type( manifest-type ) = abap_false. INSERT |Invalid package type: { manifest-type }| INTO TABLE result. ENDIF. IF manifest-private <> abap_false AND manifest-private <> abap_true. INSERT |Invalid private flag: { manifest-private }| INTO TABLE result. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_url( manifest-homepage ) = abap_false. INSERT |Invalid homepage URL: { manifest-homepage }| INTO TABLE result. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_email( manifest-bugs-email ) = abap_false. INSERT |Invalid bugs email: { manifest-bugs-email }| INTO TABLE result. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_url( manifest-bugs-url ) = abap_false. INSERT |Invalid bugs URL: { manifest-bugs-url }| INTO TABLE result. ENDIF. " This should not be a URL to an html project page that you put in your browser. It's for computers. " Example: git+https://github.com/abapPM/abapPM.git IF /apmg/cl_apm_package_json_vali=>is_valid_url( manifest-repository-url ) = abap_false. INSERT |Invalid repository URL: { manifest-repository-url }| INTO TABLE result. ENDIF. ENDMETHOD. METHOD validate_persons. DATA values TYPE string_table. IF /apmg/cl_apm_package_json_vali=>is_valid_email( manifest-author-email ) = abap_false. INSERT |Invalid author email: { manifest-author-email }| INTO TABLE result. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_url( manifest-author-url ) = abap_false. INSERT |Invalid author URL: { manifest-author-url }| INTO TABLE result. ENDIF. CLEAR values. LOOP AT manifest-contributors INTO DATA(person). COLLECT person-name INTO values. IF /apmg/cl_apm_package_json_vali=>is_valid_email( person-email ) = abap_false. INSERT |Invalid contributor email: { person-name } { person-email }| INTO TABLE result. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_url( person-url ) = abap_false. INSERT |Invalid contributor URL: { person-name } { person-url }| INTO TABLE result. ENDIF. ENDLOOP. IF lines( manifest-contributors ) <> lines( values ). INSERT |Duplicate contributors| INTO TABLE result. ENDIF. CLEAR values. LOOP AT manifest-maintainers INTO person. COLLECT person-name INTO values. IF /apmg/cl_apm_package_json_vali=>is_valid_email( person-email ) = abap_false. INSERT |Invalid maintainer email: { person-name } { person-email }| INTO TABLE result. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_url( person-url ) = abap_false. INSERT |Invalid maintainer URL: { person-name } { person-url }| INTO TABLE result. ENDIF. ENDLOOP. IF lines( manifest-maintainers ) <> lines( values ). INSERT |Duplicate maintainers| INTO TABLE result. ENDIF. ENDMETHOD. METHOD validate_arrays. DATA values TYPE string_table. CLEAR values. LOOP AT manifest-cpu INTO DATA(value). COLLECT value INTO values. IF /apmg/cl_apm_package_json_vali=>is_valid_cpu( value ) = abap_false. INSERT |Invalid CPU: { value }| INTO TABLE result. ENDIF. ENDLOOP. IF lines( manifest-cpu ) <> lines( values ). INSERT |Duplicate CPU values| INTO TABLE result. ENDIF. CLEAR values. LOOP AT manifest-db INTO value. COLLECT value INTO values. IF /apmg/cl_apm_package_json_vali=>is_valid_db( value ) = abap_false. INSERT |Invalid database: { value }| INTO TABLE result. ENDIF. ENDLOOP. IF lines( manifest-db ) <> lines( values ). INSERT |Duplicate database values| INTO TABLE result. ENDIF. CLEAR values. LOOP AT manifest-os INTO value. COLLECT value INTO values. IF /apmg/cl_apm_package_json_vali=>is_valid_os( value ) = abap_false. INSERT |Invalid operating system: { value }| INTO TABLE result. ENDIF. ENDLOOP. IF lines( manifest-os ) <> lines( values ). INSERT |Duplicate operating system values| INTO TABLE result. ENDIF. ENDMETHOD. METHOD validate_engines. DATA values TYPE string_table. CLEAR values. LOOP AT manifest-engines INTO DATA(dependency). COLLECT dependency-key INTO values. IF /apmg/cl_apm_package_json_vali=>is_valid_engine( dependency-key ) = abap_false. INSERT |Invalid engine: { dependency-key }| INTO TABLE result. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_version_range( dependency-range ) = abap_false. INSERT |Invalid engine version: { dependency-key } { dependency-range }| INTO TABLE result. ENDIF. ENDLOOP. IF lines( manifest-engines ) <> lines( values ). INSERT |Duplicate engines| INTO TABLE result. ENDIF. ENDMETHOD. METHOD validate_dependencies. DATA values TYPE string_table. CLEAR values. LOOP AT manifest-dependencies INTO DATA(dependency). COLLECT dependency-key INTO values. IF /apmg/cl_apm_package_json_vali=>is_valid_name( dependency-key ) = abap_false. INSERT |Invalid dependency: { dependency-key }| INTO TABLE result. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_version_range( dependency-range ) = abap_false. INSERT |Invalid dependency version: { dependency-key } { dependency-range }| INTO TABLE result. ENDIF. ENDLOOP. IF lines( manifest-dependencies ) <> lines( values ). INSERT |Duplicate dependencies| INTO TABLE result. ENDIF. CLEAR values. LOOP AT manifest-dev_dependencies INTO dependency. COLLECT dependency-key INTO values. IF /apmg/cl_apm_package_json_vali=>is_valid_name( dependency-key ) = abap_false. INSERT |Invalid dev dependency: { dependency-key }| INTO TABLE result. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_version_range( dependency-range ) = abap_false. INSERT |Invalid dev dependency version: { dependency-key } { dependency-range }| INTO TABLE result. ENDIF. IF line_exists( manifest-dependencies[ key = dependency-key ] ). INSERT |Dev dependency { dependency-key } already included in dependencies| INTO TABLE result. ENDIF. ENDLOOP. IF lines( manifest-dev_dependencies ) <> lines( values ). INSERT |Duplicate dev dependencies| INTO TABLE result. ENDIF. CLEAR values. LOOP AT manifest-optional_dependencies INTO dependency. COLLECT dependency-key INTO values. IF /apmg/cl_apm_package_json_vali=>is_valid_name( dependency-key ) = abap_false. INSERT |Invalid optional dependency: { dependency-key }| INTO TABLE result. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_version_range( dependency-range ) = abap_false. INSERT |Invalid optional dependency version: { dependency-key } { dependency-range }| INTO TABLE result. ENDIF. IF line_exists( manifest-dependencies[ key = dependency-key ] ). INSERT |Optional dependency { dependency-key } already included in dependencies| INTO TABLE result. ENDIF. IF line_exists( manifest-dev_dependencies[ key = dependency-key ] ). INSERT |Optional dependency { dependency-key } already included in dev dependencies| INTO TABLE result. ENDIF. ENDLOOP. IF lines( manifest-optional_dependencies ) <> lines( values ). INSERT |Duplicate optional dependencies| INTO TABLE result. ENDIF. CLEAR values. LOOP AT manifest-bundle_dependencies INTO DATA(value). COLLECT value INTO values. IF /apmg/cl_apm_package_json_vali=>is_valid_name( value ) = abap_false. INSERT |Invalid bundle dependency: { value }| INTO TABLE result. ENDIF. IF NOT line_exists( manifest-dependencies[ key = value ] ). INSERT |Bundle dependency { value } not included in dependencies| INTO TABLE result. ENDIF. ENDLOOP. IF lines( manifest-bundle_dependencies ) <> lines( values ). INSERT |Duplicate bundle dependencies| INTO TABLE result. ENDIF. ENDMETHOD. METHOD validate_sap_package. IF manifest-sap_package-default IS NOT INITIAL AND /apmg/cl_apm_package_json_vali=>is_valid_sap_package( manifest-sap_package-default ) = abap_false. INSERT |Invalid default SAP package: { manifest-sap_package-default }| INTO TABLE result. ENDIF. IF /apmg/cl_apm_package_json_vali=>is_valid_abap_language_version( manifest-sap_package-abap_language_version ) = abap_false. INSERT |Invalid ABAP language version: { manifest-sap_package-abap_language_version }| INTO TABLE result. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_package_json_vali IMPLEMENTATION. METHOD check. APPEND LINES OF lcl_validate=>validate_single_values( manifest ) TO result. APPEND LINES OF lcl_validate=>validate_arrays( manifest ) TO result. APPEND LINES OF lcl_validate=>validate_persons( manifest ) TO result. APPEND LINES OF lcl_validate=>validate_engines( manifest ) TO result. APPEND LINES OF lcl_validate=>validate_dependencies( manifest ) TO result. APPEND LINES OF lcl_validate=>validate_sap_package( manifest ) TO result. ENDMETHOD. METHOD is_scoped. result = xsdbool( name IS NOT INITIAL AND name(1) = '@' AND name CS '/' ). ENDMETHOD. METHOD is_valid_abap_language_version. DATA(vers_val) = vers. SHIFT vers_val LEFT DELETING LEADING '!'. " not result = xsdbool( vers_val IS INITIAL OR vers_val = /apmg/if_apm_types=>c_abap_language_version-standard OR vers_val = /apmg/if_apm_types=>c_abap_language_version-key_user OR vers_val = /apmg/if_apm_types=>c_abap_language_version-cloud_development OR vers_val = /apmg/if_apm_types=>c_abap_language_version-ignore OR vers_val = /apmg/if_apm_types=>c_abap_language_version-undefined ). ENDMETHOD. METHOD is_valid_cpu. DATA(cpu_val) = cpu. SHIFT cpu_val LEFT DELETING LEADING '!'. " not result = xsdbool( cpu_val IS INITIAL OR cpu_val = /apmg/if_apm_types=>c_cpu-x86_64 OR cpu_val = /apmg/if_apm_types=>c_cpu-power_pc OR cpu_val = /apmg/if_apm_types=>c_cpu-sparc ). ENDMETHOD. METHOD is_valid_db. DATA(db_val) = db. SHIFT db_val LEFT DELETING LEADING '!'. result = xsdbool( db_val IS INITIAL OR db_val = /apmg/if_apm_types=>c_db-db2 OR db_val = /apmg/if_apm_types=>c_db-db400 OR db_val = /apmg/if_apm_types=>c_db-db6 OR db_val = /apmg/if_apm_types=>c_db-hdb OR db_val = /apmg/if_apm_types=>c_db-informix OR db_val = /apmg/if_apm_types=>c_db-mssql OR db_val = /apmg/if_apm_types=>c_db-oracle OR db_val = /apmg/if_apm_types=>c_db-sap_db OR db_val = /apmg/if_apm_types=>c_db-sybase ). ENDMETHOD. METHOD is_valid_email. " Email address validation (RFC 5322) CONSTANTS c_email_regex TYPE string VALUE '[\w!#$%&*+/=?`{|}~^-]+(?:\.[\w!#$%&*+/=?`{|}~^-]+)*@(?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,6}'. IF email IS INITIAL. result = abap_true. ELSE. FIND REGEX c_email_regex IN email ##REGEX_POSIX. result = xsdbool( sy-subrc = 0 ). ENDIF. ENDMETHOD. METHOD is_valid_engine. result = xsdbool( engine IS INITIAL OR engine = /apmg/if_apm_types=>c_engine-abap OR engine = /apmg/if_apm_types=>c_engine-apm ). ENDMETHOD. METHOD is_valid_name. DATA(scope) = ``. DATA(package_name) = name. IF is_scoped( name ). scope = substring_before( val = name sub = '/' ). package_name = substring_after( val = name sub = '/' ). result = is_valid_scope( scope ). CHECK result = abap_true. ENDIF. IF strlen( package_name ) BETWEEN /apmg/if_apm_types=>c_package_name-min_length AND /apmg/if_apm_types=>c_package_name-max_length. FIND REGEX /apmg/if_apm_types=>c_package_name-regex IN package_name RESPECTING CASE ##REGEX_POSIX. result = xsdbool( sy-subrc = 0 ). ELSE. result = abap_false. ENDIF. ENDMETHOD. METHOD is_valid_os. DATA(os_val) = os. SHIFT os_val LEFT DELETING LEADING '!'. " not result = xsdbool( os_val IS INITIAL OR os_val = /apmg/if_apm_types=>c_os-aix OR os_val = /apmg/if_apm_types=>c_os-hp_ux OR os_val = /apmg/if_apm_types=>c_os-linux OR os_val = /apmg/if_apm_types=>c_os-ms_windows OR os_val = /apmg/if_apm_types=>c_os-os_390 OR os_val = /apmg/if_apm_types=>c_os-os_400 OR os_val = /apmg/if_apm_types=>c_os-solaris ). ENDMETHOD. METHOD is_valid_package_type. result = xsdbool( type IS INITIAL OR type = /apmg/if_apm_types=>c_package_type-common_abap OR type = /apmg/if_apm_types=>c_package_type-module ). ENDMETHOD. METHOD is_valid_sap_package. DATA package_type TYPE c LENGTH 1. " Limit to local, customer, namespaced, and partner packages (see type-pool TPAK) cl_package_helper=>check_package_name( EXPORTING i_package_name = package IMPORTING e_package_type = package_type EXCEPTIONS OTHERS = 1 ). result = xsdbool( sy-subrc = 0 AND package_type CA '$ZNJ' ). " Workaround for missing validation of empty namespace IF result = abap_true AND package CP '//*'. result = abap_false. ENDIF. ENDMETHOD. METHOD is_valid_scope. IF strlen( scope ) BETWEEN /apmg/if_apm_types=>c_scope-min_length AND /apmg/if_apm_types=>c_scope-max_length. FIND REGEX /apmg/if_apm_types=>c_scope-regex IN scope RESPECTING CASE ##REGEX_POSIX. result = xsdbool( sy-subrc = 0 ). ELSE. result = abap_false. ENDIF. ENDMETHOD. METHOD is_valid_timestamp. CONSTANTS c_regex TYPE string VALUE `^(\d{4})-(\d{2})-(\d{2})(T)(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|$)` ##NO_TEXT. FIND REGEX c_regex IN timestamp ##REGEX_POSIX. result = xsdbool( sy-subrc = 0 ). ENDMETHOD. METHOD is_valid_url. TRY. IF url IS NOT INITIAL. /apmg/cl_apm_url=>parse( url ). ENDIF. result = abap_true. CATCH /apmg/cx_apm_error. result = abap_false. ENDTRY. ENDMETHOD. METHOD is_valid_version. " Check if it is a semantic version result = /apmg/cl_apm_semver_functions=>valid( version ). ENDMETHOD. METHOD is_valid_version_range. " Check if it is a semantic version range TRY. /apmg/cl_apm_semver_range=>create( range ). result = abap_true. CATCH /apmg/cx_apm_error. result = abap_false. ENDTRY. ENDMETHOD. ENDCLASS. CLASS LCL_VALIDATE_ DEFINITION. PUBLIC SECTION. CLASS-METHODS validate_single_values IMPORTING !packument TYPE /apmg/if_apm_types=>ty_packument RETURNING VALUE(result) TYPE string_table. CLASS-METHODS validate_dist_tags IMPORTING !packument TYPE /apmg/if_apm_types=>ty_packument RETURNING VALUE(result) TYPE string_table. CLASS-METHODS validate_times IMPORTING !packument TYPE /apmg/if_apm_types=>ty_packument RETURNING VALUE(result) TYPE string_table. CLASS-METHODS validate_users IMPORTING !packument TYPE /apmg/if_apm_types=>ty_packument RETURNING VALUE(result) TYPE string_table. ENDCLASS. CLASS LCL_VALIDATE_ IMPLEMENTATION. METHOD validate_single_values. IF packument-_id IS INITIAL. INSERT |Missing id| INTO TABLE result. ENDIF. ENDMETHOD. METHOD validate_dist_tags. DATA values TYPE string_table. CLEAR values. LOOP AT packument-dist_tags INTO DATA(dist_tag). COLLECT dist_tag-key INTO values. IF /apmg/cl_apm_package_json_vali=>is_valid_version( dist_tag-value ) = abap_false. INSERT |Invalid dist-tag version: { dist_tag-key } { dist_tag-value }| INTO TABLE result. ENDIF. IF NOT line_exists( packument-versions[ key = dist_tag-value ] ). INSERT |Dist-tag version does not exist: { dist_tag-key } { dist_tag-value }| INTO TABLE result. ENDIF. ENDLOOP. IF lines( packument-dist_tags ) <> lines( values ). INSERT |Duplicate dist-tags| INTO TABLE result. ENDIF. IF NOT line_exists( packument-dist_tags[ key = 'latest' ] ). INSERT |"latest" dist-tags is missing| INTO TABLE result. ENDIF. ENDMETHOD. METHOD validate_times. DATA values TYPE string_table. CLEAR values. LOOP AT packument-time INTO DATA(time). COLLECT time-key INTO values. IF time-key <> 'created' AND time-key <> 'modified' AND NOT line_exists( packument-versions[ key = time-key ] ). INSERT |Timestamp version does not exist: { time-key }| INTO TABLE result. ENDIF. ENDLOOP. IF lines( packument-time ) <> lines( values ). INSERT |Duplicate timestamps| INTO TABLE result. ENDIF. ENDMETHOD. METHOD validate_users. DATA values TYPE string_table. CLEAR values. LOOP AT packument-users INTO DATA(user). COLLECT user-name INTO values. IF user-stars NOT BETWEEN 0 AND 5. INSERT |Invalid number of stars: { user-name } { user-stars }| INTO TABLE result. ENDIF. ENDLOOP. IF lines( packument-users ) <> lines( values ). INSERT |Duplicate user names| INTO TABLE result. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_pacote IMPLEMENTATION. METHOD /apmg/if_apm_pacote~delete. db_persist->delete( pacote-key ). ENDMETHOD. METHOD /apmg/if_apm_pacote~exists. TRY. db_persist->load( pacote-key ). result = abap_true. CATCH /apmg/cx_apm_error. result = abap_false. ENDTRY. ENDMETHOD. METHOD /apmg/if_apm_pacote~get. result = pacote-packument. ENDMETHOD. METHOD /apmg/if_apm_pacote~get_json. result = pacote-json. ENDMETHOD. METHOD /apmg/if_apm_pacote~get_version. result = pacote-packument-versions[ key = version ]-manifest. ENDMETHOD. METHOD /apmg/if_apm_pacote~get_versions. LOOP AT pacote-packument-versions ASSIGNING FIELD-SYMBOL(). IF -manifest-deprecated IS INITIAL OR with_deprecated = abap_true. INSERT -manifest-version INTO TABLE result. ENDIF. ENDLOOP. ENDMETHOD. METHOD /apmg/if_apm_pacote~load. pacote-json = db_persist->load( pacote-key )-value. pacote-packument = convert_json_to_packument( pacote-json ). result = me. ENDMETHOD. METHOD /apmg/if_apm_pacote~manifest. result = request( url = |{ registry }/{ pacote-name }/{ version }{ write_request( write ) }| abbreviated = abbreviated )->cdata( ). check_result( result ). ENDMETHOD. METHOD /apmg/if_apm_pacote~packument. result = request( |{ registry }/{ pacote-name }{ write_request( write ) }| )->cdata( ). check_result( result ). /apmg/if_apm_pacote~set_json( result ). ENDMETHOD. METHOD /apmg/if_apm_pacote~save. db_persist->save( key = pacote-key value = pacote-json ). ENDMETHOD. METHOD /apmg/if_apm_pacote~set. pacote-packument = packument. pacote-json = convert_packument_to_json( packument ). result = me. ENDMETHOD. METHOD /apmg/if_apm_pacote~set_json. pacote-json = json. pacote-packument = convert_json_to_packument( json ). result = me. ENDMETHOD. METHOD /apmg/if_apm_pacote~tarball. DATA(response) = request( filename ). IF response->is_ok( ) = abap_false. check_result( response->cdata( ) ). ELSE. result = response->data( ). ENDIF. ENDMETHOD. METHOD check_packument. " packument has a lot of hoisted fields but no version DATA(manifest) = CORRESPONDING /apmg/if_apm_types=>ty_manifest( packument ). manifest-version = '1.0.0'. DATA(issues) = /apmg/cl_apm_package_json_vali=>check( manifest ). INSERT LINES OF LCL_VALIDATE_=>validate_single_values( packument ) INTO TABLE issues. INSERT LINES OF LCL_VALIDATE_=>validate_dist_tags( packument ) INTO TABLE issues. INSERT LINES OF LCL_VALIDATE_=>validate_times( packument ) INTO TABLE issues. INSERT LINES OF LCL_VALIDATE_=>validate_users( packument ) INTO TABLE issues. IF issues IS NOT INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Invalid packument:\n{ concat_lines_of( table = issues sep = |\n| ) }|. ENDIF. ENDMETHOD. METHOD check_result. TRY. DATA(error_message) = /apmg/cl_apm_ajson=>parse( json )->get_string( '/error' ). CATCH /apmg/cx_apm_ajson_error INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. IF error_message IS NOT INITIAL. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = error_message. ENDIF. ENDMETHOD. METHOD class_constructor. db_persist = /apmg/cl_apm_persist_apm=>get_instance( ). ENDMETHOD. METHOD constructor. me->registry = registry. pacote-key = get_packument_key( name ). pacote-name = escape( val = name format = cl_abap_format=>e_url_full ). IF packument IS NOT INITIAL. pacote-json = packument. pacote-packument = convert_json_to_packument( pacote-json ). ELSE. TRY. /apmg/if_apm_pacote~load( ). CATCH /apmg/cx_apm_error ##NO_HANDLER. ENDTRY. ENDIF. ENDMETHOD. METHOD convert_json_to_packument. TYPES: " Copy of schema but without object attributes (which need to be converted to tables) BEGIN OF ty_packument_partial, name TYPE /apmg/if_apm_types=>ty_name, description TYPE string, readme TYPE string, homepage TYPE string, icon TYPE string, bugs TYPE /apmg/if_apm_types=>ty_bugs, license TYPE string, keywords TYPE string_table, main TYPE string, man TYPE string_table, author TYPE /apmg/if_apm_types=>ty_person, repository TYPE /apmg/if_apm_types=>ty_repository, _id TYPE string, _rev TYPE string, access TYPE string, END OF ty_packument_partial. DATA: json_partial TYPE ty_packument_partial, generic TYPE /apmg/if_apm_types=>ty_generic, time TYPE /apmg/if_apm_types=>ty_time, person TYPE /apmg/if_apm_types=>ty_person, user TYPE /apmg/if_apm_types=>ty_user, version TYPE /apmg/if_apm_types=>ty_version_manifest, attachment TYPE /apmg/if_apm_types=>ty_attachment, packument TYPE /apmg/if_apm_types=>ty_packument. TRY. DATA(ajson) = /apmg/cl_apm_ajson=>parse( json )->to_abap_corresponding_only( )->map( /apmg/cl_apm_ajson_extensions=>from_camel_case_underscore( ) ). ajson->to_abap( IMPORTING ev_container = json_partial ). packument = CORRESPONDING #( json_partial ). " Transpose dist-tags, times, users, versions... LOOP AT ajson->members( '/dist-tags' ) INTO generic-key. generic-value = ajson->get( '/dist-tags/' && generic-key ). INSERT generic INTO TABLE packument-dist_tags. ENDLOOP. LOOP AT ajson->members( '/time' ) INTO time-key. time-timestamp = ajson->get_timestampl( '/time/' && time-key ). INSERT time INTO TABLE packument-time. ENDLOOP. LOOP AT ajson->members( '/maintainers' ) INTO DATA(key). person-name = ajson->get( '/maintainers/' && key && '/name' ). person-email = ajson->get( '/maintainers/' && key && '/email' ). person-url = ajson->get( '/maintainers/' && key && '/url' ). person-avatar = ajson->get( '/maintainers/' && key && '/avatar' ). INSERT person INTO TABLE packument-maintainers. ENDLOOP. LOOP AT ajson->members( '/users' ) INTO user-name. user-stars = ajson->get( '/users/' && user-name ). INSERT user INTO TABLE packument-users. ENDLOOP. LOOP AT ajson->members( '/_attachments' ) INTO attachment-key. attachment-tarball-content_type = ajson->get( '/_attachments/' && attachment-key && '/content_type' ). attachment-tarball-data = ajson->get( '/_attachments/' && attachment-key && '/data' ). attachment-tarball-length = ajson->get_integer( '/_attachments/' && attachment-key && '/length' ). INSERT attachment INTO TABLE packument-_attachments. ENDLOOP. LOOP AT ajson->members( '/versions' ) INTO version-key. DATA(ajson_version) = ajson->slice( '/versions/' && version-key ). " this also validates the version manifest version-manifest = /apmg/cl_apm_package_json=>convert_json_to_manifest( ajson_version->stringify( ) ). INSERT version INTO TABLE packument-versions. ENDLOOP. check_packument( packument ). result = sort_packument( packument ). CATCH /apmg/cx_apm_ajson_error INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD convert_packument_to_json. TRY. DATA(ajson) = /apmg/cl_apm_ajson=>new( )->keep_item_order( )->set( iv_path = '/' iv_val = packument )->map( /apmg/cl_apm_ajson_mapping=>create_compound_mapper( ii_mapper1 = /apmg/cl_apm_ajson_mapping=>create_rename( VALUE #( ( from = 'dist_tags' to = 'dist-tags' ) ) ) ii_mapper2 = /apmg/cl_apm_ajson_extensions=>to_camel_case_underscore( ) ) ). " Transpose dist-tags, times, users, versions... from arrays to objects ajson->setx( '/dist-tags:{ }' ). LOOP AT packument-dist_tags INTO DATA(generic). ajson->set( iv_path = 'dist-tags/' && generic-key iv_val = generic-value ). ENDLOOP. ajson->setx( '/time:{ }' ). LOOP AT packument-time INTO DATA(time). ajson->set_timestampl( iv_path = 'time/' && time-key iv_val = time-timestamp ). ENDLOOP. ajson->setx( '/users:{ }' ). LOOP AT packument-users INTO DATA(user). ajson->set( iv_path = 'users/' && user-name iv_val = user-stars ). ENDLOOP. ajson->setx( '/_attachments:{ }' ). LOOP AT packument-_attachments INTO DATA(attachment). ajson->set( iv_path = '_attachments/' && attachment-key && '/content_type' iv_val = attachment-tarball-content_type ). ajson->set( iv_path = '_attachments/' && attachment-key && '/data' iv_val = attachment-tarball-data ). ajson->set_integer( iv_path = '_attachments/' && attachment-key && '/length' iv_val = attachment-tarball-length ). ENDLOOP. ajson->setx( '/versions:{ }' ). LOOP AT packument-versions ASSIGNING FIELD-SYMBOL(). DATA(version_json) = /apmg/cl_apm_package_json=>convert_manifest_to_json( manifest = -manifest is_complete = is_complete is_deprecated = is_deprecated ). DATA(ajson_version) = /apmg/cl_apm_ajson=>parse( iv_json = version_json iv_keep_item_order = abap_true ). ajson->set( iv_path = 'versions/' && -key iv_val = ajson_version ). ENDLOOP. IF is_deprecated = abap_true. ajson = ajson->filter( /apmg/cl_apm_ajson_extensions=>filter_deprecated( ) ). ELSEIF is_complete = abap_false. ajson = ajson->filter( /apmg/cl_apm_ajson_extensions=>filter_empty_zero_null( ) ). ENDIF. result = ajson->stringify( 2 ). CATCH /apmg/cx_apm_ajson_error INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDMETHOD. METHOD factory. READ TABLE instances ASSIGNING FIELD-SYMBOL() WITH TABLE KEY name = name. IF sy-subrc = 0. result = -instance. ELSE. result = NEW /apmg/cl_apm_pacote( registry = registry name = name packument = packument ). DATA(instance) = VALUE ty_instance( name = name instance = result ). INSERT instance INTO TABLE instances. ENDIF. ENDMETHOD. METHOD get_agent. result = /apmg/cl_apm_http_agent=>create( ). IF abbreviated = abap_true. result->global_headers( )->set( iv_key = /apmg/if_apm_http_agent=>c_header-accept iv_val = c_abbreviated_json ). ELSE. result->global_headers( )->set( iv_key = /apmg/if_apm_http_agent=>c_header-accept iv_val = /apmg/if_apm_http_agent=>c_content_type-json ). ENDIF. DATA(components) = /apmg/cl_apm_url=>parse( url )->components. " Get/set auth token DATA(auth) = /apmg/cl_apm_http_login_manage=>get( components-host ). IF auth IS NOT INITIAL. result->global_headers( )->set( iv_key = /apmg/if_apm_http_agent=>c_header-authorization iv_val = auth ). ENDIF. ENDMETHOD. METHOD get_packument_from_key. SPLIT key AT ':' INTO DATA(prefix) result DATA(suffix) ##NEEDED. result = to_lower( result ). ENDMETHOD. METHOD get_packument_key. result = |{ /apmg/if_apm_persist_apm=>c_key_type-packument }:{ to_upper( name ) }|. ENDMETHOD. METHOD injector. READ TABLE instances ASSIGNING FIELD-SYMBOL() WITH TABLE KEY name = name. IF sy-subrc = 0. -instance = mock. ELSE. DATA(instance) = VALUE ty_instance( name = name instance = mock ). INSERT instance INTO TABLE instances. ENDIF. ENDMETHOD. METHOD request. IF abbreviated IS INITIAL. result = get_agent( registry )->request( url ). ELSE. DATA(headers) = NEW /apmg/cl_apm_string_map( )->set( iv_key = 'Accept' iv_val = 'application/vnd.npm.install-v1+json' ). result = get_agent( registry )->request( url = url headers = headers ). ENDIF. ENDMETHOD. METHOD sort_packument. result = packument. SORT result-dist_tags BY key. SORT result-time BY key. SORT result-maintainers BY name. SORT result-users BY name. SORT result-versions BY key. SORT result-_attachments BY key. SORT result-keywords. ENDMETHOD. METHOD write_request. IF write = abap_true. result = '?write=true'. ENDIF. ENDMETHOD. ENDCLASS. CLASS lcl_persist_utils DEFINITION. PUBLIC SECTION. CLASS-METHODS get_package_description IMPORTING package TYPE string RETURNING VALUE(result) TYPE string. CLASS-METHODS get_user_description IMPORTING username TYPE string RETURNING VALUE(result) TYPE string. CLASS-METHODS get_update_function RETURNING VALUE(result) TYPE funcname. PRIVATE SECTION. CLASS-DATA update_function TYPE funcname. ENDCLASS. CLASS lcl_persist_utils IMPLEMENTATION. METHOD get_package_description. SELECT SINGLE ctext FROM tdevct INTO @result WHERE devclass = @package AND spras = @sy-langu ##SUBRC_OK. ENDMETHOD. METHOD get_user_description. DATA user_address TYPE addr3_val. DATA(user_name) = CONV xubname( username ). CALL FUNCTION 'SUSR_USER_ADDRESS_READ' EXPORTING user_name = user_name IMPORTING user_address = user_address EXCEPTIONS user_address_not_found = 1 OTHERS = 2. IF sy-subrc = 0. result = user_address-name_text. ELSE. result = user_name. ENDIF. ENDMETHOD. METHOD get_update_function. IF update_function IS INITIAL. update_function = 'CALL_V1_PING'. TRY. CALL FUNCTION update_function. CATCH cx_sy_dyn_call_illegal_method. " Fallback update_function = 'BANK_OBJ_WORKL_RELEASE_LOCKS'. ENDTRY. ENDIF. result = update_function. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_persist_apm IMPLEMENTATION. METHOD /apmg/if_apm_persist_apm~delete. DELETE FROM (/apmg/if_apm_persist_apm=>c_tabname) WHERE keys = @key. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error deleting { key }|. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_persist_apm~list. DATA db_entries TYPE STANDARD TABLE OF /apmg/if_apm_persist_apm=>ty_zabappm WITH KEY keys. IF filter IS INITIAL. SELECT * FROM (/apmg/if_apm_persist_apm=>c_tabname) INTO TABLE @db_entries WHERE timestamp BETWEEN @from AND @to ORDER BY PRIMARY KEY ##SUBRC_OK. ELSE. SELECT * FROM (/apmg/if_apm_persist_apm=>c_tabname) INTO TABLE @db_entries WHERE timestamp BETWEEN @from AND @to AND keys LIKE @filter ORDER BY PRIMARY KEY ##SUBRC_OK. ENDIF. LOOP AT db_entries ASSIGNING FIELD-SYMBOL(). DATA(db_entry) = VALUE /apmg/if_apm_persist_apm=>ty_list_item( keys = -keys value = -value user = -luser timestamp = -timestamp ). SPLIT -keys AT ':' INTO db_entry-key_type db_entry-key_name db_entry-key_extra. INSERT db_entry INTO TABLE result. ENDLOOP. ENDMETHOD. METHOD /apmg/if_apm_persist_apm~load. SELECT SINGLE * FROM (/apmg/if_apm_persist_apm=>c_tabname) INTO @result WHERE keys = @key. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error loading { key }|. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_persist_apm~lock. CALL FUNCTION 'ENQUEUE_EZABAPPM' EXPORTING mode_zabappm = mode keys = key EXCEPTIONS foreign_lock = 1 system_failure = 2 OTHERS = 3. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. DATA(dummy_update_function) = lcl_persist_utils=>get_update_function( ). " trigger dummy update task to automatically release locks at commit CALL FUNCTION dummy_update_function IN UPDATE TASK. ENDMETHOD. METHOD /apmg/if_apm_persist_apm~save. IF validate_key( key ) = abap_false. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Invalid key { key }|. ENDIF. DATA(db_entry) = VALUE /apmg/if_apm_persist_apm=>ty_zabappm( keys = key value = replace( val = value sub = cl_abap_char_utilities=>cr_lf with = cl_abap_char_utilities=>newline occ = 0 ) luser = sy-uname ). GET TIME STAMP FIELD db_entry-timestamp. MODIFY (/apmg/if_apm_persist_apm=>c_tabname) FROM @db_entry. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error saving { key }|. ENDIF. ENDMETHOD. METHOD explain_key. SPLIT key AT ':' INTO DATA(key_type) DATA(name) DATA(extra). CASE key_type. WHEN /apmg/if_apm_persist_apm=>c_key_type-package. result-key_type = 'Packages'. result-description = lcl_persist_utils=>get_package_description( name ). CASE extra. WHEN /apmg/if_apm_persist_apm=>c_key_extra-package_json. result-extra = 'Package JSON'. result-content_type = /apmg/if_apm_persist_apm=>c_content_type-json. WHEN /apmg/if_apm_persist_apm=>c_key_extra-package_readme. result-extra = 'Readme'. result-content_type = /apmg/if_apm_persist_apm=>c_content_type-markdown. WHEN OTHERS. " Should not happen. Open issue result-extra = 'Unknown key extra'. result-content_type = /apmg/if_apm_persist_apm=>c_content_type-text. ENDCASE. WHEN /apmg/if_apm_persist_apm=>c_key_type-settings. result-key_type = 'Settings'. IF name = /apmg/if_apm_persist_apm=>c_key_name-global_settings. result-description = 'Global Settings'. result-extra = 'For all users'. ELSE. result-description = 'Personal Settings'. result-extra = |User: { lcl_persist_utils=>get_user_description( extra ) }|. ENDIF. result-content_type = /apmg/if_apm_persist_apm=>c_content_type-json. WHEN OTHERS. " Should not happen. Open issue result-key_type = 'Unknown type of key'. result-content_type = /apmg/if_apm_persist_apm=>c_content_type-text. ENDCASE. ENDMETHOD. METHOD get_instance. IF db_instance IS INITIAL. db_instance = NEW /apmg/cl_apm_persist_apm( ). ENDIF. result = db_instance. ENDMETHOD. METHOD injector. db_instance = mock. ENDMETHOD. METHOD validate_key. SPLIT key AT ':' INTO DATA(key_type) DATA(rest) ##NEEDED. result = xsdbool( sy-subrc = 0 AND ( key_type = /apmg/if_apm_persist_apm=>c_key_type-package OR key_type = /apmg/if_apm_persist_apm=>c_key_type-settings ) ). ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_persist_apm_setup IMPLEMENTATION. METHOD delete_ddic. TRY. CALL FUNCTION 'RS_DD_DELETE_OBJ' EXPORTING no_ask = no_ask objname = objname objtype = objtype no_ask_delete_append = no_ask_delete_append EXCEPTIONS not_executed = 1 object_not_found = 2 object_not_specified = 3 permission_failure = 4 dialog_needed = 5 OTHERS = 6. CATCH cx_sy_dyn_call_param_not_found. TRY. " try to force deletion for APPENDs CALL FUNCTION 'RS_DD_DELETE_OBJ' EXPORTING no_ask = no_ask objname = objname objtype = objtype aie_force_deletion = no_ask_delete_append EXCEPTIONS not_executed = 1 object_not_found = 2 object_not_specified = 3 permission_failure = 4 dialog_needed = 5 OTHERS = 6. CATCH cx_sy_dyn_call_param_not_found. " no_ask_delete_append and aie_force_deletion not available in lower releases CALL FUNCTION 'RS_DD_DELETE_OBJ' EXPORTING no_ask = no_ask objname = objname objtype = objtype EXCEPTIONS not_executed = 1 object_not_found = 2 object_not_specified = 3 permission_failure = 4 dialog_needed = 5 OTHERS = 6. ENDTRY. ENDTRY. IF sy-subrc = 5. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |{ objtype } { objname } has dependencies and must be deleted manually|. ELSEIF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error deleting { objtype } { objname }|. ENDIF. ENDMETHOD. METHOD install. IF logo_exists( ) = abap_false. logo_create( ). ENDIF. IF table_exists( ) = abap_false. table_create( ). ENDIF. IF lock_exists( ) = abap_false. lock_create( ). ENDIF. ENDMETHOD. METHOD lock_create. DATA: dd26e TYPE STANDARD TABLE OF dd26e WITH KEY ddlanguage viewname tabname tabpos, dd27p TYPE STANDARD TABLE OF dd27p WITH KEY viewname objpos ddlanguage viewfield tabname fieldname. DATA(dd25v) = VALUE dd25v( viewname = /apmg/if_apm_persist_apm=>c_lock aggtype = 'E' roottab = /apmg/if_apm_persist_apm=>c_tabname ddlanguage = /apmg/if_apm_persist_apm=>c_english ddtext = 'apm - Persistence' ). APPEND INITIAL LINE TO dd26e ASSIGNING FIELD-SYMBOL(). -viewname = /apmg/if_apm_persist_apm=>c_lock. -tabname = /apmg/if_apm_persist_apm=>c_tabname. -tabpos = '0001'. -fortabname = /apmg/if_apm_persist_apm=>c_tabname. -enqmode = 'E'. APPEND INITIAL LINE TO dd27p ASSIGNING FIELD-SYMBOL(). -viewname = /apmg/if_apm_persist_apm=>c_lock. -objpos = '0001'. -viewfield = 'KEYS'. -tabname = /apmg/if_apm_persist_apm=>c_tabname. -fieldname = 'KEYS'. -keyflag = abap_true. CALL FUNCTION 'DDIF_ENQU_PUT' EXPORTING name = /apmg/if_apm_persist_apm=>c_lock dd25v_wa = dd25v TABLES dd26e_tab = dd26e dd27p_tab = dd27p EXCEPTIONS enqu_not_found = 1 name_inconsistent = 2 enqu_inconsistent = 3 put_failure = 4 put_refused = 5 OTHERS = 6. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. DATA(obj_name) = CONV sobj_name( /apmg/if_apm_persist_apm=>c_lock ). CALL FUNCTION 'TR_TADIR_INTERFACE' EXPORTING wi_tadir_pgmid = 'R3TR' wi_tadir_object = 'ENQU' wi_tadir_obj_name = obj_name wi_set_genflag = abap_true wi_test_modus = abap_false wi_tadir_devclass = /apmg/if_apm_persist_apm=>c_devclass EXCEPTIONS OTHERS = 1. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. CALL FUNCTION 'DDIF_ENQU_ACTIVATE' EXPORTING name = /apmg/if_apm_persist_apm=>c_lock EXCEPTIONS not_found = 1 put_failure = 2 OTHERS = 3. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error activating { /apmg/if_apm_persist_apm=>c_lock }|. ENDIF. ENDMETHOD. METHOD lock_delete. delete_ddic( objtype = 'L' objname = /apmg/if_apm_persist_apm=>c_lock ). ENDMETHOD. METHOD lock_exists. SELECT COUNT(*) FROM dd25l INTO @DATA(count) WHERE viewname = @/apmg/if_apm_persist_apm=>c_lock. result = xsdbool( count > 0 ). ENDMETHOD. METHOD logo_create. DATA objs_table TYPE STANDARD TABLE OF v_obj_s WITH KEY objectname objecttype tabname. DATA(objh) = VALUE objh( objectname = /apmg/if_apm_persist_apm=>c_zapm objecttype = 'L' objcateg = 'APPL' checkid = 'L' objnamelen = '30' objtransp = '2' luser = sy-uname ldate = sy-datum objcharset = '1' ). DATA(objt) = VALUE objt( language = /apmg/if_apm_persist_apm=>c_english objectname = /apmg/if_apm_persist_apm=>c_zapm objecttype = 'L' ddtext = 'apm' ). DATA(objs) = VALUE objs( objectname = /apmg/if_apm_persist_apm=>c_zapm objecttype = 'L' tabname = /apmg/if_apm_persist_apm=>c_tabname ddic = abap_true prim_table = abap_true ). APPEND objs TO objs_table. DATA(objsl) = VALUE objsl( objectname = /apmg/if_apm_persist_apm=>c_zapm objecttype = 'L' trwcount = '01' tpgmid = 'R3TR' tobject = 'TABU' tobj_name = /apmg/if_apm_persist_apm=>c_tabname tobjkey = '/&/*' masknlen = 7 maskklen = 2 prim_table = abap_true ). CALL FUNCTION 'OBJ_GENERATE' EXPORTING iv_objectname = objh-objectname iv_objecttype = objh-objecttype iv_maint_mode = 'I' iv_objecttext = objt-ddtext iv_objcateg = objh-objcateg iv_objtransp = objh-objtransp iv_no_correction = abap_true TABLES tt_v_obj_s = objs_table EXCEPTIONS illegal_call = 1 object_not_found = 2 generate_error = 3 transport_error = 4 object_enqueue_failed = 5 OTHERS = 6. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. " No API? No choice INSERT objsl FROM @objsl ##SUBRC_OK. ENDMETHOD. METHOD logo_delete. DATA(objh) = VALUE objh( objectname = /apmg/if_apm_persist_apm=>c_zapm objecttype = 'L' ). CALL FUNCTION 'OBJ_GENERATE' EXPORTING iv_objectname = objh-objectname iv_objecttype = objh-objecttype iv_maint_mode = 'D' iv_no_correction = abap_true EXCEPTIONS illegal_call = 1 object_not_found = 2 generate_error = 3 transport_error = 4 object_enqueue_failed = 5 OTHERS = 6. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. " No API? No choice DELETE FROM objsl WHERE objectname = @/apmg/if_apm_persist_apm=>c_zapm AND objecttype = 'L' ##SUBRC_OK. ENDMETHOD. METHOD logo_exists. SELECT COUNT(*) FROM objh INTO @DATA(count) WHERE objectname = @/apmg/if_apm_persist_apm=>c_zapm AND objecttype = 'L'. result = xsdbool( count > 0 ). ENDMETHOD. METHOD table_create. DATA: subrc LIKE sy-subrc, dd03p TYPE STANDARD TABLE OF dd03p WITH KEY tabname fieldname position. DATA(dd02v) = VALUE dd02v( tabname = /apmg/if_apm_persist_apm=>c_tabname ddlanguage = /apmg/if_apm_persist_apm=>c_english tabclass = 'TRANSP' ddtext = 'apm - Persistence' contflag = 'A' exclass = '1' ). DATA(dd09l) = VALUE dd09l( tabname = /apmg/if_apm_persist_apm=>c_tabname as4local = 'A' tabkat = '1' tabart = 'APPL0' bufallow = 'X' pufferung = 'P' ). APPEND INITIAL LINE TO dd03p ASSIGNING FIELD-SYMBOL(). -tabname = /apmg/if_apm_persist_apm=>c_tabname. -fieldname = 'KEYS'. "KEY is not allowed -position = '0001'. -keyflag = 'X'. -notnull = 'X'. -datatype = 'CHAR'. -leng = '000120'. -ddlanguage = /apmg/if_apm_persist_apm=>c_english. -ddtext = 'Key'. APPEND INITIAL LINE TO dd03p ASSIGNING . -tabname = /apmg/if_apm_persist_apm=>c_tabname. -fieldname = 'VALUE'. -position = '0002'. -datatype = 'STRG'. -ddlanguage = /apmg/if_apm_persist_apm=>c_english. -ddtext = 'Value'. APPEND INITIAL LINE TO dd03p ASSIGNING . -tabname = /apmg/if_apm_persist_apm=>c_tabname. -fieldname = 'LUSER'. -position = '0003'. -rollname = 'AS4USER'. -datatype = 'CHAR'. -leng = '000012'. -ddlanguage = /apmg/if_apm_persist_apm=>c_english. -ddtext = 'Last Changed By'. APPEND INITIAL LINE TO dd03p ASSIGNING . -tabname = /apmg/if_apm_persist_apm=>c_tabname. -fieldname = 'TIMESTAMP'. -position = '0004'. -rollname = 'TIMESTAMPL'. -datatype = 'DEC'. -leng = '000021'. -decimals = '00007'. -ddlanguage = /apmg/if_apm_persist_apm=>c_english. -ddtext = 'Last Changed At'. CALL FUNCTION 'DDIF_TABL_PUT' EXPORTING name = /apmg/if_apm_persist_apm=>c_tabname dd02v_wa = dd02v dd09l_wa = dd09l TABLES dd03p_tab = dd03p EXCEPTIONS tabl_not_found = 1 name_inconsistent = 2 tabl_inconsistent = 3 put_failure = 4 put_refused = 5 OTHERS = 6. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. DATA(obj_name) = CONV sobj_name( /apmg/if_apm_persist_apm=>c_tabname ). CALL FUNCTION 'TR_TADIR_INTERFACE' EXPORTING wi_tadir_pgmid = 'R3TR' wi_tadir_object = 'TABL' wi_tadir_obj_name = obj_name wi_set_genflag = abap_true wi_test_modus = abap_false wi_tadir_devclass = /apmg/if_apm_persist_apm=>c_devclass EXCEPTIONS OTHERS = 1. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_t100. ENDIF. CALL FUNCTION 'DDIF_TABL_ACTIVATE' EXPORTING name = /apmg/if_apm_persist_apm=>c_tabname auth_chk = abap_false IMPORTING rc = subrc EXCEPTIONS not_found = 1 put_failure = 2 OTHERS = 3. IF sy-subrc <> 0 OR subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error activating { /apmg/if_apm_persist_apm=>c_tabname }|. ENDIF. ENDMETHOD. METHOD table_delete. DATA: subrc TYPE sy-subrc, BEGIN OF dd02l, tabname TYPE dd02l-tabname, tabclass TYPE dd02l-tabclass, sqltab TYPE dd02l-sqltab, END OF dd02l. DATA(no_ask) = abap_true. SELECT SINGLE tabname, tabclass, sqltab FROM dd02l INTO CORRESPONDING FIELDS OF @dd02l WHERE tabname = @/apmg/if_apm_persist_apm=>c_tabname AND as4local = 'A' AND as4vers = '0000'. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Table { /apmg/if_apm_persist_apm=>c_tabname } not found|. ENDIF. CALL FUNCTION 'DD_EXISTS_DATA' EXPORTING reftab = dd02l-sqltab tabclass = dd02l-tabclass tabname = dd02l-tabname IMPORTING subrc = subrc EXCEPTIONS missing_reftab = 1 sql_error = 2 buffer_overflow = 3 unknown_error = 4 OTHERS = 5. IF sy-subrc = 0 AND subrc = 0. no_ask = abap_false. ENDIF. delete_ddic( objtype = 'T' objname = /apmg/if_apm_persist_apm=>c_tabname no_ask = no_ask ). ENDMETHOD. METHOD table_exists. SELECT COUNT(*) FROM dd02l INTO @DATA(count) WHERE tabname = @/apmg/if_apm_persist_apm=>c_tabname. result = xsdbool( count > 0 ). ENDMETHOD. METHOD uninstall. IF lock_exists( ) = abap_true. lock_delete( ). ENDIF. IF table_exists( ) = abap_true. table_delete( ). ENDIF. IF logo_exists( ) = abap_true. logo_delete( ). ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_popup_utils IMPLEMENTATION. METHOD create_package. DATA: package_data TYPE zif_abapgit_sap_package=>ty_create, create_it TYPE abap_bool. package_data-devclass = condense( to_upper( package ) ). IF package IS NOT INITIAL AND zcl_abapgit_factory=>get_sap_package( package_data-devclass )->exists( ) = abap_true. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Package { package_data-devclass } already exists|. ENDIF. /apmg/cl_apm_gui_factory=>get_popups( )->popup_to_create_package( EXPORTING is_package_data = package_data IMPORTING es_package_data = package_data ev_create = create_it ). IF create_it = abap_true. TRY. zcl_abapgit_factory=>get_sap_package( package_data-devclass )->create( package_data ). result = package_data-devclass. COMMIT WORK AND WAIT. CATCH zcx_abapgit_exception INTO DATA(error). RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error. ENDTRY. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_progress_bar IMPLEMENTATION. METHOD /apmg/if_apm_progress_bar~off. " Clear the status bar CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'. ENDMETHOD. METHOD /apmg/if_apm_progress_bar~set_total. me->total = total. CLEAR: time_next, date_next. ENDMETHOD. METHOD /apmg/if_apm_progress_bar~show. CONSTANTS c_wait_secs TYPE i VALUE 2. GET TIME. DATA(current_time) = sy-uzeit. IF time_next IS INITIAL AND date_next IS INITIAL. time_next = current_time. date_next = sy-datum. ENDIF. " Only do a progress indication if enough time has passed IF current_time >= time_next AND sy-datum = date_next OR sy-datum > date_next. DATA(percentage) = calculate_percentage( current ). CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR' EXPORTING percentage = percentage text = text. time_next = current_time + c_wait_secs. ENDIF. IF sy-datum > date_next. date_next = sy-datum. ENDIF. IF time_next < current_time. date_next = sy-datum + 1. ENDIF. ENDMETHOD. METHOD calculate_percentage. TRY. result = current / total * 100. CASE result. WHEN 0. result = 1. WHEN 100. result = 99. ENDCASE. CATCH cx_sy_zerodivide. result = 0. ENDTRY. ENDMETHOD. METHOD get_instance. " Max one progress indicator at a time is supported IF global_instance IS INITIAL. global_instance = NEW /apmg/cl_apm_progress_bar( ). ENDIF. global_instance->set_total( total ). result = global_instance. ENDMETHOD. METHOD set_instance. global_instance = instance. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_readme IMPLEMENTATION. METHOD /apmg/if_apm_readme~delete. db_persist->delete( readme-key ). ENDMETHOD. METHOD /apmg/if_apm_readme~exists. TRY. db_persist->load( readme-key ). result = abap_true. CATCH /apmg/cx_apm_error. result = abap_false. ENDTRY. ENDMETHOD. METHOD /apmg/if_apm_readme~get. result = readme-markdown. " Add final newline IF substring( val = reverse( result ) off = 0 len = 1 ) <> |\n|. result = result && |\n|. ENDIF. ENDMETHOD. METHOD /apmg/if_apm_readme~load. readme-markdown = db_persist->load( readme-key )-value. result = me. ENDMETHOD. METHOD /apmg/if_apm_readme~save. db_persist->save( key = readme-key value = /apmg/if_apm_readme~get( ) ). ENDMETHOD. METHOD /apmg/if_apm_readme~set. readme-markdown = markdown. result = me. ENDMETHOD. METHOD class_constructor. db_persist = /apmg/cl_apm_persist_apm=>get_instance( ). ENDMETHOD. METHOD constructor. * IF zcl_readme_valid=>is_valid_sap_package( package ) = abap_false * RAISE EXCEPTION TYPE zcx_error_text EXPORTING text = |Invalid package: { package }| * ENDIF me->package = package. readme-key = get_package_key( package ). readme-markdown = markdown. TRY. /apmg/if_apm_readme~load( ). CATCH /apmg/cx_apm_error ##NO_HANDLER. ENDTRY. ENDMETHOD. METHOD factory. READ TABLE instances ASSIGNING FIELD-SYMBOL() WITH TABLE KEY package = package. IF sy-subrc = 0. result = -instance. ELSE. result = NEW /apmg/cl_apm_readme( package = package markdown = markdown ). DATA(instance) = VALUE ty_instance( package = package instance = result ). INSERT instance INTO TABLE instances. ENDIF. ENDMETHOD. METHOD get_package_from_key. SPLIT key AT ':' INTO DATA(prefix) result DATA(suffix) ##NEEDED. result = to_upper( result ). ENDMETHOD. METHOD get_package_key. result = |{ /apmg/if_apm_persist_apm=>c_key_type-package }:{ package }:| && |{ /apmg/if_apm_persist_apm=>c_key_extra-package_readme }|. ENDMETHOD. METHOD injector. READ TABLE instances ASSIGNING FIELD-SYMBOL() WITH TABLE KEY package = package. IF sy-subrc = 0. -instance = mock. ELSE. DATA(instance) = VALUE ty_instance( package = package instance = mock ). INSERT instance INTO TABLE instances. ENDIF. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_roadmap IMPLEMENTATION. METHOD not_implemented. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'The feature has not been implemented yet'. ENDMETHOD. METHOD planned. DATA(text) = message. IF text IS INITIAL. text = 'The feature has not been implemented but is on the roadmap'. ENDIF. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = text. ENDMETHOD. ENDCLASS. CLASS /apmg/cl_apm_semver IMPLEMENTATION. METHOD compare. DATA(semver) = create( version = other loose = options-loose incpre = options-incpre ). CHECK semver IS BOUND. IF semver->version = version. result = 0. ELSE. result = compare_main( semver ). IF result = 0. result = compare_pre( semver ). ENDIF. ENDIF. ENDMETHOD. METHOD compare_build. DATA(semver) = create( version = other loose = options-loose incpre = options-incpre ). DATA(i) = 1. DO. DATA(a) = VALUE #( build[ i ] DEFAULT `` ). DATA(b) = VALUE #( semver->build[ i ] DEFAULT `` ). IF a IS INITIAL AND b IS INITIAL. result = 0. RETURN. ELSEIF b IS INITIAL. result = +1. RETURN. ELSEIF a IS INITIAL. result = -1. RETURN. ELSEIF a <> b. result = /apmg/cl_apm_semver_identifier=>compare_identifiers( a = a b = b ). RETURN. ENDIF. i = i + 1. ENDDO. ENDMETHOD. METHOD compare_main. DATA(semver) = create( version = other loose = options-loose incpre = options-incpre ). CHECK semver IS BOUND. IF major < semver->major. result = -1. ELSEIF major > semver->major. result = 1. ELSEIF minor < semver->minor. result = -1. ELSEIF minor > semver->minor. result = 1. ELSEIF patch < semver->patch. result = -1. ELSEIF patch > semver->patch. result = 1. ELSE. result = 0. ENDIF. ENDMETHOD. METHOD compare_pre. DATA(semver) = create( version = other loose = options-loose incpre = options-incpre ). CHECK semver IS BOUND. " NOT having a prerelease is > having one IF prerelease IS NOT INITIAL AND semver->prerelease IS INITIAL. result = -1. ELSEIF prerelease IS INITIAL AND semver->prerelease IS NOT INITIAL. result = +1. ELSEIF prerelease IS INITIAL AND semver->prerelease IS INITIAL. result = 0. ELSE. DATA(i) = 1. DO. DATA(a) = VALUE #( prerelease[ i ] DEFAULT `` ). DATA(b) = VALUE #( semver->prerelease[ i ] DEFAULT `` ). IF a IS INITIAL AND b IS INITIAL. result = 0. RETURN. ELSEIF b IS INITIAL. result = +1. RETURN. ELSEIF a IS INITIAL. result = -1. RETURN. ELSEIF a <> b. result = /apmg/cl_apm_semver_identifier=>compare_identifiers( a = a b = b ). RETURN. ENDIF. i = i + 1. ENDDO. ENDIF. ENDMETHOD. METHOD constructor. IF strlen( version ) > /apmg/if_apm_semver_constants=>max_length. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Version is longer than { /apmg/if_apm_semver_constants=>max_length } characters|. ENDIF. options-loose = loose. options-incpre = incpre. DATA(r) = COND #( WHEN loose = abap_true THEN /apmg/cl_apm_semver_re=>token-loose-safe_regex ELSE /apmg/cl_apm_semver_re=>token-full-safe_regex ). TRY. DATA(m) = r->create_matcher( text = /apmg/cl_apm_semver_utils=>version_trim( version ) ). IF NOT m->match( ). RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Invalid version: { version }|. ENDIF. raw = version. " these are actually numbers DATA(major_num) = CONV decfloat34( m->get_submatch( 1 ) ). DATA(minor_num) = CONV decfloat34( m->get_submatch( 2 ) ). DATA(patch_num) = CONV decfloat34( m->get_submatch( 3 ) ). IF major_num BETWEEN 0 AND /apmg/if_apm_semver_constants=>max_safe_integer. major = major_num. ELSE. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Invalid major version: { major_num }|. ENDIF. IF minor_num BETWEEN 0 AND /apmg/if_apm_semver_constants=>max_safe_integer. minor = minor_num. ELSE. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Invalid minor version: { minor_num }|. ENDIF. IF patch_num BETWEEN 0 AND /apmg/if_apm_semver_constants=>max_safe_integer. patch = patch_num. ELSE. RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Invalid patch version: { patch_num }|. ENDIF. DATA(m4) = m->get_submatch( 4 ). IF m4 IS NOT INITIAL. SPLIT m4 AT '.' INTO TABLE prerelease. LOOP AT prerelease ASSIGNING FIELD-SYMBOL(

    ).
                IF /apmg/cl_apm_semver_utils=>is_numeric( 
     ).
                  DATA(pre_num) = CONV decfloat34( 
     ).
                  
     = pre_num.
                ENDIF.
              ENDLOOP.
            ENDIF.
    
            DATA(m5) = m->get_submatch( 5 ).
            IF m5 IS NOT INITIAL.
              SPLIT m5 AT '.' INTO TABLE build.
            ENDIF.
    
          CATCH cx_sy_matcher.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text
              EXPORTING
                text = |Error evaluating regex for { version }|.
        ENDTRY.
    
        format( ).
    
      ENDMETHOD.
    
      METHOD create.
    
        DATA(descr) = cl_abap_typedescr=>describe_by_data( version ).
        DATA(kind) = descr->type_kind.
    
        IF kind = cl_abap_typedescr=>typekind_oref AND version IS INSTANCE OF /apmg/cl_apm_semver.
    
          result ?= version.
    
          IF result->options-loose = loose AND result->options-incpre = incpre.
            RETURN.
          ENDIF.
    
          result = NEW /apmg/cl_apm_semver( version = |{ result->version }| loose = loose incpre = incpre ).
    
        ELSEIF kind = cl_abap_typedescr=>typekind_char OR kind = cl_abap_typedescr=>typekind_string.
    
          result = NEW /apmg/cl_apm_semver( version = |{ version }| loose = loose incpre = incpre ).
    
        ELSE.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text
            EXPORTING
              text = |Invalid version. Must be a string or a semver. Got { descr->absolute_name }|.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD format.
    
        version = |{ major }.{ minor }.{ patch }|.
    
        IF prerelease IS NOT INITIAL.
          version = version && |-{ concat_lines_of( table = prerelease sep = '.' ) }|.
        ENDIF.
    
        version = condense( version ).
    
        result = version.
    
      ENDMETHOD.
    
      METHOD inc.
    
        DATA prerelease_tab LIKE prerelease.
    
        _inc_check(
          release_type    = release_type
          identifier      = identifier
          identifier_base = identifier_base ).
    
        CASE release_type.
          WHEN 'premajor'.
            CLEAR prerelease.
            patch = 0.
            minor = 0.
            major = major + 1.
            inc( release_type = 'pre' identifier = identifier identifier_base = identifier_base ).
          WHEN 'preminor'.
            CLEAR prerelease.
            patch = 0.
            minor = minor + 1.
            inc( release_type = 'pre' identifier = identifier identifier_base = identifier_base ).
          WHEN 'prepatch'.
            " If this is already a prerelease, it will bump to the next version
            " drop any prereleases that might already exist, since they are not
            " relevant at this point.
            CLEAR prerelease.
            inc( release_type = 'patch' identifier = identifier identifier_base = identifier_base ).
            inc( release_type = 'pre' identifier = identifier identifier_base = identifier_base ).
          WHEN 'prerelease'.
            " If the input is a non-prerelease version, this acts the same as
            " prepatch.
            IF prerelease IS INITIAL.
              inc( release_type = 'patch' identifier = identifier identifier_base = identifier_base ).
            ENDIF.
            inc( release_type = 'pre' identifier = identifier identifier_base = identifier_base ).
          WHEN 'release'.
            IF prerelease IS INITIAL.
              RAISE EXCEPTION TYPE /apmg/cx_apm_error_text
                EXPORTING
                  text = |Version { raw } is not a prerelease|.
            ENDIF.
            CLEAR prerelease.
          WHEN 'major'.
            " If this is a pre-major version, bump up to the same major version.
            " Otherwise increment major.
            " 1.0.0-5 bumps to 1.0.0
            " 1.1.0 bumps to 2.0.0
            IF minor <> 0 OR patch <> 0 OR prerelease IS INITIAL.
              major = major + 1.
            ENDIF.
            minor = 0.
            patch = 0.
            CLEAR prerelease.
          WHEN 'minor'.
            " If this is a pre-minor version, bump up to the same minor version.
            " Otherwise increment minor.
            " 1.2.0-5 bumps to 1.2.0
            " 1.2.1 bumps to 1.3.0
            IF patch <> 0 OR prerelease IS INITIAL.
              minor = minor + 1.
            ENDIF.
            patch = 0.
            CLEAR prerelease.
          WHEN 'patch'.
            " If this is not a pre-release version, it will increment the patch.
            " If it is a pre-release it will bump up to the same patch version.
            " 1.2.0-5 patches to 1.2.0
            " 1.2.0 patches to 1.2.1
            IF prerelease IS INITIAL.
              patch = patch + 1.
            ENDIF.
            CLEAR prerelease.
          WHEN 'pre'.
            " This probably shouldn't be used publicly.
            " 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction.
            IF identifier_base IS INITIAL OR identifier_base = `0`.
              DATA(base) = `0`.
            ELSE.
              base = COND #( WHEN /apmg/cl_apm_semver_utils=>is_numeric( identifier_base ) THEN `1` ELSE `0` ).
            ENDIF.
    
            IF prerelease IS INITIAL.
              prerelease = VALUE #( ( base ) ).
            ELSE.
              DATA(i) = lines( prerelease ).
              WHILE i > 0.
                IF /apmg/cl_apm_semver_utils=>is_numeric( prerelease[ i ] ).
                  prerelease[ i ] = prerelease[ i ] + 1.
                  prerelease[ i ] = condense( prerelease[ i ] ).
                  i = -2.
                ENDIF.
                i = i - 1.
              ENDWHILE.
              IF i = 0.
                " didn't increment anything
                DATA(prerelease_string) = concat_lines_of( table = prerelease sep = '.' ).
                IF identifier = prerelease_string AND identifier_base = false.
                  RAISE EXCEPTION TYPE /apmg/cx_apm_error_text
                    EXPORTING
                      text = 'Invalid increment argument: identifier already exists'.
                ENDIF.
    
                INSERT base INTO TABLE prerelease.
              ENDIF.
            ENDIF.
            IF identifier IS NOT INITIAL.
              " 1.2.0-beta.1 bumps to 1.2.0-beta.2,
              " 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
              prerelease_tab = VALUE #( ( identifier ) ( base ) ).
              IF identifier_base = false.
                prerelease_tab = VALUE #( ( identifier ) ).
              ENDIF.
    
              IF /apmg/cl_apm_semver_identifier=>compare_identifiers( a = prerelease[ 1 ] b = identifier ) = 0.
                IF NOT /apmg/cl_apm_semver_utils=>is_numeric( VALUE #( prerelease[ 2 ] DEFAULT `-` ) ).
                  prerelease = prerelease_tab.
                ENDIF.
              ELSE.
                prerelease = prerelease_tab.
              ENDIF.
            ENDIF.
          WHEN 'prepush'.
            " Used by zcl_semver_ranges->min_version
            INSERT identifier_base INTO TABLE prerelease.
          WHEN OTHERS.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text
              EXPORTING
                text = |Invalid release type argument { release_type }|.
        ENDCASE.
    
        format( ).
    
        raw = version.
    
        IF build IS NOT INITIAL.
          raw = raw && |+{ concat_lines_of( table = build sep = '.' ) }|.
        ENDIF.
    
        result = me.
    
      ENDMETHOD.
    
      METHOD to_string.
        result = version.
      ENDMETHOD.
    
      METHOD _inc_check.
    
        IF release_type CP 'pre*'.
          IF identifier IS INITIAL AND identifier_base = false.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text
              EXPORTING
                text = 'Invalid increment argument: identifier is empty'.
          ENDIF.
    
          " Avoid an invalid semver results
          IF identifier IS NOT INITIAL.
            DATA(regex) = COND #(
              WHEN options-loose = abap_true
              THEN /apmg/cl_apm_semver_re=>token-prereleaseloose-regex
              ELSE /apmg/cl_apm_semver_re=>token-prerelease-regex ).
    
            TRY.
                DATA(m) = regex->create_matcher( text = |-{ identifier }| ).
    
                IF NOT m->match( ) OR m->get_submatch( 1 ) <> identifier.
                  RAISE EXCEPTION TYPE /apmg/cx_apm_error_text
                    EXPORTING
                      text = |Invalid identifier: { identifier }|.
                ENDIF.
              CATCH cx_sy_matcher.
                RAISE EXCEPTION TYPE /apmg/cx_apm_error_text
                  EXPORTING
                    text = |Error evaluating regex for { identifier }|.
            ENDTRY.
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_semver_cli IMPLEMENTATION.
    
      METHOD main.
    
        _argv( args ).
    
        IF help = abap_true.
          result = _help( ).
        ELSE.
          _versions( ).
    
          result = _success( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD _argv.
    
        CLEAR:
          argv, versions, ranges, inc, identifier, identifier_base,
          help, loose, incpre, coerce, rtl, reverse.
    
        IF args IS INITIAL.
          help = abap_true.
          RETURN.
        ENDIF.
    
        DATA(arg) = replace(
          val  = args
          sub  = '='
          with = ` `
          occ  = 0 ).
    
        SPLIT arg AT ` ` INTO TABLE argv.
    
        DATA(idx) = 1.
        DATA(val) = ``.
        DO.
          DATA(a) = VALUE #( argv[ idx ] OPTIONAL ).
          IF a IS INITIAL.
            EXIT.
          ENDIF.
    
          CASE a.
            WHEN '-rv' OR '-rev' OR '--rev' OR '--reverse'.
              reverse = abap_true.
            WHEN '-l' OR '--loose'.
              loose = abap_true.
            WHEN '-p' OR '--include-prerelease'.
              incpre = abap_true.
            WHEN '-v' OR '--version'.
              idx = idx + 1.
              val = VALUE #( argv[ idx ] OPTIONAL ).
              INSERT val INTO TABLE versions.
            WHEN '-i' OR '--inc' OR '--increment'.
              val = VALUE #( argv[ idx + 1 ] OPTIONAL ).
              CASE val.
                WHEN 'major' OR 'minor' OR 'patch' OR 'prerelease' OR 'premajor' OR 'preminor' OR 'prepatch' OR 'release'.
                  idx = idx + 1.
                  inc = VALUE #( argv[ idx ] OPTIONAL ).
                WHEN OTHERS.
                  inc = 'patch'.
              ENDCASE.
            WHEN '--preid'.
              idx = idx + 1.
              identifier = VALUE #( argv[ idx ] OPTIONAL ).
            WHEN '-r' OR '--range'.
              idx = idx + 1.
              val = VALUE #( argv[ idx ] OPTIONAL ).
              INSERT val INTO TABLE ranges.
            WHEN '-n'.
              idx = idx + 1.
              TRY.
                  identifier_base = VALUE #( argv[ idx ] OPTIONAL ).
                CATCH cx_root.
              ENDTRY.
            WHEN '-c' OR '--coerce'.
              coerce = abap_true.
            WHEN '--rtl'.
              rtl = abap_true.
            WHEN '--ltr'.
              rtl = abap_false.
            WHEN '-h' OR '--help' OR '-?'.
              help = abap_true.
            WHEN OTHERS.
              INSERT a INTO TABLE versions.
          ENDCASE.
    
          idx = idx + 1.
        ENDDO.
    
      ENDMETHOD.
    
      METHOD _help.
    
        result = VALUE #(
          ( |SemVer { /apmg/if_apm_semver_constants=>version }| )
          ( `` )
          ( `ABAP implementation of the https://semver.org/ specification` )
          ( `Original JavaScript Copyright Isaac Z. Schlueter` )
          ( `ABAP port by Marc F. Bernard` )
          ( `` )
          ( `Usage: semver [options]  [ [...]]` )
          ( `` )
          ( `Prints valid versions sorted by SemVer precedence` )
          ( `` )
          ( `Options:` )
          ( `` )
          ( `-r --range ` )
          ( `        Print versions that match the specified range.` )
          ( `` )
          ( `-i --increment []` )
          ( `        Increment a version by the specified level.  Level can` )
          ( `        be one of: major, minor, patch, premajor, preminor,` )
          ( `        prepatch, prerelease, or release.  Default level is 'patch'.` )
          ( `        Only one version may be specified.` )
          ( `` )
          ( `--preid ` )
          ( `        Identifier to be used to prefix premajor, preminor,` )
          ( `        prepatch or prerelease version increments.` )
          ( `` )
          ( `-l --loose` )
          ( `        Interpret versions and ranges loosely` )
          ( `` )
          ( `-n ` )
          ( `        Base number to be used for the prerelease identifier.` )
          ( `        Can be either 0 or 1, or false to omit the number altogether.` )
          ( `        Defaults to 0.` )
          ( `` )
          ( `-p --include-prerelease` )
          ( `        Always include prerelease versions in range matching` )
          ( `` )
          ( `-c --coerce` )
          ( `        Coerce a string into SemVer if possible` )
          ( `        (does not imply --loose)` )
          ( `` )
          ( `--rtl` )
          ( `        Coerce version strings right to left` )
          ( `` )
          ( `--ltr` )
          ( `        Coerce version strings left to right (default)` )
          ( `` )
          ( `Program exits successfully if any valid version satisfies` )
          ( `all supplied ranges, and prints all satisfying versions.` )
          ( `If no satisfying versions are found, then exits failure.` )
          ( `Versions are printed in ascending order, so supplying` )
          ( `multiple versions to the utility will just sort them.` ) ).
    
      ENDMETHOD.
    
      METHOD _success.
    
        IF reverse = abap_true.
          versions = /apmg/cl_apm_semver_functions=>rsort( versions ).
        ELSE.
          versions = /apmg/cl_apm_semver_functions=>sort( versions ).
        ENDIF.
    
        LOOP AT versions ASSIGNING FIELD-SYMBOL().
           = /apmg/cl_apm_semver_functions=>clean( version =  loose = loose incpre = incpre ).
        ENDLOOP.
    
        IF inc IS NOT INITIAL.
          LOOP AT versions ASSIGNING .
            DATA(semver) = /apmg/cl_apm_semver_functions=>inc(
              version         = 
              release_type    = inc
              identifier      = identifier
              identifier_base = identifier_base
              loose           = loose
              incpre          = incpre ).
    
            IF semver IS BOUND.
               = semver->version.
            ENDIF.
          ENDLOOP.
        ENDIF.
    
        INSERT LINES OF versions INTO TABLE result.
    
      ENDMETHOD.
    
      METHOD _versions.
    
        DELETE versions WHERE table_line IS INITIAL.
    
        LOOP AT versions ASSIGNING FIELD-SYMBOL().
          DATA(tabix) = sy-tabix.
    
          IF coerce = abap_true.
            DATA(semver) = /apmg/cl_apm_semver_functions=>coerce(
              version = 
              rtl     = rtl
              loose   = loose
              incpre  = incpre ).
            IF semver IS BOUND.
               = semver->version.
            ELSE.
              DELETE versions INDEX tabix.
              CONTINUE.
            ENDIF.
          ENDIF.
    
          DATA(valid) = /apmg/cl_apm_semver_functions=>valid( version =  loose = loose incpre = incpre ).
    
          IF valid IS INITIAL.
            DELETE versions INDEX tabix.
          ELSE.
             = valid.
          ENDIF.
    
        ENDLOOP.
    
        IF versions IS INITIAL.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'No valid versions found'.
        ENDIF.
    
        IF inc IS NOT INITIAL AND ( lines( versions ) > 1 OR lines( ranges ) > 0 ).
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text
            EXPORTING
              text = '--inc can only be used on a single version with no range'.
        ENDIF.
    
        LOOP AT ranges ASSIGNING FIELD-SYMBOL().
          LOOP AT versions ASSIGNING .
            tabix = sy-tabix.
    
            IF NOT /apmg/cl_apm_semver_functions=>satisfies(
              version = 
              range   = 
              loose   = loose
              incpre  = incpre ).
    
              DELETE versions INDEX tabix.
            ENDIF.
    
          ENDLOOP.
        ENDLOOP.
    
        IF versions IS INITIAL.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'No valid versions found'.
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_semver_comparator IMPLEMENTATION.
    
      METHOD class_constructor.
    
        TRY.
            any_semver = /apmg/cl_apm_semver=>create( '9999.9999.9999' ).
          CATCH /apmg/cx_apm_error ##NO_HANDLER.
        ENDTRY.
    
        " any_semver must be valid
        ASSERT any_semver IS BOUND.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        options-loose  = loose.
        options-incpre = incpre.
    
        parse( /apmg/cl_apm_semver_utils=>trim( comp ) ).
    
        IF semver = any_semver.
          value = ''.
        ELSE.
          value = operator && semver->version.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD create.
    
        DATA(kind) = cl_abap_typedescr=>describe_by_data( comp )->type_kind.
    
        IF kind = cl_abap_typedescr=>typekind_oref AND comp IS INSTANCE OF /apmg/cl_apm_semver_comparator.
    
          result = comp.
    
          IF result->options-loose = loose AND result->options-incpre = incpre.
            RETURN.
          ENDIF.
    
          result = NEW /apmg/cl_apm_semver_comparator( comp = |{ result->value }| loose = loose incpre = incpre ).
    
        ELSEIF kind = cl_abap_typedescr=>typekind_char OR kind = cl_abap_typedescr=>typekind_string.
    
          result = NEW /apmg/cl_apm_semver_comparator( comp = |{ comp }| loose = loose incpre = incpre ).
    
        ELSE.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid parameter type'.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD intersects.
    
        IF comp IS INITIAL.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'A comparator is required'.
        ENDIF.
    
        DATA(semcomp) = create( comp ).
    
        CHECK semcomp IS BOUND.
    
        CASE ''.
          WHEN operator.
            IF value = ''.
              result = abap_true.
            ELSE.
              DATA(semrange) = /apmg/cl_apm_semver_range=>create( range = semcomp->value loose = loose incpre = incpre ).
    
              CHECK semrange IS BOUND.
    
              result = semrange->test( value ).
            ENDIF.
          WHEN semcomp->operator.
            IF semcomp->value = ''.
              result = abap_true.
            ELSE.
              semrange = /apmg/cl_apm_semver_range=>create( range = value loose = loose incpre = incpre ).
    
              CHECK semrange IS BOUND.
    
              result = semrange->test( semcomp->semver ).
            ENDIF.
          WHEN OTHERS.
            " Special cases where nothing can possibly be lower
            IF incpre = abap_true AND value = '<0.0.0-0' OR semcomp->value = '<0.0.0-0'.
              result = abap_false.
              RETURN.
            ENDIF.
            IF incpre = abap_false AND value CP '<0.0.0*' OR semcomp->value CP '<0.0.0*'.
              result = abap_false.
              RETURN.
            ENDIF.
    
            " Same direction increasing (> or >=)
            IF operator CP '>*' AND semcomp->operator CP '>*'.
              result = abap_true.
              RETURN.
            ENDIF.
            " Same direction decreasing (< or <=)
            IF operator CP '<*' AND semcomp->operator CP '<*'.
              result = abap_true.
              RETURN.
            ENDIF.
    
            " same SemVer and both sides are inclusive (<= or >=)
            IF semver->version = semcomp->semver->version AND operator CA '=' AND semcomp->operator CA '='.
              result = abap_true.
              RETURN.
            ENDIF.
    
            " opposite directions less than
            IF /apmg/cl_apm_semver_functions=>cmp(
              a      = semver->version
              op     = '<'
              b      = semcomp->semver->version
              loose  = loose
              incpre = incpre ) AND operator CP '>*' AND semcomp->operator CP '<*'.
              result = abap_true.
              RETURN.
            ENDIF.
            " opposite directions greater than
            IF /apmg/cl_apm_semver_functions=>cmp(
              a      = semver->version
              op     = '>'
              b      = semcomp->semver->version
              loose  = loose
              incpre = incpre ) AND operator CP '<*' AND semcomp->operator CP '>*'.
              result = abap_true.
              RETURN.
            ENDIF.
            result = abap_false.
    
    *      DATA(same_direction_increasing) = xsdbool(
    *      ( operator = '>=' OR operator = '>' ) AND
    *      ( semcomp->operator = '>=' OR semcomp->operator = '>' ) )
    *      DATA(same_direction_decreasing) = xsdbool(
    *      ( operator = '<=' OR operator = '<' ) AND
    *      ( semcomp->operator = '<=' OR semcomp->operator = '<' ) )
    *      DATA(same_semver) = xsdbool(
    *      semver->version = semcomp->semver->version )
    *      DATA(different_directions_inclusive) = xsdbool(
    *      ( operator = '>=' OR operator = '<=' ) AND
    *      ( semcomp->operator = '>=' OR semcomp->operator = '<=' ) )
    *      DATA(opposite_directions_less) = xsdbool(
    *      zcl_semver_functions=>cmp(
    *      a     = semver->version
    *      op    = '<'
    *      b     = semcomp->semver->version
    *      loose = loose ) AND
    *      ( operator = '>=' OR operator = '>' ) AND
    *      ( semcomp->operator = '<=' OR semcomp->operator = '<' ) )
    *      DATA(opposite_directions_greater) = xsdbool(
    *      zcl_semver_functions=>cmp(
    *      a     = semver->version
    *      op    = '>'
    *      b     = semcomp->semver->version
    *      loose = loose ) AND
    *      ( operator = '<=' OR operator = '<' ) AND
    *      ( semcomp->operator = '>=' OR semcomp->operator = '>' ) )
    *
    *      result = xsdbool(
    *      same_direction_increasing = abap_true OR
    *      same_direction_decreasing = abap_true OR
    *      ( same_semver = abap_true AND different_directions_inclusive = abap_true ) OR
    *      opposite_directions_less = abap_true OR
    *      opposite_directions_greater = abap_true )
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD parse.
    
        " initial comparator means anything is allowed
        IF comp IS INITIAL.
          semver = any_semver.
          RETURN.
        ENDIF.
    
        DATA(r) = COND #(
          WHEN options-loose = abap_true
          THEN /apmg/cl_apm_semver_re=>token-comparatorloose-safe_regex
          ELSE /apmg/cl_apm_semver_re=>token-comparator-safe_regex ).
    
        TRY.
            DATA(m) = r->create_matcher( text = comp ).
    
            IF NOT m->match( ).
              RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Invalid comparator: { comp }|.
            ENDIF.
    
            operator = m->get_submatch( 1 ).
    
            IF operator = '='.
              operator = ''.
            ENDIF.
    
            " if it literally is just '>' or '' then allow anything
            IF m->get_submatch( 2 ) IS INITIAL.
              semver = any_semver.
            ELSE.
              semver = /apmg/cl_apm_semver=>create(
                version = m->get_submatch( 2 )
                loose   = options-loose
                incpre  = options-incpre ).
            ENDIF.
    
          CATCH cx_sy_matcher.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Error evaluating regex for { comp }|.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD test.
    
        TRY.
            DATA(testver) = /apmg/cl_apm_semver=>create( version = version loose = options-loose incpre = options-incpre ).
    
            IF semver = any_semver OR testver = any_semver.
              result = abap_true.
            ELSE.
              result = /apmg/cl_apm_semver_functions=>cmp(
                a     = testver->version
                op    = operator
                b     = semver->version
                loose = options-loose ).
            ENDIF.
    
          CATCH /apmg/cx_apm_error.
            result = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD to_string.
        result = value.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_semver_fixtures IMPLEMENTATION.
    
      METHOD comparator_intersection.
        " [c0, c1, expected intersection, includePrerelease]
    
        result = VALUE #(
          " One is a Version
          ( c0 = '1.3.0'    c1 = '>=1.3.0'  res = abap_true )
          ( c0 = '1.3.0'    c1 = '>1.3.0'   res = abap_false )
          ( c0 = '>=1.3.0'  c1 = '1.3.0'    res = abap_true )
          ( c0 = '>1.3.0'   c1 = '1.3.0'    res = abap_false )
          " Same direction increasing
          ( c0 = '>1.3.0'   c1 = '>1.2.0'   res = abap_true )
          ( c0 = '>1.2.0'   c1 = '>1.3.0'   res = abap_true )
          ( c0 = '>=1.2.0'  c1 = '>1.3.0'   res = abap_true )
          ( c0 = '>1.2.0'   c1 = '>=1.3.0'  res = abap_true )
          " Same direction decreasing
          ( c0 = '<1.3.0'   c1 = '<1.2.0'   res = abap_true )
          ( c0 = '<1.2.0'   c1 = '<1.3.0'   res = abap_true )
          ( c0 = '<=1.2.0'  c1 = '<1.3.0'   res = abap_true )
          ( c0 = '<1.2.0'   c1 = '<=1.3.0'  res = abap_true )
          " Different directions, same semver and inclusive operator
          ( c0 = '>=1.3.0'  c1 = '<=1.3.0'  res = abap_true )
          ( c0 = '>=v1.3.0' c1 = '<=1.3.0'  res = abap_true )
          ( c0 = '>=1.3.0'  c1 = '>=1.3.0'  res = abap_true )
          ( c0 = '<=1.3.0'  c1 = '<=1.3.0'  res = abap_true )
          ( c0 = '<=1.3.0'  c1 = '<=v1.3.0' res = abap_true )
          ( c0 = '>1.3.0'   c1 = '<=1.3.0'  res = abap_false )
          ( c0 = '>=1.3.0'  c1 = '<1.3.0'   res = abap_false )
          " Opposite matching directions
          ( c0 = '>1.0.0'   c1 = '<2.0.0'   res = abap_true )
          ( c0 = '>=1.0.0'  c1 = '<2.0.0'   res = abap_true )
          ( c0 = '>=1.0.0'  c1 = '<=2.0.0'  res = abap_true )
          ( c0 = '>1.0.0'   c1 = '<=2.0.0'  res = abap_true )
          ( c0 = '<=2.0.0'  c1 = '>1.0.0'   res = abap_true )
          ( c0 = '<=1.0.0'  c1 = '>=2.0.0'  res = abap_false )
          ( c0 = ''         c1 = ''         res = abap_true )
          ( c0 = ''         c1 = '>1.0.0'   res = abap_true )
          ( c0 = '<=2.0.0'  c1 = ''         res = abap_true )
          ( c0 = '<0.0.0'   c1 = '<0.1.0'   res = abap_false )
          ( c0 = '<0.1.0'   c1 = '<0.0.0'   res = abap_false )
          ( c0 = '<0.0.0-0' c1 = '<0.1.0'   res = abap_false )
          ( c0 = '<0.1.0'   c1 = '<0.0.0-0' res = abap_false )
          ( c0 = '<0.0.0-0' c1 = '<0.1.0'   res = abap_false incpre = abap_true )
          ( c0 = '<0.1.0'   c1 = '<0.0.0-0' res = abap_false incpre = abap_true ) ).
    
      ENDMETHOD.
    
      METHOD comparisons.
        " [version1, version2, options]
    
        " version1 should be greater than version2
        " used by the cmp, eq, gt, lt, and neq tests
        result = VALUE #(
          ( v0 = '0.0.0'              v1 = '0.0.0-foo' )
          ( v0 = '0.0.1'              v1 = '0.0.0' )
          ( v0 = '1.0.0'              v1 = '0.9.9' )
          ( v0 = '0.10.0'             v1 = '0.9.0' )
          ( v0 = '0.99.0'             v1 = '0.10.0' )
          ( v0 = '2.0.0'              v1 = '1.2.3'      loose = abap_false )
          ( v0 = 'v0.0.0'             v1 = '0.0.0-foo'  loose = abap_true )
          ( v0 = 'v0.0.1'             v1 = '0.0.0'      loose = abap_true )
          ( v0 = 'v1.0.0'             v1 = '0.9.9'      loose = abap_true )
          ( v0 = 'v0.10.0'            v1 = '0.9.0'      loose = abap_true )
          ( v0 = 'v0.99.0'            v1 = '0.10.0'     loose = abap_true )
          ( v0 = 'v2.0.0'             v1 = '1.2.3'      loose = abap_true )
          ( v0 = '0.0.0'              v1 = 'v0.0.0-foo' loose = abap_true )
          ( v0 = '0.0.1'              v1 = 'v0.0.0'     loose = abap_true )
          ( v0 = '1.0.0'              v1 = 'v0.9.9'     loose = abap_true )
          ( v0 = '0.10.0'             v1 = 'v0.9.0'     loose = abap_true )
          ( v0 = '0.99.0'             v1 = 'v0.10.0'    loose = abap_true )
          ( v0 = '2.0.0'              v1 = 'v1.2.3'     loose = abap_true )
          ( v0 = '1.2.3'              v1 = '1.2.3-asdf' )
          ( v0 = '1.2.3'              v1 = '1.2.3-4' )
          ( v0 = '1.2.3'              v1 = '1.2.3-4-foo' )
          ( v0 = '1.2.3-5-foo'        v1 = '1.2.3-5' )
          ( v0 = '1.2.3-5'            v1 = '1.2.3-4' )
          ( v0 = '1.2.3-5-foo'        v1 = '1.2.3-5-Foo' )
          ( v0 = '3.0.0'              v1 = '2.7.2+asdf' )
          ( v0 = '1.2.3-a.10'         v1 = '1.2.3-a.5' )
          ( v0 = '1.2.3-a.b'          v1 = '1.2.3-a.5' )
          ( v0 = '1.2.3-a.b'          v1 = '1.2.3-a' )
          ( v0 = '1.2.3-a.b.c.10.d.5' v1 = '1.2.3-a.b.c.5.d.100' )
          ( v0 = '1.2.3-r2'           v1 = '1.2.3-r100' )
          ( v0 = '1.2.3-r100'         v1 = '1.2.3-R2' ) ).
    
      ENDMETHOD.
    
      METHOD equality.
        " [version1, version2, expected result]
    
        " version1 should be equivalent to version2
        result = VALUE #(
          ( v0 = '1.2.3'            v1 = 'v1.2.3'                   loose = abap_true )
          ( v0 = '1.2.3'            v1 = '=1.2.3'                   loose = abap_true )
          ( v0 = '1.2.3'            v1 = 'v 1.2.3'                  loose = abap_true )
          ( v0 = '1.2.3'            v1 = '= 1.2.3'                  loose = abap_true )
          ( v0 = '1.2.3'            v1 = ' v1.2.3'                  loose = abap_true )
          ( v0 = '1.2.3'            v1 = ' =1.2.3'                  loose = abap_true )
          ( v0 = '1.2.3'            v1 = ' v 1.2.3'                 loose = abap_true )
          ( v0 = '1.2.3'            v1 = ' = 1.2.3'                 loose = abap_true )
          ( v0 = '1.2.3-0'          v1 = 'v1.2.3-0'                 loose = abap_true )
          ( v0 = '1.2.3-0'          v1 = '=1.2.3-0'                 loose = abap_true )
          ( v0 = '1.2.3-0'          v1 = 'v 1.2.3-0'                loose = abap_true )
          ( v0 = '1.2.3-0'          v1 = '= 1.2.3-0'                loose = abap_true )
          ( v0 = '1.2.3-0'          v1 = ' v1.2.3-0'                loose = abap_true )
          ( v0 = '1.2.3-0'          v1 = ' =1.2.3-0'                loose = abap_true )
          ( v0 = '1.2.3-0'          v1 = ' v 1.2.3-0'               loose = abap_true )
          ( v0 = '1.2.3-0'          v1 = ' = 1.2.3-0'               loose = abap_true )
          ( v0 = '1.2.3-1'          v1 = 'v1.2.3-1'                 loose = abap_true )
          ( v0 = '1.2.3-1'          v1 = '=1.2.3-1'                 loose = abap_true )
          ( v0 = '1.2.3-1'          v1 = 'v 1.2.3-1'                loose = abap_true )
          ( v0 = '1.2.3-1'          v1 = '= 1.2.3-1'                loose = abap_true )
          ( v0 = '1.2.3-1'          v1 = ' v1.2.3-1'                loose = abap_true )
          ( v0 = '1.2.3-1'          v1 = ' =1.2.3-1'                loose = abap_true )
          ( v0 = '1.2.3-1'          v1 = ' v 1.2.3-1'               loose = abap_true )
          ( v0 = '1.2.3-1'          v1 = ' = 1.2.3-1'               loose = abap_true )
          ( v0 = '1.2.3-beta'       v1 = 'v1.2.3-beta'              loose = abap_true )
          ( v0 = '1.2.3-beta'       v1 = '=1.2.3-beta'              loose = abap_true )
          ( v0 = '1.2.3-beta'       v1 = 'v 1.2.3-beta'             loose = abap_true )
          ( v0 = '1.2.3-beta'       v1 = '= 1.2.3-beta'             loose = abap_true )
          ( v0 = '1.2.3-beta'       v1 = ' v1.2.3-beta'             loose = abap_true )
          ( v0 = '1.2.3-beta'       v1 = ' =1.2.3-beta'             loose = abap_true )
          ( v0 = '1.2.3-beta'       v1 = ' v 1.2.3-beta'            loose = abap_true )
          ( v0 = '1.2.3-beta'       v1 = ' = 1.2.3-beta'            loose = abap_true )
          ( v0 = '1.2.3-beta+build' v1 = ' = 1.2.3-beta+otherbuild' loose = abap_true )
          ( v0 = '1.2.3+build'      v1 = ' = 1.2.3+otherbuild'      loose = abap_true )
          ( v0 = '1.2.3-beta+build' v1 = '1.2.3-beta+otherbuild' )
          ( v0 = '1.2.3+build'      v1 = '1.2.3+otherbuild' )
          ( v0 = '  v1.2.3+build'   v1 = '1.2.3+otherbuild' ) ).
    
      ENDMETHOD.
    
      METHOD increments.
        " [version, inc, result, options, identifier, identifier_base]
    
        " inc(version, re, options, identifier, identifier_base) -> result
        result = VALUE #(
          ( version         = '1.2.3'                 release         = 'major'      res = '2.0.0' )
          ( version         = '1.2.3'                 release         = 'minor'      res = '1.3.0' )
          ( version         = '1.2.3'                 release         = 'patch'      res = '1.2.4' )
          ( version         = '1.2.3tag'              release         = 'major'      res = '2.0.0'                 loose      = abap_true )
          ( version         = '1.2.3-tag'             release         = 'major'      res = '2.0.0' )
          ( version         = '1.2.3'                 release         = 'fake'       res = '' )
          ( version         = '1.2.0-0'               release         = 'patch'      res = '1.2.0' )
          ( version         = 'fake'                  release         = 'major'      res = '' )
          ( version         = '1.2.3-4'               release         = 'major'      res = '2.0.0' )
          ( version         = '1.2.3-4'               release         = 'minor'      res = '1.3.0' )
          ( version         = '1.2.3-4'               release         = 'patch'      res = '1.2.3' )
          ( version         = '1.2.3-alpha.0.beta'    release         = 'major'      res = '2.0.0' )
          ( version         = '1.2.3-alpha.0.beta'    release         = 'minor'      res = '1.3.0' )
          ( version         = '1.2.3-alpha.0.beta'    release         = 'patch'      res = '1.2.3' )
          ( version         = '1.2.4'                 release         = 'prerelease' res = '1.2.5-0' )
          ( version         = '1.2.3-0'               release         = 'prerelease' res = '1.2.3-1' )
          ( version         = '1.2.3-alpha.0'         release         = 'prerelease' res = '1.2.3-alpha.1' )
          ( version         = '1.2.3-alpha.1'         release         = 'prerelease' res = '1.2.3-alpha.2' )
          ( version         = '1.2.3-alpha.2'         release         = 'prerelease' res = '1.2.3-alpha.3' )
          ( version         = '1.2.3-alpha.0.beta'    release         = 'prerelease' res = '1.2.3-alpha.1.beta' )
          ( version         = '1.2.3-alpha.1.beta'    release         = 'prerelease' res = '1.2.3-alpha.2.beta' )
          ( version         = '1.2.3-alpha.2.beta'    release         = 'prerelease' res = '1.2.3-alpha.3.beta' )
          ( version         = '1.2.3-alpha.10.0.beta' release         = 'prerelease' res = '1.2.3-alpha.10.1.beta' )
          ( version         = '1.2.3-alpha.10.1.beta' release         = 'prerelease' res = '1.2.3-alpha.10.2.beta' )
          ( version         = '1.2.3-alpha.10.2.beta' release         = 'prerelease' res = '1.2.3-alpha.10.3.beta' )
          ( version         = '1.2.3-alpha.10.beta.0' release         = 'prerelease' res = '1.2.3-alpha.10.beta.1' )
          ( version         = '1.2.3-alpha.10.beta.1' release         = 'prerelease' res = '1.2.3-alpha.10.beta.2' )
          ( version         = '1.2.3-alpha.10.beta.2' release         = 'prerelease' res = '1.2.3-alpha.10.beta.3' )
          ( version         = '1.2.3-alpha.9.beta'    release         = 'prerelease' res = '1.2.3-alpha.10.beta' )
          ( version         = '1.2.3-alpha.10.beta'   release         = 'prerelease' res = '1.2.3-alpha.11.beta' )
          ( version         = '1.2.3-alpha.11.beta'   release         = 'prerelease' res = '1.2.3-alpha.12.beta' )
          ( version         = '1.0.0'                 release         = 'prepatch'   res = '1.0.1-alpha.1.1a.0'    identifier = 'alpha.1.1a' )
          ( version         = '1.2.0'                 release         = 'prepatch'   res = '1.2.1-0' )
          ( version         = '1.2.0-1'               release         = 'prepatch'   res = '1.2.1-0' )
          ( version         = '1.2.0'                 release         = 'preminor'   res = '1.3.0-0' )
          ( version         = '1.2.3-1'               release         = 'preminor'   res = '1.3.0-0' )
          ( version         = '1.2.0'                 release         = 'premajor'   res = '2.0.0-0' )
          ( version         = '1.2.3-1'               release         = 'premajor'   res = '2.0.0-0' )
          ( version         = '1.2.0-1'               release         = 'minor'      res = '1.2.0' )
          ( version         = '1.0.0-1'               release         = 'major'      res = '1.0.0' )
          ( version         = '1.0.0-1'               release         = 'release'    res = '1.0.0' )
          ( version         = '1.2.0-1'               release         = 'release'    res = '1.2.0' )
          ( version         = '1.2.3-1'               release         = 'release'    res = '1.2.3' )
          ( version         = '1.2.3'                 release         = 'release'    res = '' )
          " identifier
          ( version         = '1.2.3'                 release         = 'major'      res = '2.0.0'                 identifier = 'dev' )
          ( version         = '1.2.3'                 release         = 'minor'      res = '1.3.0'                 identifier = 'dev' )
          ( version         = '1.2.3'                 release         = 'patch'      res = '1.2.4'                 identifier = 'dev' )
          ( version         = '1.2.3tag'              release         = 'major'      res = '2.0.0'                 identifier = 'dev'
            loose           = abap_true )
          ( version         = '1.2.3-tag'             release         = 'major'      res = '2.0.0'                 identifier = 'dev' )
          ( version         = '1.2.3'                 release         = 'fake'       res = ''                      identifier = 'dev' )
          ( version         = '1.2.0-0'               release         = 'patch'      res = '1.2.0'                 identifier = 'dev' )
          ( version         = 'fake'                  release         = 'major'      res = ''                      identifier = 'dev' )
          ( version         = '1.2.3-4'               release         = 'major'      res = '2.0.0'                 identifier = 'dev' )
          ( version         = '1.2.3-4'               release         = 'minor'      res = '1.3.0'                 identifier = 'dev' )
          ( version         = '1.2.3-4'               release         = 'patch'      res = '1.2.3'                 identifier = 'dev' )
          ( version         = '1.2.3-alpha.0.beta'    release         = 'major'      res = '2.0.0'                 identifier = 'dev' )
          ( version         = '1.2.3-alpha.0.beta'    release         = 'minor'      res = '1.3.0'                 identifier = 'dev' )
          ( version         = '1.2.3-alpha.0.beta'    release         = 'patch'      res = '1.2.3'                 identifier = 'dev' )
          ( version         = '1.2.4'                 release         = 'prerelease' res = '1.2.5-dev.0'           identifier = 'dev' )
          ( version         = '1.2.3-0'               release         = 'prerelease' res = '1.2.3-dev.0'           identifier = 'dev' )
          ( version         = '1.2.3-alpha.0'         release         = 'prerelease' res = '1.2.3-dev.0'           identifier = 'dev' )
          ( version         = '1.2.3-alpha.0'         release         = 'prerelease' res = '1.2.3-alpha.1'         identifier = 'alpha' )
          ( version         = '1.2.3-alpha.0.beta'    release         = 'prerelease' res = '1.2.3-dev.0'           identifier = 'dev' )
          ( version         = '1.2.3-alpha.0.beta'    release         = 'prerelease' res = '1.2.3-alpha.1.beta'    identifier = 'alpha' )
          ( version         = '1.2.3-alpha.10.0.beta' release         = 'prerelease' res = '1.2.3-dev.0'           identifier = 'dev' )
          ( version         = '1.2.3-alpha.10.0.beta' release         = 'prerelease' res = '1.2.3-alpha.10.1.beta' identifier = 'alpha' )
          ( version         = '1.2.3-alpha.10.1.beta' release         = 'prerelease' res = '1.2.3-alpha.10.2.beta' identifier = 'alpha' )
          ( version         = '1.2.3-alpha.10.2.beta' release         = 'prerelease' res = '1.2.3-alpha.10.3.beta' identifier = 'alpha' )
          ( version         = '1.2.3-alpha.10.beta.0' release         = 'prerelease' res = '1.2.3-dev.0'           identifier = 'dev' )
          ( version         = '1.2.3-alpha.10.beta.0' release         = 'prerelease' res = '1.2.3-alpha.10.beta.1' identifier = 'alpha' )
          ( version         = '1.2.3-alpha.10.beta.1' release         = 'prerelease' res = '1.2.3-alpha.10.beta.2' identifier = 'alpha' )
          ( version         = '1.2.3-alpha.10.beta.2' release         = 'prerelease' res = '1.2.3-alpha.10.beta.3' identifier = 'alpha' )
          ( version         = '1.2.3-alpha.9.beta'    release         = 'prerelease' res = '1.2.3-dev.0'           identifier = 'dev' )
          ( version         = '1.2.3-alpha.9.beta'    release         = 'prerelease' res = '1.2.3-alpha.10.beta'   identifier = 'alpha' )
          ( version         = '1.2.3-alpha.10.beta'   release         = 'prerelease' res = '1.2.3-alpha.11.beta'   identifier = 'alpha' )
          ( version         = '1.2.3-alpha.11.beta'   release         = 'prerelease' res = '1.2.3-alpha.12.beta'   identifier = 'alpha' )
          ( version         = '1.2.0'                 release         = 'prepatch'   res = '1.2.1-dev.0'           identifier = 'dev' )
          ( version         = '1.2.0-1'               release         = 'prepatch'   res = '1.2.1-dev.0'           identifier = 'dev' )
          ( version         = '1.2.0'                 release         = 'preminor'   res = '1.3.0-dev.0'           identifier = 'dev' )
          ( version         = '1.2.3-1'               release         = 'preminor'   res = '1.3.0-dev.0'           identifier = 'dev' )
          ( version         = '1.2.0'                 release         = 'premajor'   res = '2.0.0-dev.0'           identifier = 'dev' )
          ( version         = '1.2.3-1'               release         = 'premajor'   res = '2.0.0-dev.0'           identifier = 'dev' )
          ( version         = '1.2.3-1'               release         = 'premajor'   res = '2.0.0-dev.1'           identifier = 'dev'
            identifier_base = '1' )
          ( version         = '1.2.0-1'               release         = 'minor'      res = '1.2.0'                 identifier = 'dev' )
          ( version         = '1.0.0-1'               release         = 'major'      res = '1.0.0'                 identifier = 'dev' )
          ( version         = '1.2.3-dev.bar'         release         = 'prerelease' res = '1.2.3-dev.0'           identifier = 'dev' )
          " prerelease
          ( version         = '1.2.3-0'               release         = 'prerelease' res = '1.2.3-1.0'             identifier = '1' )
          ( version         = '1.2.3-1.0'             release         = 'prerelease' res = '1.2.3-1.1'             identifier = '1' )
          ( version         = '1.2.3-1.1'             release         = 'prerelease' res = '1.2.3-1.2'             identifier = '1' )
          ( version         = '1.2.3-1.1'             release         = 'prerelease' res = '1.2.3-2.0'             identifier = '2' )
          ( version         = '1.2.0-1'               release         = 'prerelease' res = '1.2.0-alpha.0'         identifier = 'alpha'
            identifier_base = '0' )
          ( version         = '1.2.1'                 release         = 'prerelease' res = '1.2.2-alpha.0'         identifier = 'alpha'
            identifier_base = '0' )
          ( version         = '0.2.0'                 release         = 'prerelease' res = '0.2.1-alpha.0'         identifier = 'alpha'
            identifier_base = '0' )
          ( version         = '1.2.2'                 release         = 'prerelease' res = '1.2.3-alpha.1'         identifier = 'alpha'
            identifier_base = '1' )
          ( version         = '1.2.3'                 release         = 'prerelease' res = '1.2.4-alpha.1'         identifier = 'alpha'
            identifier_base = '1' )
          ( version         = '1.2.4'                 release         = 'prerelease' res = '1.2.5-alpha.1'         identifier = 'alpha'
            identifier_base = '1' )
          ( version         = '1.2.0'                 release         = 'prepatch'   res = '1.2.1-dev.1'           identifier = 'dev'
            identifier_base = '1' )
          ( version         = '1.2.0-1'               release         = 'prepatch'   res = '1.2.1-dev.1'           identifier = 'dev'
            identifier_base = '1' )
          ( version         = '1.2.0'                 release         = 'premajor'   res = '2.0.0-dev.0'           identifier = 'dev'
            identifier_base = '0' )
          ( version         = '1.2.3-1'               release         = 'premajor'   res = '2.0.0-dev.0'           identifier = 'dev'
            identifier_base = '0' )
          ( version         = '1.2.3-dev.bar'         release         = 'prerelease' res = '1.2.3-dev.0'           identifier = 'dev'
            identifier_base = '0' )
          ( version         = '1.2.3-dev.bar'         release         = 'prerelease' res = '1.2.3-dev.1'           identifier = 'dev'
            identifier_base = '1' )
          ( version         = '1.2.3-dev.bar'         release         = 'prerelease' res = '1.2.3-dev.bar.0'       identifier = ''
            identifier_base = '0' )
          ( version         = '1.2.3-dev.bar'         release         = 'prerelease' res = '1.2.3-dev.bar.1'       identifier = ''
            identifier_base = '1' )
          ( version         = '1.2.0'                 release         = 'preminor'   res = '1.3.0-dev.1'           identifier = 'dev'
            identifier_base = '1' )
          ( version         = '1.2.3-1'               release         = 'preminor'   res = '1.3.0-dev.0'           identifier = 'dev' )
          ( version         = '1.2.0'                 release         = 'prerelease' res = '1.2.1-1'               identifier = ''
            identifier_base = '1' )
          ( version         = '1.2.0-1'               release         = 'prerelease' res = '1.2.0-alpha'           identifier = 'alpha'
            identifier_base = 'false' )
          ( version         = '1.2.1'                 release         = 'prerelease' res = '1.2.2-alpha'           identifier = 'alpha'
            identifier_base = 'false' )
          ( version         = '1.2.2'                 release         = 'prerelease' res = '1.2.3-alpha'           identifier = 'alpha'
            identifier_base = 'false' )
          ( version         = '1.2.0'                 release         = 'prepatch'   res = '1.2.1-dev'             identifier = 'dev'
            identifier_base = 'false' )
          ( version         = '1.2.0-1'               release         = 'prepatch'   res = '1.2.1-dev'             identifier = 'dev'
            identifier_base = 'false' )
          ( version         = '1.2.0'                 release         = 'premajor'   res = '2.0.0-dev'             identifier = 'dev'
            identifier_base = 'false' )
          ( version         = '1.2.3-1'               release         = 'premajor'   res = '2.0.0-dev'             identifier = 'dev'
            identifier_base = 'false' )
          ( version         = '1.2.3-dev.bar'         release         = 'prerelease' res = '1.2.3-dev'             identifier = 'dev'
            identifier_base = 'false' )
          ( version         = '1.2.3-dev.bar'         release         = 'prerelease' res = '1.2.3-dev.baz'         identifier = 'dev.baz'
            identifier_base = 'false' )
          ( version         = '1.2.0'                 release         = 'preminor'   res = '1.3.0-dev'             identifier = 'dev'
            identifier_base = 'false' )
          ( version         = '1.2.3-1'               release         = 'preminor'   res = '1.3.0-dev'             identifier = 'dev'
            identifier_base = 'false' )
          ( version         = '1.2.3-dev'             release         = 'prerelease' res = ''                      identifier = 'dev'
            identifier_base = 'false' )
          ( version         = '1.2.0-dev'             release         = 'premajor'   res = '2.0.0-dev'             identifier = 'dev'
            identifier_base = 'false' )
          ( version         = '1.2.0-dev'             release         = 'preminor'   res = '1.3.0-beta'            identifier = 'beta'
            identifier_base = 'false' )
          ( version         = '1.2.0-dev'             release         = 'prepatch'   res = '1.2.1-dev'             identifier = 'dev'
            identifier_base = 'false' )
          ( version         = '1.2.0'                 release         = 'prerelease' res = ''                      identifier = ''
            identifier_base = 'false' )
          ( version         = '1.0.0-rc.1+build.4'    release         = 'prerelease' res = '1.0.0-rc.2'            identifier = 'rc'
            identifier_base = 'false' )
          ( version         = '1.2.0'                 release         = 'prerelease' res = ''                      identifier = 'invalid/preid'
            identifier_base = 'false' )
          ( version         = '1.2.0'                 release         = 'prerelease' res = ''                      identifier = 'invalid+build'
            identifier_base = 'false' )
          ( version         = '1.2.0beta'             release         = 'prerelease' res = ''                      identifier = 'invalid/preid'
            loose           = abap_true               identifier_base = 'false' ) ).
    
      ENDMETHOD.
    
      METHOD invalid_versions.
        " [value, reason, options]
    
        " none of these are semvers
        result = VALUE #(
          ( value  = |{ repeat( val = '1' occ = /apmg/if_apm_semver_constants=>max_length ) }.0.0|
            reason = 'too long' )
          ( value  = |{ /apmg/if_apm_semver_constants=>max_safe_integer }0.0.0|
            reason = 'too big' )
          ( value  = |0.{ /apmg/if_apm_semver_constants=>max_safe_integer }0.0|
            reason = 'too big' )
          ( value  = |0.0.{ /apmg/if_apm_semver_constants=>max_safe_integer }0|
            reason = 'too big' )
          ( value  = 'hello, world'
            reason = 'not a version' )
          ( value  = 'hello, world'
            reason = 'even loose, it''s still junk' loose = abap_true )
          ( value  = 'xyz'
            reason = 'even loose as an opt, same'   loose = abap_true )
          ( value  = 'NOT VALID'
            reason = 'nothing like a version' )
          ( value  = '1.2.3.4'
            reason = 'patch of a patch' )
          ( value  = '1.2'
            reason = 'no patch' )
          ( value  = '1'
            reason = 'no minor' )
          ( value  = ''
            reason = 'no data' ) ).
        " The following test cases can't happen in ABAP due to type system
        " ( value = /a regexp/ reason = 'regexp is not a string' )
        " ( value = /1.2.3/ reason = 'semver-ish regexp is not a string' )
        " ( value = { toString: () => '1.2.3' } reason = 'obj with a tostring is not a string' )
    
      ENDMETHOD.
    
      METHOD range_exclude.
        " [range, version, options]
    
        " version should not be included by range
        result = VALUE #(
          ( range = '1.0.0 - 2.0.0'           version = '2.2.3' )
          ( range = '1.2.3+asdf - 2.4.3+asdf' version = '1.2.3-pre.2' )
          ( range = '1.2.3+asdf - 2.4.3+asdf' version = '2.4.3-alpha' )
          ( range = '^1.2.3+build'            version = '2.0.0' )
          ( range = '^1.2.3+build'            version = '1.2.0' )
          ( range = '^1.2.3'                  version = '1.2.3-pre' )
          ( range = '^1.2'                    version = '1.2.0-pre' )
          ( range = '>1.2'                    version = '1.3.0-beta' )
          ( range = '<=1.2.3'                 version = '1.2.3-beta' )
          ( range = '^1.2.3'                  version = '1.2.3-beta' )
          ( range = '=0.7.x'                  version = '0.7.0-asdf' )
          ( range = '>=0.7.x'                 version = '0.7.0-asdf' )
          ( range = '<=0.7.x'                 version = '0.7.0-asdf' )
          "( range = '1' version = '1.0.0beta' { loose: 420 } )
          ( range = '<1'                      version = '1.0.0beta'   loose  = abap_true )
          ( range = '< 1'                     version = '1.0.0beta'   loose  = abap_true )
          ( range = '1.0.0'                   version = '1.0.1' )
          ( range = '>=1.0.0'                 version = '0.0.0' )
          ( range = '>=1.0.0'                 version = '0.0.1' )
          ( range = '>=1.0.0'                 version = '0.1.0' )
          ( range = '>1.0.0'                  version = '0.0.1' )
          ( range = '>1.0.0'                  version = '0.1.0' )
          ( range = '<=2.0.0'                 version = '3.0.0' )
          ( range = '<=2.0.0'                 version = '2.9999.9999' )
          ( range = '<=2.0.0'                 version = '2.2.9' )
          ( range = '<2.0.0'                  version = '2.9999.9999' )
          ( range = '<2.0.0'                  version = '2.2.9' )
          ( range = '>=0.1.97'                version = 'v0.1.93'     loose  = abap_true )
          ( range = '>=0.1.97'                version = '0.1.93' )
          ( range = '0.1.20 || 1.2.4'         version = '1.2.3' )
          ( range = '>=0.2.3 || <0.0.1'       version = '0.0.3' )
          ( range = '>=0.2.3 || <0.0.1'       version = '0.2.2' )
          "( range = '2.x.x' version = '1.1.3' { loose: NaN } )
          ( range = '2.x.x'                   version = '3.1.3' )
          ( range = '1.2.x'                   version = '1.3.3' )
          ( range = '1.2.x || 2.x'            version = '3.1.3' )
          ( range = '1.2.x || 2.x'            version = '1.1.3' )
          ( range = '2.*.*'                   version = '1.1.3' )
          ( range = '2.*.*'                   version = '3.1.3' )
          ( range = '1.2.*'                   version = '1.3.3' )
          ( range = '1.2.* || 2.*'            version = '3.1.3' )
          ( range = '1.2.* || 2.*'            version = '1.1.3' )
          ( range = '2'                       version = '1.1.2' )
          ( range = '2.3'                     version = '2.4.1' )
          ( range = '~0.0.1'                  version = '0.1.0-alpha' )
          ( range = '~0.0.1'                  version = '0.1.0' )
          ( range = '~2.4'                    version = '2.5.0' ) " >=2.4.0 <2.5.0
          ( range = '~2.4'                    version = '2.3.9' )
          ( range = '~>3.2.1'                 version = '3.3.2' ) " >=3.2.1 <3.3.0
          ( range = '~>3.2.1'                 version = '3.2.0' ) " >=3.2.1 <3.3.0
          ( range = '~1'                      version = '0.2.3' ) " >=1.0.0 <2.0.0
          ( range = '~>1'                     version = '2.2.3' )
          ( range = '~1.0'                    version = '1.1.0' ) " >=1.0.0 <1.1.0
          ( range = '<1'                      version = '1.0.0' )
          ( range = '>=1.2'                   version = '1.1.1' )
          ( range = '1'                       version = '2.0.0beta'   loose  = abap_true )
          ( range = '~v0.5.4-beta'            version = '0.5.4-alpha' )
          ( range = '=0.7.x'                  version = '0.8.2' )
          ( range = '>=0.7.x'                 version = '0.6.2' )
          ( range = '<0.7.x'                  version = '0.7.2' )
          ( range = '<1.2.3'                  version = '1.2.3-beta' )
          ( range = '=1.2.3'                  version = '1.2.3-beta' )
          ( range = '>1.2'                    version = '1.2.8' )
          ( range = '^0.0.1'                  version = '0.0.2-alpha' )
          ( range = '^0.0.1'                  version = '0.0.2' )
          ( range = '^1.2.3'                  version = '2.0.0-alpha' )
          ( range = '^1.2.3'                  version = '1.2.2' )
          ( range = '^1.2'                    version = '1.1.9' )
          ( range = '*'                       version = 'v1.2.3-foo'  loose  = abap_true )
          " invalid versions never satisfy, but shouldn't throw
          ( range = '*'                       version = 'not a version' )
          ( range = '>=2'                     version = 'glorp' )
          ( range = '>=2'                     version = '' )
          " incpre
          ( range = '2.x'                     version = '3.0.0-pre.0' incpre = abap_true )
          ( range = '^1.0.0'                  version = '1.0.0-rc1'   incpre = abap_true )
          ( range = '^1.0.0'                  version = '2.0.0-rc1'   incpre = abap_true )
          ( range = '^1.2.3-rc2'              version = '2.0.0'       incpre = abap_true )
          ( range = '^1.0.0'                  version = '2.0.0-rc1' )
          " from to
          ( range = '1 - 2'                   version = '3.0.0-pre'   incpre = abap_true )
          ( range = '1 - 2'                   version = '2.0.0-pre' )
          ( range = '1 - 2'                   version = '1.0.0-pre' )
          ( range = '1.0 - 2'                 version = '1.0.0-pre' )
          " prerelease
          ( range = '1.1.x'                   version = '1.0.0-a' )
          ( range = '1.1.x'                   version = '1.1.0-a' )
          ( range = '1.1.x'                   version = '1.2.0-a' )
          ( range = '1.1.x'                   version = '1.2.0-a'     incpre = abap_true )
          ( range = '1.1.x'                   version = '1.0.0-a'     incpre = abap_true )
          ( range = '1.x'                     version = '1.0.0-a' )
          ( range = '1.x'                     version = '1.1.0-a' )
          ( range = '1.x'                     version = '1.2.0-a' )
          ( range = '1.x'                     version = '0.0.0-a'     incpre = abap_true )
          ( range = '1.x'                     version = '2.0.0-a'     incpre = abap_true )
          ( range = '>=1.0.0 <1.1.0'          version = '1.1.0' )
          ( range = '>=1.0.0 <1.1.0'          version = '1.1.0'       incpre = abap_true )
          ( range = '>=1.0.0 <1.1.0'          version = '1.1.0-pre' )
          ( range = '>=1.0.0 <1.1.0-pre'      version = '1.1.0-pre' )
          ( range = '== 1.0.0 || foo'         version = '2.0.0'       loose  = abap_true ) ).
    
      ENDMETHOD.
    
      METHOD range_include.
        " [range, version, options]
    
        " version should be included by range
        result = VALUE #(
          ( range = '1.0.0 - 2.0.0' version = '1.2.3' )
          ( range = '^1.2.3+build' version = '1.2.3' )
          ( range = '^1.2.3+build' version = '1.3.0' )
          ( range = '1.2.3-pre+asdf - 2.4.3-pre+asdf' version = '1.2.3' )
          ( range = '1.2.3pre+asdf - 2.4.3-pre+asdf' version = '1.2.3' loose = abap_true )
          ( range = '1.2.3-pre+asdf - 2.4.3pre+asdf' version = '1.2.3' loose = abap_true )
          ( range = '1.2.3pre+asdf - 2.4.3pre+asdf' version = '1.2.3' loose = abap_true )
          ( range = '1.2.3-pre+asdf - 2.4.3-pre+asdf' version = '1.2.3-pre.2' )
          ( range = '1.2.3-pre+asdf - 2.4.3-pre+asdf' version = '2.4.3-alpha' )
          ( range = '1.2.3+asdf - 2.4.3+asdf' version = '1.2.3' )
          ( range = '1.0.0' version = '1.0.0' )
          ( range = '>=*' version = '0.2.4' )
          ( range = '' version = '1.0.0' )
          ( range = '*' version = '1.2.3' )
          " The following test cases can't happen in ABAP due to type system
          "( range = '*' version = 'v1.2.3', { loose: 123 } )
          "( range = '>=1.0.0' version = '1.0.0', /asdf/ )
          "( range = '>=1.0.0' version = '1.0.1', { loose: null } )
          "( range = '>=1.0.0' version = '1.1.0', { loose: 0 } )
          "( range = '>1.0.0' version = '1.0.1', { loose: undefined } )
          ( range = '>1.0.0' version = '1.1.0' )
          ( range = '<=2.0.0' version = '2.0.0' )
          ( range = '<=2.0.0' version = '1.9999.9999' )
          ( range = '<=2.0.0' version = '0.2.9' )
          ( range = '<2.0.0' version = '1.9999.9999' )
          ( range = '<2.0.0' version = '0.2.9' )
          ( range = '>= 1.0.0' version = '1.0.0' )
          ( range = '>=  1.0.0' version = '1.0.1' )
          ( range = '>=   1.0.0' version = '1.1.0' )
          ( range = '> 1.0.0' version = '1.0.1' )
          ( range = '>  1.0.0' version = '1.1.0' )
          ( range = '<=   2.0.0' version = '2.0.0' )
          ( range = '<= 2.0.0' version = '1.9999.9999' )
          ( range = '<=  2.0.0' version = '0.2.9' )
          ( range = '<    2.0.0' version = '1.9999.9999' )
          ( range = |<\t2.0.0| version = '0.2.9' )
          ( range = '>=0.1.97' version = 'v0.1.97' loose = abap_true )
          ( range = '>=0.1.97' version = '0.1.97' )
          ( range = '0.1.20 || 1.2.4' version = '1.2.4' )
          ( range = '>=0.2.3 || <0.0.1' version = '0.0.0' )
          ( range = '>=0.2.3 || <0.0.1' version = '0.2.3' )
          ( range = '>=0.2.3 || <0.0.1' version = '0.2.4' )
          ( range = '||' version = '1.3.4' )
          ( range = '2.x.x' version = '2.1.3' )
          ( range = '1.2.x' version = '1.2.3' )
          ( range = '1.2.x || 2.x' version = '2.1.3' )
          ( range = '1.2.x || 2.x' version = '1.2.3' )
          ( range = 'x' version = '1.2.3' )
          ( range = '2.*.*' version = '2.1.3' )
          ( range = '1.2.*' version = '1.2.3' )
          ( range = '1.2.* || 2.*' version = '2.1.3' )
          ( range = '1.2.* || 2.*' version = '1.2.3' )
          ( range = '*' version = '1.2.3' )
          ( range = '2' version = '2.1.2' )
          ( range = '2.3' version = '2.3.1' )
          ( range = '~0.0.1' version = '0.0.1' )
          ( range = '~0.0.1' version = '0.0.2' )
          ( range = '~x' version = '0.0.9' ) " >=2.4.0 <2.5.0
          ( range = '~2' version = '2.0.9' ) " >=2.4.0 <2.5.0
          ( range = '~2.4' version = '2.4.0' ) " >=2.4.0 <2.5.0
          ( range = '~2.4' version = '2.4.5' )
          ( range = '~>3.2.1' version = '3.2.2' ) " >=3.2.1 <3.3.0,
          ( range = '~1' version = '1.2.3' ) " >=1.0.0 <2.0.0
          ( range = '~>1' version = '1.2.3' )
          ( range = '~> 1' version = '1.2.3' )
          ( range = '~1.0' version = '1.0.2' ) " >=1.0.0 <1.1.0,
          ( range = '~ 1.0' version = '1.0.2' )
          ( range = '~ 1.0.3' version = '1.0.12' )
          ( range = '~ 1.0.3alpha' version = '1.0.12' loose = abap_true )
          ( range = '>=1' version = '1.0.0' )
          ( range = '>= 1' version = '1.0.0' )
          ( range = '<1.2' version = '1.1.1' )
          ( range = '< 1.2' version = '1.1.1' )
          ( range = '~v0.5.4-pre' version = '0.5.5' )
          ( range = '~v0.5.4-pre' version = '0.5.4' )
          ( range = '=0.7.x' version = '0.7.2' )
          ( range = '<=0.7.x' version = '0.7.2' )
          ( range = '>=0.7.x' version = '0.7.2' )
          ( range = '<=0.7.x' version = '0.6.2' )
          ( range = '~1.2.1 >=1.2.3' version = '1.2.3' )
          ( range = '~1.2.1 =1.2.3' version = '1.2.3' )
          ( range = '~1.2.1 1.2.3' version = '1.2.3' )
          ( range = '~1.2.1 >=1.2.3 1.2.3' version = '1.2.3' )
          ( range = '~1.2.1 1.2.3 >=1.2.3' version = '1.2.3' )
          ( range = '>=1.2.1 1.2.3' version = '1.2.3' )
          ( range = '1.2.3 >=1.2.1' version = '1.2.3' )
          ( range = '>=1.2.3 >=1.2.1' version = '1.2.3' )
          ( range = '>=1.2.1 >=1.2.3' version = '1.2.3' )
          ( range = '>=1.2' version = '1.2.8' )
          ( range = '^1.2.3' version = '1.8.1' )
          ( range = '^0.1.2' version = '0.1.2' )
          ( range = '^0.1' version = '0.1.2' )
          ( range = '^0.0.1' version = '0.0.1' )
          ( range = '^1.2' version = '1.4.2' )
          ( range = '^1.2 ^1' version = '1.4.2' )
          ( range = '^1.2.3-alpha' version = '1.2.3-pre' )
          ( range = '^1.2.0-alpha' version = '1.2.0-pre' )
          ( range = '^0.0.1-alpha' version = '0.0.1-beta' )
          ( range = '^0.0.1-alpha' version = '0.0.1' )
          ( range = '^0.1.1-alpha' version = '0.1.1-beta' )
          ( range = '^x' version = '1.2.3' )
          ( range = 'x - 1.0.0' version = '0.9.7' )
          ( range = 'x - 1.x' version = '0.9.7' )
          ( range = '1.0.0 - x' version = '1.9.7' )
          ( range = '1.x - x' version = '1.9.7' )
          ( range = '<=7.x' version = '7.9.9' )
          ( range = '2.x' version = '2.0.0-pre.0' incpre = abap_true )
          ( range = '2.x' version = '2.1.0-pre.0' incpre = abap_true )
          ( range = '1.1.x' version = '1.1.0-a' incpre = abap_true )
          ( range = '1.1.x' version = '1.1.1-a' incpre = abap_true )
          ( range = '*' version = '1.0.0-rc1' incpre = abap_true )
          ( range = '^1.0.0-0' version = '1.0.1-rc1' incpre = abap_true )
          ( range = '^1.0.0-rc2' version = '1.0.1-rc1' incpre = abap_true )
          ( range = '^1.0.0' version = '1.0.1-rc1' incpre = abap_true )
          ( range = '^1.0.0' version = '1.1.0-rc1' incpre = abap_true )
          ( range = '1 - 2' version = '2.0.0-pre' incpre = abap_true )
          ( range = '1 - 2' version = '1.0.0-pre' incpre = abap_true )
          ( range = '1.0 - 2' version = '1.0.0-pre' incpre = abap_true )
          ( range = '=0.7.x' version = '0.7.0-asdf' incpre = abap_true )
          ( range = '>=0.7.x' version = '0.7.0-asdf' incpre = abap_true )
          ( range = '<=0.7.x' version = '0.7.0-asdf' incpre = abap_true )
          ( range = '>=1.0.0 <=1.1.0' version = '1.1.0-pre' incpre = abap_true ) ).
    
      ENDMETHOD.
    
      METHOD range_intersection.
        " [r0, r1, expected intersection]
    
        result = VALUE #(
          ( r0 = '1.3.0 || <1.0.0 >2.0.0'                        r1  = '1.3.0 || <1.0.0 >2.0.0' res = abap_true )
          ( r0 = '<1.0.0 >2.0.0'                                 r1  = '>0.0.0'                 res = abap_false )
          ( r0 = '>0.0.0'                                        r1  = '<1.0.0 >2.0.0'          res = abap_false )
          ( r0 = '<1.0.0 >2.0.0'                                 r1  = '>1.4.0 <1.6.0'          res = abap_false )
          ( r0 = '<1.0.0 >2.0.0'                                 r1  = '>1.4.0 <1.6.0 || 2.0.0' res = abap_false )
          ( r0 = '>1.0.0 <=2.0.0'                                r1  = '2.0.0'                  res = abap_true )
          ( r0 = '<1.0.0 >=2.0.0'                                r1  = '2.1.0'                  res = abap_false )
          ( r0 = '<1.0.0 >=2.0.0'                                r1  = '>1.4.0 <1.6.0 || 2.0.0' res = abap_false )
          ( r0 = '1.5.x'                                         r1  = '<1.5.0 || >=1.6.0'      res = abap_false )
          ( r0 = '<1.5.0 || >=1.6.0'                             r1  = '1.5.x'                  res = abap_false )
          ( r0 = '<1.6.16 || >=1.7.0 <1.7.11 || >=1.8.0 <1.8.2'
            r1 = '>=1.6.16 <1.7.0 || >=1.7.11 <1.8.0 || >=1.8.2' res = abap_false )
          ( r0 = '<=1.6.16 || >=1.7.0 <1.7.11 || >=1.8.0 <1.8.2'
            r1 = '>=1.6.16 <1.7.0 || >=1.7.11 <1.8.0 || >=1.8.2' res = abap_true )
          ( r0 = '>=1.0.0'                                       r1  = '<=1.0.0'                res = abap_true )
          ( r0 = '>1.0.0 <1.0.0'                                 r1  = '<=0.0.0'                res = abap_false )
          ( r0 = '*'                                             r1  = '0.0.1'                  res = abap_true )
          ( r0 = '*'                                             r1  = '>=1.0.0'                res = abap_true )
          ( r0 = '*'                                             r1  = '>1.0.0'                 res = abap_true )
          ( r0 = '*'                                             r1  = '~1.0.0'                 res = abap_true )
          ( r0 = '*'                                             r1  = '<1.6.0'                 res = abap_true )
          ( r0 = '*'                                             r1  = '<=1.6.0'                res = abap_true )
          ( r0 = '1.*'                                           r1  = '0.0.1'                  res = abap_false )
          ( r0 = '1.*'                                           r1  = '2.0.0'                  res = abap_false )
          ( r0 = '1.*'                                           r1  = '1.0.0'                  res = abap_true )
          ( r0 = '1.*'                                           r1  = '<2.0.0'                 res = abap_true )
          ( r0 = '1.*'                                           r1  = '>1.0.0'                 res = abap_true )
          ( r0 = '1.*'                                           r1  = '<=1.0.0'                res = abap_true )
          ( r0 = '1.*'                                           r1  = '^1.0.0'                 res = abap_true )
          ( r0 = '1.0.*'                                         r1  = '0.0.1'                  res = abap_false )
          ( r0 = '1.0.*'                                         r1  = '<0.0.1'                 res = abap_false )
          ( r0 = '1.0.*'                                         r1  = '>0.0.1'                 res = abap_true )
          ( r0 = '*'                                             r1  = '1.3.0 || <1.0.0 >2.0.0' res = abap_true )
          ( r0 = '1.3.0 || <1.0.0 >2.0.0'                        r1  = '*'                      res = abap_true )
          ( r0 = '1.*'                                           r1  = '1.3.0 || <1.0.0 >2.0.0' res = abap_true )
          ( r0 = 'x'                                             r1  = '0.0.1'                  res = abap_true )
          ( r0 = 'x'                                             r1  = '>=1.0.0'                res = abap_true )
          ( r0 = 'x'                                             r1  = '>1.0.0'                 res = abap_true )
          ( r0 = 'x'                                             r1  = '~1.0.0'                 res = abap_true )
          ( r0 = 'x'                                             r1  = '<1.6.0'                 res = abap_true )
          ( r0 = 'x'                                             r1  = '<=1.6.0'                res = abap_true )
          ( r0 = '1.x'                                           r1  = '0.0.1'                  res = abap_false )
          ( r0 = '1.x'                                           r1  = '2.0.0'                  res = abap_false )
          ( r0 = '1.x'                                           r1  = '1.0.0'                  res = abap_true )
          ( r0 = '1.x'                                           r1  = '<2.0.0'                 res = abap_true )
          ( r0 = '1.x'                                           r1  = '>1.0.0'                 res = abap_true )
          ( r0 = '1.x'                                           r1  = '<=1.0.0'                res = abap_true )
          ( r0 = '1.x'                                           r1  = '^1.0.0'                 res = abap_true )
          ( r0 = '1.0.x'                                         r1  = '0.0.1'                  res = abap_false )
          ( r0 = '1.0.x'                                         r1  = '<0.0.1'                 res = abap_false )
          ( r0 = '1.0.x'                                         r1  = '>0.0.1'                 res = abap_true )
          ( r0 = 'x'                                             r1  = '1.3.0 || <1.0.0 >2.0.0' res = abap_true )
          ( r0 = '1.3.0 || <1.0.0 >2.0.0'                        r1  = 'x'                      res = abap_true )
          ( r0 = '1.x'                                           r1  = '1.3.0 || <1.0.0 >2.0.0' res = abap_true )
          ( r0 = '*'                                             r1  = '*'                      res = abap_true )
          ( r0 = 'x'                                             r1  = ''                       res = abap_true ) ).
    
      ENDMETHOD.
    
      METHOD range_parse.
        " [range, canonical result, options]
    
        " '' result means it's not a valid range
        " '*' is the return value from functions.validRange(), but
        " new Range().range will be '' in those cases
        result = VALUE #(
          ( range = '1.x.x+build >2.x+build' res = '>=1.0.0 <2.0.0-0 >=3.0.0' )
          ( range = '1.0.0 - 2.0.0' res = '>=1.0.0 <=2.0.0' )
          ( range = '1.0.0 - 2.0.0' res = '>=1.0.0-0 <2.0.1-0' incpre = abap_true )
          ( range = '1 - 2' res = '>=1.0.0 <3.0.0-0' )
          ( range = '1 - 2' res = '>=1.0.0-0 <3.0.0-0' incpre = abap_true )
          ( range = '1.0 - 2.0' res = '>=1.0.0 <2.1.0-0' )
          ( range = '1.0 - 2.0' res = '>=1.0.0-0 <2.1.0-0' incpre = abap_true )
          ( range = '1.0.0' res = '1.0.0' loose = abap_false )
          ( range = '>=*' res = '*' )
          ( range = '' res = '*' )
          ( range = '*' res = '*' )
          ( range = '>=1.0.0' res = '>=1.0.0' )
          ( range = '>1.0.0' res = '>1.0.0' )
          ( range = '<=2.0.0' res = '<=2.0.0' )
          ( range = '1' res = '>=1.0.0 <2.0.0-0' )
          ( range = '<2.0.0' res = '<2.0.0' )
          ( range = '>= 1.0.0' res = '>=1.0.0' )
          ( range = '>=  1.0.0' res = '>=1.0.0' )
          ( range = '>=   1.0.0' res = '>=1.0.0' )
          ( range = '> 1.0.0' res = '>1.0.0' )
          ( range = '>  1.0.0' res = '>1.0.0' )
          ( range = '<=   2.0.0' res = '<=2.0.0' )
          ( range = '<= 2.0.0' res = '<=2.0.0' )
          ( range = '<=  2.0.0' res = '<=2.0.0' )
          ( range = '<    2.0.0' res = '<2.0.0' )
          ( range = |<\t2.0.0| res = '<2.0.0' )
          ( range = '>=0.1.97' res = '>=0.1.97' )
          ( range = '0.1.20 || 1.2.4' res = '0.1.20||1.2.4' )
          ( range = '>=0.2.3 || <0.0.1' res = '>=0.2.3||<0.0.1' )
          ( range = '||' res = '*' )
          ( range = '2.x.x' res = '>=2.0.0 <3.0.0-0' )
          ( range = '1.2.x' res = '>=1.2.0 <1.3.0-0' )
          ( range = '1.2.x || 2.x' res = '>=1.2.0 <1.3.0-0||>=2.0.0 <3.0.0-0' )
          ( range = 'x' res = '*' )
          ( range = '2.*.*' res = '>=2.0.0 <3.0.0-0' )
          ( range = '1.2.*' res = '>=1.2.0 <1.3.0-0' )
          ( range = '1.2.* || 2.*' res = '>=1.2.0 <1.3.0-0||>=2.0.0 <3.0.0-0' )
          ( range = '2' res = '>=2.0.0 <3.0.0-0' )
          ( range = '2.3' res = '>=2.3.0 <2.4.0-0' )
          ( range = '~2.4' res = '>=2.4.0 <2.5.0-0' )
          ( range = '~>3.2.1' res = '>=3.2.1 <3.3.0-0' )
          ( range = '~1' res = '>=1.0.0 <2.0.0-0' )
          ( range = '~>1' res = '>=1.0.0 <2.0.0-0' )
          ( range = '~> 1' res = '>=1.0.0 <2.0.0-0' )
          ( range = '~1.0' res = '>=1.0.0 <1.1.0-0' )
          ( range = '~ 1.0' res = '>=1.0.0 <1.1.0-0' )
          ( range = '^0' res = '<1.0.0-0' )
          ( range = '^ 1' res = '>=1.0.0 <2.0.0-0' )
          ( range = '^0.1' res = '>=0.1.0 <0.2.0-0' )
          ( range = '^1.0' res = '>=1.0.0 <2.0.0-0' )
          ( range = '^1.2' res = '>=1.2.0 <2.0.0-0' )
          ( range = '^0.0.1' res = '>=0.0.1 <0.0.2-0' )
          ( range = '^0.0.1-beta' res = '>=0.0.1-beta <0.0.2-0' )
          ( range = '^0.1.2' res = '>=0.1.2 <0.2.0-0' )
          ( range = '^1.2.3' res = '>=1.2.3 <2.0.0-0' )
          ( range = '^1.2.3-beta.4' res = '>=1.2.3-beta.4 <2.0.0-0' )
          ( range = '<1' res = '<1.0.0-0' )
          ( range = '< 1' res = '<1.0.0-0' )
          ( range = '>=1' res = '>=1.0.0' )
          ( range = '>= 1' res = '>=1.0.0' )
          ( range = '<1.2' res = '<1.2.0-0' )
          ( range = '< 1.2' res = '<1.2.0-0' )
          ( range = '>01.02.03' res = '>1.2.3' loose = abap_true )
          ( range = '>01.02.03' res = '' )
          ( range = '~1.2.3beta' res = '>=1.2.3-beta <1.3.0-0' loose = abap_true )
          ( range = '~1.2.3beta' res = '' )
          ( range = '^ 1.2 ^ 1' res = '>=1.2.0 <2.0.0-0 >=1.0.0' )
          ( range = '1.2 - 3.4.5' res = '>=1.2.0 <=3.4.5' )
          ( range = '1.2.3 - 3.4' res = '>=1.2.3 <3.5.0-0' )
          ( range = '1.2 - 3.4' res = '>=1.2.0 <3.5.0-0' )
          ( range = '>1' res = '>=2.0.0' )
          ( range = '>1.2' res = '>=1.3.0' )
          ( range = '>X' res = '<0.0.0-0' )
          ( range = 'max_safe_integer }.0.0| res = '' )
          ( range = |={ /apmg/if_apm_semver_constants=>max_safe_integer }.0.0|
              res = |{ /apmg/if_apm_semver_constants=>max_safe_integer }.0.0| )
          ( range = |^{ /apmg/if_apm_semver_constants=>max_safe_integer - 1 }.0.0|
              res = |>={ /apmg/if_apm_semver_constants=>max_safe_integer - 1 }.0.0 | &&
                    |<{ /apmg/if_apm_semver_constants=>max_safe_integer }.0.0-0| )
          " x-ranges with build metadata
          ( range = '1.x.x+build >2.x+build' res = '>=1.0.0 <2.0.0-0 >=3.0.0' )
          ( range = '>=1.x+build <2.x.x+build' res = '>=1.0.0 <2.0.0-0' )
          ( range = '1.x.x+build || 2.x.x+build' res = '>=1.0.0 <2.0.0-0||>=2.0.0 <3.0.0-0' )
          ( range = '1.x+build.123' res = '>=1.0.0 <2.0.0-0' )
          ( range = '1.x.x+meta-data' res = '>=1.0.0 <2.0.0-0' )
          ( range = '1.x.x+build.123 >2.x.x+meta-data' res = '>=1.0.0 <2.0.0-0 >=3.0.0' )
          ( range = '1.x.x+build <2.x.x+meta' res = '>=1.0.0 <2.0.0-0' )
          ( range = '>1.x+build <=2.x.x+meta' res = '>=2.0.0 <3.0.0-0' )
          ( range = ' 1.x.x+build   >2.x.x+build  ' res = '>=1.0.0 <2.0.0-0 >=3.0.0' )
          ( range = '^1.x+build' res = '>=1.0.0 <2.0.0-0' )
          ( range = '^1.x.x+build' res = '>=1.0.0 <2.0.0-0' )
          ( range = '^1.2.x+build' res = '>=1.2.0 <2.0.0-0' )
          ( range = '^1.x+meta-data' res = '>=1.0.0 <2.0.0-0' )
          ( range = '^1.x.x+build.123' res = '>=1.0.0 <2.0.0-0' )
          ( range = '~1.x+build' res = '>=1.0.0 <2.0.0-0' )
          ( range = '~1.x.x+build' res = '>=1.0.0 <2.0.0-0' )
          ( range = '~1.2.x+build' res = '>=1.2.0 <1.3.0-0' )
          ( range = '~1.x+meta-data' res = '>=1.0.0 <2.0.0-0' )
          ( range = '~1.x.x+build.123' res = '>=1.0.0 <2.0.0-0' )
          ( range = '^1.x.x+build || ~2.x.x+meta' res = '>=1.0.0 <2.0.0-0||>=2.0.0 <3.0.0-0' )
          ( range = '~1.x.x+build >2.x+meta' res = '>=1.0.0 <2.0.0-0 >=3.0.0' )
          ( range = '^1.x+build.123 <2.x.x+meta-data' res = '>=1.0.0 <2.0.0-0' )
          " x-ranges with prerelease and build
          ( range = '1.x.x-alpha+build' res = '>=1.0.0 <2.0.0-0' )
          ( range = '>1.x.x-alpha+build' res = '>=2.0.0' )
          ( range = '>=1.x.x-alpha+build <2.x.x+build' res = '>=1.0.0 <2.0.0-0' )
          ( range = '1.x.x-alpha+build || 2.x.x+build' res = '>=1.0.0 <2.0.0-0||>=2.0.0 <3.0.0-0' ) ).
    
      ENDMETHOD.
    
      METHOD valid_versions.
        " [version, major, minor, patch, prerelease[], build[]]
    
        result = VALUE #(
          " One is a Version
          ( version = '1.0.0'  major = 1 minor = 0 patch = 0 )
          ( version = '2.1.0'  major = 2 minor = 1 patch = 0 )
          ( version = '3.2.1'  major = 3 minor = 2 patch = 1 )
          ( version = 'v1.2.3' major = 1 minor = 2 patch = 3 )
          " prerelease
          ( version = '1.2.3-0'            major = 1 minor = 2 patch = 3 prerelease = VALUE #( ( `0` ) ) )
          ( version = '1.2.3-123'          major = 1 minor = 2 patch = 3 prerelease = VALUE #( ( `123` ) ) )
          ( version = '1.2.3-1.2.3'        major = 1 minor = 2 patch = 3 prerelease = VALUE #( ( `1` ) ( `2` ) ( `3` ) ) )
          ( version = '1.2.3-1a'           major = 1 minor = 2 patch = 3 prerelease = VALUE #( ( `1a` ) ) )
          ( version = '1.2.3-a1'           major = 1 minor = 2 patch = 3 prerelease = VALUE #( ( `a1` ) ) )
          ( version = '1.2.3-alpha'        major = 1 minor = 2 patch = 3 prerelease = VALUE #( ( `alpha` ) ) )
          ( version = '1.2.3-alpha.1'      major = 1 minor = 2 patch = 3 prerelease = VALUE #( ( `alpha` ) ( `1` ) ) )
          ( version = '1.2.3-alpha-1'      major = 1 minor = 2 patch = 3 prerelease = VALUE #( ( `alpha-1` ) ) )
          ( version = '1.2.3-alpha-.-beta' major = 1 minor = 2 patch = 3
            prerelease = VALUE #( ( `alpha-` ) ( `-beta` ) ) )
          " build
          ( version = '1.2.3+456'              major = 1 minor = 2 patch = 3 build = VALUE #( ( `456` ) ) )
          ( version = '1.2.3+build'            major = 1 minor = 2 patch = 3 build = VALUE #( ( `build` ) ) )
          ( version = '1.2.3+new-build'        major = 1 minor = 2 patch = 3 build = VALUE #( ( `new-build` ) ) )
          ( version = '1.2.3+build.1'          major = 1 minor = 2 patch = 3 build = VALUE #( ( `build` ) ( `1` ) ) )
          ( version = '1.2.3+build.1a'         major = 1 minor = 2 patch = 3 build = VALUE #( ( `build` ) ( `1a` ) ) )
          ( version = '1.2.3+build.a1'         major = 1 minor = 2 patch = 3 build = VALUE #( ( `build` ) ( `a1` ) ) )
          ( version = '1.2.3+build.alpha'      major = 1 minor = 2 patch = 3
            build = VALUE #( ( `build` ) ( `alpha` ) ) )
          ( version = '1.2.3+build.alpha.beta' major = 1 minor = 2 patch = 3
            build = VALUE #( ( `build` ) ( `alpha` ) ( `beta` ) ) )
          " mixed
          ( version = '1.2.3-alpha+build'      major = 1 minor = 2 patch = 3
            prerelease = VALUE #( ( `alpha` ) ) build = VALUE #( ( `build` ) ) ) ).
    
      ENDMETHOD.
    
      METHOD version_gt_range.
        " [range, version, options]
    
        " Version should be greater than range
        result = VALUE #(
          ( range = '~1.2.2'          version = '1.3.0' )
          ( range = '~0.6.1-1'        version = '0.7.1-1' )
          ( range = '1.0.0 - 2.0.0'   version = '2.0.1' )
          ( range = '1.0.0'           version = '1.0.1-beta1' )
          ( range = '1.0.0'           version = '2.0.0' )
          ( range = '<=2.0.0'         version = '2.1.1' )
          ( range = '<=2.0.0'         version = '3.2.9' )
          ( range = '<2.0.0'          version = '2.0.0' )
          ( range = '0.1.20 || 1.2.4' version = '1.2.5' )
          ( range = '2.x.x'           version = '3.0.0' )
          ( range = '1.2.x'           version = '1.3.0' )
          ( range = '1.2.x || 2.x'    version = '3.0.0' )
          ( range = '2.*.*'           version = '5.0.1' )
          ( range = '1.2.*'           version = '1.3.3' )
          ( range = '1.2.* || 2.*'    version = '4.0.0' )
          ( range = '2'               version = '3.0.0' )
          ( range = '2.3'             version = '2.4.2' )
          ( range = '~2.4'            version = '2.5.0' ) " >=2.4.0 <2.5.0
          ( range = '~2.4'            version = '2.5.5' )
          ( range = '~>3.2.1'         version = '3.3.0' ) " >=3.2.1 <3.3.0
          ( range = '~1'              version = '2.2.3' ) " >=1.0.0 <2.0.0
          ( range = '~>1'             version = '2.2.4' )
          ( range = '~> 1'            version = '3.2.3' )
          ( range = '~1.0'            version = '1.1.2' ) " >=1.0.0 <1.1.0
          ( range = '~ 1.0'           version = '1.1.0' )
          ( range = '<1.2'            version = '1.2.0' )
          ( range = '< 1.2'           version = '1.2.1' )
          ( range = '1'               version = '2.0.0beta' loose = abap_true )
          ( range = '~v0.5.4-pre'     version = '0.6.0' )
          ( range = '~v0.5.4-pre'     version = '0.6.1-pre' )
          ( range = '=0.7.x'          version = '0.8.0' )
          ( range = '=0.7.x'          version = '0.8.0-asdf' )
          ( range = '<0.7.x'          version = '0.7.0' )
          ( range = '1.0.0 - 2.0.0'   version = '2.2.3' )
          ( range = '1.0.0'           version = '1.0.1' )
          ( range = '<=2.0.0'         version = '3.0.0' )
          ( range = '<=2.0.0'         version = '2.9999.9999' )
          ( range = '<=2.0.0'         version = '2.2.9' )
          ( range = '<2.0.0'          version = '2.9999.9999' )
          ( range = '<2.0.0'          version = '2.2.9' )
          ( range = '2.x.x'           version = '3.1.3' )
          ( range = '1.2.x'           version = '1.3.3' )
          ( range = '1.2.x || 2.x'    version = '3.1.3' )
          ( range = '2.*.*'           version = '3.1.3' )
          ( range = '1.2.* || 2.*'    version = '3.1.3' )
          ( range = '2'               version = '3.1.2' )
          ( range = '2.3'             version = '2.4.1' )
          ( range = '~>3.2.1'         version = '3.3.2' ) " >=3.2.1 <3.3.0
          ( range = '~>1'             version = '2.2.3' )
          ( range = '~1.0'            version = '1.1.0' ) " >=1.0.0 <1.1.0
          ( range = '<1'              version = '1.0.0' )
          ( range = '<1'              version = '1.0.0beta' loose = abap_true )
          ( range = '< 1'             version = '1.0.0beta' loose = abap_true )
          ( range = '=0.7.x'          version = '0.8.2' )
          ( range = '<0.7.x'          version = '0.7.2' )
          ( range = '0.7.x'           version = '0.7.2-beta' ) ).
    
      ENDMETHOD.
    
      METHOD version_lt_range.
        " [range, version, options]
    
        " Version should be less than range
        result = VALUE #(
          ( range = '~1.2.2'          version = '1.2.1' )
          ( range = '~0.6.1-1'        version = '0.6.1-0' )
          ( range = '1.0.0 - 2.0.0'   version = '0.0.1' )
          ( range = '1.0.0-beta.2'    version = '1.0.0-beta.1' )
          ( range = '1.0.0'           version = '0.0.0' )
          ( range = '>=2.0.0'         version = '1.1.1' )
          ( range = '>=2.0.0'         version = '1.2.9' )
          ( range = '>2.0.0'          version = '2.0.0' )
          ( range = '0.1.20 || 1.2.4' version = '0.1.5' )
          ( range = '2.x.x'           version = '1.0.0' )
          ( range = '1.2.x'           version = '1.1.0' )
          ( range = '1.2.x || 2.x'    version = '1.0.0' )
          ( range = '2.*.*'           version = '1.0.1' )
          ( range = '1.2.*'           version = '1.1.3' )
          ( range = '1.2.* || 2.*'    version = '1.1.9999' )
          ( range = '2'               version = '1.0.0' )
          ( range = '2.3'             version = '2.2.2' )
          ( range = '~2.4'            version = '2.3.0' ) " >=2.4.0 <2.5.0
          ( range = '~2.4'            version = '2.3.5' )
          ( range = '~>3.2.1'         version = '3.2.0' ) " >=3.2.1 <3.3.0
          ( range = '~1'              version = '0.2.3' ) " >=1.0.0 <2.0.0
          ( range = '~>1'             version = '0.2.4' )
          ( range = '~> 1'            version = '0.2.3' )
          ( range = '~1.0'            version = '0.1.2' ) " >=1.0.0 <1.1.0
          ( range = '~ 1.0'           version = '0.1.0' )
          ( range = '>1.2'            version = '1.2.0' )
          ( range = '> 1.2'           version = '1.2.1' )
          ( range = '1'               version = '0.0.0beta' loose = abap_true )
          ( range = '~v0.5.4-pre'     version = '0.5.4-alpha' )
          ( range = '=0.7.x'          version = '0.6.0' )
          ( range = '=0.7.x'          version = '0.6.0-asdf' )
          ( range = '>=0.7.x'         version = '0.6.0' )
          ( range = '1.0.0 - 2.0.0'   version = '0.2.3' )
          ( range = '1.0.0'           version = '0.0.1' )
          ( range = '>=2.0.0'         version = '1.0.0' )
          ( range = '>=2.0.0'         version = '1.9999.9999' )
          ( range = '>2.0.0'          version = '1.2.9' )
          ( range = '2.x.x'           version = '1.1.3' )
          ( range = '1.2.x'           version = '1.1.3' )
          ( range = '1.2.x || 2.x'    version = '1.1.3' )
          ( range = '2.*.*'           version = '1.1.3' )
          ( range = '1.2.* || 2.*'    version = '1.1.3' )
          ( range = '2'               version = '1.9999.9999' )
          ( range = '2.3'             version = '2.2.1' )
          ( range = '~>3.2.1'         version = '2.3.2' ) " >=3.2.1 <3.3.0
          ( range = '~>1'             version = '0.2.3' )
          ( range = '~1.0'            version = '0.0.0' ) " >=1.0.0 <1.1.0
          ( range = '>1'              version = '1.0.0' )
          ( range = '2'               version = '1.0.0beta' loose = abap_true )
          ( range = '>1'              version = '1.0.0beta' loose = abap_true )
          ( range = '> 1'             version = '1.0.0beta' loose = abap_true )
          ( range = '=0.7.x'          version = '0.6.2' )
          ( range = '=0.7.x'          version = '0.7.0-asdf' )
          ( range = '^1'              version = '1.0.0-0' )
          ( range = '>=0.7.x'         version = '0.7.0-asdf' )
          ( range = '1'               version = '1.0.0beta' loose = abap_true )
          ( range = '>=0.7.x'         version = '0.6.2' )
          ( range = '>1.2.3'          version = '1.3.0-alpha' ) ).
    
      ENDMETHOD.
    
      METHOD version_not_gt_range.
        " [range, version, options]
    
        " Version should NOT be greater than range
        result = VALUE #(
          ( range = '~0.6.1-1' version = '0.6.1-1' )
          ( range = '1.0.0 - 2.0.0' version = '1.2.3' )
          ( range = '1.0.0 - 2.0.0' version = '0.9.9' )
          ( range = '1.0.0' version = '1.0.0' )
          ( range = '>=*' version = '0.2.4' )
          ( range = '' version = '1.0.0' loose = abap_true )
          ( range = '*' version = '1.2.3' )
          ( range = '*' version = 'v1.2.3-foo' )
          ( range = '>=1.0.0' version = '1.0.0' )
          ( range = '>=1.0.0' version = '1.0.1' )
          ( range = '>=1.0.0' version = '1.1.0' )
          ( range = '>1.0.0' version = '1.0.1' )
          ( range = '>1.0.0' version = '1.1.0' )
          ( range = '<=2.0.0' version = '2.0.0' )
          ( range = '<=2.0.0' version = '1.9999.9999' )
          ( range = '<=2.0.0' version = '0.2.9' )
          ( range = '<2.0.0' version = '1.9999.9999' )
          ( range = '<2.0.0' version = '0.2.9' )
          ( range = '>= 1.0.0' version = '1.0.0' )
          ( range = '>=  1.0.0' version = '1.0.1' )
          ( range = '>=   1.0.0' version = '1.1.0' )
          ( range = '> 1.0.0' version = '1.0.1' )
          ( range = '>  1.0.0' version = '1.1.0' )
          ( range = '<=   2.0.0' version = '2.0.0' )
          ( range = '<= 2.0.0' version = '1.9999.9999' )
          ( range = '<=  2.0.0' version = '0.2.9' )
          ( range = '<    2.0.0' version = '1.9999.9999' )
          ( range = |<\t2.0.0| version = '0.2.9' )
          ( range = '>=0.1.97' version = 'v0.1.97' )
          ( range = '>=0.1.97' version = '0.1.97' )
          ( range = '0.1.20 || 1.2.4' version = '1.2.4' )
          ( range = '0.1.20 || >1.2.4' version = '1.2.4' )
          ( range = '0.1.20 || 1.2.4' version = '1.2.3' )
          ( range = '0.1.20 || 1.2.4' version = '0.1.20' )
          ( range = '>=0.2.3 || <0.0.1' version = '0.0.0' )
          ( range = '>=0.2.3 || <0.0.1' version = '0.2.3' )
          ( range = '>=0.2.3 || <0.0.1' version = '0.2.4' )
          ( range = '||' version = '1.3.4' )
          ( range = '2.x.x' version = '2.1.3' )
          ( range = '1.2.x' version = '1.2.3' )
          ( range = '1.2.x || 2.x' version = '2.1.3' )
          ( range = '1.2.x || 2.x' version = '1.2.3' )
          ( range = 'x' version = '1.2.3' )
          ( range = '2.*.*' version = '2.1.3' )
          ( range = '1.2.*' version = '1.2.3' )
          ( range = '1.2.* || 2.*' version = '2.1.3' )
          ( range = '1.2.* || 2.*' version = '1.2.3' )
          ( range = '2' version = '2.1.2' )
          ( range = '2.3' version = '2.3.1' )
          ( range = '~2.4' version = '2.4.0' ) " >=2.4.0 <2.5.0
          ( range = '~2.4' version = '2.4.5' )
          ( range = '~>3.2.1' version = '3.2.2' ) " >=3.2.1 <3.3.0
          ( range = '~1' version = '1.2.3' ) " >=1.0.0 <2.0.0
          ( range = '~>1' version = '1.2.3' )
          ( range = '~> 1' version = '1.2.3' )
          ( range = '~1.0' version = '1.0.2' ) " >=1.0.0 <1.1.0
          ( range = '~ 1.0' version = '1.0.2' )
          ( range = '>=1' version = '1.0.0' )
          ( range = '>= 1' version = '1.0.0' )
          ( range = '<1.2' version = '1.1.1' )
          ( range = '< 1.2' version = '1.1.1' )
          ( range = '1' version = '1.0.0beta' loose = abap_true )
          ( range = '~v0.5.4-pre' version = '0.5.5' )
          ( range = '~v0.5.4-pre' version = '0.5.4' )
          ( range = '=0.7.x' version = '0.7.2' )
          ( range = '>=0.7.x' version = '0.7.2' )
          ( range = '=0.7.x' version = '0.7.0-asdf' )
          ( range = '>=0.7.x' version = '0.7.0-asdf' )
          ( range = '<=0.7.x' version = '0.6.2' )
          ( range = '>0.2.3 >0.2.4 <=0.2.5' version = '0.2.5' )
          ( range = '>=0.2.3 <=0.2.4' version = '0.2.4' )
          ( range = '1.0.0 - 2.0.0' version = '2.0.0' )
          ( range = '^1' version = '0.0.0-0' )
          ( range = '^3.0.0' version = '2.0.0' )
          ( range = '^1.0.0 || ~2.0.1' version = '2.0.0' )
          ( range = '^0.1.0 || ~3.0.1 || 5.0.0' version = '3.2.0' )
          ( range = '^0.1.0 || ~3.0.1 || 5.0.0' version = '1.0.0beta' loose = abap_true )
          ( range = '^0.1.0 || ~3.0.1 || 5.0.0' version = '5.0.0-0' loose = abap_true )
          ( range = '^0.1.0 || ~3.0.1 || >4 <=5.0.0' version = '3.5.0' )
          ( range = '0.7.x' version = '0.7.2-beta' incpre = abap_true ) ).
    
      ENDMETHOD.
    
      METHOD version_not_lt_range.
        " [range, version, options]
    
        " Version should NOT be less than range
        result = VALUE #(
          ( range = '~ 1.0' version = '1.1.0' )
          ( range = '~0.6.1-1' version = '0.6.1-1' )
          ( range = '1.0.0 - 2.0.0' version = '1.2.3' )
          ( range = '1.0.0 - 2.0.0' version = '2.9.9' )
          ( range = '1.0.0' version = '1.0.0' )
          ( range = '>=*' version = '0.2.4' )
          ( range = '' version = '1.0.0' loose = abap_true )
          ( range = '*' version = '1.2.3' )
          ( range = '>=1.0.0' version = '1.0.0' )
          ( range = '>=1.0.0' version = '1.0.1' )
          ( range = '>=1.0.0' version = '1.1.0' )
          ( range = '>1.0.0' version = '1.0.1' )
          ( range = '>1.0.0' version = '1.1.0' )
          ( range = '<=2.0.0' version = '2.0.0' )
          ( range = '<=2.0.0' version = '1.9999.9999' )
          ( range = '<=2.0.0' version = '0.2.9' )
          ( range = '<2.0.0' version = '1.9999.9999' )
          ( range = '<2.0.0' version = '0.2.9' )
          ( range = '>= 1.0.0' version = '1.0.0' )
          ( range = '>=  1.0.0' version = '1.0.1' )
          ( range = '>=   1.0.0' version = '1.1.0' )
          ( range = '> 1.0.0' version = '1.0.1' )
          ( range = '>  1.0.0' version = '1.1.0' )
          ( range = '<=   2.0.0' version = '2.0.0' )
          ( range = '<= 2.0.0' version = '1.9999.9999' )
          ( range = '<=  2.0.0' version = '0.2.9' )
          ( range = '<    2.0.0' version = '1.9999.9999' )
          ( range = |<\t2.0.0| version = '0.2.9' )
          ( range = '>=0.1.97' version = 'v0.1.97' )
          ( range = '>=0.1.97' version = '0.1.97' )
          ( range = '0.1.20 || 1.2.4' version = '1.2.4' )
          ( range = '0.1.20 || >1.2.4' version = '1.2.4' )
          ( range = '0.1.20 || 1.2.4' version = '1.2.3' )
          ( range = '0.1.20 || 1.2.4' version = '0.1.20' )
          ( range = '>=0.2.3 || <0.0.1' version = '0.0.0' )
          ( range = '>=0.2.3 || <0.0.1' version = '0.2.3' )
          ( range = '>=0.2.3 || <0.0.1' version = '0.2.4' )
          ( range = '||' version = '1.3.4' )
          ( range = '2.x.x' version = '2.1.3' )
          ( range = '1.2.x' version = '1.2.3' )
          ( range = '1.2.x || 2.x' version = '2.1.3' )
          ( range = '1.2.x || 2.x' version = '1.2.3' )
          ( range = 'x' version = '1.2.3' )
          ( range = '2.*.*' version = '2.1.3' )
          ( range = '1.2.*' version = '1.2.3' )
          ( range = '1.2.* || 2.*' version = '2.1.3' )
          ( range = '1.2.* || 2.*' version = '1.2.3' )
          ( range = '2' version = '2.1.2' )
          ( range = '2.3' version = '2.3.1' )
          ( range = '~2.4' version = '2.4.0' ) " >=2.4.0 <2.5.0
          ( range = '~2.4' version = '2.4.5' )
          ( range = '~>3.2.1' version = '3.2.2' ) " >=3.2.1 <3.3.0
          ( range = '~1' version = '1.2.3' ) " >=1.0.0 <2.0.0
          ( range = '~>1' version = '1.2.3' )
          ( range = '~> 1' version = '1.2.3' )
          ( range = '~1.0' version = '1.0.2' ) " >=1.0.0 <1.1.0
          ( range = '~ 1.0' version = '1.0.2' )
          ( range = '>=1' version = '1.0.0' )
          ( range = '>= 1' version = '1.0.0' )
          ( range = '<1.2' version = '1.1.1' )
          ( range = '< 1.2' version = '1.1.1' )
          ( range = '~v0.5.4-pre' version = '0.5.5' )
          ( range = '~v0.5.4-pre' version = '0.5.4' )
          ( range = '=0.7.x' version = '0.7.2' )
          ( range = '>=0.7.x' version = '0.7.2' )
          ( range = '<=0.7.x' version = '0.6.2' )
          ( range = '>0.2.3 >0.2.4 <=0.2.5' version = '0.2.5' )
          ( range = '>=0.2.3 <=0.2.4' version = '0.2.4' )
          ( range = '1.0.0 - 2.0.0' version = '2.0.0' )
          ( range = '^3.0.0' version = '4.0.0' )
          ( range = '^1.0.0 || ~2.0.1' version = '2.0.0' )
          ( range = '^0.1.0 || ~3.0.1 || 5.0.0' version = '3.2.0' )
          ( range = '^0.1.0 || ~3.0.1 || 5.0.0' version = '1.0.0beta' loose = abap_true )
          ( range = '^0.1.0 || ~3.0.1 || 5.0.0' version = '5.0.0-0' loose = abap_true )
          ( range = '^0.1.0 || ~3.0.1 || >4 <=5.0.0' version = '3.5.0' )
          ( range = '^1.0.0alpha' version = '1.0.0beta' loose = abap_true )
          ( range = '~1.0.0alpha' version = '1.0.0beta' loose = abap_true )
          ( range = '^1.0.0-alpha' version = '1.0.0beta' loose = abap_true )
          ( range = '~1.0.0-alpha' version = '1.0.0beta' loose = abap_true )
          ( range = '^1.0.0-alpha' version = '1.0.0-beta' )
          ( range = '~1.0.0-alpha' version = '1.0.0-beta' )
          ( range = '=0.1.0' version = '1.0.0' )
          ( range = '>1.2.3' version = '1.3.0-alpha' incpre = abap_true ) ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_semver_functions IMPLEMENTATION.
    
      METHOD clean.
    
        DATA(vers) = replace(
          val   = /apmg/cl_apm_semver_utils=>version_trim( version )
          regex = '^[=v]+'
          with  = '' ) ##REGEX_POSIX.
    
        DATA(semver) = parse( version = vers loose = loose incpre = incpre ).
    
        CHECK semver IS BOUND.
    
        result = semver->version.
    
      ENDMETHOD.
    
      METHOD cmp.
    
        CASE op.
          WHEN '==='.
            result = equality( a = a b = b ).
          WHEN '!=='.
            result = xsdbool( NOT equality( a = a b = b ) ).
          WHEN '' OR '=' OR '=='.
            result = eq(
              a      = a
              b      = b
              loose  = loose
              incpre = incpre ).
          WHEN '!=' OR '<>'.
            result = neq(
              a      = a
              b      = b
              loose  = loose
              incpre = incpre ).
          WHEN '>'.
            result = gt(
              a      = a
              b      = b
              loose  = loose
              incpre = incpre ).
          WHEN '>='.
            result = gte(
              a      = a
              b      = b
              loose  = loose
              incpre = incpre ).
          WHEN '<'.
            result = lt(
              a      = a
              b      = b
              loose  = loose
              incpre = incpre ).
          WHEN '<='.
            result = lte(
              a      = a
              b      = b
              loose  = loose
              incpre = incpre ).
          WHEN OTHERS.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Invalid operator: { op }|.
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD coerce.
    
        TYPES:
          BEGIN OF ty_match,
            major      TYPE string,
            minor      TYPE string,
            patch      TYPE string,
            prerelease TYPE string,
            build      TYPE string,
            offset     TYPE i,
            length     TYPE i,
            endpos     TYPE i,
          END OF ty_match.
    
        DATA matches TYPE STANDARD TABLE OF ty_match WITH KEY major minor patch prerelease build.
    
        " cl_abap_matcher has a problem with '1.2.3.4.5.6' so we use FIND REGEX
    
        IF rtl = abap_false.
          DATA(r) = COND #(
            WHEN incpre = abap_true
            THEN /apmg/cl_apm_semver_re=>token-coercefull-safe_src
            ELSE /apmg/cl_apm_semver_re=>token-coerce-safe_src ).
    
          FIND REGEX r IN version
            SUBMATCHES DATA(rest) DATA(major) DATA(minor) DATA(patch) DATA(prerelease) DATA(build) ##REGEX_POSIX.
          IF sy-subrc <> 0.
            RETURN.
          ENDIF.
        ELSE.
          " Find the right-most coercible string that does not share
          " a terminus with a more left-ward coercible string.
          " Eg, '1.2.3.4' wants to coerce '2.3.4', not '3.4' or '4'
          r = COND #(
            WHEN incpre = abap_true
            THEN /apmg/cl_apm_semver_re=>token-coercertlfull-safe_src
            ELSE /apmg/cl_apm_semver_re=>token-coercertl-safe_src ).
    
          DATA(offset) = 0.
          DO.
            FIND REGEX r IN version+offset(*) SUBMATCHES rest major minor patch prerelease build ##REGEX_POSIX.
            IF sy-subrc <> 0.
              EXIT.
            ENDIF.
            INSERT INITIAL LINE INTO TABLE matches ASSIGNING FIELD-SYMBOL().
            -major      = major.
            -minor      = minor.
            -patch      = patch.
            -prerelease = prerelease.
            -build      = build.
    
            DATA(match) = |{ major }|
                 && |{ COND #( WHEN minor IS NOT INITIAL THEN '.' && minor ) }|
                 && |{ COND #( WHEN patch IS NOT INITIAL THEN '.' && patch ) }|
                 && |{ COND #( WHEN prerelease IS NOT INITIAL THEN '-' && prerelease ) }|
                 && |{ COND #( WHEN build IS NOT INITIAL THEN '+' && build ) }|.
    
            -offset = offset.
            -length = strlen( match ).
            -endpos = -offset + -length.
            FIND REGEX '^\d' IN version+offset(*) MATCH OFFSET DATA(next_offset) ##SUBRC_OK ##REGEX_POSIX.
            offset = offset + next_offset + 1.
            IF strlen( version ) <= offset.
              EXIT.
            ENDIF.
          ENDDO.
          SORT matches BY endpos DESCENDING length DESCENDING.
          READ TABLE matches ASSIGNING  INDEX 1.
          IF sy-subrc <> 0.
            RETURN.
          ENDIF.
          major      = -major.
          minor      = -minor.
          patch      = -patch.
          prerelease = -prerelease.
          build      = -build.
        ENDIF.
    
        IF minor IS INITIAL.
          minor = '0'.
        ENDIF.
    
        IF patch IS INITIAL.
          patch = '0'.
        ENDIF.
    
        IF incpre = abap_true AND prerelease IS NOT INITIAL.
          prerelease = |-{ prerelease }|.
        ELSE.
          prerelease = ''.
        ENDIF.
    
        IF incpre = abap_true AND build IS NOT INITIAL.
          build = |+{ build }|.
        ELSE.
          build = ''.
        ENDIF.
    
        result = parse( version = |{ major }.{ minor }.{ patch }{ prerelease }{ build }| loose = loose incpre = incpre ).
    
      ENDMETHOD.
    
      METHOD compare.
    
        DATA(semver_a) = /apmg/cl_apm_semver=>create( version = a loose = loose incpre = incpre ).
        DATA(semver_b) = /apmg/cl_apm_semver=>create( version = b loose = loose incpre = incpre ).
    
        CHECK semver_a IS BOUND AND semver_b IS BOUND.
    
        result = semver_a->compare( semver_b ).
    
      ENDMETHOD.
    
      METHOD compare_build.
    
        DATA(semver_a) = /apmg/cl_apm_semver=>create( version = a loose = loose incpre = incpre ).
        DATA(semver_b) = /apmg/cl_apm_semver=>create( version = b loose = loose incpre = incpre ).
    
        CHECK semver_a IS BOUND AND semver_b IS BOUND.
    
        result = semver_a->compare( semver_b ).
        IF result = 0.
          result = semver_a->compare_build( semver_b ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD compare_loose.
        result = compare(
          a      = a
          b      = b
          loose  = abap_true
          incpre = incpre ).
      ENDMETHOD.
    
      METHOD diff.
    
        DATA(v1) = parse( version = version_1 throw_errors = abap_true ).
        DATA(v2) = parse( version = version_2 throw_errors = abap_true ).
    
        DATA(comparison) = v1->compare( v2 ).
    
        IF comparison = 0.
          RETURN.
        ENDIF.
    
        DATA(v1_higher)    = xsdbool( comparison > 0 ).
        DATA(high_version) = COND #( WHEN v1_higher = abap_true THEN v1 ELSE v2 ).
        DATA(low_version)  = COND #( WHEN v1_higher = abap_true THEN v2 ELSE v1 ).
        DATA(high_has_pre) = xsdbool( high_version->prerelease IS NOT INITIAL ).
        DATA(low_has_pre)  = xsdbool( low_version->prerelease IS NOT INITIAL ).
    
        IF low_has_pre = abap_true AND high_has_pre = abap_false.
          " Going from prerelease -> no prerelease requires some special casing
    
          " If the low version has only a major, then it will always be a major
          " Some examples:
          " 1.0.0-1 -> 1.0.0
          " 1.0.0-1 -> 1.1.1
          " 1.0.0-1 -> 2.0.0
          IF low_version->patch IS INITIAL AND low_version->minor IS INITIAL.
            result = 'major'.
            RETURN.
          ENDIF.
    
          " If the main part has no difference
          IF low_version->compare_main( high_version ) = 0.
            IF low_version->minor IS NOT INITIAL AND low_version->patch IS INITIAL.
              result = 'minor'.
              RETURN.
            ENDIF.
            result = 'patch'.
            RETURN.
          ENDIF.
        ENDIF.
    
        " add the `pre` prefix if we are going to a prerelease version
        DATA(prefix) = COND #( WHEN high_has_pre = abap_true THEN 'pre' ELSE '' ).
    
        IF v1->major <> v2->major.
          result = prefix && 'major'.
          RETURN.
        ENDIF.
    
        IF v1->minor <> v2->minor.
          result = prefix && 'minor'.
          RETURN.
        ENDIF.
    
        IF v1->patch <> v2->patch.
          result = prefix && 'patch'.
          RETURN.
        ENDIF.
    
        " high and low are preleases
        result = 'prerelease'.
    
      ENDMETHOD.
    
      METHOD eq.
        result = xsdbool( compare(
          a      = a
          b      = b
          loose  = loose
          incpre = incpre ) = 0 ).
      ENDMETHOD.
    
      METHOD equality.
    
        DATA semver_a TYPE REF TO /apmg/cl_apm_semver.
        DATA semver_b TYPE REF TO /apmg/cl_apm_semver.
    
        IF a IS BOUND AND a IS INSTANCE OF /apmg/cl_apm_semver AND b IS BOUND AND b IS INSTANCE OF /apmg/cl_apm_semver.
          semver_a ?= a.
          semver_b ?= b.
          result = xsdbool( semver_a->version = semver_b->version ).
        ELSE.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid parameter type'.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD gt.
        result = xsdbool( compare(
          a      = a
          b      = b
          loose  = loose
          incpre = incpre ) > 0 ).
      ENDMETHOD.
    
      METHOD gte.
        result = xsdbool( compare(
          a      = a
          b      = b
          loose  = loose
          incpre = incpre ) >= 0 ).
      ENDMETHOD.
    
      METHOD inc.
    
        DATA semver TYPE REF TO /apmg/cl_apm_semver.
    
        TRY.
            " Create new semver object
            DATA(kind) = cl_abap_typedescr=>describe_by_data( version )->type_kind.
    
            IF kind = cl_abap_typedescr=>typekind_oref AND version IS INSTANCE OF /apmg/cl_apm_semver.
              semver ?= version.
              result = /apmg/cl_apm_semver=>create( version = semver->version loose = loose incpre = incpre ).
            ELSE.
              result = /apmg/cl_apm_semver=>create( version = version loose = loose incpre = incpre ).
            ENDIF.
    
            CHECK result IS BOUND.
    
            result->inc( release_type = release_type identifier = identifier identifier_base = identifier_base ).
          CATCH /apmg/cx_apm_error.
            CLEAR result.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD lt.
        result = xsdbool( compare(
          a      = a
          b      = b
          loose  = loose
          incpre = incpre ) < 0 ).
      ENDMETHOD.
    
      METHOD lte.
        result = xsdbool( compare(
          a      = a
          b      = b
          loose  = loose
          incpre = incpre ) <= 0 ).
      ENDMETHOD.
    
      METHOD major.
    
        DATA(semver) = /apmg/cl_apm_semver=>create( version = version loose = loose ).
    
        CHECK semver IS BOUND.
    
        result = semver->major.
    
      ENDMETHOD.
    
      METHOD minor.
    
        DATA(semver) = /apmg/cl_apm_semver=>create( version = version loose = loose ).
    
        CHECK semver IS BOUND.
    
        result = semver->minor.
    
      ENDMETHOD.
    
      METHOD neq.
        result = xsdbool( compare(
          a      = a
          b      = b
          loose  = loose
          incpre = incpre ) <> 0 ).
      ENDMETHOD.
    
      METHOD parse.
    
        DATA(kind) = cl_abap_typedescr=>describe_by_data( version )->type_kind.
    
        IF kind = cl_abap_typedescr=>typekind_oref AND version IS INSTANCE OF /apmg/cl_apm_semver.
          result ?= version.
          RETURN.
        ENDIF.
    
        TRY.
            result = /apmg/cl_apm_semver=>create( version = version loose = loose incpre = incpre ).
          CATCH /apmg/cx_apm_error INTO DATA(error).
            IF throw_errors = abap_false.
              RETURN.
            ENDIF.
    
            RAISE EXCEPTION error.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD patch.
    
        DATA(semver) = /apmg/cl_apm_semver=>create( version = version loose = loose ).
    
        CHECK semver IS BOUND.
    
        result = semver->patch.
    
      ENDMETHOD.
    
      METHOD prerelease.
    
        DATA(semver) = parse( version = version loose = loose incpre = incpre ).
    
        CHECK semver IS BOUND.
    
        result = semver->prerelease.
    
      ENDMETHOD.
    
      METHOD rcompare.
        result = compare(
          a      = b
          b      = a
          loose  = loose
          incpre = incpre ).
      ENDMETHOD.
    
      METHOD rsort.
    
        result = list.
    
        DATA(i) = 1.
        WHILE lines( result ) > i.
          DATA(j) = 1.
          WHILE lines( result ) - i >= j.
            IF compare_build(
              b      = result[ j ]
              a      = result[ j + 1 ]
              loose  = loose
              incpre = incpre ) > 0.
    
              DATA(temp) = result[ j ].
              result[ j ] = result[ j + 1 ].
              result[ j + 1 ] = temp.
            ENDIF.
            j = j + 1.
          ENDWHILE.
          i = i + 1.
        ENDWHILE.
    
      ENDMETHOD.
    
      METHOD satisfies.
    
        TRY.
            DATA(semrange) = /apmg/cl_apm_semver_range=>create( range = range loose = loose incpre = incpre ).
    
            IF semrange IS BOUND.
              result = semrange->test( version ).
            ENDIF.
          CATCH /apmg/cx_apm_error.
            result = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD sort.
    
        result = list.
    
        DATA(i) = 1.
        WHILE lines( result ) > i.
          DATA(j) = 1.
          WHILE lines( result ) - i >= j.
            IF compare_build(
              a      = result[ j ]
              b      = result[ j + 1 ]
              loose  = loose
              incpre = incpre ) > 0.
    
              DATA(temp) = result[ j ].
              result[ j ] = result[ j + 1 ].
              result[ j + 1 ] = temp.
            ENDIF.
            j = j + 1.
          ENDWHILE.
          i = i + 1.
        ENDWHILE.
    
      ENDMETHOD.
    
      METHOD valid.
    
        TRY.
            DATA(semver) = parse( version = version loose = loose incpre = incpre ).
    
            CHECK semver IS BOUND.
    
            result = semver->version.
          CATCH /apmg/cx_apm_error ##NO_HANDLER.
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_semver_identifier IMPLEMENTATION.
    
      METHOD compare_identifiers.
    
        " Using RTTI isn't faster and there's no built in typeof
        " if (typeof a === 'number' && typeof b === 'number') {
        "  return a === b ? 0 : a < b ? -1 : 1
        " }
    
        DATA(anum) = /apmg/cl_apm_semver_utils=>is_numeric( a ).
        DATA(bnum) = /apmg/cl_apm_semver_utils=>is_numeric( b ).
    
        IF anum = abap_true AND bnum = abap_true.
          DATA(aval) = CONV decfloat34( a ).
          DATA(bval) = CONV decfloat34( b ).
          IF aval = bval.
            result = 0.
          ELSEIF aval < bval.
            result = -1.
          ELSE.
            result = +1.
          ENDIF.
        ELSE.
          IF a = b.
            result = 0.
          ELSEIF anum = abap_true AND bnum = abap_false.
            result = -1.
          ELSEIF anum = abap_false AND bnum = abap_true.
            result = +1.
          ELSEIF a < b.
            result = -1.
          ELSE.
            result = +1.
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD rcompare_identifiers.
        result = compare_identifiers( a = b b = a ).
      ENDMETHOD.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_semver_integratio IMPLEMENTATION.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_semver_range IMPLEMENTATION.
    
      METHOD constructor.
    
        options-loose  = loose.
        options-incpre = incpre.
    
        " First reduce all whitespace as much as possible so we do not have to rely
        " on potentially slow regexes like \s*. This is then stored and used for
        " future error messages as well.
        raw = /apmg/cl_apm_semver_utils=>trim( range ).
    
        " First, split on ||
        IF raw IS NOT INITIAL.
          SPLIT raw AT '||' INTO TABLE DATA(ranges).
        ELSE.
          INSERT INITIAL LINE INTO TABLE ranges.
        ENDIF.
    
        " map the range to a 2d array of comparators
        LOOP AT ranges ASSIGNING FIELD-SYMBOL().
          INSERT parse_range( /apmg/cl_apm_semver_utils=>trim(  ) ) INTO TABLE set.
        ENDLOOP.
    
        " throw out any comparator lists that are empty
        " this generally means that it was not a valid range, which is allowed
        " in loose mode, but will still throw if the WHOLE range is invalid.
        DELETE set WHERE table_line IS INITIAL.
    
        IF set IS INITIAL.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Invalid SemVer Range: { raw }|.
        ENDIF.
    
        " if we have any that are not the null set, throw out null sets.
        IF lines( set ) > 1.
    
          " keep the first one, in case they're all null sets
          DATA(first) = set[ 1 ].
    
          LOOP AT set ASSIGNING FIELD-SYMBOL().
            IF lines(  ) = 1 AND is_null_set( [ 1 ] ).
              DELETE set INDEX sy-tabix.
            ENDIF.
          ENDLOOP.
    
          IF set IS INITIAL.
            set = VALUE #( ( first ) ).
          ELSEIF lines( set ) > 1.
            " if we have any that are *, then the range is just *
            LOOP AT set ASSIGNING .
              IF lines(  ) = 1 AND is_any( [ 1 ] ).
                DATA(star) = .
                EXIT.
              ENDIF.
            ENDLOOP.
            IF star IS NOT INITIAL.
              set = VALUE #( ( star ) ).
            ENDIF.
          ENDIF.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD create.
    
        DATA comp TYPE REF TO /apmg/cl_apm_semver_comparator.
    
        DATA(kind) = cl_abap_typedescr=>describe_by_data( range )->type_kind.
    
        IF kind = cl_abap_typedescr=>typekind_oref AND range IS INSTANCE OF /apmg/cl_apm_semver_range.
    
          result ?= range.
    
          IF result->options-loose = loose AND result->options-incpre = incpre.
            RETURN.
          ENDIF.
    
          result = NEW /apmg/cl_apm_semver_range( range = |{ result->raw }| loose = loose incpre = incpre ).
    
        ELSEIF kind = cl_abap_typedescr=>typekind_oref AND range IS INSTANCE OF /apmg/cl_apm_semver_comparator.
    
          comp ?= range.
    
          result = NEW /apmg/cl_apm_semver_range( range = |{ comp->value }| loose = loose incpre = incpre ).
    
        ELSEIF kind = cl_abap_typedescr=>typekind_char OR kind = cl_abap_typedescr=>typekind_string.
    
          result = NEW /apmg/cl_apm_semver_range( range = |{ range }| loose = loose incpre = incpre ).
    
        ELSE.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid parameter type'.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD format.
        result = range( ).
      ENDMETHOD.
    
      METHOD intersects.
    
        " some
        LOOP AT set ASSIGNING FIELD-SYMBOL().
          IF is_satisfiable( comparators =  loose = loose incpre = incpre ).
            " some
            LOOP AT range->set ASSIGNING FIELD-SYMBOL().
              IF is_satisfiable( comparators =  loose = loose incpre = incpre ).
                " every
                result = abap_true.
                LOOP AT  ASSIGNING FIELD-SYMBOL().
                  " every
                  LOOP AT  ASSIGNING FIELD-SYMBOL().
                    IF NOT ->intersects( comp =  loose = loose incpre = incpre ).
                      result = abap_false.
                      EXIT.
                    ENDIF.
                  ENDLOOP.
                  IF result = abap_false.
                    EXIT.
                  ENDIF.
                ENDLOOP.
                IF result = abap_true.
                  RETURN.
                ENDIF.
              ENDIF.
            ENDLOOP.
          ENDIF.
        ENDLOOP.
    
        result = abap_false.
    
      ENDMETHOD.
    
      METHOD is_any.
        result = xsdbool( comp->value = '' ).
      ENDMETHOD.
    
      METHOD is_null_set.
        result = xsdbool( comp->value = '<0.0.0-0' ).
      ENDMETHOD.
    
      METHOD is_satisfiable.
        " take a set of comparators and determine whether there
        " exists a version which can satisfy it
    
        result = abap_true.
    
        DATA(remaining_comparators) = comparators.
    
        DATA(i) = lines( remaining_comparators ).
    
        WHILE result = abap_true AND lines( remaining_comparators ) > 0.
          DATA(test_comparator) = remaining_comparators[ i ].
    
          LOOP AT remaining_comparators ASSIGNING FIELD-SYMBOL().
            DATA(semcomp) = /apmg/cl_apm_semver_comparator=>create( test_comparator ).
    
            IF semcomp IS BOUND.
              result = semcomp->intersects( comp =  loose = loose incpre = incpre ).
            ELSE.
              result = abap_false.
            ENDIF.
    
            IF result = abap_false.
              EXIT.
            ENDIF.
          ENDLOOP.
    
          DELETE remaining_comparators INDEX i.
          i = i - 1.
        ENDWHILE.
    
      ENDMETHOD.
    
      METHOD is_x.
        result = xsdbool( id IS INITIAL OR to_lower( id ) = 'x'  OR id = '*' ).
      ENDMETHOD.
    
      METHOD parse_comparator.
        result = replace(
          val   = comp
          regex = /apmg/cl_apm_semver_re=>token-build-src
          with  = '' ) ##REGEX_POSIX.
        result = replace_carets( comp = result loose = loose incpre = incpre ).
        result = replace_tildes( comp = result loose = loose incpre = incpre ).
        result = replace_xranges( comp = result loose = loose incpre = incpre ).
        result = replace_stars( comp = result loose = loose incpre = incpre ).
      ENDMETHOD.
    
      METHOD parse_range.
    
        DATA:
          comp_values TYPE string_table,
          comparators TYPE ty_comparators.
    
        DATA(range) = range_string.
    
        " memoize range parsing for performance
        " this is a very hot path, and fully deterministic
        DATA(cache_key) = range && ':' && options-loose && ',' && options-incpre  && ',' && options-rtl.
    
        READ TABLE cache INTO DATA(cache_entry) WITH TABLE KEY key = cache_key.
        IF sy-subrc = 0.
          result = cache_entry-value.
          RETURN.
        ENDIF.
    
        " `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
        range = replace_hyphen( range = range loose = options-loose incpre = options-incpre ).
    
        " `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
        range = replace(
          val   = range
          regex = /apmg/cl_apm_semver_re=>token-comparatortrim-safe_src
          with  = /apmg/cl_apm_semver_re=>comparator_trim_replace
          occ   = /apmg/cl_apm_semver_re=>token-comparatortrim-occ ) ##REGEX_POSIX.
    
        " `~ 1.2.3` => `~1.2.3`
        range = replace(
          val   = range
          regex = /apmg/cl_apm_semver_re=>token-tildetrim-safe_src
          with  = /apmg/cl_apm_semver_re=>tilde_trim_replace
          occ   = /apmg/cl_apm_semver_re=>token-tildetrim-occ ) ##REGEX_POSIX.
    
        " `^ 1.2.3` => `^1.2.3`
        range = replace(
          val   = range
          regex = /apmg/cl_apm_semver_re=>token-carettrim-safe_src
          with  = /apmg/cl_apm_semver_re=>caret_trim_replace
          occ   = /apmg/cl_apm_semver_re=>token-carettrim-occ ) ##REGEX_POSIX.
    
        " At this point, the range is completely trimmed and
        " ready to be split into comparators.
    
        SPLIT range AT ` ` INTO TABLE DATA(comps).
    
        LOOP AT comps ASSIGNING FIELD-SYMBOL().
           = parse_comparator( comp =  loose = options-loose incpre = options-incpre ).
        ENDLOOP.
    
        range = condense( concat_lines_of( table = comps sep = ` ` ) ).
    
        IF range IS INITIAL.
          range = ` `.
        ENDIF.
    
        SPLIT range AT ` ` INTO TABLE comps.
    
        LOOP AT comps ASSIGNING .
           = replace_gte0( comp =  loose = options-loose incpre = options-incpre ).
        ENDLOOP.
    
        IF options-loose = abap_true.
          " in loose mode, throw out any that are not valid comparators
          DATA(r) = /apmg/cl_apm_semver_re=>token-comparatorloose-safe_regex.
          LOOP AT comps ASSIGNING .
            DATA(m) = r->create_matcher( text =  ).
            IF NOT m->match( ).
              DELETE comps INDEX sy-tabix.
            ENDIF.
          ENDLOOP.
        ENDIF.
    
        " if any comparators are the null set, then replace with JUST null set
        " if more than one comparator, remove any * comparators
        " also, don't include the same comparator more than once
    
        LOOP AT comps ASSIGNING .
          DATA(semcomp) = /apmg/cl_apm_semver_comparator=>create(
            comp   = 
            loose  = options-loose
            incpre = options-incpre ).
          INSERT semcomp INTO TABLE comparators.
        ENDLOOP.
    
        LOOP AT comparators ASSIGNING FIELD-SYMBOL().
          IF is_null_set(  ).
            result = VALUE #( (  ) ).
            EXIT.
          ENDIF.
          IF ->semver IS NOT INITIAL AND
            NOT line_exists( comp_values[ table_line = ->value ] ).
    
            INSERT ->value INTO TABLE comp_values.
            INSERT  INTO TABLE result.
          ENDIF.
        ENDLOOP.
    
        cache_entry-key   = cache_key.
        cache_entry-value = result.
        INSERT cache_entry INTO TABLE cache.
    
      ENDMETHOD.
    
      METHOD range.
    
        DATA comps TYPE string_table.
    
        IF formatted IS INITIAL.
          LOOP AT set ASSIGNING FIELD-SYMBOL().
            LOOP AT  ASSIGNING FIELD-SYMBOL().
              IF sy-tabix = 1.
                DATA(comp) = /apmg/cl_apm_semver_utils=>trim( ->value ).
              ELSE.
                comp = comp && ` ` && /apmg/cl_apm_semver_utils=>trim( ->value ).
              ENDIF.
            ENDLOOP.
            INSERT comp INTO TABLE comps.
          ENDLOOP.
    
          formatted = /apmg/cl_apm_semver_utils=>trim( concat_lines_of( table = comps sep = `||` ) ).
        ENDIF.
    
        result = formatted.
    
      ENDMETHOD.
    
      METHOD replace_caret.
        " ^ --> * (any, kinda silly)
        " ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0-0
        " ^2.0, ^2.0.x --> >=2.0.0 <3.0.0-0
        " ^1.2, ^1.2.x --> >=1.2.0 <2.0.0-0
        " ^1.2.3 --> >=1.2.3 <2.0.0-0
        " ^1.2.0 --> >=1.2.0 <2.0.0-0
        " ^0.0.1 --> >=0.0.1 <0.0.2-0
        " ^0.1.0 --> >=0.1.0 <0.2.0-0
        result = comp.
    
        DATA(r) = COND #(
          WHEN loose = abap_true
          THEN /apmg/cl_apm_semver_re=>token-caretloose-safe_regex
          ELSE /apmg/cl_apm_semver_re=>token-caret-safe_regex ).
    
        TRY.
            DATA(m) = r->create_matcher( text = result ).
    
            WHILE m->find_next( ).
              DATA(ma) = m->get_submatch( 1 ).
              DATA(mi) = m->get_submatch( 2 ).
              DATA(pa) = m->get_submatch( 3 ).
              DATA(pr) = m->get_submatch( 4 ).
              "DATA(bi)  = m->get_submatch( 5 )
    
              DATA(z) = COND #( WHEN incpre = abap_true THEN '-0' ELSE '' ).
    
              DATA(with) = no_replace.
    
              IF is_x( ma ).
                with = ''.
              ELSEIF is_x( mi ).
                with = |>={ ma }.0.0{ z } <{ str( ma + 1 ) }.0.0-0|.
              ELSEIF is_x( pa ).
                IF ma = '0'.
                  with = |>={ ma }.{ mi }.0{ z } <{ ma }.{ str( mi + 1 ) }.0-0|.
                ELSE.
                  with = |>={ ma }.{ mi }.0{ z } <{ str( ma + 1 ) }.0.0-0|.
                ENDIF.
              ELSEIF pr IS NOT INITIAL.
                IF ma = '0'.
                  IF mi = '0'.
                    with = |>={ ma }.{ mi }.{ pa }-{ pr } <{ ma }.{ mi }.{ str( pa + 1 ) }-0|.
                  ELSE.
                    with = |>={ ma }.{ mi }.{ pa }-{ pr } <{ ma }.{ str( mi + 1 ) }.0-0|.
                  ENDIF.
                ELSE.
                  with = |>={ ma }.{ mi }.{ pa }-{ pr } <{ str( ma + 1 ) }.0.0-0|.
                ENDIF.
              ELSE.
                IF ma = '0'.
                  IF mi = '0'.
                    with = |>={ ma }.{ mi }.{ pa }{ z } <{ ma }.{ mi }.{ str( pa + 1 )  }-0|.
                  ELSE.
                    with = |>={ ma }.{ mi }.{ pa }{ z } <{ ma }.{ str( mi + 1 ) }.0-0|.
                  ENDIF.
                ELSE.
                  with = |>={ ma }.{ mi }.{ pa } <{ str( ma + 1 ) }.0.0-0|.
                ENDIF.
              ENDIF.
    
              IF with <> no_replace.
                m->replace_found( with ).
              ENDIF.
    
              result = m->text.
            ENDWHILE.
          CATCH cx_sy_arithmetic_overflow.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Overflow'.
          CATCH cx_sy_regex cx_sy_matcher INTO DATA(error).
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD replace_carets.
    
        SPLIT /apmg/cl_apm_semver_utils=>trim( comp ) AT ` ` INTO TABLE DATA(comps).
    
        LOOP AT comps ASSIGNING FIELD-SYMBOL().
           = replace_caret( comp =  loose = loose incpre = incpre ).
        ENDLOOP.
    
        result = concat_lines_of( table = comps sep = ` ` ).
    
      ENDMETHOD.
    
      METHOD replace_gte0.
    
        DATA(regex) = COND #(
          WHEN incpre = abap_true
          THEN /apmg/cl_apm_semver_re=>token-gte0pre-safe_src
          ELSE /apmg/cl_apm_semver_re=>token-gte0-safe_src ).
    
        result = replace(
          val   = /apmg/cl_apm_semver_utils=>trim( comp )
          regex = regex
          with  = '' ) ##REGEX_POSIX.
    
      ENDMETHOD.
    
      METHOD replace_hyphen.
        " 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
        " 1.2.3 - 3.4 => >=1.2.0 <3.5.0-0 Any 3.4.x will do
        " 1.2 - 3.4 => >=1.2.0 <3.5.0-0
        result = range.
    
        DATA(r) = COND #(
          WHEN loose = abap_true
          THEN /apmg/cl_apm_semver_re=>token-hyphenrangeloose-safe_regex
          ELSE /apmg/cl_apm_semver_re=>token-hyphenrange-safe_regex ).
    
        TRY.
            DATA(m) = r->create_matcher( text = result ).
    
            IF m->match( ).
    
              DATA(from) = m->get_submatch( 1 ).
              DATA(fma)  = m->get_submatch( 2 ).
              DATA(fmi)  = m->get_submatch( 3 ).
              DATA(fpa)  = m->get_submatch( 4 ).
              DATA(fpr)  = m->get_submatch( 5 ).
              "DATA(fbi)  = m->get_submatch( 6 )
    
              DATA(to)   = m->get_submatch( 7 ).
              DATA(tma)  = m->get_submatch( 8 ).
              DATA(tmi)  = m->get_submatch( 9 ).
              DATA(tpa)  = m->get_submatch( 10 ).
              DATA(tpr)  = m->get_submatch( 11 ).
              "DATA(tbi)  = m->get_submatch( 12 )
    
              DATA(z) = COND #( WHEN incpre = abap_true THEN '-0' ELSE '' ).
    
              IF is_x( fma ).
                from = ''.
              ELSEIF is_x( fmi ).
                from = |>={ fma }.0.0{ z }|.
              ELSEIF is_x( fpa ).
                from = |>={ fma }.{ fmi }.0{ z }|.
              ELSEIF fpr IS NOT INITIAL.
                from = |>={ from }|.
              ELSE.
                from = |>={ from }{ z }|.
              ENDIF.
    
              IF is_x( tma ).
                to = ''.
              ELSEIF is_x( tmi ).
                to = |<{ str( tma + 1 ) }.0.0-0|.
              ELSEIF is_x( tpa ).
                to = |<{ tma }.{ str( tmi + 1 ) }.0-0|.
              ELSEIF tpr IS NOT INITIAL.
                to = |<={ tma }.{ tmi }.{ tpa }-{ tpr }|.
              ELSEIF z IS NOT INITIAL.
                to = |<{ tma }.{ tmi }.{ str( tpa + 1 ) }-0|.
              ELSE.
                to = |<={ to }|.
              ENDIF.
    
              DATA(with) = /apmg/cl_apm_semver_utils=>trim( |{ from } { to }| ).
    
              m->replace_found( with ).
    
              result = m->text.
            ENDIF.
          CATCH cx_sy_arithmetic_overflow.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Overflow'.
          CATCH cx_sy_regex cx_sy_matcher INTO DATA(error).
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD replace_stars.
        " Because * is AND-ed with everything else in the comparator,
        " and '' means "any version", just remove the *s entirely.
    
        " Looseness is ignored here.  star is always as loose as it gets!
        result = replace(
          val   = /apmg/cl_apm_semver_utils=>trim( comp )
          regex = /apmg/cl_apm_semver_re=>token-star-safe_src
          with  = '' ) ##REGEX_POSIX.
    
      ENDMETHOD.
    
      METHOD replace_tilde.
        " ~, ~> --> * (any, kinda silly)
        " ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0-0
        " ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0-0
        " ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0-0
        " ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0-0
        " ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0-0
        " ~0.0.1 --> >=0.0.1 <0.1.0-0
        result = comp.
    
        DATA(r) = COND #(
          WHEN loose = abap_true
          THEN /apmg/cl_apm_semver_re=>token-tildeloose-safe_regex
          ELSE /apmg/cl_apm_semver_re=>token-tilde-safe_regex ).
    
        TRY.
            DATA(m) = r->create_matcher( text = result ).
    
            WHILE m->find_next( ).
              DATA(ma) = m->get_submatch( 1 ).
              DATA(mi) = m->get_submatch( 2 ).
              DATA(pa) = m->get_submatch( 3 ).
              DATA(pr) = m->get_submatch( 4 ).
              "DATA(bi)  = m->get_submatch( 5 )
    
              DATA(with) = no_replace.
    
              IF is_x( ma ).
                with = ''.
              ELSEIF is_x( mi ).
                with = |>={ ma }.0.0 <{ str( ma + 1 ) }.0.0-0|.
              ELSEIF is_x( pa ).
                " ~1.2 == >=1.2.0 <1.3.0-0
                with = |>={ ma }.{ mi }.0 <{ ma }.{ str( mi + 1 ) }.0-0|.
              ELSEIF pr IS NOT INITIAL.
                with = |>={ ma }.{ mi }.{ pa }-{ pr } <{ ma }.{ str( mi + 1 ) }.0-0|.
              ELSE.
                " ~1.2.3 == >=1.2.3 <1.3.0-0
                with = |>={ ma }.{ mi }.{ pa } <{ ma }.{ str( mi + 1 ) }.0-0|.
              ENDIF.
    
              IF with <> no_replace.
                m->replace_found( with ).
              ENDIF.
    
              result = m->text.
            ENDWHILE.
          CATCH cx_sy_arithmetic_overflow.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Overflow'.
          CATCH cx_sy_regex cx_sy_matcher INTO DATA(error).
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD replace_tildes.
    
        SPLIT /apmg/cl_apm_semver_utils=>trim( comp ) AT ` ` INTO TABLE DATA(comps).
    
        LOOP AT comps ASSIGNING FIELD-SYMBOL().
           = replace_tilde( comp =  loose = loose incpre = incpre ).
        ENDLOOP.
    
        result = concat_lines_of( table = comps sep = ` ` ).
    
      ENDMETHOD.
    
      METHOD replace_xrange.
    
        result = /apmg/cl_apm_semver_utils=>trim( comp ).
    
        DATA(r) = COND #(
          WHEN loose = abap_true
          THEN /apmg/cl_apm_semver_re=>token-xrangeloose-safe_regex
          ELSE /apmg/cl_apm_semver_re=>token-xrange-safe_regex ).
    
        TRY.
            DATA(m) = r->create_matcher( text = result ).
    
            WHILE m->find_next( ).
              DATA(gtlt) = m->get_submatch( 1 ).
              DATA(ma) = m->get_submatch( 2 ).
              DATA(mi) = m->get_submatch( 3 ).
              DATA(pa) = m->get_submatch( 4 ).
              DATA(pr) = m->get_submatch( 5 ).
              "DATA(bi)  = m->get_submatch( 6 )
    
              DATA(xma) = is_x( ma ).
              DATA(xmi) = xsdbool( xma = abap_true OR is_x( mi ) ).
              DATA(xpa) = xsdbool( xmi = abap_true OR is_x( pa ) ).
              DATA(anyx) = xpa.
    
              IF gtlt = '=' AND anyx = abap_true.
                gtlt = ''.
              ENDIF.
    
              " if we're including prereleases in the match, then we need
              " to fix this to -0, the lowest possible prerelease value
              pr = COND #( WHEN incpre = abap_true THEN '-0' ELSE '' ).
    
              DATA(with) = no_replace.
    
              IF xma = abap_true.
                IF gtlt = '>' OR gtlt = '<'.
                  " nothing is allowed.
                  with = '<0.0.0-0'.
                ELSE.
                  " nothing is forbidden.
                  with = '*'.
                ENDIF.
              ELSEIF gtlt IS NOT INITIAL AND anyx  = abap_true.
                " we know patch is an x, because we have any x at all
                " replace X with 0
                IF xmi = abap_true.
                  mi = `0`.
                ENDIF.
                pa = `0`.
    
                CASE gtlt.
                  WHEN '>'.
                    " >1 => >=2.0.0.
                    " >1.2 => >=1.3.0.
                    gtlt = '>='.
                    IF xmi = abap_true.
                      ma = str( ma + 1 ).
                      mi = `0`.
                    ELSE.
                      mi = str( mi + 1 ).
                    ENDIF.
                    pa = `0`.
                  WHEN '<='.
                    " <=0.7.x is actually <0.8.0, since any 0.7.x should.
                    " pass.  Similarly, <=7.x is actually <8.0.0, etc.
                    gtlt = '<'.
                    IF xmi = abap_true.
                      ma = str( ma + 1 ).
                    ELSE.
                      mi = str( mi + 1 ).
                    ENDIF.
                ENDCASE.
    
                IF gtlt = '<'.
                  pr = '-0'.
                ENDIF.
    
                with = |{ gtlt }{ ma }.{ mi }.{ pa }{ pr }|.
              ELSEIF xmi = abap_true.
                with = |>={ ma }.0.0{ pr } <{ str( ma + 1 ) }.0.0-0|.
              ELSEIF xpa = abap_true.
                with = |>={ ma }.{ mi }.0{ pr } <{ ma }.{ str( mi + 1 ) }.0-0|.
              ENDIF.
    
              IF with <> no_replace.
                m->replace_found( with ).
              ENDIF.
    
              result = m->text.
            ENDWHILE.
          CATCH cx_sy_arithmetic_overflow.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Overflow'.
          CATCH cx_sy_regex cx_sy_matcher INTO DATA(error).
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD replace_xranges.
    
        SPLIT /apmg/cl_apm_semver_utils=>trim( comp ) AT ` ` INTO TABLE DATA(comps).
    
        LOOP AT comps ASSIGNING FIELD-SYMBOL().
           = replace_xrange( comp =  loose = loose incpre = incpre ).
        ENDLOOP.
    
        result = concat_lines_of( table = comps sep = ` ` ).
    
      ENDMETHOD.
    
      METHOD str.
        " strip trailing space from integer
        result = condense( |{ value }| ).
      ENDMETHOD.
    
      METHOD test.
    
        TRY.
            DATA(semver) = /apmg/cl_apm_semver=>create(
              version = version
              loose   = options-loose
              incpre  = options-incpre ).
          CATCH /apmg/cx_apm_error.
            result = abap_false.
            RETURN.
        ENDTRY.
    
        CHECK semver IS BOUND.
    
        LOOP AT set ASSIGNING FIELD-SYMBOL().
          IF test_set(
            comparators = 
            version     = semver
            loose       = options-loose
            incpre      = options-incpre ).
    
            result = abap_true.
            RETURN.
          ENDIF.
        ENDLOOP.
    
        result = abap_false.
    
      ENDMETHOD.
    
      METHOD test_set.
    
        DATA(semver) = /apmg/cl_apm_semver=>create( version = version loose = loose incpre = incpre ).
    
        CHECK semver IS BOUND.
    
        LOOP AT comparators ASSIGNING FIELD-SYMBOL().
          IF ->test( semver ) = abap_false.
            result = abap_false.
            RETURN.
          ENDIF.
        ENDLOOP.
    
        IF semver->prerelease IS NOT INITIAL AND incpre = abap_false.
          " Find the set of versions that are allowed to have prereleases
          " For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0
          " That should allow `1.2.3-pr.2` to pass.
          " However, `1.2.4-alpha.notready` should NOT be allowed,
          " even though it's within the range set by the comparators.
          LOOP AT comparators ASSIGNING .
            IF ->semver = /apmg/cl_apm_semver_comparator=>any_semver.
              CONTINUE.
            ENDIF.
    
            IF ->semver->prerelease IS NOT INITIAL AND
               ->semver->major = semver->major AND
               ->semver->minor = semver->minor AND
               ->semver->patch = semver->patch.
              result = abap_true.
              RETURN.
            ENDIF.
          ENDLOOP.
    
          " Version has a -pre, but it's not one of the ones we like.
          result = abap_false.
          RETURN.
        ENDIF.
    
        result = abap_true.
    
      ENDMETHOD.
    
      METHOD to_string.
        result = range( ).
      ENDMETHOD.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_semver_ranges IMPLEMENTATION.
    
      METHOD gtr.
        result = outside(
          version = version
          range   = range
          hilo    = '>'
          loose   = loose
          incpre  = incpre ).
      ENDMETHOD.
    
      METHOD intersects.
    
        DATA(semrange1) = /apmg/cl_apm_semver_range=>create( range = r1 loose = loose incpre = incpre ).
    
        DATA(semrange2) = /apmg/cl_apm_semver_range=>create( range = r2 loose = loose incpre = incpre ).
    
        result = semrange1->intersects( range = semrange2 loose = loose incpre = incpre ).
    
      ENDMETHOD.
    
      METHOD ltr.
        result = outside(
          version = version
          range   = range
          hilo    = '<'
          loose   = loose
          incpre  = incpre ).
      ENDMETHOD.
    
      METHOD max_satisfying.
    
        DATA:
          max   TYPE string,
          maxsv TYPE REF TO /apmg/cl_apm_semver.
    
        TRY.
            DATA(semrange) = /apmg/cl_apm_semver_range=>create( range = range loose = loose incpre = incpre ).
          CATCH /apmg/cx_apm_error.
            result = ''.
            RETURN.
        ENDTRY.
    
        LOOP AT versions ASSIGNING FIELD-SYMBOL().
          IF semrange->test(  ).
            " satisfies(v, range, options)
            IF max IS INITIAL OR maxsv->compare(  ) = -1.
              " compare(max, v, true)
              max = .
              maxsv = /apmg/cl_apm_semver=>create( version = max loose = loose incpre = incpre ).
            ENDIF.
          ENDIF.
        ENDLOOP.
    
        result = max.
    
      ENDMETHOD.
    
      METHOD min_satisfying.
    
        DATA:
          min   TYPE string,
          minsv TYPE REF TO /apmg/cl_apm_semver.
    
        TRY.
            DATA(semrange) = /apmg/cl_apm_semver_range=>create( range = range loose = loose incpre = incpre ).
          CATCH /apmg/cx_apm_error.
            result = ''.
            RETURN.
        ENDTRY.
    
        LOOP AT versions ASSIGNING FIELD-SYMBOL().
          IF semrange->test(  ).
            " satisfies(v, range, options)
            IF min IS INITIAL OR minsv->compare(  ) = +1.
              " compare(min, v, true)
              min = .
              minsv = /apmg/cl_apm_semver=>create( version = min loose = loose incpre = incpre ).
            ENDIF.
          ENDIF.
        ENDLOOP.
    
        result = min.
    
      ENDMETHOD.
    
      METHOD min_version.
    
        DATA setmin TYPE REF TO /apmg/cl_apm_semver.
    
        DATA(semrange) = /apmg/cl_apm_semver_range=>create( range = range loose = loose incpre = incpre ).
    
        DATA(minver) = /apmg/cl_apm_semver=>create( '0.0.0' ).
        IF semrange->test( minver ).
          result = minver.
          RETURN.
        ENDIF.
    
        minver = /apmg/cl_apm_semver=>create( '0.0.0-0' ).
        IF semrange->test( minver ).
          result = minver.
          RETURN.
        ENDIF.
    
        CLEAR minver.
    
        LOOP AT semrange->set ASSIGNING FIELD-SYMBOL().
          DATA(comparators) = .
    
          CLEAR setmin.
          LOOP AT comparators ASSIGNING FIELD-SYMBOL().
            " Clone to avoid manipulating the comparator's semver object.
            DATA(compver) = /apmg/cl_apm_semver=>create( ->semver->version ).
    
            CASE ->operator.
              WHEN '>'.
                IF compver->prerelease IS INITIAL.
                  compver->inc( 'patch' ).
                ELSE.
                  compver->inc( release_type = 'prepush' identifier_base = '0' ).
                ENDIF.
    
                IF setmin IS INITIAL OR /apmg/cl_apm_semver_functions=>gt( a = compver b = setmin ).
                  setmin = compver.
                ENDIF.
    
              WHEN '' OR '>='.
                IF setmin IS INITIAL OR /apmg/cl_apm_semver_functions=>gt( a = compver b = setmin ).
                  setmin = compver.
                ENDIF.
    
              WHEN '<' OR '<='.
                " Ignore maximum versions
                ASSERT 0 = 0.
    
              WHEN OTHERS.
                RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Unexpected operation: { ->operator }|.
            ENDCASE.
          ENDLOOP.
    
          IF setmin IS NOT INITIAL AND ( minver IS INITIAL OR /apmg/cl_apm_semver_functions=>gt( a = minver b = setmin ) ).
            minver = setmin.
          ENDIF.
        ENDLOOP.
    
        IF minver IS NOT INITIAL AND semrange->test( minver ).
          result = minver.
          RETURN.
        ENDIF.
    
        CLEAR result.
    
      ENDMETHOD.
    
      METHOD outside.
    
        DATA:
          high TYPE REF TO /apmg/cl_apm_semver_comparator,
          low  TYPE REF TO /apmg/cl_apm_semver_comparator.
    
        DATA(semver) = /apmg/cl_apm_semver=>create( version = version loose = loose incpre = incpre ).
    
        DATA(semrange) = /apmg/cl_apm_semver_range=>create( range = range loose = loose incpre = incpre ).
    
        IF hilo NA '<>'.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Must provide a hilo val of "<" or ">"'.
        ENDIF.
    
        DATA(comp)  = hilo.
        DATA(ecomp) = comp && '='.
    
        " If it satisfies the range it is not outside
        IF /apmg/cl_apm_semver_functions=>satisfies(
          version = semver
          range   = semrange
          loose   = loose
          incpre  = incpre ).
    
          result = abap_false.
          RETURN.
        ENDIF.
    
        LOOP AT semrange->set ASSIGNING FIELD-SYMBOL().
          DATA(comparators) = .
    
          CLEAR: high, low.
    
          LOOP AT comparators ASSIGNING FIELD-SYMBOL().
            IF  = /apmg/cl_apm_semver_comparator=>any_semver.
               = /apmg/cl_apm_semver_comparator=>create( '>=0.0.0' ).
            ENDIF.
    
            IF high IS NOT BOUND.
              high = .
            ENDIF.
            IF low IS NOT BOUND.
              low = .
            ENDIF.
    
            CASE hilo.
              WHEN '>'.
                IF /apmg/cl_apm_semver_functions=>gt(
                  a      = ->semver
                  b      = high->semver
                  loose  = loose
                  incpre = incpre ).
    
                  high = .
                ELSEIF /apmg/cl_apm_semver_functions=>lt(
                  a      = ->semver
                  b      = low->semver
                  loose  = loose
                  incpre = incpre ).
    
                  low = .
                ENDIF.
              WHEN '<'.
                IF /apmg/cl_apm_semver_functions=>lt(
                  a      = ->semver
                  b      = high->semver
                  loose  = loose
                  incpre = incpre ).
    
                  high = .
                ELSEIF /apmg/cl_apm_semver_functions=>gt(
                  a      = ->semver
                  b      = low->semver
                  loose  = loose
                  incpre = incpre ).
    
                  low = .
                ENDIF.
            ENDCASE.
          ENDLOOP.
    
          " If the edge version comparator has a operator then our version isn't outside it
          IF high->operator = comp OR high->operator = ecomp.
            result = abap_false.
            RETURN.
          ENDIF.
    
          " If the lowest version comparator has an operator and our version
          " is less than it then it isn't higher than the range
          CASE hilo.
            WHEN '>'.
              IF ( low->operator IS INITIAL OR low->operator = comp ) AND
                /apmg/cl_apm_semver_functions=>lte( a = semver b = low->semver ).
    
                result = abap_false.
                RETURN.
              ELSEIF low->operator = ecomp AND /apmg/cl_apm_semver_functions=>lt( a = semver b = low->semver ).
                result = abap_false.
                RETURN.
              ENDIF.
            WHEN '<'.
              IF ( low->operator IS INITIAL OR low->operator = comp ) AND
                /apmg/cl_apm_semver_functions=>gte( a = semver b = low->semver ).
    
                result = abap_false.
                RETURN.
              ELSEIF low->operator = ecomp AND /apmg/cl_apm_semver_functions=>gt( a = semver b = low->semver ).
                result = abap_false.
                RETURN.
              ENDIF.
          ENDCASE.
    
        ENDLOOP.
    
        result = abap_true.
    
      ENDMETHOD.
    
      METHOD simplify.
        " Given a set of versions and a range, create a "simplified" range
        " that includes the same versions that the original range does
        " If the original range is shorter than the simplified one, return that.
    
        TYPES:
          BEGIN OF ty_min_max,
            min TYPE string,
            max TYPE string,
          END OF ty_min_max,
          ty_set TYPE STANDARD TABLE OF ty_min_max WITH KEY min max.
    
        DATA minmax TYPE ty_min_max.
        DATA set TYPE ty_set.
        DATA ranges TYPE string_table.
    
        IF versions IS INITIAL.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Empty version list'.
        ENDIF.
    
        DATA(semrange) = /apmg/cl_apm_semver_range=>create( range = range loose = loose incpre = incpre ).
    
        DATA(first) = ``.
        DATA(prev) = ``.
    
        DATA(v) = /apmg/cl_apm_semver_functions=>sort( list = versions loose = loose incpre = incpre ).
    
        LOOP AT v ASSIGNING FIELD-SYMBOL().
          IF /apmg/cl_apm_semver_functions=>satisfies(
            version = 
            range   = semrange
            loose   = loose
            incpre  = incpre ).
    
            prev = .
            IF first IS INITIAL.
              first = .
            ENDIF.
          ELSE.
            IF prev IS NOT INITIAL.
              minmax = VALUE #( min = first max = prev ).
              INSERT minmax INTO TABLE set.
            ENDIF.
            CLEAR prev.
            CLEAR first.
          ENDIF.
        ENDLOOP.
    
        IF first IS NOT INITIAL.
          minmax = VALUE #( min = first max = `` ).
          INSERT minmax INTO TABLE set.
        ENDIF.
    
        LOOP AT set ASSIGNING FIELD-SYMBOL().
          IF -min = -max.
            INSERT -min INTO TABLE ranges.
          ELSEIF -max IS INITIAL AND v[ 1 ] = -min.
            INSERT |*| INTO TABLE ranges.
          ELSEIF -max IS INITIAL.
            INSERT |>={ -min }| INTO TABLE ranges.
          ELSEIF v[ 1 ] = -min.
            INSERT |<={ -max }| INTO TABLE ranges.
          ELSE.
            INSERT |{ -min } - { -max }| INTO TABLE ranges.
          ENDIF.
        ENDLOOP.
    
        DATA(simplified) = concat_lines_of( table = ranges sep = ' || ' ).
    
        DATA(kind) = cl_abap_typedescr=>describe_by_data( range )->type_kind.
    
        IF kind = cl_abap_typedescr=>typekind_char OR kind = cl_abap_typedescr=>typekind_string.
          DATA(original) = CONV string( range ).
        ELSE.
          original = semrange->to_string( ).
        ENDIF.
    
        IF strlen( simplified ) < strlen( original ).
          result = simplified.
        ELSE.
          result = range.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD subset.
        " Complex range `r1 || r2 || ...` is a subset of `R1 || R2 || ...` iff:
        " - Every simple range `r1, r2, ...` is a null set, OR
        " - Every simple range `r1, r2, ...` which is not a null set is a subset of
        "   some `R1, R2, ...`
        "
        " Simple range `c1 c2 ...` is a subset of simple range `C1 C2 ...` iff:
        " - If c is only the ANY comparator
        "   - If C is only the ANY comparator, return true
        "   - Else if in prerelease mode, return false
        "   - else replace c with `[>=0.0.0]`
        " - If C is only the ANY comparator
        "   - if in prerelease mode, return true
        "   - else replace C with `[>=0.0.0]`
        " - Let EQ be the set of = comparators in c
        " - If EQ is more than one, return true (null set)
        " - Let GT be the highest > or >= comparator in c
        " - Let LT be the lowest < or <= comparator in c
        " - If GT and LT, and GT.semver > LT.semver, return true (null set)
        " - If any C is a = range, and GT or LT are set, return false
        " - If EQ
        "   - If GT, and EQ does not satisfy GT, return true (null set)
        "   - If LT, and EQ does not satisfy LT, return true (null set)
        "   - If EQ satisfies every C, return true
        "   - Else return false
        " - If GT
        "   - If GT.semver is lower than any > or >= comp in C, return false
        "   - If GT is >=, and GT.semver does not satisfy every C, return false
        "   - If GT.semver has a prerelease, and not in prerelease mode
        "     - If no C has a prerelease and the GT.semver tuple, return false
        " - If LT
        "   - If LT.semver is greater than any < or <= comp in C, return false
        "   - If LT is <=, and LT.semver does not satisfy every C, return false
        "   - If GT.semver has a prerelease, and not in prerelease mode
        "     - If no C has a prerelease and the LT.semver tuple, return false
        " - Else return true
    
        " https://github.com/npm/node-semver/blob/main/ranges/subset.js
        RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'TODO'.
    
      ENDMETHOD.
    
      METHOD to_comparators.
        " Mostly just for testing and legacy API reasons
    
        DATA comp_list TYPE ty_comp_list.
    
        DATA(semrange) = /apmg/cl_apm_semver_range=>create( range = range loose = loose incpre = incpre ).
    
        LOOP AT semrange->set ASSIGNING FIELD-SYMBOL().
          CLEAR comp_list.
          LOOP AT  ASSIGNING FIELD-SYMBOL().
            INSERT ->value INTO TABLE comp_list.
          ENDLOOP.
          INSERT comp_list INTO TABLE result.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD valid_range.
    
        TRY.
            " Return '*' instead of '' so that truthiness works.
            " This will throw if it's invalid anyway
            DATA(semrange) = /apmg/cl_apm_semver_range=>create( range = range loose = loose incpre = incpre ).
    
            IF semrange IS BOUND AND semrange->range( ) IS NOT INITIAL.
              result = semrange->range( ).
            ELSE.
              result = '*'.
            ENDIF.
          CATCH /apmg/cx_apm_error.
            result = ''.
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_semver_re IMPLEMENTATION.
    
      METHOD class_constructor.
    
        " The following Regular Expressions can be used for tokenizing,
        " validating, and parsing SemVer version strings.
    
        " ## POSIX: Additional logic required since POSIX regex is greedy
        " The whitespace after "v" or "=" will be trimmed separately
        create_token(
          name  = 'V'
          value = |[v=]*| ).
        " value = |[v=\\s]*| ) " PCRE
    
        create_token(
          name   = 'VTRIM'
          value  = |([v=]+)\\s+(\\d)|
          global = abap_true ).
    
        " ## Numeric Identifier
        " A single `0`, or a non-zero digit followed by zero or more digits.
    
        create_token(
          name  = 'NUMERICIDENTIFIER'
          value = |0\|[1-9]\\d*| ).
        create_token(
          name  = 'NUMERICIDENTIFIERLOOSE'
          value = |\\d+| ).
    
        " ## Non-numeric Identifier
        " Zero or more digits, followed by a letter or hyphen, and then zero or
        " more letters, digits, or hyphens.
    
        create_token(
          name  = 'NONNUMERICIDENTIFIER'
          value = |\\d*[a-zA-Z-]{ letter_dash_number }*| ).
    
        " ## Main Version
        " Three dot-separated numeric identifiers.
    
        create_token(
          name  = 'MAINVERSION'
          value = |({ token-numericidentifier-src })\\.| &&
                  |({ token-numericidentifier-src })\\.| &&
                  |({ token-numericidentifier-src })| ).
    
        create_token(
          name  = 'MAINVERSIONLOOSE'
          value = |({ token-numericidentifierloose-src })\\.| &&
                  |({ token-numericidentifierloose-src })\\.| &&
                  |({ token-numericidentifierloose-src })| ).
    
        " ## Pre-release Version Identifier
        " A numeric identifier, or a non-numeric identifier.
        " Non-numberic identifiers include numeric identifiers but can be longer.
        " Therefore non-numeric identifiers must go first.
    
        create_token(
          name  = 'PRERELEASEIDENTIFIER'
          value = |(?:{ token-nonnumericidentifier-src }\|| &&
                  |{ token-numericidentifier-src })| ).
    
        create_token(
          name  = 'PRERELEASEIDENTIFIERLOOSE'
          value = |(?:{ token-nonnumericidentifier-src }\|| &&
                  |{ token-numericidentifierloose-src })| ).
    
        " ## Pre-release Version
        " Hyphen, followed by one or more dot-separated pre-release version
        " identifiers.
    
        create_token(
          name  = 'PRERELEASE'
          value = |(?:-({ token-prereleaseidentifier-src }| &&
                  |(?:\\.{ token-prereleaseidentifier-src })*))| ).
    
        create_token(
          name  = 'PRERELEASELOOSE'
          value = |(?:-?({ token-prereleaseidentifierloose-src }| &&
                  |(?:\\.{ token-prereleaseidentifierloose-src })*))| ).
    
        " ## Build Metadata Identifier
        " Any combination of digits, letters, or hyphens.
    
        create_token(
          name  = 'BUILDIDENTIFIER'
          value = |{ letter_dash_number }+| ).
    
        " ## Build Metadata
        " Plus sign, followed by one or more period-separated build metadata
        " identifiers.
    
        create_token(
          name  = 'BUILD'
          value = |(?:\\+({ token-buildidentifier-src }| &&
                  |(?:\\.{ token-buildidentifier-src })*))| ).
    
        " ## Full Version String
        " A main version, followed optionally by a pre-release version and
        " build metadata.
    
        " Note that the only major, minor, patch, and pre-release sections of
        " the version string are capturing groups.  The build metadata is not a
        " capturing group, because it should not ever be used in version
        " comparison.
    
        create_token(
          name  = 'FULLPLAIN'
          value = |v?{ token-mainversion-src }{ token-prerelease-src }?{ token-build-src }?| ).
    
        create_token(
          name  = 'FULL'
          value = |^{ token-fullplain-src }$| ).
    
        " like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
        " also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
        " common in the npm registry.
    
        create_token(
          name  = 'LOOSEPLAIN'
          value = |{ token-v-src }{ token-mainversionloose-src }{ token-prereleaseloose-src }?{ token-build-src }?| ).
    
        create_token(
          name  = 'LOOSE'
          value = |^{ token-looseplain-src }$| ).
    
        create_token(
          name  = 'GTLT'
          value = |((?:<\|>)?=?)| ).
    
        " Something like "2.*" or "1.2.x".
        " Note that "x.x" is a valid xRange identifer, meaning "any version"
        " Only the first item is strictly required.
    
        create_token(
          name  = 'XRANGEIDENTIFIERLOOSE'
          value = |{ token-numericidentifierloose-src }\|x\|X\|\\*| ).
        create_token(
          name  = 'XRANGEIDENTIFIER'
          value = |{ token-numericidentifier-src }\|x\|X\|\\*| ).
    
        create_token(
          name  = 'XRANGEPLAIN'
          value = |{ token-v-src }({ token-xrangeidentifier-src })| &&
                  |(?:\\.({ token-xrangeidentifier-src })| &&
                  |(?:\\.({ token-xrangeidentifier-src })| &&
                  |(?:{ token-prerelease-src })?| &&
                  |{ token-build-src }?| &&
                  |)?)?| ).
    
        create_token(
          name  = 'XRANGEPLAINLOOSE'
          value = |{ token-v-src }({ token-xrangeidentifierloose-src })| &&
                  |(?:\\.({ token-xrangeidentifierloose-src })| &&
                  |(?:\\.({ token-xrangeidentifierloose-src })| &&
                  |(?:{ token-prereleaseloose-src })?| &&
                  |{ token-build-src }?| &&
                  |)?)?| ).
    
        create_token(
          name  = 'XRANGE'
          value = |^{ token-gtlt-src }\\s*{ token-xrangeplain-src }$| ).
        create_token(
          name  = 'XRANGELOOSE'
          value = |^{ token-gtlt-src }\\s*{ token-xrangeplainloose-src }$| ).
    
        " Coercion.
        " Extract anything that could conceivably be a part of a valid semver
    
        create_token(
          name  = 'COERCEPLAIN'
          value = |(^\|[^\\d])(\\d\{1,{ /apmg/if_apm_semver_constants=>max_safe_component_length }\})| &&
                  |(?:\\.(\\d\{1,{ /apmg/if_apm_semver_constants=>max_safe_component_length }\}))?| &&
                  |(?:\\.(\\d\{1,{ /apmg/if_apm_semver_constants=>max_safe_component_length }\}))?| ).
        create_token(
          name  = 'COERCE'
          value = |{ token-coerceplain-src }(?:$\|[^\\d])| ).
        create_token(
          name  = 'COERCEFULL'
          value = |{ token-coerceplain-src }| &&
                  |(?:{ token-prerelease-src })?| &&
                  |(?:{ token-build-src })?| &&
                  |(?:$\|[^\\d])| ).
        create_token(
          name   = 'COERCERTL'
          value  = token-coerce-src
          global = abap_true ).
        create_token(
          name   = 'COERCERTLFULL'
          value  = token-coercefull-src
          global = abap_true ).
    
        " Tilde ranges.
        " Meaning is "reasonably at or greater than"
    
        create_token(
          name  = 'LONETILDE'
          value = |(?:~>?)| ).
    
        create_token(
          name   = 'TILDETRIM'
          value  = |(\\s*){ token-lonetilde-src }\\s+|
          global = abap_true ).
    
        create_token(
          name  = 'TILDE'
          value = |^{ token-lonetilde-src }{ token-xrangeplain-src }$| ).
        create_token(
          name  = 'TILDELOOSE'
          value = |^{ token-lonetilde-src }{ token-xrangeplainloose-src }$| ).
    
        " Caret ranges.
        " Meaning is "at least and backwards compatible with"
    
        create_token(
          name  = 'LONECARET'
          value = |(?:\\^)| ).
    
        create_token(
          name   = 'CARETTRIM'
          value  = |(\\s*){ token-lonecaret-src }\\s+|
          global = abap_true ).
    
        create_token(
          name  = 'CARET'
          value = |^{ token-lonecaret-src }{ token-xrangeplain-src }$| ).
        create_token(
          name  = 'CARETLOOSE'
          value = |^{ token-lonecaret-src }{ token-xrangeplainloose-src }$| ).
    
        " A simple gt/lt/eq thing, or just "" to indicate "any version"
    
        create_token(
          name  = 'COMPARATORLOOSE'
          value = |^{ token-gtlt-src }\\s*({ token-looseplain-src })$\|^$| ).
        create_token(
          name  = 'COMPARATOR'
          value = |^{ token-gtlt-src }\\s*({ token-fullplain-src })$\|^$| ).
    
        " An expression to strip any whitespace between the gtlt and the thing
        " it modifies, so that `> 1.2.3` ==> `>1.2.3`
    
        create_token(
          name   = 'COMPARATORTRIM'
          value  = |(\\s*){ token-gtlt-src }\\s*({ token-looseplain-src }\|| &&
                   |{ token-xrangeplain-src })|
          global = abap_true ).
    
        " Something like `1.2.3 - 1.2.4`
        " Note that these all use the loose form, because they'll be
        " checked against either the strict or loose comparator form
        " later.
    
        create_token(
          name  = 'HYPHENRANGE'
          value = |^\\s*({ token-xrangeplain-src })| &&
                  |\\s+-\\s+| &&
                  |({ token-xrangeplain-src })| &&
                  |\\s*$| ).
    
        create_token(
          name  = 'HYPHENRANGELOOSE'
          value = |^\\s*({ token-xrangeplainloose-src })| &&
                  |\\s+-\\s+| &&
                  |({ token-xrangeplainloose-src })| &&
                  |\\s*$| ).
    
        " Star ranges basically just allow anything at all.
    
        create_token(
          name  = 'STAR'
          value = |(<\|>)?=?\\s*\\*| ).
    
        " >=0.0.0 is like a star
    
        create_token(
          name  = 'GTE0'
          value = |^\\s*>=\\s*0\\.0\\.0\\s*$| ).
        create_token(
          name  = 'GTE0PRE'
          value = |^\\s*>=\\s*0\\.0\\.0-0\\s*$| ).
    
      ENDMETHOD.
    
      METHOD create_token.
    
        FIELD-SYMBOLS  TYPE ty_token.
    
        ASSIGN COMPONENT name OF STRUCTURE token TO .
        ASSERT sy-subrc = 0.
    
        -src        = value.
        -regex      = NEW cl_abap_regex( pattern = value ) ##REGEX_POSIX.
        -safe_src   = make_safe_regex( value ).
        -safe_regex = NEW cl_abap_regex( pattern = make_safe_regex( value ) ) ##REGEX_POSIX.
        -occ        = COND #( WHEN global = abap_true THEN 0 ELSE 1 ).
    
      ENDMETHOD.
    
      METHOD make_safe_regex.
    
        TYPES:
          BEGIN OF ty_regex_replacement,
            token TYPE string,
            max   TYPE i,
          END OF ty_regex_replacement,
          ty_regex_replacements TYPE STANDARD TABLE OF ty_regex_replacement WITH KEY token max.
    
        " Replace some greedy regex tokens to prevent regex dos issues. These regex are
        " used internally via the safeRe object since all inputs in this library get
        " normalized first to trim and collapse all extra whitespace. The original
        " regexes are exported for userland consumption and lower level usage. A
        " future breaking change could export the safer regex only with a note that
        " all input should have extra whitespace removed.
    
        DATA(safe_regex_replacements) = VALUE ty_regex_replacements(
          ( token = `\s`               max = 1 )
          ( token = `\d`               max = /apmg/if_apm_semver_constants=>max_length )
          ( token = letter_dash_number max = /apmg/if_apm_semver_constants=>max_safe_build_length ) ).
    
        result = value.
    
        LOOP AT safe_regex_replacements INTO DATA(safe_regex_replacement).
          result = replace(
            val  = result
            sub  = |{ safe_regex_replacement-token }*|
            with = |{ safe_regex_replacement-token }\{0,{ safe_regex_replacement-max }\}|
            occ  = 0 ).
          result = replace(
            val  = result
            sub  = |{ safe_regex_replacement-token }+|
            with = |{ safe_regex_replacement-token }\{1,{ safe_regex_replacement-max }\}|
            occ  = 0 ).
        ENDLOOP.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_semver_sap IMPLEMENTATION.
    
      METHOD sap_component_to_semver.
    
        DATA cvers TYPE cvers.
    
        SELECT SINGLE * FROM cvers INTO @cvers WHERE component = @component.
        IF sy-subrc <> 0.
          RAISE EXCEPTION TYPE cx_abap_invalid_value
            EXPORTING
              value = |{ component }|.
        ENDIF.
    
        result = sap_release_to_semver( release = cvers-release support_pack = cvers-extrelease ).
    
      ENDMETHOD.
    
      METHOD sap_release_to_semver.
    
        " 750 > 7.50.x
        FIND REGEX '^(\d)(\d\d)\s*$' IN release SUBMATCHES DATA(ma) DATA(mi) ##REGEX_POSIX.
        IF sy-subrc <> 0.
          " 75E > 7.50.50x
          " FIXME? Maybe this should be 7.50.x as well?
          FIND REGEX '^(\d)(\d)([A-Z])\s*$' IN release SUBMATCHES ma mi DATA(pa) ##REGEX_POSIX.
          IF sy-subrc = 0.
            mi = mi * 10.
            pa = ( find( val = sy-abcde sub = pa ) + 1 ) * 100. " a=100, b=200, ...
          ELSE.
            " 1809 > 18.9.x
            FIND REGEX '^([1-9]\d)(\d\d)\s*$' IN release SUBMATCHES ma mi ##REGEX_POSIX.
            IF sy-subrc = 0.
              pa = 0.
            ELSE.
              " 2011_1_731 > 2011.1.731
              FIND REGEX '^20\d+_\d+_(\d)(\d\d)\s*$' IN release SUBMATCHES ma mi ##REGEX_POSIX.
              IF sy-subrc <> 0.
                " ST-A/PI: 01V_731 > 7.3.1
                FIND REGEX '^01._(\d)(\d\d)\s*$' IN release SUBMATCHES ma mi ##REGEX_POSIX.
                IF sy-subrc <> 0.
                  " unknown pattern... open GitHub issue
                  RAISE EXCEPTION TYPE cx_abap_invalid_value
                    EXPORTING
                      value = release && '/' && support_pack.
                ENDIF.
              ENDIF.
            ENDIF.
          ENDIF.
        ENDIF.
    
        DATA(int_ma) = CONV i( condense( ma ) ).
        DATA(int_mi) = CONV i( condense( mi ) ).
        DATA(int_pa) = CONV i( condense( pa ) ).
    
        IF support_pack IS NOT INITIAL AND support_pack CO ' 0123456789'.
          int_pa = int_pa + CONV i( condense( support_pack ) ).
        ENDIF.
    
        result = condense( |{ int_ma }.{ int_mi }.{ int_pa }| ).
    
      ENDMETHOD.
    
      METHOD semver_to_sap_release.
    
        FIND REGEX '^(\d)\.(\d{1,2})\.(\d+)' IN version SUBMATCHES DATA(ma) DATA(mi) DATA(pa) ##REGEX_POSIX.
        IF sy-subrc = 0.
          IF mi < 10.
            mi = '0' && mi.
          ENDIF.
          IF pa < 100.
            pa = ''.
          ELSE.
            " 75E <- 7.50.500
            mi = mi(1).
            pa = substring( val = sy-abcde off = ( pa DIV 100 - 1 ) len = 1 ). " 1xx=a, 2xx=b, ...
          ENDIF.
        ELSE.
          " 1809 <- 18.9.0
          FIND REGEX '^([1-9]\d)\.(\d{1,2})\.(\d)' IN version SUBMATCHES ma mi pa ##REGEX_POSIX.
          IF sy-subrc = 0.
            IF mi < 10.
              mi = '0' && mi.
            ENDIF.
            pa = ''.
          ELSE.
            " unknown pattern...
            RAISE EXCEPTION TYPE cx_abap_invalid_value
              EXPORTING
                value = version.
          ENDIF.
        ENDIF.
    
        result = |{ ma }{ mi }{ pa }|.
    
      ENDMETHOD.
    
      METHOD semver_to_sap_release_sp.
    
        release = semver_to_sap_release( version ).
    
        FIND REGEX '^(\d+)\.(\d+)\.(\d+)' IN version SUBMATCHES DATA(ma) DATA(mi) DATA(pa) ##NEEDED ##REGEX_POSIX.
        IF sy-subrc = 0 AND pa > 100.
          pa = substring( val = pa off = strlen( pa ) - 2 ).
        ENDIF.
    
        support_pack = |{ pa ALIGN = RIGHT WIDTH = 10 PAD = '0' }|.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_semver_utils IMPLEMENTATION.
    
      METHOD is_numeric.
        " Unsigned number (could be bigger than int4 or even int8)
    
        TRY.
            result = xsdbool( condense( |{ data }| ) CO '0123456789' ).
          CATCH cx_root.
            " can't be converted to string/numeric
            result = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD trim.
        " Remove leading and trailing tab, cr, lf and spaces Like JavaScript trim
        result = condense(
          val = replace(
            val   = data
            regex = `[\t\n\r]`
            with  = ` `
            occ   = 0 )
          del = ` ` ) ##REGEX_POSIX.
      ENDMETHOD.
    
      METHOD version_trim.
        " POSIX: Remove whitespace after "v" or "=" to avoid issue with greedy regex
        result = replace(
          val   = trim( data )
          regex = /apmg/cl_apm_semver_re=>token-vtrim-src
          with  = /apmg/cl_apm_semver_re=>version_trim_replace
          occ   = /apmg/cl_apm_semver_re=>token-vtrim-occ ) ##REGEX_POSIX.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_settings IMPLEMENTATION.
    
      METHOD /apmg/if_apm_settings~delete.
    
        IF name = /apmg/if_apm_settings=>c_global.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Global settings can not be deleted'.
        ENDIF.
    
        db_persist->delete( key ).
    
      ENDMETHOD.
    
      METHOD /apmg/if_apm_settings~get.
    
        result = settings.
    
        IF name <> /apmg/if_apm_settings=>c_global.
          merge_settings( CHANGING cs_settings = result ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD /apmg/if_apm_settings~get_json.
    
        TRY.
            DATA(ajson) = /apmg/cl_apm_ajson=>new(
              )->keep_item_order(
              )->set(
                iv_path = '/'
                iv_val  = /apmg/if_apm_settings~get( )
              )->map( /apmg/cl_apm_ajson_mapping=>create_to_camel_case( ) ).
    
            IF is_complete = abap_false.
              ajson = ajson->filter( /apmg/cl_apm_ajson_extensions=>filter_empty_zero_null( ) ).
            ENDIF.
    
            result = ajson->stringify( 2 ) && |\n|.
          CATCH /apmg/cx_apm_ajson_error INTO DATA(error).
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD /apmg/if_apm_settings~is_valid.
    
        result = xsdbool( check_settings( settings ) IS INITIAL ).
    
      ENDMETHOD.
    
      METHOD /apmg/if_apm_settings~load.
    
        /apmg/if_apm_settings~set_json( db_persist->load( key )-value ).
        result = me.
    
      ENDMETHOD.
    
      METHOD /apmg/if_apm_settings~save.
    
        IF /apmg/if_apm_settings~is_valid( ) = abap_false.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Invalid settings: { key }|.
        ENDIF.
    
        " Save complete JSON including empty values for easy editing
        db_persist->save(
          key   = key
          value = /apmg/if_apm_settings~get_json( abap_true ) ).
    
      ENDMETHOD.
    
      METHOD /apmg/if_apm_settings~set.
    
        IF check_settings( settings ) IS NOT INITIAL.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Invalid settings: { key }|.
        ENDIF.
    
        me->settings = CORRESPONDING #( settings ).
        result = me.
    
      ENDMETHOD.
    
      METHOD /apmg/if_apm_settings~set_json.
    
        DATA settings TYPE /apmg/if_apm_settings=>ty_settings.
    
        TRY.
            DATA(ajson) = /apmg/cl_apm_ajson=>parse( json
              )->to_abap_corresponding_only(
              )->map( /apmg/cl_apm_ajson_extensions=>from_camel_case_underscore( ) ).
    
            ajson->to_abap( IMPORTING ev_container = settings ).
    
            IF check_settings( settings ) IS NOT INITIAL.
              RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Invalid settings: { key }|.
            ENDIF.
    
            me->settings = CORRESPONDING #( settings ).
          CATCH /apmg/cx_apm_ajson_error INTO DATA(error).
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_prev EXPORTING previous = error.
        ENDTRY.
    
        result = me.
    
      ENDMETHOD.
    
      METHOD check_settings.
    
        TRY.
            IF is_settings-registry IS NOT INITIAL.
              DATA(url) = /apmg/cl_apm_url=>parse( is_settings-registry ).
    
              IF url->components-path IS NOT INITIAL.
                INSERT |Registry URL must not include any trailing slash or path: { is_settings-registry }|
                  INTO TABLE result.
              ENDIF.
              IF url->components-query IS NOT INITIAL.
                INSERT |Registry URL must not include any query: { is_settings-registry }| INTO TABLE result.
              ENDIF.
              IF url->components-fragment IS NOT INITIAL.
                INSERT |Registry URL must not include any fragment: { is_settings-registry }| INTO TABLE result.
              ENDIF.
            ENDIF.
          CATCH /apmg/cx_apm_error.
            INSERT |Invalid registry URL: { is_settings-registry }| INTO TABLE result.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD class_constructor.
    
        db_persist = /apmg/cl_apm_persist_apm=>get_instance( ).
    
      ENDMETHOD.
    
      METHOD constructor.
    
        " Expect ABAP user name or "GLOBAL"
        ASSERT strlen( name ) BETWEEN 1 AND 12.
    
        me->name = name.
        key = get_setting_key( name ).
    
        TRY.
            /apmg/if_apm_settings~load( ).
          CATCH /apmg/cx_apm_error.
            IF name = /apmg/if_apm_settings=>c_global.
              settings = get_default( ).
            ENDIF.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD factory.
    
        READ TABLE instances ASSIGNING FIELD-SYMBOL() WITH TABLE KEY name = name.
        IF sy-subrc = 0.
          result = -instance.
        ELSE.
          result = NEW /apmg/cl_apm_settings( name ).
    
          DATA(instance) = VALUE ty_instance(
            name     = name
            instance = result ).
          INSERT instance INTO TABLE instances.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_default.
    
        " Default values for settings
        " TODO: Change to production registry
        result-registry = /apmg/if_apm_settings=>c_playground.
    
        result-list_settings-order_by = 'PACKAGE'.
    
      ENDMETHOD.
    
      METHOD get_setting_key.
    
        IF name = /apmg/if_apm_settings=>c_global.
          result = |{ /apmg/if_apm_persist_apm=>c_key_type-settings }:{ /apmg/if_apm_settings=>c_global }:ALL|.
        ELSE.
          result = |{ /apmg/if_apm_persist_apm=>c_key_type-settings }:{ /apmg/if_apm_settings=>c_user }:{ name }|.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD initialize_global_settings.
    
        DATA(global) = factory( /apmg/if_apm_settings=>c_global ).
    
        " Check if global settings exist already
        TRY.
            DATA(settings) = global->load( )->get( ).
          CATCH /apmg/cx_apm_error ##NO_HANDLER.
        ENDTRY.
    
        IF settings IS NOT INITIAL.
          RETURN.
        ENDIF.
    
        global->set( get_default( ) ).
    
        " Save defaults to global settings
        db_persist->save(
          key   = get_setting_key( /apmg/if_apm_settings=>c_global )
          value = global->get_json( ) ).
    
      ENDMETHOD.
    
      METHOD injector.
    
        READ TABLE instances ASSIGNING FIELD-SYMBOL() WITH TABLE KEY name = name.
        IF sy-subrc = 0.
          -instance = mock.
        ELSE.
          DATA(instance) = VALUE ty_instance(
            name     = name
            instance = mock ).
          INSERT instance INTO TABLE instances.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD merge_settings.
    
        TRY.
            DATA(global) = factory( /apmg/if_apm_settings=>c_global )->get( ).
          CATCH /apmg/cx_apm_error ##NO_HANDLER.
            " Just use defaults
        ENDTRY.
    
        DATA(default) = get_default( ).
    
        DO.
          " Current settings
          ASSIGN COMPONENT sy-index OF STRUCTURE cs_settings TO FIELD-SYMBOL().
          IF sy-subrc <> 0.
            EXIT.
          ENDIF.
    
          IF  IS INITIAL.
            " Global settings
            ASSIGN COMPONENT sy-index OF STRUCTURE global TO FIELD-SYMBOL().
            ASSERT sy-subrc = 0.
    
            IF  IS INITIAL.
              " apm default settings
              ASSIGN COMPONENT sy-index OF STRUCTURE default TO FIELD-SYMBOL().
              ASSERT sy-subrc = 0.
    
               = .
            ELSE.
               = .
            ENDIF.
          ENDIF.
        ENDDO.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS lcx_error DEFINITION FINAL INHERITING FROM cx_no_check.
      PUBLIC SECTION.
    
        INTERFACES if_t100_message.
        CONSTANTS:
          BEGIN OF c_error_signature,
            msgid TYPE symsgid VALUE 'SY',
            msgno TYPE symsgno VALUE '002', " &
            attr1 TYPE scx_attrname VALUE 'MSG',
            attr2 TYPE scx_attrname VALUE '',
            attr3 TYPE scx_attrname VALUE '',
            attr4 TYPE scx_attrname VALUE '',
          END OF c_error_signature.
        DATA msg TYPE string READ-ONLY.
    
        CLASS-METHODS raise
          IMPORTING
            iv_msg TYPE string.
      PRIVATE SECTION.
    ENDCLASS.
    
    CLASS lcx_error IMPLEMENTATION.
      METHOD raise.
        DATA lx_e TYPE REF TO lcx_error.
        CREATE OBJECT lx_e.
        lx_e->msg = iv_msg.
        lx_e->if_t100_message~t100key = c_error_signature.
        RAISE EXCEPTION lx_e.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_string_map IMPLEMENTATION.
    
      METHOD clear.
    
        IF mv_read_only = abap_true.
          lcx_error=>raise( 'String map is read only' ).
        ENDIF.
    
        CLEAR mt_entries.
    
      ENDMETHOD.
    
      METHOD constructor.
        mv_is_strict = abap_true.
        mv_case_insensitive = iv_case_insensitive.
        mv_list_mode = iv_list_mode.
    
        IF iv_from IS NOT INITIAL.
          DATA lo_type TYPE REF TO cl_abap_typedescr.
          lo_type = cl_abap_typedescr=>describe_by_data( iv_from ).
    
          CASE lo_type->type_kind.
            WHEN cl_abap_typedescr=>typekind_struct1 OR cl_abap_typedescr=>typekind_struct2.
              me->from_struc( iv_from ).
    
            WHEN cl_abap_typedescr=>typekind_oref.
              DATA lo_from TYPE REF TO /apmg/cl_apm_string_map.
              TRY.
                  lo_from ?= iv_from.
                CATCH cx_sy_move_cast_error.
                  lcx_error=>raise( 'Incorrect string map instance to copy from' ).
              ENDTRY.
    
              IF mt_entries IS INITIAL AND mv_case_insensitive = abap_false.
                me->mt_entries = lo_from->mt_entries. " shortcut, maybe remove for safety
              ELSE.
                me->from_map( lo_from ).
              ENDIF.
    
            WHEN cl_abap_typedescr=>typekind_table.
              me->from_entries( iv_from ).
    
            WHEN cl_abap_typedescr=>typekind_string OR cl_abap_typedescr=>typekind_char.
              me->from_string( iv_from ).
    
            WHEN OTHERS.
              lcx_error=>raise( |Incorrect input for string_map=>create, typekind { lo_type->type_kind }| ).
          ENDCASE.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD create.
        CREATE OBJECT ro_instance
          EXPORTING
            iv_list_mode        = iv_list_mode
            iv_case_insensitive = iv_case_insensitive
            iv_from             = iv_from.
      ENDMETHOD.
    
      METHOD delete.
    
        IF mv_read_only = abap_true.
          lcx_error=>raise( 'String map is read only' ).
        ENDIF.
    
        DATA lv_key TYPE string.
    
        IF mv_case_insensitive = abap_true.
          lv_key = to_upper( iv_key ).
        ELSE.
          lv_key = iv_key.
        ENDIF.
    
        DELETE mt_entries WHERE k = lv_key.
    
      ENDMETHOD.
    
      METHOD freeze.
        mv_read_only = abap_true.
      ENDMETHOD.
    
      METHOD from_entries.
    
        FIELD-SYMBOLS  TYPE ty_entry.
    
        LOOP AT it_entries ASSIGNING  CASTING.
          set(
            iv_key = -k
            iv_val = -v ).
        ENDLOOP.
    
        ro_instance = me.
    
      ENDMETHOD.
    
      METHOD from_map.
    
        from_entries( io_string_map->mt_entries ).
        ro_instance = me.
    
      ENDMETHOD.
    
      METHOD from_string.
    
        IF iv_string_params IS INITIAL.
          RETURN.
        ENDIF.
    
        DATA lt_lines TYPE string_table.
        FIELD-SYMBOLS  LIKE LINE OF lt_lines.
        SPLIT iv_string_params AT ',' INTO TABLE lt_lines.
    
        DATA lv_key TYPE string.
        DATA lv_val TYPE string.
    
        LOOP AT lt_lines ASSIGNING .
          SPLIT  AT '=' INTO lv_key lv_val.
          SHIFT lv_key RIGHT DELETING TRAILING space.
          SHIFT lv_key LEFT DELETING LEADING space.
          SHIFT lv_val RIGHT DELETING TRAILING space.
          SHIFT lv_val LEFT DELETING LEADING space.
          IF lv_key IS INITIAL.
            lcx_error=>raise( 'Empty key in initialization string is not allowed' ).
            " value can be initial, even a,b,c is ok to create sets
          ENDIF.
          set(
            iv_key = lv_key
            iv_val = lv_val ).
        ENDLOOP.
    
        ro_instance = me.
    
      ENDMETHOD.
    
      METHOD from_struc.
    
        DATA lo_type TYPE REF TO cl_abap_typedescr.
        DATA lo_struc TYPE REF TO cl_abap_structdescr.
        FIELD-SYMBOLS  LIKE LINE OF lo_struc->components.
        FIELD-SYMBOLS  TYPE any.
    
        lo_type = cl_abap_typedescr=>describe_by_data( is_container ).
        IF lo_type->type_kind <> cl_abap_typedescr=>typekind_struct1
          AND lo_type->type_kind <> cl_abap_typedescr=>typekind_struct2.
          lcx_error=>raise( 'Only structures supported' ).
        ENDIF.
    
        lo_struc ?= lo_type.
        LOOP AT lo_struc->components ASSIGNING .
          CHECK -type_kind CO 'bsI8PaeFCNgXyDT'. " values
          ASSIGN COMPONENT -name OF STRUCTURE is_container TO .
          ASSERT sy-subrc = 0.
          set(
            iv_key = |{ -name }|
            iv_val = |{  }| ).
        ENDLOOP.
    
        ro_instance = me.
    
      ENDMETHOD.
    
      METHOD get.
    
        DATA lv_key TYPE string.
        FIELD-SYMBOLS  LIKE LINE OF mt_entries.
    
        IF mv_case_insensitive = abap_true.
          lv_key = to_upper( iv_key ).
        ELSE.
          lv_key = iv_key.
        ENDIF.
    
        READ TABLE mt_entries ASSIGNING  WITH KEY k = lv_key.
        IF sy-subrc = 0.
          rv_val = -v.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD has.
    
        DATA lv_key TYPE string.
    
        IF mv_case_insensitive = abap_true.
          lv_key = to_upper( iv_key ).
        ELSE.
          lv_key = iv_key.
        ENDIF.
    
        READ TABLE mt_entries TRANSPORTING NO FIELDS WITH KEY k = lv_key.
        rv_has = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD is_empty.
        rv_yes = boolc( lines( mt_entries ) = 0 ).
      ENDMETHOD.
    
      METHOD keys.
    
        FIELD-SYMBOLS  LIKE LINE OF mt_entries.
        LOOP AT mt_entries ASSIGNING .
          APPEND -k TO rt_keys.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD merge.
    
        FIELD-SYMBOLS  LIKE LINE OF mt_entries.
    
        LOOP AT io_string_map->mt_entries ASSIGNING .
          set(
            iv_key = -k
            iv_val = -v ).
        ENDLOOP.
    
        ro_instance = me.
    
      ENDMETHOD.
    
      METHOD set.
    
        DATA ls_entry LIKE LINE OF mt_entries.
        DATA lv_key TYPE string.
        FIELD-SYMBOLS  LIKE LINE OF mt_entries.
    
        IF mv_read_only = abap_true.
          lcx_error=>raise( 'String map is read only' ).
        ENDIF.
    
        IF mv_case_insensitive = abap_true.
          lv_key = to_upper( iv_key ).
        ELSE.
          lv_key = iv_key.
        ENDIF.
    
        IF mv_list_mode = abap_true.
          ls_entry-k = lv_key.
          ls_entry-v = iv_val.
          INSERT ls_entry INTO TABLE mt_entries.
        ELSE.
          READ TABLE mt_entries ASSIGNING  WITH KEY k = lv_key.
          IF sy-subrc = 0.
            -v = iv_val.
          ELSE.
            ls_entry-k = lv_key.
            ls_entry-v = iv_val.
            INSERT ls_entry INTO TABLE mt_entries.
          ENDIF.
        ENDIF.
    
        ro_map = me.
    
      ENDMETHOD.
    
      METHOD setx.
    
        DATA lv_key TYPE string.
        DATA lv_val TYPE string.
    
        ro_map = me.
    
        IF iv_str IS INITIAL.
          RETURN.
        ENDIF.
    
        SPLIT iv_str AT ':' INTO lv_key lv_val.
        CONDENSE lv_key.
        CONDENSE lv_val.
    
        IF lv_key IS INITIAL.
          RETURN.
        ENDIF.
    
        set(
          iv_key = lv_key
          iv_val = lv_val ).
    
      ENDMETHOD.
    
      METHOD size.
    
        rv_size = lines( mt_entries ).
    
      ENDMETHOD.
    
      METHOD strict.
        mv_is_strict = iv_strict.
        ro_instance = me.
      ENDMETHOD.
    
      METHOD to_entries.
    
        DATA lo_ttype TYPE REF TO cl_abap_tabledescr.
        DATA lo_dtype TYPE REF TO cl_abap_datadescr.
        DATA lo_stype TYPE REF TO cl_abap_structdescr.
    
        lo_ttype ?= cl_abap_typedescr=>describe_by_data( ct_entries ).
        lo_dtype = lo_ttype->get_table_line_type( ).
    
        IF lo_dtype->kind <> cl_abap_typedescr=>kind_struct.
          lcx_error=>raise( 'Unsupported table line type' ).
        ENDIF.
    
        lo_stype ?= lo_dtype.
    
        IF lines( lo_stype->components ) <> 2.
          lcx_error=>raise( 'Wrong number of fields in target table (must be 2)' ).
        ENDIF.
    
        FIELD-SYMBOLS  LIKE LINE OF lo_stype->components.
        LOOP AT lo_stype->components ASSIGNING .
          IF NOT ( -type_kind = cl_abap_typedescr=>typekind_char OR -type_kind = cl_abap_typedescr=>typekind_string ).
            lcx_error=>raise( 'Wrong type of fields in target table (must be char or string)' ).
          ENDIF.
        ENDLOOP.
    
        FIELD-SYMBOLS  LIKE LINE OF mt_entries.
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE any.
        LOOP AT mt_entries ASSIGNING .
          APPEND INITIAL LINE TO ct_entries ASSIGNING .
          ASSERT sy-subrc = 0.
          ASSIGN COMPONENT 1 OF STRUCTURE  TO .
          ASSERT sy-subrc = 0.
          ASSIGN COMPONENT 2 OF STRUCTURE  TO .
          ASSERT sy-subrc = 0.
           = -k.
           = -v.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD to_string.
    
        DATA lv_size TYPE i.
        FIELD-SYMBOLS  LIKE LINE OF mt_entries.
    
        lv_size = lines( mt_entries ).
        LOOP AT mt_entries ASSIGNING .
          rv_string = rv_string && -k && '=' && -v.
          IF sy-tabix < lv_size.
            rv_string = rv_string && ','.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD to_struc.
    
        DATA lo_type TYPE REF TO cl_abap_typedescr.
        DATA lo_struc TYPE REF TO cl_abap_structdescr.
        DATA lv_field TYPE string.
        FIELD-SYMBOLS  LIKE LINE OF mt_entries.
        FIELD-SYMBOLS  TYPE any.
    
        lo_type = cl_abap_typedescr=>describe_by_data( cs_container ).
        IF lo_type->type_kind <> cl_abap_typedescr=>typekind_struct1
          AND lo_type->type_kind <> cl_abap_typedescr=>typekind_struct2.
          lcx_error=>raise( 'Only structures supported' ).
        ENDIF.
    
        lo_struc ?= lo_type.
        LOOP AT mt_entries ASSIGNING .
          lv_field = to_upper( -k ).
          ASSIGN COMPONENT lv_field OF STRUCTURE cs_container TO .
          IF sy-subrc = 0.
            " TODO check target type ?
             = -v.
          ELSEIF mv_is_strict = abap_false.
            CONTINUE.
          ELSE.
            lcx_error=>raise( |Component { lv_field } not found in target| ).
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD values.
    
        FIELD-SYMBOLS  LIKE LINE OF mt_entries.
        LOOP AT mt_entries ASSIGNING .
          APPEND -v TO rt_values.
        ENDLOOP.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS lcl_tar_helpers DEFINITION.
    
      PUBLIC SECTION.
    
        CLASS-METHODS from_octal
          IMPORTING
            !octal        TYPE string
          RETURNING
            VALUE(result) TYPE i.
    
        CLASS-METHODS to_octal
          IMPORTING
            !number       TYPE numeric
          RETURNING
            VALUE(result) TYPE string.
    
        CLASS-METHODS from_xstring
          IMPORTING
            !data         TYPE xstring
          RETURNING
            VALUE(result) TYPE string
          RAISING
            /apmg/cx_apm_error.
    
        CLASS-METHODS to_xstring
          IMPORTING
            !data         TYPE simple
          RETURNING
            VALUE(result) TYPE xstring
          RAISING
            /apmg/cx_apm_error.
    
      PRIVATE SECTION.
    
        CLASS-DATA:
          convert_in  TYPE REF TO cl_abap_conv_in_ce,
          convert_out TYPE REF TO cl_abap_conv_out_ce.
    
    ENDCLASS.
    
    CLASS lcl_tar_helpers IMPLEMENTATION.
    
      METHOD from_octal.
    
        DATA(offset) = 0.
    
        DO strlen( octal ) TIMES.
          result = result * 8 + octal+offset(1).
          offset = offset + 1.
        ENDDO.
    
      ENDMETHOD.
    
      METHOD to_octal.
    
        DATA(temp_number) = CONV i( number ).
    
        WHILE temp_number > 0.
          result      = |{ temp_number MOD 8 }{ result }|.
          temp_number = temp_number DIV 8.
        ENDWHILE.
    
        IF result IS INITIAL.
          result = '0'.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD from_xstring.
    
        IF convert_in IS INITIAL.
          convert_in = cl_abap_conv_in_ce=>create( encoding = 'UTF-8' ).
        ENDIF.
    
        TRY.
            convert_in->convert(
              EXPORTING
                input = data
                n     = xstrlen( data )
              IMPORTING
                data  = result ).
    
          CATCH cx_sy_codepage_converter_init
                cx_sy_conversion_codepage
                cx_parameter_invalid_type.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error converting from xstring'.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD to_xstring.
    
        IF convert_out IS INITIAL.
          convert_out = cl_abap_conv_out_ce=>create( encoding = 'UTF-8' ).
        ENDIF.
    
        DATA(string_data) = CONV string( data ).
    
        TRY.
            convert_out->convert(
              EXPORTING
                data   = string_data
              IMPORTING
                buffer = result ).
    
          CATCH cx_sy_codepage_converter_init
                cx_sy_conversion_codepage
                cx_parameter_invalid_type.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error converting to xstring'.
        ENDTRY.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS lcl_pax DEFINITION.
    
    * Pax format stores keyword lists in ustar blocks
    * https://pubs.opengroup.org/onlinepubs/009695399/utilities/pax.html
    *
    * A keyword list consists of records constructed as follows:
    * "%d %s=%s\n", , , 
    
      PUBLIC SECTION.
    
        CLASS-METHODS decode_keywords
          IMPORTING
            block         TYPE xstring
          RETURNING
            VALUE(result) TYPE /apmg/cl_apm_tar=>ty_keywords.
    
        CLASS-METHODS encode_keywords
          IMPORTING
            keywords      TYPE /apmg/cl_apm_tar=>ty_keywords
          RETURNING
            VALUE(result) TYPE xstring.
    
        CLASS-METHODS merge_keywords
          IMPORTING
            global        TYPE /apmg/cl_apm_tar=>ty_keywords
            extended      TYPE /apmg/cl_apm_tar=>ty_keywords
          RETURNING
            VALUE(result) TYPE /apmg/cl_apm_tar=>ty_keywords.
    
    ENDCLASS.
    
    CLASS lcl_pax IMPLEMENTATION.
    
      METHOD decode_keywords.
    
        DATA pax_records TYPE string_table.
    
        DATA(pax_data) = cl_binary_convert=>xstring_utf8_to_string( block ).
    
        SPLIT pax_data AT cl_abap_char_utilities=>newline INTO TABLE pax_records.
    
        LOOP AT pax_records ASSIGNING FIELD-SYMBOL().
          SPLIT  AT ` ` INTO DATA(octal_len) DATA(key_val).
          DATA(len) = lcl_tar_helpers=>from_octal( octal_len ) - 1.
          IF strlen( key_val ) <> len.
            ASSERT 0 = 0. " ignore this inconsistency
          ENDIF.
    
          SPLIT key_val AT `=` INTO DATA(key) DATA(value).
          DATA(keyword) = VALUE /apmg/cl_apm_tar=>ty_keyword(
            keyword = key
            value   = value ).
          INSERT keyword INTO TABLE result.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD encode_keywords.
    
        DATA pax_records TYPE string_table.
        DATA block TYPE x LENGTH /apmg/cl_apm_tar=>c_blocksize.
    
        LOOP AT keywords ASSIGNING FIELD-SYMBOL().
          DATA(pax_record) = |{ -keyword }={ -value }|.
          DATA(len) = strlen( pax_record ) + 1. " +1 for newline
          DATA(octal_len) = lcl_tar_helpers=>to_octal( len ).
          pax_record = octal_len && pax_record.
          INSERT pax_record INTO TABLE pax_records.
        ENDLOOP.
    
        DATA(pax_data) = concat_lines_of(
          table = pax_records
          sep   = cl_abap_char_utilities=>newline ).
    
        pax_data = pax_data && cl_abap_char_utilities=>newline.
    
        result = cl_binary_convert=>string_to_xstring_utf8( pax_data ).
    
        len = /apmg/cl_apm_tar=>c_blocksize - xstrlen( result ).
    
        IF len < 0.
          " TODO: What if the keywords don't fit into a blocK?
          ASSERT 1 = 2.
        ELSE.
          result = result && block(len).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD merge_keywords.
    
        result = global.
    
        LOOP AT extended ASSIGNING FIELD-SYMBOL().
          READ TABLE result ASSIGNING FIELD-SYMBOL()
            WITH TABLE KEY keyword = -keyword.
          IF sy-subrc = 0.
            -value = -value.
          ELSE.
            INSERT  INTO TABLE result.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS lcl_7zip DEFINITION.
    
      PUBLIC SECTION.
    
        CLASS-METHODS decode_longlink
          IMPORTING
            block_1       TYPE xstring
            block_2       TYPE xstring
          RETURNING
            VALUE(result) TYPE string
          RAISING
            /apmg/cx_apm_error.
    
    ENDCLASS.
    
    CLASS lcl_7zip IMPLEMENTATION.
    
      METHOD decode_longlink.
    
        CONSTANTS c_longlink TYPE string VALUE `././@LongLink`.
    
        DATA(header) = CONV /apmg/cl_apm_tar=>ty_header( lcl_tar_helpers=>from_xstring( block_1 ) ).
    
        IF header-name = c_longlink.
          result = lcl_tar_helpers=>from_xstring( block_2 ).
        ENDIF.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS /apmg/cl_apm_tar IMPLEMENTATION.
    
      METHOD append.
    
        " TODO: Support long filenames (pax)
        IF strlen( name ) > 100.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = |Filename longer than 100 characters: { name }|.
        ENDIF.
    
        " List
        DATA(file) = VALUE ty_file(
          name     = name
          date     = date
          time     = time
          mode     = mode
          typeflag = typeflag
          keywords = keywords
          size     = xstrlen( content ) ).
    
        IF date IS INITIAL.
          file-date = sy-datum.
        ENDIF.
        IF time IS INITIAL.
          file-time = sy-uzeit.
        ENDIF.
        IF mode IS INITIAL.
          file-mode = c_mode_default.
        ENDIF.
        IF typeflag IS INITIAL.
          file-typeflag = c_typeflag-file.
        ENDIF.
        file-unixtime = _to_unixtime( date = file-date time = file-time ).
    
        INSERT file INTO TABLE tar_files.
        IF sy-subrc <> 0.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error adding file (list)'.
        ENDIF.
    
        " Data
        DATA(item) = VALUE ty_tar_item(
          name    = name
          content = content ).
        INSERT item INTO TABLE tar_data.
        IF sy-subrc <> 0.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error adding file (data)'.
        ENDIF.
    
        result = me.
    
      ENDMETHOD.
    
      METHOD class_constructor.
    
        " Generate a char 256 null
        DATA x TYPE x LENGTH 4 VALUE '00000000'.
    
        FIELD-SYMBOLS  TYPE c.
    
        ASSIGN x TO  CASTING ##SUBRC_OK.
    
        null = .
        DO 8 TIMES.
          null = null && null.
        ENDDO.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        me->force_ustar = force_ustar.
    
      ENDMETHOD.
    
      METHOD delete.
    
        DELETE tar_files WHERE name = CONV string( name ).
        IF sy-subrc <> 0.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error deleting file (list)'.
        ENDIF.
    
        DELETE tar_data WHERE name = CONV string( name ).
        IF sy-subrc <> 0.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error deleting file (data)'.
        ENDIF.
    
        result = me.
    
      ENDMETHOD.
    
      METHOD file_count.
    
        LOOP AT tar_files TRANSPORTING NO FIELDS WHERE typeflag = c_typeflag-file.
          result = result + 1.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD get.
    
        READ TABLE tar_data ASSIGNING FIELD-SYMBOL() WITH TABLE KEY name = name.
        IF sy-subrc = 0.
          result = -content.
        ELSE.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error getting file'.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD gunzip.
    
        cl_abap_gzip=>decompress_binary_with_header(
          EXPORTING
            gzip_in = gzip
          IMPORTING
            raw_out = result ).
    
      ENDMETHOD.
    
      METHOD gzip.
    
        cl_abap_gzip=>compress_binary_with_header(
          EXPORTING
            raw_in   = tar
          IMPORTING
            gzip_out = result ).
    
      ENDMETHOD.
    
      METHOD list.
    
        result = tar_files.
    
      ENDMETHOD.
    
      METHOD load.
    
        DATA(size) = xstrlen( tar ).
    
        IF size = 0 OR size MOD c_blocksize <> 0.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error loading file (blocksize)'.
        ENDIF.
    
        CLEAR tar_files.
    
        DATA(offset) = 0.
        DO.
          IF offset + c_blocksize > size.
            EXIT.
          ENDIF.
    
          " Header block
          DATA(block) = tar+offset(c_blocksize).
          offset = offset + c_blocksize.
    
          DATA(header) = CONV ty_header( _from_xstring( block ) ).
    
          _remove_nulls( CHANGING data = header ).
    
          IF header IS INITIAL.
            CONTINUE.
          ENDIF.
    
          " Get extended header for keywords and filename
          CASE header-typeflag.
            WHEN c_typeflag-global_header.
              DATA(global) = lcl_pax=>decode_keywords( block ).
              CONTINUE.
            WHEN c_typeflag-extended_header.
              DATA(extended) = lcl_pax=>decode_keywords( block ).
              CONTINUE.
            WHEN c_typeflag-long_link.
              " Two blocks
              DATA(next_block) = tar+offset(c_blocksize).
              DATA(longlink) = lcl_7zip=>decode_longlink(
                block_1 = block
                block_2 = next_block ).
              offset = offset + c_blocksize.
              CONTINUE.
          ENDCASE.
    
          IF force_ustar = abap_true.
            IF header-magic <> c_ustar_magic.
              RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error loading file (ustar)'.
            ELSEIF header-version <> c_ustar_version AND header-version <> ` `.
              RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error loading file (version)'.
            ENDIF.
          ENDIF.
    
          DATA(file) = VALUE ty_file(
            name     = _to_filename( prefix = header-prefix name = header-name )
            size     = _unpad( header-size )
            mode     = _unpad( header-mode )
            unixtime = _unpad( header-mtime ) ).
    
          _from_unixtime(
            EXPORTING
              unixtime = file-unixtime
            IMPORTING
              date     = file-date
              time     = file-time ).
    
          IF header-typeflag IS INITIAL.
            file-typeflag = c_typeflag-file.
          ELSE.
            file-typeflag = header-typeflag.
          ENDIF.
    
          file-keywords = lcl_pax=>merge_keywords(
            global   = global
            extended = extended ).
    
          " Long filename
          IF longlink IS NOT INITIAL.
            " 7-zip
            file-name = longlink.
            CLEAR longlink.
          ELSE.
            " Pax
            READ TABLE file-keywords ASSIGNING FIELD-SYMBOL()
              WITH TABLE KEY keyword = 'path'.
            IF sy-subrc = 0.
              file-name = -value.
            ENDIF.
          ENDIF.
    
          INSERT file INTO TABLE tar_files.
    
          CLEAR extended.
    
          " Data blocks
          DATA(item)   = VALUE ty_tar_item( name = file-name ).
          DATA(length) = file-size.
          DATA(count)  = ( file-size - 1 ) DIV c_blocksize + 1.
    
          DO count TIMES.
            IF length > c_blocksize.
              block = tar+offset(c_blocksize).
            ELSE.
              block = tar+offset(length).
            ENDIF.
            CONCATENATE item-content block INTO item-content IN BYTE MODE.
            offset = offset + c_blocksize.
            length = length - c_blocksize.
          ENDDO.
    
          INSERT item INTO TABLE tar_data.
        ENDDO.
    
        result = me.
    
      ENDMETHOD.
    
      METHOD new.
    
        result = NEW #( force_ustar ).
    
      ENDMETHOD.
    
      METHOD save.
    
        " TODO?: Support other types
        LOOP AT tar_files ASSIGNING FIELD-SYMBOL()
          WHERE typeflag = c_typeflag-file OR typeflag = c_typeflag-directory.
    
          IF strlen( -name ) > 255.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error saving file (name)'.
          ELSEIF -name CA '\'.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error saving file (path)'.
          ENDIF.
    
          " Add extended header block for pax keywords
          IF -keywords IS NOT INITIAL.
            DATA(pax) = abap_true.
            DATA(keywords) = lcl_pax=>encode_keywords( -keywords ).
            CONCATENATE result keywords INTO result IN BYTE MODE.
            CONTINUE.
          ENDIF.
    
          " Header block
          DATA(header) = VALUE ty_header(
            mode     = _pad( number = -mode     length = 7 )
            uid      = ''
            gid      = ''
            size     = _pad( number = -size     length = 11 )
            mtime    = _pad( number = -unixtime length = 11 )
            typeflag = -typeflag
            magic    = c_ustar_magic
            version  = c_ustar_version
            uname    = to_lower( cl_abap_syst=>get_user_name( ) )
            gname    = ''
            linkname = ''
            devminor = ''
            devmajor = ''
            padding  = '' ).
    
          _from_filename(
            EXPORTING
              filename = -name
            IMPORTING
              prefix   = header-prefix
              name     = header-name ).
    
          _append_nulls( CHANGING data = header ).
    
          header-chksum = `        `. " 8 spaces
          header-chksum = _pad( number = _checksum( header ) length = 7 ) && null.
    
          DATA(block)  = CONV ty_block( _to_xstring( header ) ).
          CONCATENATE result block INTO result IN BYTE MODE.
    
          " Data blocks
          READ TABLE tar_data ASSIGNING FIELD-SYMBOL() WITH TABLE KEY name = -name.
          IF sy-subrc <> 0.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error saving file (data)'.
          ENDIF.
    
          DATA(offset) = 0.
          DATA(length) = -size.
          DATA(count)  = ( length - 1 ) DIV c_blocksize + 1.
    
          DO count TIMES.
            IF length > c_blocksize.
              block = -content+offset(c_blocksize).
            ELSE.
              block = -content+offset(length).
            ENDIF.
            CONCATENATE result block INTO result IN BYTE MODE.
            offset = offset + c_blocksize.
            length = length - c_blocksize.
          ENDDO.
    
        ENDLOOP.
    
        IF pax = abap_true.
          " Add two null blocks
          CLEAR block.
          CONCATENATE result block block INTO result IN BYTE MODE.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD unpacked_size.
    
        LOOP AT tar_files ASSIGNING FIELD-SYMBOL() WHERE typeflag = c_typeflag-file.
          result = result + -size.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD _append_nulls.
    
        DATA(count) = 0.
        DO.
          count = count + 1.
          ASSIGN COMPONENT count OF STRUCTURE data TO FIELD-SYMBOL().
          IF sy-subrc <> 0.
            EXIT.
          ENDIF.
           =  && null.
        ENDDO.
    
      ENDMETHOD.
    
      METHOD _checksum.
    
        DATA(xstring) = _to_xstring( data ).
        DATA(i) = 0.
    
        DO xstrlen( xstring ) TIMES.
          DATA(x) = xstring+i(1).
          result = result + x.
          i = i + 1.
        ENDDO.
    
      ENDMETHOD.
    
      METHOD _from_filename.
    
        DATA(temp_name) = filename.
        DO.
          IF strlen( temp_name ) <= 100.
            name = temp_name.
            EXIT.
          ENDIF.
    
          " Shorten name by moving part of path to prefix
          SPLIT temp_name AT c_path_sep INTO DATA(temp_prefix) temp_name.
          IF sy-subrc <> 0.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error file name too long'.
          ENDIF.
    
          IF prefix IS INITIAL.
            prefix = temp_prefix.
          ELSE.
            prefix = prefix && c_path_sep && temp_prefix.
          ENDIF.
        ENDDO.
    
      ENDMETHOD.
    
      METHOD _from_octal.
        result = lcl_tar_helpers=>from_octal( octal ).
      ENDMETHOD.
    
      METHOD _from_unixtime.
    
        TRY.
            DATA(timestamp) = cl_abap_tstmp=>add(
              tstmp = c_epoch
              secs  = unixtime ).
    
          CATCH cx_parameter_invalid_range
                cx_parameter_invalid_type.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error converting from UNIX time'.
        ENDTRY.
    
        CONVERT TIME STAMP timestamp TIME ZONE 'UTC' INTO DATE date TIME time.
    
      ENDMETHOD.
    
      METHOD _from_xstring.
        result = lcl_tar_helpers=>from_xstring( data ).
      ENDMETHOD.
    
      METHOD _pad.
    
        result = |{ _to_octal( number ) ALIGN = RIGHT PAD = '0' WIDTH = length }|.
    
      ENDMETHOD.
    
      METHOD _remove_nulls.
    
        DATA(count) = 0.
    
        DO.
          count = count + 1.
          ASSIGN COMPONENT count OF STRUCTURE data TO FIELD-SYMBOL().
          IF sy-subrc <> 0.
            EXIT.
          ENDIF.
          REPLACE ALL OCCURRENCES OF null(1) IN  WITH ''.
        ENDDO.
    
      ENDMETHOD.
    
      METHOD _to_filename.
    
        IF prefix IS INITIAL.
          result = name.
        ELSE.
          result = prefix && c_path_sep && name.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD _to_octal.
        result = lcl_tar_helpers=>to_octal( number ).
      ENDMETHOD.
    
      METHOD _to_unixtime.
    
        DATA timestamp TYPE timestamp.
    
        CONVERT DATE date TIME time INTO TIME STAMP timestamp TIME ZONE 'UTC'.
    
        TRY.
            result = cl_abap_tstmp=>subtract(
              tstmp1 = timestamp
              tstmp2 = c_epoch ).
    
          CATCH cx_parameter_invalid_range
                cx_parameter_invalid_type.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Error converting to UNIX time'.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD _to_xstring.
        result = lcl_tar_helpers=>to_xstring( data ).
      ENDMETHOD.
    
      METHOD _unpad.
    
        DATA(temp_data) = CONV string( data ).
    
        temp_data = replace(
          val  = temp_data
          sub  = ` `
          with = ''
          occ  = 0 ).
    
        result = _from_octal( condense( data ) ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_trace IMPLEMENTATION.
    
      METHOD cdata.
    
        GET PARAMETER ID 'ZAPM_TRACE' FIELD DATA(trace) ##EXISTS.
        GET PARAMETER ID 'ZAPM_TRACE_DIR' FIELD DATA(dir) ##EXISTS.
    
        CHECK trace = abap_true.
    
        GET TIME STAMP FIELD DATA(timestamp).
    
        TRY.
            /apmg/cl_apm_gui_factory=>get_frontend_services( )->file_download(
              iv_path = |{ dir }/apm-trace-{ timestamp }.log|
              iv_xstr = zcl_abapgit_convert=>string_to_xstring_utf8( cdata ) ).
          CATCH zcx_abapgit_exception /apmg/cx_apm_error.
            ASSERT 0 = 0.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD xdata.
    
        GET PARAMETER ID 'ZAPM_TRACE' FIELD DATA(trace) ##EXISTS.
        GET PARAMETER ID 'ZAPM_TRACE_DIR' FIELD DATA(dir) ##EXISTS.
    
        CHECK trace = abap_true.
    
        GET TIME STAMP FIELD DATA(timestamp).
    
        TRY.
            /apmg/cl_apm_gui_factory=>get_frontend_services( )->file_download(
              iv_path = |{ dir }/apm-trace-{ timestamp }.bin|
              iv_xstr = xdata ).
          CATCH zcx_abapgit_exception /apmg/cx_apm_error.
            ASSERT 0 = 0.
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_url IMPLEMENTATION.
    
      METHOD constructor.
        me->components = components.
      ENDMETHOD.
    
      METHOD default_port.
    
        CASE to_lower( scheme ).
          WHEN 'file'.
            result = ''.
          WHEN 'ftp'.
            result = '21'.
          WHEN 'http'.
            result = '80'.
          WHEN 'https'.
            result = '443'.
          WHEN 'ws'.
            result = '80'.
          WHEN 'wss'.
            result = '443'.
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD is_special_scheme.
    
        CASE to_lower( scheme ).
          WHEN 'file' OR 'ftp' OR 'http' OR 'https' OR 'ws' OR 'wss'.
            result = abap_true.
          WHEN OTHERS.
            result = abap_false.
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD normalize_path.
    
        DATA normalized_path TYPE string_table.
    
        CHECK path IS NOT INITIAL.
    
        DATA(len) = strlen( path ) - 1.
        SPLIT path AT '/' INTO TABLE DATA(path_segments).
    
        LOOP AT path_segments INTO DATA(segment).
          IF segment = '.' OR segment IS INITIAL.
            " Ignore '.' and empty segments
            CONTINUE.
          ELSEIF segment = '..' AND lines( normalized_path ) > 0.
            " Remove previous segment for '..'
            DELETE normalized_path INDEX lines( normalized_path ).
          ELSE.
            APPEND segment TO normalized_path.
          ENDIF.
        ENDLOOP.
    
        IF path+len(1) = '/'.
          APPEND '' TO normalized_path.
        ENDIF.
    
        " Reconstruct the normalized path
        LOOP AT normalized_path INTO segment.
          result = |{ result }/{ segment }|.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD parse.
    
        DATA components TYPE ty_url_components.
    
        IF url IS INITIAL.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'No URL'.
        ENDIF.
    
        " Remove leading/trailing spaces
        DATA(remaining) = condense( url ).
        DATA(authority) = ``.
    
        " Parse scheme
        DATA(delimiter) = find( val = remaining sub = ':' ).
        IF delimiter < 0.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid URL: no scheme found'.
        ENDIF.
    
        components-scheme = to_lower( remaining(delimiter) ).
    
        validate_scheme( components-scheme ).
        components-is_special = is_special_scheme( components-scheme ).
    
        " Remove scheme and ':' from remaining string
        delimiter = delimiter + 1.
        remaining = remaining+delimiter.
    
        " Check if URL has authority (starts with '//')
        IF strlen( remaining ) >= 2 AND remaining(2) = '//'.
          remaining = remaining+2.
    
          " Find end of authority
          delimiter = find( val = remaining sub = '/' ).
          IF delimiter < 0.
            authority = remaining.
            CLEAR remaining.
          ELSE.
            authority = remaining(delimiter).
            remaining = remaining+delimiter.
          ENDIF.
    
          " Split off fragment
          delimiter = find( val = authority sub = '#' ).
          IF delimiter >= 0.
            authority = authority(delimiter).
            remaining = authority+delimiter.
          ENDIF.
    
          " Parse authority section
          parse_authority(
            EXPORTING
              authority = authority
              scheme    = components-scheme
            IMPORTING
              username  = components-username
              password  = components-password
              host      = components-host
              port      = components-port ).
        ENDIF.
    
        " Find query and fragment positions
        DATA(query_pos) = find( val = remaining sub = '?' ).
        DATA(fragment_pos) = find( val = remaining sub = '#' ).
    
        " Set path first
        CASE 0.
          WHEN query_pos.
            " URL starts with ?
            components-path = ''.
            remaining = remaining+1.
    
            " Find fragment after query
            fragment_pos = find( val = remaining sub = '#' ).
            IF fragment_pos >= 0.
              components-query = remaining(fragment_pos).
              fragment_pos = fragment_pos + 1.
              components-fragment = remaining+fragment_pos.
            ELSE.
              components-query = remaining.
            ENDIF.
          WHEN fragment_pos.
            " URL starts with #
            components-path = ''.
            fragment_pos = fragment_pos + 1.
            components-fragment = remaining+1.
          WHEN OTHERS.
            " Normal case - extract path
            IF query_pos > 0 AND ( fragment_pos < 0 OR query_pos < fragment_pos ).
              " Path ends with ?
              components-path = remaining(query_pos).
              query_pos = query_pos + 1.
              IF fragment_pos > query_pos.
                DATA(query_len) = fragment_pos - query_pos.
                components-query = remaining+query_pos(query_len).
                fragment_pos = fragment_pos + 1.
                components-fragment = remaining+fragment_pos.
              ELSE.
                components-query = remaining+query_pos.
              ENDIF.
            ELSEIF fragment_pos > 0.
              " Path ends with #
              components-path = remaining(fragment_pos).
              fragment_pos = fragment_pos + 1.
              components-fragment = remaining+fragment_pos.
            ELSE.
              " Only path
              components-path = remaining.
            ENDIF.
        ENDCASE.
    
        components-path     = percent_decode( normalize_path( components-path ) ).
        components-query    = percent_decode( components-query ).
        components-fragment = percent_decode( components-fragment ).
    
        result = NEW /apmg/cl_apm_url( components ).
    
      ENDMETHOD.
    
      METHOD parse_authority.
    
        DATA(temp) = authority.
    
        " Parse username and password
        DATA(delimiter) = find( val = temp sub = '@' ).
        IF delimiter >= 0.
          DATA(credentials) = temp(delimiter).
          delimiter = delimiter + 1.
          temp = temp+delimiter.
    
          delimiter = find( val = credentials sub = ':' ).
          IF delimiter >= 0.
            username = percent_decode( |{ credentials(delimiter) }| ).
            delimiter = delimiter + 1.
            password = percent_decode( |{ credentials+delimiter }| ).
          ELSE.
            username = percent_decode( credentials ).
          ENDIF.
        ENDIF.
    
        " Parse host and port
        " First check if we have an IPv6 address
        IF temp IS NOT INITIAL AND temp(1) = '['.
          " Find the closing bracket
          delimiter = find( val = temp sub = ']' ).
          IF delimiter < 0.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid IPv6 address: missing closing bracket'.
          ENDIF.
    
          " Extract IPv6 address without brackets
          DATA(host_len) = delimiter - 1.
          host = temp+1(host_len).
    
          " Check if there's a port after the IPv6 address
          delimiter = delimiter + 1.
          IF strlen( temp ) > delimiter AND temp+delimiter(1) = ':'.
            delimiter = delimiter + 1.
            port = temp+delimiter.
          ENDIF.
        ELSE.
          " Regular hostname or IPv4
          delimiter = find( val = temp sub = ':' ).
          IF delimiter >= 0.
            host = temp(delimiter).
            delimiter = delimiter + 1.
            port = temp+delimiter.
          ELSE.
            host = temp.
          ENDIF.
    
          " TODO: punycode
        ENDIF.
    
        " Validate port if present
        IF port IS NOT INITIAL.
          IF NOT matches( val = port regex = '^\d+$' ) ##REGEX_POSIX.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid port number'.
          ENDIF.
          IF port NOT BETWEEN 0 AND 65535.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Port number out of range'.
          ENDIF.
        ENDIF.
    
        " Validate host
        IF is_special_scheme( scheme ).
          IF host IS INITIAL.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Missing host'.
          ENDIF.
        ELSE.
          IF host CA | \n\t\r#/:<>?@[\\]^\||.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Host contain invalid code point'.
          ENDIF.
        ENDIF.
    
        " Validate IPv4 or IPv6 address if present
        IF temp IS NOT INITIAL AND temp(1) = '['.
          validate_ipv6_address( host ).
        ELSEIF host CO '0123456789. '.
          validate_ipv4_address( host ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD percent_decode.
    
        result = cl_http_utility=>unescape_url( |{ raw }| ).
    
        IF raw IS NOT INITIAL AND result IS INITIAL.
          result = raw.
          RETURN.
        ENDIF.
    
        " Replace "hash"
        result = replace(
          val  = result
          sub  = '%23'
          with = '#'
          occ  = 0 ).
    
        " Escape "tick"
        result = replace(
          val  = result
          sub  = |'|
          with = '%27'
          occ  = 0 ).
    
        " Preserve "plus"
        DATA(idx) = 0.
        DO strlen( raw ) TIMES.
          IF raw+idx(1) = '+'.
            DATA(idx2) = idx + 1.
            result = |{ result(idx) }+{ result+idx2(*) }|.
          ENDIF.
          idx = idx + 1.
        ENDDO.
    
      ENDMETHOD.
    
      METHOD percent_encode.
    
        result = escape( val = |{ raw }| format = cl_abap_format=>e_url ).
    
        " Unescape "tick"
        result = replace(
          val  = result
          sub  = '%2527'
          with = '%27'
          occ  = 0 ).
    
      ENDMETHOD.
    
      METHOD serialize.
    
        DATA(url) = |{ components-scheme }:|.
    
        " Add authority if host is present
        IF components-host IS NOT INITIAL OR components-scheme = 'file'.
          url = |{ url }//|.
    
          " Add credentials if present
          IF components-username IS NOT INITIAL.
            url = |{ url }{ percent_encode( components-username ) }|.
          ENDIF.
          IF components-password IS NOT INITIAL.
            url = |{ url }:{ percent_encode( components-password ) }|.
          ENDIF.
          IF components-username IS NOT INITIAL OR components-password IS NOT INITIAL.
            url = |{ url }@|.
          ENDIF.
    
          " Add host and port
          url = |{ url }{ components-host }|.
          IF components-port IS NOT INITIAL.
            url = |{ url }:{ components-port }|.
          ENDIF.
        ENDIF.
    
        " Add path
        IF components-path IS NOT INITIAL.
          IF components-path(1) <> '/'.
            url = |{ url }/|.
          ENDIF.
          url = |{ url }{ percent_encode( components-path ) }|.
        ENDIF.
    
        " Add query
        IF components-query IS NOT INITIAL.
          url = |{ url }?{ percent_encode( components-query ) }|.
        ENDIF.
    
        " Add fragment
        IF components-fragment IS NOT INITIAL.
          url = |{ url }#{ percent_encode( components-fragment ) }|.
        ENDIF.
    
        result = url.
    
      ENDMETHOD.
    
      METHOD validate_ipv4_address.
    
        IF address IS NOT INITIAL AND address(1) = '.'.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid IPv4 address: initial segment is empty'.
        ENDIF.
    
        DATA(len) = strlen( address ) - 1.
        IF len >= 0 AND address+len(1) = '.'.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid IPv4 address: last segment is empty'.
        ENDIF.
    
        " Split by period
        SPLIT address AT '.' INTO TABLE DATA(parts).
    
        " Basic validation of IPv4 format
        IF lines( parts ) <> 4.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid IPv4 address: not four segments'.
        ENDIF.
    
        " Check each part
        LOOP AT parts INTO DATA(part).
          IF NOT matches( val = part regex = '^\d+$' ) ##REGEX_POSIX.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid IPv4 address: non-numeric segment'.
          ENDIF.
          IF part NOT BETWEEN 0 AND 255.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid IPv4 address: segment exceeds 255'.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD validate_ipv6_address.
    
        IF address(1) = ':'.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid IPv6 address: initial piece is empty'.
        ENDIF.
    
        DATA(len) = strlen( address ) - 1.
        IF len >= 0 AND address+len(1) = ':'.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid IPv6 address: last piece is empty'.
        ENDIF.
    
        " Split by colons
        SPLIT address AT ':' INTO TABLE DATA(parts).
    
        " Basic validation of IPv6 format
        IF lines( parts ) > 8.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid IPv6 address: too many pieces'.
        ENDIF.
    
        " Uncompressed addresses must have 8 parts
        IF address NS '::' AND lines( parts ) <> 8.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid IPv6 address: too few pieces'.
        ENDIF.
    
        " Check each part
        DATA(count) = 0.
        LOOP AT parts INTO DATA(part).
          " Empty part is allowed for :: notation, but only once
          IF part IS INITIAL.
            count = count + 1.
            IF count > 1.
              RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid IPv6 address: multiple empty pieces'.
            ENDIF.
            CONTINUE.
          ENDIF.
    
          " Validate hexadecimal format and length
          IF NOT matches( val = part regex = '^[0-9A-Fa-f]{1,4}$' ) ##REGEX_POSIX.
            RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid IPv6 address: invalid hexadecimal piece'.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD validate_scheme.
    
        IF NOT matches( val = scheme regex = '^[A-Za-z][-A-Za-z0-9+.]*' ) ##REGEX_POSIX.
          RAISE EXCEPTION TYPE /apmg/cx_apm_error_text EXPORTING text = 'Invalid scheme'.
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS /apmg/cl_apm_url_params IMPLEMENTATION.
    
      METHOD append.
        APPEND INITIAL LINE TO params ASSIGNING FIELD-SYMBOL().
        -key   = key.
        -value = value.
      ENDMETHOD.
    
      METHOD constructor.
        me->params = params.
      ENDMETHOD.
    
      METHOD create.
        result = NEW #( params = params ).
      ENDMETHOD.
    
      METHOD delete.
        IF value IS SUPPLIED.
          DELETE params WHERE key = key AND value = value.
        ELSE.
          DELETE params WHERE key = key.
        ENDIF.
      ENDMETHOD.
    
      METHOD get.
        READ TABLE params WITH KEY key = key ASSIGNING FIELD-SYMBOL().
        IF sy-subrc = 0.
          result = -value.
        ENDIF.
      ENDMETHOD.
    
      METHOD get_all.
        LOOP AT params ASSIGNING FIELD-SYMBOL() WHERE key = key.
          INSERT  INTO TABLE result.
        ENDLOOP.
      ENDMETHOD.
    
      METHOD has.
        IF value IS SUPPLIED.
          READ TABLE params WITH KEY key = key value = value TRANSPORTING NO FIELDS ##SUBRC_OK.
        ELSE.
          READ TABLE params WITH KEY key = key TRANSPORTING NO FIELDS.
        ENDIF.
        result = xsdbool( sy-subrc = 0 ).
      ENDMETHOD.
    
      METHOD parse.
    
        DATA:
          param  TYPE ty_param,
          params TYPE ty_params.
    
        SPLIT query AT '&' INTO TABLE DATA(key_values).
    
        LOOP AT key_values ASSIGNING FIELD-SYMBOL().
          CLEAR param.
    
          param-key = substring_before(
            val = 
            sub = '=' ).
    
          param-value = substring_after(
            val = 
            sub = '=' ).
    
          INSERT param INTO TABLE params.
        ENDLOOP.
    
        result = NEW #( params = params ).
    
      ENDMETHOD.
    
      METHOD set.
        delete( key ).
        append( key = key value = value ).
      ENDMETHOD.
    
      METHOD sort.
        SORT params.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_abap_language_vers IMPLEMENTATION.
    
      METHOD check_abap_language_version.
    
        " Check if ABAP language version matches repository setting
        IF is_item-abap_language_version IS NOT INITIAL AND iv_abap_language_version <> is_item-abap_language_version.
          zcx_abapgit_exception=>raise(
            |Object { is_item-obj_type } { is_item-obj_name } has { get_description( iv_abap_language_version ) }| &&
            | but repository is set to { get_description( is_item-abap_language_version ) }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        mo_dot_abapgit = io_dot_abapgit.
    
        IF zcl_abapgit_feature=>is_enabled( c_feature_flag ) = abap_false.
          mv_has_abap_language_vers = abap_undefined.
        ELSEIF get_abap_language_vers_by_repo( ) = zif_abapgit_dot_abapgit=>c_abap_language_version-undefined.
          mv_has_abap_language_vers = abap_undefined.
        ELSEIF get_abap_language_vers_by_repo( ) = zif_abapgit_dot_abapgit=>c_abap_language_version-ignore.
          mv_has_abap_language_vers = abap_false.
        ELSE.
          mv_has_abap_language_vers = abap_true.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_abap_language_vers_by_devc.
    
        DATA lv_class TYPE string.
        DATA lv_abap_lang_version_devc TYPE string.
        DATA lo_abap_language_version_cfg TYPE REF TO object.
    
        lv_class = 'CL_ABAP_LANGUAGE_VERSION_CFG'.
    
        TRY.
            CALL METHOD (lv_class)=>('GET_INSTANCE')
              RECEIVING
                ro_instance = lo_abap_language_version_cfg.
    
            " For non-existing packages, GET_PACKAGE_DEFAULT_VERSION returns "standard"
            " but we want to return "undefined" in this case to allow any new packages
            IF zcl_abapgit_factory=>get_sap_package( iv_package )->exists( ) = abap_true.
              CALL METHOD lo_abap_language_version_cfg->('IF_ABAP_LANGUAGE_VERSION_CFG~GET_PACKAGE_DEFAULT_VERSION')
                EXPORTING
                  iv_package_name             = iv_package
                RECEIVING
                  rv_default_language_version = lv_abap_lang_version_devc.
            ELSE.
              lv_abap_lang_version_devc = '-'.
            ENDIF.
    
            CASE lv_abap_lang_version_devc.
              WHEN zif_abapgit_aff_types_v1=>co_abap_language_version-standard.
                rv_abap_language_version = zif_abapgit_dot_abapgit=>c_abap_language_version-standard.
              WHEN zif_abapgit_aff_types_v1=>co_abap_language_version-key_user.
                rv_abap_language_version = zif_abapgit_dot_abapgit=>c_abap_language_version-key_user.
              WHEN zif_abapgit_aff_types_v1=>co_abap_language_version-cloud_development.
                rv_abap_language_version = zif_abapgit_dot_abapgit=>c_abap_language_version-cloud_development.
              WHEN OTHERS.
                rv_abap_language_version = zif_abapgit_dot_abapgit=>c_abap_language_version-undefined.
            ENDCASE.
    
          CATCH cx_root.
            rv_abap_language_version = zif_abapgit_dot_abapgit=>c_abap_language_version-undefined.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get_abap_language_vers_by_objt.
    
        DATA lv_class TYPE string.
        DATA lo_abap_language_version TYPE REF TO object.
    
        IF mv_has_abap_language_vers = abap_undefined.
          rv_allowed_abap_langu_version = c_any_abap_language_version.
        ELSEIF mv_has_abap_language_vers = abap_false.
          rv_allowed_abap_langu_version = c_no_abap_language_version.
        ELSE. " abap_true
    
          lv_class = 'CL_ABAP_LANGUAGE_VERSION'.
    
          TRY.
              CALL METHOD (lv_class)=>('GET_INSTANCE')
                RECEIVING
                  ro_version_handler = lo_abap_language_version.
    
              CALL METHOD lo_abap_language_version->('IF_ABAP_LANGUAGE_VERSION~GET_DEFAULT_VERSION')
                EXPORTING
                  iv_object_type     = iv_object_type
                  iv_package         = iv_package
                RECEIVING
                  rv_default_version = rv_allowed_abap_langu_version.
    
            CATCH cx_root.
              rv_allowed_abap_langu_version = get_default_abap_language_vers( iv_object_type ).
          ENDTRY.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_abap_language_vers_by_repo.
        rv_abap_language_version = mo_dot_abapgit->get_abap_language_version( ).
        IF rv_abap_language_version IS INITIAL.
          rv_abap_language_version = zif_abapgit_dot_abapgit=>c_abap_language_version-undefined.
        ENDIF.
      ENDMETHOD.
    
      METHOD get_default_abap_language_vers.
    
        IF zcl_abapgit_factory=>get_environment( )->is_sap_cloud_platform( ) = abap_true.
          " On BTP, default to ABAP for Cloud Development
          rv_abap_language_version = zif_abapgit_aff_types_v1=>co_abap_language_version_cloud-cloud_development.
        ELSE.
          " Differentiate between source code object and non-source code objects
          CASE iv_object_type.
            WHEN 'BDEF' OR 'CLAS' OR 'FUGR' OR 'FUGS' OR 'INTF' OR 'PROG' OR 'TYPE'.
              rv_abap_language_version = zif_abapgit_aff_types_v1=>co_abap_language_version_src-standard.
            WHEN OTHERS.
              rv_abap_language_version = zif_abapgit_aff_types_v1=>co_abap_language_version-standard.
          ENDCASE.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_description.
    
        CASE iv_abap_language_version.
          WHEN zif_abapgit_aff_types_v1=>co_abap_language_version-standard
            OR zif_abapgit_aff_types_v1=>co_abap_language_version_src-standard.
            rv_description = 'Standard ABAP'.
          WHEN zif_abapgit_aff_types_v1=>co_abap_language_version-key_user
            OR zif_abapgit_aff_types_v1=>co_abap_language_version_src-key_user.
            rv_description = 'ABAP for Key Users'.
          WHEN zif_abapgit_aff_types_v1=>co_abap_language_version-cloud_development
            OR zif_abapgit_aff_types_v1=>co_abap_language_version_src-cloud_development.
            rv_description = 'ABAP for Cloud Development'.
          WHEN OTHERS.
            rv_description = 'Undefined'.
        ENDCASE.
    
        rv_description = |ABAP language version "{ rv_description }"|.
    
      ENDMETHOD.
    
      METHOD get_repo_abap_language_version.
    
        DATA lv_abap_language_version TYPE string.
    
        IF mv_has_abap_language_vers <> abap_undefined. " abap_true or abap_false
          lv_abap_language_version = mo_dot_abapgit->get_abap_language_version( ).
        ENDIF.
    
        CASE lv_abap_language_version.
          WHEN zif_abapgit_dot_abapgit=>c_abap_language_version-standard.
            rv_abap_language_version = zif_abapgit_aff_types_v1=>co_abap_language_version_src-standard.
          WHEN zif_abapgit_dot_abapgit=>c_abap_language_version-key_user.
            rv_abap_language_version = zif_abapgit_aff_types_v1=>co_abap_language_version_src-key_user.
          WHEN zif_abapgit_dot_abapgit=>c_abap_language_version-cloud_development.
            rv_abap_language_version = zif_abapgit_aff_types_v1=>co_abap_language_version_src-cloud_development.
          WHEN zif_abapgit_dot_abapgit=>c_abap_language_version-ignore.
            rv_abap_language_version = c_no_abap_language_version.
          WHEN OTHERS. " undefined or feature off
            rv_abap_language_version = c_any_abap_language_version.
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD is_import_allowed.
    
        DATA lv_package_version TYPE string.
    
        lv_package_version = get_abap_language_vers_by_devc( iv_package ).
    
        CASE get_abap_language_vers_by_repo( ).
          WHEN zif_abapgit_dot_abapgit=>c_abap_language_version-undefined
            OR zif_abapgit_dot_abapgit=>c_abap_language_version-ignore.
            rv_allowed = abap_true.
          WHEN OTHERS.
            IF get_abap_language_vers_by_repo( ) = lv_package_version.
              " allow packages that match repo setting
              rv_allowed = abap_true.
            ELSEIF lv_package_version = zif_abapgit_dot_abapgit=>c_abap_language_version-undefined.
              " always allow new packages
              rv_allowed = abap_true.
            ELSE.
              rv_allowed = abap_false.
            ENDIF.
        ENDCASE.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_adt_link IMPLEMENTATION.
    
      METHOD link_transport.
    * call to CL_CTS_ADT_TM_URI_BUILDER=>CREATE_ADT_URI replaced with logic that works on all systems,
        rv_link = |adt://{ sy-sysid }/sap/bc/adt/cts/transportrequests/{ iv_transport }|.
      ENDMETHOD.
    
      METHOD generate.
    
        DATA: lv_adt_link       TYPE string.
        DATA: lo_adt_uri_mapper TYPE REF TO object.
        DATA: lo_adt_objref     TYPE REF TO object.
        DATA: lo_adt_sub_objref TYPE REF TO object.
        DATA: lv_program        TYPE progname.
        DATA: lv_include        TYPE progname.
    
        FIELD-SYMBOLS:  TYPE string.
    
        get_adt_objects_and_names(
          EXPORTING
            iv_obj_name       = iv_obj_name
            iv_obj_type       = iv_obj_type
          IMPORTING
            eo_adt_uri_mapper = lo_adt_uri_mapper
            eo_adt_objectref  = lo_adt_objref
            ev_program        = lv_program
            ev_include        = lv_include ).
    
        TRY.
            IF iv_sub_obj_name IS NOT INITIAL.
    
              IF ( lv_program <> iv_obj_name AND lv_include IS INITIAL ) OR
                 ( lv_program = lv_include AND iv_sub_obj_name IS NOT INITIAL ).
                lv_include = iv_sub_obj_name.
              ENDIF.
    
              CALL METHOD lo_adt_uri_mapper->('IF_ADT_URI_MAPPER~MAP_INCLUDE_TO_OBJREF')
                EXPORTING
                  program     = lv_program
                  include     = lv_include
                  line        = iv_line_number
                  line_offset = 0
                  end_line    = iv_line_number
                  end_offset  = 1
                RECEIVING
                  result      = lo_adt_sub_objref.
              IF lo_adt_sub_objref IS NOT INITIAL.
                lo_adt_objref = lo_adt_sub_objref.
              ENDIF.
    
            ENDIF.
    
            ASSIGN ('LO_ADT_OBJREF->REF_DATA-URI') TO .
            ASSERT sy-subrc = 0.
    
            CONCATENATE 'adt://' sy-sysid  INTO lv_adt_link.
    
            rv_result = lv_adt_link.
    
          CATCH cx_root.
            zcx_abapgit_exception=>raise( 'ADT Jump Error' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get_adt_objects_and_names.
    
        DATA lv_obj_type       TYPE trobjtype.
        DATA lv_obj_name       TYPE trobj_name.
        DATA lo_object         TYPE REF TO cl_wb_object.
        DATA lo_adt            TYPE REF TO object.
    
        FIELD-SYMBOLS  TYPE string.
    
        lv_obj_name = iv_obj_name.
        lv_obj_type = iv_obj_type.
    
        TRY.
            cl_wb_object=>create_from_transport_key(
              EXPORTING
                p_object    = lv_obj_type
                p_obj_name  = lv_obj_name
              RECEIVING
                p_wb_object = lo_object
              EXCEPTIONS
                OTHERS      = 1 ).
            IF sy-subrc <> 0.
              zcx_abapgit_exception=>raise( 'ADT Jump Error' ).
            ENDIF.
    
            CALL METHOD ('CL_ADT_TOOLS_CORE_FACTORY')=>('GET_INSTANCE')
              RECEIVING
                result = lo_adt.
    
            IF is_adt_jump_possible( io_object = lo_object
                                     io_adt    = lo_adt ) = abap_false.
              zcx_abapgit_exception=>raise( 'ADT Jump Error' ).
            ENDIF.
    
            CALL METHOD lo_adt->('IF_ADT_TOOLS_CORE_FACTORY~GET_URI_MAPPER')
              RECEIVING
                result = eo_adt_uri_mapper.
    
            CALL METHOD eo_adt_uri_mapper->('IF_ADT_URI_MAPPER~MAP_WB_OBJECT_TO_OBJREF')
              EXPORTING
                wb_object = lo_object
              RECEIVING
                result    = eo_adt_objectref.
    
            ASSIGN ('EO_ADT_OBJECTREF->REF_DATA-URI') TO .
            ASSERT sy-subrc = 0.
    
            CALL METHOD eo_adt_uri_mapper->('IF_ADT_URI_MAPPER~MAP_OBJREF_TO_INCLUDE')
              EXPORTING
                uri     = 
              IMPORTING
                program = ev_program
                include = ev_include.
    
          CATCH cx_root.
            zcx_abapgit_exception=>raise( 'ADT Jump Error' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD is_adt_jump_possible.
    
        DATA: lo_wb_request         TYPE REF TO cl_wb_request,
              lo_adt_uri_mapper_vit TYPE REF TO object,
              lv_vit_wb_request     TYPE abap_bool.
    
        cl_wb_request=>create_from_object_ref(
          EXPORTING
            p_wb_object       = io_object
          RECEIVING
            p_wb_request      = lo_wb_request
          EXCEPTIONS
            illegal_operation = 1
            cancelled         = 2
            OTHERS            = 3 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'ADT Jump Error' ).
        ENDIF.
    
        TRY.
            CALL METHOD io_adt->('IF_ADT_TOOLS_CORE_FACTORY~GET_URI_MAPPER_VIT')
              RECEIVING
                result = lo_adt_uri_mapper_vit.
    
            CALL METHOD lo_adt_uri_mapper_vit->('IF_ADT_URI_MAPPER_VIT~IS_VIT_WB_REQUEST')
              EXPORTING
                wb_request = lo_wb_request
              RECEIVING
                result     = lv_vit_wb_request.
    
            rv_is_adt_jump_possible = boolc( NOT lv_vit_wb_request = abap_true ).
    
          CATCH cx_root.
            zcx_abapgit_exception=>raise( 'ADT Jump Error' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD jump.
    
        DATA lv_adt_link TYPE string.
    
        TRY.
            lv_adt_link = generate(
              iv_obj_name     = iv_obj_name
              iv_obj_type     = iv_obj_type
              iv_sub_obj_name = iv_sub_obj_name
              iv_line_number  = iv_line_number ).
    
            /apmg/cl_apm_gui_factory=>get_frontend_services( )->execute( iv_document = lv_adt_link ).
    
          CATCH cx_root.
            zcx_abapgit_exception=>raise( 'ADT Jump Error' ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_AFF_REGISTRY IMPLEMENTATION.
    
      METHOD constructor.
        mv_aff_enabled = zcl_abapgit_feature=>is_enabled( c_aff_feature ).
      ENDMETHOD.
    
      METHOD initialize_registry_table.
        register( 'APLO' ).
        register( 'BGQC' ).
        register( 'CDBO' ).
        register( 'CHKC' ).
        register( 'CHKO' ).
        register( 'CHKV' ).
        register( 'COTA' ).
        register( 'DESD' ).
        register( 'DRTY' ).
        register( 'DTEB' ).
        register( 'DSFI' ).
        register( 'DRAS' ).
        register( 'DSFD' ).
        register( 'EVTB' ).
        register( 'EEEC' ).
        register( 'GSMP' ).
        register( iv_obj_type     = 'INTF'
                  iv_experimental = abap_true ).
        register( 'SAJT' ).
        register( 'SAJC' ).
        register( 'SMBC' ).
        register( 'SWCR' ).
        register( 'NONT' ).
        register( 'RONT' ).
        register( 'UIAD' ).
        register( 'UIPG' ).
        register( 'UIST' ).
      ENDMETHOD.
    
      METHOD register.
        DATA ls_registry_entry TYPE ty_registry_entry.
    
        ls_registry_entry-obj_type = iv_obj_type.
        ls_registry_entry-experimental = iv_experimental.
        INSERT ls_registry_entry INTO TABLE gt_registry.
      ENDMETHOD.
    
      METHOD zif_abapgit_aff_registry~is_supported_object_type.
    
        DATA ls_registry_entry TYPE ty_registry_entry.
    
        IF gt_registry IS INITIAL.
          initialize_registry_table( ).
        ENDIF.
    
        READ TABLE gt_registry WITH TABLE KEY obj_type = iv_obj_type INTO ls_registry_entry.
        IF sy-subrc = 0 AND ls_registry_entry-experimental = abap_false.
          rv_result = abap_true.
        ELSEIF sy-subrc = 0 AND mv_aff_enabled = abap_true.
          rv_result = abap_true.
        ELSE.
          rv_result = abap_false.
        ENDIF.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_aff_factory IMPLEMENTATION.
    
      METHOD get_registry.
        IF gi_registry IS NOT BOUND.
          CREATE OBJECT gi_registry TYPE zcl_abapgit_aff_registry.
        ENDIF.
        ri_registry = gi_registry.
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_aff_injector IMPLEMENTATION.
    
      METHOD set_registry.
        zcl_abapgit_aff_factory=>gi_registry = ii_registry.
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS lcl_in DEFINITION.
      PUBLIC SECTION.
        CLASS-METHODS convert
          IMPORTING
            !iv_data         TYPE xsequence
            !iv_length       TYPE i OPTIONAL
          RETURNING
            VALUE(rv_string) TYPE string
          RAISING
            zcx_abapgit_exception.
      PRIVATE SECTION.
        CLASS-DATA go_conv_new TYPE REF TO object.
        CLASS-DATA go_conv_old TYPE REF TO object.
    ENDCLASS.
    
    CLASS lcl_in IMPLEMENTATION.
      METHOD convert.
    
        DATA lv_class TYPE string.
        DATA lx_error TYPE REF TO cx_root.
        DATA lv_ignore_cerr TYPE abap_bool.
    
        IF go_conv_new IS INITIAL AND go_conv_old IS INITIAL.
          TRY.
              CALL METHOD ('CL_ABAP_CONV_CODEPAGE')=>create_in
                RECEIVING
                  instance = go_conv_new.
            CATCH cx_sy_dyn_call_illegal_class.
    * ignore conversion errors on non-unicode systems
              lv_ignore_cerr = boolc( cl_abap_char_utilities=>charsize = 1 ).
    
              lv_class = 'CL_ABAP_CONV_IN_CE'.
              CALL METHOD (lv_class)=>create
                EXPORTING
                  encoding    = 'UTF-8'
                  ignore_cerr = lv_ignore_cerr
                RECEIVING
                  conv        = go_conv_old.
          ENDTRY.
        ENDIF.
    
        TRY.
            IF go_conv_new IS NOT INITIAL.
              CALL METHOD go_conv_new->('IF_ABAP_CONV_IN~CONVERT')
                EXPORTING
                  source = iv_data
                RECEIVING
                  result = rv_string.
            ELSE.
              CALL METHOD go_conv_old->('CONVERT')
                EXPORTING
                  input = iv_data
                  n     = iv_length
                IMPORTING
                  data  = rv_string.
            ENDIF.
          CATCH cx_parameter_invalid_range
            cx_sy_codepage_converter_init
            cx_sy_conversion_codepage
            cx_parameter_invalid_type INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS LCL_OUT_ DEFINITION.
      PUBLIC SECTION.
        CLASS-METHODS convert
          IMPORTING
            !iv_string        TYPE string
          RETURNING
            VALUE(rv_xstring) TYPE xstring
          RAISING
            zcx_abapgit_exception.
      PRIVATE SECTION.
        CLASS-DATA go_conv_new TYPE REF TO object.
        CLASS-DATA go_conv_old TYPE REF TO object.
    ENDCLASS.
    
    CLASS LCL_OUT_ IMPLEMENTATION.
      METHOD convert.
        DATA lx_error TYPE REF TO cx_root.
        DATA lv_class TYPE string.
    
        IF go_conv_new IS INITIAL AND go_conv_old IS INITIAL.
          TRY.
              CALL METHOD ('CL_ABAP_CONV_CODEPAGE')=>create_out
                RECEIVING
                  instance = go_conv_new.
            CATCH cx_sy_dyn_call_illegal_class.
              lv_class = 'CL_ABAP_CONV_OUT_CE'.
              CALL METHOD (lv_class)=>create
                EXPORTING
                  encoding = 'UTF-8'
                RECEIVING
                  conv     = go_conv_old.
          ENDTRY.
        ENDIF.
    
        TRY.
            IF go_conv_new IS NOT INITIAL.
              CALL METHOD go_conv_new->('IF_ABAP_CONV_OUT~CONVERT')
                EXPORTING
                  source = iv_string
                RECEIVING
                  result = rv_xstring.
            ELSE.
              CALL METHOD go_conv_old->('CONVERT')
                EXPORTING
                  data   = iv_string
                IMPORTING
                  buffer = rv_xstring.
            ENDIF.
          CATCH cx_parameter_invalid_range
                cx_sy_codepage_converter_init
                cx_sy_conversion_codepage
                cx_parameter_invalid_type INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS lcl_bcp47_language_table DEFINITION CREATE PRIVATE.
    
      PUBLIC SECTION.
        TYPES: BEGIN OF ty_language_mapping,
                 sap1_code  TYPE sy-langu,
                 sap2_code  TYPE laiso,
                 bcp47_code TYPE string,
                 text       TYPE string,
               END OF ty_language_mapping,
               ty_language_mappings TYPE STANDARD TABLE OF ty_language_mapping WITH DEFAULT KEY.
        CLASS-DATA gt_language_mappings TYPE ty_language_mappings.
        CLASS-METHODS:
          sap1_to_text
            IMPORTING
              im_sap1        TYPE sy-langu
            RETURNING
              VALUE(re_text) TYPE string,
          sap1_to_sap2
            IMPORTING
              im_sap1        TYPE sy-langu
            RETURNING
              VALUE(re_sap2) TYPE laiso
            RAISING
              zcx_abapgit_exception,
          sap2_to_sap1
            IMPORTING
              im_sap2        TYPE laiso
            RETURNING
              VALUE(re_sap1) TYPE sy-langu
            RAISING
              zcx_abapgit_exception,
          sap1_to_bcp47
            IMPORTING
              im_sap1         TYPE sy-langu
            RETURNING
              VALUE(re_bcp47) TYPE string
            RAISING
              zcx_abapgit_exception,
          bcp47_to_sap1
            IMPORTING
              im_bcp47       TYPE string
            RETURNING
              VALUE(re_sap1) TYPE sy-langu
            RAISING
              zcx_abapgit_exception.
      PROTECTED SECTION.
      PRIVATE SECTION.
        CLASS-METHODS fill_language_mappings.
        CLASS-METHODS
          fill_language_mapping
            IMPORTING
              im_sap1  TYPE string
              im_sap2  TYPE string
              im_bcp47 TYPE string
              iv_text  TYPE string OPTIONAL.
    
    ENDCLASS.
    
    CLASS lcl_bcp47_language_table IMPLEMENTATION.
    
      METHOD sap1_to_bcp47.
        DATA lv_language_mapping TYPE ty_language_mapping.
    
        IF gt_language_mappings IS INITIAL OR lines( gt_language_mappings ) = 0.
          fill_language_mappings( ).
        ENDIF.
    
        LOOP AT gt_language_mappings INTO lv_language_mapping WHERE sap1_code = im_sap1.
          IF re_bcp47 IS INITIAL OR strlen( re_bcp47 ) > strlen( lv_language_mapping-bcp47_code ).
            re_bcp47 = lv_language_mapping-bcp47_code.
          ENDIF.
        ENDLOOP.
    
        IF re_bcp47 IS INITIAL.
          zcx_abapgit_exception=>raise( |Could not map SAP1 language code { im_sap1 } to BCP47 language code.| ).
        ENDIF.
      ENDMETHOD.
    
      METHOD bcp47_to_sap1.
        DATA lv_language_mapping TYPE ty_language_mapping.
    
        IF gt_language_mappings IS INITIAL OR lines( gt_language_mappings ) = 0.
          fill_language_mappings( ).
        ENDIF.
    
        LOOP AT gt_language_mappings INTO lv_language_mapping.
          IF to_lower( lv_language_mapping-bcp47_code ) = to_lower( im_bcp47 ) AND re_sap1 IS INITIAL.
            re_sap1 = lv_language_mapping-sap1_code.
          ENDIF.
        ENDLOOP.
    
        IF re_sap1 IS INITIAL.
          zcx_abapgit_exception=>raise( |Could not map BCP47 language code { im_bcp47 } to SAP1 language code.| ).
        ENDIF.
      ENDMETHOD.
    
      METHOD sap1_to_text.
        DATA lv_language_mapping TYPE ty_language_mapping.
    
        IF gt_language_mappings IS INITIAL.
          fill_language_mappings( ).
        ENDIF.
    
        READ TABLE gt_language_mappings WITH KEY sap1_code = im_sap1 INTO lv_language_mapping.
        IF sy-subrc = 0.
          re_text = lv_language_mapping-text.
        ELSE.
          re_text = 'Unknonw language'.
        ENDIF.
      ENDMETHOD.
    
      METHOD sap1_to_sap2.
        DATA lv_language_mapping TYPE ty_language_mapping.
    
        IF gt_language_mappings IS INITIAL.
          fill_language_mappings( ).
        ENDIF.
    
        READ TABLE gt_language_mappings WITH KEY sap1_code = im_sap1 INTO lv_language_mapping.
        re_sap2 = lv_language_mapping-sap2_code.
    
        IF re_sap2 IS INITIAL.
          zcx_abapgit_exception=>raise( |Could not map SAP1 language code { im_sap1 } to SAP2 language code.| ).
        ENDIF.
      ENDMETHOD.
    
      METHOD sap2_to_sap1.
        DATA lv_language_mapping TYPE ty_language_mapping.
    
        IF gt_language_mappings IS INITIAL.
          fill_language_mappings( ).
        ENDIF.
    
        READ TABLE gt_language_mappings WITH KEY sap2_code = im_sap2 INTO lv_language_mapping.
        re_sap1 = lv_language_mapping-sap1_code.
    
        IF re_sap1 IS INITIAL.
          zcx_abapgit_exception=>raise( |Could not map SAP2 language code { im_sap2 } to SAP1 language code.| ).
        ENDIF.
      ENDMETHOD.
    
      METHOD fill_language_mapping.
        DATA lv_line TYPE ty_language_mapping.
        DATA lv_sap1 TYPE sy-langu.
    
        IF strlen( im_sap1 ) = 4.
          TRY.
              lv_sap1 = zcl_abapgit_convert=>uccp( im_sap1 ).
            CATCH cx_root.
              " Language is not supported in this system -> ignore it
              " Should someone try to use the language in a repo, it will result in an error (see above)
              RETURN.
          ENDTRY.
        ELSEIF strlen( im_sap1 ) = 1.
          lv_sap1 = im_sap1.
        ENDIF.
    
        lv_line-bcp47_code = im_bcp47.
        lv_line-sap2_code = im_sap2.
        lv_line-sap1_code = lv_sap1.
        lv_line-text      = iv_text.
    
        APPEND lv_line TO gt_language_mappings.
        CLEAR lv_line.
      ENDMETHOD.
    
      METHOD fill_language_mappings.
        fill_language_mapping( im_sap1  = 'a'
                               im_sap2  = 'AF'
                               im_bcp47 = 'af'
                               iv_text  = 'Afrikaans' ).
    
        fill_language_mapping( im_sap1  = 'BF51'
                               im_sap2  = 'SQ'
                               im_bcp47 = 'sq'
                               iv_text  = 'Albanian' ).
    
        fill_language_mapping( im_sap1  = 'A'
                               im_sap2  = 'AR'
                               im_bcp47 = 'ar-SA'
                               iv_text  = 'Arabic' ).
    
        fill_language_mapping( im_sap1  = 'BA15'
                               im_sap2  = 'EU'
                               im_bcp47 = 'eu'
                               iv_text  = 'Basque' ).
    
        fill_language_mapping( im_sap1  = 'B8F3'
                               im_sap2  = 'BS'
                               im_bcp47 = 'bs'
                               iv_text  = 'Bosnian' ).
    
        fill_language_mapping( im_sap1  = 'W'
                               im_sap2  = 'BG'
                               im_bcp47 = 'bg'
                               iv_text  = 'Bulgarian' ).
    
        fill_language_mapping( im_sap1  = 'c'
                               im_sap2  = 'CA'
                               im_bcp47 = 'ca'
                               iv_text  = 'Catalan' ).
    
        fill_language_mapping( im_sap1  = '1'
                               im_sap2  = 'ZH'
                               im_bcp47 = 'zh'
                               iv_text  = 'Chinese' ).
    
        fill_language_mapping( im_sap1  = '1'
                               im_sap2  = 'ZH'
                               im_bcp47 = 'zh-Hans'
                               iv_text  = 'Chinese (Simplified)' ).
    
        fill_language_mapping( im_sap1  = 'B343'
                               im_sap2  = '3C'
                               im_bcp47 = 'zh-SG'
                               iv_text  = 'Chinese (Singapore)' ).
    
        fill_language_mapping( im_sap1  = 'M'
                               im_sap2  = 'ZF'
                               im_bcp47 = 'zh-Hant'
                               iv_text  = 'Chinese (Traditional)' ).
    
        fill_language_mapping( im_sap1  = '6'
                               im_sap2  = 'HR'
                               im_bcp47 = 'hr'
                               iv_text  = 'Croatian' ).
    
        fill_language_mapping( im_sap1  = 'C'
                               im_sap2  = 'CS'
                               im_bcp47 = 'cs'
                               iv_text  = 'Czech' ).
    
        fill_language_mapping( im_sap1  = 'K'
                               im_sap2  = 'DA'
                               im_bcp47 = 'da'
                               iv_text  = 'Danish' ).
    
        fill_language_mapping( im_sap1  = 'N'
                               im_sap2  = 'NL'
                               im_bcp47 = 'nl'
                               iv_text  = 'Dutch' ).
    
        fill_language_mapping( im_sap1  = 'N'
                               im_sap2  = 'NL'
                               im_bcp47 = 'nl-NL'
                               iv_text  = 'Dutch' ).
    
        fill_language_mapping( im_sap1  = 'B284'
                               im_sap2  = '1D'
                               im_bcp47 = 'nl-BE'
                               iv_text  = 'Dutch (Belgium)' ).
    
        fill_language_mapping( im_sap1  = 'E'
                               im_sap2  = 'EN'
                               im_bcp47 = 'en'
                               iv_text  = 'English' ).
    
        fill_language_mapping( im_sap1  = 'E'
                               im_sap2  = 'EN'
                               im_bcp47 = 'en-US'
                               iv_text  = 'English (American)' ).
    
        fill_language_mapping( im_sap1  = 'B46E'
                               im_sap2  = '6N'
                               im_bcp47 = 'en-GB'
                               iv_text  = 'English (British)' ).
    
        fill_language_mapping( im_sap1  = 'B285'
                               im_sap2  = '1E'
                               im_bcp47 = 'en-AU'
                               iv_text  = 'English (Australia)' ).
    
        fill_language_mapping( im_sap1  = 'B2E5'
                               im_sap2  = '2E'
                               im_bcp47 = 'en-BZ'
                               iv_text  = 'English (Belize)' ).
    
        fill_language_mapping( im_sap1  = 'B345'
                               im_sap2  = '3E'
                               im_bcp47 = 'en-CA'
                               iv_text  = 'English (Canada)' ).
    
        fill_language_mapping( im_sap1  = 'B405'
                               im_sap2  = '5E'
                               im_bcp47 = 'en-HK'
                               iv_text  = 'English (Hong Kong SAR China)' ).
    
        fill_language_mapping( im_sap1  = 'B465'
                               im_sap2  = '6E'
                               im_bcp47 = 'en-IN'
                               iv_text  = 'English (India)' ).
    
        fill_language_mapping( im_sap1  = 'B4C5'
                               im_sap2  = '7E'
                               im_bcp47 = 'en-ID'
                               iv_text  = 'English' ).
    
        fill_language_mapping( im_sap1  = 'B525'
                               im_sap2  = '8E'
                               im_bcp47 = 'en-IE'
                               iv_text  = 'English (Ireland)' ).
    
        fill_language_mapping( im_sap1  = 'B585'
                               im_sap2  = '9E'
                               im_bcp47 = 'en-JM'
                               iv_text  = 'English (Jamaica)' ).
    
        fill_language_mapping( im_sap1  = 'B225'
                               im_sap2  = '0E'
                               im_bcp47 = 'en-MY'
                               iv_text  = 'English (Malaysia)' ).
    
        fill_language_mapping( im_sap1  = 'B28E'
                               im_sap2  = '1N'
                               im_bcp47 = 'en-NZ'
                               iv_text  = 'English (New Zealand)' ).
    
        fill_language_mapping( im_sap1  = 'B2EE'
                               im_sap2  = '2N'
                               im_bcp47 = 'en-PH'
                               iv_text  = 'English (Philippines)' ).
    
        fill_language_mapping( im_sap1  = 'B34E'
                               im_sap2  = '3N'
                               im_bcp47 = 'en-SG'
                               iv_text  = 'English (Singapore)' ).
    
        fill_language_mapping( im_sap1  = 'B3AE'
                               im_sap2  = '4N'
                               im_bcp47 = 'en-ZA'
                               iv_text  = 'English (South Africa)' ).
    
        fill_language_mapping( im_sap1  = 'B40E'
                               im_sap2  = '5N'
                               im_bcp47 = 'en-TT'
                               iv_text  = 'English (Trinidad & Tobago)' ).
    
        fill_language_mapping( im_sap1  = 'B4CE'
                               im_sap2  = '7N'
                               im_bcp47 = 'en-ZW'
                               iv_text  = 'English (Zimbabwe)' ).
    
        fill_language_mapping( im_sap1  = '9'
                               im_sap2  = 'ET'
                               im_bcp47 = 'et'
                               iv_text  = 'Estonian' ).
    
        fill_language_mapping( im_sap1  = 'U'
                               im_sap2  = 'FI'
                               im_bcp47 = 'fi'
                               iv_text  = 'Finnish' ).
    
        fill_language_mapping( im_sap1  = 'F'
                               im_sap2  = 'FR'
                               im_bcp47 = 'fr'
                               iv_text  = 'French' ).
    
        fill_language_mapping( im_sap1  = 'F'
                               im_sap2  = 'FR'
                               im_bcp47 = 'fr-FR'
                               iv_text  = 'French' ).
    
        fill_language_mapping( im_sap1  = 'B286'
                               im_sap2  = '1F'
                               im_bcp47 = 'fr-BE'
                               iv_text  = 'French (Belgium)' ).
    
        fill_language_mapping( im_sap1  = 'B2E6'
                               im_sap2  = '2F'
                               im_bcp47 = 'fr-CM'
                               iv_text  = 'French (Cameroon)' ).
    
        fill_language_mapping( im_sap1  = 'B346'
                               im_sap2  = '3F'
                               im_bcp47 = 'fr-CA'
                               iv_text  = 'French (Canada)' ).
    
        fill_language_mapping( im_sap1  = 'B3A6'
                               im_sap2  = '4F'
                               im_bcp47 = 'fr-CG'
                               iv_text  = 'French (Congo-Brazzaville)' ).
    
        fill_language_mapping( im_sap1  = 'B406'
                               im_sap2  = '5F'
                               im_bcp47 = 'fr-CI'
                               iv_text  = 'French (Cote d''Ivoire)' ).
    
        fill_language_mapping( im_sap1  = 'B466'
                               im_sap2  = '6F'
                               im_bcp47 = 'fr-HT'
                               iv_text  = 'French (Haiti)' ).
    
        fill_language_mapping( im_sap1  = 'B4C6'
                               im_sap2  = '7F'
                               im_bcp47 = 'fr-LU'
                               iv_text  = 'French (Luxembourg)' ).
    
        fill_language_mapping( im_sap1  = 'B526'
                               im_sap2  = '8F'
                               im_bcp47 = 'fr-ML'
                               iv_text  = 'French (Mali)' ).
    
        fill_language_mapping( im_sap1  = 'B586'
                               im_sap2  = '9F'
                               im_bcp47 = 'fr-MC'
                               iv_text  = 'French (Monaco)' ).
    
        fill_language_mapping( im_sap1  = 'B288'
                               im_sap2  = '1H'
                               im_bcp47 = 'fr-MA'
                               iv_text  = 'French (Morocco)' ).
    
        fill_language_mapping( im_sap1  = 'B2E8'
                               im_sap2  = '2H'
                               im_bcp47 = 'fr-RE'
                               iv_text  = 'French (Reunion)' ).
    
        fill_language_mapping( im_sap1  = 'B348'
                               im_sap2  = '3H'
                               im_bcp47 = 'fr-SN'
                               iv_text  = 'French (Senegal)' ).
    
        fill_language_mapping( im_sap1  = 'B3A8'
                               im_sap2  = '4H'
                               im_bcp47 = 'fr-CH'
                               iv_text  = 'French (Switzerland)' ).
    
        fill_language_mapping( im_sap1  = 'BAC4'
                               im_sap2  = 'GD'
                               im_bcp47 = 'gd'
                               iv_text  = 'Scottish Gaelic' ).
    
        fill_language_mapping( im_sap1  = 'BACC'
                               im_sap2  = 'GL'
                               im_bcp47 = 'gl'
                               iv_text  = 'Galician' ).
    
        fill_language_mapping( im_sap1  = 'D'
                               im_sap2  = 'DE'
                               im_bcp47 = 'de'
                               iv_text  = 'German' ).
    
        fill_language_mapping( im_sap1  = 'D'
                               im_sap2  = 'DE'
                               im_bcp47 = 'de-DE'
                               iv_text  = 'German' ).
    
        fill_language_mapping( im_sap1  = 'B287'
                               im_sap2  = '1G'
                               im_bcp47 = 'de-AT'
                               iv_text  = 'German (Austria)' ).
    
        fill_language_mapping( im_sap1  = 'B2E7'
                               im_sap2  = '2G'
                               im_bcp47 = 'de-LI'
                               iv_text  = 'German (Liechtenstein)' ).
    
        fill_language_mapping( im_sap1  = 'B347'
                               im_sap2  = '3G'
                               im_bcp47 = 'de-LU'
                               iv_text  = 'German (Luxembourg)' ).
    
        fill_language_mapping( im_sap1  = 'B3A7'
                               im_sap2  = '4G'
                               im_bcp47 = 'de-CH'
                               iv_text  = 'German (Switzerland)' ).
    
        fill_language_mapping( im_sap1  = 'G'
                               im_sap2  = 'EL'
                               im_bcp47 = 'el'
                               iv_text  = 'Greek' ).
    
        fill_language_mapping( im_sap1  = 'B'
                               im_sap2  = 'HE'
                               im_bcp47 = 'he'
                               iv_text  = 'Hebrew' ).
    
        fill_language_mapping( im_sap1  = 'BB29'
                               im_sap2  = 'HI'
                               im_bcp47 = 'hi'
                               iv_text  = 'Hindi' ).
    
        fill_language_mapping( im_sap1  = 'H'
                               im_sap2  = 'HU'
                               im_bcp47 = 'hu'
                               iv_text  = 'Hungarian' ).
    
        fill_language_mapping( im_sap1  = 'b'
                               im_sap2  = 'IS'
                               im_bcp47 = 'is'
                               iv_text  = 'Icelandic' ).
    
        fill_language_mapping( im_sap1  = 'i'
                               im_sap2  = 'ID'
                               im_bcp47 = 'id'
                               iv_text  = 'Indonesian' ).
    
        fill_language_mapping( im_sap1  = 'BAC1'
                               im_sap2  = 'GA'
                               im_bcp47 = 'ga'
                               iv_text  = 'Irish' ).
    
        fill_language_mapping( im_sap1  = 'I'
                               im_sap2  = 'IT'
                               im_bcp47 = 'it'
                               iv_text  = 'Italian' ).
    
        fill_language_mapping( im_sap1  = 'I'
                               im_sap2  = 'IT'
                               im_bcp47 = 'it-IT'
                               iv_text  = 'Italian' ).
    
        fill_language_mapping( im_sap1  = 'B289'
                               im_sap2  = '1I'
                               im_bcp47 = 'it-CH'
                               iv_text  = 'Italian (Swiss)' ).
    
        fill_language_mapping( im_sap1  = 'J'
                               im_sap2  = 'JA'
                               im_bcp47 = 'ja'
                               iv_text  = 'Japanese' ).
    
        fill_language_mapping( im_sap1  = '3'
                               im_sap2  = 'KO'
                               im_bcp47 = 'ko'
                               iv_text  = 'Korean' ).
    
        fill_language_mapping( im_sap1  = '3'
                               im_sap2  = 'KO'
                               im_bcp47 = 'ko-KR'
                               iv_text  = 'Korean' ).
    
        fill_language_mapping( im_sap1  = 'Y'
                               im_sap2  = 'LV'
                               im_bcp47 = 'lv'
                               iv_text  = 'Latvian' ).
    
        fill_language_mapping( im_sap1  = 'X'
                               im_sap2  = 'LT'
                               im_bcp47 = 'lt'
                               iv_text  = 'Lithuanian' ).
    
        fill_language_mapping( im_sap1  = '7'
                               im_sap2  = 'MS'
                               im_bcp47 = 'ms'
                               iv_text  = 'Malay' ).
    
        fill_language_mapping( im_sap1  = '7'
                               im_sap2  = 'MS'
                               im_bcp47 = 'ms-MY'
                               iv_text  = 'Malay' ).
    
        fill_language_mapping( im_sap1  = 'B28D'
                               im_sap2  = '1M'
                               im_bcp47 = 'ms-BN'
                               iv_text  = 'Malay (Brunei)' ).
    
        fill_language_mapping( im_sap1  = 'O'
                               im_sap2  = 'NO'
                               im_bcp47 = 'no'
                               iv_text  = 'Norwegian' ).
    
        fill_language_mapping( im_sap1  = 'L'
                               im_sap2  = 'PL'
                               im_bcp47 = 'pl'
                               iv_text  = 'Polish' ).
    
        fill_language_mapping( im_sap1  = 'P'
                               im_sap2  = 'PT'
                               im_bcp47 = 'pt'
                               iv_text  = 'Portuguese' ).
    
        fill_language_mapping( im_sap1  = 'P'
                               im_sap2  = 'PT'
                               im_bcp47 = 'pt-BR'
                               iv_text  = 'Portuguese' ).
    
        fill_language_mapping( im_sap1  = 'BEED'
                               im_sap2  = 'RM'
                               im_bcp47 = 'rm'
                               iv_text  = 'Romansh' ).
    
        fill_language_mapping( im_sap1  = '4'
                               im_sap2  = 'RO'
                               im_bcp47 = 'ro'
                               iv_text  = 'Romanian' ).
    
        fill_language_mapping( im_sap1  = '4'
                               im_sap2  = 'RO'
                               im_bcp47 = 'ro-RO'
                               iv_text  = 'Romanian' ).
    
        fill_language_mapping( im_sap1  = 'R'
                               im_sap2  = 'RU'
                               im_bcp47 = 'ru-RU'
                               iv_text  = 'Russian' ).
    
        fill_language_mapping( im_sap1  = '0'
                               im_sap2  = 'SR'
                               im_bcp47 = 'sr'
                               iv_text  = 'Serbian' ).
    
        fill_language_mapping( im_sap1  = '0'
                               im_sap2  = 'SR'
                               im_bcp47 = 'sr-Cyrl'
                               iv_text  = 'Serbian' ).
    
        fill_language_mapping( im_sap1  = 'd'
                               im_sap2  = 'SH'
                               im_bcp47 = 'sr-Latn'
                               iv_text  = 'Serbian (Latin)' ).
    
        fill_language_mapping( im_sap1  = 'Q'
                               im_sap2  = 'SK'
                               im_bcp47 = 'sk'
                               iv_text  = 'Slovak' ).
    
        fill_language_mapping( im_sap1  = '5'
                               im_sap2  = 'SL'
                               im_bcp47 = 'sl'
                               iv_text  = 'Slovenian' ).
    
        fill_language_mapping( im_sap1  = 'BF42'
                               im_sap2  = 'SB'
                               im_bcp47 = 'wen'
                               iv_text  = '' ).
    
        fill_language_mapping( im_sap1  = 'B9B3'
                               im_sap2  = 'DS'
                               im_bcp47 = 'dsb'
                               iv_text  = '' ).
    
        fill_language_mapping( im_sap1  = 'BB33'
                               im_sap2  = 'HS'
                               im_bcp47 = 'hsb'
                               iv_text  = '' ).
    
        fill_language_mapping( im_sap1  = 'S'
                               im_sap2  = 'ES'
                               im_bcp47 = 'es'
                               iv_text  = 'Spanish' ).
    
        fill_language_mapping( im_sap1  = 'S'
                               im_sap2  = 'ES'
                               im_bcp47 = 'es-ES'
                               iv_text  = 'Spanish' ).
    
        fill_language_mapping( im_sap1  = 'B293'
                               im_sap2  = '1S'
                               im_bcp47 = 'es-AR'
                               iv_text  = 'Spanish (Argentina)' ).
    
        fill_language_mapping( im_sap1  = 'B2F3'
                               im_sap2  = '2S'
                               im_bcp47 = 'es-BO'
                               iv_text  = 'Spanish (Bolivia)' ).
    
        fill_language_mapping( im_sap1  = 'B353'
                               im_sap2  = '3S'
                               im_bcp47 = 'es-CL'
                               iv_text  = 'Spanish (Chile)' ).
    
        fill_language_mapping( im_sap1  = 'B233'
                               im_sap2  = '0S'
                               im_bcp47 = 'es-CO'
                               iv_text  = 'Spanish (Colombia)' ).
    
        fill_language_mapping( im_sap1  = 'B3B3'
                               im_sap2  = '4S'
                               im_bcp47 = 'es-CR'
                               iv_text  = 'Spanish (Costa Rica)' ).
    
        fill_language_mapping( im_sap1  = 'B413'
                               im_sap2  = '5S'
                               im_bcp47 = 'es-DO'
                               iv_text  = 'Spanish (Dominican Republic)' ).
    
        fill_language_mapping( im_sap1  = 'B473'
                               im_sap2  = '6S'
                               im_bcp47 = 'es-EC'
                               iv_text  = 'Spanish (Ecuador)' ).
    
        fill_language_mapping( im_sap1  = 'B4D3'
                               im_sap2  = '7S'
                               im_bcp47 = 'es-SV'
                               iv_text  = 'Spanish (El Salvador)' ).
    
        fill_language_mapping( im_sap1  = 'B533'
                               im_sap2  = '8S'
                               im_bcp47 = 'es-GT'
                               iv_text  = 'Spanish (Guatemala)' ).
    
        fill_language_mapping( im_sap1  = 'B593'
                               im_sap2  = '9S'
                               im_bcp47 = 'es-HN'
                               iv_text  = 'Spanish (Honduras)' ).
    
        fill_language_mapping( im_sap1  = 'B298'
                               im_sap2  = '1X'
                               im_bcp47 = 'es-MX'
                               iv_text  = 'Spanish (Mexico)' ).
    
        fill_language_mapping( im_sap1  = 'B2F8'
                               im_sap2  = '2X'
                               im_bcp47 = 'es-NI'
                               iv_text  = 'Spanish (Nicaragua)' ).
    
        fill_language_mapping( im_sap1  = 'B358'
                               im_sap2  = '3X'
                               im_bcp47 = 'es-PA'
                               iv_text  = 'Spanish (Panama)' ).
    
        fill_language_mapping( im_sap1  = 'B3B8'
                               im_sap2  = '4X'
                               im_bcp47 = 'es-PY'
                               iv_text  = 'Spanish (Paraguay)' ).
    
        fill_language_mapping( im_sap1  = 'B418'
                               im_sap2  = '5X'
                               im_bcp47 = 'es-PE'
                               iv_text  = 'Spanish (Peru)' ).
    
        fill_language_mapping( im_sap1  = 'B478'
                               im_sap2  = '6X'
                               im_bcp47 = 'es-PR'
                               iv_text  = 'Spanish (Puerto Rico)' ).
    
        fill_language_mapping( im_sap1  = 'B4D8'
                               im_sap2  = '7X'
                               im_bcp47 = 'es-UY'
                               iv_text  = 'Spanish (Uruguay)' ).
    
        fill_language_mapping( im_sap1  = 'B538'
                               im_sap2  = '8X'
                               im_bcp47 = 'es-VE'
                               iv_text  = 'Spanish (Venezuela)' ).
    
        fill_language_mapping( im_sap1  = 'BF57'
                               im_sap2  = 'SW'
                               im_bcp47 = 'sw'
                               iv_text  = 'Swahili' ).
    
        fill_language_mapping( im_sap1  = 'V'
                               im_sap2  = 'SV'
                               im_bcp47 = 'sv'
                               iv_text  = 'Swedish' ).
    
        fill_language_mapping( im_sap1  = 'BFAC'
                               im_sap2  = 'TL'
                               im_bcp47 = 'tl'
                               iv_text  = 'Tagalog' ).
    
        fill_language_mapping( im_sap1  = '2'
                               im_sap2  = 'TH'
                               im_bcp47 = 'th'
                               iv_text  = 'Thai' ).
    
        fill_language_mapping( im_sap1  = 'T'
                               im_sap2  = 'TR'
                               im_bcp47 = 'tr'
                               iv_text  = 'Turkish' ).
    
        fill_language_mapping( im_sap1  = '8'
                               im_sap2  = 'UK'
                               im_bcp47 = 'uk'
                               iv_text  = 'Ukrainian' ).
    
        fill_language_mapping( im_sap1  = 'C069'
                               im_sap2  = 'VI'
                               im_bcp47 = 'vi'
                               iv_text  = 'Vietnamese' ).
    
        fill_language_mapping( im_sap1  = 'C0C1'
                               im_sap2  = 'WA'
                               im_bcp47 = 'wa'
                               iv_text  = 'Wa' ).
    
        fill_language_mapping( im_sap1  = 'Z'
                               im_sap2  = 'Z1'
                               im_bcp47 = 'z1'
                               iv_text  = 'Customer reserve' ).
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_convert IMPLEMENTATION.
    
      METHOD base64_to_xstring.
    
        rv_xstr = cl_http_utility=>decode_x_base64( iv_base64 ).
    
      ENDMETHOD.
    
      METHOD conversion_exit_isola_output.
    
        language_sap1_to_sap2(
          EXPORTING
            im_lang_sap1  = iv_spras
          RECEIVING
            re_lang_sap2  = rv_spras
          EXCEPTIONS
            no_assignment = 1
            OTHERS        = 2 ).                              "#EC CI_SUBRC
    
        TRANSLATE rv_spras TO UPPER CASE.
    
      ENDMETHOD.
    
      METHOD int_to_xstring4.
    * returns xstring of length 4 containing the integer value iv_i
    
        DATA lv_x TYPE x LENGTH 4.
    
        lv_x = iv_i.
        rv_xstring = lv_x.
    
      ENDMETHOD.
    
      METHOD language_bcp47_to_sap1.
        DATA lv_converter_instance TYPE REF TO object.
        DATA lv_converter_class_name TYPE string VALUE `CL_AFF_LANGUAGE_CONVERTER`.
        DATA lv_regex TYPE REF TO cl_abap_regex.
        DATA lv_abap_matcher TYPE REF TO cl_abap_matcher.
    
        DATA lv_sap2_lang_code TYPE laiso.
    
        TRY.
            CALL METHOD (lv_converter_class_name)=>create_instance
              RECEIVING
                result = lv_converter_instance.
    
            TRY.
                CALL METHOD lv_converter_instance->(`IF_AFF_LANGUAGE_CONVERTER~BCP47_TO_SAP1`)
                  EXPORTING
                    language = im_lang_bcp47
                  RECEIVING
                    result   = re_lang_sap1.
    
              CATCH cx_static_check.
                RAISE no_assignment.
            ENDTRY.
    
          CATCH cx_sy_dyn_call_error.
            TRY.
                re_lang_sap1 = lcl_bcp47_language_table=>bcp47_to_sap1( im_lang_bcp47 ).
              CATCH zcx_abapgit_exception.
    
                CREATE OBJECT lv_regex EXPORTING pattern = `[A-Z0-9]{2}` ##REGEX_POSIX.
                lv_abap_matcher = lv_regex->create_matcher( text = im_lang_bcp47 ).
    
                IF abap_true = lv_abap_matcher->match( ).
                  "Fallback try to convert from SAP language
                  lv_sap2_lang_code = im_lang_bcp47.
    
                  language_sap2_to_sap1(
                    EXPORTING
                      im_lang_sap2  = lv_sap2_lang_code
                    RECEIVING
                      re_lang_sap1  = re_lang_sap1
                    EXCEPTIONS
                      no_assignment = 1
                      OTHERS        = 2 ).
                  IF sy-subrc <> 0.
                    RAISE no_assignment.
                  ENDIF.
    
                ELSE.
                  RAISE no_assignment.
                ENDIF.
            ENDTRY.
        ENDTRY.
      ENDMETHOD.
    
      METHOD language_sap1_to_bcp47.
        DATA lv_converter_instance TYPE REF TO object.
        DATA lv_converter_class_name TYPE string VALUE `CL_AFF_LANGUAGE_CONVERTER`.
    
        TRY.
            CALL METHOD (lv_converter_class_name)=>create_instance
              RECEIVING
                result = lv_converter_instance.
    
            TRY.
                CALL METHOD lv_converter_instance->(`IF_AFF_LANGUAGE_CONVERTER~SAP1_TO_BCP47`)
                  EXPORTING
                    language = im_lang_sap1
                  RECEIVING
                    result   = re_lang_bcp47.
              CATCH cx_static_check.
                RAISE no_assignment.
            ENDTRY.
          CATCH cx_sy_dyn_call_error.
            TRY.
                re_lang_bcp47 = lcl_bcp47_language_table=>sap1_to_bcp47( im_lang_sap1 ).
              CATCH zcx_abapgit_exception.
                RAISE no_assignment.
            ENDTRY.
        ENDTRY.
      ENDMETHOD.
    
      METHOD language_sap1_to_sap2.
    
        TRY.
            re_lang_sap2 = lcl_bcp47_language_table=>sap1_to_sap2( im_lang_sap1 ).
          CATCH zcx_abapgit_exception.
            RAISE no_assignment.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD language_sap1_to_text.
        re_text = lcl_bcp47_language_table=>sap1_to_text( im_lang_sap1 ).
      ENDMETHOD.
    
      METHOD language_sap2_to_sap1.
    
        TRY.
            re_lang_sap1 = lcl_bcp47_language_table=>sap2_to_sap1( im_lang_sap2 ).
          CATCH zcx_abapgit_exception.
            RAISE no_assignment.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD split_string.
    
        FIND FIRST OCCURRENCE OF cl_abap_char_utilities=>cr_lf IN iv_string.
    
        " Convert string into table depending on separator type CR_LF vs. LF
        IF sy-subrc = 0.
          SPLIT iv_string AT cl_abap_char_utilities=>cr_lf INTO TABLE rt_lines.
        ELSE.
          SPLIT iv_string AT cl_abap_char_utilities=>newline INTO TABLE rt_lines.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD string_to_tab.
    
        DATA lv_length TYPE i.
        DATA lv_iterations TYPE i.
        DATA lv_offset TYPE i.
    
        FIELD-SYMBOLS  TYPE any.
    
        CLEAR et_tab.
        ev_size = strlen( iv_str ).
    
        APPEND INITIAL LINE TO et_tab ASSIGNING .
         = iv_str.
        lv_length = cl_abap_typedescr=>describe_by_data(  )->length / cl_abap_char_utilities=>charsize.
        lv_iterations = ev_size DIV lv_length.
    
        DO lv_iterations TIMES.
          lv_offset = sy-index * lv_length.
          APPEND INITIAL LINE TO et_tab ASSIGNING .
           = iv_str+lv_offset.
        ENDDO.
    
      ENDMETHOD.
    
      METHOD string_to_xstring.
    
        rv_xstr = string_to_xstring_utf8( iv_str ).
    
      ENDMETHOD.
    
      METHOD string_to_xstring_utf8.
    
        rv_xstring = LCL_OUT_=>convert( iv_string ).
    
      ENDMETHOD.
    
      METHOD string_to_xstring_utf8_bom.
    
        IF iv_string IS INITIAL.
          RETURN.
        ENDIF.
    
        rv_xstring = string_to_xstring_utf8( iv_string ).
    
        " Add UTF-8 BOM
        IF xstrlen( rv_xstring ) < 3 OR rv_xstring(3) <> cl_abap_char_utilities=>byte_order_mark_utf8.
          rv_xstring = cl_abap_char_utilities=>byte_order_mark_utf8 && rv_xstring.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD uccp.
    
        DATA lv_class    TYPE string.
        DATA lv_xstr     TYPE xstring.
        DATA lo_instance TYPE REF TO object.
    
        lv_class = 'CL_ABAP_CONV_IN_CE'.
    
        TRY.
            CALL METHOD (lv_class)=>uccp
              EXPORTING
                uccp = iv_uccp
              RECEIVING
                char = rv_char.
          CATCH cx_sy_dyn_call_illegal_class.
            lv_xstr = iv_uccp.
    
            CALL METHOD ('CL_ABAP_CONV_CODEPAGE')=>create_in
              EXPORTING
                codepage = 'UTF-16'
              RECEIVING
                instance = lo_instance.
    
    * convert endianness
            CONCATENATE lv_xstr+1(1) lv_xstr(1) INTO lv_xstr IN BYTE MODE.
    
            CALL METHOD lo_instance->('IF_ABAP_CONV_IN~CONVERT')
              EXPORTING
                source = lv_xstr
              RECEIVING
                result = rv_char.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD xstring_remove_bom.
    
        rv_xstr = iv_xstr.
    
        " cl_abap_conv_in_ce does not handle BOM in non-Unicode systems, so we remove it
        IF cl_abap_char_utilities=>charsize = 1 AND xstrlen( rv_xstr ) > 3
            AND rv_xstr(3) = cl_abap_char_utilities=>byte_order_mark_utf8.
    
          rv_xstr = rv_xstr+3.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD xstring_to_bintab.
    
        DATA lv_length TYPE i.
        DATA lv_iterations TYPE i.
        DATA lv_offset TYPE i.
        DATA lv_struct TYPE abap_bool.
    
        FIELD-SYMBOLS  TYPE any.
    
        CLEAR et_bintab.
        ev_size = xstrlen( iv_xstr ).
    
        IF iv_xstr IS INITIAL.
          RETURN.
        ENDIF.
    
        APPEND INITIAL LINE TO et_bintab ASSIGNING .
        lv_struct = boolc(
          cl_abap_typedescr=>describe_by_data(  )->type_kind = cl_abap_typedescr=>typekind_struct1 ).
        IF lv_struct = abap_true.
          ASSIGN COMPONENT 1 OF STRUCTURE  TO .
        ENDIF.
         = iv_xstr.
    
        lv_length = cl_abap_typedescr=>describe_by_data(  )->length.
        ASSERT lv_length > 0.
    
        lv_iterations = ( ev_size - 1 ) DIV lv_length.
    
        DO lv_iterations TIMES.
          lv_offset = sy-index * lv_length.
          APPEND INITIAL LINE TO et_bintab ASSIGNING .
          IF lv_struct = abap_true.
            ASSIGN COMPONENT 1 OF STRUCTURE  TO .
          ENDIF.
           = iv_xstr+lv_offset.
        ENDDO.
    
      ENDMETHOD.
    
      METHOD xstring_to_int.
    
    * use the built-in type conversion
        rv_i = iv_xstring.
    
      ENDMETHOD.
    
      METHOD xstring_to_string_utf8.
    
        DATA lv_data   TYPE xstring.
        DATA lv_length TYPE i.
    
        " Remove BOM for non-Unicode systems
        lv_data = xstring_remove_bom( iv_data ).
    
        lv_length = iv_length.
        IF lv_length <= 0.
          lv_length = xstrlen( lv_data ).
        ENDIF.
    
        rv_string = lcl_in=>convert(
          iv_data   = lv_data
          iv_length = lv_length ).
    
      ENDMETHOD.
    
      METHOD xstring_to_string_utf8_raw.
    
        DATA lv_length TYPE i.
    
        lv_length = iv_length.
        IF lv_length <= 0.
          lv_length = xstrlen( iv_data ).
        ENDIF.
    
        rv_string = lcl_in=>convert(
          iv_data   = iv_data
          iv_length = lv_length ).
    
      ENDMETHOD.
    
      METHOD xstring_to_string_utf8_bom.
    
        DATA lv_xstring TYPE xstring.
    
        IF iv_xstring IS INITIAL.
          RETURN.
        ENDIF.
    
        lv_xstring = iv_xstring.
        " Add UTF-8 BOM
        IF xstrlen( lv_xstring ) < 3 OR lv_xstring(3) <> cl_abap_char_utilities=>byte_order_mark_utf8.
          lv_xstring = cl_abap_char_utilities=>byte_order_mark_utf8 && lv_xstring.
        ENDIF.
    
        rv_string = xstring_to_string_utf8( lv_xstring ).
    
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_cts_api IMPLEMENTATION.
    
      METHOD get_current_transport_for_obj.
        DATA: lv_object_lockable   TYPE abap_bool,
              lv_locked            TYPE abap_bool,
              lv_transport_request TYPE trkorr,
              ls_tlock             TYPE tlock,
              lt_tlock             TYPE STANDARD TABLE OF tlock WITH DEFAULT KEY,
              lt_transports        TYPE STANDARD TABLE OF trkorr WITH DEFAULT KEY,
              lv_task              TYPE trkorr,
              lv_tr_object_name    TYPE trobj_name.
    
        lv_tr_object_name = iv_object_name.
    
        CALL FUNCTION 'TR_CHECK_OBJECT_LOCK'
          EXPORTING
            wi_pgmid             = iv_program_id
            wi_object            = iv_object_type
            wi_objname           = lv_tr_object_name
          IMPORTING
            we_lockable_object   = lv_object_lockable
            we_locked            = lv_locked
            we_lock_order        = lv_transport_request
            we_lock_task         = lv_task
          TABLES
            wt_tlock             = lt_tlock
          EXCEPTIONS
            empty_key            = 1
            no_systemname        = 2
            no_systemtype        = 3
            unallowed_lock_order = 4
            OTHERS               = 5.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        IF lv_locked = abap_false.
          zcx_abapgit_exception=>raise( |Object { iv_program_id }-{ iv_object_type }-{ iv_object_name } is not locked| ).
        ENDIF.
    
        IF lv_object_lockable = abap_false.
          zcx_abapgit_exception=>raise( |Object type { iv_program_id }-{ iv_object_type } not lockable| ).
        ENDIF.
    
        LOOP AT lt_tlock INTO ls_tlock.
          COLLECT ls_tlock-trkorr INTO lt_transports.
        ENDLOOP.
    
        IF lines( lt_transports ) = 1.
          rv_transport = lv_transport_request.
        ELSE.
          rv_transport = zif_abapgit_definitions=>c_multiple_transports.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_current_transport_from_db.
    
        " This method is used for objects that are included in transports but not locked
        " for example, namespaces (NSPC) or table entries (TABU)
        " Ignore unreleased SAP piece lists
        SELECT SINGLE a~trkorr FROM e070 AS a JOIN e071 AS b ON a~trkorr = b~trkorr
          INTO rv_transport
          WHERE ( a~trstatus = 'D' OR a~trstatus = 'L' )
            AND a~trfunction <> 'G'
            AND NOT ( a~trfunction = 'F' AND ( a~tarsystem = '' OR a~tarsystem = 'SAP' ) )
            AND b~pgmid = iv_program_id AND b~object = iv_object_type AND b~obj_name = iv_object_name.
    
      ENDMETHOD.
    
      METHOD is_object_locked_in_transport.
        DATA: ls_object_key        TYPE e071,
              lv_type_check_result TYPE c LENGTH 1,
              ls_lock_key          TYPE tlock_int,
              lv_lock_flag         TYPE c LENGTH 1.
    
        ls_object_key-pgmid = iv_program_id.
        ls_object_key-object = iv_object_type.
        ls_object_key-obj_name = iv_object_name.
    
        CALL FUNCTION 'TR_CHECK_TYPE'
          EXPORTING
            wi_e071     = ls_object_key
          IMPORTING
            pe_result   = lv_type_check_result
            we_lock_key = ls_lock_key.
    
        IF lv_type_check_result <> 'L'.
          zcx_abapgit_exception=>raise( |Object type { iv_program_id }-{ iv_object_type } not lockable| ).
        ENDIF.
    
        CALL FUNCTION 'TRINT_CHECK_LOCKS'
          EXPORTING
            wi_lock_key = ls_lock_key
          IMPORTING
            we_lockflag = lv_lock_flag
          EXCEPTIONS
            empty_key   = 1
            OTHERS      = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        rv_locked = boolc( lv_lock_flag <> space ).
      ENDMETHOD.
    
      METHOD is_object_type_lockable.
        DATA: ls_object_key        TYPE e071,
              lv_type_check_result TYPE c LENGTH 1.
    
        ls_object_key-pgmid = iv_program_id.
        ls_object_key-object = iv_object_type.
        ls_object_key-obj_name = '_'. " Dummy value #2071
    
        CALL FUNCTION 'TR_CHECK_TYPE'
          EXPORTING
            wi_e071   = ls_object_key
          IMPORTING
            pe_result = lv_type_check_result.
    
        rv_lockable = boolc( lv_type_check_result = 'L' ).
      ENDMETHOD.
    
      METHOD is_object_type_transportable.
        DATA: ls_object_key        TYPE e071,
              lv_type_check_result TYPE c LENGTH 1.
    
        ls_object_key-pgmid = iv_program_id.
        ls_object_key-object = iv_object_type.
        ls_object_key-obj_name = '_'. " Dummy value #2071
    
        CALL FUNCTION 'TR_CHECK_TYPE'
          EXPORTING
            wi_e071   = ls_object_key
          IMPORTING
            pe_result = lv_type_check_result.
    
        rv_transportable = boolc( lv_type_check_result CA 'RTL' OR iv_object_type = 'TABU' ).
      ENDMETHOD.
    
      METHOD zif_abapgit_cts_api~change_transport_type.
    
        DATA:
          ls_request_header  TYPE trwbo_request_header,
          lt_request_headers TYPE trwbo_request_headers.
    
        CALL FUNCTION 'ENQUEUE_E_TRKORR'
          EXPORTING
            trkorr         = iv_transport_request
          EXCEPTIONS
            foreign_lock   = 1
            system_failure = 2
            OTHERS         = 3.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CALL FUNCTION 'TR_READ_REQUEST_WITH_TASKS'
          EXPORTING
            iv_trkorr          = iv_transport_request
          IMPORTING
            et_request_headers = lt_request_headers
          EXCEPTIONS
            invalid_input      = 1
            OTHERS             = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        LOOP AT lt_request_headers INTO ls_request_header WHERE trfunction = iv_transport_type_from.
    
          CALL FUNCTION 'TRINT_READ_REQUEST_HEADER'
            EXPORTING
              iv_read_e070   = abap_true
              iv_read_e070c  = abap_true
            CHANGING
              cs_request     = ls_request_header
            EXCEPTIONS
              empty_trkorr   = 1
              not_exist_e070 = 2
              OTHERS         = 3.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          CALL FUNCTION 'TRINT_CHANGE_TRFUNCTION'
            EXPORTING
              iv_new_trfunction      = iv_transport_type_to
            CHANGING
              cs_request_header      = ls_request_header
            EXCEPTIONS
              action_aborted_by_user = 1
              change_not_allowed     = 2
              db_access_error        = 3
              OTHERS                 = 4.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
        ENDLOOP.
    
        CALL FUNCTION 'DEQUEUE_E_TRKORR'
          EXPORTING
            trkorr = iv_transport_request.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_cts_api~read_request_and_tasks.
    
        DATA lt_request_headers TYPE trwbo_request_headers.
        DATA ls_row             LIKE LINE OF lt_request_headers.
        DATA ls_task            LIKE LINE OF rt_tasks.
    
        CALL FUNCTION 'TR_READ_REQUEST_WITH_TASKS'
          EXPORTING
            iv_trkorr          = iv_request
          IMPORTING
            et_request_headers = lt_request_headers
          EXCEPTIONS
            invalid_input      = 1
            OTHERS             = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        LOOP AT lt_request_headers INTO ls_row.
          ls_task-trkorr  = ls_row-trkorr.
          ls_task-as4user = ls_row-as4user.
    
          INSERT ls_task INTO TABLE rt_tasks.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_cts_api~confirm_transport_messages.
    
        TYPES: BEGIN OF ty_s_message,
                 id TYPE symsgid,
                 ty TYPE symsgty,
                 no TYPE symsgno,
                 v1 TYPE symsgv,
                 v2 TYPE symsgv,
                 v3 TYPE symsgv,
                 v4 TYPE symsgv,
               END OF ty_s_message.
    
        DATA ls_message TYPE ty_s_message.
    
        FIELD-SYMBOLS:  TYPE STANDARD TABLE.
    
        IF mv_confirm_transp_msgs_called = abap_true.
          RETURN.
        ENDIF.
    
        " remember the call to avoid duplicates in GT_CONFIRMED_MESSAGES
        mv_confirm_transp_msgs_called = abap_true.
    
        " Auto-confirm certain messages (requires SAP Note 1609940)
        PERFORM dummy IN PROGRAM saplstrd IF FOUND.  "load function group STRD once into memory
    
        ASSIGN ('(SAPLSTRD)GT_CONFIRMED_MESSAGES') TO .
    
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        " Object can only be created in package of namespace
        ls_message-id = 'TR'.
        ls_message-no = '007'.
        INSERT ls_message INTO TABLE .
    
        " Original system set to "SAP"
        ls_message-id = 'TR'.
        ls_message-no = '013'.
        INSERT ls_message INTO TABLE .
    
        " Make repairs in foreign namespaces only if they are urgent
        ls_message-id = 'TR'.
        ls_message-no = '852'.
        INSERT ls_message INTO TABLE .
    
        " Make repairs in foreign namespaces only if they are urgent
        ls_message-id = 'TK'.
        ls_message-no = '016'.
        INSERT ls_message INTO TABLE .
    
        rv_messages_confirmed = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_cts_api~create_transport_entries.
    
        DATA lt_tables      TYPE tredt_objects.
        DATA lt_table_keys  TYPE STANDARD TABLE OF e071k.
        DATA lv_with_dialog TYPE abap_bool.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_tables.
        FIELD-SYMBOLS  LIKE LINE OF lt_table_keys.
    
        cl_table_utilities_brf=>create_transport_entries(
          EXPORTING
            it_table_ins = it_table_ins
            it_table_upd = it_table_upd
            it_table_del = it_table_del
            iv_tabname   = iv_tabname
          CHANGING
            ct_e071      = lt_tables
            ct_e071k     = lt_table_keys ).
    
        " cl_table_utilities_brf=>write_transport_entries does not allow passing a request
    
        CALL FUNCTION 'TR_OBJECTS_CHECK'
          TABLES
            wt_ko200                = lt_tables
            wt_e071k                = lt_table_keys
          EXCEPTIONS
            cancel_edit_other_error = 1
            show_only_other_error   = 2
            OTHERS                  = 3.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        IF iv_transport IS INITIAL.
          lv_with_dialog = abap_true.
        ENDIF.
    
        READ TABLE lt_tables ASSIGNING  INDEX 1.
        ASSERT sy-subrc = 0.
    
        LOOP AT lt_table_keys ASSIGNING .
          -objfunc = -objfunc.
        ENDLOOP.
    
        CALL FUNCTION 'TR_OBJECT_INSERT'
          EXPORTING
            wi_order                = iv_transport
            wi_ko200                = 
            iv_no_show_option       = abap_true
          TABLES
            wt_e071k                = lt_table_keys
          EXCEPTIONS
            cancel_edit_other_error = 1
            show_only_other_error   = 2
            OTHERS                  = 3.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_cts_api~get_r3tr_obj_for_limu_obj.
    
        CLEAR ev_object.
        CLEAR ev_obj_name.
    
        CASE iv_object.
          WHEN 'MESS'.
            ev_object = 'MSAG'.
            ev_obj_name = substring( val = iv_obj_name
                                     len = strlen( iv_obj_name ) - 3 ).
          WHEN 'TABT'.
    * Technical Attributes of a Table
            ev_object = 'TABL'.
            ev_obj_name = iv_obj_name.
          WHEN 'DTED'.
    * Data Element Definition
            ev_object = 'DTEL'.
            ev_obj_name = iv_obj_name.
          WHEN 'DOMD'.
    * Domain Definition
            ev_object = 'DOMA'.
            ev_obj_name = iv_obj_name.
          WHEN OTHERS.
            CALL FUNCTION 'GET_R3TR_OBJECT_FROM_LIMU_OBJ'
              EXPORTING
                p_limu_objtype = iv_object
                p_limu_objname = iv_obj_name
              IMPORTING
                p_r3tr_objtype = ev_object
                p_r3tr_objname = ev_obj_name
              EXCEPTIONS
                no_mapping     = 1
                OTHERS         = 2.
            IF sy-subrc <> 0 OR ev_obj_name IS INITIAL.
              zcx_abapgit_exception=>raise( |No R3TR Object found for { iv_object } { iv_obj_name }| ).
            ENDIF.
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_cts_api~get_transports_for_list.
    
        DATA lv_request TYPE trkorr.
        DATA lt_tlock TYPE SORTED TABLE OF tlock WITH NON-UNIQUE KEY object hikey.
        DATA ls_object_key TYPE e071.
        DATA lv_type_check_result TYPE c LENGTH 1.
        DATA ls_lock_key TYPE tlock_int.
        DATA ls_transport LIKE LINE OF rt_transports.
    
        FIELD-SYMBOLS  LIKE LINE OF it_items.
        FIELD-SYMBOLS  LIKE LINE OF lt_tlock.
    
    * Workarounds to improve performance, note that IT_ITEMS might
    * contain 1000s of rows, see standard logic in function module
    * TR_CHECK_OBJECT_LOCK
    
    * avoid database lookups in TLOCK for each item,
        SELECT * FROM tlock INTO TABLE lt_tlock.
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        LOOP AT it_items ASSIGNING .
          CLEAR lv_request.
    
          ls_object_key-pgmid = 'R3TR'.
          ls_object_key-object = -obj_type.
          ls_object_key-obj_name = -obj_name.
    
          CALL FUNCTION 'TR_CHECK_TYPE'
            EXPORTING
              wi_e071     = ls_object_key
            IMPORTING
              we_lock_key = ls_lock_key
              pe_result   = lv_type_check_result.
    
          IF lv_type_check_result = 'L'.
            LOOP AT lt_tlock ASSIGNING 
                WHERE object = ls_lock_key-obj
                AND hikey >= ls_lock_key-low
                AND lokey <= ls_lock_key-hi.                  "#EC PORTABLE
              IF lv_request IS INITIAL.
                lv_request = -trkorr.
              ELSE.
                lv_request = zif_abapgit_definitions=>c_multiple_transports.
                EXIT.
              ENDIF.
            ENDLOOP.
          ELSEIF is_object_type_transportable( -obj_type ) = abap_true.
            lv_request = get_current_transport_from_db(
              iv_object_type = -obj_type
              iv_object_name = -obj_name ).
          ENDIF.
    
          IF lv_request IS NOT INITIAL.
            ls_transport-obj_type = -obj_type.
            ls_transport-obj_name = -obj_name.
            ls_transport-trkorr = lv_request.
            INSERT ls_transport INTO TABLE rt_transports.
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_cts_api~get_transport_for_object.
    
        IF is_item-obj_type IS NOT INITIAL AND is_item-obj_name IS NOT INITIAL.
    
          IF is_object_type_lockable( is_item-obj_type ) = abap_true AND
             is_object_locked_in_transport(
               iv_object_type = is_item-obj_type
               iv_object_name = is_item-obj_name ) = abap_true.
    
            rv_transport = get_current_transport_for_obj(
              iv_object_type = is_item-obj_type
              iv_object_name = is_item-obj_name ).
    
          ELSEIF is_object_type_transportable( is_item-obj_type ) = abap_true.
    
            rv_transport = get_current_transport_from_db(
              iv_object_type = is_item-obj_type
              iv_object_name = is_item-obj_name ).
    
          ENDIF.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_cts_api~insert_transport_object.
    
        CALL FUNCTION 'RS_CORR_INSERT'
          EXPORTING
            object              = iv_obj_name
            object_class        = iv_object
            devclass            = iv_package
            master_language     = iv_language
            korrnum             = iv_transport
            mode                = iv_mode
            global_lock         = abap_true
            suppress_dialog     = abap_true
          EXCEPTIONS
            cancelled           = 1
            permission_failure  = 2
            unknown_objectclass = 3
            OTHERS              = 4.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_cts_api~is_chrec_possible_for_package.
        IF iv_package IS NOT INITIAL.
          rv_possible = zcl_abapgit_factory=>get_sap_package( iv_package )->are_changes_recorded_in_tr_req( ).
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_cts_api~list_open_requests.
    
        SELECT trkorr FROM e070
          INTO TABLE rt_trkorr
          WHERE trstatus = zif_abapgit_cts_api=>c_transport_status-modifiable
          AND trfunction = zif_abapgit_cts_api=>c_transport_type-wb_request
          AND as4date IN it_date
          ORDER BY PRIMARY KEY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_cts_api~list_r3tr_by_request.
    
        TYPES: BEGIN OF ty_contents,
                 trkorr   TYPE e071-trkorr,
                 as4pos   TYPE e071-as4pos,
                 pgmid    TYPE e071-pgmid,
                 object   TYPE e071-object,
                 obj_name TYPE e071-obj_name,
               END OF ty_contents.
    
        DATA lt_tasks    TYPE STANDARD TABLE OF trkorr WITH DEFAULT KEY.
        DATA lt_contents TYPE STANDARD TABLE OF ty_contents WITH DEFAULT KEY.
        DATA ls_contents LIKE LINE OF lt_contents.
        DATA ls_list     LIKE LINE OF rt_list.
    
        SELECT trkorr FROM e070 INTO TABLE lt_tasks
          WHERE strkorr = iv_request
          AND trfunction = zif_abapgit_cts_api=>c_transport_type-wb_task
          ORDER BY PRIMARY KEY.
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        SELECT trkorr as4pos pgmid object obj_name FROM e071
          INTO TABLE lt_contents
          FOR ALL ENTRIES IN lt_tasks
          WHERE trkorr = lt_tasks-table_line
          ORDER BY PRIMARY KEY.
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        LOOP AT lt_contents INTO ls_contents.
          CASE ls_contents-pgmid.
            WHEN 'R3TR'.
              ls_list-object = ls_contents-object.
              ls_list-obj_name = ls_contents-obj_name.
              INSERT ls_list INTO TABLE rt_list.
            WHEN 'LIMU'.
              TRY.
                  zif_abapgit_cts_api~get_r3tr_obj_for_limu_obj(
                    EXPORTING
                      iv_object   = ls_contents-object
                      iv_obj_name = ls_contents-obj_name
                    IMPORTING
                      ev_object   = ls_list-object
                      ev_obj_name = ls_list-obj_name ).
                  INSERT ls_list INTO TABLE rt_list.
                CATCH zcx_abapgit_exception ##NO_HANDLER.
              ENDTRY.
          ENDCASE.
        ENDLOOP.
    
        SORT rt_list BY object obj_name.
        DELETE ADJACENT DUPLICATES FROM rt_list COMPARING object obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_cts_api~read.
    
        DATA ls_request TYPE trwbo_request.
        DATA ls_key     LIKE LINE OF ls_request-keys.
    
        FIELD-SYMBOLS  LIKE LINE OF rs_request-keys.
    
        ls_request-h-trkorr = iv_trkorr.
    
        CALL FUNCTION 'TRINT_READ_REQUEST'
          EXPORTING
            iv_read_e070       = abap_true
            iv_read_e07t       = abap_true
            iv_read_e070c      = abap_true
            iv_read_e070m      = abap_true
            iv_read_objs_keys  = abap_true
            iv_read_objs       = abap_true
            iv_read_attributes = abap_true
          CHANGING
            cs_request         = ls_request
          EXCEPTIONS
            error_occured      = 1
            OTHERS             = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
    * move to output structure
        rs_request-trstatus = ls_request-h-trstatus.
        rs_request-as4date  = ls_request-h-as4date.
        rs_request-as4user  = ls_request-h-as4user.
        LOOP AT ls_request-keys INTO ls_key.
          APPEND INITIAL LINE TO rs_request-keys ASSIGNING .
          MOVE-CORRESPONDING ls_key TO .
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_cts_api~read_description.
    
        SELECT SINGLE as4text FROM e07t
          INTO rv_description
          WHERE trkorr = iv_trkorr
          AND langu = sy-langu.
        IF sy-subrc <> 0.
    * fallback to any language
          SELECT SINGLE as4text FROM e07t
            INTO rv_description
            WHERE trkorr = iv_trkorr ##SUBRC_OK. "#EC CI_NOORDER
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_cts_api~read_user.
    
        SELECT SINGLE as4user FROM e070 INTO rv_uname
          WHERE trkorr = iv_trkorr ##SUBRC_OK.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_cts_api~validate_transport_request.
    
        CONSTANTS:
          BEGIN OF c_tr_status,
            modifiable           TYPE trstatus VALUE 'D',
            modifiable_protected TYPE trstatus VALUE 'L',
          END OF c_tr_status.
    
        DATA ls_request TYPE zif_abapgit_cts_api=>ty_transport_data.
    
        ls_request = zif_abapgit_cts_api~read( iv_transport_request ).
    
        IF ls_request-trstatus <> c_tr_status-modifiable
            AND ls_request-trstatus <> c_tr_status-modifiable_protected.
          " Task/request &1 has already been released
          MESSAGE e064(tk) WITH iv_transport_request INTO zcx_abapgit_exception=>null.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_default_transport IMPLEMENTATION.
    
      METHOD clear.
    
        CALL FUNCTION 'TR_TASK_RESET'
          EXPORTING
            iv_username      = is_default_task-username
            iv_order         = is_default_task-ordernum
            iv_task          = is_default_task-tasknum
            iv_dialog        = abap_false
          EXCEPTIONS
            invalid_username = 1
            invalid_order    = 2
            invalid_task     = 3
            OTHERS           = 4.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        store( ).
    
      ENDMETHOD.
    
      METHOD restore.
    
        IF ms_save IS INITIAL.
          " There wasn't a default transport request before
          " so we needn't restore anything.
          RETURN.
        ENDIF.
    
        CALL FUNCTION 'TR_TASK_SET'
          EXPORTING
            iv_order          = ms_save-ordernum
            iv_task           = ms_save-tasknum
          EXCEPTIONS
            invalid_username  = 1
            invalid_category  = 2
            invalid_client    = 3
            invalid_validdays = 4
            invalid_order     = 5
            invalid_task      = 6
            OTHERS            = 7.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD set_internal.
    
        CALL FUNCTION 'TR_TASK_SET'
          EXPORTING
            iv_order          = iv_transport
            iv_validdays      = 1
          EXCEPTIONS
            invalid_username  = 1
            invalid_category  = 2
            invalid_client    = 3
            invalid_validdays = 4
            invalid_order     = 5
            invalid_task      = 6
            OTHERS            = 7.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD store.
    
        TRY.
            ms_save = zif_abapgit_default_transport~get( ).
          CATCH zcx_abapgit_exception.
            CLEAR ms_save.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_default_transport~get.
    
        DATA lt_e070use TYPE STANDARD TABLE OF e070use WITH DEFAULT KEY.
        DATA ls_line    LIKE LINE OF lt_e070use.
    
        CALL FUNCTION 'TR_TASK_GET'
          TABLES
            tt_e070use       = lt_e070use
          EXCEPTIONS
            invalid_username = 1
            invalid_category = 2
            invalid_client   = 3
            OTHERS           = 4.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        READ TABLE lt_e070use INTO ls_line INDEX 1.
        IF sy-subrc = 0.
          MOVE-CORRESPONDING ls_line TO rs_default_task.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_default_transport~reset.
    
        DATA: ls_default_task TYPE e070use.
    
        IF mv_is_set_by_abapgit = abap_false.
          " if the default transport request task isn't set
          " by us there is nothing to do.
          RETURN.
        ENDIF.
    
        CLEAR mv_is_set_by_abapgit.
    
        ls_default_task = zif_abapgit_default_transport~get( ).
    
        IF ls_default_task IS NOT INITIAL.
    
          clear( ls_default_task ).
    
        ENDIF.
    
        restore( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_default_transport~set.
    
        " checks whether object changes of the package are reordered in transport
        " requests. If true then we set the default task, so that no annoying
        " transport request popups are shown while deserializing.
    
        IF mv_is_set_by_abapgit = abap_true.
          " the default transport request task is already set by us
          " -> no reason to do it again.
          RETURN.
        ENDIF.
    
        IF iv_transport IS INITIAL.
          zcx_abapgit_exception=>raise( |No transport request was supplied| ).
        ENDIF.
    
        set_internal( iv_transport ).
    
        mv_is_set_by_abapgit = abap_true.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_dependencies IMPLEMENTATION.
    
      METHOD get_ddls_dependencies.
    
        DATA: lt_ddls_name TYPE TABLE OF ddsymtab,
              ls_ddls_name TYPE ddsymtab.
    
        ls_ddls_name-name = iv_ddls_name.
        INSERT ls_ddls_name INTO TABLE lt_ddls_name.
    
        PERFORM ('DDLS_GET_DEP') IN PROGRAM ('RADMASDL')
                                 TABLES lt_ddls_name rt_dependency ##PERF_NO_FORM.
    
      ENDMETHOD.
    
      METHOD resolve.
    
        DATA: lv_tabclass TYPE dd02l-tabclass.
    
        FIELD-SYMBOLS:  LIKE LINE OF ct_tadir.
    
        " misuse field KORRNUM to fix deletion sequence
        " higher value means later deletion
    
        LOOP AT ct_tadir ASSIGNING .
          CASE -object.
            WHEN 'DEVC'.
              " Packages last
              -korrnum = '999000'.
            WHEN 'DOMA'.
              -korrnum = '900000'.
            WHEN 'PARA'.
              " PARA after DTEL
              -korrnum = '810000'.
            WHEN 'DTEL'.
              -korrnum = '800000'.
            WHEN 'SHLP'.
              " SHLP after TABL
              -korrnum = '760000'.
            WHEN 'TTYP' OR 'TABL' OR 'VIEW'.
              SELECT SINGLE tabclass FROM dd02l
                INTO lv_tabclass
                WHERE tabname = -obj_name
                AND as4local = 'A'
                AND as4vers = '0000'.
              IF sy-subrc = 0 AND lv_tabclass = 'APPEND'.
                " delete append structures before database tables
                -korrnum = '730000'.
              ELSE.
                -korrnum = '750000'.
              ENDIF.
            WHEN 'ENQU'.
              " ENQU before TABL
              -korrnum = '725000'.
            WHEN 'DDLS'.
              " DDLS after DCLS but before other DDIC
              -korrnum = '720000'.
            WHEN 'DDLX'.
              " DDLX before DDLS
              -korrnum = '719000'.
            WHEN 'AUTH'.
              " AUTH after DCLS
              -korrnum = '715000'.
            WHEN 'SUSH'.
              " SUSH after SUSC, SRVB, and G4BA
              -korrnum = '712000'.
            WHEN 'SUSC'.
              " SUSC after SUSO
              -korrnum = '711000'.
            WHEN 'SUSO'.
              " SUSO after DCLS
              -korrnum = '710000'.
            WHEN 'DCLS'.
              " AUTH and SUSO after DCLS
              -korrnum = '705000'.
            WHEN 'G4BA' OR 'G4BS' OR 'IWMO' OR 'IWSV' OR 'IWVB'.
              " after SRVB
              -korrnum = '610000'.
            WHEN 'SRVD'.
              " after SRVB
              -korrnum = '600500'.
            WHEN 'SRVB'.
              -korrnum = '600000'.
            WHEN 'IASP'.
              -korrnum = '552000'.
            WHEN 'IARP'.
              -korrnum = '551000'.
            WHEN 'IATU'.
              -korrnum = '550000'.
            WHEN 'SOD1'.
              -korrnum = '311000'.
            WHEN 'SOD2'.
              -korrnum = '310000'.
            WHEN 'ACID'.
              " ACID after PROG/FUGR/CLAS
              -korrnum = '300000'.
            WHEN 'FUGR'.
              -korrnum = '260000'.
            WHEN 'PROG'.
              " delete includes after main programs
              SELECT COUNT(*) FROM reposrc
                WHERE progname = -obj_name
                AND r3state = 'A'
                AND subc = 'I'.
              IF sy-subrc = 0.
                -korrnum = '250000'.
              ELSE.
                -korrnum = '240000'.
              ENDIF.
            WHEN 'INTF'.
              -korrnum = '230000'.
            WHEN 'CLAS'.
              -korrnum = '220000'.
            WHEN 'IDOC'.
              -korrnum = '200000'.
            WHEN 'IOBJ'.
              -korrnum = '195000'.
            WHEN 'ODSO'.
              -korrnum = '190000'.
            WHEN 'WDCA'.
              -korrnum = '174000'.
            WHEN 'WDYA'.
              -korrnum = '173000'.
            WHEN 'WDCC'.
              -korrnum = '172000'.
            WHEN 'WDYN'.
              -korrnum = '171000'.
            WHEN 'IEXT'.
              -korrnum = '150000'.
            WHEN 'SAPC'.
              " SAPC after SICF
              -korrnum = '140000'.
            WHEN 'SPRX'.
              " SPRX generates objects, so remove those early
              -korrnum = '135000'.
            WHEN 'WEBI'.
              -korrnum = '134000'.
            WHEN 'PINF'.
              " PINF before exposed objects
              -korrnum = '130000'.
            WHEN OTHERS.
              -korrnum = '100000'.
          ENDCASE.
        ENDLOOP.
    
        IF iv_skip_ddic = abap_false.
          resolve_ddic( CHANGING ct_tadir = ct_tadir ).
        ENDIF.
        resolve_packages( CHANGING ct_tadir = ct_tadir ).
    
        SORT ct_tadir BY korrnum ASCENDING.
    
      ENDMETHOD.
    
      METHOD resolve_ddic.
    * this will make sure the deletion sequence of structures/tables work
    * in case they have dependencies with .INCLUDE
    
        TYPES: BEGIN OF ty_edge,
                 from TYPE ty_item,
                 to   TYPE ty_item,
               END OF ty_edge.
    
        DATA: lt_nodes        TYPE TABLE OF ty_item,
              lt_edges        TYPE TABLE OF ty_edge,
              lt_findstrings  TYPE TABLE OF rsfind,
              lv_plus         TYPE i VALUE 1,
              lv_find_obj_cls TYPE euobj-id,
              lv_index        TYPE i,
              lv_before       TYPE i,
              lt_founds       TYPE TABLE OF rsfindlst,
              lt_scope        TYPE STANDARD TABLE OF seu_obj,
              lt_dependency   TYPE ty_dedenpencies.
    
        FIELD-SYMBOLS:       TYPE zif_abapgit_definitions=>ty_tadir,
                             TYPE ty_dependency,
                        TYPE zif_abapgit_definitions=>ty_tadir,
                                  LIKE LINE OF ct_tadir,
                                   LIKE LINE OF lt_edges,
                                  LIKE LINE OF lt_founds,
                                   LIKE LINE OF lt_nodes.
    
        " build nodes
        LOOP AT ct_tadir ASSIGNING 
            WHERE object = 'TABL'
            OR object = 'VIEW'
            OR object = 'TTYP'.
          APPEND INITIAL LINE TO lt_nodes ASSIGNING .
          -obj_name = -obj_name.
          -obj_type = -object.
        ENDLOOP.
    
        APPEND 'TABL' TO lt_scope.
        APPEND 'VIEW' TO lt_scope.
        APPEND 'STRU' TO lt_scope.
        APPEND 'TTYP' TO lt_scope.
    
        " build edges
        LOOP AT lt_nodes ASSIGNING .
    
          CLEAR lt_findstrings.
          APPEND -obj_name TO lt_findstrings.
          lv_find_obj_cls = -obj_type.
    
          CALL FUNCTION 'RS_EU_CROSSREF'
            EXPORTING
              i_find_obj_cls           = lv_find_obj_cls
            TABLES
              i_findstrings            = lt_findstrings
              o_founds                 = lt_founds
              i_scope_object_cls       = lt_scope
            EXCEPTIONS
              not_executed             = 1
              not_found                = 2
              illegal_object           = 3
              no_cross_for_this_object = 4
              batch                    = 5
              batchjob_error           = 6
              wrong_type               = 7
              object_not_exist         = 8
              OTHERS                   = 9.
          IF sy-subrc <> 0.
            CONTINUE.
          ENDIF.
    
          LOOP AT lt_founds ASSIGNING .
            APPEND INITIAL LINE TO lt_edges ASSIGNING .
            -from = .
    
            -to-obj_name   = -object.
            CASE -object_cls.
              WHEN 'DS'
                  OR 'DT'.
                -to-obj_type = 'TABL'.
              WHEN 'DV'.
                -to-obj_type = 'VIEW'.
              WHEN 'DA'.
                -to-obj_type = 'TTYP'.
              WHEN OTHERS.
                zcx_abapgit_exception=>raise( 'resolve_ddic, unknown object_cls' ).
            ENDCASE.
          ENDLOOP.
    
        ENDLOOP.
    
        " build DDLS edges
        SORT ct_tadir. "binary search
        LOOP AT ct_tadir ASSIGNING 
                         WHERE object = 'DDLS'.
    
          CLEAR: lt_dependency.
    
          APPEND INITIAL LINE TO lt_nodes ASSIGNING .
          -obj_name = -obj_name.
          -obj_type = -object.
    
          lt_dependency = get_ddls_dependencies( -obj_name ).
    
          LOOP AT lt_dependency ASSIGNING 
                                WHERE deptyp = 'DDLS'
                                AND refname = -obj_name.
    
            READ TABLE ct_tadir ASSIGNING 
                                WITH KEY pgmid    = 'R3TR'
                                         object   = 'DDLS'
                                         obj_name = -depname
                                BINARY SEARCH.
            CHECK sy-subrc = 0.
    
            APPEND INITIAL LINE TO lt_edges ASSIGNING .
            -from = .
            -to-obj_name = -depname.
            -to-obj_type = 'DDLS'.
    
          ENDLOOP.
    
        ENDLOOP.
    
        DO.
          lv_before = lines( lt_nodes ).
          LOOP AT lt_nodes ASSIGNING .
            lv_index = sy-tabix.
            READ TABLE lt_edges WITH KEY
              from-obj_name = -obj_name
              from-obj_type = -obj_type
              TRANSPORTING NO FIELDS.
            IF sy-subrc <> 0.
              LOOP AT ct_tadir ASSIGNING 
                  WHERE obj_name = -obj_name
                  AND object = -obj_type.
                -korrnum = -korrnum + lv_plus.
                CONDENSE -korrnum.
              ENDLOOP.
              DELETE lt_edges
                WHERE to-obj_name = -obj_name
                AND to-obj_type = -obj_type.
              DELETE lt_nodes INDEX lv_index.
              EXIT. " make sure the sequence is fixed
            ENDIF.
          ENDLOOP.
          IF lv_before = lines( lt_nodes ).
            EXIT.
          ENDIF.
          lv_plus = lv_plus + 1.
        ENDDO.
    
      ENDMETHOD.
    
      METHOD resolve_packages.
    
        DATA: lt_subpackages TYPE zif_abapgit_sap_package=>ty_devclass_tt.
    
        FIELD-SYMBOLS:             LIKE LINE OF ct_tadir,
                              LIKE LINE OF lt_subpackages,
                        LIKE LINE OF ct_tadir.
    
        " List subpackage before corresponding superpackage
    
        LOOP AT ct_tadir ASSIGNING 
                         WHERE object = 'DEVC'.
    
          lt_subpackages = zcl_abapgit_factory=>get_sap_package( |{ -obj_name }| )->list_subpackages( ).
    
          LOOP AT lt_subpackages ASSIGNING .
    
            READ TABLE ct_tadir ASSIGNING 
                                WITH KEY object   = 'DEVC'
                                         obj_name = .
            IF sy-subrc = 0.
              -korrnum = condense( |{ -korrnum - 1 }| ).
            ENDIF.
    
          ENDLOOP.
    
        ENDLOOP.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_dot_abapgit IMPLEMENTATION.
    
      METHOD add_ignore.
    
        DATA: lv_name TYPE string.
    
        FIELD-SYMBOLS:  LIKE LINE OF ms_data-ignore.
    
        lv_name = iv_path && iv_filename.
    
        READ TABLE ms_data-ignore FROM lv_name TRANSPORTING NO FIELDS.
        IF sy-subrc = 0.
          RETURN.
        ENDIF.
    
        APPEND INITIAL LINE TO ms_data-ignore ASSIGNING .
         = lv_name.
    
      ENDMETHOD.
    
      METHOD build_default.
    
        DATA: ls_data TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit.
    
        ls_data-master_language = sy-langu.
        ls_data-starting_folder = '/src/'.
        ls_data-folder_logic    = zif_abapgit_dot_abapgit=>c_folder_logic-prefix.
    
        CREATE OBJECT ro_dot_abapgit
          EXPORTING
            is_data = ls_data.
    
      ENDMETHOD.
    
      METHOD constructor.
        ms_data = is_data.
      ENDMETHOD.
    
      METHOD deserialize.
    
        DATA: lv_xml  TYPE string,
              ls_data TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit.
    
        lv_xml = zcl_abapgit_convert=>xstring_to_string_utf8( iv_xstr ).
    
        ls_data = from_xml( lv_xml ).
    
        CREATE OBJECT ro_dot_abapgit
          EXPORTING
            is_data = ls_data.
    
      ENDMETHOD.
    
      METHOD determine_i18n_parameters.
    
        rs_i18n_params-main_language         = get_main_language( ).
        rs_i18n_params-use_lxe               = use_lxe( ).
        rs_i18n_params-main_language_only    = iv_main_language_only.
        rs_i18n_params-translation_languages = zcl_abapgit_lxe_texts=>get_translation_languages(
          iv_main_language  = get_main_language( )
          it_i18n_languages = get_i18n_languages( ) ).
    
      ENDMETHOD.
    
      METHOD from_xml.
    
        DATA: lv_xml TYPE string.
    
        lv_xml = iv_xml.
    
        CALL TRANSFORMATION id
          OPTIONS value_handling = 'accept_data_loss'
          SOURCE XML lv_xml
          RESULT data = rs_data.
    
    * downward compatibility
        IF rs_data-folder_logic IS INITIAL.
          rs_data-folder_logic = zif_abapgit_dot_abapgit=>c_folder_logic-prefix.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_abap_language_version.
        rv_abap_language_version = ms_data-abap_language_version.
      ENDMETHOD.
    
      METHOD get_data.
        rs_data = ms_data.
      ENDMETHOD.
    
      METHOD get_folder_logic.
        rv_logic = ms_data-folder_logic.
      ENDMETHOD.
    
      METHOD get_i18n_languages.
        rt_languages = ms_data-i18n_languages.
      ENDMETHOD.
    
      METHOD get_main_language.
        rv_language = ms_data-master_language.
        IF rv_language IS INITIAL.
          rv_language = sy-langu.
        ENDIF.
      ENDMETHOD.
    
      METHOD get_name.
        rv_name = ms_data-name.
      ENDMETHOD.
    
      METHOD get_objs_without_translation.
        rt_list = ms_data-without_translation.
      ENDMETHOD.
    
      METHOD get_original_system.
        rv_original_system = ms_data-original_system.
      ENDMETHOD.
    
      METHOD get_requirements.
        rt_requirements = ms_data-requirements.
      ENDMETHOD.
    
      METHOD get_signature.
    
        rs_signature-path     = zif_abapgit_definitions=>c_root_dir.
        rs_signature-filename = zif_abapgit_definitions=>c_dot_abapgit.
        rs_signature-sha1     = zcl_abapgit_hash=>sha1_blob( serialize( ) ).
    
      ENDMETHOD.
    
      METHOD get_starting_folder.
        rv_path = ms_data-starting_folder.
      ENDMETHOD.
    
      METHOD get_version_constant.
        rv_version_constant = ms_data-version_constant.
      ENDMETHOD.
    
      METHOD is_ignored.
    
        DATA: lv_name     TYPE string,
              lv_starting TYPE string,
              lv_dot      TYPE string,
              lv_ignore   TYPE string.
    
        lv_name = iv_path && iv_filename.
    
        CONCATENATE ms_data-starting_folder '*' INTO lv_starting.
    
        " Always allow .abapgit.xml and .apack-manifest.xml
        CONCATENATE '/' zif_abapgit_definitions=>c_dot_abapgit INTO lv_dot.
        IF lv_name = lv_dot.
          RETURN.
        ENDIF.
        CONCATENATE '/' zif_abapgit_apack_definitions=>c_dot_apack_manifest INTO lv_dot.
        IF lv_name = lv_dot.
          RETURN.
        ENDIF.
    
        " Ignore all files matching pattern in ignore list
        LOOP AT ms_data-ignore INTO lv_ignore.
          " # needs to be escaped since it's the escape character
          " and used as namespace separator in filenames, for example
          lv_ignore = replace(
            val  = lv_ignore
            sub  = '#'
            with = '##'
            occ  = 0 ).
          IF lv_name CP lv_ignore.
            rv_ignored = abap_true.
            RETURN.
          ENDIF.
        ENDLOOP.
    
        " Ignore all files outside of starting folder tree
        IF ms_data-starting_folder <> '/' AND NOT lv_name CP lv_starting.
          rv_ignored = abap_true.
        ENDIF.
    
        IF iv_path = zif_abapgit_data_config=>c_default_path.
          rv_ignored = abap_false.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD remove_ignore.
    
        DATA: lv_name TYPE string.
    
        lv_name = iv_path && iv_filename.
    
        DELETE TABLE ms_data-ignore FROM lv_name.
    
      ENDMETHOD.
    
      METHOD serialize.
    
        DATA lv_xml TYPE string.
    
        lv_xml = to_xml( ms_data ).
    
        rv_xstr = zcl_abapgit_convert=>string_to_xstring_utf8_bom( lv_xml ).
    
      ENDMETHOD.
    
      METHOD set_abap_language_version.
        ms_data-abap_language_version = iv_abap_language_version.
      ENDMETHOD.
    
      METHOD set_folder_logic.
        ms_data-folder_logic = iv_logic.
      ENDMETHOD.
    
      METHOD set_i18n_languages.
        ms_data-i18n_languages = it_languages.
      ENDMETHOD.
    
      METHOD set_name.
        ms_data-name = iv_name.
      ENDMETHOD.
    
      METHOD set_objs_without_translation.
        ms_data-without_translation = it_list.
      ENDMETHOD.
    
      METHOD set_original_system.
        ms_data-original_system = iv_original_system.
      ENDMETHOD.
    
      METHOD set_requirements.
        ms_data-requirements = it_requirements.
      ENDMETHOD.
    
      METHOD set_starting_folder.
        ms_data-starting_folder = iv_path.
      ENDMETHOD.
    
      METHOD set_version_constant.
        ms_data-version_constant = iv_version_constant.
      ENDMETHOD.
    
      METHOD to_file.
        rs_file-path     = zif_abapgit_definitions=>c_root_dir.
        rs_file-filename = zif_abapgit_definitions=>c_dot_abapgit.
        rs_file-data     = serialize( ).
        rs_file-sha1     = zcl_abapgit_hash=>sha1_blob( rs_file-data ).
      ENDMETHOD.
    
      METHOD to_xml.
    
        CALL TRANSFORMATION id
          OPTIONS initial_components = 'suppress'
          SOURCE data = is_data
          RESULT XML rv_xml.
    
        rv_xml = zcl_abapgit_xml_pretty=>print( rv_xml ).
    
        REPLACE FIRST OCCURRENCE
          OF REGEX '<\?xml version="1\.0" encoding="[\w-]+"\?>'
          IN rv_xml
          WITH '' ##REGEX_POSIX.
        ASSERT sy-subrc = 0.
    
      ENDMETHOD.
    
      METHOD use_lxe.
    
        IF iv_yes <> abap_undefined.
          ms_data-use_lxe = iv_yes.
        ENDIF.
    
        rv_yes = ms_data-use_lxe.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_ECATT_CONFIG_DOWNL IMPLEMENTATION.
    
      METHOD download.
    
        " Downport
    
        DATA: lv_partyp TYPE string.
    
        load_help = im_load_help.
        typ = im_object_type.
    
        TRY.
            cl_apl_ecatt_object=>show_object(
              EXPORTING
                im_obj_type = im_object_type
                im_name     = im_object_name
                im_version  = im_object_version
              IMPORTING
                re_object   = ecatt_object ).
          CATCH cx_ecatt INTO ex_ecatt.
            RETURN.
        ENDTRY.
    
        lv_partyp = cl_apl_ecatt_const=>params_type_par.
    
        set_attributes_to_template( ).
        ecatt_config ?= ecatt_object.
    
        CALL METHOD ('SET_ECATT_OBJECTS_TO_TEMPLATE'). " doesn't exist in 702
    
    * MS180406
        set_var_mode_to_dom( ).
    * ENDMS180406
        get_general_params_data( im_params = ecatt_config->params
                                 im_ptyp   = lv_partyp ).
        LOOP AT parm INTO wa_parm.
          set_general_params_data_to_dom( ).
          IF NOT wa_parm-val_type IS INITIAL.
            set_deep_stru_to_dom( ecatt_config->params ).
            set_deep_data_to_dom( im_params = ecatt_config->params
                                  im_pindex = wa_parm-pindex ).
          ENDIF.
        ENDLOOP.
    
        set_variants_to_dom( ecatt_config->params ).
    
        download_data( ).
    
      ENDMETHOD.
    
      METHOD download_data.
    
        " Downport
    
        mv_xml_stream = zcl_abapgit_ecatt_helper=>download_data( template_over_all ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_ecatt_download~get_xml_stream.
    
        rv_xml_stream = mv_xml_stream.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_ECATT_CONFIG_UPL IMPLEMENTATION.
    
      METHOD upload_data_from_stream.
    
        " Downport
        template_over_all = zcl_abapgit_ecatt_helper=>upload_data_from_stream( mv_external_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_ecatt_upload~set_stream_for_upload.
    
        " downport from CL_ABAPGIT_ECATT_DATA_UPLOAD SET_STREAM_FOR_UPLOAD
        mv_external_xml = iv_xml.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_ECATT_DATA_DOWNL IMPLEMENTATION.
    
      METHOD download.
    
        " Downport
    
        DATA: lv_partyp TYPE string.
    
        load_help = im_load_help.
    
        TRY.
            cl_apl_ecatt_object=>show_object(
              EXPORTING
                im_obj_type = im_object_type
                im_name     = im_object_name
                im_version  = im_object_version
              IMPORTING
                re_object   = ecatt_object ).
          CATCH cx_ecatt INTO ex_ecatt.
            RETURN.
        ENDTRY.
    
        typ = im_object_type.
    
        lv_partyp = cl_apl_ecatt_const=>params_type_par.
    
        ecatt_data ?= ecatt_object.
        set_attributes_to_template( ).
        get_general_params_data( im_params = ecatt_data->params
                                 im_ptyp   = lv_partyp ).
    
        LOOP AT parm INTO wa_parm.
          set_general_params_data_to_dom( ).
          IF NOT wa_parm-val_type IS INITIAL.
            set_deep_stru_to_dom( ecatt_data->params ).
            set_deep_data_to_dom( im_params = ecatt_data->params
                                  im_pindex = wa_parm-pindex ).
          ENDIF.
        ENDLOOP.
    
    * MS180406
        set_var_mode_to_dom( ).
    * ENDMS180406
        set_variants_to_dom( ecatt_data->params ).
    
        download_data( ).
    
      ENDMETHOD.
    
      METHOD download_data.
    
        " Downport
    
        mv_xml_stream = zcl_abapgit_ecatt_helper=>download_data( template_over_all ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_ecatt_download~get_xml_stream.
    
        rv_xml_stream = mv_xml_stream.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_ecatt_data_upload IMPLEMENTATION.
    
      METHOD upload.
        SET HANDLER on_ev_object_saved FOR ALL INSTANCES.
    
        ms_current_object-s_obj_type = ch_object-s_obj_type.
        ms_current_object-d_obj_name = ch_object-d_obj_name.
        ms_current_object-d_obj_ver = ch_object-d_obj_ver.
    
        TRY.
            super->upload( CHANGING ch_object = ch_object ).
            SET HANDLER on_ev_object_saved FOR ALL INSTANCES ACTIVATION abap_false.
          CLEANUP.
            SET HANDLER on_ev_object_saved FOR ALL INSTANCES ACTIVATION abap_false.
        ENDTRY.
    
        IF mx_ecatt_apl IS BOUND.
          raise_upload_exception( previous = mx_ecatt_apl ).
        ENDIF.
      ENDMETHOD.
    
      METHOD upload_data_from_stream.
    
        " Downport
        template_over_all = zcl_abapgit_ecatt_helper=>upload_data_from_stream( mv_external_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_ecatt_upload~set_stream_for_upload.
    
        " downport from CL_ABAPGIT_ECATT_DATA_UPLOAD SET_STREAM_FOR_UPLOAD
        mv_external_xml = iv_xml.
    
      ENDMETHOD.
    
      METHOD on_ev_object_saved.
        DATA lo_ecatt_td TYPE REF TO cl_apl_ecatt_test_data.
    
        " Trickery to remove any local variants that do not exist on the remote on pull.
    
        SET HANDLER on_ev_object_saved FOR ALL INSTANCES ACTIVATION abap_false.
    
        TRY.
            IF ex_ecatt_object->object_type <> ms_current_object-s_obj_type OR
               ex_ecatt_object->object_name <> ms_current_object-d_obj_name OR
               ex_ecatt_object->object_version <> ms_current_object-d_obj_ver.
              CREATE OBJECT mx_ecatt_apl
                EXPORTING
                  textid    = cx_ecatt_apl=>any_text
                  free_text = 'Unexpected object in save sequence'.
              RETURN.
            ENDIF.
    
            lo_ecatt_td ?= ex_ecatt_object.
            lo_ecatt_td->params->delete_variants( '*' ).
            TRY.
                CALL METHOD ('GET_VARIANTS_FROM_DOM_NEW')
                  EXPORTING
                    im_params = lo_ecatt_td->params.
              CATCH cx_sy_dyn_call_error.
                get_variants_from_dom( lo_ecatt_td->params ).
            ENDTRY.
            lo_ecatt_td->save( ).
          CATCH cx_ecatt_apl INTO mx_ecatt_apl.
            RETURN.
        ENDTRY.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_ecatt_helper IMPLEMENTATION.
    
      METHOD build_xml_of_object.
    
        " downport of CL_APL_ECATT_DOWNLOAD=>BUILD_XML_OF_OBJECT
    
        DATA: lo_load_help_dummy TYPE REF TO cl_apl_ecatt_load_help,
              lx_ecatt           TYPE REF TO cx_ecatt_apl,
              lv_text            TYPE string,
              li_download        TYPE REF TO zif_abapgit_ecatt_download.
    
        "download method will create the xml stream
        "note: it's the redefined download( ) of each object type specific download, which is called
        TRY.
            CREATE OBJECT lo_load_help_dummy
              EXPORTING
                im_maintain_function = ''.
    
            io_download->download( im_object_name    = iv_object_name
                                   im_object_version = iv_object_version
                                   im_object_type    = iv_object_type
                                   im_load_help      = lo_load_help_dummy ).
    
          CATCH cx_ecatt_apl INTO lx_ecatt.
            lv_text = lx_ecatt->get_text( ).
            zcx_abapgit_exception=>raise( lv_text ).
            " note, exception cx_ecatt_ui_attachment doesn't exist in 702
          CATCH cx_ecatt ##NO_HANDLER.
            "will never be raised from download, when called with mv_generate_xml_no_download = 'X'.
        ENDTRY.
    
        li_download ?= io_download.
    
        rv_xml_stream = li_download->get_xml_stream( ).
    
      ENDMETHOD.
    
      METHOD download_data.
    
        DATA:
          lo_xml TYPE REF TO cl_apl_ecatt_xml.
    
        TRY.
            CALL METHOD cl_apl_ecatt_xml=>('CREATE') " doesn't exist in 702
              EXPORTING
                im_type = c_xml
              RECEIVING
                re_xml  = lo_xml.
    
            lo_xml->set_attributes( im_dom = ii_template_over_all ).
    
            lo_xml->get_attributes( IMPORTING ex_xml = rv_xml_stream ).
    
          CATCH cx_ecatt_apl_xml.
            RETURN.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD upload_data_from_stream.
    
        DATA:
          lo_xml           TYPE REF TO cl_apl_ecatt_xml,
          lv_xstr          TYPE xstring,
          li_nc_xmlref_typ TYPE REF TO if_ixml_node_collection,
          li_n_xmlref_typ  TYPE REF TO if_ixml_node,
          lv_index         TYPE i VALUE 0,
          lv_count         TYPE i.
    
        lv_xstr = iv_xml_stream.
    
        CALL METHOD cl_apl_ecatt_xml=>('CREATE') " doesn't exist in 702
          EXPORTING
            im_type = c_xml
          RECEIVING
            re_xml  = lo_xml.
    
    * whitespace stripping needs a namespace
    * remove white spaces only at the time of upload
        lo_xml->stream_to_dom( im_xstream            = lv_xstr
                               im_ignore_white_space = 'X'
                               im_uri                = cl_apl_xml_const=>schema_uri ).
    
        lo_xml->get_attributes( IMPORTING ex_dom = ri_template_over_all ).
    
    * MD: Workaround, because nodes starting with "XML" are not allowed
        li_nc_xmlref_typ ?= ri_template_over_all->get_elements_by_tag_name_ns( 'XMLREF_TYP' ).
        CALL METHOD li_nc_xmlref_typ->('GET_LENGTH')  " downport
          RECEIVING
            rval = lv_count.
    
        WHILE lv_index < lv_count.
          li_n_xmlref_typ = li_nc_xmlref_typ->get_item( lv_index ).
          li_n_xmlref_typ->set_name( 'X-MLREF_TYP' ).
          lv_index = lv_index + 1.
        ENDWHILE.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_ecatt_script_downl IMPLEMENTATION.
    
      METHOD download.
    
        " Downport
    
        load_help = im_load_help.
        typ = im_object_type.
    
        TRY.
            cl_apl_ecatt_object=>show_object(
              EXPORTING
                im_obj_type = im_object_type
                im_name     = im_object_name
                im_version  = im_object_version
              IMPORTING
                re_object   = ecatt_object ).
          CATCH cx_ecatt INTO ex_ecatt.
            RETURN.
        ENDTRY.
    
        toolname = ecatt_object->attrib->get_tool_name( ).
        set_attributes_to_template( ).
    
        IF toolname = cl_apl_ecatt_const=>toolname_ecatt.
    
          ecatt_script ?= ecatt_object.
    
          set_script_to_template( ).
    
          TRY.
              get_general_params_data( ecatt_script->params ).
            CATCH cx_ecatt_apl.                              "#EC NO_HANDLER
    *         proceed with download and report errors later
          ENDTRY.
    
          LOOP AT parm INTO wa_parm.
            TRY.
                IF wa_parm-value = ''.
                  CLEAR wa_parm-value.
                ENDIF.
                set_general_params_data_to_dom( ).
                IF NOT wa_parm-pstruc_typ IS INITIAL.
                  set_deep_stru_to_dom( ecatt_script->params ).
                  set_deep_data_to_dom( ecatt_script->params ).
                  IF wa_parm-xmlref_typ = cl_apl_ecatt_const=>ref_type_c_tcd.
                    set_control_data_for_tcd( is_param  = wa_parm
                                              io_params = ecatt_script->params ).
    
                  ENDIF.
                ENDIF.
              CATCH cx_ecatt_apl.                            "#EC NO_HANDLER
    *         proceed with download and report errors later
            ENDTRY.
          ENDLOOP.
    
        ELSE.
    
          set_blob_to_template( ).
          set_artmp_to_template( ).
    
        ENDIF.
    
        download_data( ).
    
      ENDMETHOD.
    
      METHOD download_data.
    
        " Downport
    
        mv_xml_stream = zcl_abapgit_ecatt_helper=>download_data( template_over_all ).
    
      ENDMETHOD.
    
      METHOD escape_control_data.
    
        " Downport
    
        DATA: li_iter     TYPE REF TO if_ixml_node_iterator,
              li_textit   TYPE REF TO if_ixml_node_iterator,
              li_abapctrl TYPE REF TO if_ixml_node_collection,
              li_text     TYPE REF TO if_ixml_text,
              li_filter   TYPE REF TO if_ixml_node_filter,
              li_list     TYPE REF TO if_ixml_node_list,
              lv_value    TYPE etdom_name,
              li_vars     TYPE REF TO if_ixml_element,
              li_elem     TYPE REF TO if_ixml_element.
    
        li_vars = ii_element->find_from_name_ns( iv_tabname ).
        li_filter = ii_element->create_filter_node_type( if_ixml_node=>co_node_text ).
        IF li_vars IS NOT INITIAL.
          li_abapctrl = ii_element->get_elements_by_tag_name_ns( iv_node ).
    
    * just for debugging
          li_iter = li_abapctrl->create_iterator( ).
          li_elem ?= li_iter->get_next( ).
          WHILE li_elem IS NOT INITIAL.
            li_list = li_elem->get_children( ).
    
            li_textit = li_list->create_rev_iterator_filtered( li_filter ).
            li_text ?= li_textit->get_next( ).
            IF li_text IS NOT INITIAL.
              lv_value = li_text->get_data( ).
              IF lv_value(1) = cl_abap_char_utilities=>minchar.
                REPLACE SECTION OFFSET 0 LENGTH 1 OF lv_value WITH space.
                li_text->set_value( value = lv_value ).
              ENDIF.
            ENDIF.
            CLEAR: li_textit, li_list, li_elem, lv_value.
            li_elem ?= li_iter->get_next( ).
          ENDWHILE.
          CLEAR: li_abapctrl, li_elem, li_iter.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD set_artmp_to_template.
    
        " Downport
    
        DATA: li_artmp_node   TYPE REF TO if_ixml_element,
              lv_rc           TYPE sy-subrc,
              lv_text         TYPE string,
              lv_rc_args_tmpl TYPE i,
              lv_errmsg       TYPE string.
    
        li_artmp_node = template_over_all->create_simple_element(
                          name   = 'ECET_ARTMP'
                          parent = root_node ).
    
        ecatt_extprog->get_args_tmpl(
          IMPORTING
            ex_xml_arg_tmpl = lv_text
            ex_rc           = lv_rc_args_tmpl
            ex_errmsg       = lv_errmsg ).
    
        IF li_artmp_node IS INITIAL OR lv_rc_args_tmpl > 0.
          raise_download_exception(
              textid        = cx_ecatt_apl_util=>download_processing
              previous      = ex_ecatt
              called_method = 'CL_APL_ECATT_SCRIPT_DOWNLOAD->SET_ARTMP_TO_TEMPLATE'
              free_text     = lv_errmsg ).
        ENDIF.
    
        lv_rc = li_artmp_node->set_value( lv_text ).
        IF lv_rc <> 0.
          raise_download_exception(
                textid        = cx_ecatt_apl_util=>download_processing
                previous      = ex_ecatt
                called_method = 'CL_APL_ECATT_SCRIPT_DOWNLOAD->SET_ARTMP_TO_TEMPLATE' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD set_blob_to_template.
    
        " Downport
    
        DATA: li_blob_node TYPE REF TO if_ixml_element,
              lv_rc        TYPE sy-subrc,
              lv_text      TYPE string.
    
        li_blob_node = template_over_all->create_simple_element(
                      name   = 'ECET_BLOBS'
                      parent = root_node ).
    
        IF li_blob_node IS INITIAL.
          raise_download_exception(
                textid        = cx_ecatt_apl_util=>download_processing
                previous      = ex_ecatt
                called_method = 'CL_APL_ECATT_SCRIPT_DOWNLOAD->SET_BLOB_TO_TEMPLATE' ).
        ENDIF.
    
        ecatt_extprog->get_blob(
          EXPORTING
            im_whole_data = 1
          IMPORTING
            ex_xml_blob   = lv_text ).
    
        lv_rc = li_blob_node->set_value( lv_text ).
        IF lv_rc <> 0.
          raise_download_exception(
                textid        = cx_ecatt_apl_util=>download_processing
                previous      = ex_ecatt
                called_method = 'CL_APL_ECATT_SCRIPT_DOWNLOAD->SET_BLOB_TO_TEMPLATE' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD set_control_data_for_tcd.
    
        " Downport
    
        DATA: lt_params TYPE ettcd_params_tabtype,
              lt_verbs  TYPE ettcd_verbs_tabtype,
              lt_vars   TYPE ettcd_vars_tabtype,
              lt_dp_tab TYPE ettcd_dp_tab_tabtype,
              lt_dp_for TYPE ettcd_dp_for_tabtype,
              lt_dp_pro TYPE ettcd_dp_pro_tabtype,
              lt_dp_fld TYPE ettcd_dp_fld_tabtype,
              lt_svars  TYPE ettcd_svars_tabtype.
    
        DATA: li_element   TYPE REF TO if_ixml_element,
              li_deep_tcd  TYPE REF TO if_ixml_element,
              lv_rc        TYPE sy-subrc,
              lv_name      TYPE string,
              lv_parname   TYPE string,
              lo_pval_xml  TYPE REF TO cl_apl_ecatt_xml_data,
              lo_ctrl_tabs TYPE REF TO cl_apl_ecatt_control_tables.
    
        FIELD-SYMBOLS:  TYPE STANDARD TABLE.
    
        IF is_param-xmlref_typ <> cl_apl_ecatt_const=>ref_type_c_tcd OR io_params IS INITIAL.
          RETURN.
        ENDIF.
    
        lv_parname = is_param-pname.
    
        io_params->get_param_value(     "TCD command interface
          EXPORTING
            im_var_id   = cl_apl_ecatt_const=>varid_default_val
            im_pname    = lv_parname
            im_pindex   = is_param-pindex
          IMPORTING
            ex_pval_xml = lo_pval_xml ).
    
        lo_ctrl_tabs = lo_pval_xml->get_control_tables_ref( ).
        IF lo_ctrl_tabs IS INITIAL.
          RETURN.
        ENDIF.
    
        lo_ctrl_tabs->get_control_tables(          "Read 8 control tables
          IMPORTING
            ex_params = lt_params
            ex_verbs  = lt_verbs
            ex_vars   = lt_vars
            ex_dp_tab = lt_dp_tab
            ex_dp_for = lt_dp_for
            ex_dp_pro = lt_dp_pro
            ex_dp_fld = lt_dp_fld
            ex_svars  = lt_svars ).
    
        IF lt_params IS INITIAL OR
           lt_verbs  IS INITIAL OR
           lt_vars   IS INITIAL OR
           lt_dp_tab IS INITIAL OR
           lt_dp_for IS INITIAL OR
           lt_dp_pro IS INITIAL OR
           lt_dp_fld IS INITIAL OR
           lt_svars  IS INITIAL.
    
          RETURN.
        ENDIF.
    
        li_deep_tcd = template_over_all->create_simple_element_ns(
                        name   = cl_apl_xml_const=>upl_tcd_node
                        parent = ap_current_param ).
    
        IF li_deep_tcd IS INITIAL.
          raise_download_exception(
                textid   = cx_ecatt_apl_util=>download_processing
                previous = ex_ecatt ).
        ENDIF.
    
        DO 8 TIMES.                                "Loop at 8 control tables
          CASE sy-index.
            WHEN 1.
              lv_name = 'ETTCD_PARAMS_TABTYPE'.
              ASSIGN lt_params TO .
            WHEN 2.
              lv_name = 'ETTCD_VERBS_TABTYPE'.
              ASSIGN lt_verbs TO .
            WHEN 3.
              lv_name = 'ETTCD_VARS_TABTYPE'.
              ASSIGN lt_vars TO .
            WHEN 4.
              lv_name = 'ETTCD_DP_TAB_TABTYPE'.
              ASSIGN lt_dp_tab TO .
            WHEN 5.
              lv_name = 'ETTCD_DP_FOR_TABTYPE'.
              ASSIGN lt_dp_for TO .
            WHEN 6.
              lv_name = 'ETTCD_DP_PRO_TABTYPE'.
              ASSIGN lt_dp_pro TO .
            WHEN 7.
              lv_name = 'ETTCD_DP_FLD_TABTYPE'.
              ASSIGN lt_dp_fld TO .
            WHEN 8.
              lv_name = 'ETTCD_SVARS_TABTYPE'.
              ASSIGN lt_svars TO .
          ENDCASE.
    
          CALL FUNCTION 'SDIXML_DATA_TO_DOM'       "Generate branch
            EXPORTING
              name         = lv_name
              dataobject   = 
            IMPORTING
              data_as_dom  = li_element
            EXCEPTIONS
              illegal_name = 1
              OTHERS       = 2.
    
          IF sy-subrc <> 0.
            raise_download_exception(
                  textid   = cx_ecatt_apl_util=>download_processing
                  previous = ex_ecatt ).
          ENDIF.
    
    * Hang a branch in the main tree
          lv_rc = li_deep_tcd->append_child( li_element ).
    
          IF lv_rc <> 0.
            raise_download_exception(
                  textid   = cx_ecatt_apl_util=>download_processing
                  previous = ex_ecatt ).
          ENDIF.
          FREE li_element.
          UNASSIGN .
        ENDDO.
    
        escape_control_data( ii_element = li_deep_tcd
          iv_tabname = 'ETTCD_VARS_TABTYPE'
          iv_node    = 'CB_INDEX' ).
    
        escape_control_data(
          ii_element = li_deep_tcd
          iv_tabname = 'ETTCD_VERBS_TABTYPE'
          iv_node    = 'NAME' ).
    
        FREE: lt_dp_tab, lt_dp_for, lt_dp_fld, lt_svars,
              lt_params, lt_vars,   lt_dp_pro, lt_verbs.
    
      ENDMETHOD.
    
      METHOD set_script_to_template.
    
        " Downport
    
        DATA:
          lt_text    TYPE etxml_line_tabtype,
          li_element TYPE REF TO if_ixml_element,
          lv_rc      TYPE sy-subrc.
    
        ecatt_script->get_script_text( CHANGING scripttext = lt_text ).
    
        mi_script_node = template_over_all->create_simple_element(
                            name = 'SCRIPT'
                            parent = root_node ).
    
        IF mi_script_node IS INITIAL.
          raise_download_exception(
                textid        = cx_ecatt_apl_util=>download_processing
                previous      = ex_ecatt
                called_method = 'CL_APL_ECATT_SCRIPT_DOWNLOAD->SET_SCRIPT_TO_TEMPLATE' ).
        ENDIF.
    
        CALL FUNCTION 'SDIXML_DATA_TO_DOM'
          EXPORTING
            name         = 'ETXML_LINE_TABTYPE'
            dataobject   = lt_text
          IMPORTING
            data_as_dom  = li_element
          CHANGING
            document     = template_over_all
          EXCEPTIONS
            illegal_name = 1
            OTHERS       = 2.
        IF sy-subrc <> 0.
          raise_download_exception(
                textid        = cx_ecatt_apl_util=>download_processing
                previous      = ex_ecatt
                called_method = 'CL_APL_ECATT_SCRIPT_DOWNLOAD->SET_SCRIPT_TO_TEMPLATE' ).
    
        ENDIF.
    
        lv_rc = mi_script_node->append_child( li_element ).
        IF lv_rc <> 0.
          raise_download_exception(
                textid        = cx_ecatt_apl_util=>download_processing
                previous      = ex_ecatt
                called_method = 'CL_APL_ECATT_SCRIPT_DOWNLOAD->SET_SCRIPT_TO_TEMPLATE' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_ecatt_download~get_xml_stream.
    
        rv_xml_stream = mv_xml_stream.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_ECATT_SCRIPT_UPL IMPLEMENTATION.
    
      METHOD upload_data_from_stream.
    
        " Downport
        template_over_all = zcl_abapgit_ecatt_helper=>upload_data_from_stream( mv_external_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_ecatt_upload~set_stream_for_upload.
    
        " downport from CL_ABAPGIT_ECATT_DATA_UPLOAD SET_STREAM_FOR_UPLOAD
        mv_external_xml = iv_xml.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_ecatt_sp_download IMPLEMENTATION.
    
      METHOD download.
    
        " We inherit from CL_APL_ECATT_DOWNLOAD because CL_APL_ECATT_SP_DOWNLOAD
        " doesn't exist in 702
    
        " Downport
    
        load_help = im_load_help.
        typ = im_object_type.
    
        TRY.
            cl_apl_ecatt_object=>show_object(
              EXPORTING
                im_obj_type = im_object_type
                im_name     = im_object_name
                im_version  = im_object_version
              IMPORTING
                re_object   = ecatt_object ).
          CATCH cx_ecatt INTO ex_ecatt.
            RETURN.
        ENDTRY.
    
        set_attributes_to_template( ).
    
        set_sp_data_to_template( ).
    
        download_data( ).
    
      ENDMETHOD.
    
      METHOD download_data.
    
        " Downport
    
        mv_xml_stream = zcl_abapgit_ecatt_helper=>download_data( template_over_all ).
    
      ENDMETHOD.
    
      METHOD set_sp_data_to_template.
    
        " downport
    
        DATA: li_dom                     TYPE REF TO if_ixml_document,
              li_start_profile_data_node TYPE REF TO if_ixml_element,
              li_element                 TYPE REF TO if_ixml_element,
              lv_sp_xml                  TYPE etxml_line_str,
              lo_ecatt_sp                TYPE REF TO object.
    
        FIELD-SYMBOLS:  TYPE data.
    
        li_start_profile_data_node = template_over_all->create_simple_element(
                                       name = 'START_PROFILE'
                                       parent = root_node ).
    
        ASSIGN ('ECATT_OBJECT') TO .
        ASSERT sy-subrc = 0.
    
        lo_ecatt_sp = .
    
        TRY.
            CALL METHOD lo_ecatt_sp->('GET_SP_ATTRIBUTES')
              IMPORTING
                e_sp_xml = lv_sp_xml.
          CATCH cx_ecatt_apl ##NO_HANDLER.
        ENDTRY.
    
        CALL FUNCTION 'SDIXML_XML_TO_DOM'
          EXPORTING
            xml      = lv_sp_xml
          IMPORTING
            document = li_dom.
    
        li_element = li_dom->get_root_element( ).
        li_start_profile_data_node->append_child( li_element ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_ecatt_download~get_xml_stream.
    
        rv_xml_stream = mv_xml_stream.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_ecatt_sp_upload IMPLEMENTATION.
    
      METHOD get_ecatt_sp.
    
        " downport
    
        DATA: li_ixml               TYPE REF TO if_ixml,
              li_section            TYPE REF TO if_ixml_element,
              li_dom                TYPE REF TO if_ixml_document,
              li_root               TYPE REF TO if_ixml_node,
              lv_start_profile      TYPE etxml_line_str,
              lv_exception_occurred TYPE etonoff,
              lo_ecatt_sp           TYPE REF TO object.
    
        FIELD-SYMBOLS:  TYPE any.
    
        TRY.
            li_section = template_over_all->find_from_name_ns( 'START_PROFILE' ).
    
            IF NOT li_section IS INITIAL.
              li_ixml = cl_ixml=>create( ).
              li_dom  = li_ixml->create_document( ).
              li_root ?= li_section->clone( ).
              li_dom->append_child( li_root ).
              CALL FUNCTION 'SDIXML_DOM_TO_XML'
                EXPORTING
                  document      = li_dom
                IMPORTING
                  xml_as_string = lv_start_profile.
    
              ASSIGN ('ECATT_OBJECT') TO .
              ASSERT sy-subrc = 0.
    
              lo_ecatt_sp = .
    
              CALL METHOD lo_ecatt_sp->('SET_SP_ATTRIBUTES')
                EXPORTING
                  i_sp_xml = lv_start_profile.
    
            ENDIF.
          CATCH cx_ecatt_apl.
            lv_exception_occurred = 'X'.
        ENDTRY.
    
        IF lv_exception_occurred = 'X'.
          raise_upload_exception( previous = exception_to_raise ).
        ENDIF.
      ENDMETHOD.
    
      METHOD upload.
    
        " We inherit from CL_APL_ECATT_UPLOAD because CL_APL_ECATT_SP_UPLOAD
        " doesn't exist in 702
    
        " Downport
    
        "26.03.2013
    
        DATA: lx_ecatt    TYPE REF TO cx_ecatt_apl,
              lv_exists   TYPE etonoff,
              lv_exc_occ  TYPE etonoff,
              ls_tadir    TYPE tadir,
              lo_ecatt_sp TYPE REF TO object.
    
        FIELD-SYMBOLS:  TYPE any,
                           TYPE data,
                           TYPE data.
    
        TRY.
            ch_object-i_devclass = ch_object-d_devclass.
    
            ASSIGN COMPONENT 'D_AKH' OF STRUCTURE ch_object
                   TO . " doesn't exist in 702
            ASSIGN COMPONENT 'I_AKH' OF STRUCTURE ch_object
                   TO . " doesn't exist in 702
            IF  IS ASSIGNED AND  IS ASSIGNED.
               = .
            ENDIF.
    
            super->upload( CHANGING ch_object = ch_object ).
    
            upload_data_from_stream( ch_object-filename ).
    
          CATCH cx_ecatt_apl INTO lx_ecatt.
            IF template_over_all IS INITIAL.
              RAISE EXCEPTION lx_ecatt.
            ELSE.
              lv_exc_occ = 'X'.
            ENDIF.
        ENDTRY.
    
        TRY.
            CALL METHOD ('GET_ATTRIBUTES_FROM_DOM_NEW') " doesn't exist in 720
              CHANGING
                ch_object = ch_object.
          CATCH cx_ecatt_apl INTO lx_ecatt.
            lv_exc_occ = 'X'.
        ENDTRY.
    
        ASSIGN ecatt_object TO .
    
        lo_ecatt_sp = .
    
        TRY.
            get_ecatt_sp( ).
          CATCH cx_ecatt_apl INTO lx_ecatt.
            lv_exc_occ = 'X'.
        ENDTRY.
    
        TRY.
            lv_exists = cl_apl_ecatt_object=>existence_check_object(
                          im_name               = ch_object-d_obj_name
                          im_version            = ch_object-d_obj_ver
                          im_obj_type           = ch_object-s_obj_type
                          im_exists_any_version = 'X' ).
    
            IF lv_exists = space.
              CALL METHOD lo_ecatt_sp->('SET_TADIR_FOR_NEW_OBJECT')
                EXPORTING
                  im_tadir_for_new_object = tadir_preset.
            ENDIF.
          CATCH cx_ecatt.
            CLEAR lv_exists.
        ENDTRY.
    
        TRY.
            CALL METHOD lo_ecatt_sp->('SAVE')
              EXPORTING
                im_do_commit = 'X'.
          CATCH cx_ecatt_apl INTO lx_ecatt.
            lv_exc_occ = 'X'.
        ENDTRY.
        " Releasing enqueue after uploading
        TRY.
            ecatt_object->close_object( im_suppress_events = 'X' ).
          CATCH cx_ecatt_apl INTO lx_ecatt ##NO_HANDLER.
        ENDTRY.
    
    *     get devclass from existing object
        TRY.
            cl_apl_ecatt_object=>get_tadir_entry(
              EXPORTING im_obj_name = ch_object-d_obj_name
                        im_obj_type = ch_object-s_obj_type
              IMPORTING ex_tadir = ls_tadir ).
    
            ch_object-d_devclass = ls_tadir-devclass.
    
          CATCH cx_ecatt.
            CLEAR ls_tadir.
        ENDTRY.
        IF lv_exc_occ = 'X'.
          raise_upload_exception( previous = lx_ecatt ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD upload_data_from_stream.
    
        " Downport
        template_over_all = zcl_abapgit_ecatt_helper=>upload_data_from_stream( mv_external_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_ecatt_upload~set_stream_for_upload.
    
        " downport from CL_APL_ECATT_START_PROFIL SET_STREAM_FOR_UPLOAD
        mv_external_xml = iv_xml.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_ecatt_system_downl IMPLEMENTATION.
    
      METHOD download.
    
        " Downport
    
        load_help = im_load_help.
        typ = im_object_type.
    
        TRY.
            cl_apl_ecatt_object=>show_object(
              EXPORTING
                im_obj_type = im_object_type
                im_name     = im_object_name
                im_version  = im_object_version
              IMPORTING
                re_object   = ecatt_object ).
          CATCH cx_ecatt INTO ex_ecatt.
            RETURN.
        ENDTRY.
    
        set_attributes_to_template( ).
        set_systems_data_to_template( ).
        download_data( ).
    
      ENDMETHOD.
    
      METHOD download_data.
    
        " Downport
    
        mv_xml_stream = zcl_abapgit_ecatt_helper=>download_data( template_over_all ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_ecatt_download~get_xml_stream.
    
        rv_xml_stream = mv_xml_stream.
    
      ENDMETHOD.
    
      METHOD set_systems_data_to_template.
    
        DATA: lo_ecatt_systems TYPE REF TO cl_apl_ecatt_system_data,
              lt_sys_data      TYPE etsys_def_tabtype,
              ls_sys_data      TYPE etsys_def,
              li_item          TYPE REF TO if_ixml_element,
              li_sysdata_node  TYPE REF TO if_ixml_element.
    
        lo_ecatt_systems ?= ecatt_object.
        lt_sys_data = lo_ecatt_systems->get_system_data( ).
    
        li_sysdata_node = template_over_all->create_simple_element(
                            name = 'SYSTEMS_DATA'
                            parent = root_node ).
    
        etpar_node = template_over_all->create_simple_element(
                       name = 'ETSYS_DEF'
                       parent = li_sysdata_node ).
    
        LOOP AT lt_sys_data INTO ls_sys_data.
    
          CLEAR: ls_sys_data-sys_desc, ls_sys_data-instance.
    
          CALL FUNCTION 'SDIXML_DATA_TO_DOM'
            EXPORTING
              name         = 'item'
              dataobject   = ls_sys_data
            IMPORTING
              data_as_dom  = li_item
            CHANGING
              document     = template_over_all
            EXCEPTIONS
              illegal_name = 1
              OTHERS       = 2.
          ASSERT sy-subrc = 0.
    
          etpar_node->append_child( li_item ).
    
        ENDLOOP.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_ECATT_SYSTEM_UPL IMPLEMENTATION.
    
      METHOD upload_data_from_stream.
    
        " Downport
        template_over_all = zcl_abapgit_ecatt_helper=>upload_data_from_stream( mv_external_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_ecatt_upload~set_stream_for_upload.
    
        " downport from CL_APL_ECATT_SYSTEMS_UPLOAD SET_STREAM_FOR_UPLOAD
        mv_external_xml = iv_xml.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_ECATT_VAL_OBJ_DOWN IMPLEMENTATION.
    
      METHOD download.
    
        " We inherit from CL_APL_ECATT_DOWNLOAD because CL_APL_ECATT_VO_DOWNLOAD
        " doesn't exist in 702
    
        " Downport
    
        DATA: lv_partyp   TYPE string,
              lo_ecatt_vo TYPE REF TO object.
    
        FIELD-SYMBOLS:  TYPE any,
                          TYPE REF TO cl_apl_ecatt_params.
    
        load_help = im_load_help.
        typ = im_object_type.
    
        TRY.
            cl_apl_ecatt_object=>show_object(
              EXPORTING
                im_obj_type = im_object_type
                im_name     = im_object_name
                im_version  = im_object_version
              IMPORTING
                re_object   = ecatt_object ).
          CATCH cx_ecatt INTO ex_ecatt.
            RETURN.
        ENDTRY.
    
        lv_partyp = cl_apl_ecatt_const=>params_type_par.
    
        ASSIGN ('ECATT_OBJECT') TO .
        ASSERT sy-subrc = 0.
    
        lo_ecatt_vo = .
    
        set_attributes_to_template( ).
        set_ecatt_impl_detail( ).
        set_ecatt_flags( ).
        set_business_msgs( ).
    
        ASSIGN lo_ecatt_vo->('PARAMS')
               TO .
        ASSERT sy-subrc = 0.
    
        get_general_params_data( im_params = 
                                 im_ptyp   = lv_partyp ).
        LOOP AT parm INTO wa_parm.
          set_general_params_data_to_dom( ).
          IF NOT wa_parm-val_type IS INITIAL.
            set_deep_stru_to_dom(  ).
            set_deep_data_to_dom( im_params = 
                                  im_pindex = wa_parm-pindex ).
          ENDIF.
        ENDLOOP.
    
        set_variants_to_dom(  ).
    
        download_data( ).
    
      ENDMETHOD.
    
      METHOD download_data.
    
        " Downport
    
        mv_xml_stream = zcl_abapgit_ecatt_helper=>download_data( template_over_all ).
    
      ENDMETHOD.
    
      METHOD set_business_msgs.
    
        DATA:
          lt_buss_msg_ref   TYPE zif_abapgit_ecatt=>ty_bus_msgs,
          li_element        TYPE REF TO if_ixml_element,
          li_insert_objects TYPE REF TO if_ixml_element,
          lo_ecatt_vo       TYPE REF TO object.
    
        FIELD-SYMBOLS:  TYPE any.
    
        ASSIGN ('ECATT_OBJECT') TO .
        ASSERT sy-subrc = 0.
    
        lo_ecatt_vo = .
    
        mi_objects_node = template_over_all->create_simple_element(
                                               name   = 'BUSINESS_MESSAGES'
                                               parent = root_node ).
    
        CALL METHOD lo_ecatt_vo->('GET_BUSSINESS_MSG')
          IMPORTING
            ex_buss_msg_ref = lt_buss_msg_ref.
    
        CALL FUNCTION 'SDIXML_DATA_TO_DOM'
          EXPORTING
            name         = 'ETVO_MSG'
            dataobject   = lt_buss_msg_ref
          IMPORTING
            data_as_dom  = li_element
          CHANGING
            document     = template_over_all
          EXCEPTIONS
            illegal_name = 1
            OTHERS       = 2.
        IF sy-subrc <> 0.
          MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                  WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ENDIF.
    
        li_insert_objects ?= template_over_all->find_from_name( 'BUSINESS_MESSAGES' ).
    
        li_insert_objects->append_child( li_element ).
    
      ENDMETHOD.
    
      METHOD set_ecatt_flags.
    
        DATA:
          lv_invert_validation TYPE zif_abapgit_ecatt=>ty_invert_validation,
          lv_error_prio        TYPE zif_abapgit_ecatt=>ty_error_prio,
          li_element           TYPE REF TO if_ixml_element,
          li_insert_objects    TYPE REF TO if_ixml_element,
          lo_ecatt_vo          TYPE REF TO object.
    
        FIELD-SYMBOLS:  TYPE any.
    
        mi_objects_node = template_over_all->create_simple_element(
                                               name   = 'VO_FLAGS'
                                               parent = root_node ).
    
        ASSIGN ('ECATT_OBJECT') TO .
        ASSERT sy-subrc = 0.
    
        lo_ecatt_vo = .
    
        CALL METHOD lo_ecatt_vo->('GET_INVERT_VALIDATION_FLAG')
          RECEIVING
            re_invert_validation = lv_invert_validation.
    
        CALL FUNCTION 'SDIXML_DATA_TO_DOM'
          EXPORTING
            name         = 'INVERT_VALIDATION'
            dataobject   = lv_invert_validation
          IMPORTING
            data_as_dom  = li_element
          CHANGING
            document     = template_over_all
          EXCEPTIONS
            illegal_name = 1
            OTHERS       = 2.
        IF sy-subrc <> 0.
          MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                  WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ENDIF.
    
        li_insert_objects ?= template_over_all->find_from_name( 'VO_FLAGS' ).
    
        li_insert_objects->append_child( li_element ).
    
        CALL METHOD lo_ecatt_vo->('GET_ERROR_PRIORITY')
          RECEIVING
            re_error_prio = lv_error_prio.
    
        CALL FUNCTION 'SDIXML_DATA_TO_DOM'
          EXPORTING
            name         = 'ERROR_PRIORITY'
            dataobject   = lv_error_prio
          IMPORTING
            data_as_dom  = li_element
          CHANGING
            document     = template_over_all
          EXCEPTIONS
            illegal_name = 1
            OTHERS       = 2.
        IF sy-subrc <> 0.
          MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                  WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ENDIF.
    
        li_insert_objects = template_over_all->find_from_name( 'VO_FLAGS' ).
    
        li_insert_objects->append_child( li_element ).
    
      ENDMETHOD.
    
      METHOD set_ecatt_impl_detail.
    
        DATA:
          ls_impl_details   TYPE zif_abapgit_ecatt=>ty_impl_det,
          li_element        TYPE REF TO if_ixml_element,
          li_insert_objects TYPE REF TO if_ixml_element,
          lo_ecatt_vo       TYPE REF TO object.
    
        FIELD-SYMBOLS:  TYPE any.
    
        mi_objects_node = template_over_all->create_simple_element(
                                               name   = 'IMPL_DETAILS'
                                               parent = root_node ).
    
        ASSIGN ('ECATT_OBJECT') TO .
        ASSERT sy-subrc = 0.
    
        lo_ecatt_vo = .
    
        CALL METHOD lo_ecatt_vo->('GET_IMPL_DETAILS')
          RECEIVING
            re_impl_details = ls_impl_details.
    
        CALL FUNCTION 'SDIXML_DATA_TO_DOM'
          EXPORTING
            name         = 'IMPL_DET'
            dataobject   = ls_impl_details
          IMPORTING
            data_as_dom  = li_element
          CHANGING
            document     = template_over_all
          EXCEPTIONS
            illegal_name = 1
            OTHERS       = 2.
    
        IF sy-subrc <> 0.
          MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                  WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ENDIF.
    
        li_insert_objects = template_over_all->find_from_name( 'IMPL_DETAILS' ).
    
        li_insert_objects->append_child( li_element ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_ecatt_download~get_xml_stream.
    
        rv_xml_stream = mv_xml_stream.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_ecatt_val_obj_upl IMPLEMENTATION.
    
      METHOD get_business_msgs_from_dom.
    
        " downport from CL_APL_ECATT_VO_UPLOAD
    
        DATA: li_section            TYPE REF TO if_ixml_element,
              lt_buss_msg_ref       TYPE zif_abapgit_ecatt=>ty_bus_msgs,
              lv_exception_occurred TYPE etonoff,
              lo_ecatt_vo           TYPE REF TO object.
    
        FIELD-SYMBOLS:  TYPE any.
    
        li_section = template_over_all->find_from_name_ns( 'ETVO_MSG' ).
    
        IF NOT li_section IS INITIAL.
          CALL FUNCTION 'SDIXML_DOM_TO_DATA'
            EXPORTING
              data_as_dom    = li_section
            IMPORTING
              dataobject     = lt_buss_msg_ref
            EXCEPTIONS
              illegal_object = 1
              OTHERS         = 2.
          IF sy-subrc <> 0.
            CLEAR lt_buss_msg_ref.
          ENDIF.
        ENDIF.
    
        ASSIGN ('ECATT_OBJECT') TO .
        ASSERT sy-subrc = 0.
    
        lo_ecatt_vo = .
    
        TRY.
            CALL METHOD lo_ecatt_vo->('SET_BUSSINESS_MSG')
              EXPORTING
                im_buss_msg_ref = lt_buss_msg_ref.
          CATCH cx_ecatt_apl INTO exception_to_raise.
            lv_exception_occurred = 'X'.
        ENDTRY.
    
        IF lv_exception_occurred = 'X'.
          raise_upload_exception( previous = exception_to_raise ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_impl_detail_from_dom.
    
        " downport from CL_APL_ECATT_VO_UPLOAD
    
        DATA: li_section            TYPE REF TO if_ixml_element,
              ls_impl_details       TYPE zif_abapgit_ecatt=>ty_impl_det,
              lv_exception_occurred TYPE etonoff,
              lo_ecatt_vo           TYPE REF TO object.
    
        FIELD-SYMBOLS:  TYPE any.
    
        li_section = template_over_all->find_from_name_ns( 'IMPL_DET' ).
    
        IF NOT li_section IS INITIAL.
          CALL FUNCTION 'SDIXML_DOM_TO_DATA'
            EXPORTING
              data_as_dom    = li_section
            IMPORTING
              dataobject     = ls_impl_details
            EXCEPTIONS
              illegal_object = 1
              OTHERS         = 2.
          IF sy-subrc <> 0.
            CLEAR ls_impl_details.
          ENDIF.
        ENDIF.
    
        ASSIGN ('ECATT_OBJECT') TO .
        ASSERT sy-subrc = 0.
    
        lo_ecatt_vo = .
    
        TRY.
            CALL METHOD lo_ecatt_vo->('SET_IMPL_DETAILS')
              EXPORTING
                im_impl_details = ls_impl_details.
          CATCH cx_ecatt_apl INTO exception_to_raise.
            lv_exception_occurred = 'X'.
        ENDTRY.
    
        IF lv_exception_occurred = 'X'.
          raise_upload_exception( previous = exception_to_raise ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_vo_flags_from_dom.
    
        " downport from CL_APL_ECATT_VO_UPLOAD
    
        DATA: li_section            TYPE REF TO if_ixml_element,
              lv_error_prio         TYPE zif_abapgit_ecatt=>ty_error_prio,
              lv_invert_validation  TYPE zif_abapgit_ecatt=>ty_invert_validation,
              lv_exception_occurred TYPE etonoff,
              lo_ecatt_vo           TYPE REF TO object.
    
        FIELD-SYMBOLS:  TYPE any.
    
        li_section = template_over_all->find_from_name_ns( 'INVERT_VALIDATION' ).
    
        IF NOT li_section IS INITIAL.
          CALL FUNCTION 'SDIXML_DOM_TO_DATA'
            EXPORTING
              data_as_dom    = li_section
            IMPORTING
              dataobject     = lv_invert_validation
            EXCEPTIONS
              illegal_object = 1
              OTHERS         = 2.
          IF sy-subrc <> 0.
            CLEAR lv_invert_validation.
          ENDIF.
        ENDIF.
    
        ASSIGN ('ECATT_OBJECT') TO .
        ASSERT sy-subrc = 0.
    
        lo_ecatt_vo = .
    
        TRY.
            CALL METHOD lo_ecatt_vo->('SET_INVERT_VALIDATION_FLAG')
              EXPORTING
                im_invert_validation = lv_invert_validation.
    
          CATCH cx_ecatt_apl INTO exception_to_raise.
            lv_exception_occurred = 'X'.
        ENDTRY.
    
        li_section = template_over_all->find_from_name_ns( 'ERROR_PRIORITY' ).
    
        IF NOT li_section IS INITIAL.
          CALL FUNCTION 'SDIXML_DOM_TO_DATA'
            EXPORTING
              data_as_dom    = li_section
            IMPORTING
              dataobject     = lv_error_prio
            EXCEPTIONS
              illegal_object = 1
              OTHERS         = 2.
          IF sy-subrc <> 0.
            CLEAR lv_invert_validation.
          ENDIF.
        ENDIF.
    
        TRY.
            CALL METHOD lo_ecatt_vo->('SET_ERROR_PRIORITY')
              EXPORTING
                im_error_prio = lv_error_prio.
          CATCH cx_ecatt_apl INTO exception_to_raise.
            lv_exception_occurred = 'X'.
        ENDTRY.
    
        IF lv_exception_occurred = 'X'.
          raise_upload_exception( previous = exception_to_raise ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD upload.
    
        " We inherit from CL_APL_ECATT_UPLOAD because CL_APL_ECATT_VO_UPLOAD
        " doesn't exist in 702
    
        " downport from CL_APL_ECATT_VO_UPLOAD
    
        DATA: lx_ex       TYPE REF TO cx_ecatt_apl,
              lv_exists   TYPE etonoff,
              lv_exc_occ  TYPE etonoff,
              ls_tadir    TYPE tadir,
              lo_ecatt_vo TYPE REF TO object,
              lo_params   TYPE REF TO cl_apl_ecatt_params.
    
        FIELD-SYMBOLS:  TYPE any,
                          TYPE data,
                           TYPE data,
                           TYPE data.
    
        TRY.
            ch_object-i_devclass = ch_object-d_devclass.
    
            ASSIGN COMPONENT 'D_AKH' OF STRUCTURE ch_object
                   TO . " doesn't exist in 702
            ASSIGN COMPONENT 'I_AKH' OF STRUCTURE ch_object
                   TO . " doesn't exist in 702
            IF  IS ASSIGNED AND  IS ASSIGNED.
               = .
            ENDIF.
    
            super->upload( CHANGING ch_object = ch_object ).
    
            upload_data_from_stream( ch_object-filename ).
          CATCH cx_ecatt_apl INTO lx_ex.
            IF template_over_all IS INITIAL.
              RAISE EXCEPTION lx_ex.
            ELSE.
              lv_exc_occ = 'X'.
            ENDIF.
        ENDTRY.
    
        TRY.
            CALL METHOD ('GET_ATTRIBUTES_FROM_DOM_NEW') " doesn't exit in 702
              CHANGING
                ch_object = ch_object.
          CATCH cx_ecatt_apl INTO lx_ex.
            lv_exc_occ = 'X'.
        ENDTRY.
    
        ASSIGN ('ECATT_OBJECT') TO .
        ASSERT sy-subrc = 0.
    
        lo_ecatt_vo = .
    
        ASSIGN lo_ecatt_vo->('PARAMS') TO .
        ASSERT sy-subrc = 0.
    
        lo_params = .
    
        TRY.
            get_impl_detail_from_dom( ).
          CATCH cx_ecatt_apl INTO lx_ex.
            lv_exc_occ = 'X'.
        ENDTRY.
    
        TRY.
            get_vo_flags_from_dom( ).
          CATCH cx_ecatt_apl INTO lx_ex.
            lv_exc_occ = 'X'.
        ENDTRY.
    
        TRY.
            get_business_msgs_from_dom( ).
          CATCH cx_ecatt_apl INTO lx_ex.
            lv_exc_occ = 'X'.
        ENDTRY.
    
        TRY.
            CALL METHOD ('GET_PARAMS_FROM_DOM_NEW') " doesn't exist in 702
              EXPORTING
                im_params = lo_params.
          CATCH cx_ecatt_apl INTO lx_ex.
            lv_exc_occ = 'X'.
        ENDTRY.
    
        TRY.
            get_variants_from_dom( lo_params ).
          CATCH cx_ecatt_apl INTO lx_ex.
            lv_exc_occ = 'X'.
        ENDTRY.
    
        TRY.
            lv_exists = cl_apl_ecatt_object=>existence_check_object(
                    im_name               = ch_object-d_obj_name
                    im_version            = ch_object-d_obj_ver
                    im_obj_type           = ch_object-s_obj_type
                    im_exists_any_version = 'X' ).
    
            IF lv_exists = space.
              CALL METHOD lo_ecatt_vo->('SET_TADIR_FOR_NEW_OBJECT')
                EXPORTING
                  im_tadir_for_new_object = tadir_preset.
            ENDIF.
          CATCH cx_ecatt.
            CLEAR lv_exists.
        ENDTRY.
    
        TRY.
            CALL METHOD lo_ecatt_vo->('SAVE')
              EXPORTING
                im_do_commit = 'X'.
          CATCH cx_ecatt_apl INTO lx_ex.
            lv_exc_occ = 'X'.
        ENDTRY.
    
    *     get devclass from existing object
        TRY.
            cl_apl_ecatt_object=>get_tadir_entry(
              EXPORTING im_obj_name = ch_object-d_obj_name
                        im_obj_type = ch_object-s_obj_type
              IMPORTING ex_tadir = ls_tadir ).
    
            ch_object-d_devclass = ls_tadir-devclass.
    
          CATCH cx_ecatt.
            CLEAR ls_tadir.
        ENDTRY.
        IF lv_exc_occ = 'X'.
          raise_upload_exception( previous = lx_ex ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD upload_data_from_stream.
    
        " Downport
        template_over_all = zcl_abapgit_ecatt_helper=>upload_data_from_stream( mv_external_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_ecatt_upload~set_stream_for_upload.
    
        " downport from CL_ABAPGIT_ECATT_DATA_UPLOAD SET_STREAM_FOR_UPLOAD
        mv_external_xml = iv_xml.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_environment IMPLEMENTATION.
    
      METHOD is_system_changes_allowed.
    
        DATA:
          lv_systemedit         TYPE tadir-edtflag,
          lv_sys_cliinddep_edit TYPE t000-ccnocliind,
          lv_is_shadow          TYPE abap_bool,
          ls_upginfo            TYPE uvers,
          lv_is_upgrade         TYPE abap_bool.
    
        CALL FUNCTION 'TR_SYS_PARAMS'
          IMPORTING
            systemedit         = lv_systemedit
            sys_cliinddep_edit = lv_sys_cliinddep_edit
          EXCEPTIONS
            no_systemname      = 1
            no_systemtype      = 2
            OTHERS             = 3.
        IF sy-subrc <> 0.
          " Assume system can't be changed
          RETURN.
        ENDIF.
    
        CALL FUNCTION 'UPG_IS_SHADOW_SYSTEM'
          IMPORTING
            ev_shadow = lv_is_shadow.
    
        CALL FUNCTION 'UPG_GET_ACTIVE_COMP_UPGRADE'
          EXPORTING
            iv_component = 'SAP_BASIS'
            iv_upgtype   = 'A'
            iv_buffered  = abap_false
          IMPORTING
            ev_upginfo   = ls_upginfo
          EXCEPTIONS
            OTHERS       = 4.
        IF sy-subrc = 0 AND ls_upginfo-putstatus NA 'ITU'.
          lv_is_upgrade = abap_true.
        ENDIF.
    
        " SAP system has status 'not modifiable' (TK 102)
        " Changes to repository objects are not permitted in this client (TK 729)
        " Shadow system
        " Running upgrade
        rv_result = boolc(
          lv_systemedit <> 'N' AND
          lv_sys_cliinddep_edit NA '23' AND
          lv_is_shadow <> abap_true AND
          lv_is_upgrade <> abap_true ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_environment~check_parallel_processing.
    
        " If check fails, see transactions RZ12
        DATA:
          lt_setup      TYPE STANDARD TABLE OF rzllitab,
          ls_setup      LIKE LINE OF lt_setup,
          lt_erfc_setup TYPE STANDARD TABLE OF rzlliclass,
          lt_instances  TYPE STANDARD TABLE OF msxxlist WITH DEFAULT KEY.
    
        " Check if server group for parallel processing exists
        CALL FUNCTION 'SMLG_GET_SETUP'
          EXPORTING
            grouptype          = 'S'
          TABLES
            setup              = lt_setup
            erfc_setup         = lt_erfc_setup
          EXCEPTIONS
            foreign_lock       = 1
            system_failure     = 2
            invalid_group_type = 3
            OTHERS             = 4.
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        READ TABLE lt_setup INTO ls_setup WITH KEY classname = iv_group.
        IF sy-subrc = 0 AND ls_setup-applserver IS NOT INITIAL.
    
          " Check if assigned server instance exists
          CALL FUNCTION 'TH_SERVER_LIST'
            TABLES
              list = lt_instances.
    
          READ TABLE lt_instances TRANSPORTING NO FIELDS WITH KEY name = ls_setup-applserver.
          IF sy-subrc = 0.
            rv_checked = abap_true.
          ENDIF.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_environment~compare_with_inactive.
        rv_result = zif_abapgit_environment~is_sap_cloud_platform( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_environment~get_available_user_sessions.
    
        DATA:
          lv_act_sessions TYPE i,
          lv_max_sessions TYPE i,
          lv_subrc        TYPE sy-subrc.
    
        CALL FUNCTION 'TH_USER_INFO'
          IMPORTING
            act_sessions = lv_act_sessions
            max_sessions = lv_max_sessions
            rc           = lv_subrc.
    
        IF lv_subrc = 0.
          rv_sessions = lv_max_sessions - lv_act_sessions.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_environment~get_basis_release.
    
        SELECT SINGLE release extrelease FROM cvers INTO (rs_result-release, rs_result-sp)
          WHERE component = 'SAP_BASIS' ##SUBRC_OK.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_environment~get_system_language_filter.
        DATA lv_translation_detective_lang TYPE spras.
        DATA lv_pseudo_translation_language TYPE spras.
        FIELD-SYMBOLS  LIKE LINE OF rt_system_language_filter.
    
        " Translation Object Detective
        " https://help.sap.com/docs/ABAP_PLATFORM_NEW/ceb25152cb0d4adba664cebea2bf4670/88a3d3cbccf64601975acabaccdfde45.html
        CALL FUNCTION 'CONVERSION_EXIT_ISOLA_INPUT'
          EXPORTING
            input            = '1Q'
          IMPORTING
            output           = lv_translation_detective_lang
          EXCEPTIONS
            unknown_language = 1
            OTHERS           = 2.
        IF sy-subrc = 1.
          " The language for Translation Object Detective was not setup
        ENDIF.
        IF NOT lv_translation_detective_lang IS INITIAL.
          APPEND INITIAL LINE TO rt_system_language_filter ASSIGNING .
          -sign = 'E'.
          -option = 'EQ'.
          -low = lv_translation_detective_lang.
        ENDIF.
        " 1943470 - Using technical language key 2Q to create pseudo-translations of ABAP developments
        " https://launchpad.support.sap.com/#/notes/1943470
        CALL FUNCTION 'CONVERSION_EXIT_ISOLA_INPUT'
          EXPORTING
            input            = '2Q'
          IMPORTING
            output           = lv_pseudo_translation_language
          EXCEPTIONS
            unknown_language = 1
            OTHERS           = 2.
        IF sy-subrc = 1.
          " The language for Pseudo Translation was not setup
        ENDIF.
        IF NOT lv_pseudo_translation_language IS INITIAL.
          APPEND INITIAL LINE TO rt_system_language_filter ASSIGNING .
          -sign = 'E'.
          -option = 'EQ'.
          -low = lv_pseudo_translation_language.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_environment~init_parallel_processing.
    
        DATA: lv_group TYPE rzlli_apcl.
    
        lv_group = iv_group.
    
        " SPBT_INITIALIZE gives error PBT_ENV_ALREADY_INITIALIZED if called
        " multiple times in same session
        CALL FUNCTION 'SPBT_INITIALIZE'
          EXPORTING
            group_name                     = lv_group
          IMPORTING
            free_pbt_wps                   = rv_free_work_processes
          EXCEPTIONS
            invalid_group_name             = 1
            internal_error                 = 2
            pbt_env_already_initialized    = 3
            currently_no_resources_avail   = 4
            no_pbt_resources_found         = 5
            cant_init_different_pbt_groups = 6
            OTHERS                         = 7 ##FM_SUBRC_OK.
        " If SPBT_INITIALIZE fails, check transactions RZ12, SM50, SM21, SARFC
    
      ENDMETHOD.
    
      METHOD zif_abapgit_environment~is_merged.
        DATA lr_marker TYPE REF TO data ##NEEDED.
    
        IF mv_is_merged = abap_undefined.
          TRY.
              CREATE DATA lr_marker TYPE REF TO ('LIF_ABAPMERGE_MARKER').
              "No exception --> marker found
              mv_is_merged = abap_true.
    
            CATCH cx_sy_create_data_error.
              mv_is_merged = abap_false.
          ENDTRY.
        ENDIF.
        rv_result = mv_is_merged.
      ENDMETHOD.
    
      METHOD zif_abapgit_environment~is_repo_object_changes_allowed.
        IF mv_modifiable = abap_undefined.
          mv_modifiable = is_system_changes_allowed( ).
        ENDIF.
        rv_result = mv_modifiable.
      ENDMETHOD.
    
      METHOD zif_abapgit_environment~is_restart_required.
        " This method will be used in the context of SAP Cloud Platform:
        " Pull/Push operations are executed in background jobs.
        " In case of the respective application server needs to be restarted,
        " it is required to terminate the background job and reschedule again.
        rv_result = abap_false.
        TRY.
            CALL METHOD ('CL_APJ_SCP_TOOLS')=>('IS_RESTART_REQUIRED')
              RECEIVING
                restart_required = rv_result.
          CATCH cx_sy_dyn_call_illegal_method cx_sy_dyn_call_illegal_class.
            rv_result = abap_false.
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_environment~is_sap_cloud_platform.
        IF mv_cloud = abap_undefined.
          TRY.
              CALL METHOD ('CL_COS_UTILITIES')=>('IS_SAP_CLOUD_PLATFORM')
                RECEIVING
                  rv_is_sap_cloud_platform = mv_cloud.
            CATCH cx_sy_dyn_call_error.
              mv_cloud = abap_false.
          ENDTRY.
        ENDIF.
        rv_result = mv_cloud.
      ENDMETHOD.
    
      METHOD zif_abapgit_environment~is_sap_object_allowed.
    
        rv_allowed = cl_enh_badi_def_utility=>is_sap_system( ).
        IF rv_allowed = abap_true.
          RETURN.
        ENDIF.
    
        rv_allowed = zcl_abapgit_exit=>get_instance( )->allow_sap_objects( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_environment~is_variant_maintenance.
    
        DATA:
          lt_variscreens TYPE STANDARD TABLE OF rsdynnr
                              WITH NON-UNIQUE DEFAULT KEY.
    
        " Memory is set in LSVARF08 / EXPORT_SCREEN_TABLES.
        IMPORT variscreens = lt_variscreens FROM MEMORY ID '%_SCRNR_%'.
    
        rv_is_variant_maintenance = boolc( lines( lt_variscreens ) > 0 ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_USER_RECORD IMPLEMENTATION.
    
      METHOD build_cache.
    
        " Get user details
        TRY.
            check_user_exists(
              EXPORTING
                iv_user     = iv_user
              IMPORTING
                ev_fullname = rs_user-name
                ev_email    = rs_user-email ).
          CATCH zcx_abapgit_exception.
            " Could not find user, try to get from other clients
            rs_user = get_user_dtls_from_other_clnt( iv_user ).
        ENDTRY.
    
        rs_user-user = iv_user.
        INSERT rs_user INTO TABLE gt_user.
    
      ENDMETHOD.
    
      METHOD check_user_exists.
    
        DATA lt_return  TYPE STANDARD TABLE OF bapiret2 WITH DEFAULT KEY.
        DATA ls_address TYPE bapiaddr3.
        DATA lt_smtp    TYPE TABLE OF bapiadsmtp.
        DATA ls_smtp    LIKE LINE OF lt_smtp.
    
        CALL FUNCTION 'BAPI_USER_GET_DETAIL'
          EXPORTING
            username = iv_user
          IMPORTING
            address  = ls_address
          TABLES
            return   = lt_return
            addsmtp  = lt_smtp.
        LOOP AT lt_return TRANSPORTING NO FIELDS WHERE type CA 'EA'.
          zcx_abapgit_exception=>raise( |User: { iv_user } not found| ).
        ENDLOOP.
    
        ev_fullname = ls_address-fullname.
    
        " Choose the first email from SU01
        SORT lt_smtp BY consnumber ASCENDING.
    
        LOOP AT lt_smtp INTO ls_smtp.
          ev_email = ls_smtp-e_mail.
          EXIT.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD get_user_dtls_from_other_clnt.
    
        CONSTANTS lc_cc_category TYPE string VALUE 'C'.
        TYPES ty_dev_clients TYPE SORTED TABLE OF sy-mandt WITH UNIQUE KEY table_line.
        DATA lt_dev_clients TYPE ty_dev_clients.
        FIELD-SYMBOLS  LIKE LINE OF lt_dev_clients.
    
        " Could not find the user, try other development clients
        SELECT mandt FROM t000 INTO TABLE lt_dev_clients
            WHERE cccategory = lc_cc_category AND mandt <> sy-mandt
            ORDER BY PRIMARY KEY.
    
        LOOP AT lt_dev_clients ASSIGNING .
          SELECT SINGLE u~bname p~name_text a~smtp_addr INTO (rs_user-user, rs_user-name, rs_user-email)
              FROM usr21 AS u
              INNER JOIN adrp AS p ON p~persnumber = u~persnumber
                                  AND p~client     = u~mandt
              INNER JOIN adr6 AS a ON a~persnumber = u~persnumber
                                  AND a~addrnumber = u~addrnumber
                                  AND a~client     = u~mandt
              CLIENT SPECIFIED
              WHERE u~mandt      = 
                AND u~bname      = iv_user
                AND p~date_from <= sy-datum
                AND p~date_to   >= sy-datum
                AND a~date_from <= sy-datum.
          IF sy-subrc = 0.
            EXIT.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD read_cache.
    
        READ TABLE gt_user INTO rs_user WITH TABLE KEY user = iv_user.
        IF sy-subrc <> 0.
          rs_user = build_cache( iv_user ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD reset.
        CLEAR gt_user.
      ENDMETHOD.
    
      METHOD zif_abapgit_user_record~get_email.
    
        rv_email = read_cache( iv_username )-email.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_user_record~get_name.
    
        rv_name = read_cache( iv_username )-name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_user_record~get_title.
    * the queried username might not exist, refactored for open-abap compatibility
    
        DATA lr_addr3             TYPE REF TO data.
        FIELD-SYMBOLS   TYPE any.
        FIELD-SYMBOLS  TYPE simple.
    
        TRY.
            CREATE DATA lr_addr3 TYPE ('ADDR3_VAL').
          CATCH cx_sy_create_data_error.
            RETURN.
        ENDTRY.
        ASSIGN lr_addr3->* TO .
    
        CALL FUNCTION 'SUSR_USER_ADDRESS_READ'
          EXPORTING
            user_name              = iv_username
          IMPORTING
            user_address           = 
          EXCEPTIONS
            user_address_not_found = 1
            OTHERS                 = 2.
        IF sy-subrc = 0.
          ASSIGN COMPONENT 'NAME_TEXT' OF STRUCTURE  TO .
          rv_title = .
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_env_factory IMPLEMENTATION.
    
      METHOD get_user_record.
        IF gi_user_record IS NOT BOUND.
          CREATE OBJECT gi_user_record TYPE zcl_abapgit_user_record.
        ENDIF.
    
        ri_user_record = gi_user_record.
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_env_injector IMPLEMENTATION.
    
      METHOD set_user_record.
        zcl_abapgit_env_factory=>gi_user_record = ii_user_record.
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_exit IMPLEMENTATION.
    
      METHOD get_instance.
    
        DATA lv_class_name TYPE string.
    
        IF gi_global_exit IS NOT INITIAL.
          ri_exit = gi_global_exit.
          RETURN.
        ENDIF.
    
        IF zcl_abapgit_factory=>get_environment( )->is_merged( ) = abap_true.
          " Prevent accidental usage of exit handlers in the developer version
          lv_class_name = |\\PROGRAM={ sy-repid }\\CLASS={ lv_class_name }|.
        ENDIF.
    
        " Prevent non-mocked exit calls in unit tests
        IF is_running_in_test_context( ) = abap_false.
          TRY.
              CREATE OBJECT gi_exit TYPE (lv_class_name).
            CATCH cx_sy_create_object_error ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
        CREATE OBJECT gi_global_exit TYPE zcl_abapgit_exit. " this class
    
        ri_exit = gi_global_exit.
    
      ENDMETHOD.
    
      METHOD is_running_in_test_context.
    
        IF sy-sysid = 'ABC'.
          " always run on open-abap
          rv_running_in_test_context = abap_true.
          RETURN.
        ENDIF.
    
        " Check if the local test class can be accessed by RTTI. If so the current process is running in a unit test.
        " Note this approach only works for the developer version. The standalone version will always report not running in
        " test context which should be fine as there are no unit tests delivered in it.
        cl_abap_typedescr=>describe_by_name(
          EXPORTING
            p_name         = |\\PROGRAM={ sy-repid }\\CLASS=LTCL_TEST|
          EXCEPTIONS
            type_not_found = 1
            OTHERS         = 2 ).
        rv_running_in_test_context = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~adjust_display_commit_url.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->adjust_display_commit_url(
                EXPORTING
                  iv_repo_url    = iv_repo_url
                  iv_repo_name   = iv_repo_name
                  iv_repo_key    = iv_repo_key
                  iv_commit_hash = iv_commit_hash
                CHANGING
                  cv_display_url = cv_display_url ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~adjust_display_filename.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              rv_filename = gi_exit->adjust_display_filename(
                is_repo_meta = is_repo_meta
                iv_filename  = iv_filename ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
        IF rv_filename IS INITIAL.
          rv_filename = iv_filename.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~allow_sap_objects.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              rv_allowed = gi_exit->allow_sap_objects( ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~change_committer_info.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->change_committer_info(
                EXPORTING
                  iv_repo_url = iv_repo_url
                CHANGING
                  cv_name     = cv_name
                  cv_email    = cv_email ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~change_local_host.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->change_local_host( CHANGING ct_hosts = ct_hosts ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~change_max_parallel_processes.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->change_max_parallel_processes(
                EXPORTING
                  iv_package       = iv_package
                CHANGING
                  cv_max_processes = cv_max_processes ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~change_proxy_authentication.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->change_proxy_authentication(
                EXPORTING
                  iv_repo_url             = iv_repo_url
                CHANGING
                  cv_proxy_authentication = cv_proxy_authentication ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~change_proxy_port.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->change_proxy_port(
                EXPORTING
                  iv_repo_url   = iv_repo_url
                CHANGING
                  cv_proxy_port = cv_proxy_port ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~change_proxy_url.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->change_proxy_url(
                EXPORTING
                  iv_repo_url  = iv_repo_url
                CHANGING
                  cv_proxy_url = cv_proxy_url ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~change_rfc_server_group.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->change_rfc_server_group( CHANGING cv_group = cv_group ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~change_supported_data_objects.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->change_supported_data_objects( CHANGING ct_objects = ct_objects ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~change_supported_object_types.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->change_supported_object_types( CHANGING ct_types = ct_types ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~change_tadir.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->change_tadir(
                EXPORTING
                  iv_package            = iv_package
                  ii_log                = ii_log
                  is_dot_abapgit        = is_dot_abapgit
                  iv_ignore_subpackages = iv_ignore_subpackages
                  iv_only_local_objects = iv_only_local_objects
                CHANGING
                  ct_tadir              = ct_tadir ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~create_http_client.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              ri_client = gi_exit->create_http_client( iv_url ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~custom_serialize_abap_clif.
    
        " This exit might be called twice per object
        " 1st call: it_source = initial
        "    Can be used for serializing complete source
        "    If source is returned, there will be no second call
        " 2nd call: it_source = code as serialized by abapGit
        "    Can be used for post-processing of source
        IF gi_exit IS NOT INITIAL.
          TRY.
              rt_source = gi_exit->custom_serialize_abap_clif(
                is_class_key = is_class_key
                it_source    = it_source ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
        IF rt_source IS INITIAL.
          rt_source = it_source.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~deserialize_postprocess.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->deserialize_postprocess(
                EXPORTING
                  it_remote        = it_remote
                  is_step          = is_step
                  ii_log           = ii_log
                CHANGING
                  ct_updated_files = ct_updated_files ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~get_ci_tests.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->get_ci_tests(
                EXPORTING
                  iv_object   = iv_object
                CHANGING
                  ct_ci_repos = ct_ci_repos ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~get_ssl_id.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              rv_ssl_id = gi_exit->get_ssl_id( ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
        IF rv_ssl_id IS INITIAL.
          rv_ssl_id = 'ANONYM'.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~http_client.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->http_client(
                iv_url    = iv_url
                ii_client = ii_client ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~pre_calculate_repo_status.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->pre_calculate_repo_status(
                EXPORTING
                  is_repo_meta = is_repo_meta
                CHANGING
                  ct_local     = ct_local
                  ct_remote    = ct_remote ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~serialize_postprocess.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->serialize_postprocess(
                EXPORTING
                  iv_package = iv_package
                  ii_log     = ii_log
                CHANGING
                  ct_files   = ct_files ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_exit~validate_after_push.
    
        IF gi_exit IS NOT INITIAL.
          TRY.
              gi_exit->validate_after_push( ii_repo_online ).
            CATCH cx_sy_ref_is_initial cx_sy_dyn_call_illegal_method ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_function_module IMPLEMENTATION.
    
      METHOD zif_abapgit_function_module~function_exists.
    
        DATA lv_function_module_name TYPE c LENGTH 30.
        DATA lv_exists TYPE string.
    
        lv_function_module_name = iv_function_module_name.
        lv_exists = 'FUNCTION_EXISTS'.
    
        TRY.
            CALL FUNCTION lv_exists
              EXPORTING
                funcname           = lv_function_module_name
              EXCEPTIONS
                function_not_exist = 1
                OTHERS             = 2.
            rv_exists = boolc( sy-subrc = 0 ).
          CATCH cx_sy_dyn_call_illegal_func.
    * then its running in ABAP Cloud Programming Model, assume nothing is released
    * I could not find any way to check for this -Hvam
            rv_exists = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_longtexts IMPLEMENTATION.
    
      METHOD escape_name.
        " Prepare name for SQL LIKE condition
        rv_object = iv_object_name.
    
        IF 'CA,CE,CO,CT,IA,IE,IO,WC,FU,FX,DI,IS,PS' CS iv_longtext_id.
          " Document types of objects with sub-objects
          rv_object+30 = '%'.
        ELSEIF 'OD' CS iv_longtext_id.
          rv_object+10 = '%'.
        ENDIF.
    
        rv_object = replace(
          val  = rv_object
          sub  = '_'
          with = '#_'
          occ  = 0 ).
      ENDMETHOD.
    
      METHOD read.
    
        DATA: ls_longtext TYPE zif_abapgit_longtexts=>ty_longtext,
              lv_object   TYPE dokil-object,
              lt_dokil    TYPE zif_abapgit_definitions=>ty_dokil_tt.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_dokil.
    
        IF iv_object_name CA '#'.
          zcx_abapgit_exception=>raise( |Invalid name for longtext: { iv_longtext_id } { iv_object_name }| ).
        ENDIF.
    
        lv_object = escape_name(
          iv_longtext_id = iv_longtext_id
          iv_object_name = iv_object_name ).
    
        IF lines( it_dokil ) > 0.
    
          lt_dokil = it_dokil.
    
          IF iv_main_lang_only = abap_true.
            DELETE lt_dokil WHERE masterlang <> abap_true.
          ENDIF.
    
        ELSEIF iv_longtext_id IS NOT INITIAL.
          IF iv_main_lang_only = abap_true.
            SELECT * FROM dokil
                     INTO TABLE lt_dokil
                     WHERE id     = iv_longtext_id
                     AND object LIKE lv_object ESCAPE '#'
                     AND masterlang = abap_true
                     ORDER BY PRIMARY KEY.
          ELSE.
            SELECT * FROM dokil
                     INTO TABLE lt_dokil
                     WHERE id     = iv_longtext_id
                     AND object LIKE lv_object ESCAPE '#'
                     ORDER BY PRIMARY KEY.
          ENDIF.
        ELSE.
    
          zcx_abapgit_exception=>raise( |serialize_longtexts parameter error| ).
    
        ENDIF.
    
        LOOP AT lt_dokil ASSIGNING 
                         WHERE txtlines > 0.
    
          CLEAR: ls_longtext.
    
          ls_longtext-dokil = .
    
          CALL FUNCTION 'DOCU_READ'
            EXPORTING
              id      = -id
              langu   = -langu
              object  = -object
              typ     = -typ
              version = -version
            IMPORTING
              head    = ls_longtext-head
            TABLES
              line    = ls_longtext-lines.
    
          IF iv_clear_fields = abap_true.
            CLEAR: ls_longtext-head-tdfuser,
                   ls_longtext-head-tdfreles,
                   ls_longtext-head-tdfdate,
                   ls_longtext-head-tdftime,
                   ls_longtext-head-tdluser,
                   ls_longtext-head-tdlreles,
                   ls_longtext-head-tdldate,
                   ls_longtext-head-tdltime.
          ENDIF.
    
          INSERT ls_longtext INTO TABLE rt_longtexts.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_longtexts~changed_by.
    
        DATA: lt_longtexts TYPE zif_abapgit_longtexts=>ty_longtexts.
        FIELD-SYMBOLS:  TYPE zif_abapgit_longtexts=>ty_longtext.
    
        lt_longtexts = read( iv_object_name  = iv_object_name
                             iv_longtext_id  = iv_longtext_id
                             it_dokil        = it_dokil
                             iv_clear_fields = abap_false ).
    
        READ TABLE lt_longtexts INDEX 1 ASSIGNING .
        IF sy-subrc = 0.
          rv_user = -head-tdluser.
          IF rv_user IS INITIAL.
            rv_user = -head-tdfuser.
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_longtexts~delete.
    
        DATA: lt_dokil  TYPE zif_abapgit_definitions=>ty_dokil_tt,
              lv_object TYPE dokil-object.
    
        FIELD-SYMBOLS:  TYPE dokil.
    
        lv_object = escape_name(
          iv_longtext_id = iv_longtext_id
          iv_object_name = iv_object_name ).
    
        SELECT * FROM dokil
          INTO TABLE lt_dokil
          WHERE id = iv_longtext_id AND object LIKE lv_object ESCAPE '#'
          ORDER BY PRIMARY KEY.
    
        LOOP AT lt_dokil ASSIGNING .
    
          CALL FUNCTION 'DOCU_DEL'
            EXPORTING
              id       = -id
              langu    = -langu
              object   = -object
              typ      = -typ
            EXCEPTIONS
              ret_code = 1
              OTHERS   = 2.
    
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_longtexts~deserialize.
    
        DATA: lt_longtexts    TYPE zif_abapgit_longtexts=>ty_longtexts,
              lv_object       TYPE dokil-object,
              lt_dokil        TYPE zif_abapgit_definitions=>ty_dokil_tt,
              lv_no_main_lang TYPE dokil-masterlang.
    
        FIELD-SYMBOLS:  TYPE zif_abapgit_longtexts=>ty_longtext,
                           TYPE dokil.
    
        lv_object = escape_name(
          iv_longtext_id = iv_longtext_id
          iv_object_name = iv_object_name ).
    
        ii_xml->read(
          EXPORTING
            iv_name = iv_longtext_name
          CHANGING
            cg_data = lt_longtexts ).
    
        LOOP AT lt_longtexts ASSIGNING .
    
          lv_no_main_lang = boolc( iv_main_language <> -dokil-langu ).
    
          CALL FUNCTION 'DOCU_UPDATE'
            EXPORTING
              head          = -head
              state         = c_docu_state_active
              typ           = -dokil-typ
              version       = -dokil-version
              no_masterlang = lv_no_main_lang
            TABLES
              line          = -lines.
    
        ENDLOOP.
    
        " Read existing texts and check if they were deserialized above
        " If not, delete the texts
        SELECT * FROM dokil
          INTO TABLE lt_dokil
          WHERE id = iv_longtext_id AND object LIKE lv_object ESCAPE '#'
          ORDER BY PRIMARY KEY.
    
        LOOP AT lt_dokil ASSIGNING .
    
          READ TABLE lt_longtexts TRANSPORTING NO FIELDS WITH KEY
            dokil-id     = -id
            dokil-langu  = -langu
            dokil-object = -object
            dokil-typ    = -typ.
          IF sy-subrc <> 0.
            CALL FUNCTION 'DOCU_DEL'
              EXPORTING
                id       = -id
                langu    = -langu
                object   = -object
                typ      = -typ
              EXCEPTIONS
                ret_code = 1
                OTHERS   = 2.
    
            IF sy-subrc <> 0.
              zcx_abapgit_exception=>raise_t100( ).
            ENDIF.
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_longtexts~serialize.
    
        rt_longtexts = read( iv_object_name    = iv_object_name
                             iv_longtext_id    = iv_longtext_id
                             it_dokil          = it_dokil
                             iv_main_lang_only = io_i18n_params->ms_params-main_language_only ).
    
        IF rt_longtexts IS SUPPLIED.
          RETURN.
        ENDIF.
    
        ii_xml->add( iv_name = iv_longtext_name
                     ig_data = rt_longtexts ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_lxe_texts IMPLEMENTATION.
    
      METHOD check_langs_versus_installed.
    
        DATA lt_installed_hash TYPE HASHED TABLE OF laiso WITH UNIQUE KEY table_line.
        FIELD-SYMBOLS  LIKE LINE OF it_languages.
    
        CLEAR: et_intersection, et_missfits.
        lt_installed_hash = it_installed.
    
        LOOP AT it_languages ASSIGNING .
          READ TABLE lt_installed_hash WITH KEY table_line =  TRANSPORTING NO FIELDS.
          IF sy-subrc = 0.
            APPEND  TO et_intersection.
          ELSE.
            APPEND  TO et_missfits.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD convert_lang_string_to_table.
    
        DATA:
          lt_langs_str          TYPE string_table,
          lv_laiso              TYPE laiso,
          lv_skip_main_lang_iso TYPE laiso.
    
        FIELD-SYMBOLS:
            LIKE LINE OF lt_langs_str.
    
        " Keep * as indicator for 'all installed languages'
        IF iv_langs = '*'.
          APPEND iv_langs TO rt_languages.
          RETURN.
        ENDIF.
    
        " Convert string of 2-letter ISO languages into table of sy-langu codes
        SPLIT iv_langs AT ',' INTO TABLE lt_langs_str.
    
        LOOP AT lt_langs_str ASSIGNING .
          lv_laiso = condense( to_upper(  ) ).
          APPEND lv_laiso TO rt_languages.
        ENDLOOP.
    
        IF iv_skip_main_language IS NOT INITIAL.
          lv_skip_main_lang_iso = langu_to_laiso_safe( iv_skip_main_language ).
          DELETE rt_languages WHERE table_line = lv_skip_main_lang_iso.
        ENDIF.
    
        SORT rt_languages.
        DELETE ADJACENT DUPLICATES FROM rt_languages.
    
      ENDMETHOD.
    
      METHOD convert_table_to_lang_string.
    
        DATA:
          lt_langs_str TYPE string_table.
    
        FIELD-SYMBOLS:
           LIKE LINE OF it_languages,
            TYPE string.
    
        " Convert table of sy-langu codes into string of 2-letter ISO languages
        LOOP AT it_languages ASSIGNING .
          " Keep * as indicator for 'all installed languages'
          IF  = '*'.
            CLEAR lt_langs_str.
            APPEND '*' TO lt_langs_str.
            EXIT.
          ENDIF.
    
          APPEND INITIAL LINE TO lt_langs_str ASSIGNING .
           = .
        ENDLOOP.
    
        CONCATENATE LINES OF lt_langs_str INTO rv_langs SEPARATED BY ','.
    
      ENDMETHOD.
    
      METHOD deserialize_from_po.
    
        DATA lv_lang LIKE LINE OF mo_i18n_params->ms_params-translation_languages.
        DATA lt_po_files TYPE zif_abapgit_i18n_file=>ty_table_of.
        DATA li_po LIKE LINE OF lt_po_files.
        DATA lt_text_pairs_tmp TYPE ty_lxe_translation-text_pairs.
        DATA lt_obj_list TYPE lxe_tt_colob.
        DATA lv_main_lang TYPE lxeisolang.
        DATA lv_target_lang TYPE lxeisolang.
        DATA lv_changed TYPE abap_bool.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_obj_list.
    
        lt_obj_list = get_lxe_object_list(
          iv_object_name = iv_object_name
          iv_object_type = iv_object_type ).
    
        IF lt_obj_list IS INITIAL.
          RETURN.
        ENDIF.
    
        lt_po_files  = mo_files->read_i18n_files( ).
        lv_main_lang = get_lang_iso4( langu_to_laiso_safe( mo_i18n_params->ms_params-main_language ) ).
    
        LOOP AT mo_i18n_params->ms_params-translation_languages INTO lv_lang.
          lv_target_lang = get_lang_iso4( lv_lang ).
    
          LOOP AT lt_po_files INTO li_po.
            IF li_po->lang( ) = to_lower( lv_lang ). " Not quite efficient but the list is presumably very short
              EXIT.
            ELSE.
              CLEAR li_po.
            ENDIF.
          ENDLOOP.
    
          CHECK li_po IS BOUND. " Ignore missing files, missing translation is not a crime
    
          LOOP AT lt_obj_list ASSIGNING .
    
            lt_text_pairs_tmp = read_lxe_object_text_pair(
              iv_s_lang    = lv_main_lang
              iv_t_lang    = lv_target_lang
              iv_custmnr   = -custmnr
              iv_objtype   = -objtype
              iv_objname   = -objname
              iv_read_only = abap_false ).
    
            li_po->translate(
              CHANGING
                cv_changed    = lv_changed
                ct_text_pairs = lt_text_pairs_tmp ).
    
            IF lv_changed = abap_true AND lines( lt_text_pairs_tmp ) > 0.
              " If lt_text_pairs_tmp is empty it raises error, while this is a practical case
              write_lxe_object_text_pair(
                iv_s_lang  = lv_main_lang
                iv_t_lang  = lv_target_lang
                iv_custmnr = -custmnr
                iv_objtype = -objtype
                iv_objname = -objname
                it_pcx_s1  = lt_text_pairs_tmp ).
    
              rv_changed = abap_true.
            ENDIF.
          ENDLOOP.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD deserialize_xml.
    
        DATA:
          lt_lxe_texts      TYPE ty_lxe_translations,
          ls_lxe_item       LIKE LINE OF lt_lxe_texts,
          lt_text_pairs_tmp LIKE ls_lxe_item-text_pairs.
    
        mi_xml_in->read(
          EXPORTING iv_name = iv_lxe_text_name
          CHANGING  cg_data = lt_lxe_texts ).
    
        LOOP AT lt_lxe_texts INTO ls_lxe_item.
          " Call Read first for buffer prefill
    
          lt_text_pairs_tmp = read_lxe_object_text_pair(
            iv_s_lang    = ls_lxe_item-source_lang
            iv_t_lang    = ls_lxe_item-target_lang
            iv_custmnr   = ls_lxe_item-custmnr
            iv_objtype   = ls_lxe_item-objtype
            iv_objname   = ls_lxe_item-objname
            iv_read_only = abap_false ).
    
          "Call actual Write FM
          write_lxe_object_text_pair(
            iv_s_lang  = ls_lxe_item-source_lang
            iv_t_lang  = ls_lxe_item-target_lang
            iv_custmnr = ls_lxe_item-custmnr
            iv_objtype = ls_lxe_item-objtype
            iv_objname = ls_lxe_item-objname
            it_pcx_s1  = ls_lxe_item-text_pairs ).
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD detect_unsupported_languages.
    
        check_langs_versus_installed(
          EXPORTING
            it_languages = it_languages
            it_installed = get_installed_languages( )
          IMPORTING
            et_missfits = rt_unsupported_languages ).
    
      ENDMETHOD.
    
      METHOD get_installed_languages.
    
        DATA:
          lv_index               TYPE i,
          lv_langu               TYPE sy-langu,
          lv_laiso               TYPE laiso,
          lv_installed_languages TYPE string,
          lt_language_filter     TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        IF gt_installed_languages_cache IS INITIAL.
          CALL FUNCTION 'SYSTEM_INSTALLED_LANGUAGES'
            IMPORTING
              languages       = lv_installed_languages
            EXCEPTIONS
              sapgparam_error = 1                " Error requesting profile parameter
              OTHERS          = 2.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'Fail to get system SYSTEM_INSTALLED_LANGUAGES' ).
          ENDIF.
    
          lt_language_filter = zcl_abapgit_factory=>get_environment( )->get_system_language_filter( ).
    
          DO strlen( lv_installed_languages ) TIMES.
            lv_index = sy-index - 1.
            lv_langu = lv_installed_languages+lv_index(1).
    
            IF lv_langu NOT IN lt_language_filter.
              CONTINUE.
            ENDIF.
    
            lv_laiso = langu_to_laiso_safe( lv_langu ).
            APPEND lv_laiso TO gt_installed_languages_cache.
          ENDDO.
        ENDIF.
    
        rt_languages = gt_installed_languages_cache.
    
      ENDMETHOD.
    
      METHOD get_lang_iso4.
    
        DATA ls_lang LIKE LINE OF gt_lxe_lang_cache.
        DATA lt_lang TYPE STANDARD TABLE OF lxe_t002.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_lang.
    
        IF gt_lxe_lang_cache IS INITIAL. " Cache
    
          CALL FUNCTION 'LXE_T002_GET_LANGUAGES'
            EXPORTING
              r3_lang_only = abap_true
            TABLES
              lt_lang      = lt_lang.
    
          LOOP AT lt_lang ASSIGNING .
            CLEAR ls_lang.
            ls_lang-language  = -language.
            ls_lang-r3_lang   = -r3_lang.
            IF ls_lang-language = 'zhTW'.
              ls_lang-langshort = 'ZF'.
            ELSE.
              ls_lang-langshort = to_upper( -language ).
            ENDIF.
            INSERT ls_lang INTO TABLE gt_lxe_lang_cache.
          ENDLOOP.
    
        ENDIF.
    
        READ TABLE gt_lxe_lang_cache INTO ls_lang WITH TABLE KEY iso2 COMPONENTS langshort = iv_src.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Failed to convert [{ iv_src }] lang to iso639| ).
        ENDIF.
    
        rv_iso4 = ls_lang-language.
    
      ENDMETHOD.
    
      METHOD get_lxe_object_list.
    
        DATA lv_object_name TYPE trobj_name.
    
        lv_object_name = iv_object_name.
    
        CALL FUNCTION 'LXE_OBJ_EXPAND_TRANSPORT_OBJ'
          EXPORTING
            pgmid           = 'R3TR'
            object          = iv_object_type
            obj_name        = lv_object_name
          TABLES
            ex_colob        = rt_obj_list
          EXCEPTIONS
            unknown_object  = 1
            unknown_ta_type = 2
            OTHERS          = 3.
        IF sy-subrc <> 0.
          RETURN. " Ignore error and return empty list
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_translation_languages.
    
        " Returns a list of translation languages for serialization
        " If the setting is initial, no translations shall be serialized
        " If the setting is `*`, all installed system languages shall be serialized
        " Else, the setting shall contain all languages to be serialized
    
        DATA lv_main_lang_laiso TYPE laiso.
    
        IF it_i18n_languages IS NOT INITIAL.
          READ TABLE it_i18n_languages TRANSPORTING NO FIELDS WITH KEY table_line = '*'.
          IF sy-subrc = 0.
            rt_languages = get_installed_languages( ).
          ELSE.
            check_langs_versus_installed(
              EXPORTING
                it_languages = it_i18n_languages
                it_installed = get_installed_languages( )
              IMPORTING
                et_intersection = rt_languages ).
          ENDIF.
        ENDIF.
    
        " Remove main language from translation languages
        lv_main_lang_laiso = langu_to_laiso_safe( iv_main_language ).
        DELETE rt_languages WHERE table_line = lv_main_lang_laiso.
    
      ENDMETHOD.
    
      METHOD iso4_to_iso2.
        rv_laiso = iv_lxe_lang+0(2).
      ENDMETHOD.
    
      METHOD langu_to_laiso_safe.
    
        zcl_abapgit_convert=>language_sap1_to_sap2(
          EXPORTING
            im_lang_sap1  = iv_langu
          RECEIVING
            re_lang_sap2  = rv_laiso
          EXCEPTIONS
            no_assignment = 1
            OTHERS        = 2 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Could not convert lang [{ iv_langu }] to ISO| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD read_lxe_object_text_pair.
    
        DATA:
          lv_error TYPE lxestring.
    
        TRY.
            CALL FUNCTION 'LXE_OBJ_TEXT_PAIR_READ'
              EXPORTING
                s_lang    = iv_s_lang
                t_lang    = iv_t_lang
                custmnr   = iv_custmnr
                objtype   = iv_objtype
                objname   = iv_objname
                read_only = iv_read_only
              IMPORTING
                err_msg   = lv_error  " doesn't exist in NW <= 750
              TABLES
                lt_pcx_s1 = rt_text_pairs_tmp.
            IF lv_error IS NOT INITIAL.
              zcx_abapgit_exception=>raise( lv_error ).
            ENDIF.
    
          CATCH cx_sy_dyn_call_param_not_found.
    
            CALL FUNCTION 'LXE_OBJ_TEXT_PAIR_READ'
              EXPORTING
                s_lang    = iv_s_lang
                t_lang    = iv_t_lang
                custmnr   = iv_custmnr
                objtype   = iv_objtype
                objname   = iv_objname
                read_only = iv_read_only
              TABLES
                lt_pcx_s1 = rt_text_pairs_tmp.
    
        ENDTRY.
    
        remove_irrelevant(
          EXPORTING
            iv_objtype        = iv_objtype
          CHANGING
            ct_text_pairs_tmp = rt_text_pairs_tmp ).
    
      ENDMETHOD.
    
      METHOD read_text_items.
    
        DATA:
          lt_obj_list      TYPE lxe_tt_colob,
          lv_main_lang     TYPE lxeisolang,
          ls_lxe_text_item LIKE LINE OF rt_text_items.
    
        FIELD-SYMBOLS:
             LIKE LINE OF mo_i18n_params->ms_params-translation_languages,
           LIKE LINE OF lt_obj_list.
    
        lt_obj_list = get_lxe_object_list(
          iv_object_name = iv_object_name
          iv_object_type = iv_object_type ).
    
        IF lt_obj_list IS INITIAL.
          RETURN.
        ENDIF.
    
        " Get list of languages that need to be serialized (already resolves * and installed languages)
        lv_main_lang = get_lang_iso4( langu_to_laiso_safe( mo_i18n_params->ms_params-main_language ) ).
    
        LOOP AT lt_obj_list ASSIGNING .
          CLEAR ls_lxe_text_item.
          ls_lxe_text_item-custmnr = -custmnr.
          ls_lxe_text_item-objtype = -objtype.
          ls_lxe_text_item-objname = -objname.
    
          LOOP AT mo_i18n_params->ms_params-translation_languages ASSIGNING .
            ls_lxe_text_item-source_lang = lv_main_lang.
            ls_lxe_text_item-target_lang = get_lang_iso4(  ).
            IF ls_lxe_text_item-source_lang = ls_lxe_text_item-target_lang.
              CONTINUE. " if source = target -> skip
            ENDIF.
    
            ls_lxe_text_item-text_pairs = read_lxe_object_text_pair(
              iv_s_lang    = ls_lxe_text_item-source_lang
              iv_t_lang    = ls_lxe_text_item-target_lang
              iv_custmnr   = ls_lxe_text_item-custmnr
              iv_objtype   = ls_lxe_text_item-objtype
              iv_objname   = ls_lxe_text_item-objname ).
    
            IF ls_lxe_text_item-text_pairs IS NOT INITIAL.
              APPEND ls_lxe_text_item TO rt_text_items.
            ENDIF.
          ENDLOOP.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD remove_irrelevant.
    
        IF iv_objtype = 'RPT4'.
          DELETE ct_text_pairs_tmp WHERE textkey = 'DUMMY KEY FOR DDIC FLAG COPY'. " see #7314
        ENDIF.
        " Add more when identified ...
    
      ENDMETHOD.
    
      METHOD serialize_as_po.
    
        DATA lt_lxe_texts TYPE ty_lxe_translations.
        DATA lo_po_file TYPE REF TO zcl_abapgit_po_file.
        DATA lv_lang LIKE LINE OF mo_i18n_params->ms_params-translation_languages.
        FIELD-SYMBOLS  LIKE LINE OF lt_lxe_texts.
    
        lt_lxe_texts = read_text_items(
          iv_object_name   = iv_object_name
          iv_object_type   = iv_object_type ).
    
        LOOP AT mo_i18n_params->ms_params-translation_languages INTO lv_lang.
          lv_lang = to_lower( lv_lang ).
          CREATE OBJECT lo_po_file
            EXPORTING
              iv_suppress_comments = mo_i18n_params->ms_params-suppress_po_comments
              iv_lang              = lv_lang.
          LOOP AT lt_lxe_texts ASSIGNING .
            IF iso4_to_iso2( -target_lang ) = lv_lang.
              lo_po_file->push_text_pairs(
                iv_objtype    = -objtype
                iv_objname    = -objname
                it_text_pairs = -text_pairs ).
            ENDIF.
          ENDLOOP.
          mo_files->add_i18n_file( lo_po_file ).
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD serialize_xml.
    
        DATA lt_lxe_texts TYPE ty_lxe_translations.
    
        lt_lxe_texts = read_text_items(
          iv_object_name   = iv_object_name
          iv_object_type   = iv_object_type ).
    
        IF lines( lt_lxe_texts ) > 0.
          mi_xml_out->add(
            iv_name = iv_lxe_text_name
            ig_data = lt_lxe_texts ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD write_lxe_object_text_pair.
    
        DATA:
          lv_status TYPE lxestatprc,
          lv_error  TYPE lxestring.
    
        TRY.
            CALL FUNCTION 'LXE_OBJ_TEXT_PAIR_WRITE'
              EXPORTING
                s_lang    = iv_s_lang
                t_lang    = iv_t_lang
                custmnr   = iv_custmnr
                objtype   = iv_objtype
                objname   = iv_objname
              IMPORTING
                pstatus   = lv_status
                err_msg   = lv_error  " doesn't exist in NW <= 750
              TABLES
                lt_pcx_s1 = it_pcx_s1.
            IF lv_error IS NOT INITIAL.
              zcx_abapgit_exception=>raise( lv_error ).
            ENDIF.
    
          CATCH cx_sy_dyn_call_param_not_found.
    
            CALL FUNCTION 'LXE_OBJ_TEXT_PAIR_WRITE'
              EXPORTING
                s_lang    = iv_s_lang
                t_lang    = iv_t_lang
                custmnr   = iv_custmnr
                objtype   = iv_objtype
                objname   = iv_objname
              IMPORTING
                pstatus   = lv_status
              TABLES
                lt_pcx_s1 = it_pcx_s1.
    
        ENDTRY.
    
        IF lv_status <> 'S'.
          zcx_abapgit_exception=>raise( 'Error updating translations' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_lxe_texts~deserialize.
    
        DATA lv_changed TYPE abap_bool.
    
        mo_i18n_params = io_i18n_params.
        mi_xml_in      = ii_xml.
        mo_files       = io_files.
    
        " MAYBE TODO: see comment in serialize
    
        IF 1 = 1.
          lv_changed = deserialize_from_po(
            iv_object_type = iv_object_type
            iv_object_name = iv_object_name ).
    
          IF lv_changed = abap_true.
            zcl_abapgit_factory=>get_cts_api( )->insert_transport_object(
              iv_object   = iv_object_type
              iv_obj_name = iv_object_name
              iv_package  = iv_package
              iv_language = mo_i18n_params->ms_params-main_language ).
          ENDIF.
        ELSE.
          deserialize_xml(
            iv_object_type = iv_object_type
            iv_object_name = iv_object_name ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_lxe_texts~serialize.
    
        mo_i18n_params = io_i18n_params.
        mi_xml_out     = ii_xml.
        mo_files       = io_files.
    
        " MAYBE TODO
        " if other formats are needed, including the old in-XML approach
        " here is the place to implement it. Supposed architecture:
        " I18N_PARAMS should contain an option which format to use
        " The option should be originally maintained in dot_abapgit structures (e.g. `translation_storage_format`)
        " Consequently it comes here
        " The serialize method can read it and call a corresponding submethod,
        " e.g. serialize_xml or serialize_as_po or ...
        " both ii_xml and io_files are accessible intentionally to enable both XML based or file based formats
        " access to json can be easily added too,
        " or maybe (maybe) some kind of zif_ag_object_ctl with all DAO instead
    
        IF 1 = 1.
          serialize_as_po(
            iv_object_type = iv_object_type
            iv_object_name = iv_object_name ).
        ELSE.
          serialize_xml(
            iv_object_type = iv_object_type
            iv_object_name = iv_object_name ).
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_SAP_NAMESPACE IMPLEMENTATION.
    
      METHOD zif_abapgit_sap_namespace~exists.
        DATA lv_editflag TYPE trnspace-editflag.
        DATA lo_obj TYPE REF TO object.
        DATA lo_nsp TYPE REF TO object.
        FIELD-SYMBOLS  TYPE any.
        TRY.
            SELECT SINGLE editflag FROM ('TRNSPACE') INTO lv_editflag WHERE namespace = iv_namespace.
            rv_yes = boolc( sy-subrc = 0 ).
          CATCH cx_sy_dynamic_osql_error.
            ASSIGN ('XCO_CP_SYSTEM=>NAMESPACE') TO .
            lo_obj = .
            CALL METHOD lo_obj->('IF_XCO_CP_NAMESPACE_FACTORY~FOR')
              EXPORTING
                iv_value     = iv_namespace
              RECEIVING
                ro_namespace = lo_nsp.
            CALL METHOD lo_nsp->('IF_XCO_CP_NAMESPACE~EXISTS')
              RECEIVING
                rv_exists = rv_yes.
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_namespace~is_editable.
        DATA lv_editflag TYPE trnspace-editflag.
        DATA lo_obj TYPE REF TO object.
        DATA lo_nsp TYPE REF TO object.
        FIELD-SYMBOLS  TYPE any.
        TRY.
            SELECT SINGLE editflag FROM ('TRNSPACE') INTO lv_editflag WHERE namespace = iv_namespace.
            rv_yes = boolc( sy-subrc = 0 AND lv_editflag = 'X' ).
          CATCH cx_sy_dynamic_osql_error.
            ASSIGN ('XCO_CP_SYSTEM=>NAMESPACE') TO .
            lo_obj = .
            CALL METHOD lo_obj->('IF_XCO_CP_NAMESPACE_FACTORY~FOR')
              EXPORTING
                iv_value     = iv_namespace
              RECEIVING
                ro_namespace = lo_nsp.
            CALL METHOD lo_nsp->('IF_XCO_CP_NAMESPACE~IS_CHANGEABLE')
              RECEIVING
                rv_exists = rv_yes.
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_namespace~split_by_name.
    * use this method instead of function module RS_NAME_SPLIT_NAMESPACE
        DATA lv_regex  TYPE string.
        DATA lv_length TYPE i.
        DATA lr_ex     TYPE REF TO cx_root.
    
        lv_regex = '^\/[^\/]{1,8}\/'.
    
        TRY.
            FIND REGEX lv_regex IN iv_obj_with_namespace MATCH LENGTH lv_length ##REGEX_POSIX.
          CATCH cx_root INTO lr_ex.
            zcx_abapgit_exception=>raise( lr_ex->get_text( ) ).
        ENDTRY.
    
        IF sy-subrc = 0 AND lv_length > 1.
          rs_obj_namespace-namespace = iv_obj_with_namespace(lv_length).
          rs_obj_namespace-obj_without_namespace = iv_obj_with_namespace+lv_length.
        ELSE.
          IF iv_obj_with_namespace(1) = '/'.
            zcx_abapgit_exception=>raise( |The object { iv_obj_with_namespace } has an invalid namespace| ).
          ENDIF.
          rs_obj_namespace-obj_without_namespace = iv_obj_with_namespace.
        ENDIF.
    
        IF iv_allow_slash_in_name = abap_false AND rs_obj_namespace-obj_without_namespace CA '/'.
          zcx_abapgit_exception=>raise(
           |Object without namespace { rs_obj_namespace-obj_without_namespace } contains a '/'| ).
        ENDIF.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_sap_package IMPLEMENTATION.
    
      METHOD constructor.
        mv_package = iv_package.
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_package~are_changes_recorded_in_tr_req.
    
        DATA: li_package TYPE REF TO if_package.
    
        cl_package_factory=>load_package(
          EXPORTING
            i_package_name             = mv_package
          IMPORTING
            e_package                  = li_package
          EXCEPTIONS
            object_not_existing        = 1
            unexpected_error           = 2
            intern_err                 = 3
            no_access                  = 4
            object_locked_and_modified = 5
            OTHERS                     = 6 ).
    
        CASE sy-subrc.
          WHEN 0.
            rv_are_changes_rec_in_tr_req = li_package->wbo_korr_flag.
          WHEN 1.
            " For new packages, derive from package name
            rv_are_changes_rec_in_tr_req = boolc( mv_package(1) <> '$' AND mv_package(1) <> 'T' ).
          WHEN OTHERS.
            zcx_abapgit_exception=>raise_t100( ).
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_package~check_object_type.
    
        " check package restrictions, closed package, descriptive or
        " functional package
        cl_pak_object_types=>check_object_type(
          EXPORTING
            i_working_mode         = 'I'
            i_package_name         = mv_package
            i_pgmid                = 'R3TR'
            i_object_type          = iv_obj_type
          EXCEPTIONS
            wrong_object_type      = 1
            package_not_extensible = 2
            package_not_loaded     = 3
            OTHERS                 = 4 ).
        CASE sy-subrc.
          WHEN 0.
            RETURN.
          WHEN 2.
            zcx_abapgit_exception=>raise( |Object type { iv_obj_type } not allowed for package { mv_package }| ).
          WHEN OTHERS.
            zcx_abapgit_exception=>raise_t100( ).
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_package~create.
    
        DATA: lv_err       TYPE string,
              li_package   TYPE REF TO if_package,
              ls_package   TYPE scompkdtln,
              lv_component TYPE dlvunit.
    
        ASSERT NOT is_package-devclass IS INITIAL.
    
        cl_package_factory=>load_package(
          EXPORTING
            i_package_name             = is_package-devclass
          EXCEPTIONS
            object_not_existing        = 1
            unexpected_error           = 2
            intern_err                 = 3
            no_access                  = 4
            object_locked_and_modified = 5 ).
        IF sy-subrc = 0.
          " Package already exists. We assume this is fine. Its properties might be changed later at
          " DEVC deserialization.
          RETURN.
        ENDIF.
    
        MOVE-CORRESPONDING is_package TO ls_package.
    
        " Set software component to 'HOME' if none is set at this point.
        " Otherwise SOFTWARE_COMPONENT_INVALID will be raised.
        IF ls_package-dlvunit IS INITIAL.
          ls_package-dlvunit = 'HOME'.
        ENDIF.
    
        " For transportable packages, get default transport and layer
        IF ls_package-devclass(1) <> '$' AND ls_package-pdevclass IS INITIAL.
          ls_package-pdevclass = zif_abapgit_sap_package~get_default_transport_layer( ).
        ENDIF.
    
        " Derive change recording based on software component (top level package)
        IF ls_package-parentcl IS INITIAL AND ls_package-dlvunit IS NOT INITIAL.
          "L: Local customer developments (standard)
          "Z: Local generations
          "J: Local customer developments (ABAP for cloud development)
          SELECT SINGLE component FROM cvers INTO lv_component
            WHERE component = ls_package-dlvunit AND comp_type IN ('L', 'Z', 'J').
          IF sy-subrc <> 0.
            ls_package-korrflag = abap_true.
          ENDIF.
        ENDIF.
    
        cl_package_factory=>create_new_package(
          EXPORTING
            i_reuse_deleted_object     = abap_true
    *        i_suppress_dialog          = abap_true " does not exist in 730
          IMPORTING
            e_package                  = li_package
          CHANGING
            c_package_data             = ls_package
          EXCEPTIONS
            object_already_existing    = 1
            object_just_created        = 2
            not_authorized             = 3
            wrong_name_prefix          = 4
            undefined_name             = 5
            reserved_local_name        = 6
            invalid_package_name       = 7
            short_text_missing         = 8
            software_component_invalid = 9
            layer_invalid              = 10
            author_not_existing        = 11
            component_not_existing     = 12
            component_missing          = 13
            prefix_in_use              = 14
            unexpected_error           = 15
            intern_err                 = 16
            no_access                  = 17
    *        invalid_translation_depth  = 18
    *        wrong_mainpack_value       = 19
    *        superpackage_invalid       = 20
    *        error_in_cts_checks        = 21
            OTHERS                     = 18 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        li_package->save(
    *      EXPORTING
    *        i_suppress_dialog     = abap_true    " Controls whether popups can be transmitted
          EXCEPTIONS
            object_invalid        = 1
            object_not_changeable = 2
            cancelled_in_corr     = 3
            permission_failure    = 4
            unexpected_error      = 5
            intern_err            = 6
            OTHERS                = 7 ).
        IF sy-subrc <> 0.
    
          MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
            WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 INTO lv_err.
    
          " Here we have to delete the package,
          " otherwise it would remain in the memory
          " and cannot created again in this session.
          li_package->delete(
            EXCEPTIONS
              object_not_empty      = 1
              object_not_changeable = 2
              object_invalid        = 3
              intern_err            = 4
              OTHERS                = 5 ).
    
          zcx_abapgit_exception=>raise( lv_err ).
    
        ENDIF.
    
        li_package->set_changeable( abap_false ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_package~create_child.
    
        DATA: li_parent TYPE REF TO if_package,
              ls_child  TYPE zif_abapgit_sap_package=>ty_create.
    
        cl_package_factory=>load_package(
          EXPORTING
            i_package_name             = mv_package
          IMPORTING
            e_package                  = li_parent
          EXCEPTIONS
            object_not_existing        = 1
            unexpected_error           = 2
            intern_err                 = 3
            no_access                  = 4
            object_locked_and_modified = 5 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        ls_child-devclass  = iv_child.
        ls_child-korrflag  = li_parent->wbo_korr_flag.
        ls_child-dlvunit   = li_parent->software_component.
        ls_child-component = li_parent->application_component.
        ls_child-ctext     = iv_child.
        ls_child-parentcl  = mv_package.
        ls_child-pdevclass = li_parent->transport_layer.
        ls_child-as4user   = sy-uname.
    
        zif_abapgit_sap_package~create( ls_child ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_package~create_local.
    
        DATA: ls_package TYPE zif_abapgit_sap_package=>ty_create.
    
        ls_package-devclass  = mv_package.
        ls_package-ctext     = mv_package.
        ls_package-parentcl  = '$TMP'.
        ls_package-dlvunit   = 'LOCAL'.
        ls_package-as4user   = sy-uname.
    
        zif_abapgit_sap_package~create( ls_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_package~exists.
    
        cl_package_factory=>load_package(
          EXPORTING
            i_package_name             = mv_package
          EXCEPTIONS
            object_not_existing        = 1
            unexpected_error           = 2
            intern_err                 = 3
            no_access                  = 4
            object_locked_and_modified = 5 ).
        rv_bool = boolc( sy-subrc <> 1 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_package~get.
    
        DATA li_package TYPE REF TO if_package.
    
        cl_package_factory=>load_package(
          EXPORTING
            i_package_name             = mv_package
          IMPORTING
            e_package                  = li_package
          EXCEPTIONS
            object_not_existing        = 1
            unexpected_error           = 2
            intern_err                 = 3
            no_access                  = 4
            object_locked_and_modified = 5
            OTHERS                     = 6 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        rs_package-devclass  = li_package->package_name.
        rs_package-dlvunit   = li_package->software_component.
        rs_package-component = li_package->application_component.
        rs_package-ctext     = li_package->short_text.
        rs_package-parentcl  = li_package->super_package_name.
        rs_package-pdevclass = li_package->transport_layer.
        rs_package-as4user   = li_package->changed_by.
        rs_package-korrflag  = li_package->wbo_korr_flag.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_package~get_default_transport_layer.
    
        " Get default transport layer
        TRY.
            CALL FUNCTION 'TR_GET_TRANSPORT_TARGET'
              EXPORTING
                iv_use_default             = abap_true
                iv_get_layer_only          = abap_true
              IMPORTING
                ev_layer                   = rv_transport_layer
              EXCEPTIONS
                wrong_call                 = 1
                invalid_input              = 2
                cts_initialization_failure = 3
                OTHERS                     = 4.
            IF sy-subrc <> 0.
          " Return empty layer (i.e. "local workbench request" for the package)
              CLEAR rv_transport_layer.
            ENDIF.
          CATCH cx_sy_dyn_call_illegal_func.
    * the function module doesnt exist in open-abap
            CLEAR rv_transport_layer.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_package~get_transport_type.
    
        DATA:
          lv_pkg_name TYPE e071-obj_name,
          lv_obj_name TYPE tadir-obj_name,
          lv_role     TYPE trnrole.
    
        lv_pkg_name = mv_package.
        lv_obj_name = mv_package.
    
        CALL FUNCTION 'TR_GET_REQUEST_TYPE'
          EXPORTING
            iv_pgmid          = 'R3TR'
            iv_object         = 'DEVC'
            iv_obj_name       = lv_pkg_name
          IMPORTING
            ev_request_type   = rs_transport_type-request
            ev_task_type      = rs_transport_type-task
          EXCEPTIONS
            no_request_needed = 1
            invalid_object    = 2
            system_error      = 3
            OTHERS            = 4.
    
        CASE sy-subrc.
          WHEN 0 OR 1.
            RETURN.
          WHEN 2.
            " For new packages, set to workbench request
            rs_transport_type-request = 'K'.
    
            CALL FUNCTION 'TR_GET_NAMESPACE_AND_ROLE'
              EXPORTING
                iv_pgmid                   = 'R3TR'
                iv_object                  = 'DEVC'
                iv_objname                 = lv_obj_name
              IMPORTING
                ev_role                    = lv_role
              EXCEPTIONS
                namespace_not_existing     = 1
                invalid_object             = 2
                namespace_not_determinable = 3
                OTHERS                     = 4.
            IF sy-subrc = 0 AND lv_role = 'C'.
              " Namespace with repair license requires repair task
              rs_transport_type-task = 'R'.
            ELSE.
              " Otherwise use correction task
              rs_transport_type-task = 'S'.
            ENDIF.
          WHEN OTHERS.
            zcx_abapgit_exception=>raise_t100( ).
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_package~list_subpackages.
    
        DATA: lt_list     LIKE rt_list.
    
        SELECT devclass FROM tdevc
          INTO TABLE lt_list
          WHERE parentcl = mv_package
          ORDER BY PRIMARY KEY.               "#EC CI_SUBRC "#EC CI_GENBUFF
    
        rt_list = lt_list.
        WHILE lines( lt_list ) > 0.
    
          SELECT devclass FROM tdevc
            INTO TABLE lt_list
            FOR ALL ENTRIES IN lt_list
            WHERE parentcl = lt_list-table_line
            ORDER BY PRIMARY KEY.             "#EC CI_SUBRC "#EC CI_GENBUFF
          APPEND LINES OF lt_list TO rt_list.
    
        ENDWHILE.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_package~list_superpackages.
    
        DATA: lt_list   LIKE rt_list,
              lv_parent TYPE tdevc-parentcl.
    
        APPEND mv_package TO rt_list.
    
        lv_parent = zif_abapgit_sap_package~read_parent( ).
    
        IF sy-subrc = 0 AND NOT lv_parent IS INITIAL.
          lt_list = zcl_abapgit_factory=>get_sap_package( lv_parent )->list_superpackages( ).
          APPEND LINES OF lt_list TO rt_list.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_package~read_description.
        DATA li_package TYPE REF TO if_package.
    
        cl_package_factory=>load_package(
          EXPORTING
            i_package_name             = mv_package
            i_force_reload             = abap_true
          IMPORTING
            e_package                  = li_package
          EXCEPTIONS
            object_not_existing        = 1
            unexpected_error           = 2
            intern_err                 = 3
            no_access                  = 4
            object_locked_and_modified = 5
            OTHERS                     = 6 ).
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        rv_description = li_package->short_text.
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_package~read_parent.
    
        SELECT SINGLE parentcl FROM tdevc INTO rv_parentcl
          WHERE devclass = mv_package.                      "#EC CI_GENBUFF
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Inconsistent package structure! Cannot find parent for { mv_package }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_package~read_responsible.
        SELECT SINGLE as4user FROM tdevc
          INTO rv_responsible
          WHERE devclass = mv_package ##SUBRC_OK.           "#EC CI_GENBUFF
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_package~validate_name.
    
        IF mv_package IS INITIAL.
          zcx_abapgit_exception=>raise( 'Package name must not be empty' ).
        ENDIF.
    
        IF mv_package = '$TMP'.
          zcx_abapgit_exception=>raise( 'It is not possible to use $TMP, use a different (local) package' ).
        ENDIF.
    
        " Check if package name is allowed
        cl_package_helper=>check_package_name(
          EXPORTING
            i_package_name       = mv_package
          EXCEPTIONS
            undefined_name       = 1
            wrong_name_prefix    = 2
            reserved_local_name  = 3
            invalid_package_name = 4 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Package name { mv_package } is not valid| ).
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_sap_report IMPLEMENTATION.
    
      METHOD authorization_check.
    
        IF is_item IS NOT INITIAL.
          TRY.
              CALL FUNCTION 'RS_ACCESS_PERMISSION'
                EXPORTING
                  mode                           = iv_mode
                  object                         = is_item-obj_name
                  object_class                   = is_item-obj_type
                  suppress_corr_check            = abap_true
                  suppress_language_check        = abap_true
                  suppress_extend_dialog         = abap_true
                  abap_langu_version_upon_insert = is_item-abap_language_version " does not exist on lower releases
                EXCEPTIONS
                  canceled_in_corr               = 1
                  enqueued_by_user               = 2
                  enqueue_system_failure         = 3
                  illegal_parameter_values       = 4
                  locked_by_author               = 5
                  no_modify_permission           = 6
                  no_show_permission             = 7
                  permission_failure             = 8
                  request_language_denied        = 9
                  OTHERS                         = 10 ##FM_SUBRC_OK.
            CATCH cx_sy_dyn_call_param_not_found.
              CALL FUNCTION 'RS_ACCESS_PERMISSION'
                EXPORTING
                  mode                     = iv_mode
                  object                   = is_item-obj_name
                  object_class             = is_item-obj_type
                  suppress_corr_check      = abap_true
                  suppress_language_check  = abap_true
                  suppress_extend_dialog   = abap_true
                EXCEPTIONS
                  canceled_in_corr         = 1
                  enqueued_by_user         = 2
                  enqueue_system_failure   = 3
                  illegal_parameter_values = 4
                  locked_by_author         = 5
                  no_modify_permission     = 6
                  no_show_permission       = 7
                  permission_failure       = 8
                  request_language_denied  = 9
                  OTHERS                   = 10 ##FM_SUBRC_OK.
          ENDTRY.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_report~delete_report.
    
        authorization_check(
          iv_mode = 'DELETE'
          is_item = is_item ).
    
        DELETE REPORT iv_name.
    
        IF sy-subrc <> 0 AND iv_raise_error = abap_true.
          zcx_abapgit_exception=>raise( |Error deleting report { iv_name }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_report~insert_report.
    
        ASSERT iv_state CA ' AI'.
        ASSERT iv_program_type CA ' 1FIJKMST'.
    
        authorization_check(
          iv_mode = 'MODIFY'
          is_item = is_item ).
    
        IF iv_state IS INITIAL.
          INSERT REPORT iv_name FROM it_source.
        ELSEIF iv_program_type IS INITIAL AND iv_extension_type IS INITIAL.
          INSERT REPORT iv_name FROM it_source
            STATE iv_state.
        ELSEIF iv_extension_type IS INITIAL.
          INSERT REPORT iv_name FROM it_source
            STATE iv_state
            PROGRAM TYPE iv_program_type.
        ELSE.
          INSERT REPORT iv_name FROM it_source
            STATE iv_state
            EXTENSION TYPE iv_extension_type
            PROGRAM TYPE iv_program_type.
        ENDIF.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error inserting report { iv_name }| ).
        ENDIF.
    
        " In lower releases, INSERT REPORT does not support setting ABAP Language version (VERSION)
        " Therefore, update the flag directly
        UPDATE progdir SET uccheck = iv_version WHERE name = iv_name AND state = iv_state.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_report~read_progdir.
    
        DATA ls_sapdir TYPE progdir.
    
        CALL FUNCTION 'READ_PROGDIR'
          EXPORTING
            i_progname = iv_name
            i_state    = iv_state
          IMPORTING
            e_progdir  = ls_sapdir
          EXCEPTIONS
            not_exists = 1
            OTHERS     = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        MOVE-CORRESPONDING ls_sapdir TO rs_progdir.
    
        CLEAR: rs_progdir-edtx,
               rs_progdir-cnam,
               rs_progdir-cdat,
               rs_progdir-unam,
               rs_progdir-udat,
               rs_progdir-levl,
               rs_progdir-vern,
               rs_progdir-rmand,
               rs_progdir-sdate,
               rs_progdir-stime,
               rs_progdir-idate,
               rs_progdir-itime,
               rs_progdir-varcl,
               rs_progdir-state.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_report~read_report.
    
        ASSERT iv_state CA ' AI'.
    
        authorization_check(
          iv_mode = 'SHOW'
          is_item = is_item ).
    
        IF iv_state IS INITIAL.
          READ REPORT iv_name INTO rt_source.
        ELSE.
          READ REPORT iv_name INTO rt_source STATE iv_state.
        ENDIF.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error reading report { iv_name }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_report~update_progdir.
    
        DATA ls_progdir_new TYPE progdir.
    
        CALL FUNCTION 'READ_PROGDIR'
          EXPORTING
            i_progname = is_progdir-name
            i_state    = iv_state
          IMPORTING
            e_progdir  = ls_progdir_new
          EXCEPTIONS
            not_exists = 1
            OTHERS     = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'Error reading program directory' ).
        ENDIF.
    
        ls_progdir_new-ldbname = is_progdir-ldbname.
        ls_progdir_new-dbna    = is_progdir-dbna.
        ls_progdir_new-dbapl   = is_progdir-dbapl.
        ls_progdir_new-rload   = is_progdir-rload.
        ls_progdir_new-fixpt   = is_progdir-fixpt.
        ls_progdir_new-appl    = is_progdir-appl.
        ls_progdir_new-rstat   = is_progdir-rstat.
        ls_progdir_new-uccheck = is_progdir-uccheck.
        ls_progdir_new-sqlx    = is_progdir-sqlx.
        ls_progdir_new-clas    = is_progdir-clas.
        ls_progdir_new-secu    = is_progdir-secu.
    
        CALL FUNCTION 'UPDATE_PROGDIR'
          EXPORTING
            i_progdir    = ls_progdir_new
            i_progname   = ls_progdir_new-name
            i_state      = ls_progdir_new-state
          EXCEPTIONS
            not_executed = 1
            OTHERS       = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'Error updating program directory' ).
        ENDIF.
    
        " Function UPDATE_PROGDIR does not update VARCL, so we do it here
        SELECT SINGLE * FROM progdir INTO ls_progdir_new
          WHERE name  = ls_progdir_new-name
            AND state = ls_progdir_new-state.
        IF sy-subrc = 0 AND is_progdir-varcl <> ls_progdir_new-varcl.
          UPDATE progdir SET varcl = is_progdir-varcl
            WHERE name  = ls_progdir_new-name
              AND state = ls_progdir_new-state.               "#EC CI_SUBRC
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_sap_report~update_report.
    
        DATA lt_new TYPE string_table.
        DATA lt_old TYPE string_table.
    
        lt_new = it_source.
        lt_old = zif_abapgit_sap_report~read_report( iv_name ).
    
        IF lt_old <> lt_new.
          zif_abapgit_sap_report~insert_report(
            iv_name           = iv_name
            it_source         = it_source
            iv_state          = iv_state
            iv_program_type   = iv_program_type
            iv_extension_type = iv_extension_type
            iv_package        = iv_package
            iv_version        = iv_version
            is_item           = is_item ).
    
          rv_updated = abap_true.
        ELSE.
          rv_updated = abap_false.
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_tadir IMPLEMENTATION.
    
      METHOD add_local_packages.
    
        FIELD-SYMBOLS:
           LIKE LINE OF it_packages,
             LIKE LINE OF ct_tadir.
    
        LOOP AT it_packages ASSIGNING .
    
          " Local packages are not in TADIR, only in TDEVC, act as if they were
          IF  CP '$*'. " OR  CP 'T*' ).
            APPEND INITIAL LINE TO ct_tadir ASSIGNING .
            -pgmid      = 'R3TR'.
            -object     = 'DEVC'.
            -obj_name   = .
            -devclass   = .
            -srcsystem  = sy-sysid.
            -masterlang = sy-langu.
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD add_namespace.
    
        DATA ls_tadir TYPE zif_abapgit_definitions=>ty_tadir.
        DATA ls_obj_with_namespace TYPE zif_abapgit_definitions=>ty_obj_namespace.
    
        TRY.
            ls_obj_with_namespace = zcl_abapgit_factory=>get_sap_namespace( )->split_by_name( iv_object ).
          CATCH zcx_abapgit_exception.
            "Ignore the exception like before the replacement of the FM RS_NAME_SPLIT_NAMESPACE
            RETURN.
        ENDTRY.
    
        IF ls_obj_with_namespace-namespace IS NOT INITIAL.
    
          READ TABLE ct_tadir_nspc TRANSPORTING NO FIELDS
            WITH KEY pgmid = 'R3TR' object = 'NSPC' obj_name = ls_obj_with_namespace-namespace.
          IF sy-subrc <> 0.
            ls_tadir-pgmid      = 'R3TR'.
            ls_tadir-object     = 'NSPC'.
            ls_tadir-obj_name   = ls_obj_with_namespace-namespace.
            ls_tadir-devclass   = iv_package.
            ls_tadir-srcsystem  = sy-sysid.
            ls_tadir-masterlang = sy-langu.
            INSERT ls_tadir INTO TABLE ct_tadir.
            INSERT ls_tadir INTO TABLE ct_tadir_nspc.
          ENDIF.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD add_namespaces.
    
        DATA lt_tadir_nspc TYPE zif_abapgit_definitions=>ty_tadir_tt.
    
        FIELD-SYMBOLS  LIKE LINE OF ct_tadir.
    
        " Namespaces are not in TADIR, but are necessary for creating objects in transportable packages
        LOOP AT ct_tadir ASSIGNING  WHERE obj_name(1) = '/'.
          add_namespace(
            EXPORTING
              iv_package    = iv_package
              iv_object     = -obj_name
            CHANGING
              ct_tadir      = ct_tadir
              ct_tadir_nspc = lt_tadir_nspc ).
        ENDLOOP.
    
        " Root package of repo might not exist yet but needs to be considered, too
        IF iv_package CP '/*'.
          add_namespace(
            EXPORTING
              iv_package    = iv_package
              iv_object     = iv_package
            CHANGING
              ct_tadir      = ct_tadir
              ct_tadir_nspc = lt_tadir_nspc ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD build.
    
        DATA lt_packages TYPE zif_abapgit_sap_package=>ty_devclass_tt.
    
        select_objects(
          EXPORTING
            iv_package            = iv_package
            iv_ignore_subpackages = iv_ignore_subpackages
            iv_only_local_objects = iv_only_local_objects
            iv_ignore_delflag     = iv_ignore_delflag
          IMPORTING
            et_tadir              = rt_tadir
            et_packages           = lt_packages ).
    
        add_local_packages(
          EXPORTING
            it_packages = lt_packages
          CHANGING
            ct_tadir    = rt_tadir ).
    
        add_namespaces(
          EXPORTING
            iv_package = iv_package
          CHANGING
            ct_tadir   = rt_tadir ).
    
        determine_path(
          EXPORTING
            iv_package = iv_package
            io_dot     = io_dot
          CHANGING
            ct_tadir   = rt_tadir ).
    
      ENDMETHOD.
    
      METHOD check_exists.
    
        DATA: li_progress TYPE REF TO zif_abapgit_progress,
              ls_item     TYPE zif_abapgit_definitions=>ty_item.
    
        FIELD-SYMBOLS:  LIKE LINE OF it_tadir.
    
        li_progress = zcl_abapgit_progress=>get_instance( lines( it_tadir ) ).
    
    * rows from database table TADIR are not removed for
    * transportable objects until the transport is released
        LOOP AT it_tadir ASSIGNING .
          IF sy-tabix MOD 200 = 0.
            li_progress->show(
              iv_current = sy-tabix
              iv_text    = |Check object exists { -object } { -obj_name }| ).
          ENDIF.
    
          ls_item-obj_type = -object.
          ls_item-obj_name = -obj_name.
          ls_item-devclass = -devclass.
    
          IF /apmg/cl_apm_abapgit_objects=>exists( ls_item ) = abap_true.
            APPEND  TO rt_tadir.
          ENDIF.
        ENDLOOP.
    
        li_progress->off( ).
    
      ENDMETHOD.
    
      METHOD determine_path.
    
        DATA:
          lv_path         TYPE string,
          lo_folder_logic TYPE REF TO zcl_abapgit_folder_logic,
          lv_last_package TYPE devclass VALUE cl_abap_char_utilities=>horizontal_tab.
    
        FIELD-SYMBOLS  LIKE LINE OF ct_tadir.
    
        lo_folder_logic = zcl_abapgit_folder_logic=>get_instance( ).
    
        LOOP AT ct_tadir ASSIGNING .
    
          IF lv_last_package <> -devclass.
            "Change in Package
            lv_last_package = -devclass.
    
            IF NOT io_dot IS INITIAL.
              lv_path = lo_folder_logic->package_to_path(
                iv_top     = iv_package
                io_dot     = io_dot
                iv_package = -devclass ).
            ENDIF.
          ENDIF.
    
          -path = lv_path.
          -korrnum = ''.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD select_objects.
    
        DATA:
          lt_excludes  TYPE RANGE OF trobjtype,
          ls_exclude   LIKE LINE OF lt_excludes,
          lt_srcsystem TYPE RANGE OF tadir-srcsystem,
          lt_delflag   TYPE RANGE OF tadir-delflag,
          ls_delflag   LIKE LINE OF lt_delflag,
          ls_srcsystem LIKE LINE OF lt_srcsystem.
    
        " Determine packages to read
        IF iv_ignore_subpackages = abap_false.
          et_packages = zcl_abapgit_factory=>get_sap_package( iv_package )->list_subpackages( ).
        ENDIF.
        INSERT iv_package INTO et_packages INDEX 1.
    
        " Exclude object types with tadir entries that are included elsewhere
        ls_exclude-sign   = 'I'.
        ls_exclude-option = 'EQ'.
        ls_exclude-low    = 'SOTR'. " automatically created for SAP packages (DEVC)
        APPEND ls_exclude TO lt_excludes.
        ls_exclude-low    = 'SOTS'. " automatically created for SAP packages (DEVC)
        APPEND ls_exclude TO lt_excludes.
        ls_exclude-low    = 'SFB1'. " covered by business function sets (SFBS)
        APPEND ls_exclude TO lt_excludes.
        ls_exclude-low    = 'SFB2'. " covered by business functions (SFBF)
        APPEND ls_exclude TO lt_excludes.
        ls_exclude-low    = 'STOB'. " auto generated by core data services (DDLS)
        APPEND ls_exclude TO lt_excludes.
    
        " Limit to objects belonging to this system
        IF iv_only_local_objects = abap_true.
          ls_srcsystem-sign   = 'I'.
          ls_srcsystem-option = 'EQ'.
          ls_srcsystem-low    = sy-sysid.
          APPEND ls_srcsystem TO lt_srcsystem.
        ENDIF.
    
        IF iv_ignore_delflag = abap_false.
          ls_delflag-sign   = 'I'.
          ls_delflag-option = 'EQ'.
          ls_delflag-low    = abap_false.
          APPEND ls_delflag TO lt_delflag.
        ENDIF.
    
        IF et_packages IS NOT INITIAL.
          SELECT * FROM tadir INTO CORRESPONDING FIELDS OF TABLE et_tadir
            FOR ALL ENTRIES IN et_packages
            WHERE devclass = et_packages-table_line
            AND pgmid      = 'R3TR'
            AND object     NOT IN lt_excludes
            AND delflag    IN lt_delflag
            AND srcsystem  IN lt_srcsystem
            ORDER BY PRIMARY KEY ##TOO_MANY_ITAB_FIELDS. "#EC CI_GENBUFF "#EC CI_SUBRC
        ENDIF.
    
        SORT et_tadir BY devclass pgmid object obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_tadir~delete_single.
    
        DATA ls_tadir TYPE tadir.
    
        " cast
        ls_tadir-pgmid    = iv_pgmid.
        ls_tadir-object   = iv_object.
        ls_tadir-obj_name = iv_obj_name.
    
        CALL FUNCTION 'TR_TADIR_INTERFACE'
          EXPORTING
            wi_delete_tadir_entry          = abap_true
            wi_tadir_pgmid                 = ls_tadir-pgmid
            wi_tadir_object                = ls_tadir-object
            wi_tadir_obj_name              = ls_tadir-obj_name
            wi_test_modus                  = abap_false
          EXCEPTIONS
            tadir_entry_not_existing       = 1
            tadir_entry_ill_type           = 2
            no_systemname                  = 3
            no_systemtype                  = 4
            original_system_conflict       = 5
            object_reserved_for_devclass   = 6
            object_exists_global           = 7
            object_exists_local            = 8
            object_is_distributed          = 9
            obj_specification_not_unique   = 10
            no_authorization_to_delete     = 11
            devclass_not_existing          = 12
            simultanious_set_remove_repair = 13
            order_missing                  = 14
            no_modification_of_head_syst   = 15
            pgmid_object_not_allowed       = 16
            masterlanguage_not_specified   = 17
            devclass_not_specified         = 18
            specify_owner_unique           = 19
            loc_priv_objs_no_repair        = 20
            gtadir_not_reached             = 21
            object_locked_for_order        = 22
            change_of_class_not_allowed    = 23
            no_change_from_sap_to_tmp      = 24
            OTHERS                         = 25.
        IF sy-subrc > 1.
          " No error if entry does not exist
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_tadir~get_object_package.
    
        DATA: ls_tadir TYPE zif_abapgit_definitions=>ty_tadir,
              ls_item  TYPE zif_abapgit_definitions=>ty_item.
    
        ls_tadir = zif_abapgit_tadir~read_single(
          iv_pgmid    = iv_pgmid
          iv_object   = iv_object
          iv_obj_name = iv_obj_name ).
    
        IF ls_tadir-delflag = abap_true.
          RETURN. "Mark for deletion -> return nothing
        ENDIF.
    
        ls_item-obj_type = ls_tadir-object.
        ls_item-obj_name = ls_tadir-obj_name.
        ls_item-devclass = ls_tadir-devclass.
    
        IF /apmg/cl_apm_abapgit_objects=>exists( ls_item ) = abap_false.
          RETURN.
        ENDIF.
    
        rv_devclass = ls_tadir-devclass.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_tadir~insert_single.
    
        DATA ls_tadir TYPE tadir.
    
        " cast
        ls_tadir-pgmid    = iv_pgmid.
        ls_tadir-object   = iv_object.
        ls_tadir-obj_name = iv_obj_name.
        ls_tadir-devclass = iv_package.
    
        CALL FUNCTION 'TR_TADIR_INTERFACE'
          EXPORTING
            wi_test_modus                  = abap_false
            wi_tadir_pgmid                 = ls_tadir-pgmid
            wi_tadir_object                = ls_tadir-object
            wi_tadir_obj_name              = ls_tadir-obj_name
            wi_tadir_author                = sy-uname
            wi_tadir_devclass              = ls_tadir-devclass
            wi_tadir_masterlang            = iv_language
            wi_tadir_srcsystem             = iv_srcsystem
            wi_set_genflag                 = iv_set_genflag
            iv_set_edtflag                 = iv_set_edtflag
          EXCEPTIONS
            tadir_entry_not_existing       = 1
            tadir_entry_ill_type           = 2
            no_systemname                  = 3
            no_systemtype                  = 4
            original_system_conflict       = 5
            object_reserved_for_devclass   = 6
            object_exists_global           = 7
            object_exists_local            = 8
            object_is_distributed          = 9
            obj_specification_not_unique   = 10
            no_authorization_to_delete     = 11
            devclass_not_existing          = 12
            simultanious_set_remove_repair = 13
            order_missing                  = 14
            no_modification_of_head_syst   = 15
            pgmid_object_not_allowed       = 16
            masterlanguage_not_specified   = 17
            devclass_not_specified         = 18
            specify_owner_unique           = 19
            loc_priv_objs_no_repair        = 20
            gtadir_not_reached             = 21
            object_locked_for_order        = 22
            change_of_class_not_allowed    = 23
            no_change_from_sap_to_tmp      = 24
            OTHERS                         = 25.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_tadir~read.
    
        DATA li_exit TYPE REF TO zif_abapgit_exit.
        DATA lr_tadir TYPE REF TO zif_abapgit_definitions=>ty_tadir.
        DATA lt_filter TYPE zif_abapgit_definitions=>ty_tadir_tt.
        DATA ls_dot_data TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit.
    
        ASSERT iv_package IS NOT INITIAL.
    
        " Start recursion
        " hmm, some problems here, should TADIR also build path?
        rt_tadir = build(
          iv_package            = iv_package
          io_dot                = io_dot
          iv_ignore_subpackages = iv_ignore_subpackages
          iv_only_local_objects = iv_only_local_objects
          iv_ignore_delflag     = iv_ignore_delflag ).
    
        IF io_dot IS NOT INITIAL.
          ls_dot_data = io_dot->get_data( ).
        ENDIF.
    
        li_exit = zcl_abapgit_exit=>get_instance( ).
        li_exit->change_tadir(
          EXPORTING
            iv_package            = iv_package
            ii_log                = ii_log
            is_dot_abapgit        = ls_dot_data
            iv_ignore_subpackages = iv_ignore_subpackages
            iv_only_local_objects = iv_only_local_objects
          CHANGING
            ct_tadir              = rt_tadir ).
    
        IF it_filter IS NOT INITIAL.
          "Apply filter manually instead of calling zcl_abapgit_repo_filter->apply,
          "so that we can execute a unit test. The method applies addition filtering
          "and does therefore additional selects
          lt_filter = it_filter.
          SORT lt_filter BY object obj_name.
          LOOP AT rt_tadir REFERENCE INTO lr_tadir.
            READ TABLE lt_filter TRANSPORTING NO FIELDS
                     WITH KEY object = lr_tadir->object
                              obj_name = lr_tadir->obj_name
                              BINARY SEARCH.
            IF sy-subrc <> 0.
              DELETE rt_tadir.
            ENDIF.
          ENDLOOP.
        ENDIF.
    
        IF iv_check_exists = abap_true.
          rt_tadir = check_exists( rt_tadir ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_tadir~read_single.
    
        SELECT SINGLE * FROM tadir INTO CORRESPONDING FIELDS OF rs_tadir
          WHERE pgmid = iv_pgmid
          AND object = iv_object
          AND obj_name = iv_obj_name.                         "#EC CI_SUBRC
        CLEAR rs_tadir-korrnum.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_factory IMPLEMENTATION.
    
      METHOD get_cts_api.
        IF gi_cts_api IS NOT BOUND.
          CREATE OBJECT gi_cts_api TYPE zcl_abapgit_cts_api.
        ENDIF.
    
        ri_cts_api = gi_cts_api.
      ENDMETHOD.
    
      METHOD get_default_transport.
    
        IF gi_default_transport IS NOT BOUND.
          CREATE OBJECT gi_default_transport TYPE zcl_abapgit_default_transport.
        ENDIF.
    
        ri_default_transport = gi_default_transport.
    
      ENDMETHOD.
    
      METHOD get_environment.
        IF gi_environment IS NOT BOUND.
          CREATE OBJECT gi_environment TYPE zcl_abapgit_environment.
        ENDIF.
        ri_environment = gi_environment.
      ENDMETHOD.
    
      METHOD get_function_module.
    
        IF gi_function_module IS INITIAL.
          CREATE OBJECT gi_function_module TYPE zcl_abapgit_function_module.
        ENDIF.
    
        ri_function_module = gi_function_module.
    
      ENDMETHOD.
    
      METHOD get_longtexts.
    
        IF gi_longtext IS NOT BOUND.
          CREATE OBJECT gi_longtext TYPE zcl_abapgit_longtexts.
        ENDIF.
        ri_longtexts = gi_longtext.
    
      ENDMETHOD.
    
      METHOD get_lxe_texts.
    
        IF gi_lxe_texts IS NOT BOUND.
          CREATE OBJECT gi_lxe_texts TYPE zcl_abapgit_lxe_texts.
        ENDIF.
        ri_lxe_texts = gi_lxe_texts.
    
      ENDMETHOD.
    
      METHOD get_sap_namespace.
    
        IF gi_sap_namespace IS NOT BOUND.
          CREATE OBJECT gi_sap_namespace TYPE zcl_abapgit_sap_namespace.
        ENDIF.
    
        ri_namespace = gi_sap_namespace.
    
      ENDMETHOD.
    
      METHOD get_sap_package.
    
        DATA ls_sap_package TYPE ty_sap_package.
        FIELD-SYMBOLS  TYPE ty_sap_package.
    
        ASSERT iv_package IS NOT INITIAL.
    
        READ TABLE gt_sap_package ASSIGNING 
                                  WITH TABLE KEY package = iv_package.
        IF sy-subrc <> 0.
    
          ls_sap_package-package = iv_package.
          CREATE OBJECT ls_sap_package-instance TYPE zcl_abapgit_sap_package
            EXPORTING
              iv_package = iv_package.
    
          INSERT ls_sap_package
                 INTO TABLE gt_sap_package
                 ASSIGNING .
    
        ENDIF.
    
        ri_sap_package = -instance.
    
      ENDMETHOD.
    
      METHOD get_sap_report.
    
        IF gi_sap_report IS NOT BOUND.
          CREATE OBJECT gi_sap_report TYPE zcl_abapgit_sap_report.
        ENDIF.
    
        ri_report = gi_sap_report.
    
      ENDMETHOD.
    
      METHOD get_tadir.
    
        IF gi_tadir IS INITIAL.
          CREATE OBJECT gi_tadir TYPE zcl_abapgit_tadir.
        ENDIF.
    
        ri_tadir = gi_tadir.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_feature IMPLEMENTATION.
    
      METHOD is_enabled.
    
        DATA:
          lv_features TYPE string,
          lt_features TYPE string_table.
    
        IF zcl_abapgit_factory=>get_environment( )->is_merged( ) = abap_true.
          RETURN.
        ENDIF.
    
        lv_features = /apmg/cl_apm_settings=>factory( )->get( )-experimental_features.
        CONDENSE lv_features NO-GAPS.
    
        rv_run = boolc( lv_features = abap_true ).
    
        IF iv_feature IS NOT INITIAL.
          SPLIT lv_features AT ',' INTO TABLE lt_features.
          READ TABLE lt_features TRANSPORTING NO FIELDS WITH TABLE KEY table_line = iv_feature.
          rv_run = boolc( rv_run = abap_true OR sy-subrc = 0 ).
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_field_rules IMPLEMENTATION.
    
      METHOD create.
        CREATE OBJECT ro_result TYPE zcl_abapgit_field_rules.
      ENDMETHOD.
    
      METHOD fill_value.
        DATA lv_timestamp TYPE timestampl.
        CASE iv_rule.
          WHEN zif_abapgit_field_rules=>c_fill_rule-date.
            cv_value = sy-datum.
          WHEN zif_abapgit_field_rules=>c_fill_rule-time.
            cv_value = sy-uzeit.
          WHEN zif_abapgit_field_rules=>c_fill_rule-timestamp.
            GET TIME STAMP FIELD lv_timestamp.
            cv_value = lv_timestamp.
          WHEN zif_abapgit_field_rules=>c_fill_rule-user.
            cv_value = sy-uname.
          WHEN zif_abapgit_field_rules=>c_fill_rule-client.
            cv_value = sy-mandt.
          WHEN zif_abapgit_field_rules=>c_fill_rule-package.
            cv_value = iv_package.
          WHEN zif_abapgit_field_rules=>c_fill_rule-abap_language_version.
            cv_value = iv_abap_language_version.
        ENDCASE.
      ENDMETHOD.
    
      METHOD zif_abapgit_field_rules~add.
        DATA ls_item TYPE ty_item.
    
        ls_item-tabname   = iv_table.
        ls_item-fieldname = iv_field.
        ls_item-fill_rule = iv_fill_rule.
        INSERT ls_item INTO TABLE mt_item.
    
        ro_self = me.
      ENDMETHOD.
    
      METHOD zif_abapgit_field_rules~apply_clear_logic.
        DATA ls_item TYPE ty_item.
    
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE any.
    
        IF mt_item IS INITIAL.
          RETURN.
        ENDIF.
    
        LOOP AT ct_data ASSIGNING .
          LOOP AT mt_item INTO ls_item WHERE tabname = iv_table.
            ASSIGN COMPONENT ls_item-fieldname OF STRUCTURE  TO .
            IF sy-subrc = 0.
              CLEAR .
            ENDIF.
          ENDLOOP.
        ENDLOOP.
      ENDMETHOD.
    
      METHOD zif_abapgit_field_rules~apply_fill_logic.
        DATA ls_item TYPE ty_item.
    
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE any.
    
        IF mt_item IS INITIAL.
          RETURN.
        ENDIF.
    
        LOOP AT ct_data ASSIGNING .
          LOOP AT mt_item INTO ls_item WHERE tabname = iv_table.
            ASSIGN COMPONENT ls_item-fieldname OF STRUCTURE  TO .
            IF sy-subrc = 0.
              fill_value(
                EXPORTING
                  iv_rule                  = ls_item-fill_rule
                  iv_package               = iv_package
                  iv_abap_language_version = iv_abap_language_version
                CHANGING
                  cv_value                 =  ).
            ENDIF.
          ENDLOOP.
        ENDLOOP.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_FILENAME_LOGIC IMPLEMENTATION.
    
      METHOD detect_obj_definition.
    
        ev_is_xml  = boolc( iv_ext = to_upper( c_package_file-extension ) AND strlen( iv_type ) = 4 ).
        ev_is_json = boolc( iv_ext = to_upper( c_json_file-extension ) AND strlen( iv_type ) = 4 ).
    
      ENDMETHOD.
    
      METHOD file_to_object.
    
        DATA:
          lv_name TYPE string,
          lv_type TYPE string,
          lv_ext  TYPE string.
    
        " Guess object type and name
        SPLIT iv_filename AT '.' INTO lv_name lv_type lv_ext.
        lv_type = to_upper( lv_type ).
        lv_ext  = to_upper( lv_ext ).
    
        " Handle namespaces
        REPLACE ALL OCCURRENCES OF '#' IN lv_name WITH '/'.
        REPLACE ALL OCCURRENCES OF '#' IN lv_type WITH '/'.
        REPLACE ALL OCCURRENCES OF '#' IN lv_ext WITH '/'.
    
        " Assume AFF namespace convention
        IF zcl_abapgit_aff_factory=>get_registry( )->is_supported_object_type( |{ lv_type }| ) = abap_true.
          REPLACE ALL OCCURRENCES OF '(' IN lv_name WITH '/'.
          REPLACE ALL OCCURRENCES OF ')' IN lv_name WITH '/'.
        ENDIF.
    
        " Get original object name
        lv_name = name_unescape( lv_name ).
    
        CLEAR es_item.
        es_item-obj_type = lv_type.
        es_item-obj_name = to_upper( lv_name ).
    
        " Get mapping specific to object type
        map_filename_to_object(
          EXPORTING
            iv_item_part_of_filename = lv_name " original-cased object name part only
            iv_path     = iv_path
            io_dot      = io_dot
            iv_package  = iv_devclass
          CHANGING
            cs_item     = es_item ).
    
        detect_obj_definition(
          EXPORTING
            iv_ext     = lv_ext
            iv_type    = lv_type
          IMPORTING
            ev_is_xml  = ev_is_xml
            ev_is_json = ev_is_json ).
    
      ENDMETHOD.
    
      METHOD get_lang_and_ext.
    
        DATA lt_filename_elements TYPE string_table.
        DATA lv_lang_suffix TYPE string.
        DATA lv_sap1 TYPE sy-langu.
    
        SPLIT iv_filename AT '.' INTO TABLE lt_filename_elements.
    
        READ TABLE lt_filename_elements INDEX lines( lt_filename_elements ) INTO ev_ext.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Could not derive file extension of file { iv_filename }| ).
        ENDIF.
    
        READ TABLE lt_filename_elements WITH KEY table_line = `i18n` TRANSPORTING NO FIELDS.
        IF sy-subrc = 0.
          READ TABLE lt_filename_elements INDEX ( sy-tabix + 1 ) INTO lv_lang_suffix.
          IF sy-subrc = 0.
            IF ev_ext = `po`.
              ev_lang = to_lower( lv_lang_suffix ).
            ELSEIF ev_ext = `properties`.
              lv_sap1 = zcl_abapgit_convert=>language_bcp47_to_sap1( lv_lang_suffix ).
              ev_lang = zcl_abapgit_convert=>language_sap1_to_sap2( lv_sap1 ). " actually it is to_upper( ISO-639 )
            ELSE.
              zcx_abapgit_exception=>raise( |Unexpected translation file format { iv_filename }| ).
            ENDIF.
          ENDIF.
        ENDIF.
    
        IF ev_lang IS INITIAL.
          CLEAR ev_ext.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD i18n_file_to_object.
    
        DATA lo_dot TYPE REF TO zcl_abapgit_dot_abapgit.
    
        CLEAR: es_item, ev_lang, ev_ext.
        lo_dot = zcl_abapgit_dot_abapgit=>build_default( ).
    
        file_to_object(
          EXPORTING
            iv_filename = iv_filename
            iv_path     = iv_path
            io_dot      = lo_dot
          IMPORTING
            es_item     = es_item ).
    
        get_lang_and_ext(
          EXPORTING
            iv_filename = iv_filename
          IMPORTING
            ev_lang     = ev_lang
            ev_ext      = ev_ext ).
    
      ENDMETHOD.
    
      METHOD is_obj_definition_file.
    
        DATA:
          lv_xml  TYPE abap_bool,
          lv_json TYPE abap_bool,
          lv_name TYPE string,
          lv_type TYPE string,
          lv_ext  TYPE string.
    
        SPLIT to_upper( iv_filename ) AT '.' INTO lv_name lv_type lv_ext.
    
        detect_obj_definition(
          EXPORTING
            iv_ext     = lv_ext
            iv_type    = lv_type
          IMPORTING
            ev_is_xml  = lv_xml
            ev_is_json = lv_json ).
    
        rv_yes = boolc( lv_json = abap_true OR lv_xml = abap_true ).
    
      ENDMETHOD.
    
      METHOD map_filename_to_object.
    
        DATA lv_class TYPE seoclsname.
    
        " TODO: Add check for supported object types to avoid calls to non-existing classes
        " /apmg/cl_apm_abapgit_objects=>is_type_supported( is_item-obj_type )
        " This will trigger class constructor of zcl_abapgit_objects_bridge reading table seometarel
        " which is currently not supported by abaplint test runner
    
        TRY.
            lv_class = 'ZCL_ABAPGIT_OBJECT_' && cs_item-obj_type.
    
            CALL METHOD (lv_class)=>('ZIF_ABAPGIT_OBJECT~MAP_FILENAME_TO_OBJECT')
              EXPORTING
                iv_item_part_of_filename = iv_item_part_of_filename
                iv_path     = iv_path
                io_dot      = io_dot
                iv_package  = iv_package
              CHANGING
                cs_item     = cs_item.
          CATCH cx_sy_dyn_call_illegal_class.
            " Map data config to TABU object type
            IF cs_item-obj_type = 'CONF'.
              cs_item-obj_type = 'TABU'.
            ENDIF.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD map_object_to_filename.
    
        DATA lv_class TYPE seoclsname.
    
        " TODO: Add check for supported object types to avoid calls to non-existing classes
        " /apmg/cl_apm_abapgit_objects=>is_type_supported( is_item-obj_type )
        " This will trigger class constructor of zcl_abapgit_objects_bridge reading table seometarel
        " which is currently not supported by abaplint test runner
    
        " TODO: maybe refactor the logic, as currently only 2 object types have own naming
        " the map_* methods are static, so they cannot reuse ms_item passed to the class
        " and the "custom" naming is scattered among the large codebase
    
        TRY.
            lv_class = 'ZCL_ABAPGIT_OBJECT_' && is_item-obj_type.
    
            CALL METHOD (lv_class)=>('ZIF_ABAPGIT_OBJECT~MAP_OBJECT_TO_FILENAME')
              EXPORTING
                is_item     = is_item
                iv_ext      = iv_ext
                iv_extra    = iv_extra
              CHANGING
                cv_item_part_of_filename = cv_item_part_of_filename.
          CATCH cx_sy_dyn_call_illegal_class ##NO_HANDLER.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD name_escape.
        " Some characters in object names cause problems when identifying the object later
        " -> we escape these characters here
        " cl_http_utility=>escape_url doesn't do dots but escapes slash which we use for namespaces
        " -> we escape just some selected characters
        rv_name = iv_name.
        REPLACE ALL OCCURRENCES OF `#` IN rv_name WITH '%23'.
        REPLACE ALL OCCURRENCES OF `%` IN rv_name WITH '%25'.
        REPLACE ALL OCCURRENCES OF `.` IN rv_name WITH '%2e'.
        REPLACE ALL OCCURRENCES OF `<` IN rv_name WITH '%3c'.
        REPLACE ALL OCCURRENCES OF `=` IN rv_name WITH '%3d'.
        REPLACE ALL OCCURRENCES OF `>` IN rv_name WITH '%3e'.
        REPLACE ALL OCCURRENCES OF `?` IN rv_name WITH '%3f'.
      ENDMETHOD.
    
      METHOD name_unescape.
        " Replace all %xy with encoded character
        rv_name = cl_http_utility=>unescape_url( iv_name ).
      ENDMETHOD.
    
      METHOD object_to_file.
    
        DATA lv_obj_name TYPE string.
        DATA lv_obj_type TYPE string.
        DATA lv_nb_of_slash TYPE string.
        DATA lv_keep_case TYPE abap_bool.
    
        " Get escaped object name
        lv_obj_name = to_lower( name_escape( is_item-obj_name ) ).
        lv_obj_type = to_lower( is_item-obj_type ).
    
        rv_filename = lv_obj_name.
    
        " Get mapping specific to object type
        TRY.
            map_object_to_filename(
              EXPORTING
                is_item     = is_item
                iv_ext      = iv_ext
                iv_extra    = iv_extra
              CHANGING
                cv_item_part_of_filename = rv_filename ).
          CATCH zcx_abapgit_exception ##NO_HANDLER.
        ENDTRY.
    
        CONCATENATE rv_filename '.' lv_obj_type INTO rv_filename.
    
        IF iv_extra IS NOT INITIAL.
          CONCATENATE rv_filename '.' iv_extra INTO rv_filename.
        ENDIF.
    
        IF iv_ext IS NOT INITIAL.
          CONCATENATE rv_filename '.' iv_ext INTO rv_filename.
        ENDIF.
    
        IF zcl_abapgit_aff_factory=>get_registry( )->is_supported_object_type( is_item-obj_type ) = abap_true.
          FIND ALL OCCURRENCES OF `/` IN rv_filename MATCH COUNT lv_nb_of_slash.
          IF lv_nb_of_slash = 2.
            REPLACE FIRST OCCURRENCE OF `/` IN rv_filename WITH `(`.
            REPLACE `/` IN rv_filename WITH `)`.
          ENDIF.
        ELSE.
          REPLACE ALL OCCURRENCES OF '/' IN rv_filename WITH '#'.
        ENDIF.
    
        IF iv_ext = 'properties'.
          lv_keep_case = abap_true.
        ENDIF.
    
        IF lv_keep_case = abap_false. " The default behavior is to lowercase all filenames
          TRANSLATE rv_filename TO LOWER CASE.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD object_to_i18n_file.
    
        rv_filename = object_to_file(
          is_item  = is_item
          iv_extra = |i18n.{ iv_lang_suffix }|
          iv_ext   = iv_ext ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS lcl_package_to_path_cache DEFINITION.
    
      PUBLIC SECTION.
    
        CLASS-METHODS get
          IMPORTING
            !iv_top        TYPE devclass
            !io_dot        TYPE REF TO zcl_abapgit_dot_abapgit
            !iv_package    TYPE devclass
          RETURNING
            VALUE(rv_path) TYPE string.
    
        CLASS-METHODS add
          IMPORTING
            !iv_top     TYPE devclass
            !io_dot     TYPE REF TO zcl_abapgit_dot_abapgit
            !iv_package TYPE devclass
            !iv_path    TYPE string.
    
      PRIVATE SECTION.
    
        TYPES:
          BEGIN OF ty_buffer,
            top             TYPE devclass,
            starting_folder TYPE string,
            folder_logic    TYPE string,
            package         TYPE devclass,
            path            TYPE string,
          END OF ty_buffer.
    
        CLASS-DATA gt_buffer
          TYPE HASHED TABLE OF ty_buffer
          WITH UNIQUE KEY top starting_folder folder_logic package.
    
    ENDCLASS.
    
    CLASS lcl_package_to_path_cache IMPLEMENTATION.
    
      METHOD get.
    
        FIELD-SYMBOLS  LIKE LINE OF gt_buffer.
    
        READ TABLE gt_buffer ASSIGNING  WITH TABLE KEY
          top             = iv_top
          starting_folder = io_dot->get_starting_folder( )
          folder_logic    = io_dot->get_folder_logic( )
          package         = iv_package.
        IF sy-subrc = 0.
          rv_path = -path.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD add.
    
        DATA ls_buffer LIKE LINE OF gt_buffer.
    
        CLEAR ls_buffer.
        ls_buffer-top             = iv_top.
        ls_buffer-starting_folder = io_dot->get_starting_folder( ).
        ls_buffer-folder_logic    = io_dot->get_folder_logic( ).
        ls_buffer-package         = iv_package.
        ls_buffer-path            = iv_path.
        INSERT ls_buffer INTO TABLE gt_buffer.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS lcl_path_to_package_cache DEFINITION.
    
      PUBLIC SECTION.
    
        CLASS-METHODS get
          IMPORTING
            !iv_top           TYPE devclass
            !io_dot           TYPE REF TO zcl_abapgit_dot_abapgit
            !iv_path          TYPE string
          RETURNING
            VALUE(rv_package) TYPE devclass.
    
        CLASS-METHODS add
          IMPORTING
            !iv_top     TYPE devclass
            !io_dot     TYPE REF TO zcl_abapgit_dot_abapgit
            !iv_path    TYPE string
            !iv_package TYPE devclass.
    
      PRIVATE SECTION.
    
        TYPES:
          BEGIN OF ty_buffer,
            top             TYPE devclass,
            starting_folder TYPE string,
            folder_logic    TYPE string,
            path            TYPE string,
            package         TYPE devclass,
          END OF ty_buffer.
    
        CLASS-DATA gt_buffer
          TYPE HASHED TABLE OF ty_buffer
          WITH UNIQUE KEY top starting_folder folder_logic path.
    
    ENDCLASS.
    
    CLASS lcl_path_to_package_cache IMPLEMENTATION.
    
      METHOD get.
    
        FIELD-SYMBOLS  LIKE LINE OF gt_buffer.
    
        READ TABLE gt_buffer ASSIGNING  WITH TABLE KEY
          top             = iv_top
          starting_folder = io_dot->get_starting_folder( )
          folder_logic    = io_dot->get_folder_logic( )
          path            = iv_path.
        IF sy-subrc = 0.
          rv_package = -package.
          RETURN.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD add.
    
        DATA ls_buffer LIKE LINE OF gt_buffer.
    
        CLEAR ls_buffer.
        ls_buffer-top             = iv_top.
        ls_buffer-starting_folder = io_dot->get_starting_folder( ).
        ls_buffer-folder_logic    = io_dot->get_folder_logic( ).
        ls_buffer-path            = iv_path.
        ls_buffer-package         = iv_package.
        INSERT ls_buffer INTO TABLE gt_buffer.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_folder_logic IMPLEMENTATION.
    
      METHOD get_instance.
        CREATE OBJECT ro_instance.
      ENDMETHOD.
    
      METHOD get_parent.
        DATA: ls_parent LIKE LINE OF mt_parent.
    
        " Check that package is included in the TOP package hierarchy
        IF mt_top_subpackages IS INITIAL.
          mt_top_subpackages = zcl_abapgit_factory=>get_sap_package( iv_top )->list_subpackages( ).
        ENDIF.
    
        READ TABLE mt_top_subpackages TRANSPORTING NO FIELDS WITH KEY devclass = iv_package.
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        "Determine Parent Package
        READ TABLE mt_parent INTO ls_parent
          WITH TABLE KEY devclass = iv_package.
        IF sy-subrc <> 0.
          rv_parent = zcl_abapgit_factory=>get_sap_package( iv_package )->read_parent( ).
          ls_parent-devclass = iv_package.
          ls_parent-parentcl = rv_parent.
          INSERT ls_parent INTO TABLE mt_parent.
        ELSE.
          rv_parent = ls_parent-parentcl.
        ENDIF.
      ENDMETHOD.
    
      METHOD package_to_path.
    
        DATA: lv_len          TYPE i,
              lv_path         TYPE string,
              lv_message      TYPE string,
              lv_parentcl     TYPE tdevc-parentcl,
              lv_folder_logic TYPE string.
    
        rv_path = lcl_package_to_path_cache=>get(
          iv_top     = iv_top
          io_dot     = io_dot
          iv_package = iv_package ).
        IF rv_path IS NOT INITIAL.
          RETURN.
        ENDIF.
    
        IF iv_top = iv_package.
          rv_path = io_dot->get_starting_folder( ).
        ELSE.
          lv_parentcl = get_parent(
            iv_top     = iv_top
            iv_package = iv_package ).
    
          " If the parent package can not be determined, we return an initial path and handle
          " it outside of this class (in zcl_abapgit_file_status)
          IF lv_parentcl IS NOT INITIAL.
            lv_folder_logic = io_dot->get_folder_logic( ).
            CASE lv_folder_logic.
              WHEN zif_abapgit_dot_abapgit=>c_folder_logic-full.
                lv_len = 0.
                IF iv_package(1) = '$'.
                  lv_len = 1.
                ENDIF.
              WHEN zif_abapgit_dot_abapgit=>c_folder_logic-prefix.
                lv_len = strlen( lv_parentcl ).
    
                IF iv_package(lv_len) <> lv_parentcl.
                  " If abapGit project is installed in package ZZZ, all subpackages should be named
                  " ZZZ_something. This will define the folder name in the zip file to be "something",
                  " similarly with online projects. Alternatively change to FULL folder logic
                  lv_message = |PREFIX: Unexpected package naming |
                            && |(top: { iv_top }, parent: { lv_parentcl }, child: { iv_package }). |
                            && |Try using the folder logic FULL|.
                  zcx_abapgit_exception=>raise( lv_message ).
                ENDIF.
              WHEN zif_abapgit_dot_abapgit=>c_folder_logic-mixed.
                lv_len = strlen( iv_top ).
    
                IF iv_package(lv_len) <> iv_top.
                  lv_message = |MIXED: Unexpected package naming |
                            && |(top: { iv_top }, parent: { lv_parentcl }, child: { iv_package }). |
                            && |Try using the folder logic FULL|.
                  zcx_abapgit_exception=>raise( lv_message ).
                ENDIF.
              WHEN OTHERS.
                zcx_abapgit_exception=>raise( |Invalid folder logic: { lv_folder_logic }| ).
            ENDCASE.
    
            lv_path = iv_package+lv_len.
            IF strlen( lv_path ) = 0.
              zcx_abapgit_exception=>raise( |Folder logic: length = 0, parent: {
                lv_parentcl }, child: { iv_package }| ).
            ENDIF.
    
            IF lv_path(1) = '_'.
              lv_path = lv_path+1.
            ENDIF.
            IF strlen( lv_path ) = 0.
              zcx_abapgit_exception=>raise( |Folder logic: length = 0, parent: {
                lv_parentcl }, child: { iv_package }| ).
            ENDIF.
    
            TRANSLATE lv_path USING '/#'.
            TRANSLATE lv_path TO LOWER CASE.
            CONCATENATE lv_path '/' INTO lv_path.
    
            rv_path = package_to_path( iv_top     = iv_top
                                       io_dot     = io_dot
                                       iv_package = lv_parentcl ).
    
            CONCATENATE rv_path lv_path INTO rv_path.
          ENDIF.
        ENDIF.
    
        lcl_package_to_path_cache=>add(
          iv_top     = iv_top
          io_dot     = io_dot
          iv_package = iv_package
          iv_path    = rv_path ).
    
      ENDMETHOD.
    
      METHOD path_to_package.
    
        DATA: lv_length               TYPE i,
              lv_parent               TYPE devclass,
              ls_package              TYPE zif_abapgit_sap_package=>ty_create,
              lv_new                  TYPE string,
              lv_path                 TYPE string,
              lv_absolute_name        TYPE string,
              lv_folder_logic         TYPE string,
              lt_unique_package_names TYPE HASHED TABLE OF devclass WITH UNIQUE KEY table_line.
    
        lv_length = strlen( io_dot->get_starting_folder( ) ).
        IF lv_length > strlen( iv_path ).
    * treat as not existing locally
          RETURN.
        ENDIF.
    
        rv_package = lcl_path_to_package_cache=>get(
          iv_top  = iv_top
          io_dot  = io_dot
          iv_path = iv_path ).
        IF rv_package IS NOT INITIAL AND iv_create_if_not_exists = abap_false.
          RETURN.
        ENDIF.
    
        lv_path    = iv_path+lv_length.
        lv_parent  = iv_top.
        rv_package = iv_top.
    
        " Automatically create package using minimal properties
        " Details will be updated during deserialization
        IF iv_create_if_not_exists = abap_true.
          IF iv_top(1) = '$'.
            zcl_abapgit_factory=>get_sap_package( iv_top )->create_local( ).
          ELSE.
            ls_package-devclass = iv_top.
            ls_package-ctext = iv_top.
            ls_package-as4user = sy-uname.
            zcl_abapgit_factory=>get_sap_package( iv_top )->create( ls_package ).
          ENDIF.
        ENDIF.
    
        INSERT iv_top INTO TABLE lt_unique_package_names.
    
        WHILE lv_path CA '/'.
          SPLIT lv_path AT '/' INTO lv_new lv_path.
    
          lv_folder_logic = io_dot->get_folder_logic( ).
          CASE lv_folder_logic.
            WHEN zif_abapgit_dot_abapgit=>c_folder_logic-full.
              lv_absolute_name = lv_new.
              TRANSLATE lv_absolute_name USING '#/'.
              IF iv_top(1) = '$'.
                CONCATENATE '$' lv_absolute_name INTO lv_absolute_name.
              ENDIF.
            WHEN zif_abapgit_dot_abapgit=>c_folder_logic-prefix.
              CONCATENATE rv_package '_' lv_new INTO lv_absolute_name.
            WHEN zif_abapgit_dot_abapgit=>c_folder_logic-mixed.
              CONCATENATE iv_top '_' lv_new INTO lv_absolute_name.
            WHEN OTHERS.
              zcx_abapgit_exception=>raise( |Invalid folder logic: { lv_folder_logic }| ).
          ENDCASE.
    
          TRANSLATE lv_absolute_name TO UPPER CASE.
    
          IF strlen( lv_absolute_name ) > 30.
            zcx_abapgit_exception=>raise( |Package { lv_absolute_name } exceeds ABAP 30-characters name limit| ).
          ENDIF.
    
          rv_package = lv_absolute_name.
          READ TABLE lt_unique_package_names TRANSPORTING NO FIELDS
            WITH TABLE KEY table_line = rv_package.
          IF sy-subrc = 0.
            zcx_abapgit_exception=>raise( |Package { rv_package } has a subpackage with the same name| ).
          ELSE.
            INSERT rv_package INTO TABLE lt_unique_package_names.
          ENDIF.
    
          IF zcl_abapgit_factory=>get_sap_package( rv_package )->exists( ) = abap_false AND
              iv_create_if_not_exists = abap_true.
    
            zcl_abapgit_factory=>get_sap_package( lv_parent )->create_child( rv_package ).
          ENDIF.
    
          lv_parent = rv_package.
        ENDWHILE.
    
        lcl_path_to_package_cache=>add(
          iv_top     = iv_top
          io_dot     = io_dot
          iv_path    = iv_path
          iv_package = rv_package ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_gui_jumper IMPLEMENTATION.
    
      METHOD jump_bw.
    
        DATA:
          lv_exit  TYPE abap_bool,
          lv_tlogo TYPE c LENGTH 4, "rstlogo
          lv_objnm TYPE c LENGTH 40. "rsawbnobjnm
    
        lv_tlogo = is_item-obj_type.
        lv_objnm = is_item-obj_name.
    
        TRY.
            CALL METHOD ('CL_RSAWBN_AWB')=>('IS_SUPPORTED_NAVIGATION')
              EXPORTING
                i_tlogo               = lv_tlogo
                i_fcode               = 'DISPLAY'
              IMPORTING
                re_is_supported_fcode = lv_exit.
    
            IF lv_exit = abap_false.
              RETURN.
            ENDIF.
          CATCH cx_root.
            " Not a BW system
            RETURN.
        ENDTRY.
    
        TRY.
            CALL METHOD ('CL_RSAWBN_AWB')=>('NAVIGATE_FROM_APPLICATION')
              EXPORTING
                i_tlogo                = lv_tlogo
                i_objnm                = lv_objnm
                i_new_mode             = iv_new_window
              IMPORTING
                e_exit_own_application = lv_exit.
    
          CATCH cx_root.
            " Older release without i_new_mode
            CALL METHOD ('CL_RSAWBN_AWB')=>('NAVIGATE_FROM_APPLICATION')
              EXPORTING
                i_tlogo                = lv_tlogo
                i_objnm                = lv_objnm
              IMPORTING
                e_exit_own_application = lv_exit.
        ENDTRY.
    
        rv_exit = lv_exit.
    
      ENDMETHOD.
    
      METHOD jump_tr.
    
        DATA:
          lv_e071_object   TYPE e071-object,
          lv_e071_obj_name TYPE e071-obj_name.
    
        lv_e071_object   = is_item-obj_type.
        lv_e071_obj_name = is_item-obj_name.
    
        CALL FUNCTION 'TR_OBJECT_JUMP_TO_TOOL'
          EXPORTING
            iv_action         = 'SHOW'
            iv_pgmid          = 'R3TR'
            iv_object         = lv_e071_object
            iv_obj_name       = lv_e071_obj_name
          EXCEPTIONS
            jump_not_possible = 1
            OTHERS            = 2.
    
        rv_exit = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD jump_wb.
    
        CALL FUNCTION 'RS_TOOL_ACCESS'
          EXPORTING
            operation           = 'SHOW'
            object_name         = is_item-obj_name
            object_type         = is_item-obj_type
            devclass            = is_item-devclass
            in_new_window       = iv_new_window
          EXCEPTIONS
            not_executed        = 1
            invalid_object_type = 2
            OTHERS              = 3.
    
        rv_exit = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD jump_wb_line.
    
        IF iv_line_number IS NOT INITIAL AND iv_sub_obj_type IS NOT INITIAL AND iv_sub_obj_name IS NOT INITIAL.
    
          " For the line navigation we have to supply the sub object type (iv_sub_obj_type).
          " If we use is_item-obj_type it navigates only to the object.
          CALL FUNCTION 'RS_TOOL_ACCESS'
            EXPORTING
              operation           = 'SHOW'
              object_name         = is_item-obj_name
              object_type         = iv_sub_obj_type
              devclass            = is_item-devclass
              include             = iv_sub_obj_name
              position            = iv_line_number
              in_new_window       = iv_new_window
            EXCEPTIONS
              not_executed        = 1
              invalid_object_type = 2
              OTHERS              = 3.
    
          rv_exit = boolc( sy-subrc = 0 ).
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_gui_jumper~jump.
    
        " WebGUI cannot open windows or ADT
        IF /apmg/cl_apm_gui_factory=>get_frontend_services( )->is_webgui( ) = abap_true.
          zcx_abapgit_exception=>raise( |Jump not possible in WebGUI| ).
        ENDIF.
    
        " Try all generic jump options
    
        " 1) ADT Jump
        rv_exit = zif_abapgit_gui_jumper~jump_adt(
          is_item         = is_item
          iv_sub_obj_name = is_sub_item-obj_name
          iv_line_number  = iv_line_number ).
    
        IF rv_exit = abap_true.
          RETURN.
        ENDIF.
    
        " 2) WB Jump with Line Number
        rv_exit = jump_wb_line(
          is_item         = is_item
          iv_sub_obj_name = is_sub_item-obj_name
          iv_sub_obj_type = is_sub_item-obj_type
          iv_line_number  = iv_line_number
          iv_new_window   = iv_new_window ).
    
        IF rv_exit = abap_true.
          RETURN.
        ENDIF.
    
        " 3) WB Jump without Line Number
        rv_exit = jump_wb(
          is_item       = is_item
          iv_new_window = iv_new_window ).
    
        IF rv_exit = abap_true.
          RETURN.
        ENDIF.
    
        " 4) Transport Tool Jump
        rv_exit = jump_tr( is_item ).
    
        IF rv_exit = abap_true.
          RETURN.
        ENDIF.
    
        " 5) BW Jump
        rv_exit = jump_bw(
          is_item       = is_item
          iv_new_window = iv_new_window ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_gui_jumper~jump_adt.
    
        " Open object in ADT (if enabled)
    
        DATA lv_adt_jump_enabled TYPE abap_bool.
    
        lv_adt_jump_enabled = /apmg/cl_apm_settings=>factory( )->get( )-gui_settings-adt_jump_enabled.
    
        IF lv_adt_jump_enabled = abap_true.
          TRY.
              zcl_abapgit_adt_link=>jump(
                iv_obj_name     = is_item-obj_name
                iv_obj_type     = is_item-obj_type
                iv_sub_obj_name = iv_sub_obj_name
                iv_line_number  = iv_line_number ).
    
              rv_exit = abap_true.
            CATCH zcx_abapgit_exception ##NO_HANDLER.
              " Use fallback
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_gui_jumper~jump_batch_input.
    
        DATA lv_msg TYPE c LENGTH 80.
    
        IF iv_new_window = abap_true.
          CALL FUNCTION 'ABAP4_CALL_TRANSACTION'
            STARTING NEW TASK 'GIT'
            EXPORTING
              tcode                 = iv_tcode
              mode_val              = 'E'
            TABLES
              using_tab             = it_bdcdata
            EXCEPTIONS
              system_failure        = 1 MESSAGE lv_msg
              communication_failure = 2 MESSAGE lv_msg
              resource_failure      = 3
              OTHERS                = 4.
        ELSE.
          CALL FUNCTION 'ABAP4_CALL_TRANSACTION'
            EXPORTING
              tcode     = iv_tcode
              mode_val  = 'E'
            TABLES
              using_tab = it_bdcdata
            EXCEPTIONS
              OTHERS    = 4.
        ENDIF.
    
        CASE sy-subrc.
          WHEN 1 OR 2.
            zcx_abapgit_exception=>raise( |Batch input error for transaction { iv_tcode }: { lv_msg }| ).
          WHEN 3 OR 4.
            zcx_abapgit_exception=>raise( |Batch input error for transaction { iv_tcode }| ).
        ENDCASE.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_hash IMPLEMENTATION.
    
      METHOD adler32.
    
        CONSTANTS: lc_adler TYPE i VALUE 65521,
                   lc_max_b TYPE i VALUE 1800000000.
    
        DATA: lv_index TYPE i,
              lv_a     TYPE i VALUE 1,
              lv_b     TYPE i VALUE 0,
              lv_x     TYPE x LENGTH 2,
              lv_ca    TYPE c LENGTH 4,
              lv_cb    TYPE c LENGTH 4,
              lv_char8 TYPE c LENGTH 8.
    
        DO xstrlen( iv_xstring ) TIMES.
          lv_index = sy-index - 1.
    
          lv_a = lv_a + iv_xstring+lv_index(1).
          lv_b = lv_b + lv_a.
    
    * delay the MOD operation until the integer might overflow
    * articles describe 5552 additions are allowed, but this assumes unsigned integers
    * instead of allowing a fixed number of additions before running MOD, then
    * just compare value of lv_b, this is 1 operation less than comparing and adding
          IF lv_b > lc_max_b.
            lv_a = lv_a MOD lc_adler.
            lv_b = lv_b MOD lc_adler.
          ENDIF.
        ENDDO.
    
        lv_a = lv_a MOD lc_adler.
        lv_b = lv_b MOD lc_adler.
    
        lv_x = lv_a.
        lv_ca = lv_x.
    
        lv_x = lv_b.
        lv_cb = lv_x.
    
        CONCATENATE lv_cb lv_ca INTO lv_char8.
    
        rv_checksum = lv_char8.
    
      ENDMETHOD.
    
      METHOD sha1.
    
        DATA: lv_len     TYPE i,
              lv_char10  TYPE c LENGTH 10,
              lv_string  TYPE string,
              lv_xstring TYPE xstring.
    
        lv_len = xstrlen( iv_data ).
        lv_char10 = lv_len.
        CONDENSE lv_char10.
        CONCATENATE iv_type lv_char10 INTO lv_string SEPARATED BY space.
        lv_xstring = zcl_abapgit_convert=>string_to_xstring_utf8( lv_string ).
    
        lv_string = lv_xstring.
        CONCATENATE lv_string '00' INTO lv_string.
        lv_xstring = lv_string.
    
        CONCATENATE lv_xstring iv_data INTO lv_xstring IN BYTE MODE.
    
        rv_sha1 = sha1_raw( lv_xstring ).
    
      ENDMETHOD.
    
      METHOD sha1_blob.
        rv_sha1 = sha1( iv_type = zif_abapgit_git_definitions=>c_type-blob
                        iv_data = iv_data ).
      ENDMETHOD.
    
      METHOD sha1_commit.
        rv_sha1 = sha1( iv_type = zif_abapgit_git_definitions=>c_type-commit
                        iv_data = iv_data ).
      ENDMETHOD.
    
      METHOD sha1_raw.
    
        DATA: lv_hash  TYPE string,
              lv_key   TYPE xstring,
              lx_error TYPE REF TO cx_abap_message_digest.
        TRY.
            cl_abap_hmac=>calculate_hmac_for_raw(
          EXPORTING
            if_key        = lv_key
            if_data       = iv_data
          IMPORTING
            ef_hmacstring = lv_hash ).
          CATCH cx_abap_message_digest INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        rv_sha1 = lv_hash.
        TRANSLATE rv_sha1 TO LOWER CASE.
    
      ENDMETHOD.
    
      METHOD sha1_string.
    
        DATA: lv_hash  TYPE string,
              lv_key   TYPE xstring,
              lx_error TYPE REF TO cx_abap_message_digest.
        TRY.
            cl_abap_hmac=>calculate_hmac_for_char(
          EXPORTING
            if_key        = lv_key
            if_data       = iv_data
          IMPORTING
            ef_hmacstring = lv_hash ).
          CATCH cx_abap_message_digest INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        rv_sha1 = lv_hash.
        TRANSLATE rv_sha1 TO LOWER CASE.
    
      ENDMETHOD.
    
      METHOD sha1_tag.
        rv_sha1 = sha1( iv_type = zif_abapgit_git_definitions=>c_type-tag
                        iv_data = iv_data ).
      ENDMETHOD.
    
      METHOD sha1_tree.
        rv_sha1 = sha1( iv_type = zif_abapgit_git_definitions=>c_type-tree
                        iv_data = iv_data ).
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_I18N_PARAMS IMPLEMENTATION.
    
      METHOD build_language_filter.
        IF mt_language_filter IS INITIAL.
          " translation_languages are includes, system langs are excludes, so the do not interfere
          IF ms_params-translation_languages IS NOT INITIAL.
            mt_language_filter = iso_langs_to_lang_filter( ms_params-translation_languages ).
          ELSE.
            mt_language_filter = zcl_abapgit_factory=>get_environment( )->get_system_language_filter( ).
          ENDIF.
        ENDIF.
        rt_language_filter = mt_language_filter.
      ENDMETHOD.
    
      METHOD constructor.
        IF is_params IS NOT INITIAL.
          ms_params = is_params.
        ELSE.
          ms_params-main_language         = iv_main_language.
          ms_params-main_language_only    = iv_main_language_only.
          ms_params-translation_languages = it_translation_langs.
          ms_params-use_lxe               = iv_use_lxe.
        ENDIF.
        ASSERT ms_params-main_language IS NOT INITIAL.
      ENDMETHOD.
    
      METHOD iso_langs_to_lang_filter.
    
        DATA lv_laiso LIKE LINE OF it_iso_filter.
        DATA lv_langu TYPE sy-langu.
        DATA ls_range LIKE LINE OF rt_language_filter.
    
        ls_range-sign = 'I'.
        ls_range-option = 'EQ'.
    
        LOOP AT it_iso_filter INTO lv_laiso.
    
          zcl_abapgit_convert=>language_sap2_to_sap1(
            EXPORTING
              im_lang_sap2  = lv_laiso
            RECEIVING
              re_lang_sap1  = lv_langu
            EXCEPTIONS
              no_assignment = 1
              OTHERS        = 2 ).
          IF sy-subrc <> 0.
            CONTINUE.
          ENDIF.
    
          ls_range-low = lv_langu.
          APPEND ls_range TO rt_language_filter.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD is_lxe_applicable.
    
        rv_yes = boolc( ms_params-main_language_only = abap_false AND
           ms_params-use_lxe = abap_true AND
           ms_params-translation_languages IS NOT INITIAL ).
    
      ENDMETHOD.
    
      METHOD match_obj_patterns.
    
        DATA lv_pattern TYPE string.
        DATA lv_path TYPE string.
    
        LOOP AT it_wo_translation_patterns INTO lv_pattern.
          CHECK lv_pattern IS NOT INITIAL.
          IF NOT lv_pattern+0(1) CA '*/'.
            " The idea is to simplify entering package paths e.g. subpkg/* instead of /src/subpkg/* or */subpkg/*
            " or object names: zcl.clas instead of */zcl.clas
            " But maybe it is a bad idea ... to see on practice
            lv_pattern = `*/` && lv_pattern.
          ENDIF.
    
          " Compose simplified file path
          lv_path = is_tadir-path && to_lower( is_tadir-obj_name ) && `.` && to_lower( is_tadir-object ).
    
          IF lv_path CP lv_pattern.
            rv_yes = abap_true.
            EXIT.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD new.
        CREATE OBJECT ro_instance
          EXPORTING
            iv_main_language      = iv_main_language
            iv_main_language_only = iv_main_language_only
            it_translation_langs  = it_translation_langs
            iv_use_lxe            = iv_use_lxe
            is_params             = is_params.
      ENDMETHOD.
    
      METHOD normalize_obj_patterns.
    
        DATA lv_pattern TYPE string.
    
        LOOP AT it_wo_translation_patterns INTO lv_pattern.
          CONDENSE lv_pattern.
          CHECK lv_pattern IS NOT INITIAL.
          lv_pattern = to_lower( lv_pattern ).
          " TODO validation if needed
          APPEND lv_pattern TO rt_wo_translation_clean.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD trim_saplang_keyed_table.
    
        DATA lv_laiso TYPE laiso.
        DATA lv_index TYPE i.
    
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE sy-langu.
    
        IF ms_params-translation_languages IS INITIAL OR iv_lang_field_name IS INITIAL.
          RETURN. " Nothing to filter
        ENDIF.
    
        LOOP AT ct_tab ASSIGNING .
          lv_index = sy-tabix.
          ASSIGN COMPONENT iv_lang_field_name OF STRUCTURE  TO .
          ASSERT sy-subrc = 0.
    
          IF iv_keep_master_lang = abap_true AND  = ms_params-main_language.
            CONTINUE. " Just keep it
          ENDIF.
    
          zcl_abapgit_convert=>language_sap1_to_sap2(
            EXPORTING
              im_lang_sap1  = 
            RECEIVING
              re_lang_sap2  = lv_laiso
            EXCEPTIONS
              no_assignment = 1
              OTHERS        = 2 ).
          IF sy-subrc <> 0.
            DELETE ct_tab INDEX lv_index. " Not in the list anyway ...
            CONTINUE.
          ENDIF.
    
          " Not a sorted table, but presumably the list is small, so no significant performance flow
          READ TABLE ms_params-translation_languages TRANSPORTING NO FIELDS WITH KEY table_line = lv_laiso.
          IF sy-subrc <> 0.
            DELETE ct_tab INDEX lv_index.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD trim_saplang_list.
    
        DATA lv_langu TYPE sy-langu.
        DATA lv_laiso TYPE laiso.
        DATA lv_index TYPE i.
    
        IF ms_params-translation_languages IS INITIAL.
          RETURN. " Nothing to filter
        ENDIF.
    
        LOOP AT ct_sap_langs INTO lv_langu.
          lv_index = sy-tabix.
    
          zcl_abapgit_convert=>language_sap1_to_sap2(
            EXPORTING
              im_lang_sap1  = lv_langu
            RECEIVING
              re_lang_sap2  = lv_laiso
            EXCEPTIONS
              no_assignment = 1
              OTHERS        = 2 ).
          IF sy-subrc <> 0.
            DELETE ct_sap_langs INDEX lv_index. " Not in the list anyway ...
            CONTINUE.
          ENDIF.
    
          " Not a sorted table, but presumably the list is small, so no significant performance flow
          READ TABLE ms_params-translation_languages TRANSPORTING NO FIELDS WITH KEY table_line = lv_laiso.
          IF sy-subrc <> 0.
            DELETE ct_sap_langs INDEX lv_index.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_injector IMPLEMENTATION.
    
      METHOD set_default_transport.
        zcl_abapgit_factory=>gi_default_transport = ii_default_transport.
      ENDMETHOD.
    
      METHOD set_cts_api.
        zcl_abapgit_factory=>gi_cts_api = ii_cts_api.
      ENDMETHOD.
    
      METHOD set_environment.
        zcl_abapgit_factory=>gi_environment = ii_environment.
      ENDMETHOD.
    
      METHOD set_exit.
        zcl_abapgit_exit=>gi_global_exit = ii_exit.
      ENDMETHOD.
    
      METHOD set_function_module.
        zcl_abapgit_factory=>gi_function_module = ii_function_module.
      ENDMETHOD.
    
      METHOD set_longtexts.
        zcl_abapgit_factory=>gi_longtext = ii_longtexts.
      ENDMETHOD.
    
      METHOD set_lxe_texts.
        zcl_abapgit_factory=>gi_lxe_texts = ii_lxe_texts.
      ENDMETHOD.
    
      METHOD set_sap_namespace.
        zcl_abapgit_factory=>gi_sap_namespace = ii_namespace.
      ENDMETHOD.
    
      METHOD set_sap_package.
    
        DATA: ls_sap_package TYPE zcl_abapgit_factory=>ty_sap_package.
        FIELD-SYMBOLS:  TYPE zcl_abapgit_factory=>ty_sap_package.
    
        READ TABLE zcl_abapgit_factory=>gt_sap_package
             ASSIGNING 
             WITH TABLE KEY package = iv_package.
    
        IF sy-subrc <> 0.
    
          ls_sap_package-package = iv_package.
          INSERT ls_sap_package
                 INTO TABLE zcl_abapgit_factory=>gt_sap_package
                 ASSIGNING .
    
        ENDIF.
    
        -instance = ii_sap_package.
    
      ENDMETHOD.
    
      METHOD clear_sap_package.
        CLEAR zcl_abapgit_factory=>gt_sap_package.
      ENDMETHOD.
    
      METHOD set_sap_report.
        zcl_abapgit_factory=>gi_sap_report = ii_report.
      ENDMETHOD.
    
      METHOD set_tadir.
        zcl_abapgit_factory=>gi_tadir = ii_tadir.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_ITEM_GRAPH IMPLEMENTATION.
    
      METHOD add_edge.
        DATA ls_edge LIKE LINE OF mt_edges.
        ASSERT is_from IS NOT INITIAL.
        ASSERT is_to IS NOT INITIAL.
        ls_edge-from = is_from.
        ls_edge-to   = is_to.
        APPEND ls_edge TO mt_edges.
      ENDMETHOD.
    
      METHOD constructor.
        INSERT LINES OF it_items INTO TABLE mt_vertices.
      ENDMETHOD.
    
      METHOD get_next.
    * find a vertex with no inbound edges, if it does not exist pick anything
    
        DATA ls_vertex LIKE LINE OF mt_vertices.
        DATA lv_index  TYPE i.
    
        LOOP AT mt_vertices INTO ls_vertex.
          lv_index = sy-tabix.
          READ TABLE mt_edges WITH KEY sec_to COMPONENTS
            to-obj_type = ls_vertex-obj_type
            to-obj_name = ls_vertex-obj_name
            TRANSPORTING NO FIELDS.
          IF sy-subrc <> 0.
            remove_vertex( lv_index ).
            rs_item = ls_vertex.
            RETURN.
          ENDIF.
        ENDLOOP.
    
        IF mv_warning = abap_false.
    * only issue the warning once per graph
          ii_log->add_warning( |Cycle detected in item graph| ).
          mv_warning = abap_true.
        ENDIF.
    
        READ TABLE mt_vertices INTO rs_item INDEX 1.
        ASSERT sy-subrc = 0.
        remove_vertex( 1 ).
    
      ENDMETHOD.
    
      METHOD has_vertices.
        rv_bool = boolc( lines( mt_vertices ) > 0 ).
      ENDMETHOD.
    
      METHOD remove_vertex.
        DATA ls_vertex LIKE LINE OF mt_vertices.
    
        READ TABLE mt_vertices INDEX iv_index INTO ls_vertex.
        ASSERT sy-subrc = 0.
    
        DELETE mt_vertices INDEX iv_index.
        DELETE mt_edges USING KEY sec_from
          WHERE from-obj_type = ls_vertex-obj_type
          AND from-obj_name = ls_vertex-obj_name.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS lcl_aff_filter DEFINITION FINAL.
      PUBLIC SECTION.
        INTERFACES /apmg/if_apm_ajson_filter.
        TYPES:
          BEGIN OF ty_path_value_pair,
            path  TYPE string,
            value TYPE string,
          END OF ty_path_value_pair,
          ty_skip_paths TYPE STANDARD TABLE OF ty_path_value_pair WITH KEY path.
    
        METHODS constructor
          IMPORTING iv_skip_paths TYPE ty_skip_paths OPTIONAL
          RAISING   /apmg/cx_apm_ajson_error.
      PRIVATE SECTION.
        DATA mt_skip_paths TYPE ty_skip_paths.
    ENDCLASS.
    
    CLASS lcl_aff_filter IMPLEMENTATION.
    
      METHOD /apmg/if_apm_ajson_filter~keep_node.
    
        DATA lv_path TYPE string.
    
        lv_path = is_node-path && is_node-name.
    
        READ TABLE mt_skip_paths WITH KEY path = lv_path value = is_node-value TRANSPORTING NO FIELDS.
        IF boolc( sy-subrc = 0 ) = abap_true
          AND iv_visit = /apmg/if_apm_ajson_filter=>visit_type-value.
          rv_keep = abap_false.
          RETURN.
        ELSE.
          READ TABLE mt_skip_paths WITH KEY path = lv_path TRANSPORTING NO FIELDS.
          IF boolc( sy-subrc = 0 ) = abap_true
            AND iv_visit = /apmg/if_apm_ajson_filter=>visit_type-value.
            rv_keep = abap_true.
            RETURN.
          ENDIF.
        ENDIF.
    
        IF is_node-type = 'bool' AND is_node-value = 'false' AND iv_visit = /apmg/if_apm_ajson_filter=>visit_type-value.
          rv_keep = abap_false.
          RETURN.
        ENDIF.
    
        " AFF: if INTEGER type is initial (0) then is will be skipped
        "      However, if type is $required, it should be serialized.
        IF NOT ( ( iv_visit = /apmg/if_apm_ajson_filter=>visit_type-value AND is_node-value IS NOT INITIAL ) OR
             ( iv_visit <> /apmg/if_apm_ajson_filter=>visit_type-value AND is_node-children > 0 ) ).
          rv_keep = abap_false.
          RETURN.
        ENDIF.
    
        rv_keep = abap_true.
    
      ENDMETHOD.
    
      METHOD constructor.
        " extract annotations and build table for values to be skipped ( path/name | value )
        DATA lo_abap_language_pair TYPE ty_path_value_pair.
        lo_abap_language_pair-path = `/header/abapLanguageVersion`.
        lo_abap_language_pair-value = 'standard'.
    
        APPEND lo_abap_language_pair TO mt_skip_paths.
    
        IF iv_skip_paths IS NOT INITIAL.
          APPEND LINES OF iv_skip_paths TO mt_skip_paths.
        ENDIF.
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_json_handler IMPLEMENTATION.
    
      METHOD deserialize.
        DATA lo_ajson TYPE REF TO /apmg/if_apm_ajson.
    
        CLEAR ev_data.
    
        lo_ajson = /apmg/cl_apm_ajson=>parse( iv_content
          )->map( /apmg/cl_apm_ajson_mapping=>create_to_snake_case( ) ).
    
        map2abap_original_language( CHANGING co_ajson = lo_ajson ).
        set_defaults( EXPORTING it_defaults = iv_defaults
                      CHANGING  co_ajson    = lo_ajson ).
        map2abap_abap_language_version( CHANGING co_ajson = lo_ajson ).
        map2abap_custom_enum( EXPORTING it_enum_mappings = iv_enum_mappings
                              CHANGING co_ajson          = lo_ajson ).
    
        lo_ajson->to_abap( IMPORTING ev_container = ev_data ).
    
      ENDMETHOD.
    
      METHOD map2abap_abap_language_version.
        DATA:
          lv_enum_abap TYPE string,
          lv_enum_json TYPE string.
    
        lv_enum_json = co_ajson->get_string( '/header/abap_language_version' ).
        IF lv_enum_json = zif_abapgit_dot_abapgit=>c_abap_language_version-standard.
          lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version_src-standard.
        ELSEIF lv_enum_json = zif_abapgit_dot_abapgit=>c_abap_language_version-cloud_development.
          lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version-cloud_development.
        ELSEIF lv_enum_json = zif_abapgit_dot_abapgit=>c_abap_language_version-key_user.
          lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version-key_user.
        ENDIF.
    
        co_ajson->set_string( iv_path = '/header/abap_language_version'
                              iv_val  = lv_enum_abap ).
      ENDMETHOD.
    
      METHOD map2abap_custom_enum.
        DATA:
          lv_enum_json    TYPE string,
          ls_enum_mapping TYPE ty_enum_mapping,
          ls_mapping      TYPE ty_json_abap_mapping.
    
        LOOP AT it_enum_mappings INTO ls_enum_mapping.
          lv_enum_json = co_ajson->get_string( ls_enum_mapping-path ).
          READ TABLE ls_enum_mapping-mappings WITH KEY json = lv_enum_json INTO ls_mapping.
          IF sy-subrc = 0.
            co_ajson->set_string( iv_path = ls_enum_mapping-path
                                  iv_val  = ls_mapping-abap ).
          ENDIF.
        ENDLOOP.
      ENDMETHOD.
    
      METHOD map2abap_original_language.
        DATA:
          lv_bcp47_language    TYPE string,
          lv_original_language TYPE sy-langu.
    
        lv_bcp47_language = co_ajson->get_string( '/header/original_language' ).
    
        lv_original_language = zcl_abapgit_convert=>language_bcp47_to_sap1( lv_bcp47_language ).
    
        co_ajson->set_string( iv_path = '/header/original_language'
                              iv_val  = lv_original_language ).
      ENDMETHOD.
    
      METHOD map2json_abap_language_version.
        DATA:
          lv_enum_abap TYPE string,
          lv_enum_json TYPE string.
    
        lv_enum_abap = co_ajson->get_string( '/header/abapLanguageVersion' ).
        IF lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version_src-standard
          OR lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version-standard.
          lv_enum_json = zif_abapgit_dot_abapgit=>c_abap_language_version-standard.
        ELSEIF lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version-cloud_development.
          lv_enum_json = zif_abapgit_dot_abapgit=>c_abap_language_version-cloud_development.
        ELSEIF lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version-key_user.
          lv_enum_json = zif_abapgit_dot_abapgit=>c_abap_language_version-key_user.
        ENDIF.
    
        co_ajson->set_string( iv_path = '/header/abapLanguageVersion'
                              iv_val  = lv_enum_json ).
      ENDMETHOD.
    
      METHOD map2json_custom_enum.
        DATA:
          lv_enum_abap    TYPE string,
          ls_enum_mapping TYPE ty_enum_mapping,
          ls_mapping      TYPE ty_json_abap_mapping.
    
        LOOP AT it_enum_mappings INTO ls_enum_mapping.
          lv_enum_abap = co_ajson->get_string( ls_enum_mapping-path ).
          READ TABLE ls_enum_mapping-mappings WITH KEY abap = lv_enum_abap INTO ls_mapping.
          IF sy-subrc = 0.
            co_ajson->set_string( iv_path = ls_enum_mapping-path
                                  iv_val  = ls_mapping-json ).
          ENDIF.
        ENDLOOP.
      ENDMETHOD.
    
      METHOD map2json_original_language.
        DATA:
          lv_bcp47_language    TYPE string,
          lv_original_language TYPE sy-langu.
    
        lv_original_language = co_ajson->get_string( '/header/originalLanguage' ).
    
        lv_bcp47_language = zcl_abapgit_convert=>language_sap1_to_bcp47( lv_original_language ).
    
        co_ajson->set_string( iv_path = '/header/originalLanguage'
                              iv_val  = lv_bcp47_language ).
      ENDMETHOD.
    
      METHOD serialize.
        DATA: lt_st_source TYPE abap_trans_srcbind_tab,
              lv_json      TYPE string,
              lo_ajson     TYPE REF TO /apmg/if_apm_ajson,
              lo_filter    TYPE REF TO lcl_aff_filter.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_st_source.
    
        APPEND INITIAL LINE TO lt_st_source ASSIGNING .
        GET REFERENCE OF iv_data INTO -value.
    
        lo_ajson = /apmg/cl_apm_ajson=>new( iv_keep_item_order = abap_true
          )->set( iv_path = '/'
                  iv_val  = iv_data
          )->map( /apmg/cl_apm_ajson_mapping=>create_to_camel_case( ) ).
    
        map2json_original_language( CHANGING co_ajson = lo_ajson ).
        map2json_abap_language_version( CHANGING co_ajson = lo_ajson ).
        map2json_custom_enum( EXPORTING it_enum_mappings = iv_enum_mappings
                              CHANGING co_ajson          = lo_ajson ).
    
        CREATE OBJECT lo_filter EXPORTING iv_skip_paths = iv_skip_paths.
    
        " files end with an empty line (EOF)
        lv_json = lo_ajson->clone( )->filter( lo_filter )->stringify( 2 ) && cl_abap_char_utilities=>newline.
    
        rv_result = zcl_abapgit_convert=>string_to_xstring_utf8( lv_json ).
      ENDMETHOD.
    
      METHOD set_defaults.
        DATA:
          lv_enum_json TYPE string,
          ls_default   TYPE ty_path_value_pair.
    
        LOOP AT it_defaults INTO ls_default.
          lv_enum_json = co_ajson->get_string( ls_default-path ).
          IF lv_enum_json = ``.
            co_ajson->set_string( iv_path = ls_default-path
                                  iv_val  = ls_default-value ).
          ENDIF.
        ENDLOOP.
      ENDMETHOD.
    ENDCLASS.
    
    *"* use this source file for the definition and implementation of
    *"* local helper classes, interface definitions and type
    *"* declarations
    
    CLASS lcl_json_path DEFINITION CREATE PUBLIC.
    
      PUBLIC SECTION.
        METHODS:
          serialize_rec
            IMPORTING io_reader     TYPE REF TO if_sxml_reader
                      it_path       TYPE string_table
            CHANGING  ct_json_paths TYPE string_table.
    
        CLASS-METHODS: deserialize
          IMPORTING it_json_path     TYPE string_table
          RETURNING VALUE(rv_result) TYPE string
          RAISING   zcx_abapgit_exception.
    
      PROTECTED SECTION.
      PRIVATE SECTION.
    
        METHODS:
          is_array
            IMPORTING io_reader        TYPE REF TO if_sxml_reader
            RETURNING VALUE(rv_result) TYPE abap_bool.
        METHODS:
          is_string_open
            IMPORTING io_reader        TYPE REF TO if_sxml_reader
            RETURNING VALUE(rv_result) TYPE abap_bool.
        METHODS:
          is_object
            IMPORTING io_reader        TYPE REF TO if_sxml_reader
            RETURNING VALUE(rv_result) TYPE abap_bool.
        METHODS:
          serialize_rec_array
            IMPORTING io_reader     TYPE REF TO if_sxml_reader
                      it_path       TYPE string_table
            CHANGING  ct_json_paths TYPE string_table.
        METHODS:
          get_json_path
            IMPORTING it_path          TYPE string_table
            RETURNING VALUE(rv_result) TYPE string.
        CLASS-METHODS get_path_elements
          IMPORTING iv_path          TYPE string
          RETURNING VALUE(rt_result) TYPE string_table
          RAISING   zcx_abapgit_exception.
        CLASS-METHODS build_json
          IMPORTING it_path_elements TYPE string_table
                    iv_value         TYPE string
          CHANGING  cv_json_string   TYPE string.
        CLASS-METHODS path_contains_array
          IMPORTING iv_path          TYPE string
          RETURNING VALUE(rv_result) TYPE abap_bool.
        CLASS-METHODS: to_json
          IMPORTING iv_json_path     TYPE string
          RETURNING VALUE(ro_result) TYPE REF TO /apmg/cl_apm_ajson
          RAISING   /apmg/cx_apm_ajson_error
                    zcx_abapgit_exception.
        CLASS-METHODS: is_primitiv
          IMPORTING iv_string        TYPE string
          RETURNING VALUE(rv_result) TYPE abap_bool.
        CLASS-METHODS: is_comment_or_empty_line
          IMPORTING iv_line          TYPE string
          RETURNING VALUE(rv_result) TYPE abap_bool.
    
    ENDCLASS.
    
    CLASS lcl_json_path IMPLEMENTATION.
    
      METHOD to_json.
        DATA: lv_path          TYPE string,
              lv_value         TYPE string,
              lt_path_elements TYPE string_table,
              lv_json          TYPE string.
    
        FIND REGEX `(.*)=(.*$)` IN iv_json_path SUBMATCHES lv_path lv_value ##REGEX_POSIX.
    
        IF path_contains_array( lv_path ) = abap_true.
    
          lt_path_elements = get_path_elements( lv_path ).
    
          build_json( EXPORTING it_path_elements = lt_path_elements
                                iv_value         = lv_value
                      CHANGING  cv_json_string   = lv_json ).
    
          ro_result = /apmg/cl_apm_ajson=>parse( lv_json ).
        ELSE.
    
          REPLACE FIRST OCCURRENCE OF '$.' IN lv_path WITH ''.
          REPLACE '.' IN lv_path WITH '/'.
          ro_result = /apmg/cl_apm_ajson=>create_empty( iv_keep_item_order = abap_true ).
          ro_result->set( iv_path = lv_path
                          iv_val  = lv_value ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD path_contains_array.
        DATA lv_array_pattern TYPE string VALUE `.*\[.*\].*`.
        rv_result = boolc( matches( val   = iv_path
                                    regex = lv_array_pattern ) ) ##REGEX_POSIX.
      ENDMETHOD.
    
      METHOD build_json.
        DATA: lt_new_path_element TYPE string_table,
              lv_sub_match        TYPE string,
              lv_key_name         TYPE string,
              lv_key_value        TYPE string,
              lv_name             TYPE string,
              lv_first_elem       TYPE string.
    
        lt_new_path_element = it_path_elements.
    
        IF lines( lt_new_path_element ) = 0.
          RETURN.
        ENDIF.
    
        READ TABLE lt_new_path_element INTO lv_first_elem INDEX 1.
    
        IF lv_first_elem = `$`. " is root level
    
          DELETE lt_new_path_element INDEX 1.
          build_json( EXPORTING it_path_elements = lt_new_path_element
                                iv_value         = iv_value
                      CHANGING  cv_json_string   = cv_json_string ).
    
        ELSEIF is_primitiv( lv_first_elem ) = abap_true.
    
          cv_json_string = cv_json_string && | \{"{ lv_first_elem+1 }": |.
    
          DELETE lt_new_path_element INDEX 1.
    
          build_json( EXPORTING it_path_elements = lt_new_path_element
                                iv_value         = iv_value
                      CHANGING  cv_json_string   = cv_json_string ).
    
          cv_json_string = cv_json_string && ` }`.
    
        ELSE. " is array
    
          FIND REGEX `\[(.*)\]` IN lv_first_elem SUBMATCHES lv_sub_match ##REGEX_POSIX.
          FIND REGEX `(\w+)(?==='([^']*)')` IN lv_sub_match SUBMATCHES lv_key_name lv_key_value ##REGEX_POSIX.
          READ TABLE lt_new_path_element INTO lv_name INDEX 2.
    
          DELETE lt_new_path_element INDEX 1.
          DELETE lt_new_path_element INDEX 1.
    
          IF lines( lt_new_path_element ) = 0.
    
            cv_json_string = cv_json_string &&
              |[ \{ "{ lv_key_name }": "{ lv_key_value }", "{ lv_name+1 }": "{ iv_value }"\} ]|.
    
          ELSE.
    
            cv_json_string = cv_json_string && |[ \{ "{ lv_key_name }": "{ lv_key_value }", "{ lv_name+1 }":|.
    
            build_json( EXPORTING it_path_elements = lt_new_path_element
                                  iv_value         = iv_value
                        CHANGING  cv_json_string   = cv_json_string ).
    
            cv_json_string = cv_json_string && `} ] `.
    
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD is_primitiv.
    
        FIND REGEX `^.\w+` IN iv_string ##REGEX_POSIX. " string start with .
        rv_result = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD get_path_elements.
        DATA: lv_pcre_pattern TYPE string,
              lt_match_result TYPE match_result_tab,
              lv_match        TYPE match_result,
              lv_hit          TYPE string,
              lx_find         TYPE REF TO cx_root.
    
        lv_pcre_pattern = `(^\$)|(\.\w+)|(\[[^\]]*\])`.
    
        TRY.
            FIND ALL OCCURRENCES OF REGEX lv_pcre_pattern IN iv_path RESULTS lt_match_result ##REGEX_POSIX.
          CATCH cx_sy_find_infinite_loop cx_sy_range_out_of_bounds cx_sy_invalid_regex cx_sy_regex_too_complex INTO lx_find.
            zcx_abapgit_exception=>raise_with_text( lx_find ).
        ENDTRY.
        LOOP AT lt_match_result INTO lv_match.
          lv_hit = substring( val = iv_path
                              off = lv_match-offset
                              len = lv_match-length ).
          APPEND lv_hit TO rt_result.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD is_array.
        rv_result = boolc( io_reader->name = 'array' ).
      ENDMETHOD.
    
      METHOD is_string_open.
        rv_result = boolc( io_reader->name = 'str' AND io_reader->node_type = if_sxml_node=>co_nt_element_open ).
      ENDMETHOD.
    
      METHOD is_object.
        rv_result = boolc( io_reader->name = 'object' ).
      ENDMETHOD.
    
      METHOD serialize_rec.
        DATA: lt_new_path TYPE string_table,
              lv_key      TYPE string.
    
        lt_new_path = it_path.
    
        IF io_reader->read_next_node( ) IS INITIAL.
          RETURN.
        ENDIF.
    
        IF is_string_open( io_reader ) = abap_true.
    
          APPEND io_reader->value TO lt_new_path.
          lv_key = get_json_path( lt_new_path ).
    
          io_reader->read_next_node( ).
          lv_key = |{ lv_key }={ io_reader->value }|.
          APPEND lv_key TO ct_json_paths.
    
          io_reader->read_next_node( ).
          DELETE lt_new_path INDEX lines( lt_new_path ).
    
          serialize_rec( EXPORTING io_reader     = io_reader
                                   it_path       = lt_new_path
                         CHANGING  ct_json_paths = ct_json_paths ).
    
        ELSEIF is_object( io_reader ) = abap_true AND io_reader->node_type = if_sxml_node=>co_nt_element_open.
    
          APPEND io_reader->value TO lt_new_path.
          serialize_rec( EXPORTING io_reader     = io_reader
                                   it_path       = lt_new_path
                         CHANGING  ct_json_paths = ct_json_paths ).
    
        ELSEIF is_array( io_reader ) = abap_true AND io_reader->node_type = if_sxml_node=>co_nt_element_open.
    
          APPEND io_reader->value TO lt_new_path.
          serialize_rec_array( EXPORTING io_reader     = io_reader
                                         it_path       = lt_new_path
                               CHANGING  ct_json_paths = ct_json_paths ).
    
        ELSEIF ( is_object( io_reader ) = abap_true OR is_array( io_reader ) = abap_true )
                 AND io_reader->node_type = if_sxml_node=>co_nt_element_close.
    
          DELETE lt_new_path INDEX lines( lt_new_path ).
          serialize_rec( EXPORTING io_reader     = io_reader
                                   it_path       = lt_new_path
                         CHANGING  ct_json_paths = ct_json_paths ).
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD serialize_rec_array.
        DATA: lt_new_path  TYPE string_table,
              lv_json_path TYPE string,
              lv_array_key TYPE string.
    
        lt_new_path = it_path.
    
        IF io_reader->read_next_node( ) IS INITIAL.
          RETURN.
        ENDIF.
    
        IF is_string_open( io_reader ) = abap_true.
    
          APPEND io_reader->value TO lt_new_path.
          lv_json_path = get_json_path( lt_new_path ).
    
          io_reader->read_next_node( ).
          lv_json_path = |{ lv_json_path }={ io_reader->value }|.
          APPEND lv_json_path TO ct_json_paths.
          io_reader->read_next_node( ).
    
          serialize_rec( EXPORTING io_reader     = io_reader
                                   it_path       = lt_new_path
                         CHANGING  ct_json_paths = ct_json_paths ).
    
        ELSEIF is_object( io_reader ) = abap_true AND io_reader->node_type = if_sxml_node=>co_nt_element_open.
    
          io_reader->read_next_node( ).
          lv_array_key = io_reader->value.
          io_reader->read_next_node( ).
          lv_array_key = |[?(@.{ lv_array_key }=='{ io_reader->value }')]|.
          APPEND lv_array_key TO lt_new_path.
          io_reader->read_next_node( ).
    
          io_reader->read_next_node( ).
          APPEND io_reader->value TO lt_new_path.
          lv_json_path = get_json_path( lt_new_path ).
    
          io_reader->read_next_node( ).
          lv_json_path = |{ lv_json_path }={ io_reader->value }|.
          APPEND lv_json_path TO ct_json_paths.
          io_reader->read_next_node( ).
    
          DELETE lt_new_path INDEX lines( lt_new_path ).
          serialize_rec_array( EXPORTING io_reader     = io_reader
                                         it_path       = lt_new_path
                               CHANGING  ct_json_paths = ct_json_paths ).
    
        ELSEIF is_array( io_reader ) = abap_true AND io_reader->node_type = if_sxml_node=>co_nt_element_open.
    
          APPEND io_reader->value TO lt_new_path.
          serialize_rec_array( EXPORTING io_reader     = io_reader
                                         it_path       = lt_new_path
                               CHANGING  ct_json_paths = ct_json_paths ).
    
        ELSEIF ( is_object( io_reader ) = abap_true OR is_array( io_reader ) = abap_true )
                 AND io_reader->node_type = if_sxml_node=>co_nt_element_close.
    
          DELETE lt_new_path INDEX lines( lt_new_path ).
          serialize_rec_array( EXPORTING io_reader     = io_reader
                                         it_path       = lt_new_path
                               CHANGING  ct_json_paths = ct_json_paths ).
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_json_path.
        rv_result = concat_lines_of( table = it_path
                                     sep   = `.` ).
        REPLACE ALL OCCURRENCES OF `.[` IN rv_result WITH `[`.
    
      ENDMETHOD.
    
      METHOD deserialize.
    
        DATA: lo_merged                 TYPE REF TO /apmg/if_apm_ajson,
              lv_json_path              TYPE string,
              lo_deserialization_result TYPE REF TO /apmg/if_apm_ajson,
              lx_ajson                  TYPE REF TO /apmg/cx_apm_ajson_error.
    
        TRY.
            lo_merged = /apmg/cl_apm_ajson=>parse( `{}` ).
          CATCH /apmg/cx_apm_ajson_error INTO lx_ajson.
            zcx_abapgit_exception=>raise_with_text( lx_ajson ).
        ENDTRY.
    
        LOOP AT it_json_path INTO lv_json_path.
          IF is_comment_or_empty_line( lv_json_path ) = abap_true.
            CONTINUE.
          ENDIF.
    
          TRY.
              lo_deserialization_result = to_json( lv_json_path ).
            CATCH /apmg/cx_apm_ajson_error INTO lx_ajson.
              zcx_abapgit_exception=>raise_with_text( lx_ajson ).
          ENDTRY.
    
          TRY.
              lo_merged = /apmg/cl_apm_ajson_utilities=>new( )->merge( io_json_a = lo_merged
                                                                      io_json_b = lo_deserialization_result ).
            CATCH /apmg/cx_apm_ajson_error INTO lx_ajson.
              zcx_abapgit_exception=>raise_with_text( lx_ajson ).
          ENDTRY.
    
        ENDLOOP.
    
        TRY.
            rv_result = lo_merged->stringify( 2 ).
          CATCH /apmg/cx_apm_ajson_error INTO lx_ajson.
            zcx_abapgit_exception=>raise_with_text( lx_ajson ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD is_comment_or_empty_line.
    
        IF iv_line IS INITIAL.
          rv_result = abap_true.
          RETURN.
        ENDIF.
    
        FIND REGEX `^!` IN iv_line ##REGEX_POSIX.
        IF sy-subrc = 0.
          rv_result = abap_true.
          RETURN.
        ENDIF.
    
        FIND REGEX `^#` IN iv_line ##REGEX_POSIX.
        IF sy-subrc = 0.
          rv_result = abap_true.
          RETURN.
        ENDIF.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_json_path IMPLEMENTATION.
    
      METHOD deserialize.
    
        rv_result = lcl_json_path=>deserialize( it_json_path ).
    
      ENDMETHOD.
    
      METHOD serialize.
        DATA: lo_json_path    TYPE REF TO lcl_json_path,
              lv_json_xstring TYPE xstring,
              lt_root_path    TYPE string_table,
              lo_reader       TYPE REF TO if_sxml_reader,
              lx_parse_error  TYPE REF TO cx_sxml_parse_error.
    
        lv_json_xstring = zcl_abapgit_convert=>string_to_xstring_utf8( iv_json ).
        lo_reader = cl_sxml_string_reader=>create( input = lv_json_xstring ).
    
        TRY.
            IF lo_reader->read_next_node( ) IS INITIAL.
              RETURN.
            ENDIF.
          CATCH cx_sxml_parse_error INTO lx_parse_error.
            zcx_abapgit_exception=>raise_with_text( lx_parse_error ).
        ENDTRY.
    
        APPEND `$` TO lt_root_path.
    
        CREATE OBJECT lo_json_path.
        lo_json_path->serialize_rec( EXPORTING io_reader     = lo_reader
                                               it_path       = lt_root_path
                                     CHANGING  ct_json_paths = rt_result ).
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_language IMPLEMENTATION.
    
      METHOD class_constructor.
    
        DATA lv_dummy TYPE string.
    
        GET LOCALE LANGUAGE gv_login_language COUNTRY lv_dummy MODIFIER lv_dummy.
    
      ENDMETHOD.
    
      METHOD restore_login_language.
    
        SET LOCALE LANGUAGE gv_login_language.
    
      ENDMETHOD.
    
      METHOD set_current_language.
    
        SET LOCALE LANGUAGE iv_language.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_objects_activation IMPLEMENTATION.
    
      METHOD activate.
    
        " Make sure that all changes are committed since any activation error will lead to a rollback
        COMMIT WORK AND WAIT.
    
        IF use_new_activation_logic( ) = abap_true.
          activate_new(
            iv_ddic = iv_ddic
            ii_log  = ii_log ).
        ELSE.
          activate_old(
            iv_ddic = iv_ddic
            ii_log  = ii_log ).
        ENDIF.
    
        update_where_used( ii_log ).
    
      ENDMETHOD.
    
      METHOD activate_ddic.
    
        DATA: lt_gentab     TYPE STANDARD TABLE OF dcgentb,
              lv_rc         TYPE sy-subrc,
              ls_gentab     LIKE LINE OF lt_gentab,
              lt_deltab     TYPE STANDARD TABLE OF dcdeltb,
              lt_action_tab TYPE STANDARD TABLE OF dctablres,
              lv_logname    TYPE ddmass-logname.
    
        FIELD-SYMBOLS:  LIKE LINE OF gt_objects.
    
        LOOP AT gt_objects ASSIGNING .
          " Filter types supported by mass activation
          IF is_ddic_type( -object ) = abap_false.
            CONTINUE.
          ENDIF.
          ls_gentab-tabix = sy-tabix.
    
          get_ddic_type(
            EXPORTING
              iv_obj_type = -object
              iv_obj_name = -obj_name
            IMPORTING
              ev_type     = ls_gentab-type
              ev_name     = ls_gentab-name
              ev_id       = ls_gentab-indx ).
    
          INSERT ls_gentab INTO TABLE lt_gentab.
        ENDLOOP.
    
        IF lt_gentab IS NOT INITIAL.
    
          lv_logname = |ABAPGIT_{ sy-datum }_{ sy-uzeit }|.
    
          IF lines( lt_gentab ) = 1.
            ii_log->add_info( |> Mass activating 1 DDIC object| ).
          ELSE.
            ii_log->add_info( |> Mass activating { lines( lt_gentab ) } DDIC objects| ).
          ENDIF.
          ii_log->add_info( |Log name: { lv_logname }| ).
    
          CALL FUNCTION 'DD_MASS_ACT_C3'
            EXPORTING
              ddmode         = 'O'         " activate changes in Original System
              frcact         = abap_true   " force Activation
              medium         = 'T'         " transport order
              device         = 'T'         " saves to table DDRPH?
              version        = 'M'         " activate newest version
              logname        = lv_logname
              write_log      = abap_true
              log_head_tail  = abap_true
              t_on           = space
              prid           = 1
            IMPORTING
              act_rc         = lv_rc
            TABLES
              gentab         = lt_gentab
              deltab         = lt_deltab
              cnvtab         = lt_action_tab
            EXCEPTIONS
              access_failure = 1
              no_objects     = 2
              locked         = 3
              internal_error = 4
              OTHERS         = 5.
    
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          IF lv_rc > 0.
            add_errors_and_warnings_to_log(
              iv_logname = lv_logname
              ii_log     = ii_log ).
          ENDIF.
    
          IF lv_rc > 4.
            zcx_abapgit_exception=>raise( 'Activation cancelled. Check the inactive objects.' ).
          ENDIF.
    
          " Remove objects from activation queue to avoid double activation in activate_old
          LOOP AT lt_gentab INTO ls_gentab.
            DELETE gt_objects WHERE object = ls_gentab-type AND obj_name = ls_gentab-name.
          ENDLOOP.
          DELETE gt_objects WHERE object = 'INDX' OR object = 'XINX' OR object = 'MCID'.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD activate_new.
    
        IF gt_objects IS INITIAL.
          RETURN.
        ENDIF.
    
        IF iv_ddic = abap_true.
    
          activate_ddic( ii_log ).
    
        ELSE.
    
          activate_old( ii_log ).
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD activate_old.
    
        DATA:
          lv_popup     TYPE abap_bool,
          lv_no_ui     TYPE abap_bool,
          lv_try_again TYPE abap_bool,
          lv_msg       TYPE string,
          lo_checklist TYPE REF TO cl_wb_checklist.
    
        IF gt_objects IS NOT INITIAL.
    
          IF /apmg/cl_apm_gui_factory=>get_frontend_services( )->gui_is_available( ) = abap_true.
            IF /apmg/cl_apm_settings=>factory( )->get( )-gui_settings-activate_wo_popup = abap_true.
              lv_popup = abap_false.
            ELSE.
              lv_popup = abap_true.
            ENDIF.
          ELSE.
            lv_popup = abap_false.
          ENDIF.
    
          lv_no_ui = boolc( lv_popup = abap_false ).
    
          IF iv_ddic = abap_true.
            lv_msg = |(with DDIC)|.
          ENDIF.
          IF lines( gt_objects ) = 1.
            ii_log->add_info( |> Activating 1 object { lv_msg }| ).
          ELSE.
            ii_log->add_info( |> Activating { lines( gt_objects ) } objects { lv_msg }| ).
          ENDIF.
    
          TRY.
              CALL FUNCTION 'RS_WORKING_OBJECTS_ACTIVATE'
                EXPORTING
                  activate_ddic_objects  = iv_ddic
                  with_popup             = lv_popup
                  ui_decoupled           = lv_no_ui
                IMPORTING
                  p_checklist            = lo_checklist
                TABLES
                  objects                = gt_objects
                EXCEPTIONS
                  excecution_error       = 1
                  cancelled              = 2
                  insert_into_corr_error = 3
                  OTHERS                 = 4 ##FM_SUBRC_OK.
            CATCH cx_sy_dyn_call_param_not_found.
              CALL FUNCTION 'RS_WORKING_OBJECTS_ACTIVATE'
                EXPORTING
                  activate_ddic_objects  = iv_ddic
                  with_popup             = lv_popup
                IMPORTING
                  p_checklist            = lo_checklist
                TABLES
                  objects                = gt_objects
                EXCEPTIONS
                  excecution_error       = 1
                  cancelled              = 2
                  insert_into_corr_error = 3
                  OTHERS                 = 4 ##FM_SUBRC_OK.
          ENDTRY.
          CASE sy-subrc.
            WHEN 1 OR 3 OR 4.
              zcx_abapgit_exception=>raise_t100( ).
            WHEN 2.
              lv_msg = 'Check the log and inactive objects.'.
              IF lv_popup = abap_false.
                lv_try_again = add_activation_errors_to_log(
                  ii_log       = ii_log
                  io_checklist = lo_checklist ).
                IF lv_try_again = abap_true.
                  lv_msg = 'Turn on "Activation Popup" in "Personal Settings" and try again'.
                ENDIF.
              ENDIF.
              zcx_abapgit_exception=>raise( |Activation cancelled. { lv_msg }| ).
          ENDCASE.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD add.
    
    * function group SEWORKINGAREA
    * function module RS_INSERT_INTO_WORKING_AREA
    * class CL_WB_ACTIVATION_WORK_AREA
    
        FIELD-SYMBOLS:   TYPE dwinactiv,
                        LIKE LINE OF gt_classes.
    
        IF iv_type = 'CLAS' OR iv_type = 'INTF'.
          APPEND INITIAL LINE TO gt_classes ASSIGNING .
          -object  = iv_type.
          -clsname = iv_name.
        ELSE.
          APPEND INITIAL LINE TO gt_objects ASSIGNING .
          -object     = iv_type.
          -obj_name   = iv_name.
          -delet_flag = iv_delete.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD add_activation_errors_to_log.
    
        DATA:
          ls_item    TYPE zif_abapgit_definitions=>ty_item,
          lt_message TYPE swbme_error_tab.
    
        FIELD-SYMBOLS:
               TYPE string,
           LIKE LINE OF lt_message.
    
        io_checklist->get_error_messages( IMPORTING p_error_tab = lt_message ).
    
        LOOP AT lt_message ASSIGNING  WHERE mtype = 'E'.
          " When activating without popup, includes used in multiple main programs cause error
          " Run again WITH activation popup (see abapGit, Personal Settings)
          IF -message-msgid = 'EU' AND -message-msgno = '404'.
            rv_try_again = abap_true.
          ENDIF.
          CLEAR ls_item.
          IF strlen( -object_text ) > 5.
            ls_item-obj_type = -object_text(4).
            ls_item-obj_name = -object_text+5(*).
          ELSEIF -show_req IS NOT INITIAL.
            ls_item-obj_name = -show_req->object_name.
            SELECT SINGLE tadir FROM euobjedit INTO ls_item-obj_type
              WHERE type = -show_req->object_type.
          ENDIF.
          LOOP AT -mtext ASSIGNING .
            IF sy-tabix = 1.
              ii_log->add(
                iv_type   = 'E'
                iv_msg    = 
                iv_class  = -message-msgid
                iv_number = -message-msgno
                is_item   = ls_item ).
            ELSE.
              ii_log->add_error(
                iv_msg  = 
                is_item = ls_item ).
            ENDIF.
          ENDLOOP.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD add_errors_and_warnings_to_log.
    
        DATA: lt_lines      TYPE STANDARD TABLE OF trlog,
              lv_logname_db TYPE ddprh-protname.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_lines.
    
        lv_logname_db = iv_logname.
    
        CALL FUNCTION 'TR_READ_LOG'
          EXPORTING
            iv_log_type   = 'DB'
            iv_logname_db = lv_logname_db
          TABLES
            et_lines      = lt_lines
          EXCEPTIONS
            invalid_input = 1
            access_error  = 2
            OTHERS        = 3.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        " Only error messages
        DELETE lt_lines WHERE severity <> 'E'
                          AND severity <> 'W'.
        " Remove "Return code..." message
        DELETE lt_lines WHERE class = 'D0' AND number = '319'.
    
        LOOP AT lt_lines ASSIGNING .
          ii_log->add( iv_msg    = -line
                       iv_type   = -severity
                       iv_class  = -class
                       iv_number = |{ -number }| ).
        ENDLOOP.
    
        ii_log->add_info( |View complete activation log in program RSPUTPRT (type D, log name { iv_logname })| ).
    
      ENDMETHOD.
    
      METHOD add_item.
        add( iv_type = is_item-obj_type
             iv_name = is_item-obj_name ).
      ENDMETHOD.
    
      METHOD clear.
        CLEAR gt_objects.
        CLEAR gt_classes.
      ENDMETHOD.
    
      METHOD get_ddic_type.
    
        DATA lv_obj_name TYPE e071-obj_name.
    
        ev_type = iv_obj_type.
    
        IF ev_type = 'INDX' OR ev_type = 'XINX' OR ev_type = 'MCID'.
          lv_obj_name = iv_obj_name. "cast
    
          CALL FUNCTION 'DD_E071_TO_DD'
            EXPORTING
              object        = ev_type
              obj_name      = lv_obj_name
            IMPORTING
              name          = ev_name
              id            = ev_id
            EXCEPTIONS
              illegal_input = 1
              OTHERS        = 2.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
        ELSE.
          ev_name = iv_obj_name.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD is_active.
    
        " Checks if object is active or not
        "
        " Note: If object does not exist, this method returns true
        " is_not_inactive might be a better name but we avoid the double negative
    
        IF is_ddic_type( is_item-obj_type ) = abap_true
          AND c_para     NS is_item-obj_type
          AND c_switches NS is_item-obj_type.
          rv_active = is_ddic_active( is_item ).
        ELSE.
          rv_active = is_non_ddic_active( is_item ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD is_ddic_active.
    
        DATA:
          lv_type  TYPE ddobjtyp,
          lv_name  TYPE ddobjname,
          lv_id    TYPE ddobjectid,
          lv_state TYPE ddgotstate.
    
        get_ddic_type(
          EXPORTING
            iv_obj_type = is_item-obj_type
            iv_obj_name = is_item-obj_name
          IMPORTING
            ev_type     = lv_type
            ev_name     = lv_name
            ev_id       = lv_id ).
    
        " Check if an inactive version of the DDIC object exists
        " state = 'A' checks if an active version exists but does not detect new or modified objects
        " state = 'M' checks for all possible versions so we can find out if an inactive one exists
        " See documentation of the function module
        CALL FUNCTION 'DDIF_STATE_GET'
          EXPORTING
            type          = lv_type
            name          = lv_name
            id            = lv_id
            state         = 'M'
          IMPORTING
            gotstate      = lv_state
          EXCEPTIONS
            illegal_input = 1
            OTHERS        = 2.
    
        rv_active = boolc( sy-subrc = 0 AND ( lv_state = '' OR lv_state = 'A' ) ).
    
      ENDMETHOD.
    
      METHOD is_ddic_type.
    
        " Determine if object can be handled by mass activation (see RADMASUTC form ma_tab_check)
    
        rv_result = abap_true.
    
        IF c_domain   NS iv_obj_type AND c_types      NS iv_obj_type AND
           c_technset NS iv_obj_type AND c_f4_objects NS iv_obj_type AND
           c_enqueue  NS iv_obj_type AND c_sqsc       NS iv_obj_type AND
           c_stob     NS iv_obj_type AND c_ntab       NS iv_obj_type AND
           c_cds      NS iv_obj_type AND c_para       NS iv_obj_type AND
           c_switches NS iv_obj_type AND iv_obj_type <> c_enhd       AND
           c_aspect   NS iv_obj_type AND c_scalarfunc NS iv_obj_type.
          rv_result = abap_false.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD is_non_ddic_active.
    
        DATA:
          lt_messages TYPE STANDARD TABLE OF sprot_u WITH DEFAULT KEY,
          ls_e071     TYPE e071,
          lt_e071     TYPE STANDARD TABLE OF e071 WITH DEFAULT KEY.
    
        ls_e071-object   = is_item-obj_type.
        ls_e071-obj_name = is_item-obj_name.
        INSERT ls_e071 INTO TABLE lt_e071.
    
        CALL FUNCTION 'RS_INACTIVE_OBJECTS_WARNING'
          EXPORTING
            suppress_protocol         = abap_false
            with_program_includes     = abap_false
            suppress_dictionary_check = abap_false
          TABLES
            p_e071                    = lt_e071
            p_xmsg                    = lt_messages.
    
        rv_active = boolc( lt_messages IS INITIAL ).
    
      ENDMETHOD.
    
      METHOD update_where_used.
    
        DATA: ls_class   LIKE LINE OF gt_classes,
              lo_cross   TYPE REF TO cl_wb_crossreference,
              ls_item    TYPE zif_abapgit_definitions=>ty_item,
              lv_msg     TYPE string,
              lv_error   TYPE c LENGTH 1,
              lv_include TYPE syrepid.
    
        LOOP AT gt_classes INTO ls_class.
          CASE ls_class-object.
            WHEN 'CLAS'.
              lv_include = cl_oo_classname_service=>get_classpool_name( ls_class-clsname ).
            WHEN 'INTF'.
              lv_include = cl_oo_classname_service=>get_interfacepool_name( ls_class-clsname ).
          ENDCASE.
    
          CREATE OBJECT lo_cross
            EXPORTING
              p_name    = lv_include
              p_include = lv_include.
    
          lo_cross->index_actualize( IMPORTING p_error = lv_error ).
    
          IF lv_error = abap_true.
            ls_item-obj_type = ls_class-object.
            ls_item-obj_name = ls_class-clsname.
            lv_msg = |Error updating where-used list for { ls_item-obj_type } { ls_item-obj_name }.|
              && | Check for syntax errors|.
            ii_log->add(
              iv_msg  = lv_msg
              is_item = ls_item ).
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD use_new_activation_logic.
    
        IF zcl_abapgit_factory=>get_function_module( )->function_exists( 'DD_MASS_ACT_C3' ) = abap_true.
          rv_use_new_activation_logic = abap_true.
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_XML_INPUT IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor( iv_filename ).
        parse( iv_xml ).
        fix_xml( ).
    
      ENDMETHOD.
    
      METHOD fix_xml.
    
        DATA: li_git  TYPE REF TO if_ixml_element,
              li_abap TYPE REF TO if_ixml_node.
    
        li_git ?= mi_xml_doc->find_from_name_ns( depth = 0
                                                 name = c_abapgit_tag ).
        li_abap = li_git->get_first_child( ).
    
        mi_xml_doc->get_root( )->remove_child( li_git ).
        mi_xml_doc->get_root( )->append_child( li_abap ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_xml_input~get_metadata.
        rs_metadata = ms_metadata.
      ENDMETHOD.
    
      METHOD zif_abapgit_xml_input~get_raw.
        ri_raw = mi_xml_doc.
      ENDMETHOD.
    
      METHOD zif_abapgit_xml_input~read.
    
        DATA: lx_error TYPE REF TO cx_transformation_error,
              lt_rtab  TYPE abap_trans_resbind_tab.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_rtab.
    
        ASSERT NOT iv_name IS INITIAL.
    
        CLEAR cg_data. "Initialize result to avoid problems with empty values
    
        APPEND INITIAL LINE TO lt_rtab ASSIGNING .
        -name = iv_name.
        GET REFERENCE OF cg_data INTO -value.
    
        TRY.
            CALL TRANSFORMATION id
              OPTIONS value_handling = 'accept_data_loss'
              SOURCE XML mi_xml_doc
              RESULT (lt_rtab).
          CATCH cx_transformation_error INTO lx_error.
            IF mv_filename IS INITIAL.
              zcx_abapgit_exception=>raise( lx_error->if_message~get_text( ) ).
            ELSE.
              zcx_abapgit_exception=>raise( |File { mv_filename }: { lx_error->if_message~get_text( ) }| ).
            ENDIF.
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_objects_compare IMPLEMENTATION.
    
      METHOD get_comparator.
    
        DATA lv_class_name TYPE seoclsname.
    
        CONCATENATE 'ZCL_ABAPGIT_OBJECT_' is_item-obj_type '_COMPAR' INTO lv_class_name.
    
        IF zcl_abapgit_factory=>get_environment( )->is_merged( ) = abap_true.
          " Prevent accidental usage of object handlers in the developer version
          lv_class_name = |\\PROGRAM={ sy-repid }\\CLASS={ lv_class_name }|.
        ENDIF.
    
        TRY.
            CREATE OBJECT ri_comparator TYPE (lv_class_name)
              EXPORTING
                is_item = is_item.
          CATCH cx_sy_create_object_error ##NO_HANDLER.
            " No instance, no comparator for this object type
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get_result.
    
        " this method is used for comparing local with remote objects
        " before pull, this is useful eg. when overwriting a TABL object.
        " only the main XML file is used for comparison
    
        DATA:
          ls_remote_file    TYPE zif_abapgit_git_definitions=>ty_file,
          ls_local_file     TYPE zif_abapgit_definitions=>ty_file_item,
          li_remote_version TYPE REF TO zif_abapgit_xml_input,
          li_local_version  TYPE REF TO zif_abapgit_xml_input,
          li_log            TYPE REF TO zif_abapgit_log,
          ls_msg            TYPE zif_abapgit_log=>ty_log_out,
          lt_msg            TYPE zif_abapgit_log=>ty_log_outs,
          ls_result         TYPE zif_abapgit_comparator=>ty_result.
    
        " REMOTE
        " if file does not exist in remote, we don't need to validate
        READ TABLE it_remote WITH KEY file COMPONENTS filename = iv_filename INTO ls_remote_file.
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        CREATE OBJECT li_remote_version TYPE zcl_abapgit_xml_input
          EXPORTING
            iv_xml      = zcl_abapgit_convert=>xstring_to_string_utf8( ls_remote_file-data )
            iv_filename = iv_filename.
    
        " LOCAL
        " if file does not exist locally, we don't need to validate
        READ TABLE it_local WITH KEY file-filename = iv_filename INTO ls_local_file.
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        CREATE OBJECT li_local_version TYPE zcl_abapgit_xml_input
          EXPORTING
            iv_xml      = zcl_abapgit_convert=>xstring_to_string_utf8( ls_local_file-file-data )
            iv_filename = iv_filename.
    
        " COMPARE
        CREATE OBJECT li_log TYPE zcl_abapgit_log.
    
        ls_result = ii_comparator->compare(
          ii_local  = li_local_version
          ii_remote = li_remote_version
          ii_log    = li_log ).
    
        rv_result = ls_result-text.
    
        " To keep it simple, append the log messages to the result
        lt_msg = li_log->get_messages( ).
    
        LOOP AT lt_msg INTO ls_msg.
          rv_result = |{ rv_result }, { ls_msg-text }|.
        ENDLOOP.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_OBJECTS_FACTORY IMPLEMENTATION.
    
      METHOD get_gui_jumper.
    
        IF gi_gui_jumper IS INITIAL.
          CREATE OBJECT gi_gui_jumper TYPE zcl_abapgit_gui_jumper.
        ENDIF.
    
        ri_gui_jumper = gi_gui_jumper.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_OBJECTS_FILES IMPLEMENTATION.
    
      METHOD add.
        APPEND is_file TO mt_files.
      ENDMETHOD.
    
      METHOD add_abap.
    
        DATA: lv_source TYPE string,
              ls_file   TYPE zif_abapgit_git_definitions=>ty_file.
    
        CONCATENATE LINES OF it_abap INTO lv_source SEPARATED BY cl_abap_char_utilities=>newline.
    * when editing files via eg. GitHub web interface it adds a newline at end of file
        lv_source = lv_source && cl_abap_char_utilities=>newline.
    
        ls_file-path = '/'.
        ls_file-filename = zcl_abapgit_filename_logic=>object_to_file(
          is_item  = ms_item
          iv_extra = iv_extra
          iv_ext   = 'abap' ).
        ls_file-data = zcl_abapgit_convert=>string_to_xstring_utf8( lv_source ).
    
        APPEND ls_file TO mt_files.
    
      ENDMETHOD.
    
      METHOD add_i18n_file.
    
        DATA ls_file TYPE zif_abapgit_git_definitions=>ty_file.
    
        ls_file-data = ii_i18n_file->render( ).
        IF ls_file-data IS INITIAL.
          RETURN. " Don't add empty files
        ENDIF.
    
        ls_file-path     = '/'.
        ls_file-filename = zcl_abapgit_filename_logic=>object_to_i18n_file(
          is_item  = ms_item
          iv_lang_suffix = ii_i18n_file->lang_suffix( )
          iv_ext   = ii_i18n_file->ext( ) ).
    
        APPEND ls_file TO mt_files.
    
      ENDMETHOD.
    
      METHOD add_raw.
    
        DATA: ls_file TYPE zif_abapgit_git_definitions=>ty_file.
    
        ls_file-path     = '/'.
        ls_file-data     = iv_data.
        ls_file-filename = zcl_abapgit_filename_logic=>object_to_file(
          is_item  = ms_item
          iv_extra = iv_extra
          iv_ext   = iv_ext ).
    
        APPEND ls_file TO mt_files.
    
      ENDMETHOD.
    
      METHOD add_string.
    
        DATA: ls_file TYPE zif_abapgit_git_definitions=>ty_file.
    
        ls_file-path = '/'.
        ls_file-filename = zcl_abapgit_filename_logic=>object_to_file(
          is_item  = ms_item
          iv_extra = iv_extra
          iv_ext   = iv_ext ).
        ls_file-data = zcl_abapgit_convert=>string_to_xstring_utf8( iv_string ).
    
        APPEND ls_file TO mt_files.
    
      ENDMETHOD.
    
      METHOD add_xml.
    
        DATA: lv_xml  TYPE string,
              ls_file TYPE zif_abapgit_git_definitions=>ty_file.
    
        lv_xml = ii_xml->render( iv_normalize = iv_normalize
                                 is_metadata = is_metadata ).
        ls_file-path = '/'.
    
        ls_file-filename = zcl_abapgit_filename_logic=>object_to_file(
          is_item  = ms_item
          iv_extra = iv_extra
          iv_ext   = 'xml' ).
    
        REPLACE FIRST OCCURRENCE
          OF REGEX '<\?xml version="1\.0" encoding="[\w-]+"\?>'
          IN lv_xml
          WITH '' ##REGEX_POSIX.
        ASSERT sy-subrc = 0.
    
        ls_file-data = zcl_abapgit_convert=>string_to_xstring_utf8_bom( lv_xml ).
    
        APPEND ls_file TO mt_files.
      ENDMETHOD.
    
      METHOD constructor.
        ms_item = is_item.
        mv_path = iv_path.
      ENDMETHOD.
    
      METHOD contains_file.
        DATA: lv_filename TYPE string.
    
        lv_filename = zcl_abapgit_filename_logic=>object_to_file(
          is_item  = ms_item
          iv_extra = iv_extra
          iv_ext   = iv_ext ).
    
        IF mv_path IS NOT INITIAL.
          READ TABLE mt_files TRANSPORTING NO FIELDS
              WITH KEY file_path
              COMPONENTS path     = mv_path
                         filename = lv_filename.
        ELSE.
          READ TABLE mt_files TRANSPORTING NO FIELDS
              WITH KEY file
              COMPONENTS filename = lv_filename.
        ENDIF.
    
        IF sy-subrc = 0.
          rv_present = abap_true.
        ENDIF.
      ENDMETHOD.
    
      METHOD get_accessed_files.
        rt_files = mt_accessed_files.
      ENDMETHOD.
    
      METHOD get_files.
        rt_files = mt_files.
      ENDMETHOD.
    
      METHOD get_file_pattern.
        rv_pattern = zcl_abapgit_filename_logic=>object_to_file(
          is_item  = ms_item
          iv_ext   = '*' ).
        " Escape special characters for use with 'covers pattern' (CP)
        REPLACE ALL OCCURRENCES OF '#' IN rv_pattern WITH '##'.
        REPLACE ALL OCCURRENCES OF '+' IN rv_pattern WITH '#+'.
      ENDMETHOD.
    
      METHOD is_json_metadata.
    
        DATA lv_pattern TYPE string.
    
        FIELD-SYMBOLS  LIKE LINE OF mt_files.
    
        lv_pattern = |*.{ to_lower( ms_item-obj_type ) }.json|.
    
        LOOP AT mt_files ASSIGNING  WHERE filename CP lv_pattern.
          rv_result = abap_true.
          EXIT.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD mark_accessed.
    
        FIELD-SYMBOLS  LIKE LINE OF mt_accessed_files.
    
        READ TABLE mt_accessed_files TRANSPORTING NO FIELDS
          WITH KEY path = iv_path filename = iv_file.
        IF sy-subrc > 0. " Not found ? -> Add
          APPEND INITIAL LINE TO mt_accessed_files ASSIGNING .
          -path     = iv_path.
          -filename = iv_file.
          -sha1     = iv_sha1.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD new.
        CREATE OBJECT ro_files
          EXPORTING
            is_item = is_item
            iv_path = iv_path.
      ENDMETHOD.
    
      METHOD read_abap.
    
        DATA: lv_filename TYPE string,
              lv_data     TYPE xstring,
              lv_abap     TYPE string.
    
        lv_filename = zcl_abapgit_filename_logic=>object_to_file(
          is_item  = ms_item
          iv_extra = iv_extra
          iv_ext   = 'abap' ).
    
        lv_data = read_file( iv_filename = lv_filename
                             iv_error    = iv_error ).
    
        IF lv_data IS INITIAL. " Post-handling of iv_error = false
          RETURN.
        ENDIF.
    
        lv_abap = zcl_abapgit_convert=>xstring_to_string_utf8( lv_data ).
    
        SPLIT lv_abap AT cl_abap_char_utilities=>newline INTO TABLE rt_abap.
    
      ENDMETHOD.
    
      METHOD read_file.
    
        FIELD-SYMBOLS      LIKE LINE OF mt_files.
    
        IF mv_path IS NOT INITIAL.
          READ TABLE mt_files ASSIGNING 
              WITH KEY file_path
              COMPONENTS path     = mv_path
                         filename = iv_filename.
        ELSE.
          READ TABLE mt_files ASSIGNING 
              WITH KEY file
              COMPONENTS filename = iv_filename.
        ENDIF.
    
        IF sy-subrc <> 0.
          IF iv_error = abap_true.
            zcx_abapgit_exception=>raise( |File not found: { iv_filename }| ).
          ELSE.
            RETURN.
          ENDIF.
        ENDIF.
    
        " Update access table
        mark_accessed(
          iv_path = -path
          iv_file = -filename
          iv_sha1 = -sha1 ).
    
        rv_data = -data.
    
      ENDMETHOD.
    
      METHOD read_i18n_files.
    
        DATA:
          lv_lang       TYPE laiso,
          lv_ext        TYPE string,
          lo_po         TYPE REF TO zcl_abapgit_po_file,
          lo_properties TYPE REF TO zcl_abapgit_properties_file.
    
        FIELD-SYMBOLS  LIKE LINE OF mt_files.
    
        LOOP AT mt_files ASSIGNING .
    
          CHECK find( val = -filename
                      sub = '.i18n.' ) > 0. " Only i18n files are relevant
    
          zcl_abapgit_filename_logic=>i18n_file_to_object(
            EXPORTING
              iv_path     = -path
              iv_filename = -filename
            IMPORTING
              ev_lang     = lv_lang
              ev_ext      = lv_ext ).
    
          CASE lv_ext.
            WHEN 'po'.
              CREATE OBJECT lo_po EXPORTING iv_lang = lv_lang.
              lo_po->parse( -data ).
              APPEND lo_po TO rt_i18n_files.
            WHEN 'properties'.
              CREATE OBJECT lo_properties EXPORTING iv_lang = lv_lang.
              lo_properties->parse( -data ).
              APPEND lo_properties TO rt_i18n_files.
            WHEN OTHERS.
              CONTINUE. " Unsupported i18n file type
          ENDCASE.
    
          mark_accessed(
            iv_path = -path
            iv_file = -filename
            iv_sha1 = -sha1 ).
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD read_raw.
    
        DATA: lv_filename TYPE string.
    
        lv_filename = zcl_abapgit_filename_logic=>object_to_file(
          is_item  = ms_item
          iv_extra = iv_extra
          iv_ext   = iv_ext ).
    
        rv_data = read_file( lv_filename ).
    
      ENDMETHOD.
    
      METHOD read_string.
    
        DATA: lv_filename TYPE string,
              lv_data     TYPE xstring.
    
        lv_filename = zcl_abapgit_filename_logic=>object_to_file(
          is_item  = ms_item
          iv_extra = iv_extra
          iv_ext   = iv_ext ).
    
        lv_data = read_file( lv_filename ).
    
        rv_string = zcl_abapgit_convert=>xstring_to_string_utf8( lv_data ).
    
      ENDMETHOD.
    
      METHOD read_xml.
    
        DATA: lv_filename TYPE string,
              lv_data     TYPE xstring,
              lv_xml      TYPE string.
    
        lv_filename = zcl_abapgit_filename_logic=>object_to_file(
          is_item  = ms_item
          iv_extra = iv_extra
          iv_ext   = 'xml' ).
    
        lv_data = read_file( lv_filename ).
    
        lv_xml = zcl_abapgit_convert=>xstring_to_string_utf8( lv_data ).
    
        CREATE OBJECT ri_xml
          TYPE zcl_abapgit_xml_input
          EXPORTING
            iv_xml      = lv_xml
            iv_filename = lv_filename.
    
      ENDMETHOD.
    
      METHOD set_files.
    
        FIELD-SYMBOLS:  LIKE LINE OF it_files.
    
        CLEAR mt_files.
    
        " Set only files matching the pattern for this object
        " If a path has been defined in the constructor, then the path has to match, too
        LOOP AT it_files ASSIGNING  WHERE filename CP get_file_pattern( ).
          IF mv_path IS INITIAL.
            INSERT  INTO TABLE mt_files.
          ELSEIF mv_path = -path.
            INSERT  INTO TABLE mt_files.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_objects_generic IMPLEMENTATION.
    
      METHOD after_import.
    
        DATA: lt_cts_object_entry TYPE STANDARD TABLE OF e071 WITH DEFAULT KEY,
              ls_cts_object_entry LIKE LINE OF lt_cts_object_entry,
              lt_cts_key          TYPE STANDARD TABLE OF e071k WITH DEFAULT KEY.
    
        FIELD-SYMBOLS  LIKE LINE OF mt_object_method.
    
        ls_cts_object_entry-pgmid    = 'R3TR'.
        ls_cts_object_entry-object   = ms_item-obj_type.
        ls_cts_object_entry-obj_name = ms_item-obj_name.
        INSERT ls_cts_object_entry INTO TABLE lt_cts_object_entry.
    
        READ TABLE mt_object_method ASSIGNING 
          WITH KEY
            objectname = ms_item-obj_type
            objecttype = 'L'
            method = 'AFTER_IMP'.
        IF sy-subrc = 0.
    * client is actually optional for most AIM, but let's supply it and hope
    * that those client-independent-ones just ignore it
          CALL FUNCTION -methodname
            EXPORTING
              iv_tarclient  = sy-mandt
              iv_is_upgrade = abap_false
            TABLES
              tt_e071       = lt_cts_object_entry
              tt_e071k      = lt_cts_key.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD apply_clear_logic.
        IF mo_field_rules IS BOUND.
          mo_field_rules->apply_clear_logic( EXPORTING iv_table = |{ iv_table }|
                                             CHANGING  ct_data  = ct_data ).
        ENDIF.
      ENDMETHOD.
    
      METHOD apply_fill_logic.
        IF mo_field_rules IS BOUND.
          mo_field_rules->apply_fill_logic(
            EXPORTING
              iv_table   = |{ iv_table }|
              iv_package = iv_package
            CHANGING
              ct_data    = ct_data ).
        ENDIF.
      ENDMETHOD.
    
      METHOD before_export.
    
        DATA: lt_cts_object_entry TYPE STANDARD TABLE OF e071 WITH DEFAULT KEY,
              ls_cts_object_entry LIKE LINE OF lt_cts_object_entry,
              lt_cts_key          TYPE STANDARD TABLE OF e071k WITH DEFAULT KEY,
              lv_client           TYPE trclient.
    
        FIELD-SYMBOLS  LIKE LINE OF mt_object_method.
    
        READ TABLE mt_object_method ASSIGNING 
          WITH KEY
            objectname = ms_item-obj_type
            objecttype = 'L'
            method     = 'BEFORE_EXP'.
        IF sy-subrc = 0.
          lv_client = sy-mandt.
    
          ls_cts_object_entry-pgmid    = 'R3TR'.
          ls_cts_object_entry-object   = ms_item-obj_type.
          ls_cts_object_entry-obj_name = ms_item-obj_name.
          INSERT ls_cts_object_entry INTO TABLE lt_cts_object_entry.
    
          CALL FUNCTION -methodname
            EXPORTING
              iv_client = lv_client
            TABLES
              tt_e071   = lt_cts_object_entry
              tt_e071k  = lt_cts_key.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        CONSTANTS lc_logical_transport_object TYPE c LENGTH 1 VALUE 'L'.
    
        SELECT SINGLE * FROM objh INTO ms_object_header
          WHERE objectname = is_item-obj_type
          AND objecttype = lc_logical_transport_object.
        IF sy-subrc <> 0.
          RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDIF.
    
        " object tables
        SELECT * FROM objsl INTO CORRESPONDING FIELDS OF TABLE mt_object_table
          WHERE objectname = is_item-obj_type
          AND objecttype = lc_logical_transport_object
          AND tobject = 'TABU'
          ORDER BY PRIMARY KEY.
        IF mt_object_table IS INITIAL.
          zcx_abapgit_exception=>raise( |Obviously corrupted object-type { is_item-obj_type }: No tables defined| ).
        ENDIF.
    
        " remove duplicate table/table-key entries
        " same table with different keys is ok
        SORT mt_object_table BY tobj_name tobjkey.
        DELETE ADJACENT DUPLICATES FROM mt_object_table COMPARING tobj_name tobjkey.
    
        " object methods
        SELECT * FROM objm INTO TABLE mt_object_method
          WHERE objectname = is_item-obj_type
          AND objecttype = lc_logical_transport_object
          ORDER BY PRIMARY KEY.
    
        ms_item = is_item.
        mv_language = iv_language.
        mo_field_rules = io_field_rules.
    
      ENDMETHOD.
    
      METHOD corr_insert.
    
    * this will also insert into TADIR
        zcl_abapgit_factory=>get_cts_api( )->insert_transport_object(
          iv_object   = ms_item-obj_type
          iv_obj_name = ms_item-obj_name
          iv_package  = iv_package
          iv_language = mv_language ).
    
      ENDMETHOD.
    
      METHOD delete.
    
        DATA: lv_where   TYPE string,
              lv_primary TYPE objsl-tobj_name.
    
        FIELD-SYMBOLS  LIKE LINE OF mt_object_table.
    
        lv_primary = get_primary_table( ).
    
        LOOP AT mt_object_table ASSIGNING .
          lv_where = get_where_clause( -tobj_name ).
          ASSERT NOT lv_where IS INITIAL.
    
          DELETE FROM (-tobj_name) WHERE (lv_where).
    
          IF -tobj_name = lv_primary.
            ASSERT sy-dbcnt <= 1. "Just to be on the very safe side
          ENDIF.
        ENDLOOP.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD deserialize.
    
        validate( io_xml ).
    
        delete( iv_package ).
    
        deserialize_data(
          io_xml     = io_xml
          iv_package = iv_package ).
    
        after_import( ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD deserialize_data.
    
        DATA: lr_ref TYPE REF TO data.
    
        FIELD-SYMBOLS:   TYPE STANDARD TABLE,
                        LIKE LINE OF mt_object_table.
    
        LOOP AT mt_object_table ASSIGNING .
    
          CREATE DATA lr_ref TYPE STANDARD TABLE OF (-tobj_name).
          ASSIGN lr_ref->* TO .
    
          io_xml->read(
            EXPORTING
              iv_name = -tobj_name
            CHANGING
              cg_data =  ).
          apply_fill_logic(
            EXPORTING
              iv_table   = -tobj_name
              iv_package = iv_package
            CHANGING
              ct_data    =  ).
    
          INSERT (-tobj_name) FROM TABLE .
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( |Error inserting data, { -tobj_name }| ).
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD distribute_name_to_components.
    
        DATA: lt_key_component_uncovered  LIKE it_key_component,
              ls_key_component_uncovered  LIKE LINE OF lt_key_component_uncovered,
              ls_objkey_sub               LIKE cs_objkey,
              lv_objkey_sub_pos           TYPE i,
              lv_remaining_length         TYPE i,
              lv_count_components_covered LIKE ls_objkey_sub-num.
    
        DATA lv_len LIKE ls_key_component_uncovered-leng.
    
        lt_key_component_uncovered = it_key_component.
        ls_objkey_sub-num = cs_objkey-num.
        lv_objkey_sub_pos = 0.
    
    *    we want to fill the attribute values which are not covered by explicit key components yet
        lv_count_components_covered = ls_objkey_sub-num - 1.
        DO lv_count_components_covered TIMES.
          DELETE lt_key_component_uncovered INDEX 1.
        ENDDO.
    
        LOOP AT lt_key_component_uncovered INTO ls_key_component_uncovered.
          CLEAR ls_objkey_sub-value.
    
    *      Some datatype used in the key might exceed the total remaining characters length (e. g. SICF)
          TRY.
              lv_remaining_length = strlen( |{ substring( val = cs_objkey-value
                                                          off = lv_objkey_sub_pos ) }| ).
            CATCH cx_sy_range_out_of_bounds.
              lv_remaining_length = 0.
              RETURN. ">>>>>>>>>>>>>>>>>>>>>>>>>>>
          ENDTRY.
          IF ls_key_component_uncovered-leng <= lv_remaining_length.
            lv_len = ls_key_component_uncovered-leng.
          ELSE.
            lv_len = lv_remaining_length.
          ENDIF.
    
          ls_objkey_sub-value = |{ substring( val = cs_objkey-value
                                              off = lv_objkey_sub_pos
                                              len = lv_len ) }|.
          ls_objkey_sub-num = cv_non_value_pos.
    
          INSERT ls_objkey_sub INTO TABLE ct_objkey.
    
          lv_objkey_sub_pos = lv_objkey_sub_pos + ls_key_component_uncovered-leng.
          cv_non_value_pos = cv_non_value_pos + 1.
          CLEAR ls_objkey_sub.
    
          IF lv_objkey_sub_pos = strlen( cs_objkey-value ).
            cs_objkey-num = cv_non_value_pos.
            EXIT. "end splitting - all characters captured
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD exists.
    
        DATA: lv_where_clause TYPE string,
              lv_primary      TYPE objsl-tobj_name,
              lr_table_line   TYPE REF TO data.
    
        FIELD-SYMBOLS:  TYPE any.
    
        lv_primary = get_primary_table( ).
    
        lv_where_clause = get_where_clause( lv_primary ).
    
        CREATE DATA lr_table_line TYPE (lv_primary).
        ASSIGN lr_table_line->* TO .
    
        SELECT SINGLE * FROM (lv_primary) INTO  WHERE (lv_where_clause).
        rv_bool = boolc( sy-dbcnt > 0 ).
    
      ENDMETHOD.
    
      METHOD get_key_fields.
    
        DATA: lv_table TYPE ddobjname.
    
        lv_table = iv_table.
    
        CALL FUNCTION 'DDIF_NAMETAB_GET'
          EXPORTING
            tabname   = lv_table
          TABLES
            dfies_tab = rt_keys
          EXCEPTIONS
            not_found = 1
            OTHERS    = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        DELETE rt_keys WHERE keyflag = abap_false.
    
      ENDMETHOD.
    
      METHOD get_primary_table.
    
        DATA: ls_object_table LIKE LINE OF mt_object_table.
        DATA: lt_object_table LIKE mt_object_table.
    
        " There might be several tables marked as "primary"
        " Sort by DB key so we get first one in the list
        lt_object_table = mt_object_table.
        SORT lt_object_table.
    
        READ TABLE lt_object_table INTO ls_object_table WITH KEY prim_table = abap_true.
        IF sy-subrc <> 0.
          " Fallback. For some objects, no primary table is explicitly flagged
          " Then, the one with only one key field shall be chosen
          READ TABLE lt_object_table INTO ls_object_table WITH KEY tobjkey = '/&'. "#EC CI_SUBRC
        ENDIF.
        IF ls_object_table IS INITIAL.
          zcx_abapgit_exception=>raise( |Object { ms_item-obj_type } has got no defined primary table| ).
        ENDIF.
    
        rv_table = ls_object_table-tobj_name.
    
      ENDMETHOD.
    
      METHOD get_where_clause.
    
        DATA: lv_objkey_pos      TYPE i,
              lv_next_objkey_pos TYPE i,
              lv_value_pos       TYPE i,
              lv_objkey_length   TYPE i,
              lt_objkey          TYPE ty_t_objkey,
              ls_objkey          LIKE LINE OF lt_objkey,
              lv_non_value_pos   TYPE numc3,
              lt_key_fields      TYPE ddfields.
    
        DATA: lv_is_asterix      TYPE abap_bool,
              lv_where_statement TYPE string,
              lv_key_pos         TYPE i,
              lv_value128        TYPE string.
    
        FIELD-SYMBOLS  LIKE LINE OF mt_object_table.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_key_fields.
    
        READ TABLE mt_object_table ASSIGNING  WITH KEY tobj_name = iv_tobj_name.
        ASSERT sy-subrc = 0.
    
        lt_key_fields = get_key_fields( iv_tobj_name ).
    
    *   analyze the object key and compose the key (table)
        CLEAR lt_objkey.
        CLEAR ls_objkey.
        lv_objkey_pos = 0.
        lv_non_value_pos = 1.
        lv_value_pos = 0.
        lv_objkey_length = strlen( -tobjkey ).
    
        WHILE lv_objkey_pos <= lv_objkey_length.
          ls_objkey-num = lv_non_value_pos.
    *     command
          IF -tobjkey+lv_objkey_pos(1) = '/'.
            IF NOT ls_objkey-value IS INITIAL.
    *        We reached the end of a key-definition.
    *        this key part may address multiple fields.
    *        E. g. six characters may address one boolean field and a five-digit version field.
    *        Thus, we need to analyze the remaining key components which have not been covered yet.
              split_value_to_keys(
                EXPORTING
                  it_key_component = lt_key_fields
                CHANGING
                  ct_objkey        = lt_objkey
                  cs_objkey        = ls_objkey
                  cv_non_value_pos = lv_non_value_pos ).
            ENDIF.
            lv_next_objkey_pos = lv_objkey_pos + 1.
    *       '*' means all further key values
            IF -tobjkey+lv_next_objkey_pos(1) = '*'.
              ls_objkey-value = '*'.
              INSERT ls_objkey INTO TABLE lt_objkey.
              CLEAR ls_objkey.
              lv_non_value_pos = lv_non_value_pos + 1.
              lv_objkey_pos = lv_objkey_pos + 1.
    *       object name
            ELSEIF -tobjkey+lv_next_objkey_pos(1) = '&'.
              ls_objkey-value = ms_item-obj_name.
    *    The object name might comprise multiple key components (e. g. WDCC)
    *    This string needs to be split
              distribute_name_to_components(
                EXPORTING
                  it_key_component = lt_key_fields
                CHANGING
                  ct_objkey        = lt_objkey
                  cs_objkey        = ls_objkey
                  cv_non_value_pos = lv_non_value_pos ).
              CLEAR ls_objkey.
              lv_objkey_pos = lv_objkey_pos + 1.
    *       language
            ELSEIF -tobjkey+lv_next_objkey_pos(1) = 'L'.
              ls_objkey-value = mv_language.
              INSERT ls_objkey INTO TABLE lt_objkey.
              CLEAR ls_objkey.
              lv_non_value_pos = lv_non_value_pos + 1.
              lv_objkey_pos = lv_objkey_pos + 1.
    *       Client
            ELSEIF -tobjkey+lv_next_objkey_pos(1) = 'C'.
              ls_objkey-value = sy-mandt.
              INSERT ls_objkey INTO TABLE lt_objkey.
              CLEAR ls_objkey.
              lv_non_value_pos = lv_non_value_pos + 1.
              lv_objkey_pos = lv_objkey_pos + 1.
            ENDIF.
            lv_value_pos = 0.
    *     value
          ELSE.
            ls_objkey-value+lv_value_pos(1) = -tobjkey+lv_objkey_pos(1).
            lv_value_pos = lv_value_pos + 1.
          ENDIF.
    
          lv_objkey_pos = lv_objkey_pos + 1.
        ENDWHILE.
    
    *    Similarly to that, fixed values might be supplied in the object key which actually make up key components
        IF NOT ls_objkey-value IS INITIAL.
          split_value_to_keys(
            EXPORTING
              it_key_component = lt_key_fields
            CHANGING
              ct_objkey        = lt_objkey
              cs_objkey        = ls_objkey
              cv_non_value_pos = lv_non_value_pos ).
        ENDIF.
    
    *   compose the where clause
        lv_is_asterix = abap_false.
        lv_key_pos = 1.
    
        LOOP AT lt_key_fields ASSIGNING .
          READ TABLE lt_objkey INTO ls_objkey
            WITH TABLE KEY num = lv_key_pos.
          IF sy-subrc <> 0 OR -fieldname = 'LANGU'.
            CLEAR ls_objkey.
            lv_key_pos = lv_key_pos + 1.
            CONTINUE.
          ENDIF.
          IF ls_objkey-value = '*'.
            lv_is_asterix = abap_true.
          ENDIF.
          IF lv_is_asterix = abap_true.
            CONTINUE.
          ENDIF.
          IF NOT lv_where_statement IS INITIAL.
            CONCATENATE lv_where_statement 'AND' INTO lv_where_statement
              SEPARATED BY space.
          ENDIF.
          lv_value128 = cl_abap_dyn_prg=>quote( ls_objkey-value ).
          CONCATENATE lv_where_statement -fieldname '='
            lv_value128 INTO lv_where_statement SEPARATED BY space.
          lv_key_pos = lv_key_pos + 1.
        ENDLOOP.
    
        rv_where = condense( lv_where_statement ).
    
      ENDMETHOD.
    
      METHOD serialize.
    
        before_export( ).
    
        serialize_data( io_xml ).
    
      ENDMETHOD.
    
      METHOD serialize_data.
    
        DATA: lr_ref   TYPE REF TO data,
              lv_where TYPE string.
    
        FIELD-SYMBOLS:          TYPE STANDARD TABLE,
                        LIKE LINE OF mt_object_table.
    
        LOOP AT mt_object_table ASSIGNING .
    
          CREATE DATA lr_ref TYPE STANDARD TABLE OF (-tobj_name).
          ASSIGN lr_ref->* TO .
    
          lv_where = get_where_clause( -tobj_name ).
    
          SELECT * FROM (-tobj_name)
            INTO TABLE 
            WHERE (lv_where)
            ORDER BY PRIMARY KEY.
    
          apply_clear_logic( EXPORTING iv_table = -tobj_name
                             CHANGING  ct_data  =  ).
    
          io_xml->add(
            iv_name = -tobj_name
            ig_data =  ).
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD split_value_to_keys.
    
        DATA: lt_key_component_uncovered LIKE it_key_component,
              ls_dummy                   LIKE LINE OF ct_objkey,
              ls_key_component_uncovered LIKE LINE OF lt_key_component_uncovered,
              ls_objkey_sub              LIKE cs_objkey,
              lv_objkey_sub_pos          TYPE i.
    
        lt_key_component_uncovered = it_key_component.
    
    *    we want to fill the attribute values which are not covered by explicit key components yet
        LOOP AT ct_objkey INTO ls_dummy.
          DELETE lt_key_component_uncovered INDEX 1.
        ENDLOOP.
    
        ls_objkey_sub-num = cs_objkey-num.
        lv_objkey_sub_pos = 0.
        LOOP AT lt_key_component_uncovered INTO ls_key_component_uncovered.
          CLEAR ls_objkey_sub-value.
          ls_objkey_sub-value = cs_objkey-value+lv_objkey_sub_pos(ls_key_component_uncovered-leng).
          ls_objkey_sub-num = cv_non_value_pos.
    
          INSERT ls_objkey_sub INTO TABLE ct_objkey.
    
          lv_objkey_sub_pos = lv_objkey_sub_pos + ls_key_component_uncovered-leng.
          cv_non_value_pos = cv_non_value_pos + 1.
          CLEAR ls_objkey_sub.
    
          IF lv_objkey_sub_pos = strlen( cs_objkey-value ).
            cs_objkey-num = cv_non_value_pos.
            EXIT. "end splitting - all characters captured
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD validate.
    
        DATA: lv_where   TYPE string,
              lv_primary TYPE objsl-tobj_name,
              lr_ref     TYPE REF TO data.
    
        FIELD-SYMBOLS:  TYPE STANDARD TABLE.
    
        lv_primary = get_primary_table( ).
    
        CREATE DATA lr_ref TYPE STANDARD TABLE OF (lv_primary).
        ASSIGN lr_ref->* TO .
    
        io_xml->read(
          EXPORTING
            iv_name = lv_primary
          CHANGING
            cg_data =  ).
    
        IF lines(  ) = 0.
          zcx_abapgit_exception=>raise( |Primary table { lv_primary } not found in imported container| ).
        ELSEIF lines(  ) <> 1.
          zcx_abapgit_exception=>raise( |Primary table { lv_primary } contains more than one instance!| ).
        ENDIF.
    
        lv_where = get_where_clause( lv_primary ).
    
    *  validate that max one local instance was affected by the import
        SELECT COUNT(*) FROM (lv_primary) WHERE (lv_where).
        IF sy-dbcnt > 1.
          zcx_abapgit_exception=>raise( |More than one instance exists locally in primary table { lv_primary }| ).
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_objects_injector IMPLEMENTATION.
    
      METHOD set_gui_jumper.
    
        zcl_abapgit_objects_factory=>gi_gui_jumper = ii_gui_jumper.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_objects_super IMPLEMENTATION.
    
      METHOD clear_abap_language_version.
    
        " Used during serializing of objects
        IF ms_item-abap_language_version = zcl_abapgit_abap_language_vers=>c_no_abap_language_version.
          " Ignore ABAP language version
          CLEAR cv_abap_language_version.
        ELSEIF ms_item-abap_language_version <> zcl_abapgit_abap_language_vers=>c_any_abap_language_version.
          " Check if ABAP language version matches repository setting
          zcl_abapgit_abap_language_vers=>check_abap_language_version(
            iv_abap_language_version = cv_abap_language_version
            is_item                  = ms_item ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD constructor.
        ms_item = is_item.
        ASSERT NOT ms_item IS INITIAL.
        mv_language = iv_language.
        ASSERT NOT mv_language IS INITIAL.
    
        IF io_files IS NOT INITIAL.
          mo_files = io_files.
        ELSE.
          mo_files = zcl_abapgit_objects_files=>new( is_item ). " New file collection
        ENDIF.
    
        IF io_i18n_params IS NOT INITIAL.
          mo_i18n_params = io_i18n_params.
        ELSE.
          mo_i18n_params = zcl_abapgit_i18n_params=>new( ). " All defaults
        ENDIF.
    
      ENDMETHOD.
    
      METHOD corr_insert.
    
        DATA: lv_object       TYPE trobj_name,
              lv_object_class TYPE tadir-object.
    
        IF ig_object_class IS NOT INITIAL.
          lv_object_class = ig_object_class.
          IF ig_object_class = 'DICT'.
            CONCATENATE ms_item-obj_type ms_item-obj_name INTO lv_object.
          ELSE.
            lv_object = ms_item-obj_name.
          ENDIF.
        ELSE.
          lv_object_class = ms_item-obj_type.
          lv_object       = ms_item-obj_name.
        ENDIF.
    
        zcl_abapgit_factory=>get_cts_api( )->insert_transport_object(
          iv_object   = lv_object_class
          iv_obj_name = lv_object
          iv_package  = iv_package
          iv_language = mv_language ).
    
      ENDMETHOD.
    
      METHOD delete_ddic.
    
        DATA: lv_objname TYPE rsedd0-ddobjname,
              lv_objtype TYPE rsedd0-ddobjtype.
    
        lv_objname = ms_item-obj_name.
        lv_objtype = iv_objtype.
    
        TRY.
            CALL FUNCTION 'RS_DD_DELETE_OBJ'
              EXPORTING
                no_ask               = iv_no_ask
                objname              = lv_objname
                objtype              = lv_objtype
                no_ask_delete_append = iv_no_ask_delete_append
              EXCEPTIONS
                not_executed         = 1
                object_not_found     = 2
                object_not_specified = 3
                permission_failure   = 4
                dialog_needed        = 5
                OTHERS               = 6 ##FM_SUBRC_OK.
          CATCH cx_sy_dyn_call_param_not_found.
            TRY.
                " try to force deletion for APPENDs
                CALL FUNCTION 'RS_DD_DELETE_OBJ'
                  EXPORTING
                    no_ask               = iv_no_ask
                    objname              = lv_objname
                    objtype              = lv_objtype
                    aie_force_deletion   = iv_no_ask_delete_append
                  EXCEPTIONS
                    not_executed         = 1
                    object_not_found     = 2
                    object_not_specified = 3
                    permission_failure   = 4
                    dialog_needed        = 5
                    OTHERS               = 6 ##FM_SUBRC_OK.
              CATCH cx_sy_dyn_call_param_not_found.
                " no_ask_delete_append and aie_force_deletion not available in lower releases
                CALL FUNCTION 'RS_DD_DELETE_OBJ'
                  EXPORTING
                    no_ask               = iv_no_ask
                    objname              = lv_objname
                    objtype              = lv_objtype
                  EXCEPTIONS
                    not_executed         = 1
                    object_not_found     = 2
                    object_not_specified = 3
                    permission_failure   = 4
                    dialog_needed        = 5
                    OTHERS               = 6 ##FM_SUBRC_OK.
            ENDTRY.
        ENDTRY.
    
        IF sy-subrc = 5.
          zcx_abapgit_exception=>raise( |Object { ms_item-obj_type } { ms_item-obj_name
                                        } has dependencies and must be deleted manually| ).
        ELSEIF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error deleting { ms_item-obj_type } { ms_item-obj_name }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD delete_longtexts.
    
        zcl_abapgit_factory=>get_longtexts( )->delete(
          iv_longtext_id = iv_longtext_id
          iv_object_name = ms_item-obj_name ).
    
      ENDMETHOD.
    
      METHOD deserialize_longtexts.
    
        zcl_abapgit_factory=>get_longtexts( )->deserialize(
          ii_xml           = ii_xml
          iv_longtext_name = iv_longtext_name
          iv_object_name   = ms_item-obj_name
          iv_longtext_id   = iv_longtext_id
          iv_main_language = mv_language ).
    
      ENDMETHOD.
    
      METHOD exists_a_lock_entry_for.
    
        DATA: lt_lock_entries TYPE STANDARD TABLE OF seqg3.
        DATA: lv_argument TYPE seqg3-garg.
    
        IF iv_prefix IS INITIAL.
          lv_argument = iv_argument.
        ELSE.
          lv_argument = |{ iv_prefix  }{ iv_argument }|.
          OVERLAY lv_argument WITH '                                          '.
          lv_argument = lv_argument && '*'.
        ENDIF.
    
        CALL FUNCTION 'ENQUEUE_READ'
          EXPORTING
            guname                = '*'
            garg                  = lv_argument
          TABLES
            enq                   = lt_lock_entries
          EXCEPTIONS
            communication_failure = 1
            system_failure        = 2
            OTHERS                = 3.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        READ TABLE lt_lock_entries TRANSPORTING NO FIELDS
                                   WITH KEY gobj = iv_lock_object.
        IF sy-subrc = 0.
          rv_exists_a_lock_entry = abap_true.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_abap_language_version.
    
        " This is limited to DDIC objects
        TRY.
            CALL METHOD ('CL_DD_ABAP_LANGUAGE_VERSION')=>get_abap_language_version
              EXPORTING
                iv_object_type           = ms_item-obj_type
                iv_object_name           = ms_item-obj_name
              RECEIVING
                rv_abap_language_version = rv_abap_language_version.
          CATCH cx_root.
            " does not exist in lower releases
            RETURN.
        ENDTRY.
    
        clear_abap_language_version( CHANGING cv_abap_language_version = rv_abap_language_version ).
    
      ENDMETHOD.
    
      METHOD get_accessed_files.
        rt_files = mo_files->get_accessed_files( ).
      ENDMETHOD.
    
      METHOD get_metadata.
    
        DATA: lv_class TYPE string.
    
        lv_class = cl_abap_classdescr=>describe_by_object_ref( me )->get_relative_name( ).
    
        REPLACE FIRST OCCURRENCE OF 'ZCL_ABAPGIT' IN lv_class WITH 'LCL'.
    
        rs_metadata-class = lv_class.
        rs_metadata-version = 'v1.0.0'.
    
      ENDMETHOD.
    
      METHOD is_active.
    
        rv_active = zcl_abapgit_objects_activation=>is_active( ms_item ).
    
      ENDMETHOD.
    
      METHOD serialize_longtexts.
    
        zcl_abapgit_factory=>get_longtexts( )->serialize(
          iv_object_name   = ms_item-obj_name
          iv_longtext_name = iv_longtext_name
          iv_longtext_id   = iv_longtext_id
          it_dokil         = it_dokil
          io_i18n_params   = mo_i18n_params
          ii_xml           = ii_xml ).
    
      ENDMETHOD.
    
      METHOD set_abap_language_version.
    
        " Used during deserializing of objects
        IF ms_item-abap_language_version = zcl_abapgit_abap_language_vers=>c_no_abap_language_version.
          " ABAP language version is derived from object type and target package (see zcl_abapgit_objects->deserialize)
          cv_abap_language_version = ms_item-abap_language_version.
        ELSEIF ms_item-abap_language_version <> zcl_abapgit_abap_language_vers=>c_any_abap_language_version.
          " Check if ABAP language version matches repository setting
          zcl_abapgit_abap_language_vers=>check_abap_language_version(
            iv_abap_language_version = cv_abap_language_version
            is_item                  = ms_item ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD set_default_package.
    
        " In certain cases we need to set the package via ABAP memory
        " because we can't supply it via the APIs.
        "
        " Set default package, see function module RS_CORR_INSERT FORM get_current_devclass.
        "
        " We use ABAP memory instead the SET parameter because it is
        " more reliable. SET parameter doesn't work when multiple objects
        " are deserialized which uses the ABAP memory mechanism.
        " We don't need to reset the memory as it is done in above mentioned form routine.
    
        EXPORT current_devclass FROM iv_package TO MEMORY ID 'EUK'.
    
      ENDMETHOD.
    
      METHOD set_default_transport.
    
        " In certain cases we need to set the transport via ABAP memory
        " because we can't supply it via the APIs.
        "
        " See function module RS_CORR_INSERT
    
        EXPORT tasknr FROM iv_transport TO MEMORY ID 'EUT'.
    
      ENDMETHOD.
    
      METHOD tadir_delete.
    
        zcl_abapgit_factory=>get_tadir( )->delete_single(
          iv_object   = ms_item-obj_type
          iv_obj_name = ms_item-obj_name ).
    
      ENDMETHOD.
    
      METHOD tadir_insert.
    
        zcl_abapgit_factory=>get_tadir( )->insert_single(
          iv_object   = ms_item-obj_type
          iv_obj_name = ms_item-obj_name
          iv_package  = iv_package
          iv_language = mv_language ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_objects_program IMPLEMENTATION.
    
      METHOD add_tpool.
    
        FIELD-SYMBOLS:   LIKE LINE OF it_tpool,
                        LIKE LINE OF rt_tpool.
    
        LOOP AT it_tpool ASSIGNING .
          APPEND INITIAL LINE TO rt_tpool ASSIGNING .
          MOVE-CORRESPONDING  TO .
          IF -id = 'S'.
            -split = -entry.
            -entry = -entry+8.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD auto_correct_cua_adm.
        " issue #1807 automatic correction of CUA interfaces saved incorrectly in the past (ADM was not saved in the XML)
    
        CONSTANTS:
          lc_num_n_space TYPE string VALUE ' 0123456789',
          lc_num_only    TYPE string VALUE '0123456789'.
    
        FIELD-SYMBOLS:
           TYPE rsmpe_pfk,
           TYPE rsmpe_act,
           TYPE rsmpe_men.
    
        IF cs_adm IS NOT INITIAL
            AND cs_adm-actcode CO lc_num_n_space
            AND cs_adm-mencode CO lc_num_n_space
            AND cs_adm-pfkcode CO lc_num_n_space. "Check performed in form check_adm of include LSMPIF03
          RETURN.
        ENDIF.
    
        LOOP AT is_cua-act ASSIGNING .
          IF -code+6(14) IS INITIAL AND -code(6) CO lc_num_only.
            cs_adm-actcode = -code.
          ENDIF.
        ENDLOOP.
    
        LOOP AT is_cua-men ASSIGNING .
          IF -code+6(14) IS INITIAL AND -code(6) CO lc_num_only.
            cs_adm-mencode = -code.
          ENDIF.
        ENDLOOP.
    
        LOOP AT is_cua-pfk ASSIGNING .
          IF -code+6(14) IS INITIAL AND -code(6) CO lc_num_only.
            cs_adm-pfkcode = -code.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD deserialize_cua.
    
        DATA: ls_tr_key TYPE trkey,
              ls_adm    TYPE rsmpe_adm.
    
        IF lines( is_cua-sta ) = 0
            AND lines( is_cua-fun ) = 0
            AND lines( is_cua-men ) = 0
            AND lines( is_cua-mtx ) = 0
            AND lines( is_cua-act ) = 0
            AND lines( is_cua-but ) = 0
            AND lines( is_cua-pfk ) = 0
            AND lines( is_cua-set ) = 0
            AND lines( is_cua-doc ) = 0
            AND lines( is_cua-tit ) = 0
            AND lines( is_cua-biv ) = 0.
          RETURN.
        ENDIF.
    
        SELECT SINGLE devclass INTO ls_tr_key-devclass
          FROM tadir
          WHERE pgmid = 'R3TR'
          AND object = ms_item-obj_type
          AND obj_name = ms_item-obj_name.                  "#EC CI_GENBUFF
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'not found in tadir' ).
        ENDIF.
    
        ls_tr_key-obj_type = ms_item-obj_type.
        ls_tr_key-obj_name = ms_item-obj_name.
        ls_tr_key-sub_type = 'CUAD'.
        ls_tr_key-sub_name = iv_program_name.
    
        ls_adm = is_cua-adm.
        auto_correct_cua_adm( EXPORTING is_cua = is_cua CHANGING cs_adm = ls_adm ).
    
        sy-tcode = 'SE41' ##WRITE_OK. " evil hack, workaround to handle fixes in note 2159455
        CALL FUNCTION 'RS_CUA_INTERNAL_WRITE'
          EXPORTING
            program   = iv_program_name
            language  = mv_language
            tr_key    = ls_tr_key
            adm       = ls_adm
            state     = c_state-inactive
          TABLES
            sta       = is_cua-sta
            fun       = is_cua-fun
            men       = is_cua-men
            mtx       = is_cua-mtx
            act       = is_cua-act
            but       = is_cua-but
            pfk       = is_cua-pfk
            set       = is_cua-set
            doc       = is_cua-doc
            tit       = is_cua-tit
            biv       = is_cua-biv
          EXCEPTIONS
            not_found = 1
            OTHERS    = 2.
        IF sy-subrc <> 0.
    * if moving code from SAPlink, see https://github.com/abapGit/abapGit/issues/562
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        zcl_abapgit_objects_activation=>add(
          iv_type = 'CUAD'
          iv_name = iv_program_name ).
    
      ENDMETHOD.
    
      METHOD deserialize_dynpros.
    
        CONSTANTS lc_rpyty_force_off TYPE c LENGTH 1 VALUE '/'.
    
        DATA: lv_name            TYPE dwinactiv-obj_name,
              lt_d020s_to_delete TYPE TABLE OF d020s,
              ls_d020s           LIKE LINE OF lt_d020s_to_delete,
              lt_params          TYPE TABLE OF d023s,
              ls_dynpro          LIKE LINE OF it_dynpros.
    
        FIELD-SYMBOLS:  TYPE rpy_dyfatc.
    
        " Delete DYNPROs which are not in the list
        CALL FUNCTION 'RS_SCREEN_LIST'
          EXPORTING
            dynnr     = ''
            progname  = ms_item-obj_name
          TABLES
            dynpros   = lt_d020s_to_delete
          EXCEPTIONS
            not_found = 1
            OTHERS    = 2.
        IF sy-subrc = 2.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        SORT lt_d020s_to_delete BY dnum ASCENDING.
    
    * ls_dynpro is changed by the function module, a field-symbol will cause
    * the program to dump since it_dynpros cannot be changed
        LOOP AT it_dynpros INTO ls_dynpro.
    
          READ TABLE lt_d020s_to_delete WITH KEY dnum = ls_dynpro-header-screen
            TRANSPORTING NO FIELDS
            BINARY SEARCH.
          IF sy-subrc = 0.
            DELETE lt_d020s_to_delete INDEX sy-tabix.
          ENDIF.
    
          " todo: kept for compatibility, remove after grace period #3680
          ls_dynpro-flow_logic = uncondense_flow(
            it_flow = ls_dynpro-flow_logic
            it_spaces = ls_dynpro-spaces ).
    
          IF ls_dynpro-flow_logic IS INITIAL.
            ls_dynpro-flow_logic = mo_files->read_abap( iv_extra = 'screen_' && ls_dynpro-header-screen ).
          ENDIF.
    
          LOOP AT ls_dynpro-fields ASSIGNING .
    * if the DDIC element has a PARAMETER_ID and the flag "from_dict" is active
    * the import will enable the SET-/GET_PARAM flag. In this case: "force off"
            IF -param_id IS NOT INITIAL
                AND -from_dict = abap_true.
              IF -set_param IS INITIAL.
                -set_param = lc_rpyty_force_off.
              ENDIF.
              IF -get_param IS INITIAL.
                -get_param = lc_rpyty_force_off.
              ENDIF.
            ENDIF.
    
    * If the previous conditions are met the value 'F' will be taken over
    * during de-serialization potentially overlapping other fields in the screen,
    * we set the tag to the correct value 'X'
            IF -type = 'CHECK'
                AND -from_dict = abap_true
                AND -text IS INITIAL
                AND -modific IS INITIAL.
              -modific = 'X'.
            ENDIF.
    
            "fix for issue #2747:
            IF -foreignkey IS INITIAL.
              -foreignkey = lc_rpyty_force_off.
            ENDIF.
    
          ENDLOOP.
    
          IF ls_dynpro-header-type CA c_native_dynpro AND ls_dynpro-nat_header IS NOT INITIAL.
            DELETE FROM d021t WHERE prog = ls_dynpro-header-program AND dynr = ls_dynpro-header-screen ##SUBRC_OK.
            INSERT d021t FROM TABLE ls_dynpro-nat_texts ##SUBRC_OK.
    
            ls_dynpro-nat_header-dgen = sy-datum.
            ls_dynpro-nat_header-tgen = sy-uzeit.
    
            CALL FUNCTION 'RPY_DYNPRO_INSERT_NATIVE'
              EXPORTING
                header             = ls_dynpro-nat_header
                dynprotext         = ls_dynpro-header-descript
              TABLES
                fieldlist          = ls_dynpro-nat_fields
                flowlogic          = ls_dynpro-flow_logic
                params             = lt_params
              EXCEPTIONS
                cancelled          = 1
                already_exists     = 2
                program_not_exists = 3
                not_executed       = 4
                OTHERS             = 5.
          ELSE.
            CALL FUNCTION 'RPY_DYNPRO_INSERT'
              EXPORTING
                header                 = ls_dynpro-header
                suppress_exist_checks  = abap_true
                suppress_generate      = ls_dynpro-header-no_execute
              TABLES
                containers             = ls_dynpro-containers
                fields_to_containers   = ls_dynpro-fields
                flow_logic             = ls_dynpro-flow_logic
              EXCEPTIONS
                cancelled              = 1
                already_exists         = 2
                program_not_exists     = 3
                not_executed           = 4
                missing_required_field = 5
                illegal_field_value    = 6
                field_not_allowed      = 7
                not_generated          = 8
                illegal_field_position = 9
                OTHERS                 = 10.
          ENDIF.
          IF sy-subrc <> 2 AND sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          CONCATENATE ls_dynpro-header-program ls_dynpro-header-screen
            INTO lv_name RESPECTING BLANKS.
          ASSERT NOT lv_name IS INITIAL.
    
          zcl_abapgit_objects_activation=>add(
            iv_type = 'DYNP'
            iv_name = lv_name ).
    
        ENDLOOP.
    
        " Delete obsolete screens
        LOOP AT lt_d020s_to_delete INTO ls_d020s.
    
          CALL FUNCTION 'RS_SCRP_DELETE'
            EXPORTING
              dynnr                  = ls_d020s-dnum
              progname               = ms_item-obj_name
              with_popup             = abap_false
            EXCEPTIONS
              enqueued_by_user       = 1
              enqueue_system_failure = 2
              not_executed           = 3
              not_exists             = 4
              no_modify_permission   = 5
              popup_canceled         = 6.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD deserialize_exit_include.
    
        DATA:
          lv_progname TYPE reposrc-progname,
          lv_title    TYPE rglif-title.
    
        " Includes in SAP exit function groups must be processed in active state only
        " (check in RS_INSERT_INTO_WORKING_AREA)
        lv_title = get_program_title( it_tpool ).
    
        SELECT SINGLE progname FROM reposrc INTO lv_progname
          WHERE progname = is_progdir-name
          AND r3state = c_state-active.
    
        IF sy-subrc = 0.
          update_program(
            is_progdir = is_progdir
            it_source  = it_source
            iv_title   = lv_title
            iv_state   = '' ).
        ELSE.
          insert_program(
            is_progdir = is_progdir
            it_source  = it_source
            iv_title   = lv_title
            iv_package = iv_package
            iv_state   = '' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD deserialize_program.
    
        DATA:
          lv_progname TYPE reposrc-progname,
          lv_title    TYPE rglif-title.
    
        IF is_exit_include( is_progdir-name ) = abap_true.
          deserialize_exit_include(
            is_progdir = is_progdir
            it_source  = it_source
            it_tpool   = it_tpool
            iv_package = iv_package ).
          RETURN.
        ENDIF.
    
        zcl_abapgit_factory=>get_cts_api( )->insert_transport_object(
          iv_object   = 'ABAP'
          iv_obj_name = is_progdir-name
          iv_package  = iv_package
          iv_language = mv_language ).
    
        lv_title = get_program_title( it_tpool ).
    
        " Check if program already exists
        SELECT SINGLE progname FROM reposrc INTO lv_progname
          WHERE progname = is_progdir-name
          AND r3state = c_state-active.
    
        IF sy-subrc = 0.
          update_program(
            is_progdir = is_progdir
            it_source  = it_source
            iv_title   = lv_title ).
        ELSE.
          insert_program(
            is_progdir = is_progdir
            it_source  = it_source
            iv_title   = lv_title
            iv_package = iv_package ).
        ENDIF.
    
        zcl_abapgit_factory=>get_sap_report( )->update_progdir(
          is_progdir = is_progdir
          iv_package = iv_package ).
    
        zcl_abapgit_objects_activation=>add(
          iv_type = 'REPS'
          iv_name = is_progdir-name ).
    
      ENDMETHOD.
    
      METHOD deserialize_textpool.
    
        DATA lv_language TYPE sy-langu.
        DATA lv_state    TYPE c.
        DATA lv_delete   TYPE abap_bool.
    
        IF iv_language IS INITIAL.
          lv_language = mv_language.
        ELSE.
          lv_language = iv_language.
        ENDIF.
    
        IF lv_language = mv_language.
          lv_state = c_state-inactive. "Textpool in main language needs to be activated
        ELSE.
          lv_state = c_state-active. "Translations are always active
        ENDIF.
    
        IF it_tpool IS INITIAL.
          IF iv_is_include = abap_false OR lv_state = c_state-active.
            DELETE TEXTPOOL iv_program "Remove initial description from textpool if
              LANGUAGE lv_language     "original program does not have a textpool
              STATE lv_state.
    
            lv_delete = abap_true.
          ELSE.
            INSERT TEXTPOOL iv_program "In case of includes: Deletion of textpool in
              FROM it_tpool            "main language cannot be activated because
              LANGUAGE lv_language     "this would activate the deletion of the textpool
              STATE lv_state.          "of the mail program -> insert empty textpool
          ENDIF.
        ELSE.
          INSERT TEXTPOOL iv_program
            FROM it_tpool
            LANGUAGE lv_language
            STATE lv_state.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'error from INSERT TEXTPOOL' ).
          ENDIF.
        ENDIF.
    
        "Textpool in main language needs to be activated (not for FUGS/FUGX)
        IF lv_state = c_state-inactive AND iv_program NP 'SAPLX*'.
          zcl_abapgit_objects_activation=>add(
            iv_type   = 'REPT'
            iv_name   = iv_program
            iv_delete = lv_delete ).
        ENDIF.
      ENDMETHOD.
    
      METHOD get_program_title.
    
        DATA ls_tpool LIKE LINE OF it_tpool.
    
        FIELD-SYMBOLS  TYPE any.
    
        READ TABLE it_tpool INTO ls_tpool WITH KEY id = 'R'.
        IF sy-subrc = 0.
          " there is a bug in RPY_PROGRAM_UPDATE, the header line of TTAB is not
          " cleared, so the title length might be inherited from a different program.
          ASSIGN ('(SAPLSIFP)TTAB') TO .
          IF sy-subrc = 0.
            CLEAR .
          ENDIF.
    
          rv_title = ls_tpool-entry.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD insert_program.
    
        TRY.
            CALL FUNCTION 'RPY_PROGRAM_INSERT'
              EXPORTING
                development_class = iv_package
                program_name      = is_progdir-name
                program_type      = is_progdir-subc
                title_string      = iv_title
                save_inactive     = iv_state
                suppress_dialog   = abap_true
                uccheck           = is_progdir-uccheck " does not exist on lower releases
              TABLES
                source_extended   = it_source
              EXCEPTIONS
                already_exists    = 1
                cancelled         = 2
                name_not_allowed  = 3
                permission_error  = 4
                OTHERS            = 5 ##FM_SUBRC_OK.
          CATCH cx_sy_dyn_call_param_not_found.
            CALL FUNCTION 'RPY_PROGRAM_INSERT'
              EXPORTING
                development_class = iv_package
                program_name      = is_progdir-name
                program_type      = is_progdir-subc
                title_string      = iv_title
                save_inactive     = iv_state
                suppress_dialog   = abap_true
              TABLES
                source_extended   = it_source
              EXCEPTIONS
                already_exists    = 1
                cancelled         = 2
                name_not_allowed  = 3
                permission_error  = 4
                OTHERS            = 5 ##FM_SUBRC_OK.
        ENDTRY.
        IF sy-subrc = 3.
    
          " For cases that standard function does not handle (like FUGR),
          " we save active and inactive version of source with the given PROGRAM TYPE.
          " Without the active version, the code will not be visible in case of activation errors.
          zcl_abapgit_factory=>get_sap_report( )->insert_report(
            iv_name         = is_progdir-name
            iv_package      = iv_package
            it_source       = it_source
            iv_state        = c_state-active
            iv_version      = is_progdir-uccheck
            iv_program_type = is_progdir-subc ).
    
          zcl_abapgit_factory=>get_sap_report( )->insert_report(
            iv_name         = is_progdir-name
            iv_package      = iv_package
            it_source       = it_source
            iv_state        = c_state-inactive
            iv_version      = is_progdir-uccheck
            iv_program_type = is_progdir-subc ).
    
        ELSEIF sy-subrc > 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD is_any_dynpro_locked.
    
        DATA: lt_dynpros TYPE ty_dynpro_tt,
              lv_object  TYPE seqg3-garg.
    
        FIELD-SYMBOLS:  TYPE ty_dynpro.
    
        lt_dynpros = serialize_dynpros( iv_program ).
    
        LOOP AT lt_dynpros ASSIGNING .
    
          lv_object = |{ -header-screen }{ -header-program }|.
    
          IF exists_a_lock_entry_for( iv_lock_object = 'ESCRP'
                                      iv_argument    = lv_object ) = abap_true.
            rv_is_any_dynpro_locked = abap_true.
            EXIT.
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD is_cua_locked.
    
        DATA: lv_object TYPE eqegraarg.
    
        lv_object = |CU{ iv_program }|.
        OVERLAY lv_object WITH '                                          '.
        lv_object = lv_object && '*'.
    
        rv_is_cua_locked = exists_a_lock_entry_for( iv_lock_object = 'ESCUAPAINT'
                                                    iv_argument    = lv_object ).
    
      ENDMETHOD.
    
      METHOD is_exit_include.
        rv_is_exit_include = boolc(
          iv_program CP 'LX*' OR iv_program CP 'SAPLX*' OR
          iv_program+1 CP '/LX*' OR iv_program+1 CP '/SAPLX*' ).
      ENDMETHOD.
    
      METHOD is_text_locked.
    
        DATA: lv_object TYPE eqegraarg.
    
        lv_object = |*{ iv_program }|.
    
        rv_is_text_locked = exists_a_lock_entry_for( iv_lock_object = 'EABAPTEXTE'
                                                     iv_argument    = lv_object ).
    
      ENDMETHOD.
    
      METHOD read_tpool.
    
        FIELD-SYMBOLS:   LIKE LINE OF it_tpool,
                        LIKE LINE OF rt_tpool.
    
        LOOP AT it_tpool ASSIGNING .
          APPEND INITIAL LINE TO rt_tpool ASSIGNING .
          MOVE-CORRESPONDING  TO .
          IF -id = 'S'.
            CONCATENATE -split -entry
              INTO -entry
              RESPECTING BLANKS.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD serialize_cua.
    
        CALL FUNCTION 'RS_CUA_INTERNAL_FETCH'
          EXPORTING
            program         = iv_program_name
            language        = mv_language
            state           = c_state-active
          IMPORTING
            adm             = rs_cua-adm
          TABLES
            sta             = rs_cua-sta
            fun             = rs_cua-fun
            men             = rs_cua-men
            mtx             = rs_cua-mtx
            act             = rs_cua-act
            but             = rs_cua-but
            pfk             = rs_cua-pfk
            set             = rs_cua-set
            doc             = rs_cua-doc
            tit             = rs_cua-tit
            biv             = rs_cua-biv
          EXCEPTIONS
            not_found       = 1
            unknown_version = 2
            OTHERS          = 3.
        IF sy-subrc > 1.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD serialize_dynpros.
        DATA: ls_header               TYPE rpy_dyhead,
              lt_containers           TYPE dycatt_tab,
              lt_fields_to_containers TYPE dyfatc_tab,
              lt_flow_logic           TYPE swydyflow,
              lt_d020s                TYPE TABLE OF d020s,
              lt_texts                TYPE TABLE OF d021t,
              lt_fieldlist_int        TYPE TABLE OF d021s. "internal format
    
        FIELD-SYMBOLS:        LIKE LINE OF lt_d020s,
                        TYPE scrpostyle,
                          LIKE LINE OF lt_containers,
                              LIKE LINE OF lt_fields_to_containers,
                             LIKE LINE OF rt_dynpro,
                          LIKE LINE OF lt_fieldlist_int.
    
        "#2746: relevant flag values (taken from include MSEUSBIT)
        CONSTANTS: lc_flg1ddf TYPE x VALUE '20',
                   lc_flg3fku TYPE x VALUE '08',
                   lc_flg3for TYPE x VALUE '04',
                   lc_flg3fdu TYPE x VALUE '02'.
    
        CALL FUNCTION 'RS_SCREEN_LIST'
          EXPORTING
            dynnr     = ''
            progname  = iv_program_name
          TABLES
            dynpros   = lt_d020s
          EXCEPTIONS
            not_found = 1
            OTHERS    = 2.
        IF sy-subrc = 2.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        SORT lt_d020s BY dnum ASCENDING.
    
    * loop dynpros and skip generated selection screens
        LOOP AT lt_d020s ASSIGNING 
            WHERE type <> 'S' AND type <> 'W' AND type <> 'J'
            AND NOT dnum IS INITIAL.
    
          CALL FUNCTION 'RPY_DYNPRO_READ'
            EXPORTING
              progname             = iv_program_name
              dynnr                = -dnum
            IMPORTING
              header               = ls_header
            TABLES
              containers           = lt_containers
              fields_to_containers = lt_fields_to_containers
              flow_logic           = lt_flow_logic
            EXCEPTIONS
              cancelled            = 1
              not_found            = 2
              permission_error     = 3
              OTHERS               = 4.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          "#2746: we need the dynpro fields in internal format:
          FREE lt_fieldlist_int.
    
          CALL FUNCTION 'RPY_DYNPRO_READ_NATIVE'
            EXPORTING
              progname   = iv_program_name
              dynnr      = -dnum
            TABLES
              fieldlist  = lt_fieldlist_int
              fieldtexts = lt_texts.
    
          LOOP AT lt_fields_to_containers ASSIGNING .
    * output style is a NUMC field, the XML conversion will fail if it contains invalid value
    * field does not exist in all versions
            ASSIGN COMPONENT 'OUTPUTSTYLE' OF STRUCTURE  TO .
            IF sy-subrc = 0 AND  = '  '.
              CLEAR .
            ENDIF.
    
            "2746: we apply the same logic as in SAPLWBSCREEN
            "for setting or unsetting the foreignkey field:
            UNASSIGN .
            READ TABLE lt_fieldlist_int ASSIGNING  WITH KEY fnam = -name.
            IF  IS ASSIGNED.
              IF -flg1 O lc_flg1ddf AND
                  -flg3 O lc_flg3for AND
                  -flg3 Z lc_flg3fdu AND
                  -flg3 Z lc_flg3fku.
                -foreignkey = 'X'.
              ELSE.
                CLEAR -foreignkey.
              ENDIF.
            ENDIF.
    
            IF -from_dict = abap_true AND
               -modific   <> 'F' AND
               -modific   <> 'X'.
              CLEAR -text.
            ENDIF.
          ENDLOOP.
    
          LOOP AT lt_containers ASSIGNING .
            IF -c_resize_v = abap_false.
              CLEAR -c_line_min.
            ENDIF.
            IF -c_resize_h = abap_false.
              CLEAR -c_coln_min.
            ENDIF.
          ENDLOOP.
    
          APPEND INITIAL LINE TO rt_dynpro ASSIGNING .
          -header = ls_header.
    
          " Store flow logic as separate ABAP files instead of XML
          mo_files->add_abap(
            iv_extra = 'screen_' && ls_header-screen
            it_abap  = lt_flow_logic ).
    
          READ TABLE lt_fieldlist_int TRANSPORTING NO FIELDS WITH KEY fill = 'X'.
          IF ls_header-type CA c_native_dynpro AND sy-subrc = 0.
            " In particular for dynpros with splitter
            -nat_header = .
            CLEAR: -nat_header-dgen, -nat_header-tgen.
            -nat_fields = lt_fieldlist_int.
            -nat_texts  = lt_texts.
          ELSE.
            -containers = lt_containers.
            -fields     = lt_fields_to_containers.
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD serialize_program.
    
        DATA: ls_progdir      TYPE zif_abapgit_sap_report=>ty_progdir,
              lv_program_name TYPE syrepid,
              lt_dynpros      TYPE ty_dynpro_tt,
              ls_cua          TYPE ty_cua,
              li_report       TYPE REF TO zif_abapgit_sap_report,
              lt_source       TYPE TABLE OF abaptxt255,
              lt_tpool        TYPE textpool_table,
              ls_tpool        LIKE LINE OF lt_tpool,
              li_xml          TYPE REF TO zif_abapgit_xml_output.
    
        IF iv_program IS INITIAL.
          lv_program_name = is_item-obj_name.
        ELSE.
          lv_program_name = iv_program.
        ENDIF.
    
        zcl_abapgit_language=>set_current_language( mv_language ).
    
        CALL FUNCTION 'RPY_PROGRAM_READ'
          EXPORTING
            program_name     = lv_program_name
            with_includelist = abap_false
            with_lowercase   = abap_true
          TABLES
            source_extended  = lt_source
            textelements     = lt_tpool
          EXCEPTIONS
            cancelled        = 1
            not_found        = 2
            permission_error = 3
            OTHERS           = 4.
    
        IF sy-subrc = 2.
          zcl_abapgit_language=>restore_login_language( ).
          RETURN.
        ELSEIF sy-subrc <> 0.
          zcl_abapgit_language=>restore_login_language( ).
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        zcl_abapgit_language=>restore_login_language( ).
    
        " If inactive version exists, then RPY_PROGRAM_READ does not return the active code
        li_report = zcl_abapgit_factory=>get_sap_report( ).
    
        TRY.
            " Raises exception if inactive version does not exist
            ls_progdir = li_report->read_progdir(
              iv_name  = lv_program_name
              iv_state = c_state-inactive ).
    
            " Explicitly request active source code
            lt_source = li_report->read_report(
              iv_name  = lv_program_name
              iv_state = c_state-active ).
          CATCH zcx_abapgit_exception ##NO_HANDLER.
        ENDTRY.
    
        ls_progdir = li_report->read_progdir(
          iv_name  = lv_program_name
          iv_state = c_state-active ).
    
        clear_abap_language_version( CHANGING cv_abap_language_version = ls_progdir-uccheck ).
    
        IF io_xml IS BOUND.
          li_xml = io_xml.
        ELSE.
          CREATE OBJECT li_xml TYPE zcl_abapgit_xml_output.
        ENDIF.
    
        li_xml->add( iv_name = 'PROGDIR'
                     ig_data = ls_progdir ).
        IF ls_progdir-subc = '1' OR ls_progdir-subc = 'M'.
          lt_dynpros = serialize_dynpros( lv_program_name ).
          li_xml->add( iv_name = 'DYNPROS'
                       ig_data = lt_dynpros ).
    
          ls_cua = serialize_cua( lv_program_name ).
          IF NOT ls_cua IS INITIAL.
            li_xml->add( iv_name = 'CUA'
                         ig_data = ls_cua ).
          ENDIF.
        ENDIF.
    
        READ TABLE lt_tpool WITH KEY id = 'R' INTO ls_tpool.
        IF sy-subrc = 0 AND ls_tpool-key = '' AND ls_tpool-length = 0.
          DELETE lt_tpool INDEX sy-tabix.
        ENDIF.
    
        li_xml->add( iv_name = 'TPOOL'
                     ig_data = add_tpool( lt_tpool ) ).
    
        IF NOT io_xml IS BOUND.
          io_files->add_xml( iv_extra = iv_extra
                             ii_xml   = li_xml ).
        ENDIF.
    
        strip_generation_comments( CHANGING ct_source = lt_source ).
    
        io_files->add_abap( iv_extra = iv_extra
                            it_abap  = lt_source ).
    
      ENDMETHOD.
    
      METHOD strip_generation_comments.
    
        FIELD-SYMBOLS  TYPE any. " Assuming CHAR (e.g. abaptxt255_tab) or string (FUGR)
    
        IF ms_item-obj_type <> 'FUGR'.
          RETURN.
        ENDIF.
    
        " Case 1: MV FM main prog and TOPs
        READ TABLE ct_source INDEX 1 ASSIGNING .
        IF sy-subrc = 0 AND  CP '#**regenerated at *'.
          DELETE ct_source INDEX 1.
          RETURN.
        ENDIF.
    
        " Case 2: MV FM includes
        IF lines( ct_source ) < 5. " Generation header length
          RETURN.
        ENDIF.
    
        READ TABLE ct_source INDEX 1 ASSIGNING .
        ASSERT sy-subrc = 0.
        IF NOT  CP '#*---*'.
          RETURN.
        ENDIF.
    
        READ TABLE ct_source INDEX 2 ASSIGNING .
        ASSERT sy-subrc = 0.
        IF NOT  CP '#**'.
          RETURN.
        ENDIF.
    
        READ TABLE ct_source INDEX 3 ASSIGNING .
        ASSERT sy-subrc = 0.
        IF NOT  CP '#**generation date:*'.
          RETURN.
        ENDIF.
    
        READ TABLE ct_source INDEX 4 ASSIGNING .
        ASSERT sy-subrc = 0.
        IF NOT  CP '#**generator version:*'.
          RETURN.
        ENDIF.
    
        READ TABLE ct_source INDEX 5 ASSIGNING .
        ASSERT sy-subrc = 0.
        IF NOT  CP '#*---*'.
          RETURN.
        ENDIF.
    
        DELETE ct_source INDEX 4.
        DELETE ct_source INDEX 3.
    
      ENDMETHOD.
    
      METHOD uncondense_flow.
    
        DATA: lv_spaces LIKE LINE OF it_spaces.
    
        FIELD-SYMBOLS:    LIKE LINE OF it_flow,
                        LIKE LINE OF rt_flow.
    
        LOOP AT it_flow ASSIGNING .
          APPEND INITIAL LINE TO rt_flow ASSIGNING .
          -line = -line.
    
          READ TABLE it_spaces INDEX sy-tabix INTO lv_spaces.
          IF sy-subrc = 0.
            SHIFT -line RIGHT BY lv_spaces PLACES IN CHARACTER MODE.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD update_program.
    
        zcl_abapgit_language=>set_current_language( mv_language ).
    
        CALL FUNCTION 'RPY_PROGRAM_UPDATE'
          EXPORTING
            program_name     = is_progdir-name
            title_string     = iv_title
            save_inactive    = iv_state
          TABLES
            source_extended  = it_source
          EXCEPTIONS
            cancelled        = 1
            permission_error = 2
            not_found        = 3
            OTHERS           = 4.
    
        IF sy-subrc <> 0.
          zcl_abapgit_language=>restore_login_language( ).
    
          IF sy-msgid = 'EU' AND sy-msgno = '510'.
            zcx_abapgit_exception=>raise( 'User is currently editing program' ).
          ELSEIF sy-msgid = 'EU' AND sy-msgno = '522'.
            " for generated table maintenance function groups, the author is set to SAP* instead of the user which
            " generates the function group. This hits some standard checks, pulling new code again sets the author
            " to the current user which avoids the check
            IF is_exit_include( is_progdir-name ) = abap_false.
              zcx_abapgit_exception=>raise( |Delete function group and pull again, { is_progdir-name } (EU522)| ).
            ENDIF.
          ELSE.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
        ENDIF.
    
        zcl_abapgit_language=>restore_login_language( ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_acid IMPLEMENTATION.
    
      METHOD create_object.
    
        DATA: lv_name TYPE aab_id_name.
    
        lv_name = ms_item-obj_name.
    
        CREATE OBJECT ro_aab
          EXPORTING
            im_name          = lv_name
          EXCEPTIONS
            name_not_allowed = 1
            OTHERS           = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    * looks like "changed by user" is not stored in the database
        rv_user = c_user_unknown.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lo_aab TYPE REF TO cl_aab_id.
    
        lo_aab = create_object( ).
        lo_aab->enqueue(
          EXCEPTIONS
            foreign_lock = 1
            system_error = 2
            cts_error    = 3
            OTHERS       = 4 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
        lo_aab->delete(
          EXCEPTIONS
            prop_error       = 1
            propt_error      = 2
            act_error        = 3
            cts_error        = 4
            cts_devclass     = 5
            id_not_found     = 6
            no_authorization = 7
            id_still_used    = 8
            where_used_error = 9
            OTHERS           = 10 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
        lo_aab->dequeue( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_description TYPE aab_id_descript,
              lo_aab         TYPE REF TO cl_aab_id.
    
        io_xml->read( EXPORTING iv_name = 'DESCRIPTION'
                      CHANGING  cg_data = lv_description ).
    
        lo_aab = create_object( ).
    
        lo_aab->enqueue(
          EXCEPTIONS
            foreign_lock = 1
            system_error = 2
            cts_error    = 3
            OTHERS       = 4 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        lo_aab->set_descript(
          EXPORTING
            im_descript      = lv_description
          EXCEPTIONS
            no_authorization = 1
            OTHERS           = 2 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        tadir_insert( iv_package ).
    
        lo_aab->save(
          EXCEPTIONS
            no_descript_specified = 1
            no_changes_found      = 2
            prop_error            = 3
            propt_error           = 4
            act_error             = 5
            cts_error             = 6
            sync_attributes_error = 7
            action_canceled       = 8
            OTHERS                = 9 ).
        IF sy-subrc >= 3.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        lo_aab->dequeue( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_state TYPE abap_bool,
              lo_aab   TYPE REF TO cl_aab_id.
    
        lo_aab = create_object( ).
    
        lo_aab->get_state( IMPORTING ex_state = lv_state ).
        rv_bool = boolc( lv_state = abap_true ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lo_aab         TYPE REF TO cl_aab_id,
              lv_description TYPE aab_id_descript.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        lo_aab = create_object( ).
    
        lo_aab->get_descript(
          IMPORTING ex_descript = lv_description
          EXCEPTIONS no_description_found = 1 ).
    
        io_xml->add( iv_name = 'DESCRIPTION'
                     ig_data = lv_description ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_aifc IMPLEMENTATION.
    
      METHOD authorization_check.
        DATA: lx_root TYPE REF TO cx_root.
    
        rv_success = abap_false.
        TRY.
            CALL METHOD mo_abapgit_util->('/AIF/IF_ABAPGIT_AIFC_UTIL~AUTHORIZATION_CHECK')
              RECEIVING
                rv_success = rv_success.
    
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise_with_text( lx_root ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD clear_client.
        DATA:
          BEGIN OF ls_data_to_clear,
            mandt  TYPE sy-mandt,
            client TYPE sy-mandt,
          END OF ls_data_to_clear.
    
        FIELD-SYMBOLS:
           TYPE any.
    
        LOOP AT ct_data ASSIGNING .
          MOVE-CORRESPONDING ls_data_to_clear TO .
        ENDLOOP.
      ENDMETHOD.
    
      METHOD compress_interface.
        DATA: lx_root TYPE REF TO cx_root.
    
        TRY.
            CALL METHOD mo_abapgit_util->('/AIF/IF_ABAPGIT_AIFC_UTIL~COMPRESS_INTERFACE')
              EXPORTING
                is_ifkeys  = is_ifkeys
              RECEIVING
                rv_success = rv_success.
    
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise_with_text( lx_root ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD constructor.
        DATA: lx_exc_ref TYPE REF TO cx_sy_dyn_call_error.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        ms_icd_data_key = is_item-obj_name.
    
        TRY.
            CALL METHOD ('/AIF/CL_ABAPGIT_AIFC_UTIL')=>('GET_INSTANCE')
              RECEIVING
                rr_abapgit_aifc_util = mo_abapgit_util.
    
          CATCH cx_sy_dyn_call_error INTO lx_exc_ref.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
      ENDMETHOD.
    
      METHOD execute_checks.
        DATA ls_ifkeys TYPE ty_aif_key_s.
    
        DATA lr_tabledescr TYPE REF TO cl_abap_tabledescr.
        DATA lr_structdescr TYPE REF TO cl_abap_structdescr.
        DATA lr_table TYPE REF TO data.
        FIELD-SYMBOLS  TYPE STANDARD TABLE.
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS:  TYPE any.
    
        DATA: lx_root TYPE REF TO cx_root.
    
        lr_structdescr ?= cl_abap_typedescr=>describe_by_name( p_name = '/AIF/T_FINF' ).
        lr_tabledescr = cl_abap_tabledescr=>create( p_line_type = lr_structdescr ).
    
        CREATE DATA lr_table TYPE HANDLE lr_tabledescr.
        ASSIGN lr_table->* TO .
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'Field Symbol not assigned' ).
        ENDIF.
    
        TRY.
            io_xml->read( EXPORTING iv_name = '/AIF/T_FINF'
                          CHANGING  cg_data =  ).
    
            READ TABLE  ASSIGNING  INDEX 1.
            IF sy-subrc = 0.
              ASSIGN COMPONENT 'NS' OF STRUCTURE  TO .
              IF sy-subrc = 0.
                ls_ifkeys-ns = .
              ENDIF.
    
              ASSIGN COMPONENT 'IFNAME' OF STRUCTURE  TO .
              IF sy-subrc = 0.
                ls_ifkeys-ifname = .
              ENDIF.
    
              ASSIGN COMPONENT 'IFVERSION' OF STRUCTURE  TO .
              IF sy-subrc = 0.
                ls_ifkeys-ifver = .
              ENDIF.
    
              CALL METHOD mo_abapgit_util->('/AIF/IF_ABAPGIT_AIFC_UTIL~EXECUTE_CHECKS')
                EXPORTING
                  is_ifkeys  = ls_ifkeys
                  is_finf    = 
                RECEIVING
                  rv_success = rv_success.
            ENDIF.
    
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise_with_text( lx_root ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD get_content_compress.
        DATA: lx_root TYPE REF TO cx_root.
        DATA: lo_log TYPE REF TO object.
    
        DATA:
          BEGIN OF ls_item,
            obj_type TYPE tadir-object,
            obj_name TYPE tadir-obj_name,
            devclass TYPE devclass,
          END OF ls_item.
    
        TRY.
            MOVE-CORRESPONDING ms_item TO ls_item.
    
            CREATE OBJECT lo_log TYPE ('/AIF/CL_ABAPGIT_BAL_LOG')
              EXPORTING ir_git_log = io_log
                        is_item = ls_item.
    
            CALL METHOD mo_abapgit_util->('/AIF/IF_ABAPGIT_AIFC_UTIL~INITIALIZE_CONTENT_COMPRESS')
              EXPORTING
                ir_bal     = lo_log
                is_ifkey   = is_ifkeys
                iv_package = iv_package
                iv_depl_id = ms_icd_data_key-depl_scenario.
    
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise_with_text( lx_root ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD handle_table_data.
        DATA: lx_root TYPE REF TO cx_root.
    
        TRY.
            CALL METHOD mo_abapgit_util->('/AIF/IF_ABAPGIT_AIFC_UTIL~HANDLE_TABLE_DATA')
              EXPORTING
                iv_tabname = iv_tabname
                it_data    = it_data.
    
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise_with_text( lx_root ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD validate_interface.
        DATA: lx_root TYPE REF TO cx_root.
    
        rv_success = abap_false.
        TRY.
            CALL METHOD mo_abapgit_util->('/AIF/IF_ABAPGIT_AIFC_UTIL~VALIDATE_INTERFACE')
              EXPORTING
                is_ifkeys  = is_ifkeys
              RECEIVING
                rv_success = rv_success.
    
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise_with_text( lx_root ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        DATA ls_icd_data_key TYPE ty_icd_data_key.
        ls_icd_data_key-depl_scenario = ms_icd_data_key-depl_scenario.
        ls_icd_data_key-ns = ms_icd_data_key-ns.
        ls_icd_data_key-ifname = ms_icd_data_key-ifname.
        ls_icd_data_key-ifver2 = ms_icd_data_key-ifver2.
    
        CALL METHOD mo_abapgit_util->('/AIF/IF_ABAPGIT_AIFC_UTIL~CHANGED_BY')
          EXPORTING
            is_key  = ls_icd_data_key
          RECEIVING
            rv_user = rv_user.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA lx_root TYPE REF TO cx_root.
    
        TRY.
            DELETE FROM ('/AIF/ICD_DATA')
              WHERE depl_scenario = ms_icd_data_key-depl_scenario
                AND ns            = ms_icd_data_key-ns
                AND ifname        = ms_icd_data_key-ifname
                AND ifver2        = ms_icd_data_key-ifver2.
    
            DELETE FROM ('/AIF/ICD_SCINF')
              WHERE depl_scenario = ms_icd_data_key-depl_scenario
                AND ns            = ms_icd_data_key-ns
                AND ifname        = ms_icd_data_key-ifname
                AND ifver2        = ms_icd_data_key-ifver2.
    
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise( iv_text     = 'Delete not possible'
                                          ix_previous = lx_root ).
        ENDTRY.
    
        tadir_delete( ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
        DATA: lx_root TYPE REF TO cx_root.
        DATA: lt_content TYPE ty_content_t.
    
        DATA lr_tabledescr TYPE REF TO cl_abap_tabledescr.
        DATA lr_structdescr TYPE REF TO cl_abap_structdescr.
        DATA lr_table TYPE REF TO data.
        FIELD-SYMBOLS  TYPE STANDARD TABLE.
        FIELD-SYMBOLS  TYPE any.
    
        DATA ls_ifkey TYPE ty_aif_key_s.
        DATA lr_content TYPE REF TO ty_content_s.
    
        DATA lx_abap_not_a_table TYPE REF TO cx_abap_not_a_table.
    
        DATA lv_tablename TYPE string.
        FIELD-SYMBOLS:  TYPE any.
    
        IF iv_step <> zif_abapgit_object=>gc_step_id-abap.
          RETURN.
        ENDIF.
    
        TRY.
            IF execute_checks( io_xml ) = abap_false.
              zcx_abapgit_exception=>raise( 'AIF interface checks failed' ).
            ENDIF.
    
            io_xml->read( EXPORTING iv_name = `Content_table`
                          CHANGING  cg_data = lt_content ).
    
            LOOP AT lt_content REFERENCE INTO lr_content.
              TRY.
                  lv_tablename = cl_abap_dyn_prg=>check_table_name_str( val      = lr_content->tabname
                                                                        packages = '' ).
                CATCH cx_abap_not_a_table INTO lx_abap_not_a_table.
                  zcx_abapgit_exception=>raise_with_text( lx_abap_not_a_table ).
                CATCH cx_abap_not_in_package ##NO_HANDLER.
                  "that's fine
              ENDTRY.
    
              CLEAR lr_tabledescr.
              lr_structdescr ?= cl_abap_typedescr=>describe_by_name( p_name = lr_content->tabname ).
              lr_tabledescr = cl_abap_tabledescr=>create( p_line_type = lr_structdescr ).
    
              CREATE DATA lr_table TYPE HANDLE lr_tabledescr.
              ASSIGN lr_table->* TO .
              IF sy-subrc <> 0.
                zcx_abapgit_exception=>raise( 'Field Symbol not assigned' ).
              ENDIF.
    
              io_xml->read( EXPORTING iv_name = lr_content->tabname
                            CHANGING  cg_data =  ).
    
              handle_table_data( iv_tabname = lr_content->tabname
                                 it_data    =  ).
    
              IF lr_content->tabname = '/AIF/T_FINF'.
                READ TABLE  ASSIGNING  INDEX 1.
    
                ASSIGN COMPONENT 'NS' OF STRUCTURE  TO .
                IF  IS ASSIGNED.
                  ls_ifkey-ns = .
                  UNASSIGN .
                ENDIF.
    
                ASSIGN COMPONENT 'IFNAME' OF STRUCTURE  TO .
                IF  IS ASSIGNED.
                  ls_ifkey-ifname = .
                  UNASSIGN .
                ENDIF.
    
                ASSIGN COMPONENT 'IFVERSION' OF STRUCTURE  TO .
                IF  IS ASSIGNED.
                  ls_ifkey-ifver = .
                  UNASSIGN .
                ENDIF.
              ENDIF.
    
            ENDLOOP.
    
            IF ls_ifkey IS INITIAL.
              RETURN.
            ENDIF.
    
            get_content_compress( io_log     = ii_log
                                  is_ifkeys  = ls_ifkey
                                  iv_package = iv_package ).
    
            IF authorization_check( ) = abap_false.
              RETURN.
            ENDIF.
    
            IF validate_interface( ls_ifkey ) = abap_false.
              RETURN.
            ENDIF.
    
            IF compress_interface( ls_ifkey ) = abap_false.
              RETURN.
            ENDIF.
    
          CATCH cx_root INTO lx_root.
            ii_log->add_exception( ix_exc  = lx_root
                                   is_item = ms_item ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
        DATA ls_icd_data_key TYPE ty_icd_data_key.
    
        ls_icd_data_key-depl_scenario = ms_icd_data_key-depl_scenario.
        ls_icd_data_key-ns = ms_icd_data_key-ns.
        ls_icd_data_key-ifname = ms_icd_data_key-ifname.
        ls_icd_data_key-ifver2 = ms_icd_data_key-ifver2.
    
        rv_bool = abap_false.
    
        CALL METHOD mo_abapgit_util->('/AIF/IF_ABAPGIT_AIFC_UTIL~EXISTS')
          EXPORTING
            is_key  = ls_icd_data_key
          RECEIVING
            rv_bool = rv_bool.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = abap_false.
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
        rv_active = abap_true.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        TYPES: ty_rsparamsl_255_t TYPE STANDARD TABLE OF rsparamsl_255 WITH NON-UNIQUE DEFAULT KEY.
    
        DATA lv_report TYPE progname VALUE '/AIF/CONTENT_DISPLAY'.
        DATA lt_params TYPE ty_rsparamsl_255_t.
        DATA ls_param LIKE LINE OF lt_params.
    
        ls_param-selname = 'P_DEPL'.
        ls_param-kind = 'P'.
        ls_param-sign = 'I'.
        ls_param-option = 'EQ'.
        ls_param-low = ms_icd_data_key-depl_scenario.
        APPEND ls_param TO lt_params.
    
        SUBMIT (lv_report) WITH SELECTION-TABLE lt_params AND RETURN.
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
        DATA lx_root TYPE REF TO cx_root.
        DATA ls_icd_data_key TYPE ty_icd_data_key.
        DATA lt_ifdata TYPE ty_table_data_t.
    
        DATA lt_content TYPE ty_content_t.
        DATA ls_content TYPE ty_content_s.
        DATA lr_ifdata TYPE REF TO ty_table_data_s.
        FIELD-SYMBOLS  TYPE ANY TABLE.
    
        TRY.
            ls_icd_data_key-depl_scenario = ms_icd_data_key-depl_scenario.
            ls_icd_data_key-ns = ms_icd_data_key-ns.
            ls_icd_data_key-ifname = ms_icd_data_key-ifname.
            ls_icd_data_key-ifver2 = ms_icd_data_key-ifver2.
    
            CALL METHOD mo_abapgit_util->('/AIF/IF_ABAPGIT_AIFC_UTIL~GET_IF_DATA')
              EXPORTING
                is_key    = ls_icd_data_key
              RECEIVING
                rt_ifdata = lt_ifdata.
    
            LOOP AT lt_ifdata REFERENCE INTO lr_ifdata.
    
              UNASSIGN .
              ASSIGN lr_ifdata->table_data->* TO .
              IF  IS NOT ASSIGNED.
                CONTINUE.
              ENDIF.
    
              clear_client( CHANGING ct_data =  ).
    
              io_xml->add( iv_name = lr_ifdata->tabname
                           ig_data =  ).
    
              ls_content-tabname = lr_ifdata->tabname.
              APPEND ls_content TO lt_content.
    
            ENDLOOP.
    
            io_xml->add( iv_name = `Content_table`
                         ig_data = lt_content ).
    
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise( iv_text     = 'Serialize not possible'
                                          ix_previous = lx_root ).
        ENDTRY.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_amsd IMPLEMENTATION.
    
      METHOD clear_field.
    
        FIELD-SYMBOLS:  TYPE data.
    
        ASSIGN COMPONENT iv_fieldname OF STRUCTURE cs_logical_db_schema TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
    
      ENDMETHOD.
    
      METHOD clear_fields.
    
        clear_field(
          EXPORTING
            iv_fieldname         = 'METADATA-CREATED_AT'
          CHANGING
            cs_logical_db_schema = cs_logical_db_schema ).
    
        clear_field(
          EXPORTING
            iv_fieldname         = 'METADATA-CREATED_BY'
          CHANGING
            cs_logical_db_schema = cs_logical_db_schema ).
    
        clear_field(
          EXPORTING
            iv_fieldname         = 'METADATA-CHANGED_AT'
          CHANGING
            cs_logical_db_schema = cs_logical_db_schema ).
    
        clear_field(
          EXPORTING
            iv_fieldname         = 'METADATA-CHANGED_BY'
          CHANGING
            cs_logical_db_schema = cs_logical_db_schema ).
    
        clear_field(
          EXPORTING
            iv_fieldname         = 'METADATA-RESPONSIBLE'
          CHANGING
            cs_logical_db_schema = cs_logical_db_schema ).
    
        clear_field(
          EXPORTING
            iv_fieldname         = 'METADATA-MASTER_SYSTEM'
          CHANGING
            cs_logical_db_schema = cs_logical_db_schema ).
    
        clear_field(
          EXPORTING
            iv_fieldname         = 'METADATA-PACKAGE_REF'
          CHANGING
            cs_logical_db_schema = cs_logical_db_schema ).
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_logical_db_schema_key = ms_item-obj_name.
    
        TRY.
            CREATE DATA mr_logical_db_schema TYPE ('CL_AMDP_SCHEMA_OBJECT_DATA=>TY_OBJECT_DATA').
            CREATE OBJECT mi_persistence TYPE ('CL_AMDP_SCHEMA_OBJECT_PERSIST').
    
          CATCH cx_sy_create_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD fill_metadata_from_db.
    
        DATA:
          li_wb_object_operator    TYPE REF TO object,
          lr_logical_db_schema_old TYPE REF TO data.
    
        FIELD-SYMBOLS:
           TYPE any,
                      TYPE xsddatetime_z,
                      TYPE syuname,
                  TYPE xsddatetime_z,
                  TYPE syuname.
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        CREATE DATA lr_logical_db_schema_old TYPE ('CL_AMDP_SCHEMA_OBJECT_DATA=>TY_OBJECT_DATA').
        ASSIGN lr_logical_db_schema_old->* TO .
        ASSERT sy-subrc = 0.
    
        CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
          IMPORTING
            data = .
    
        ASSIGN COMPONENT 'METADATA-CREATED_BY' OF STRUCTURE cs_logical_db_schema
               TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN COMPONENT 'METADATA-CREATED_AT' OF STRUCTURE cs_logical_db_schema
               TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN COMPONENT 'METADATA-CREATED_BY' OF STRUCTURE 
               TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN COMPONENT 'METADATA-CREATED_AT' OF STRUCTURE 
               TO .
        ASSERT sy-subrc = 0.
    
         = .
         = .
    
      ENDMETHOD.
    
      METHOD get_wb_object_operator.
    
        DATA:
          ls_object_type TYPE wbobjtype,
          lx_error       TYPE REF TO cx_root.
    
        IF mi_wb_object_operator IS BOUND.
          ri_wb_object_operator = mi_wb_object_operator.
        ENDIF.
    
        ls_object_type-objtype_tr = 'AMSD'.
        ls_object_type-subtype_wb = 'TYP'.
    
        TRY.
            CALL METHOD ('CL_WB_OBJECT_OPERATOR')=>('CREATE_INSTANCE')
              EXPORTING
                object_type = ls_object_type
                object_key  = mv_logical_db_schema_key
              RECEIVING
                result      = mi_wb_object_operator.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        ri_wb_object_operator = mi_wb_object_operator.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA:
          li_wb_object_operator TYPE REF TO object,
          li_object_data_model  TYPE REF TO if_wb_object_data_model,
          lx_error              TYPE REF TO cx_root.
    
        TRY.
            li_wb_object_operator = get_wb_object_operator( ).
    
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
              IMPORTING
                eo_object_data = li_object_data_model.
    
            rv_user = li_object_data_model->get_changed_by( ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA:
          li_wb_object_operator TYPE REF TO object,
          lx_error              TYPE REF TO cx_root.
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        TRY.
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~DELETE')
              EXPORTING
                transport_request = iv_transport.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA:
          li_object_data_model  TYPE REF TO if_wb_object_data_model,
          li_wb_object_operator TYPE REF TO object,
          lx_error              TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:
           TYPE any.
    
        ASSIGN mr_logical_db_schema->* TO .
        ASSERT sy-subrc = 0.
    
        io_xml->read(
          EXPORTING
            iv_name = 'AMSD'
          CHANGING
            cg_data =  ).
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        TRY.
            CREATE OBJECT li_object_data_model TYPE ('CL_AMDP_SCHEMA_OBJECT_DATA').
    
            tadir_insert( iv_package ).
    
            IF zif_abapgit_object~exists( ) = abap_true.
    
              " We need to populate created_at, created_by, because otherwise update  is not possible
              fill_metadata_from_db( CHANGING cs_logical_db_schema =  ).
              li_object_data_model->set_data(  ).
    
              CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
                EXPORTING
                  io_object_data    = li_object_data_model
                  transport_request = iv_transport.
    
            ELSE.
    
              li_object_data_model->set_data(  ).
    
              CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~CREATE')
                EXPORTING
                  io_object_data    = li_object_data_model
                  data_selection    = 'P' " if_wb_object_data_selection_co=>c_properties
                  package           = iv_package
                  transport_request = iv_transport.
    
              CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
                EXPORTING
                  io_object_data    = li_object_data_model
                  data_selection    = 'D' " if_wb_object_data_selection_co=>c_data_content
                  transport_request = iv_transport.
    
            ENDIF.
    
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~ACTIVATE').
    
            corr_insert( iv_package ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        TRY.
            mi_persistence->get(
              p_object_key           = mv_logical_db_schema_key
              p_version              = 'A'
              p_existence_check_only = abap_true ).
            rv_bool = abap_true.
    
          CATCH cx_swb_exception.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESWB_EO'
                                                iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          li_object_data_model  TYPE REF TO if_wb_object_data_model,
          lx_error              TYPE REF TO cx_root,
          li_wb_object_operator TYPE REF TO object.
    
        FIELD-SYMBOLS:
           TYPE any.
    
        ASSIGN mr_logical_db_schema->* TO .
        ASSERT sy-subrc = 0.
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        TRY.
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
              EXPORTING
                version        = 'A'
              IMPORTING
                data           = 
                eo_object_data = li_object_data_model.
    
            clear_fields( CHANGING cs_logical_db_schema =  ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        io_xml->add(
          iv_name = 'AMSD'
          ig_data =  ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_apis IMPLEMENTATION.
    
      METHOD constructor.
    
        DATA lr_data TYPE REF TO data.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        TRY.
            CREATE DATA lr_data TYPE (c_model).
          CATCH cx_sy_create_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD initialize.
    
        IF mo_handler IS NOT BOUND.
          CREATE OBJECT mo_handler TYPE ('CL_ARS_API_ABAPGIT')
            EXPORTING
              iv_api_object_name = ms_item-obj_name.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        initialize( ).
    
        TRY.
            CALL METHOD mo_handler->('IF_ARS_API_ABAPGIT~GET_CHANGED_BY')
              RECEIVING
                rv_changed_by = rv_user.
          CATCH cx_root.
            rv_user = c_user_unknown.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
    * IF_ARS_API_ABAPGIT~DELETE_API_STATE dumps and checks fail, even though I as a developer can delete it
    
        DATA lo_db   TYPE REF TO object.
        DATA lr_data TYPE REF TO data.
        DATA lx_error TYPE REF TO cx_root.
    
        FIELD-SYMBOLS  TYPE any.
    
        CREATE DATA lr_data TYPE ('IF_ARS_STATE_DB_ACCESS=>TY_S_API_KEY').
        ASSIGN lr_data->* TO .
         = ms_item-obj_name.
        ASSERT  IS NOT INITIAL.
    
        TRY.
            CALL METHOD ('CL_ARS_STATE_DB_ACCESS')=>('GET_INSTANCE')
              RECEIVING
                ro_state_db_access = lo_db.
    
            CALL METHOD lo_db->('IF_ARS_STATE_DB_ACCESS~DELETE')
              EXPORTING
                is_api_key = .
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
    * IF_ARS_API_ABAPGIT~SAVE_API_STATE dumps in some package checks
    
        DATA lr_data              TYPE REF TO data.
        DATA lo_db                TYPE REF TO object.
        DATA lx_error TYPE REF TO cx_root.
    
        FIELD-SYMBOLS    TYPE any.
        FIELD-SYMBOLS  TYPE ANY TABLE.
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE ANY TABLE.
        FIELD-SYMBOLS   TYPE any.
        FIELD-SYMBOLS     TYPE any.
        FIELD-SYMBOLS  TYPE simple.
    
        CREATE DATA lr_data TYPE (c_model).
        ASSIGN lr_data->* TO .
        CREATE DATA lr_data TYPE ('IF_ARS_STATE_DB_ACCESS=>TY_S_HEADER').
        ASSIGN lr_data->* TO .
        CREATE DATA lr_data TYPE ('IF_ARS_STATE_DB_ACCESS=>TY_T_STATE').
        ASSIGN lr_data->* TO .
    
        io_xml->read(
          EXPORTING
            iv_name = 'APIS'
          CHANGING
            cg_data =  ).
    
        MOVE-CORRESPONDING  TO .
    
        ASSIGN COMPONENT 'API_STATES' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
    
    * the state table is sorted,
        LOOP AT  ASSIGNING .
          CREATE DATA lr_data TYPE ('IF_ARS_STATE_DB_ACCESS=>TY_S_STATE').
          ASSIGN lr_data->* TO .
          MOVE-CORRESPONDING  TO .
          MOVE-CORRESPONDING  TO .
    
          ASSIGN COMPONENT 'SOFTWARE_RELEASE_NAME' OF STRUCTURE  TO .
          ASSERT sy-subrc = 0.
           = '1908'.
          ASSIGN COMPONENT 'CREATED_AT' OF STRUCTURE  TO .
          ASSERT sy-subrc = 0.
           = sy-datum.
          ASSIGN COMPONENT 'CREATED_BY' OF STRUCTURE  TO .
          ASSERT sy-subrc = 0.
           = sy-uname.
          ASSIGN COMPONENT 'LAST_CHANGED_AT' OF STRUCTURE  TO .
          ASSERT sy-subrc = 0.
           = sy-datum.
          ASSIGN COMPONENT 'LAST_CHANGED_BY' OF STRUCTURE  TO .
          ASSERT sy-subrc = 0.
           = sy-uname.
    
          INSERT  INTO TABLE .
        ENDLOOP.
    
        TRY.
            CALL METHOD ('CL_ARS_STATE_DB_ACCESS')=>('GET_INSTANCE')
              RECEIVING
                ro_state_db_access = lo_db.
    
            CALL METHOD lo_db->('IF_ARS_STATE_DB_ACCESS~SAVE')
              EXPORTING
                is_header         = 
                it_release_states = .
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        tadir_insert( iv_package ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        TRY.
            initialize( ).
            CALL METHOD mo_handler->('IF_ARS_API_ABAPGIT~CHECK_EXISTS')
              RECEIVING
                rv_api_exists = rv_bool.
          CATCH cx_root.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
        rs_metadata-version = 'v2.0.0'.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = zif_abapgit_object~exists( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    * looks like there is no enqueue lock
    * E_ARS_API ?
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " todo
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA lr_data TYPE REF TO data.
        DATA lx_error TYPE REF TO cx_root.
    
        FIELD-SYMBOLS  TYPE any.
    
        CREATE DATA lr_data TYPE (c_model).
        ASSIGN lr_data->* TO .
    
        initialize( ).
    
        TRY.
            CALL METHOD mo_handler->('IF_ARS_API_ABAPGIT~GET_API_STATE')
              RECEIVING
                rs_apis_object = .
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        io_xml->add( iv_name = 'APIS'
                     ig_data =  ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_common_aff IMPLEMENTATION.
    
      METHOD constructor.
    
        DATA:
          lv_is_supported TYPE abap_bool,
          lo_handler      TYPE REF TO object.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        " Check if AFF handler exists and if object type is registered and supported
        TRY.
            lo_handler = get_object_handler( ).
    
            IF lo_handler IS NOT INITIAL.
              " Additional gate to allow usage of object type in abapGit
              lv_is_supported = zcl_abapgit_aff_factory=>get_registry( )->is_supported_object_type( is_item-obj_type ).
            ENDIF.
          CATCH cx_root.
            lv_is_supported = abap_false.
        ENDTRY.
    
        IF lv_is_supported IS INITIAL.
          RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD create_aff_setting_deserialize.
        DATA:
          lv_version TYPE r3state.
    
        IF zcl_abapgit_objects_activation=>is_ddic_type( ms_item-obj_type ) = abap_true.
          lv_version = 'I'.
        ELSE.
          lv_version = 'A'.
        ENDIF.
        IF ms_item-abap_language_version <> zcl_abapgit_abap_language_vers=>c_any_abap_language_version AND
           ms_item-abap_language_version <> zcl_abapgit_abap_language_vers=>c_no_abap_language_version.
          TRY.
              CREATE OBJECT ro_settings_deserialize TYPE ('CL_AFF_SETTINGS_DESERIALIZE')
                EXPORTING
                  version               = lv_version
                  language              = mv_language
                  user                  = sy-uname
                  abap_language_version = ms_item-abap_language_version.
            CATCH cx_root.
              zcx_abapgit_exception=>raise( |System does not supported ABAP language version for AFF| ).
          ENDTRY.
        ELSE.
          CREATE OBJECT ro_settings_deserialize TYPE ('CL_AFF_SETTINGS_DESERIALIZE')
            EXPORTING
              version               = lv_version
              language              = mv_language
              user                  = sy-uname.
        ENDIF.
      ENDMETHOD.
    
      METHOD get_additional_extensions.
        RETURN.
      ENDMETHOD.
    
      METHOD get_object_handler.
    
        DATA lo_handler_factory TYPE REF TO object.
    
        CREATE OBJECT lo_handler_factory TYPE ('CL_AFF_OBJECT_HANDLER_FACTORY').
    
        " If object type is not supported, this call does not throw but returns an initial handler
        CALL METHOD lo_handler_factory->('IF_AFF_OBJECT_HANDLER_FACTORY~GET_OBJECT_HANDLER')
          EXPORTING
            object_type = ms_item-obj_type
          RECEIVING
            result      = ro_object_handler.
    
      ENDMETHOD.
    
      METHOD is_file_empty.
    
        CALL METHOD io_object_json_file->('IF_AFF_FILE~IS_DELETION')
          RECEIVING
            result = rv_is_empty.
    
      ENDMETHOD.
    
      METHOD remove_abap_language_version.
        DATA lv_json TYPE string.
        DATA lv_json_wo_alv TYPE string.
        DATA li_json TYPE REF TO /apmg/if_apm_ajson.
    
        lv_json = zcl_abapgit_convert=>xstring_to_string_utf8( iv_json_as_xstring ).
    
        TRY.
            li_json = /apmg/cl_apm_ajson=>parse( iv_json            = lv_json
                                                iv_keep_item_order = abap_true ).
            li_json->delete( '/header/abapLanguageVersion' ).
            lv_json_wo_alv = li_json->stringify( 2 ).
    
            rv_json_as_xstring_wo_alv = zcl_abapgit_convert=>string_to_xstring_utf8( lv_json_wo_alv ).
    
          CATCH /apmg/cx_apm_ajson_error.
            rv_json_as_xstring_wo_alv = iv_json_as_xstring.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lr_intf_aff_obj   TYPE REF TO data,
              lr_intf_aff_log   TYPE REF TO data,
              lr_messages       TYPE REF TO data,
              lo_object_handler TYPE REF TO object,
              lo_object_aff     TYPE REF TO object,
              lo_aff_factory    TYPE REF TO object,
              lv_name           TYPE c LENGTH 120,
              lx_error          TYPE REF TO cx_root,
              lo_aff_log        TYPE REF TO object.
    
        FIELD-SYMBOLS:  TYPE any,
                        TYPE any,
                            TYPE ANY TABLE,
                             TYPE any,
                                 TYPE symsg.
    
        lv_name = ms_item-obj_name.
    
        TRY.
            lo_object_handler = get_object_handler( ).
    
            CREATE OBJECT lo_object_aff TYPE ('CL_AFF_OBJ')
               EXPORTING
                 package = iv_package
                 name    = lv_name
                 type    = ms_item-obj_type.
    
            CREATE DATA lr_intf_aff_obj TYPE REF TO ('IF_AFF_OBJ').
            ASSIGN lr_intf_aff_obj->* TO .
             ?= lo_object_aff.
    
            CREATE OBJECT lo_aff_factory TYPE ('CL_AFF_FACTORY').
            CALL METHOD lo_aff_factory->('CREATE_LOG')
              RECEIVING
                result = lo_aff_log.
    
            CREATE DATA lr_intf_aff_log TYPE REF TO ('IF_AFF_LOG').
            ASSIGN lr_intf_aff_log->* TO .
             ?= lo_aff_log.
    
            CALL METHOD lo_object_handler->('IF_AFF_OBJECT_HANDLER~DELETE')
              EXPORTING
                object = 
                log    = .
    
            CREATE DATA lr_messages TYPE ('IF_AFF_LOG=>TT_LOG_OUT').
            ASSIGN lr_messages->* TO .
    
            CALL METHOD lo_aff_log->('IF_AFF_LOG~GET_MESSAGES')
              RECEIVING
                messages = .
    
            LOOP AT  ASSIGNING .
              ASSIGN COMPONENT 'MESSAGE' OF STRUCTURE  TO .
              IF -msgty = 'E'.
                zcx_abapgit_exception=>raise_t100(
                  iv_msgid = -msgid
                  iv_msgno = -msgno
                  iv_msgv1 = -msgv1
                  iv_msgv2 = -msgv2
                  iv_msgv3 = -msgv3
                  iv_msgv4 = -msgv4 ).
              ENDIF.
            ENDLOOP.
    
            tadir_delete( ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lr_intf_aff_obj          TYPE REF TO data,
              lr_intf_aff_file         TYPE REF TO data,
              lr_intf_files_container  TYPE REF TO data,
              lr_intf_aff_log          TYPE REF TO data,
              lr_intf_aff_settings     TYPE REF TO data,
              lo_object_handler        TYPE REF TO object,
              lo_object_aff            TYPE REF TO object,
              lo_object_json_file      TYPE REF TO object,
              lo_object_file           TYPE REF TO object,
              lo_files_container       TYPE REF TO object,
              lo_settings              TYPE REF TO object,
              lo_aff_log               TYPE REF TO object,
              lo_aff_factory           TYPE REF TO object,
              lr_messages              TYPE REF TO data,
              lv_json_as_xstring       TYPE xstring,
              lx_exception             TYPE REF TO cx_root,
              lv_file_name             TYPE string,
              lo_file_name_mapper      TYPE REF TO object,
              lv_name                  TYPE c LENGTH 120,
              lv_file_as_xstring       TYPE xstring,
              ls_additional_extensions TYPE ty_extension_mapper_pairs.
    
        FIELD-SYMBOLS:           TYPE any,
                                TYPE any,
                         TYPE any,
                                 TYPE any,
                            TYPE any,
                                     TYPE ANY TABLE,
                                      TYPE any,
                                         TYPE any,
                                         TYPE any,
                                          TYPE symsg,
                        LIKE LINE OF ls_additional_extensions.
    
        lv_json_as_xstring = mo_files->read_raw( 'json' ).
        lv_name = ms_item-obj_name.
    
        " beyond here there will be dragons....
        TRY.
            lo_object_handler = get_object_handler( ).
    
            CREATE OBJECT lo_object_aff TYPE ('CL_AFF_OBJ')
              EXPORTING
                package = ms_item-devclass
                name    = lv_name
                type    = ms_item-obj_type.
    
            CREATE DATA lr_intf_aff_obj TYPE REF TO ('IF_AFF_OBJ').
            ASSIGN lr_intf_aff_obj->* TO .
             ?= lo_object_aff.
    
            CREATE OBJECT lo_files_container TYPE ('CL_AFF_FILES_CONTAINER')
              EXPORTING
                object = .
    
            CALL METHOD ('CL_AFF_FILE_NAME_MAPPER')=>for_json
              RECEIVING
                result = lo_file_name_mapper.
    
            CALL METHOD lo_file_name_mapper->('IF_AFF_FILE_NAME_MAPPER~GET_FILE_NAME_FROM_OBJECT')
              EXPORTING
                object = 
              RECEIVING
                result = lv_file_name.
    
            lo_settings = create_aff_setting_deserialize( ).
    
            CREATE OBJECT lo_object_json_file TYPE ('CL_AFF_FILE')
              EXPORTING
                name    = lv_file_name
                content = lv_json_as_xstring.
    
            CREATE DATA lr_intf_aff_file TYPE REF TO ('IF_AFF_FILE').
            ASSIGN lr_intf_aff_file->* TO .
             ?= lo_object_json_file.
    
            CALL METHOD lo_files_container->('ADD_FILE')
              EXPORTING
                file = .
    
            ls_additional_extensions = get_additional_extensions( ).
    
            LOOP AT ls_additional_extensions ASSIGNING .
    
              lv_file_as_xstring = mo_files->read_raw( -extension ).
    
              CALL METHOD -file_name_mapper->('IF_AFF_FILE_NAME_MAPPER~GET_FILE_NAME_FROM_OBJECT')
                EXPORTING
                  object = 
                RECEIVING
                  result = lv_file_name.
    
              CREATE OBJECT lo_object_file TYPE ('CL_AFF_FILE')
              EXPORTING
                name    = lv_file_name
                content = lv_file_as_xstring.
    
              CREATE DATA lr_intf_aff_file TYPE REF TO ('IF_AFF_FILE').
              ASSIGN lr_intf_aff_file->* TO .
               ?= lo_object_file.
    
              CALL METHOD lo_files_container->('ADD_FILE')
                EXPORTING
                  file = .
    
            ENDLOOP.
    
            CREATE OBJECT lo_aff_factory TYPE ('CL_AFF_FACTORY').
            CALL METHOD lo_aff_factory->('CREATE_LOG')
              RECEIVING
                result = lo_aff_log.
    
            CREATE DATA lr_intf_files_container TYPE REF TO ('IF_AFF_FILES_CONTAINER').
            ASSIGN lr_intf_files_container->* TO .
             ?= lo_files_container.
    
            CREATE DATA lr_intf_aff_log TYPE REF TO ('IF_AFF_LOG').
            ASSIGN lr_intf_aff_log->* TO .
             ?= lo_aff_log.
    
            CREATE DATA lr_intf_aff_settings TYPE REF TO ('IF_AFF_SETTINGS_DESERIALIZE').
            ASSIGN lr_intf_aff_settings->* TO .
             ?= lo_settings.
    
            CALL METHOD lo_object_handler->('IF_AFF_OBJECT_HANDLER~DESERIALIZE')
              EXPORTING
                files_container = 
                log             = 
                settings        = .
    
            CREATE DATA lr_messages TYPE ('IF_AFF_LOG=>TT_LOG_OUT').
            ASSIGN lr_messages->* TO .
    
            CALL METHOD lo_aff_log->('IF_AFF_LOG~GET_MESSAGES')
              RECEIVING
                messages = .
    
            LOOP AT  ASSIGNING .
              ASSIGN COMPONENT 'MESSAGE' OF STRUCTURE  TO .
              ASSIGN COMPONENT 'TEXT' OF STRUCTURE  TO .
              ASSIGN COMPONENT 'TYPE' OF STRUCTURE  TO .
              ii_log->add(
                iv_msg  = 
                iv_type = 
                is_item = ms_item ).
    
              IF -msgty = 'E'.
                zcx_abapgit_exception=>raise_t100(
                  iv_msgid = -msgid
                  iv_msgno = -msgno
                  iv_msgv1 = -msgv1
                  iv_msgv2 = -msgv2
                  iv_msgv3 = -msgv3
                  iv_msgv4 = -msgv4 ).
              ENDIF.
            ENDLOOP.
    
            IF is_active( ) = abap_false.
              " as DDIC-object e.g. are not deserialized in active state, activation must be performed
              zcl_abapgit_objects_activation=>add_item( ms_item ).
            ENDIF.
    
            tadir_insert( ms_item-devclass ).
    
          CATCH cx_root INTO lx_exception.
            ii_log->add_error( is_item = ms_item
                               iv_msg  = 'Error at deserialize' ).
            ii_log->add_exception(
              ix_exc  = lx_exception
              is_item = ms_item ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
        DATA: lr_intf_aff_obj   TYPE REF TO data,
              lo_object_handler TYPE REF TO object,
              lo_object_aff     TYPE REF TO object,
              lv_name           TYPE c LENGTH 120,
              lx_error          TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:  TYPE any.
    
        lv_name = ms_item-obj_name.
    
        TRY.
            lo_object_handler = get_object_handler( ).
    
            CREATE OBJECT lo_object_aff TYPE ('CL_AFF_OBJ')
               EXPORTING
                 package = ms_item-devclass
                 name    = lv_name
                 type    = ms_item-obj_type.
    
            CREATE DATA lr_intf_aff_obj TYPE REF TO ('IF_AFF_OBJ').
            ASSIGN lr_intf_aff_obj->* TO .
             ?= lo_object_aff.
    
            CALL METHOD lo_object_handler->('IF_AFF_OBJECT_HANDLER~EXISTS')
              EXPORTING
                object = 
              RECEIVING
                result = rv_bool.
    
          CATCH cx_root INTO lx_error.
            " return false instead of raising exception, because abapGit assumes
            " that raising exception = existing object
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        DATA lv_lock_object   TYPE string.
        DATA lv_argument      TYPE seqg3-garg.
    
        lv_lock_object = |{ ms_item-obj_type }{ ms_item-obj_name }*|.
        lv_argument  = lv_lock_object.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESWB_EO'
                                                iv_argument    = lv_argument ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
        DATA: lr_intf_aff_obj           TYPE REF TO data,
              lr_intf_aff_log           TYPE REF TO data,
              lr_intf_aff_settings      TYPE REF TO data,
              lr_messages               TYPE REF TO data,
              lo_object_handler         TYPE REF TO object,
              lo_object_aff             TYPE REF TO object,
              lo_object_json_file       TYPE REF TO object,
              lo_files_container        TYPE REF TO object,
              lo_settings               TYPE REF TO object,
              lo_aff_log                TYPE REF TO object,
              lo_aff_factory            TYPE REF TO object,
              lo_object_file            TYPE REF TO object,
              lv_json_as_xstring        TYPE xstring,
              lv_json_as_xstring_wo_alv TYPE xstring,
              lx_exception              TYPE REF TO cx_root,
              lv_name                   TYPE c LENGTH 120,
              lv_file_name              TYPE string,
              lo_file_name_mapper       TYPE REF TO object,
              ls_additional_extensions  TYPE ty_extension_mapper_pairs,
              lv_file_as_xstring        TYPE xstring.
    
        FIELD-SYMBOLS:           TYPE any,
                                 TYPE any,
                            TYPE any,
                                     TYPE ANY TABLE,
                                      TYPE any,
                                          TYPE symsg,
                        LIKE LINE OF ls_additional_extensions.
    
        lv_name = ms_item-obj_name.
    
        TRY.
            lo_object_handler = get_object_handler( ).
    
            CREATE OBJECT lo_object_aff TYPE ('CL_AFF_OBJ')
               EXPORTING
                 package = ms_item-devclass
                 name    = lv_name
                 type    = ms_item-obj_type.
    
            CREATE OBJECT lo_settings TYPE ('CL_AFF_SETTINGS_SERIALIZE')
              EXPORTING
                version = 'A'
                language = mv_language.
    
            CREATE OBJECT lo_aff_factory TYPE ('CL_AFF_FACTORY').
            CALL METHOD lo_aff_factory->('CREATE_LOG')
              RECEIVING
                result = lo_aff_log.
    
            CREATE DATA lr_intf_aff_log TYPE REF TO ('IF_AFF_LOG').
            ASSIGN lr_intf_aff_log->* TO .
             ?= lo_aff_log.
    
            CREATE DATA lr_intf_aff_settings TYPE REF TO ('IF_AFF_SETTINGS_SERIALIZE').
            ASSIGN lr_intf_aff_settings->* TO .
             ?= lo_settings.
    
            CREATE DATA lr_intf_aff_obj TYPE REF TO ('IF_AFF_OBJ').
            ASSIGN lr_intf_aff_obj->* TO .
             ?= lo_object_aff.
    
            CALL METHOD lo_object_handler->('IF_AFF_OBJECT_HANDLER~SERIALIZE')
              EXPORTING
                object   = 
                log      = 
                settings = 
              RECEIVING
                result   = lo_files_container.
    
            CREATE DATA lr_messages TYPE ('IF_AFF_LOG=>TT_LOG_OUT').
            ASSIGN lr_messages->* TO .
    
            CALL METHOD lo_aff_log->('IF_AFF_LOG~GET_MESSAGES')
              RECEIVING
                messages = .
    
            LOOP AT  ASSIGNING .
              ASSIGN COMPONENT 'MESSAGE' OF STRUCTURE  TO .
              IF -msgty = 'E'.
                zcx_abapgit_exception=>raise_t100(
                  iv_msgid = -msgid
                  iv_msgno = -msgno
                  iv_msgv1 = -msgv1
                  iv_msgv2 = -msgv2
                  iv_msgv3 = -msgv3
                  iv_msgv4 = -msgv4 ).
              ENDIF.
            ENDLOOP.
    
            CALL METHOD ('CL_AFF_FILE_NAME_MAPPER')=>for_json
              RECEIVING
                result = lo_file_name_mapper.
    
            CALL METHOD lo_file_name_mapper->('IF_AFF_FILE_NAME_MAPPER~GET_FILE_NAME_FROM_OBJECT')
              EXPORTING
                object = 
              RECEIVING
                result = lv_file_name.
    
            CALL METHOD lo_files_container->('IF_AFF_FILES_CONTAINER~GET_FILE')
              EXPORTING
                name   = lv_file_name
              RECEIVING
                result = lo_object_json_file.
    
            " avoid to serialize empty content (object was never activated, exists inactive only).
            IF is_file_empty( lo_object_json_file ) = abap_true.
              MESSAGE s821(eu) WITH lv_name INTO zcx_abapgit_exception=>null.
              zcx_abapgit_exception=>raise_t100( ).
            ENDIF.
    
            CALL METHOD lo_object_json_file->('IF_AFF_FILE~GET_CONTENT')
              RECEIVING
                result = lv_json_as_xstring.
    
            " Only remove ABAP language version if repository is set to ignore it
            IF ms_item-abap_language_version = zcl_abapgit_abap_language_vers=>c_no_abap_language_version.
              lv_json_as_xstring_wo_alv = remove_abap_language_version( lv_json_as_xstring ).
              mo_files->add_raw(
                iv_ext  = 'json'
                iv_data = lv_json_as_xstring_wo_alv ).
            ELSE.
              mo_files->add_raw(
                iv_ext  = 'json'
                iv_data = lv_json_as_xstring ).
            ENDIF.
    
            ls_additional_extensions = get_additional_extensions( ).
    
            LOOP AT ls_additional_extensions ASSIGNING .
    
              CALL METHOD -file_name_mapper->('IF_AFF_FILE_NAME_MAPPER~GET_FILE_NAME_FROM_OBJECT')
                EXPORTING
                  object = 
                RECEIVING
                  result = lv_file_name.
    
              CALL METHOD lo_files_container->('IF_AFF_FILES_CONTAINER~GET_FILE')
                EXPORTING
                  name   = lv_file_name
                RECEIVING
                  result = lo_object_file.
    
              CALL METHOD lo_object_file->('IF_AFF_FILE~GET_CONTENT')
                RECEIVING
                  result = lv_file_as_xstring.
    
              mo_files->add_raw(
                iv_ext  = -extension
                iv_data = lv_file_as_xstring ).
    
            ENDLOOP.
    
          CATCH cx_root INTO lx_exception.
            zcx_abapgit_exception=>raise_with_text( lx_exception ).
        ENDTRY.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_OBJECT_APLO IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
        CLEAR rv_user.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_aqbg IMPLEMENTATION.
    
      METHOD get_field_rules.
    
        ro_result = zcl_abapgit_field_rules=>create( ).
    
        ro_result->add(
          iv_table     = 'AQGDBBG'
          iv_field     = 'BGCNAM'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = 'AQGDBBG'
          iv_field     = 'BGUNAM'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user ).
    
        ro_result->add(
          iv_table     = 'AQGDBBG'
          iv_field     = 'BGCDAT'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-date
        )->add(
          iv_table     = 'AQGDBBG'
          iv_field     = 'BGUDAT'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-date ).
    
        ro_result->add(
          iv_table     = 'AQGDBBG'
          iv_field     = 'DEVC'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-package ).
    
      ENDMETHOD.
    
      METHOD get_generic.
        " transaction SQ03
        CREATE OBJECT ro_generic
          EXPORTING
            is_item        = ms_item
            io_field_rules = get_field_rules( )
            iv_language    = mv_language.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        SELECT SINGLE bgunam FROM aqgdbbg INTO rv_user WHERE num = ms_item-obj_name.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        get_generic( )->delete( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA lt_bdcdata TYPE TABLE OF bdcdata.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_bdcdata.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -program  = 'SAPMS38S'.
        -dynpro   = '0050'.
        -dynbegin = abap_true.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'RS38S-BGNUM'.
        -fval = ms_item-obj_name.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode      = 'SQ03'
          it_bdcdata    = lt_bdcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        get_generic( )->serialize( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_aqqu IMPLEMENTATION.
    
      METHOD get_field_rules.
    
        ro_result = zcl_abapgit_field_rules=>create( ).
    
    * add rules here if needed
    
      ENDMETHOD.
    
      METHOD get_generic.
        " transaction SQ01
        CREATE OBJECT ro_generic
          EXPORTING
            is_item        = ms_item
            io_field_rules = get_field_rules( )
            iv_language    = mv_language.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        rv_user = c_user_unknown.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        get_generic( )->delete( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA lt_bdcdata TYPE TABLE OF bdcdata.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_bdcdata.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -program  = 'SAPMS38R'.
        -dynpro   = '0050'.
        -dynbegin = abap_true.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'RS38R-QNUM'.
        -fval = ms_item-obj_name.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode      = 'SQ01'
          it_bdcdata    = lt_bdcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
        get_generic( )->serialize( io_xml ).
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_aqsg IMPLEMENTATION.
    
      METHOD get_field_rules.
    
        ro_result = zcl_abapgit_field_rules=>create( ).
    
    * add rules here if needed
    
      ENDMETHOD.
    
      METHOD get_generic.
        " transaction SQ02
        CREATE OBJECT ro_generic
          EXPORTING
            is_item        = ms_item
            io_field_rules = get_field_rules( )
            iv_language    = mv_language.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        rv_user = c_user_unknown.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        get_generic( )->delete( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA lt_bdcdata TYPE TABLE OF bdcdata.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_bdcdata.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -program  = 'SAPMS38O'.
        -dynpro   = '0050'.
        -dynbegin = abap_true.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'RS38Q-NAME'.
        -fval = ms_item-obj_name.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode      = 'SQ02'
          it_bdcdata    = lt_bdcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
        get_generic( )->serialize( io_xml ).
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_area IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lv_user TYPE string.
    
        SELECT SINGLE tstpnm FROM ('RSDAREA') INTO lv_user.
    
        rv_user = lv_user.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA:
          lr_area         TYPE REF TO object.
    
        CREATE OBJECT lr_area TYPE ('CL_NEW_AWB_AREA').
    
        CALL METHOD lr_area->('IF_RSAWBN_FOLDER_TREE~DELETE_NODE')
          EXPORTING
            i_nodename    = ms_item-obj_name
            i_with_dialog = ''
          EXCEPTIONS
            cancelled     = 1
            OTHERS        = 2.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error while deleting AREA: { ms_item-obj_name }| ).
        ENDIF.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA:
          lv_nodename   TYPE c LENGTH 40,
          lv_parentname TYPE c LENGTH 40,
          lv_txtsh      TYPE c LENGTH 20,
          lv_txtlg      TYPE c LENGTH 60,
          lr_area       TYPE REF TO object.
    
        io_xml->read( EXPORTING iv_name = 'NODENAME'
                      CHANGING  cg_data = lv_nodename ).
    
        io_xml->read( EXPORTING iv_name = 'PARENTNAME'
                      CHANGING  cg_data = lv_parentname ).
    
        io_xml->read( EXPORTING iv_name = 'TXTSH'
                      CHANGING  cg_data = lv_txtsh ).
    
        io_xml->read( EXPORTING iv_name = 'TXTLG'
                      CHANGING  cg_data = lv_txtlg ).
    
        CREATE OBJECT lr_area TYPE ('CL_NEW_AWB_AREA').
    
        IF zif_abapgit_object~exists( ) = abap_false.
          CALL METHOD lr_area->('IF_RSAWBN_FOLDER_TREE~CREATE_NODE')
            EXPORTING
              i_parentname = lv_parentname
              i_nodename   = lv_nodename
              i_txtsh      = lv_txtsh
              i_txtlg      = lv_txtlg
            EXCEPTIONS
              cancelled    = 1
              OTHERS       = 2.
        ELSE.
          CALL METHOD lr_area->('IF_RSAWBN_FOLDER_TREE~CHANGE_NODE_EXT')
            EXPORTING
              i_nodename = lv_nodename
              i_txtlg    = lv_txtlg
              i_with_cto = abap_false
            EXCEPTIONS
              cancelled  = 1
              OTHERS     = 2.
        ENDIF.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error while creating AREA: { ms_item-obj_name }| ).
        ENDIF.
    
        tadir_insert( iv_package ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA:
          lr_area     TYPE REF TO object,
          lr_tab_tree TYPE REF TO data,
          lr_str_tee  TYPE REF TO data.
    
        FIELD-SYMBOLS:
           TYPE STANDARD TABLE,
           TYPE any.
    
        CREATE OBJECT lr_area TYPE ('CL_NEW_AWB_AREA').
    
        CREATE DATA lr_tab_tree TYPE STANDARD TABLE OF ('RSAWBN_S_TREEORG').
        ASSIGN lr_tab_tree->* TO .
    
        CREATE DATA lr_str_tee TYPE STANDARD TABLE OF ('RSAWBN_S_TREEORG').
        ASSIGN lr_str_tee->* TO .
    
        CALL METHOD lr_area->('IF_RSAWBN_FOLDER_TREE~GET_TREE')
          EXPORTING
            i_objvers = ''
            i_langu   = ''
          IMPORTING
            e_t_tree  = 
          EXCEPTIONS
            locked    = 1
            failed    = 2
            OTHERS    = 3.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error while read AREA tree| ).
        ENDIF.
    
        READ TABLE  WITH KEY ('NODENAME') = ms_item-obj_name ASSIGNING .
    
        IF sy-subrc = 0.
          rv_bool = abap_true.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
    
        DATA:
          lr_area     TYPE REF TO object,
          lr_tab_tree TYPE REF TO data,
          lr_str_tee  TYPE REF TO data.
    
        FIELD-SYMBOLS:
           TYPE STANDARD TABLE,
           TYPE any.
    
        CREATE OBJECT lr_area TYPE ('CL_NEW_AWB_AREA').
    
        CREATE DATA lr_tab_tree TYPE STANDARD TABLE OF ('RSAWBN_S_TREEORG').
        ASSIGN lr_tab_tree->* TO .
    
        CREATE DATA lr_str_tee TYPE STANDARD TABLE OF ('RSAWBN_S_TREEORG').
        ASSIGN lr_str_tee->* TO .
    
        CALL METHOD lr_area->('IF_RSAWBN_FOLDER_TREE~GET_TREE')
          EXPORTING
            i_objvers = 'A'
            i_langu   = mv_language
          IMPORTING
            e_t_tree  = 
          EXCEPTIONS
            locked    = 1
            failed    = 2
            OTHERS    = 3.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error while read AREA tree| ).
        ENDIF.
    
        READ TABLE  WITH KEY ('NODENAME') = ms_item-obj_name ASSIGNING .
    
        IF sy-subrc = 0.
          rv_active = abap_true.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( 'ERSDAREA' ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          lr_area     TYPE REF TO object,
          lr_tab_tree TYPE REF TO data,
          lr_str_tee  TYPE REF TO data,
          lr_rsdareat TYPE REF TO data,
          lv_select   TYPE string.
    
        FIELD-SYMBOLS:
                 TYPE STANDARD TABLE,
                 TYPE any,
           TYPE any,
             TYPE any,
                TYPE any,
                TYPE any.
    
        CREATE OBJECT lr_area TYPE ('CL_NEW_AWB_AREA').
    
        CREATE DATA lr_tab_tree TYPE STANDARD TABLE OF ('RSAWBN_S_TREEORG').
        ASSIGN lr_tab_tree->* TO .
    
        CREATE DATA lr_str_tee TYPE STANDARD TABLE OF ('RSAWBN_S_TREEORG').
        ASSIGN lr_str_tee->* TO .
    
        CREATE DATA lr_rsdareat TYPE ('RSDAREAT').
        ASSIGN lr_rsdareat->* TO .
    
        CALL METHOD lr_area->('IF_RSAWBN_FOLDER_TREE~GET_TREE')
          EXPORTING
            i_objvers = 'A'
            i_langu   = mv_language
          IMPORTING
            e_t_tree  = 
          EXCEPTIONS
            locked    = 1
            failed    = 2
            OTHERS    = 3.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error while read AREA tree| ).
        ENDIF.
    
        READ TABLE  WITH KEY ('NODENAME') = ms_item-obj_name ASSIGNING .
    
        lv_select = |INFOAREA = '{ ms_item-obj_name }'|.
    
        SELECT SINGLE * FROM ('RSDAREAT') INTO  WHERE infoarea = ms_item-obj_name.
    
        ASSIGN COMPONENT 'TXTSH' OF STRUCTURE  TO .
        ASSIGN COMPONENT 'TXTLG' OF STRUCTURE  TO .
    
        ASSIGN COMPONENT 'PARENTNAME' OF STRUCTURE  TO .
    
        io_xml->add( iv_name = 'NODENAME'
                     ig_data = ms_item-obj_name ).
    
        io_xml->add( iv_name = 'PARENTNAME'
                     ig_data =  ).
    
        io_xml->add( iv_name = 'TXTSH'
                     ig_data =  ).
    
        io_xml->add( iv_name = 'TXTLG'
                     ig_data =  ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_asfc IMPLEMENTATION.
    
      METHOD get_generic.
    
        CREATE OBJECT ro_generic
          EXPORTING
            is_item     = ms_item
            iv_language = mv_language.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        rv_user = c_user_unknown. " not stored by SAP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        set_default_transport( iv_transport ).
    
        get_generic( )->delete( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        set_default_transport( iv_transport ).
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        get_generic( )->serialize( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_auth IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_fieldname = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    * looks like "changed by user" is not stored in the database
        rv_user = c_user_unknown.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        " there is a bug in SAP standard, the TADIR entries are not deleted
        " when the AUTH object is deleted in transaction SU20
    
        " FM SUSR_AUTF_DELETE_FIELD calls the UI, therefore we reimplement its logic
    
        DATA:
          lt_objlst TYPE susr_t_xuobject,
          lo_auth   TYPE REF TO cl_auth_tools.
    
        " authority check
        CREATE OBJECT lo_auth.
        IF lo_auth->authority_check_suso( actvt     = '06'
                                          fieldname = mv_fieldname ) <> 0.
          MESSAGE e463(01) WITH mv_fieldname INTO zcx_abapgit_exception=>null.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        " if field is used check
        lt_objlst = lo_auth->suso_where_used_afield( mv_fieldname ).
        IF lt_objlst IS NOT INITIAL.
          MESSAGE i453(01) WITH mv_fieldname INTO zcx_abapgit_exception=>null.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        " collect fieldname into a transport task
        IF lo_auth->add_afield_to_trkorr( mv_fieldname ) <> 0.
          "no transport -> no deletion
          MESSAGE e507(0m) INTO zcx_abapgit_exception=>null.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        DELETE FROM authx WHERE fieldname = mv_fieldname.
        IF sy-subrc <> 0.
          MESSAGE e507(0m) INTO zcx_abapgit_exception=>null.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    * see include LSAUT_FIELDF02
    
        DATA: ls_authx TYPE authx,
              lo_auth  TYPE REF TO cl_auth_tools.
    
        io_xml->read( EXPORTING iv_name = 'AUTHX'
                      CHANGING cg_data = ls_authx ).
    
        tadir_insert( iv_package ).
    
        CREATE OBJECT lo_auth.
    
        IF lo_auth->add_afield_to_trkorr( ls_authx-fieldname ) <> 0.
          zcx_abapgit_exception=>raise( 'Error deserializing AUTH' ).
        ENDIF.
    
        MODIFY authx FROM ls_authx.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'Error deserializing AUTH' ).
        ENDIF.
    
        CALL FUNCTION 'DB_COMMIT'.
        lo_auth->set_authfld_info_from_db( ls_authx-fieldname ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        SELECT SINGLE fieldname FROM authx
          INTO mv_fieldname
          WHERE fieldname = ms_item-obj_name.               "#EC CI_GENBUFF
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        IF zcl_abapgit_factory=>get_function_module( )->function_exists( 'SU20_MAINTAIN_SNGL' ) = abap_true.
          " this function module does not exist in 740
          CALL FUNCTION 'SU20_MAINTAIN_SNGL'
            EXPORTING
              id_field    = mv_fieldname
              id_wbo_mode = abap_false ##EXISTS.
          rv_exit = abap_true.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_authx TYPE authx.
    
        SELECT SINGLE * FROM authx INTO ls_authx
          WHERE fieldname = ms_item-obj_name.               "#EC CI_GENBUFF
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        io_xml->add( iv_name = 'AUTHX'
                     ig_data = ls_authx ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_avar IMPLEMENTATION.
    
      METHOD create_object.
    
        DATA: lv_name TYPE aab_var_name.
    
        lv_name = ms_item-obj_name.
    
        CREATE OBJECT ro_aab_var
          EXPORTING
            im_name          = lv_name
            im_local         = ''
          EXCEPTIONS
            name_not_allowed = 1
            user_not_valid   = 2
            no_authorization = 3
            OTHERS           = 4.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_aab TYPE REF TO cl_aab_variant.
    
        lo_aab = create_object( ).
        lo_aab->get_author( IMPORTING ex_author = rv_user ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lo_aab TYPE REF TO cl_aab_variant.
    
        lo_aab = create_object( ).
        lo_aab->enqueue( ).
        lo_aab->delete(
          EXCEPTIONS
            var_not_found    = 1
            prop_error       = 2
            propt_error      = 3
            var_id_error     = 4
            no_authorization = 5
            cts_error        = 6
            cts_devclass     = 7
            OTHERS           = 8 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error deleting AVAR { ms_item-obj_name }| ).
        ENDIF.
        lo_aab->dequeue( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_possible    TYPE abap_bool,
              lv_description TYPE aab_var_descript,
              ls_is          TYPE aab_var_obj_act,
              lt_ids         TYPE aab_var_obj_act_tab,
              lo_aab         TYPE REF TO cl_aab_variant.
    
        " AVAR can only be created in transportable packages
        lv_possible = zcl_abapgit_factory=>get_sap_package( iv_package )->are_changes_recorded_in_tr_req( ).
        IF lv_possible = abap_false.
          zcx_abapgit_exception=>raise( |Global activation variants require a transportable package| ).
        ENDIF.
    
        " Create AVAR with description and object (id) list
        io_xml->read( EXPORTING iv_name = 'DESCRIPTION'
                      CHANGING  cg_data = lv_description ).
    
        io_xml->read( EXPORTING iv_name = 'IDS'
                      CHANGING  cg_data = lt_ids ).
    
        lo_aab = create_object( ).
        lo_aab->enqueue( ).
        lo_aab->set_descript(
          EXPORTING
            im_descript      = lv_description
          EXCEPTIONS
            no_authorization = 1 ).
        IF sy-subrc <> 0.
          lo_aab->dequeue( ).
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        LOOP AT lt_ids INTO ls_is.
          lo_aab->set_id(
            EXPORTING
              im_name              = ls_is-name
              im_object            = ls_is-object
              im_actmode           = ls_is-actmode
            EXCEPTIONS
              no_authorization     = 1
              id_not_exists        = 2
              id_not_transportable = 3
              OTHERS               = 4 ).
          IF sy-subrc <> 0.
            lo_aab->dequeue( ).
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
        ENDLOOP.
    
        tadir_insert( iv_package ).
    
        lo_aab->save(
          EXCEPTIONS
            no_descript_specified = 1
            prop_error            = 2
            propt_error           = 3
            var_id_error          = 4
            no_changes_found      = 5
            cts_error             = 6 ).
        IF sy-subrc <> 0.
          lo_aab->dequeue( ).
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
        lo_aab->dequeue( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_state TYPE abap_bool,
              lo_aab   TYPE REF TO cl_aab_variant.
    
        lo_aab = create_object( ).
    
        lo_aab->get_state( IMPORTING ex_state = lv_state ).
        rv_bool = boolc( lv_state = abap_true ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lo_aab         TYPE REF TO cl_aab_variant,
              lt_ids         TYPE aab_var_obj_act_tab,
              lv_description TYPE aab_var_descript.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        lo_aab = create_object( ).
    
        lo_aab->get_descript(
          IMPORTING
            ex_descript = lv_description
          EXCEPTIONS
            no_descript_found = 1 ).
        IF sy-subrc = 0.
          io_xml->add( iv_name = 'DESCRIPTION'
                       ig_data = lv_description ).
        ENDIF.
    
        lo_aab->get_ids( IMPORTING ex_ids = lt_ids ).
    
        io_xml->add( iv_name = 'IDS'
                     ig_data = lt_ids ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_avas IMPLEMENTATION.
    
      METHOD insert_assignments.
    
        DATA: lt_assignment TYPE STANDARD TABLE OF cls_assignment,
              ls_assignment LIKE LINE OF lt_assignment,
              ls_value      LIKE LINE OF is_avas-values.
    
        LOOP AT is_avas-values INTO ls_value.
          CLEAR ls_assignment.
          ls_assignment-guid        = is_avas-header-guid.
          ls_assignment-value       = ls_value-value.
          ls_assignment-trobjtype   = is_avas-header-object-trobjtype.
          ls_assignment-sobj_name   = is_avas-header-object-sobj_name.
          ls_assignment-object_type = is_avas-header-object-object_type.
          ls_assignment-sub_key     = is_avas-header-object-sub_key.
          ls_assignment-attribute   = is_avas-header-attribute.
          ls_assignment-set_by      = sy-uname.
          ls_assignment-changed_on  = sy-datum.
          ls_assignment-remark      = ls_value-remark.
          APPEND ls_assignment TO lt_assignment.
        ENDLOOP.
    
        DELETE FROM cls_assignment WHERE guid = is_avas-header-guid.
    
        INSERT cls_assignment FROM TABLE lt_assignment.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error inserting into CLS_ASSIGNMENT| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD instantiate.
    
        DATA: lv_id  TYPE guid_32,
              lx_err TYPE REF TO cx_root.
    
        lv_id = ms_item-obj_name.
    
        TRY.
            CREATE OBJECT ro_avas
              EXPORTING
                im_assignment_id = lv_id.
          CATCH cx_pak_wb_object_locked INTO lx_err.
            zcx_abapgit_exception=>raise( |AVAS { lv_id }: locked: { lx_err->get_longtext( ) }| ).
          CATCH cx_pak_not_authorized INTO lx_err.
            zcx_abapgit_exception=>raise( |AVAS { lv_id }: not authorized: { lx_err->get_longtext( ) }| ).
          CATCH cx_pak_invalid_state INTO lx_err.
            zcx_abapgit_exception=>raise( |AVAS { lv_id }: invalid state: { lx_err->get_longtext( ) }| ).
          CATCH cx_pak_invalid_data INTO lx_err.
            zcx_abapgit_exception=>raise( |AVAS { lv_id }: invalid data: { lx_err->get_longtext( ) }| ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_avas TYPE REF TO cl_cls_attr_value_assignment.
    
        lo_avas = instantiate( ).
    
        lo_avas->if_pak_wb_object~get_last_changed( IMPORTING ex_changed_by = rv_user ).
    
        IF rv_user IS INITIAL.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lo_avas TYPE REF TO cl_cls_attr_value_assignment.
    
        lo_avas = instantiate( ).
    
        TRY.
            lo_avas->if_cls_attr_value_assignment~lock_and_refresh( im_allow_popups = abap_false ).
          CATCH cx_pak_invalid_state
              cx_pak_invalid_data
              cx_pak_not_authorized
              cx_pak_wb_object_locked.
            zcx_abapgit_exception=>raise( |AVAS error| ).
        ENDTRY.
    
        lo_avas->if_pak_wb_object~delete( ).
    
        lo_avas->if_pak_wb_object~save( ).
    
        lo_avas->if_pak_wb_object_internal~unlock( ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_avas TYPE ty_avas.
    
        io_xml->read( EXPORTING iv_name = 'AVAS'
                      CHANGING cg_data = ls_avas ).
    
    * The AVAS API cannot be used in this case, as it will always create a new GUID
    
        ASSERT NOT ls_avas-header-guid IS INITIAL.
    
        tadir_insert( iv_package ).
    
        corr_insert( iv_package ).
    
        insert_assignments( ls_avas ).
    * todo, how does links work?
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_guid TYPE cls_assignment-guid.
    
        SELECT SINGLE guid FROM cls_assignment INTO lv_guid
          WHERE guid = ms_item-obj_name.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
    
        rs_metadata = get_metadata( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for(
          iv_lock_object = 'CLS_ENQUEUE_STRU'
          iv_argument    = |{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lo_avas TYPE REF TO cl_cls_attr_value_assignment,
              ls_avas TYPE ty_avas.
    
        FIELD-SYMBOLS:  LIKE LINE OF ls_avas-values,
                         LIKE LINE OF ls_avas-links.
    
        lo_avas = instantiate( ).
    
        ls_avas-header-guid      = lo_avas->if_cls_attr_value_assignment~get_guid( ).
        ls_avas-header-attribute = lo_avas->if_cls_attr_value_assignment~get_attribute( ).
        ls_avas-header-object    = lo_avas->if_cls_attr_value_assignment~get_object( ).
    
        lo_avas->if_cls_attr_value_assignment~get_values( IMPORTING ex_values = ls_avas-values ).
    
        lo_avas->if_cls_attr_value_assignment~get_links( IMPORTING ex_links = ls_avas-links ).
    
        LOOP AT ls_avas-values ASSIGNING .
          CLEAR: -set_by, -changed_on.
        ENDLOOP.
    
        LOOP AT ls_avas-links ASSIGNING .
          CLEAR: -set_by, -changed_on.
        ENDLOOP.
    
        io_xml->add(
          iv_name = 'AVAS'
          ig_data = ls_avas ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_bdef IMPLEMENTATION.
    
      METHOD clear_field.
    
        FIELD-SYMBOLS:  TYPE data.
    
        ASSIGN COMPONENT iv_fieldname OF STRUCTURE cs_metadata
               TO .
        ASSERT sy-subrc = 0.
    
        CLEAR: .
    
      ENDMETHOD.
    
      METHOD clear_fields.
    
        FIELD-SYMBOLS:  TYPE ANY TABLE.
        FIELD-SYMBOLS:  TYPE data.
        FIELD-SYMBOLS  TYPE any.
    
        clear_field(
          EXPORTING
            iv_fieldname = 'VERSION'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CREATED_AT'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CREATED_BY'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CHANGED_AT'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CHANGED_BY'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'RESPONSIBLE'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'PACKAGE_REF'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CONTAINER_REF'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'MASTER_SYSTEM'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'MAIN_OBJECT-CHANGED_AT'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'MAIN_OBJECT-CHANGED_BY'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'MAIN_OBJECT-CREATED_AT'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'MAIN_OBJECT-CREATED_BY'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'MAIN_OBJECT-RESPONSIBLE'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'MAIN_OBJECT-PACKAGE_REF'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'MAIN_OBJECT-CONTAINER_REF'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'MAIN_OBJECT-MASTER_SYSTEM'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'SYNTAX_CONFIGURATION'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        ASSIGN COMPONENT 'LINKS' OF STRUCTURE cs_metadata TO .
        ASSERT sy-subrc = 0.
    
        LOOP AT  ASSIGNING .
          ASSIGN COMPONENT 'COMMON_ATTRIBUTES' OF STRUCTURE  TO .
          ASSERT sy-subrc = 0.
          CLEAR: .
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_behaviour_definition_key = ms_item-obj_name.
    
        TRY.
            CREATE DATA mr_behaviour_definition TYPE ('CL_BLUE_SOURCE_OBJECT_DATA=>TY_OBJECT_DATA').
            CREATE OBJECT mi_persistence TYPE ('CL_BDEF_OBJECT_PERSIST').
    
          CATCH cx_sy_create_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get_object_data.
    
        DATA:
          lr_metadata TYPE REF TO data,
          lr_data     TYPE REF TO data.
    
        FIELD-SYMBOLS:
           TYPE uccheck,
                   TYPE any,
                        TYPE any,
                          TYPE any,
                            TYPE any.
    
        CREATE DATA lr_data TYPE ('CL_BLUE_SOURCE_OBJECT_DATA=>TY_OBJECT_DATA').
        ASSIGN lr_data->* TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN COMPONENT 'METADATA' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
    
        CREATE DATA lr_metadata  TYPE ('CL_BLUE_SOURCE_OBJECT_DATA=>TY_OBJECT_DATA-METADATA').
        ASSIGN lr_metadata->* TO .
        ASSERT sy-subrc = 0.
    
        io_xml->read(
          EXPORTING
            iv_name = 'BDEF'
          CHANGING
            cg_data =  ).
    
        ASSIGN COMPONENT 'ABAP_LANGU_VERSION' OF STRUCTURE  TO .
        IF sy-subrc = 0.
          set_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
    
         = .
    
        ASSIGN COMPONENT 'CONTENT-SOURCE' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
    
         = mo_files->read_string( 'asbdef' ).
    
        CREATE OBJECT ro_object_data TYPE ('CL_BLUE_SOURCE_OBJECT_DATA').
    
        ro_object_data->set_data( p_data =  ).
    
      ENDMETHOD.
    
      METHOD get_wb_object_operator.
    
        DATA:
          ls_object_type TYPE wbobjtype,
          lx_error       TYPE REF TO cx_root.
    
        IF mi_wb_object_operator IS BOUND.
          ri_wb_object_operator = mi_wb_object_operator.
        ENDIF.
    
        ls_object_type-objtype_tr = 'BDEF'.
        ls_object_type-subtype_wb = 'BDO'.
    
        TRY.
            CALL METHOD ('CL_WB_OBJECT_OPERATOR')=>('CREATE_INSTANCE')
              EXPORTING
                object_type = ls_object_type
                object_key  = mv_behaviour_definition_key
              RECEIVING
                result      = mi_wb_object_operator.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        ri_wb_object_operator = mi_wb_object_operator.
    
      ENDMETHOD.
    
      METHOD merge_object_data.
    
        DATA:
          lo_object_data        TYPE REF TO object,
          lo_object_data_old    TYPE REF TO if_wb_object_data_model,
          lr_new                TYPE REF TO data,
          lr_old                TYPE REF TO data,
          lo_wb_object_operator TYPE REF TO object.
    
        FIELD-SYMBOLS:
                 TYPE any,
                 TYPE any,
           TYPE any,
           TYPE any.
    
        CREATE OBJECT lo_object_data TYPE ('CL_BLUE_SOURCE_OBJECT_DATA').
        lo_object_data = io_object_data.
    
        CREATE DATA lr_new TYPE ('CL_BLUE_SOURCE_OBJECT_DATA=>TY_OBJECT_DATA').
        ASSIGN lr_new->* TO .
        ASSERT sy-subrc = 0.
    
        CREATE DATA lr_old TYPE ('CL_BLUE_SOURCE_OBJECT_DATA=>TY_OBJECT_DATA').
        ASSIGN lr_old->* TO .
        ASSERT sy-subrc = 0.
    
        CALL METHOD lo_object_data->('IF_WB_OBJECT_DATA_MODEL~GET_DATA')
          EXPORTING
            p_metadata_only  = abap_false
            p_data_selection = 'AL'
          IMPORTING
            p_data           = .
    
        lo_wb_object_operator = get_wb_object_operator( ).
    
        CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
          EXPORTING
            data_selection = 'AL' " if_wb_object_data_selection_co=>c_all_data
          IMPORTING
            eo_object_data = lo_object_data_old.
    
        CALL METHOD lo_object_data_old->('GET_DATA')
          EXPORTING
            p_metadata_only  = abap_false
            p_data_selection = 'AL' " if_wb_object_data_selection_co=>c_all_data
          IMPORTING
            p_data           = .
    
        ASSIGN COMPONENT 'METADATA-DESCRIPTION' OF STRUCTURE  TO .
        ASSIGN COMPONENT 'METADATA-DESCRIPTION' OF STRUCTURE  TO .
         = .
    
        ASSIGN COMPONENT 'CONTENT-SOURCE' OF STRUCTURE  TO .
        ASSIGN COMPONENT 'CONTENT-SOURCE' OF STRUCTURE  TO .
         = .
    
        CREATE OBJECT ro_object_data_merged TYPE ('CL_BLUE_SOURCE_OBJECT_DATA').
    
        CALL METHOD ro_object_data_merged->('SET_DATA')
          EXPORTING
            p_data = .
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA:
          li_object_data_model  TYPE REF TO if_wb_object_data_model,
          li_wb_object_operator TYPE REF TO object,
          lx_error              TYPE REF TO cx_root.
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        TRY.
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
              IMPORTING
                eo_object_data = li_object_data_model.
    
            rv_user = li_object_data_model->get_changed_by( ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA:
          lx_error              TYPE REF TO cx_root,
          li_wb_object_operator TYPE REF TO object.
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        TRY.
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~DELETE')
              EXPORTING
                transport_request = iv_transport.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA:
          lo_object_data        TYPE REF TO if_wb_object_data_model,
          lo_object_data_merged TYPE REF TO if_wb_object_data_model,
          lo_wb_object_operator TYPE REF TO object,
          lx_error              TYPE REF TO cx_root,
          lr_wbobjtype          TYPE REF TO data,
          lr_category           TYPE REF TO data.
    
        FIELD-SYMBOLS:
           TYPE any,
            TYPE any,
               TYPE any.
    
        TRY.
    
            lo_object_data = get_object_data( io_xml ).
    
            CREATE DATA lr_wbobjtype TYPE ('WBOBJTYPE').
            ASSIGN lr_wbobjtype->* TO .
            ASSIGN COMPONENT 'OBJTYPE_TR' OF STRUCTURE  TO .
             = 'BDEF'.
            ASSIGN COMPONENT 'SUBTYPE_WB' OF STRUCTURE  TO .
             = 'BDO'.
    
            CREATE DATA lr_category TYPE ('WBADT_RESOURCE_CATEGORY').
            ASSIGN lr_category->* TO .
    
            CALL METHOD ('CL_BLUE_WB_UTILITY')=>('GET_RESOURCE_CATEGORY')
              EXPORTING
                is_object_type = 
              RECEIVING
                result         = .
    
            lo_wb_object_operator = get_wb_object_operator( ).
    
            tadir_insert( iv_package ).
    
            IF zif_abapgit_object~exists( ) = abap_false.
              CASE .
                WHEN '1'. "if_wb_adt_plugin_resource_co=>co_sfs_res_category_atomic.
                  CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~CREATE')
                    EXPORTING
                      io_object_data    = lo_object_data
                      data_selection    = 'AL' " if_wb_object_data_selection_co=>c_all_data
                      version           = 'I'
                      package           = iv_package
                      transport_request = iv_transport.
                WHEN '2'. "if_wb_adt_plugin_resource_co=>co_sfs_res_category_compound_s.
                  CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~CREATE')
                    EXPORTING
                      io_object_data    = lo_object_data
                      data_selection    = 'P' " if_wb_object_data_selection_co=>c_properties
                      version           = 'I'
                      package           = iv_package
                      transport_request = iv_transport.
    
                  CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
                    EXPORTING
                      io_object_data    = lo_object_data
                      data_selection    = 'D' "if_wb_object_data_selection_co=>c_data_content
                      version           = 'I'
                      transport_request = iv_transport.
                WHEN OTHERS.
              ENDCASE.
            ELSE.
              lo_object_data_merged = merge_object_data( lo_object_data ).
              CASE .
                WHEN '1'. "if_wb_adt_plugin_resource_co=>co_sfs_res_category_atomic.
                  CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
                    EXPORTING
                      io_object_data    = lo_object_data_merged
                      data_selection    = 'AL' "if_wb_object_data_selection_co=>c_all_data
                      version           = 'I'
                      transport_request = iv_transport.
                WHEN '2'. "if_wb_adt_plugin_resource_co=>co_sfs_res_category_compound_s.
                  CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
                    EXPORTING
                      io_object_data    = lo_object_data_merged
                      data_selection    = 'P' "if_wb_object_data_selection_co=>c_properties
                      version           = 'I'
                      transport_request = iv_transport.
                  CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
                    EXPORTING
                      io_object_data    = lo_object_data_merged
                      data_selection    = 'D' "if_wb_object_data_selection_co=>c_data_content
                      version           = 'I'
                      transport_request = iv_transport.
                WHEN OTHERS.
              ENDCASE.
            ENDIF.
    
            corr_insert( iv_package ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        TRY.
            mi_persistence->get(
              p_object_key           = mv_behaviour_definition_key
              p_version              = 'A'
              p_existence_check_only = abap_true ).
            rv_bool = abap_true.
    
          CATCH cx_swb_exception.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESDIC'
                                                iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          li_object_data_model  TYPE REF TO if_wb_object_data_model,
          li_wb_object_operator TYPE REF TO object,
          lx_error              TYPE REF TO cx_root,
          lv_source             TYPE string.
    
        FIELD-SYMBOLS:
           TYPE uccheck,
            TYPE any,
                        TYPE any,
                          TYPE string.
    
        ASSIGN mr_behaviour_definition->* TO .
        ASSERT sy-subrc = 0.
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        TRY.
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
              EXPORTING
                version        = 'A'
              IMPORTING
                data           = 
                eo_object_data = li_object_data_model.
    
            ASSIGN COMPONENT 'METADATA' OF STRUCTURE  TO .
            ASSERT sy-subrc = 0.
            clear_fields( CHANGING cs_metadata =  ).
    
            ASSIGN COMPONENT 'ABAP_LANGU_VERSION' OF STRUCTURE  TO .
            IF sy-subrc = 0.
              clear_abap_language_version( CHANGING cv_abap_language_version =  ).
            ENDIF.
    
            ASSIGN COMPONENT 'CONTENT-SOURCE' OF STRUCTURE  TO .
            ASSERT sy-subrc = 0.
            lv_source = .
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        io_xml->add(
          iv_name = 'BDEF'
          ig_data =  ).
    
        mo_files->add_string(
          iv_ext    = 'asbdef'
          iv_string = lv_source ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_bgqc IMPLEMENTATION.
      METHOD zif_abapgit_object~changed_by.
        DATA lo_bgqc_wbi_p    TYPE REF TO object.
        DATA lv_ref_bgqc_name TYPE REF TO data.
        DATA lv_changed_by    TYPE syuname.
        DATA lv_subrc         TYPE sy-subrc.
        DATA lx_root          TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:  TYPE any.
    
        TRY.
    
            CREATE DATA lv_ref_bgqc_name TYPE (c_bgqc_name).
            ASSIGN lv_ref_bgqc_name->* TO .
            ASSERT sy-subrc = 0.
    
            me->ms_item-obj_name = .
    
            CREATE OBJECT lo_bgqc_wbi_p TYPE (c_bgqc_wbi_p).
    
            CALL METHOD lo_bgqc_wbi_p->(c_select_changed_by)
              EXPORTING
                iv_bgqc_name  = 
              IMPORTING
                ev_changed_by = lv_changed_by
                ev_subrc      = lv_subrc.
    
            IF lv_subrc <> 0.
              zcx_abapgit_exception=>raise_t100( ).
            ENDIF.
    
            rv_user = lv_changed_by.
    
          CATCH cx_root INTO lx_root.
    
            zcx_abapgit_exception=>raise_with_text( lx_root ).
    
        ENDTRY.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_cdbo IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor( is_item        = is_item
                            iv_language    = iv_language
                            io_files       = io_files
                            io_i18n_params = io_i18n_params ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lv_user  TYPE string,
              lx_error TYPE REF TO cx_root.
    
        TRY.
            SELECT SINGLE changed_by FROM (c_table_name) INTO lv_user WHERE obj_name = ms_item-obj_name.
            IF lv_user IS INITIAL.
              SELECT SINGLE created_by FROM (c_table_name) INTO lv_user WHERE obj_name = ms_item-obj_name.
            ENDIF.
            rv_user = lv_user.
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_OBJECT_CHAR IMPLEMENTATION.
    
      METHOD instantiate_char_and_lock.
    
        DATA: lv_new  TYPE abap_bool,
              lv_name TYPE cls_attribute_name.
    
        SELECT SINGLE name FROM cls_attribute INTO lv_name WHERE name = ms_item-obj_name.
        lv_new = boolc( sy-subrc <> 0 ).
        lv_name = ms_item-obj_name.
    
        TRY.
            CREATE OBJECT ro_char
              EXPORTING
                im_name             = lv_name
                im_type_group       = iv_type_group
                im_new              = lv_new
                im_activation_state = iv_activation_state.
          CATCH cx_pak_invalid_data
              cx_pak_not_authorized
              cx_pak_invalid_state
              cx_pak_wb_object_locked.
            zcx_abapgit_exception=>raise( 'Error while instantiating CL_CLS_ATTRIBUTE' ).
        ENDTRY.
    
        IF lv_new = abap_false.
          TRY.
              ro_char->if_pak_wb_object~lock_and_refresh( ).
            CATCH cx_pak_invalid_data
                cx_pak_not_authorized
                cx_pak_invalid_state
                cx_pak_wb_object_locked.
              zcx_abapgit_exception=>raise( |Could not acquire lock, CHAR { lv_name }| ).
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE changed_by FROM cls_attribute INTO rv_user
          WHERE name = ms_item-obj_name
          AND activation_state = 'A'.
    
        IF rv_user IS INITIAL.
          SELECT SINGLE created_by FROM cls_attribute INTO rv_user
            WHERE name = ms_item-obj_name
            AND activation_state = 'A'.
        ENDIF.
    
        IF rv_user IS INITIAL.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lo_char       TYPE REF TO cl_cls_attribute,
              lv_type_group TYPE cls_attribute-type_group,
              lx_pak_error  TYPE REF TO cx_root,
              lv_text       TYPE string.
    
        SELECT SINGLE type_group FROM cls_attribute INTO lv_type_group
          WHERE name = ms_item-obj_name
          AND activation_state = 'A'.
    
        lo_char = instantiate_char_and_lock( iv_type_group       = lv_type_group
                                             iv_activation_state = cl_pak_wb_domains=>co_activation_state-active ).
    
        TRY.
            lo_char->if_pak_wb_object~delete( ).
    
            lo_char->if_pak_wb_object~save( ).
    
            lo_char->if_pak_wb_object_internal~unlock( ).
    
          CATCH cx_pak_invalid_state cx_pak_invalid_data cx_pak_not_authorized INTO lx_pak_error.
            lo_char->if_pak_wb_object_internal~unlock( ).
            lv_text = lx_pak_error->get_text( ).
            zcx_abapgit_exception=>raise( lv_text ).
          CLEANUP.
            lo_char->if_pak_wb_object_internal~unlock( ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_char        TYPE ty_char,
              ls_description LIKE LINE OF ls_char-cls_attributet,
              lo_char        TYPE REF TO cl_cls_attribute,
              lx_pak_error   TYPE REF TO cx_root,
              lv_text        TYPE string.
    
        FIELD-SYMBOLS:   LIKE LINE OF ls_char-cls_attr_value,
                           TYPE any,
                        LIKE LINE OF ls_char-cls_attr_valuet.
    
        io_xml->read( EXPORTING iv_name = 'CHAR'
                      CHANGING cg_data = ls_char ).
    
        tadir_insert( iv_package ).
    
        lo_char = instantiate_char_and_lock( iv_type_group       = ls_char-cls_attribute-type_group
                                             iv_activation_state = cl_pak_wb_domains=>co_activation_state-inactive ).
    
        TRY.
            lo_char->if_cls_attribute~set_kind( ls_char-cls_attribute-kind ).
            lo_char->if_cls_attribute~set_single_valued( ls_char-cls_attribute-is_single_valued ).
            lo_char->if_cls_attribute~set_aspect(
              im_aspect_for   = ls_char-cls_attribute-is_aspect_for
              im_aspect_value = ls_char-cls_attribute-aspect_value ).
            lo_char->if_cls_attribute~set_default_flag( ls_char-cls_attribute-default_flag ).
            lo_char->if_cls_attribute~set_default_value( ls_char-cls_attribute-default_value ).
            lo_char->if_cls_attribute~set_sub_object_treatment( ls_char-cls_attribute-sub_obj_treatm ).
            lo_char->if_cls_attribute~set_automatic_changes_allowed( ls_char-cls_attribute-automatic_change ).
            lo_char->if_cls_attribute~set_manual_changes_allowed( ls_char-cls_attribute-manu_chag_allow ).
            lo_char->if_cls_attribute~set_implicit_changes_allowed( ls_char-cls_attribute-implicit_change ).
            lo_char->if_cls_attribute~set_expl_values_dominate_links( ls_char-cls_attribute-weak_links ).
            lo_char->if_cls_attribute~set_assignment_package_rule( ls_char-cls_attribute-assignment_devc ).
    
    * Method SET_HIDE_ICON does not exist in some releases, not present in 751
            ASSIGN COMPONENT 'HIDE_ICONS' OF STRUCTURE ls_char-cls_attribute TO .
            IF sy-subrc = 0.
              CALL METHOD lo_char->('IF_CLS_ATTRIBUTE~SET_HIDE_ICON')
                EXPORTING
                  im_hide_icon = .
            ENDIF.
    
            lo_char->if_cls_attribute~set_hide_remark( ls_char-cls_attribute-hide_remark ).
            lo_char->if_cls_attribute~set_visible_in_customer_system( ls_char-cls_attribute-visible_for_cust ).
            lo_char->if_cls_attribute~set_value_table( ls_char-cls_attribute-value_table ).
            lo_char->if_cls_attribute~set_vtable_field( ls_char-cls_attribute-vtable_field ).
            lo_char->if_cls_attribute~set_vtable_icon_f( ls_char-cls_attribute-vtable_icon_f ).
            lo_char->if_cls_attribute~set_vtext_langu_f( ls_char-cls_attribute-vtext_langu_f ).
            lo_char->if_cls_attribute~set_vtext_table( ls_char-cls_attribute-vtext_table ).
            lo_char->if_cls_attribute~set_vtext_text_f( ls_char-cls_attribute-vtext_text_f ).
            lo_char->if_cls_attribute~set_vtext_value_f( ls_char-cls_attribute-vtext_value_f ).
            lo_char->if_cls_attribute~set_existing_objects_only( ls_char-cls_attribute-existing_objects ).
            lo_char->if_cls_attribute~set_objs_of_typegr( ls_char-cls_attribute-objs_of_typegr ).
            lo_char->if_cls_attribute~set_obj_values_have_subtypes( ls_char-cls_attribute-objs_w_subtype ).
            lo_char->if_cls_attribute~set_arbtry_val_type( ls_char-cls_attribute-arbtry_val_type ).
    
            READ TABLE ls_char-cls_attributet INTO ls_description WITH KEY langu = mv_language.
            IF sy-subrc <> 0.
              READ TABLE ls_char-cls_attributet INTO ls_description INDEX 1.
            ENDIF.
            lo_char->if_cls_attribute~set_description( ls_description-text ).
    
            LOOP AT ls_char-cls_attr_value ASSIGNING .
              -activation_state = 'I'.
            ENDLOOP.
            LOOP AT ls_char-cls_attr_valuet ASSIGNING .
              -activation_state = 'I'.
            ENDLOOP.
    
            lo_char->if_cls_attribute~set_values(
              im_values   = ls_char-cls_attr_value
              im_values_t = ls_char-cls_attr_valuet ).
    
            set_default_package( iv_package ).
    
            lo_char->if_pak_wb_object~save( ).
    
            lo_char->if_pak_wb_object~activate( ).
    
            lo_char->if_pak_wb_object_internal~unlock( ).
    
          CATCH cx_pak_invalid_state cx_pak_invalid_data cx_pak_not_authorized INTO lx_pak_error.
            lo_char->if_pak_wb_object_internal~unlock( ).
            lv_text = lx_pak_error->get_text( ).
            zcx_abapgit_exception=>raise( lv_text ).
          CLEANUP.
            lo_char->if_pak_wb_object_internal~unlock( ).
        ENDTRY.
    
        deserialize_longtexts( ii_xml         = io_xml
                               iv_longtext_id = c_longtext_id_char ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = cl_cls_attribute=>exists_object_attribute( ms_item-obj_name ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ECLS_ATTRIBUTE'
                                                iv_argument    = |{ ms_item-obj_name }*| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_char TYPE ty_char.
    
        CONSTANTS: lc_active TYPE c LENGTH 1 VALUE 'A'.
    
        SELECT SINGLE * FROM cls_attribute INTO ls_char-cls_attribute
          WHERE name = ms_item-obj_name
          AND activation_state = lc_active.
    * todo, ASSIGNMENT_DEVC?
    
        CLEAR: ls_char-cls_attribute-created_by,
               ls_char-cls_attribute-created_on,
               ls_char-cls_attribute-changed_by,
               ls_char-cls_attribute-changed_on.
    
        SELECT * FROM cls_attributet INTO TABLE ls_char-cls_attributet
          WHERE name = ms_item-obj_name
          AND activation_state = lc_active
          ORDER BY PRIMARY KEY.
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          DELETE ls_char-cls_attributet WHERE langu <> mv_language.
        ENDIF.
    
        SELECT * FROM cls_attr_value INTO TABLE ls_char-cls_attr_value
          WHERE name = ms_item-obj_name
          AND activation_state = lc_active
          ORDER BY PRIMARY KEY.
    
        SELECT * FROM cls_attr_valuet INTO TABLE ls_char-cls_attr_valuet
          WHERE name = ms_item-obj_name
          AND activation_state = lc_active
          ORDER BY PRIMARY KEY.
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          DELETE ls_char-cls_attr_valuet WHERE langu <> mv_language.
        ENDIF.
    
        io_xml->add( iv_name = 'CHAR'
                     ig_data = ls_char ).
    
        serialize_longtexts( ii_xml         = io_xml
                             iv_longtext_id = c_longtext_id_char ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_chdo IMPLEMENTATION.
    
      METHOD after_import.
    
        DATA: lt_cts_object_entry TYPE STANDARD TABLE OF e071 WITH DEFAULT KEY,
              ls_cts_object_entry LIKE LINE OF lt_cts_object_entry,
              lt_errormsg         TYPE STANDARD TABLE OF sprot_u WITH DEFAULT KEY.
    
        ls_cts_object_entry-pgmid    = 'R3TR'.
        ls_cts_object_entry-object   = ms_item-obj_type.
        ls_cts_object_entry-obj_name = ms_item-obj_name.
        INSERT ls_cts_object_entry INTO TABLE lt_cts_object_entry.
    
        CALL FUNCTION 'AFTER_IMP_CHDO'
          EXPORTING
            iv_tarclient  = sy-mandt
            iv_is_upgrade = abap_false
          TABLES
            tt_e071       = lt_cts_object_entry
            tt_errormsg   = lt_errormsg.
    
        LOOP AT lt_errormsg TRANSPORTING NO FIELDS WHERE severity = 'E' OR severity = 'A'.
          EXIT.
        ENDLOOP.
    
        IF sy-subrc = 0.
          zcx_abapgit_exception=>raise( 'Error from AFTER_IMP_CHDO' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_object = is_item-obj_name.
    
      ENDMETHOD.
    
      METHOD delete_tadir_cdnames.
    
        IF is_cdnames-repnamec IS NOT INITIAL AND is_cdnames-repnamec NS c_class_gen_marker.
          zcl_abapgit_factory=>get_tadir( )->delete_single(
            iv_object    = 'PROG'
            iv_obj_name  = is_cdnames-repnamec ).
        ENDIF.
    
        IF is_cdnames-repnamet IS NOT INITIAL AND is_cdnames-repnamet NS c_class_gen_marker.
          zcl_abapgit_factory=>get_tadir( )->delete_single(
            iv_object    = 'PROG'
            iv_obj_name  = is_cdnames-repnamet ).
        ENDIF.
    
        IF is_cdnames-repnamefix IS NOT INITIAL AND is_cdnames-repnamefix NS c_class_gen_marker.
          zcl_abapgit_factory=>get_tadir( )->delete_single(
            iv_object    = 'PROG'
            iv_obj_name  = is_cdnames-repnamefix ).
        ENDIF.
    
        IF is_cdnames-repnamevar IS NOT INITIAL AND is_cdnames-repnamevar NS c_class_gen_marker.
          zcl_abapgit_factory=>get_tadir( )->delete_single(
            iv_object    = 'PROG'
            iv_obj_name  = is_cdnames-repnamevar ).
        ENDIF.
    
        IF is_cdnames-fgrp IS NOT INITIAL AND is_cdnames-fgrp NS c_class_gen_marker.
          zcl_abapgit_factory=>get_tadir( )->delete_single(
            iv_object    = 'FUGR'
            iv_obj_name  = is_cdnames-fgrp ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD delete_tadir_tabl.
    
        IF is_tcdrs-tabname IS NOT INITIAL AND is_tcdrs-tabname NS c_class_gen_marker.
          zcl_abapgit_factory=>get_tadir( )->delete_single(
            iv_object    = 'TABL'
            iv_obj_name  = is_tcdrs-tabname ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE updname INTO rv_user
          FROM tcdrp
          WHERE object = mv_object.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lt_cdnames TYPE STANDARD TABLE OF cdnames,
              ls_cdnames TYPE cdnames,
              lt_tcdrs   TYPE STANDARD TABLE OF tcdrs,
              ls_tcdrs   TYPE tcdrs,
              lv_msg     TYPE symsgv.
    
        CALL FUNCTION 'CDNAMES_GET'
          EXPORTING
            iv_object        = mv_object
          TABLES
            it_tcdrs         = lt_tcdrs
            it_names         = lt_cdnames
          EXCEPTIONS
            object_space     = 1
            object_not_found = 2
            OTHERS           = 3.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CALL FUNCTION 'CHDO_DELETE'
          EXPORTING
            iv_object        = mv_object
            iv_with_tadir    = abap_true
          EXCEPTIONS
            object_is_space  = 1
            object_not_found = 2
            other_error      = 3
            OTHERS           = 4.
        IF sy-subrc <> 0.
          lv_msg = mv_object.
          zcx_abapgit_exception=>raise_t100( iv_msgid = 'CD'
                                             iv_msgno = '869'
                                             iv_msgv1 = lv_msg ).
        ENDIF.
    
        LOOP AT lt_cdnames INTO ls_cdnames.
          delete_tadir_cdnames( ls_cdnames ).
        ENDLOOP.
    
        LOOP AT lt_tcdrs INTO ls_tcdrs.
          delete_tadir_tabl( ls_tcdrs ).
        ENDLOOP.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_change_object TYPE ty_change_document.
        FIELD-SYMBOLS:  LIKE LINE OF ls_change_object-reports_generated.
        FIELD-SYMBOLS  TYPE uccheck.
    
        io_xml->read( EXPORTING iv_name = 'CHDO'
                      CHANGING  cg_data = ls_change_object ).
    
        DELETE FROM tcdobs  WHERE object = mv_object.
        DELETE FROM tcdobts WHERE object = mv_object.
        DELETE FROM tcdrps  WHERE object = mv_object.
    
        LOOP AT ls_change_object-reports_generated ASSIGNING .
          -devclass = iv_package.
    
          ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE  TO .
          IF sy-subrc = 0.
            set_abap_language_version( CHANGING cv_abap_language_version =  ).
          ENDIF.
        ENDLOOP.
    
        INSERT tcdobs  FROM TABLE ls_change_object-objects.
        INSERT tcdobts FROM TABLE ls_change_object-objects_text.
        INSERT tcdrps  FROM TABLE ls_change_object-reports_generated.
    
        tadir_insert( iv_package ).
    
        after_import( ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        SELECT COUNT(*)
          FROM tcdrp
          WHERE object = mv_object.
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: lt_bdcdata TYPE STANDARD TABLE OF bdcdata,
              ls_bdcdata LIKE LINE OF lt_bdcdata.
    
        CLEAR: ls_bdcdata.
        ls_bdcdata-program  = 'SAPMSCDO_NEW'.
        ls_bdcdata-dynpro   = '0100'.
        ls_bdcdata-dynbegin = abap_true.
        APPEND ls_bdcdata TO lt_bdcdata.
    
        CLEAR: ls_bdcdata.
        ls_bdcdata-fnam = 'TCDOB-OBJECT'.
        ls_bdcdata-fval = mv_object.
        APPEND ls_bdcdata TO lt_bdcdata.
    
        CLEAR: ls_bdcdata.
        ls_bdcdata-fnam = 'BDC_OKCODE'.
        ls_bdcdata-fval = '=DISP'.
        APPEND ls_bdcdata TO lt_bdcdata.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'SCDO'
          it_bdcdata = lt_bdcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_change_object TYPE ty_change_document,
              lt_tcdrp         TYPE STANDARD TABLE OF tcdrp,
              lt_tcdob         TYPE STANDARD TABLE OF tcdob,
              lt_tcdobt        TYPE STANDARD TABLE OF tcdobt,
              BEGIN OF ls_nulldatetime, " hack ro reset fields when they exist without syntax errors when they don't
                udate TYPE sy-datum,
                utime TYPE sy-uzeit,
              END OF ls_nulldatetime.
    
        FIELD-SYMBOLS:  LIKE LINE OF ls_change_object-reports_generated,
                                  LIKE LINE OF ls_change_object-objects,
                             LIKE LINE OF ls_change_object-objects_text.
        FIELD-SYMBOLS  TYPE uccheck.
    
        CALL FUNCTION 'CDNAMES_GET'
          EXPORTING
            iv_object        = mv_object
          TABLES
            it_tcdrp         = lt_tcdrp
            it_tcdob         = lt_tcdob
            it_tcdobt        = lt_tcdobt
          EXCEPTIONS
            object_space     = 1
            object_not_found = 2
            OTHERS           = 3.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        ls_change_object-reports_generated = lt_tcdrp.
        ls_change_object-objects           = lt_tcdob.
        ls_change_object-objects_text      = lt_tcdobt.
    
        " At import, when CHDO is generated date & time change, so always detects changes for this fields
        LOOP AT ls_change_object-reports_generated ASSIGNING .
          CLEAR: -datum, -uzeit,
                 -author, -updname,
                 -devclass.
    
          ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE  TO .
          IF sy-subrc = 0.
            clear_abap_language_version( CHANGING cv_abap_language_version =  ).
          ENDIF.
        ENDLOOP.
    
        LOOP AT ls_change_object-objects ASSIGNING .
          MOVE-CORRESPONDING ls_nulldatetime TO . " reset date and time
        ENDLOOP.
    
        LOOP AT ls_change_object-objects_text ASSIGNING .
          MOVE-CORRESPONDING ls_nulldatetime TO . " reset date and time
        ENDLOOP.
    
        io_xml->add( iv_name = 'CHDO'
                     ig_data = ls_change_object ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_OBJECT_CHKC IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lr_data        TYPE REF TO data,
              lo_chkc_db_api TYPE REF TO object,
              lv_name        TYPE c LENGTH 30,
              lx_error       TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:  TYPE any,
                          TYPE any.
    
        TRY.
            CREATE OBJECT lo_chkc_db_api TYPE ('CL_CHKC_DB_API').
            CREATE DATA lr_data TYPE ('CL_CHKC_DB_API=>TY_HEADER').
            ASSIGN lr_data->* TO .
    
            lv_name = ms_item-obj_name.
    
            CALL METHOD lo_chkc_db_api->('GET_HEADER')
              EXPORTING
                name    = lv_name
                version = 'I'
              RECEIVING
                header  = .
    
            IF  IS INITIAL.
              CALL METHOD lo_chkc_db_api->('GET_HEADER')
                EXPORTING
                  name    = lv_name
                  version = 'A'
                RECEIVING
                  header  = .
            ENDIF.
    
            ASSIGN COMPONENT 'CHANGED_BY' OF STRUCTURE  TO .
            rv_user = .
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_OBJECT_CHKO IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lr_data        TYPE REF TO data,
              lo_chko_db_api TYPE REF TO object,
              lv_name        TYPE c LENGTH 30,
              lx_error       TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:  TYPE any,
                          TYPE any.
    
        TRY.
            CREATE OBJECT lo_chko_db_api TYPE ('CL_CHKO_DB_API').
            CREATE DATA lr_data TYPE ('CL_CHKO_DB_API=>TY_HEADER').
            ASSIGN lr_data->* TO .
    
            lv_name = ms_item-obj_name.
    
            CALL METHOD lo_chko_db_api->('GET_HEADER')
              EXPORTING
                name    = lv_name
                version = 'I'
              RECEIVING
                header  = .
    
            IF  IS INITIAL.
              CALL METHOD lo_chko_db_api->('GET_HEADER')
                EXPORTING
                  name    = lv_name
                  version = 'A'
                RECEIVING
                  header  = .
            ENDIF.
    
            ASSIGN COMPONENT 'CHANGED_BY' OF STRUCTURE  TO .
            rv_user = .
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_OBJECT_CHKV IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lr_data        TYPE REF TO data,
              lo_chkv_db_api TYPE REF TO object,
              lv_name        TYPE c LENGTH 180,
              lx_error       TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:  TYPE any,
                          TYPE any.
    
        TRY.
            CREATE OBJECT lo_chkv_db_api TYPE ('CL_CHKV_DB_API').
            CREATE DATA lr_data TYPE ('CL_CHKV_DB_API=>TY_HEADER').
            ASSIGN lr_data->* TO .
    
            lv_name = ms_item-obj_name.
    
            CALL METHOD lo_chkv_db_api->('GET_HEADER')
              EXPORTING
                object_key = lv_name
                version    = 'I'
              RECEIVING
                header  = .
    
            IF  IS INITIAL.
              CALL METHOD lo_chkv_db_api->('GET_HEADER')
                EXPORTING
                  object_key = lv_name
                  version    = 'A'
                RECEIVING
                  header  = .
            ENDIF.
    
            ASSIGN COMPONENT 'CHANGED_BY' OF STRUCTURE  TO .
            rv_user = .
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_clas IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mi_object_oriented_object_fct = zcl_abapgit_oo_factory=>get_by_type( ms_item-obj_type ).
    
        mv_classpool_name = cl_oo_classname_service=>get_classpool_name( |{ is_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD deserialize_abap.
    
        DATA: ls_vseoclass             TYPE vseoclass,
              lt_source                TYPE seop_source_string,
              lt_local_definitions     TYPE seop_source_string,
              lt_local_implementations TYPE seop_source_string,
              lt_local_macros          TYPE seop_source_string,
              lt_test_classes          TYPE seop_source_string,
              ls_class_key             TYPE seoclskey,
              lt_attributes            TYPE zif_abapgit_oo_object_fnc=>ty_obj_attribute_tt.
    
        lt_source = mo_files->read_abap( ).
    
        lt_local_definitions = mo_files->read_abap(
          iv_extra = zif_abapgit_oo_object_fnc=>c_parts-locals_def
          iv_error = abap_false ).
    
        lt_local_implementations = mo_files->read_abap(
          iv_extra = zif_abapgit_oo_object_fnc=>c_parts-locals_imp
          iv_error = abap_false ).
    
        lt_local_macros = mo_files->read_abap(
          iv_extra = zif_abapgit_oo_object_fnc=>c_parts-macros
          iv_error = abap_false ).
    
        lt_test_classes = mo_files->read_abap(
          iv_extra = zif_abapgit_oo_object_fnc=>c_parts-testclasses
          iv_error = abap_false ).
    
        ls_class_key-clsname = ms_item-obj_name.
    
        ii_xml->read( EXPORTING iv_name = 'VSEOCLASS'
                      CHANGING  cg_data = ls_vseoclass ).
    
        set_abap_language_version( CHANGING cv_abap_language_version = ls_vseoclass-unicode ).
    
        ii_xml->read( EXPORTING iv_name = 'ATTRIBUTES'
                      CHANGING  cg_data = lt_attributes ).
    
        " Remove code for test classes if they have been deleted
        IF ls_vseoclass-with_unit_tests = abap_false.
          CLEAR lt_test_classes.
        ENDIF.
    
        mi_object_oriented_object_fct->create(
          EXPORTING
            iv_check      = abap_true
            iv_package    = iv_package
            it_attributes = lt_attributes
          CHANGING
            cg_properties = ls_vseoclass ).
    
        mi_object_oriented_object_fct->generate_locals(
          is_key                   = ls_class_key
          iv_package               = iv_package
          iv_version               = ls_vseoclass-unicode
          it_local_definitions     = lt_local_definitions
          it_local_implementations = lt_local_implementations
          it_local_macros          = lt_local_macros
          it_local_test_classes    = lt_test_classes ).
    
        repo_apack_replacement( CHANGING ct_source = lt_source ).
    
        mi_object_oriented_object_fct->deserialize_source(
          is_key     = ls_class_key
          iv_package = iv_package
          iv_version = ls_vseoclass-unicode
          it_source  = lt_source ).
    
      ENDMETHOD.
    
      METHOD deserialize_descr.
    
        DATA:
          ls_class_key          TYPE seoclskey,
          lt_descriptions_class TYPE zif_abapgit_oo_object_fnc=>ty_seoclasstx_tt,
          lt_descriptions_compo TYPE zif_abapgit_oo_object_fnc=>ty_seocompotx_tt,
          lt_descriptions_subco TYPE zif_abapgit_oo_object_fnc=>ty_seosubcotx_tt.
    
        ls_class_key-clsname = ms_item-obj_name.
    
        ii_xml->read( EXPORTING iv_name = 'DESCRIPTIONS_CLASS'
                      CHANGING  cg_data = lt_descriptions_class ).
    
        mi_object_oriented_object_fct->update_descriptions_class(
          is_key          = ls_class_key
          iv_language     = mv_language
          it_descriptions = lt_descriptions_class ).
    
        ii_xml->read( EXPORTING iv_name = 'DESCRIPTIONS'
                      CHANGING  cg_data = lt_descriptions_compo ).
    
        mi_object_oriented_object_fct->update_descriptions_compo(
          is_key          = ls_class_key
          it_descriptions = lt_descriptions_compo ).
    
        ii_xml->read( EXPORTING iv_name = 'DESCRIPTIONS_SUB'
                      CHANGING  cg_data = lt_descriptions_subco ).
    
        mi_object_oriented_object_fct->update_descriptions_subco(
          is_key          = ls_class_key
          it_descriptions = lt_descriptions_subco ).
    
      ENDMETHOD.
    
      METHOD deserialize_docu.
    
        DATA: lt_lines      TYPE tlinetab,
              lv_object     TYPE dokhl-object,
              lt_i18n_lines TYPE zif_abapgit_lang_definitions=>ty_i18n_lines,
              ls_i18n_lines TYPE zif_abapgit_lang_definitions=>ty_i18n_line.
    
        ii_xml->read( EXPORTING iv_name = 'LINES'
                      CHANGING cg_data = lt_lines ).
    
        lv_object = ms_item-obj_name.
    
        IF lines( lt_lines ) = 0.
          mi_object_oriented_object_fct->delete_documentation(
            iv_id          = c_longtext_id-class
            iv_object_name = lv_object
            iv_language    = mv_language ).
        ELSE.
          mi_object_oriented_object_fct->create_documentation(
            it_lines       = lt_lines
            iv_id          = c_longtext_id-class
            iv_object_name = lv_object
            iv_language    = mv_language ).
    
          ii_xml->read( EXPORTING iv_name = 'I18N_LINES'
                        CHANGING cg_data = lt_i18n_lines ).
    
          LOOP AT lt_i18n_lines INTO ls_i18n_lines.
            mi_object_oriented_object_fct->create_documentation(
              it_lines         = ls_i18n_lines-lines
              iv_id            = c_longtext_id-class
              iv_object_name   = lv_object
              iv_language      = ls_i18n_lines-language
              iv_no_masterlang = abap_true ).
          ENDLOOP.
        ENDIF.
    
        deserialize_longtexts(
          ii_xml           = ii_xml
          iv_longtext_name = c_longtext_name-attributes
          iv_longtext_id   = c_longtext_id-attributes ).
    
        deserialize_longtexts(
          ii_xml           = ii_xml
          iv_longtext_name = c_longtext_name-methods
          iv_longtext_id   = c_longtext_id-methods ).
    
        deserialize_longtexts(
          ii_xml           = ii_xml
          iv_longtext_name = c_longtext_name-events
          iv_longtext_id   = c_longtext_id-events ).
    
        deserialize_longtexts(
          ii_xml           = ii_xml
          iv_longtext_name = c_longtext_name-types
          iv_longtext_id   = c_longtext_id-types ).
    
      ENDMETHOD.
    
      METHOD deserialize_exceptions.
    
        DATA: ls_vseoclass TYPE vseoclass.
    
        ii_xml->read( EXPORTING iv_name = 'VSEOCLASS'
                      CHANGING  cg_data = ls_vseoclass ).
    
        " For exceptions that are sub-class of another exception, we need to set the category explicitly (#6490)
        IF ls_vseoclass-category = '40'.
          UPDATE seoclassdf SET category = '40' WHERE clsname = ls_vseoclass-clsname.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD deserialize_pre_ddic.
    
        DATA: ls_vseoclass TYPE vseoclass.
    
        ii_xml->read( EXPORTING iv_name = 'VSEOCLASS'
                      CHANGING  cg_data = ls_vseoclass ).
    
        set_abap_language_version( CHANGING cv_abap_language_version = ls_vseoclass-unicode ).
    
        IF ls_vseoclass-category = '40'.
          " In lower releases, creating exception classes raise a popup asking for package
          " To avoid this, we set the default package here
          set_default_package( iv_package ).
        ENDIF.
    
        mi_object_oriented_object_fct->create(
          EXPORTING
            iv_check      = abap_false
            iv_package    = iv_package
          CHANGING
            cg_properties = ls_vseoclass ).
    
      ENDMETHOD.
    
      METHOD deserialize_sotr.
        "OTR stands for Online Text Repository
        mi_object_oriented_object_fct->create_sotr(
          iv_object_name = ms_item-obj_name
          iv_package     = iv_package
          ii_xml         = ii_xml ).
      ENDMETHOD.
    
      METHOD deserialize_tpool.
    
        DATA: lv_clsname   TYPE seoclsname,
              lt_tpool_ext TYPE zif_abapgit_lang_definitions=>ty_tpool_tt,
              lt_tpool     TYPE textpool_table.
    
        ii_xml->read( EXPORTING iv_name = 'TPOOL'
                      CHANGING cg_data = lt_tpool_ext ).
        lt_tpool = read_tpool( lt_tpool_ext ).
    
        IF lines( lt_tpool ) = 0.
          RETURN.
        ENDIF.
    
        lv_clsname = ms_item-obj_name.
    
        mi_object_oriented_object_fct->insert_text_pool(
          iv_class_name = lv_clsname
          it_text_pool  = lt_tpool
          iv_language   = mv_language ).
    
      ENDMETHOD.
    
      METHOD deserialize_tpool_i18n.
    
        DATA: lv_clsname    TYPE seoclsname,
              lt_tpool      TYPE textpool_table,
              lt_i18n_tpool TYPE zif_abapgit_lang_definitions=>ty_i18n_tpools,
              ls_i18n_tpool TYPE zif_abapgit_lang_definitions=>ty_i18n_tpool.
    
        lv_clsname = ms_item-obj_name.
    
        ii_xml->read( EXPORTING iv_name = 'I18N_TPOOL'
                      CHANGING  cg_data = lt_i18n_tpool ).
    
        mo_i18n_params->trim_saplang_keyed_table(
          EXPORTING
            iv_lang_field_name = 'LANGUAGE'
          CHANGING
            ct_tab = lt_i18n_tpool ).
    
        LOOP AT lt_i18n_tpool INTO ls_i18n_tpool.
          lt_tpool = read_tpool( ls_i18n_tpool-textpool ).
          mi_object_oriented_object_fct->insert_text_pool(
            iv_class_name = lv_clsname
            it_text_pool  = lt_tpool
            iv_language   = ls_i18n_tpool-language
            iv_state      = 'A' ).
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD interface_replacement.
    
        DATA lv_tabix TYPE sy-tabix.
    
        FIELD-SYMBOLS  LIKE LINE OF ct_source.
    
        FIND REGEX '^\s*INTERFACES(:| )\s*' && iv_from_interface && '\s*.' IN TABLE ct_source MATCH LINE lv_tabix ##REGEX_POSIX.
        IF sy-subrc = 0.
          READ TABLE ct_source ASSIGNING  INDEX lv_tabix.
          ASSERT sy-subrc = 0.
    
          REPLACE FIRST OCCURRENCE OF iv_from_interface IN 
                                 WITH iv_to_interface IGNORING CASE.
    
          REPLACE ALL OCCURRENCES OF iv_from_interface && '~descriptor' IN TABLE ct_source
                                WITH iv_to_interface && '~descriptor' IGNORING CASE.
          REPLACE ALL OCCURRENCES OF iv_from_interface && '=>' IN TABLE ct_source
                                WITH iv_to_interface && '=>' IGNORING CASE.
          REPLACE ALL OCCURRENCES OF iv_from_interface && '->' IN TABLE ct_source
                                WITH iv_to_interface && '->' IGNORING CASE.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD is_class_locked.
    
        DATA: lv_argument TYPE seqg3-garg.
    
        lv_argument = ms_item-obj_name.
        OVERLAY lv_argument WITH '=============================='.
        lv_argument = lv_argument && '*'.
    
        rv_is_class_locked = exists_a_lock_entry_for( iv_lock_object = 'ESEOCLASS'
                                                      iv_argument    = lv_argument ).
    
      ENDMETHOD.
    
      METHOD repo_apack_replacement.
    
        DATA lv_apack TYPE seoclsname.
    
        " Check if SAP-version of APACK manifest exists
        SELECT SINGLE clsname INTO lv_apack
          FROM seoclass
          WHERE clsname = zif_abapgit_apack_definitions=>c_apack_interface_sap.
        IF sy-subrc = 0.
          RETURN.
        ENDIF.
    
        " If not, replace with abapGit version
        interface_replacement(
          EXPORTING
            iv_from_interface = to_lower( zif_abapgit_apack_definitions=>c_apack_interface_sap )
            iv_to_interface   = to_lower( zif_abapgit_apack_definitions=>c_apack_interface_cust )
          CHANGING
            ct_source         = ct_source ).
    
      ENDMETHOD.
    
      METHOD serialize_attr.
    
        DATA: lt_attributes TYPE zif_abapgit_oo_object_fnc=>ty_obj_attribute_tt.
    
        lt_attributes = mi_object_oriented_object_fct->read_attributes( iv_clsname ).
        IF lines( lt_attributes ) = 0.
          RETURN.
        ENDIF.
    
        ii_xml->add( iv_name = 'ATTRIBUTES'
                     ig_data = lt_attributes ).
    
      ENDMETHOD.
    
      METHOD serialize_descr_class.
    
        DATA: lt_descriptions    TYPE zif_abapgit_oo_object_fnc=>ty_seoclasstx_tt,
              lt_language_filter TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        " Main language is already in VSEOCLASS so we serialize only translations
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          RETURN.
        ENDIF.
    
        lt_descriptions = mi_object_oriented_object_fct->read_descriptions_class(
          iv_object_name = iv_clsname
          iv_language    = mv_language ).
    
        IF lines( lt_descriptions ) = 0.
          RETURN.
        ENDIF.
        " Remove technical languages
        lt_language_filter = mo_i18n_params->build_language_filter( ).
        DELETE lt_descriptions WHERE NOT langu IN lt_language_filter AND langu <> mv_language.
    
        ii_xml->add( iv_name = 'DESCRIPTIONS_CLASS'
                     ig_data = lt_descriptions ).
    
      ENDMETHOD.
    
      METHOD serialize_descr_compo.
    
        DATA: lt_descriptions    TYPE zif_abapgit_oo_object_fnc=>ty_seocompotx_tt,
              lv_language        TYPE spras,
              lt_language_filter TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          lv_language = mv_language.
        ENDIF.
    
        lt_descriptions = mi_object_oriented_object_fct->read_descriptions_compo(
          iv_object_name = iv_clsname
          iv_language    = lv_language ).
    
        IF lines( lt_descriptions ) = 0.
          RETURN.
        ENDIF.
        " Remove technical languages
        lt_language_filter = mo_i18n_params->build_language_filter( ).
        DELETE lt_descriptions WHERE NOT langu IN lt_language_filter AND langu <> mv_language.
    
        ii_xml->add( iv_name = 'DESCRIPTIONS'
                     ig_data = lt_descriptions ).
    
      ENDMETHOD.
    
      METHOD serialize_descr_subco.
    
        DATA: lt_descriptions    TYPE zif_abapgit_oo_object_fnc=>ty_seosubcotx_tt,
              lv_language        TYPE spras,
              lt_language_filter TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          lv_language = mv_language.
        ENDIF.
    
        lt_descriptions = mi_object_oriented_object_fct->read_descriptions_subco(
          iv_object_name = iv_clsname
          iv_language    = lv_language ).
    
        IF lines( lt_descriptions ) = 0.
          RETURN.
        ENDIF.
        " Remove technical languages
        lt_language_filter = mo_i18n_params->build_language_filter( ).
        DELETE lt_descriptions WHERE NOT langu IN lt_language_filter AND langu <> mv_language.
    
        ii_xml->add( iv_name = 'DESCRIPTIONS_SUB'
                     ig_data = lt_descriptions ).
    
      ENDMETHOD.
    
      METHOD serialize_docu.
    
        DATA: lt_lines      TYPE tlinetab,
              lv_object     TYPE dokhl-object,
              lv_langu      TYPE sy-langu,
              lt_i18n_lines TYPE zif_abapgit_lang_definitions=>ty_i18n_lines,
              ls_i18n_lines TYPE zif_abapgit_lang_definitions=>ty_i18n_line.
    
        lv_object = iv_clsname.
    
        lt_lines = mi_object_oriented_object_fct->read_documentation(
          iv_id          = c_longtext_id-class
          iv_object_name = lv_object
          iv_language    = mv_language ).
        IF lines( lt_lines ) > 0.
          ii_xml->add( iv_name = 'LINES'
                       ig_data = lt_lines ).
        ENDIF.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          RETURN.
        ENDIF.
    
        LOOP AT it_langu_additional INTO lv_langu.
    
          lt_lines = mi_object_oriented_object_fct->read_documentation(
            iv_id          = c_longtext_id-class
            iv_object_name = lv_object
            iv_language    = lv_langu ).
    
          IF lines( lt_lines ) > 0.
            CLEAR ls_i18n_lines.
            ls_i18n_lines-language = lv_langu.
            ls_i18n_lines-lines    = lt_lines.
            INSERT ls_i18n_lines INTO TABLE lt_i18n_lines.
          ENDIF.
    
        ENDLOOP.
    
        IF lines( lt_i18n_lines ) > 0.
          ii_xml->add( iv_name = 'I18N_LINES'
                       ig_data = lt_i18n_lines ).
        ENDIF.
    
        serialize_longtexts(
          ii_xml           = ii_xml
          iv_longtext_name = c_longtext_name-attributes
          iv_longtext_id   = c_longtext_id-attributes ).
    
        serialize_longtexts(
          ii_xml           = ii_xml
          iv_longtext_name = c_longtext_name-methods
          iv_longtext_id   = c_longtext_id-methods ).
    
        serialize_longtexts(
          ii_xml           = ii_xml
          iv_longtext_name = c_longtext_name-events
          iv_longtext_id   = c_longtext_id-events ).
    
        serialize_longtexts(
          ii_xml           = ii_xml
          iv_longtext_name = c_longtext_name-types
          iv_longtext_id   = c_longtext_id-types ).
    
      ENDMETHOD.
    
      METHOD serialize_sotr.
        mi_object_oriented_object_fct->read_sotr(
          iv_object_name = ms_item-obj_name
          io_i18n_params = mo_i18n_params
          ii_xml         = ii_xml ).
      ENDMETHOD.
    
      METHOD serialize_tpool.
    
        DATA lt_tpool TYPE textpool_table.
    
        lt_tpool = mi_object_oriented_object_fct->read_text_pool(
          iv_class_name = iv_clsname
          iv_language   = mv_language ).
        ii_xml->add( iv_name = 'TPOOL'
                     ig_data = add_tpool( lt_tpool ) ).
    
        rt_tpool = lt_tpool.
    
      ENDMETHOD.
    
      METHOD serialize_tpool_i18n.
    
        DATA: lt_tpool      TYPE textpool_table,
              lv_index      TYPE i,
              lv_langu      TYPE sy-langu,
              lt_i18n_tpool TYPE zif_abapgit_lang_definitions=>ty_i18n_tpools,
              ls_i18n_tpool TYPE zif_abapgit_lang_definitions=>ty_i18n_tpool.
    
        FIELD-SYMBOLS  LIKE LINE OF it_tpool_main.
    
        DATA lt_tpool_main LIKE SORTED TABLE OF  WITH UNIQUE KEY id key.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true OR lines( it_tpool_main ) = 0.
          RETURN.
        ENDIF.
    
        " Copy single records to be able to catch duplicate key error
        LOOP AT it_tpool_main ASSIGNING .
          INSERT  INTO TABLE lt_tpool_main.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( |Inconsistent textpool in { ms_item-obj_type } { ms_item-obj_name }| ).
          ENDIF.
        ENDLOOP.
    
        LOOP AT it_langu_additional INTO lv_langu.
    
          lt_tpool = mi_object_oriented_object_fct->read_text_pool(
            iv_class_name = iv_clsname
            iv_language   = lv_langu ).
    
          LOOP AT lt_tpool ASSIGNING .
            lv_index = sy-tabix.
            READ TABLE lt_tpool_main WITH KEY id = -id key = -key
              TRANSPORTING NO FIELDS.
            IF sy-subrc <> 0.
              DELETE lt_tpool INDEX lv_index.
            ENDIF.
          ENDLOOP.
    
          IF lines( lt_tpool ) > 0.
            CLEAR ls_i18n_tpool.
            ls_i18n_tpool-language = lv_langu.
            ls_i18n_tpool-textpool = add_tpool( lt_tpool ).
            INSERT ls_i18n_tpool INTO TABLE lt_i18n_tpool.
          ENDIF.
    
        ENDLOOP.
    
        IF lines( lt_i18n_tpool ) > 0.
          ii_xml->add( iv_name = 'I18N_TPOOL'
                       ig_data = lt_i18n_tpool ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD serialize_xml.
    
        DATA: ls_vseoclass        TYPE vseoclass,
              lt_tpool            TYPE textpool_table,
              ls_clskey           TYPE seoclskey,
              lt_langu_additional TYPE zif_abapgit_lang_definitions=>ty_langus,
              lt_language_filter  TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        ls_clskey-clsname = ms_item-obj_name.
    
        "If class was deserialized with a previous version of abapGit and current language was different
        "from main language at this time, this call would return SY-LANGU as main language. To fix
        "these objects, set SY-LANGU to main language temporarily.
        zcl_abapgit_language=>set_current_language( mv_language ).
    
        TRY.
            ls_vseoclass = mi_object_oriented_object_fct->get_class_properties( ls_clskey ).
    
            clear_abap_language_version( CHANGING cv_abap_language_version = ls_vseoclass-unicode ).
    
          CLEANUP.
            zcl_abapgit_language=>restore_login_language( ).
    
        ENDTRY.
    
        zcl_abapgit_language=>restore_login_language( ).
    
        IF mv_skip_testclass = abap_true.
          CLEAR ls_vseoclass-with_unit_tests.
        ENDIF.
    
        " Table d010tinf stores info. on languages in which program is maintained
        " Select all active translations of program texts
        " Skip main language - it was already serialized
        lt_language_filter = mo_i18n_params->build_language_filter( ).
    
        SELECT DISTINCT language
          INTO TABLE lt_langu_additional
          FROM d010tinf
          WHERE r3state  = 'A'
            AND prog     = mv_classpool_name
            AND language IN lt_language_filter
            AND language <> mv_language
          ORDER BY language.
    
        ii_xml->add( iv_name = 'VSEOCLASS'
                     ig_data = ls_vseoclass ).
    
        lt_tpool = serialize_tpool(
          ii_xml     = ii_xml
          iv_clsname = ls_clskey-clsname ).
    
        IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
          serialize_tpool_i18n(
            ii_xml              = ii_xml
            it_langu_additional = lt_langu_additional
            it_tpool_main       = lt_tpool
            iv_clsname          = ls_clskey-clsname ).
        ENDIF.
    
        IF ls_vseoclass-category = seoc_category_exception.
          serialize_sotr( ii_xml ).
        ENDIF.
    
        SELECT DISTINCT langu
          INTO TABLE lt_langu_additional
          FROM dokhl
          WHERE id     = 'CL'
            AND object = ls_clskey-clsname
            AND langu IN lt_language_filter
            AND langu <> mv_language
          ORDER BY langu.
    
        serialize_docu( ii_xml              = ii_xml
                        iv_clsname          = ls_clskey-clsname
                        it_langu_additional = lt_langu_additional ).
    
        serialize_descr_class( ii_xml     = ii_xml
                               iv_clsname = ls_clskey-clsname ).
    
        serialize_descr_compo( ii_xml     = ii_xml
                               iv_clsname = ls_clskey-clsname ).
    
        serialize_descr_subco( ii_xml     = ii_xml
                               iv_clsname = ls_clskey-clsname ).
    
        serialize_attr( ii_xml     = ii_xml
                        iv_clsname = ls_clskey-clsname ).
    
      ENDMETHOD.
    
      METHOD source_apack_replacement.
    
        DATA lv_clsname TYPE seoclsname.
    
        " Check if abapGit version of APACK manifest is used
        SELECT SINGLE clsname INTO lv_clsname
          FROM seometarel
          WHERE clsname    = ms_item-obj_name
            AND refclsname = zif_abapgit_apack_definitions=>c_apack_interface_cust
            AND version    = '1'.
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        " If yes, replace with SAP-version
        interface_replacement(
          EXPORTING
            iv_from_interface = to_lower( zif_abapgit_apack_definitions=>c_apack_interface_cust )
            iv_to_interface   = to_lower( zif_abapgit_apack_definitions=>c_apack_interface_sap )
          CHANGING
            ct_source         = ct_source ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        TYPES: BEGIN OF ty_reposrc,
                 unam  TYPE reposrc-unam,
                 udat  TYPE reposrc-udat,
                 utime TYPE reposrc-utime,
               END OF ty_reposrc.
    
        DATA: lt_reposrc  TYPE STANDARD TABLE OF ty_reposrc,
              ls_reposrc  LIKE LINE OF lt_reposrc,
              lv_include  TYPE syrepid,
              lt_includes TYPE STANDARD TABLE OF syrepid.
    
        CASE iv_extra.
          WHEN zif_abapgit_oo_object_fnc=>c_parts-locals_def.
            lv_include = cl_oo_classname_service=>get_ccdef_name( |{ ms_item-obj_name }| ).
            INSERT lv_include INTO TABLE lt_includes.
          WHEN zif_abapgit_oo_object_fnc=>c_parts-locals_imp.
            lv_include = cl_oo_classname_service=>get_ccimp_name( |{ ms_item-obj_name }| ).
            INSERT lv_include INTO TABLE lt_includes.
          WHEN zif_abapgit_oo_object_fnc=>c_parts-macros.
            lv_include = cl_oo_classname_service=>get_ccmac_name( |{ ms_item-obj_name }| ).
            INSERT lv_include INTO TABLE lt_includes.
          WHEN zif_abapgit_oo_object_fnc=>c_parts-testclasses.
            lv_include = cl_oo_classname_service=>get_ccau_name( |{ ms_item-obj_name }| ).
            INSERT lv_include INTO TABLE lt_includes.
          WHEN OTHERS.
            lt_includes = mi_object_oriented_object_fct->get_includes( ms_item-obj_name ).
        ENDCASE.
    
        ASSERT lines( lt_includes ) > 0.
    
        SELECT unam udat utime FROM reposrc
          INTO TABLE lt_reposrc
          FOR ALL ENTRIES IN lt_includes
          WHERE progname = lt_includes-table_line
          AND r3state = 'A'.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ELSE.
          SORT lt_reposrc BY udat DESCENDING utime DESCENDING.
          READ TABLE lt_reposrc INDEX 1 INTO ls_reposrc.
          ASSERT sy-subrc = 0.
          rv_user = ls_reposrc-unam.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
        DATA: ls_clskey TYPE seoclskey.
        ls_clskey-clsname = ms_item-obj_name.
    
        corr_insert( iv_package ).
    
        mi_object_oriented_object_fct->delete( ls_clskey ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        IF iv_step = zif_abapgit_object=>gc_step_id-abap.
    
          deserialize_abap( ii_xml     = io_xml
                            iv_package = iv_package ).
    
          deserialize_descr( io_xml ).
    
          deserialize_tpool( io_xml ).
    
          IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
            deserialize_tpool_i18n( io_xml ).
          ENDIF.
    
          deserialize_sotr( ii_xml     = io_xml
                            iv_package = iv_package ).
    
          deserialize_docu( io_xml ).
    
          mi_object_oriented_object_fct->add_to_activation_list( ms_item ).
    
        ELSEIF iv_step = zif_abapgit_object=>gc_step_id-early.
    
          " If class does not exist, create it
          " so DDIC that depends on it does not fail activation
          IF zif_abapgit_object~exists( ) = abap_false.
            deserialize_pre_ddic(
              ii_xml     = io_xml
              iv_package = iv_package ).
          ELSE.
            corr_insert( iv_package ).
          ENDIF.
    
        ELSEIF iv_step = zif_abapgit_object=>gc_step_id-late.
    
          deserialize_exceptions( io_xml ).
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA ls_class_key TYPE seoclskey.
    
        ls_class_key-clsname = ms_item-obj_name.
    
        rv_bool = mi_object_oriented_object_fct->exists( ls_class_key-clsname ).
    
        " Skip classes generated by DDLS (SADL)
        IF rv_bool = abap_true AND
          mi_object_oriented_object_fct->read_superclass( ls_class_key-clsname ) = 'CL_SADL_GTK_EXPOSURE_MPC'.
          rv_bool = abap_false.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-early TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        IF is_class_locked( ) = abap_true OR is_text_locked( mv_classpool_name ) = abap_true.
          rv_is_locked = abap_true.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA ls_item TYPE zif_abapgit_definitions=>ty_item.
    
        ls_item-obj_type = 'PROG'.
    
        CASE iv_extra.
          WHEN zif_abapgit_oo_object_fnc=>c_parts-locals_def.
            ls_item-obj_name = cl_oo_classname_service=>get_ccdef_name( |{ ms_item-obj_name }| ).
          WHEN zif_abapgit_oo_object_fnc=>c_parts-locals_imp.
            ls_item-obj_name = cl_oo_classname_service=>get_ccimp_name( |{ ms_item-obj_name }| ).
          WHEN zif_abapgit_oo_object_fnc=>c_parts-macros.
            ls_item-obj_name = cl_oo_classname_service=>get_ccmac_name( |{ ms_item-obj_name }| ).
          WHEN zif_abapgit_oo_object_fnc=>c_parts-testclasses.
            ls_item-obj_name = cl_oo_classname_service=>get_ccau_name( |{ ms_item-obj_name }| ).
        ENDCASE.
    
        IF ls_item-obj_name IS NOT INITIAL.
          rv_exit = zcl_abapgit_objects_factory=>get_gui_jumper( )->jump( ls_item ).
        ENDIF.
    
        " Otherwise covered by /apmg/cl_apm_abapgit_objects=>JUMP
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lt_source    TYPE seop_source_string,
              ls_class_key TYPE seoclskey.
    
        ls_class_key-clsname = ms_item-obj_name.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        CALL FUNCTION 'SEO_BUFFER_REFRESH'
          EXPORTING
            version = seoc_version_active
            force   = abap_true.
        CALL FUNCTION 'SEO_BUFFER_REFRESH'
          EXPORTING
            version = seoc_version_inactive
            force   = abap_true.
    
        lt_source = mi_object_oriented_object_fct->serialize_abap( ls_class_key ).
    
        source_apack_replacement( CHANGING ct_source = lt_source ).
    
        mo_files->add_abap( lt_source ).
    
        lt_source = mi_object_oriented_object_fct->serialize_abap(
          is_class_key = ls_class_key
          iv_type      = seop_ext_class_locals_def ).
        IF lines( lt_source ) > 0.
          mo_files->add_abap(
            iv_extra = zif_abapgit_oo_object_fnc=>c_parts-locals_def
            it_abap  = lt_source ).
        ENDIF.
    
        lt_source = mi_object_oriented_object_fct->serialize_abap(
          is_class_key = ls_class_key
          iv_type      = seop_ext_class_locals_imp ).
        IF lines( lt_source ) > 0.
          mo_files->add_abap(
            iv_extra = zif_abapgit_oo_object_fnc=>c_parts-locals_imp
            it_abap  = lt_source ).
        ENDIF.
    
        lt_source = mi_object_oriented_object_fct->serialize_abap(
          is_class_key            = ls_class_key
          iv_type                 = seop_ext_class_testclasses ).
    
        mv_skip_testclass = mi_object_oriented_object_fct->get_skip_test_classes( ).
        IF lines( lt_source ) > 0 AND mv_skip_testclass = abap_false.
          mo_files->add_abap(
            iv_extra = zif_abapgit_oo_object_fnc=>c_parts-testclasses
            it_abap  = lt_source ).
        ENDIF.
    
        lt_source = mi_object_oriented_object_fct->serialize_abap(
          is_class_key = ls_class_key
          iv_type      = seop_ext_class_macros ).
        IF lines( lt_source ) > 0.
          mo_files->add_abap(
            iv_extra = zif_abapgit_oo_object_fnc=>c_parts-macros
            it_abap  = lt_source ).
        ENDIF.
    
        serialize_xml( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_cmod IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE anam FROM modattr INTO rv_user WHERE name = ms_item-obj_name.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA lv_name TYPE modact-name.
    
        lv_name = ms_item-obj_name.
    
        CALL FUNCTION 'MOD_KUN_ACTIVATE'
          EXPORTING
            activate           = abap_false
            deactivate         = abap_true
            modname            = lv_name
          EXCEPTIONS
            call_error         = 1
            generate_error     = 2
            modattr_status     = 3
            mod_active         = 4
            mod_enqueued       = 5
            not_activated      = 6
            no_modification    = 7
            permission_failure = 8
            OTHERS             = 9.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CALL FUNCTION 'MOD_KUN_DELETE'
          EXPORTING
            modname            = lv_name
            screen             = abap_false
          EXCEPTIONS
            attr_enqueued      = 1
            mod_active         = 2
            mod_enqueued       = 3
            text_enqueued      = 4
            permission_failure = 5
            OTHERS             = 6.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_name    TYPE modact-name,
              lt_modact  TYPE TABLE OF modact,
              lt_modtext TYPE TABLE OF modtext,
              lt_modattr TYPE TABLE OF modattr.
    
        lv_name = ms_item-obj_name.
    
        DELETE FROM modact WHERE name = lv_name.
        DELETE FROM modtext WHERE name = lv_name.
        DELETE FROM modattr WHERE name = lv_name.
    
        io_xml->read( EXPORTING iv_name = 'MODACT'
                      CHANGING  cg_data = lt_modact ).
    
        io_xml->read( EXPORTING iv_name = 'MODTEXT'
                      CHANGING  cg_data = lt_modtext ).
    
        io_xml->read( EXPORTING iv_name = 'MODATTR'
                      CHANGING  cg_data = lt_modattr ).
    
        INSERT modact FROM TABLE lt_modact.
        INSERT modtext FROM TABLE lt_modtext.
        INSERT modattr FROM TABLE lt_modattr.
    
        tadir_insert( iv_package ).
    
        CALL FUNCTION 'MOD_KUN_ACTIVATE'
          EXPORTING
            activate           = abap_true
            deactivate         = abap_false
            modname            = lv_name
          EXCEPTIONS
            call_error         = 1
            generate_error     = 2
            modattr_status     = 3
            mod_active         = 4
            mod_enqueued       = 5
            not_activated      = 6
            no_modification    = 7
            permission_failure = 8
            OTHERS             = 9.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_name TYPE modact-name.
    
        SELECT SINGLE name FROM modact INTO lv_name WHERE name = ms_item-obj_name.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lt_modact  TYPE TABLE OF modact,
              lt_modtext TYPE TABLE OF modtext,
              lt_modattr TYPE TABLE OF modattr.
    
        FIELD-SYMBOLS:  TYPE modattr.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        SELECT * FROM modact INTO TABLE lt_modact WHERE name = ms_item-obj_name
          ORDER BY PRIMARY KEY.
        IF sy-subrc = 0.
          io_xml->add( iv_name = 'MODACT'
                       ig_data = lt_modact ).
        ENDIF.
    
        SELECT * FROM modtext INTO TABLE lt_modtext WHERE name = ms_item-obj_name AND sprsl = mv_language
          ORDER BY PRIMARY KEY.
        IF sy-subrc = 0.
          io_xml->add( iv_name = 'MODTEXT'
                       ig_data = lt_modtext ).
        ENDIF.
    
        SELECT * FROM modattr INTO TABLE lt_modattr WHERE name = ms_item-obj_name
          ORDER BY PRIMARY KEY.
        IF sy-subrc = 0.
          LOOP AT lt_modattr ASSIGNING .
            CLEAR:
              -cnam, -cdat,
              -unam, -udat,
              -anam, -adat,
              -fnam, -fdat.
          ENDLOOP.
    
          io_xml->add( iv_name = 'MODATTR'
                       ig_data = lt_modattr ).
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_cmpt IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        TRY.
            CALL METHOD ('CL_CMP_TEMPLATE')=>('S_GET_DB_ACCESS')
              RECEIVING
                r_ref_db_access = mo_cmp_db.
    
          CATCH cx_root.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
        mv_name = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_cmp_template TYPE REF TO object.
    
        CALL METHOD ('CL_CMP_TEMPLATE')=>('S_CREATE_FROM_DB')
          EXPORTING
            i_name         = mv_name
            i_version      = 'A'
          RECEIVING
            r_ref_template = lo_cmp_template.
    
        CALL METHOD lo_cmp_template->('IF_CMP_TEMPLATE_EDIT~GET_CHANGE_USER')
          RECEIVING
            r_user = rv_user.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_deleted TYPE abap_bool.
    
        CALL METHOD mo_cmp_db->('IF_CMP_TEMPLATE_DB~DELETE_TEMPLATE')
          EXPORTING
            i_name        = mv_name
            i_version     = 'A'
            i_flg_header  = abap_true
            i_flg_lines   = abap_true
          RECEIVING
            r_flg_deleted = lv_deleted.
    
        IF lv_deleted = abap_false.
          zcx_abapgit_exception=>raise( |Error deleting CMPT { ms_item-obj_name }| ).
        ENDIF.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lr_template TYPE REF TO data.
        FIELD-SYMBOLS:  TYPE any,
                          TYPE any,
                           TYPE any.
    
        CREATE DATA lr_template TYPE ('IF_CMP_TEMPLATE_DB=>TYP_TEMPLATE').
        ASSIGN lr_template->* TO .
    
        io_xml->read(
          EXPORTING
            iv_name = 'CMPT'
          CHANGING
            cg_data =  ).
    
        ASSIGN COMPONENT 'STR_HEADER' OF STRUCTURE  TO .
        IF sy-subrc = 0.
          ASSIGN COMPONENT 'NAME' OF STRUCTURE  TO .
          IF sy-subrc = 0.
             = ms_item-obj_name.
          ENDIF.
          ASSIGN COMPONENT 'VERSION' OF STRUCTURE  TO .
          IF sy-subrc = 0.
             = 'A'.
          ENDIF.
        ENDIF.
    
        CALL METHOD mo_cmp_db->('IF_CMP_TEMPLATE_DB~SAVE_TEMPLATE')
          EXPORTING
            i_template_db = 
            i_flg_header  = abap_true
            i_flg_lines   = abap_true.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        CALL METHOD ('CL_CMP_TEMPLATE')=>('S_TEMPLATE_EXISTS')
          EXPORTING
            i_name       = mv_name
            i_version    = 'A'
          RECEIVING
            r_flg_exists = rv_bool.
        IF rv_bool = abap_false.
          CALL METHOD ('CL_CMP_TEMPLATE')=>('S_TEMPLATE_EXISTS')
            EXPORTING
              i_name       = mv_name
              i_version    = 'I'
            RECEIVING
              r_flg_exists = rv_bool.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lr_template TYPE REF TO data.
        FIELD-SYMBOLS:  TYPE any,
                          TYPE any,
                           TYPE any.
    
        CREATE DATA lr_template TYPE ('IF_CMP_TEMPLATE_DB=>TYP_TEMPLATE').
        ASSIGN lr_template->* TO .
    
        CALL METHOD mo_cmp_db->('IF_CMP_TEMPLATE_DB~READ_TEMPLATE')
          EXPORTING
            i_name     = |{ ms_item-obj_name }|
            i_version  = 'A'
          RECEIVING
            r_template = .
    
        ASSIGN COMPONENT 'STR_HEADER' OF STRUCTURE  TO .
        IF sy-subrc = 0.
          ASSIGN COMPONENT 'NAME' OF STRUCTURE  TO .
          IF sy-subrc = 0.
            CLEAR .
          ENDIF.
          ASSIGN COMPONENT 'VERSION' OF STRUCTURE  TO .
          IF sy-subrc = 0.
            CLEAR .
          ENDIF.
          ASSIGN COMPONENT 'CHANGED_ON' OF STRUCTURE  TO .
          IF sy-subrc = 0.
            CLEAR .
          ENDIF.
          ASSIGN COMPONENT 'CHANGED_BY' OF STRUCTURE  TO .
          IF sy-subrc = 0.
            CLEAR .
          ENDIF.
          ASSIGN COMPONENT 'CHANGED_TS' OF STRUCTURE  TO .
          IF sy-subrc = 0.
            CLEAR .
          ENDIF.
        ENDIF.
    
        io_xml->add( iv_name = 'CMPT'
                     ig_data =  ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_cota IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
        DATA lx_error TYPE REF TO cx_root.
        TRY.
            SELECT SINGLE changedby FROM ('sapcontargethead') INTO rv_user
              WHERE id = ms_item-obj_name AND version = 'I'.
    
            IF rv_user IS INITIAL.
              SELECT SINGLE changedby FROM ('sapcontargethead') INTO rv_user
                WHERE id = ms_item-obj_name AND version = 'A'.
            ENDIF.
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
        DATA lx_error TYPE REF TO cx_root.
        DATA lv_cota_name TYPE c LENGTH 30.
        TRY.
            lv_cota_name = ms_item-obj_name.
            CALL METHOD ('CL_COTA_FACTORY')=>('DELETE_COTA')
              EXPORTING
                cota_name = lv_cota_name
                korrnum   = iv_transport.
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_cus0 IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_img_activity = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA ls_header TYPE ty_img_activity-header.
    
        CALL FUNCTION 'S_CUS_IMG_ACTIVITY_READ'
          EXPORTING
            img_activity        = mv_img_activity
          IMPORTING
            img_activity_header = ls_header.
    
        rv_user = ls_header-luser.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: ls_message TYPE hier_mess.
    
        CALL FUNCTION 'S_CUS_IMG_ACTIVITY_DELETE'
          EXPORTING
            img_activity = mv_img_activity
          IMPORTING
            message      = ls_message.
    
        IF ls_message-msgty <> 'S'.
          zcx_abapgit_exception=>raise( |error from delete CUS0 { mv_img_activity } S_CUS_IMG_ACTIVITY_DELETE| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_img_activity TYPE ty_img_activity,
              ls_text         LIKE LINE OF ls_img_activity-texts.
    
        io_xml->read(
          EXPORTING
            iv_name = 'CUS0'
          CHANGING
            cg_data = ls_img_activity ).
    
        READ TABLE ls_img_activity-texts INTO ls_text
                                         WITH KEY spras = mv_language.
    
        CALL FUNCTION 'S_CUS_IMG_ACTIVITY_SAVE'
          EXPORTING
            img_activity  = ls_img_activity-header-activity
            i_docu        = ls_img_activity-header-docu_id
            i_attributes  = ls_img_activity-header-attributes
            i_activity    = ls_img_activity-header-c_activity
            i_description = ls_text
            i_tcode       = ls_img_activity-header-tcode.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: ls_message TYPE hier_mess.
    
        CALL FUNCTION 'S_CUS_IMG_ACTIVITY_EXISTS'
          EXPORTING
            img_activity = mv_img_activity
          IMPORTING
            message      = ls_message.
    
        rv_bool = boolc( ls_message IS INITIAL ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = abap_true.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        DATA: lv_img_activity TYPE cus_img_ac.
    
        lv_img_activity = mv_img_activity.
    
        CALL FUNCTION 'S_CUS_IMG_ACTIVITY_MAINTAIN'
          EXPORTING
            i_display    = abap_true
          CHANGING
            img_activity = lv_img_activity.
    
        rv_exit = abap_true.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_img_activity TYPE ty_img_activity.
    
        CALL FUNCTION 'S_CUS_IMG_ACTIVITY_READ'
          EXPORTING
            img_activity        = mv_img_activity
          IMPORTING
            img_activity_header = ls_img_activity-header
          TABLES
            img_activity_texts  = ls_img_activity-texts.
    
        CLEAR: ls_img_activity-header-fuser,
               ls_img_activity-header-fdate,
               ls_img_activity-header-ftime,
               ls_img_activity-header-luser,
               ls_img_activity-header-ldate,
               ls_img_activity-header-ltime.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          DELETE ls_img_activity-texts WHERE spras <> mv_language.
        ENDIF.
    
        SORT ls_img_activity-texts.
    
        io_xml->add( iv_name = 'CUS0'
                     ig_data = ls_img_activity ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_cus1 IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_customizing_activity = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA ls_header TYPE ty_customzing_activity-activity_header.
    
        CALL FUNCTION 'S_CUS_ACTIVITY_READ'
          EXPORTING
            activity        = mv_customizing_activity
          IMPORTING
            activity_header = ls_header.
    
        rv_user = ls_header-luser.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: ls_message TYPE hier_mess.
    
        CALL FUNCTION 'S_CUS_ACTIVITY_DELETE'
          EXPORTING
            activity = mv_customizing_activity
          IMPORTING
            message  = ls_message.
    
        IF ls_message-msgty <> 'S'.
          zcx_abapgit_exception=>raise( |error from delete CUS1 { mv_customizing_activity } S_CUS_ACTIVITY_DELETE| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_customzing_activity TYPE ty_customzing_activity,
              ls_message             TYPE hier_mess.
    
        io_xml->read(
          EXPORTING
            iv_name = 'CUS1'
          CHANGING
            cg_data = ls_customzing_activity ).
    
        CALL FUNCTION 'S_CUS_ACTIVITY_SAVE'
          EXPORTING
            activity                     = ls_customzing_activity-activity_header-act_id
            activity_type                = ls_customzing_activity-activity_header-act_type
            tcode                        = ls_customzing_activity-activity_header-tcode
            customer_exit                = ls_customzing_activity-activity_customer_exit-exit_name
            customer_exit_enhancement    = ls_customzing_activity-activity_customer_exit-enhancement
            customer_exit_implementation = ls_customzing_activity-activity_customer_exit-impl_name
          IMPORTING
            message                      = ls_message
          TABLES
            activity_title               = ls_customzing_activity-activity_title
            objects                      = ls_customzing_activity-objects
            objects_texts                = ls_customzing_activity-objects_title.
    
        IF ls_message-msgty <> 'S'.
          zcx_abapgit_exception=>raise( |error from deserialize CUS1 { mv_customizing_activity } S_CUS_ACTIVITY_SAVE| ).
        ENDIF.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        CALL FUNCTION 'S_CUS_ACTIVITY_EXIST'
          EXPORTING
            activity            = mv_customizing_activity
          EXCEPTIONS
            activity_exists_not = 1
            OTHERS              = 2.
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = abap_true.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        DATA: lt_bdc_data TYPE STANDARD TABLE OF bdcdata.
        FIELD-SYMBOLS:  TYPE bdcdata.
    
        APPEND INITIAL LINE TO lt_bdc_data ASSIGNING .
        -program = 'SAPLS_CUS_ACTIVITY'.
        -dynpro = '0200'.
        -dynbegin = 'X'.
    
        APPEND INITIAL LINE TO lt_bdc_data ASSIGNING .
        -fnam = 'CUS_ACTH-ACT_ID'.
        -fval = mv_customizing_activity.
    
        APPEND INITIAL LINE TO lt_bdc_data ASSIGNING .
        -fnam = 'BDC_OKCODE'.
        -fval = '=ACT_DISP'.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'S_CUS_ACTIVITY'
          it_bdcdata = lt_bdc_data ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_customzing_activity TYPE ty_customzing_activity.
    
        CALL FUNCTION 'S_CUS_ACTIVITY_READ'
          EXPORTING
            activity               = mv_customizing_activity
          IMPORTING
            activity_header        = ls_customzing_activity-activity_header
            activity_customer_exit = ls_customzing_activity-activity_customer_exit
          TABLES
            activity_title         = ls_customzing_activity-activity_title
            objects                = ls_customzing_activity-objects
            objects_title          = ls_customzing_activity-objects_title.
    
        CLEAR: ls_customzing_activity-activity_header-fdatetime,
               ls_customzing_activity-activity_header-fuser,
               ls_customzing_activity-activity_header-ldatetime,
               ls_customzing_activity-activity_header-luser.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          DELETE ls_customzing_activity-activity_title WHERE spras <> mv_language.
        ENDIF.
    
        SORT ls_customzing_activity-activity_title.
        SORT ls_customzing_activity-objects.
        SORT ls_customzing_activity-objects_title.
    
        io_xml->add( iv_name = 'CUS1'
                     ig_data = ls_customzing_activity ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_cus2 IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_img_attribute = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA ls_header TYPE ty_customizing_attribute-header.
    
        CALL FUNCTION 'S_CUS_ATTRIBUTES_READ'
          EXPORTING
            img_attribute    = mv_img_attribute
          IMPORTING
            attribute_header = ls_header.
    
        rv_user = ls_header-luser.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: ls_message TYPE hier_mess.
    
        CALL FUNCTION 'S_CUS_ATTRIBUTES_DELETE'
          EXPORTING
            img_attribute = mv_img_attribute
          IMPORTING
            message       = ls_message.
    
        IF ls_message-msgty <> 'S'.
          zcx_abapgit_exception=>raise( |error from delete CUS2 { mv_img_attribute } S_CUS_ATTRIBUTES_DELETE| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_customizing_attribute TYPE ty_customizing_attribute,
              ls_message               TYPE hier_mess.
    
        io_xml->read(
          EXPORTING
            iv_name = 'CUS2'
          CHANGING
            cg_data = ls_customizing_attribute ).
    
        CALL FUNCTION 'S_CUS_ATTRIBUTES_SAVE'
          EXPORTING
            img_attribute         = ls_customizing_attribute-header
          IMPORTING
            message               = ls_message
          TABLES
            attributes_title      = ls_customizing_attribute-titles
            attributes_countries  = ls_customizing_attribute-countries
            attributes_components = ls_customizing_attribute-components.
    
        IF ls_message-msgty <> 'S'.
          zcx_abapgit_exception=>raise( |error from deserialize CUS2 { mv_img_attribute } S_CUS_ATTRIBUTES_SAVE| ).
        ENDIF.
    
        corr_insert( iv_package ).
    
        tadir_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        CALL FUNCTION 'S_CUS_ATTRIBUTES_EXIST'
          EXPORTING
            img_attribute         = mv_img_attribute
          EXCEPTIONS
            attributes_exists_not = 1
            OTHERS                = 2.
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = abap_true.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_customizing_attribute TYPE ty_customizing_attribute.
    
        CALL FUNCTION 'S_CUS_ATTRIBUTES_READ'
          EXPORTING
            img_attribute                 = mv_img_attribute
          IMPORTING
            attribute_header              = ls_customizing_attribute-header
          TABLES
            attribute_title               = ls_customizing_attribute-titles
            attribute_countries           = ls_customizing_attribute-countries
            attribute_components          = ls_customizing_attribute-components
            attribute_components_variants = ls_customizing_attribute-components_variants.
    
        CLEAR: ls_customizing_attribute-header-fdatetime,
               ls_customizing_attribute-header-fuser,
               ls_customizing_attribute-header-ldatetime,
               ls_customizing_attribute-header-luser.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          DELETE ls_customizing_attribute-titles WHERE spras <> mv_language.
        ENDIF.
    
        io_xml->add( iv_name = 'CUS2'
                     ig_data = ls_customizing_attribute ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_dcls IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
            is_item        = is_item
            iv_language    = iv_language
            io_files       = io_files
            io_i18n_params = io_i18n_params ).
    
        TRY.
            CALL METHOD ('CL_ACM_DCL_HANDLER_FACTORY')=>('CREATE')
              RECEIVING
                ro_handler = mo_dcl_handler.
    
          CATCH cx_root.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        DATA: lr_data  TYPE REF TO data,
              lx_error TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:   TYPE any,
                        TYPE any.
    
        CREATE DATA lr_data TYPE ('ACM_S_DCLSRC').
        ASSIGN lr_data->* TO .
    
        TRY.
            CALL METHOD mo_dcl_handler->('READ')
              EXPORTING
                iv_dclname = ms_item-obj_name
              IMPORTING
                es_dclsrc  = .
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        ASSIGN COMPONENT 'AS4USER' OF STRUCTURE  TO .
        IF sy-subrc = 0.
          rv_user = .
        ELSE.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lx_error TYPE REF TO cx_root.
    
        TRY.
            CALL METHOD mo_dcl_handler->('DELETE')
              EXPORTING
                iv_dclname = ms_item-obj_name.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lr_data                  TYPE REF TO data,
              lx_error                 TYPE REF TO cx_root,
              lv_abap_language_version TYPE uccheck.
    
        FIELD-SYMBOLS:   TYPE any,
                        TYPE any.
    
        CREATE DATA lr_data TYPE ('ACM_S_DCLSRC').
        ASSIGN lr_data->* TO .
    
        io_xml->read(
          EXPORTING
            iv_name = 'DCLS'
          CHANGING
            cg_data =  ).
    
        ASSIGN COMPONENT 'SOURCE' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
         = mo_files->read_string( 'asdcls' ).
    
        ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE  TO .
        IF sy-subrc = 0.
          lv_abap_language_version = .
          set_abap_language_version( CHANGING cv_abap_language_version = lv_abap_language_version ).
        ENDIF.
    
        tadir_insert( iv_package ).
    
        TRY.
            TRY.
                CALL METHOD mo_dcl_handler->('SAVE')
                  EXPORTING
                    iv_dclname               = ms_item-obj_name
                    iv_put_state             = 'I'
                    is_dclsrc                = 
                    iv_devclass              = iv_package
                    iv_access_mode           = 'INSERT'
                    iv_abap_language_version = lv_abap_language_version.
              CATCH cx_sy_dyn_call_param_not_found.
                CALL METHOD mo_dcl_handler->('SAVE')
                  EXPORTING
                    iv_dclname     = ms_item-obj_name
                    iv_put_state   = 'I'
                    is_dclsrc      = 
                    iv_devclass    = iv_package
                    iv_access_mode = 'INSERT'.
            ENDTRY.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        CALL METHOD mo_dcl_handler->('CHECK_EXISTENCE')
          EXPORTING
            iv_objectname = ms_item-obj_name
          RECEIVING
            rv_exists     = rv_bool.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'E_ACMDCLSRC'
                                                iv_argument    = |{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by ZCL_ABAPGIT_ADT_LINK=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lr_data  TYPE REF TO data,
              lx_error TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:   TYPE any,
                        TYPE any.
    
        CREATE DATA lr_data TYPE ('ACM_S_DCLSRC').
        ASSIGN lr_data->* TO .
    
        TRY.
            CALL METHOD mo_dcl_handler->('READ')
              EXPORTING
                iv_dclname = ms_item-obj_name
              IMPORTING
                es_dclsrc  = .
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        clear_fields( CHANGING cg_data =  ).
    
        ASSIGN COMPONENT 'SOURCE' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
    
        mo_files->add_string(
          iv_ext    = 'asdcls'
          iv_string =  ).
    
        CLEAR .
    
        io_xml->add( iv_name = 'DCLS'
                     ig_data =  ).
    
      ENDMETHOD.
    
      METHOD clear_fields.
    
        DATA:
          BEGIN OF ls_fields_to_clear,
            as4user      TYPE c,
            as4date      TYPE d,
            as4time      TYPE t,
            created_by   TYPE c,
            created_date TYPE d,
            as4local     TYPE c,
          END OF ls_fields_to_clear.
    
        FIELD-SYMBOLS:
           TYPE any.
    
        MOVE-CORRESPONDING ls_fields_to_clear TO cg_data.
    
        ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE cg_data TO .
        IF sy-subrc = 0.
          clear_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ddls IMPLEMENTATION.
    
      METHOD clear_fields.
    
        DATA:
          BEGIN OF ls_fields_to_clear,
            as4user            TYPE c,
            as4date            TYPE d,
            as4time            TYPE t,
            actflag            TYPE c,
            chgflag            TYPE c,
            abap_langu_version TYPE c,
          END OF ls_fields_to_clear.
    
        FIELD-SYMBOLS:
           TYPE any.
    
        MOVE-CORRESPONDING ls_fields_to_clear TO cg_data.
    
        ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE cg_data TO .
        IF sy-subrc = 0.
           = get_abap_language_version( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        TRY.
            CALL METHOD ('CL_DD_DDL_HANDLER_FACTORY')=>('CREATE')
              RECEIVING
                handler = mo_ddl_handler.
    
          CATCH cx_root.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD format_source_before_serialize.
    
        DATA:
          lv_len       TYPE i,
          lv_lastchar1 TYPE c,
          lv_lastchar2 TYPE c.
    
        " New line included in 751+ by CL_DD_DDL_HANDLER=>ADD_BASEOBJS_INFO_TO_DDLS
        " Change for 750-
    
        lv_len = strlen( cv_string ) - 1.
        IF lv_len < 0.
          RETURN.
        ENDIF.
        lv_lastchar1 = cv_string+lv_len(1).
    
        lv_len = strlen( cv_string ) - 2.
        IF lv_len < 0.
          RETURN.
        ENDIF.
        lv_lastchar2 = cv_string+lv_len(1).
    
        " only add a line break, if the last character is unequal to cr_lf and newline !
        IF lv_lastchar1 <> cl_abap_char_utilities=>cr_lf AND lv_lastchar1 <> cl_abap_char_utilities=>newline AND
            lv_lastchar1 <> space OR
            ( lv_lastchar1 = space AND
              ( lv_lastchar2 <> cl_abap_char_utilities=>cr_lf AND lv_lastchar2 <> cl_abap_char_utilities=>newline ) ).
          cv_string = |{ cv_string }{ cl_abap_char_utilities=>cr_lf }|.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_log_uuid.
    
        DATA lv_tstmpl       TYPE timestampl.
        DATA lv_tstmp_string TYPE string.
    
        TRY.
            cl_system_uuid=>convert_uuid_x16_static( EXPORTING uuid     = cl_system_uuid=>create_uuid_x16_static( )
                                                     IMPORTING uuid_c32 = rv_log_uuid ).
          CATCH cx_uuid_error.
            GET TIME STAMP FIELD lv_tstmpl.
            lv_tstmp_string = lv_tstmpl.
            rv_log_uuid = |{ sy-uname }{ lv_tstmp_string }|.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD is_baseinfo_supported.
    
        DATA:
          lr_data_baseinfo TYPE REF TO data.
    
        TRY.
            CREATE DATA lr_data_baseinfo TYPE ('IF_DD_DDL_TYPES=>TY_S_BASEINFO_STRING_SAVE').
            rv_supported = abap_true.
          CATCH cx_root.
            rv_supported = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD open_adt_stob.
    
        DATA: lr_data  TYPE REF TO data.
    
        FIELD-SYMBOLS:      TYPE STANDARD TABLE.
        FIELD-SYMBOLS:  TYPE STANDARD TABLE.
        FIELD-SYMBOLS:      TYPE any.
        FIELD-SYMBOLS:  TYPE any.
        FIELD-SYMBOLS:       TYPE any.
        FIELD-SYMBOLS:      TYPE any.
    
        CREATE DATA lr_data TYPE ('IF_DD_DDL_TYPES=>TY_T_DDOBJ').
        ASSIGN lr_data->* TO .
    
        CREATE DATA lr_data LIKE LINE OF .
        ASSIGN lr_data->* TO .
    
        CREATE DATA lr_data TYPE ('IF_DD_DDL_TYPES=>TY_T_ENTITY_OF_VIEW').
        ASSIGN lr_data->* TO .
    
        CREATE DATA lr_data LIKE LINE OF .
        ASSIGN lr_data->* TO .
    
        CLEAR .
        ASSIGN COMPONENT 'NAME' OF STRUCTURE  TO .
         = iv_ddls_name.
        INSERT  INTO TABLE .
    
        CALL METHOD mo_ddl_handler->('IF_DD_DDL_HANDLER~GET_VIEWNAME_FROM_ENTITYNAME')
          EXPORTING
            ddnames        = 
          IMPORTING
            view_of_entity = .
    
        READ TABLE  ASSIGNING  INDEX 1.
        IF sy-subrc = 0.
          ASSIGN COMPONENT 'DDLNAME' OF STRUCTURE  TO .
    
          zcl_abapgit_adt_link=>jump( iv_obj_name = 
                                      iv_obj_type = 'DDLS' ).
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD read_baseinfo.
    
        TRY.
            rv_baseinfo_string = mo_files->read_string( 'baseinfo' ).
    
          CATCH zcx_abapgit_exception.
            " File not found. That's ok, as the object could have been created in a
            " system where baseinfo wasn't supported.
            RETURN.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lr_data  TYPE REF TO data,
              lx_error TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:   TYPE any,
                        TYPE any.
    
        CREATE DATA lr_data TYPE ('DDDDLSRCV').
        ASSIGN lr_data->* TO .
    
        TRY.
            CALL METHOD mo_ddl_handler->('IF_DD_DDL_HANDLER~READ')
              EXPORTING
                name         = ms_item-obj_name
                get_state    = 'A'
              IMPORTING
                ddddlsrcv_wa = .
    
            ASSIGN COMPONENT 'AS4USER' OF STRUCTURE  TO .
            IF sy-subrc = 0.
              rv_user = .
            ENDIF.
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        IF rv_user IS INITIAL.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA:
          lt_deltab   TYPE TABLE OF dcdeltb,
          ls_deltab   TYPE dcdeltb,
          lt_gentab   TYPE TABLE OF dcgentb,
          lv_rc       TYPE sy-subrc,
          lv_logname  TYPE ddmass-logname.
    
        " CL_DD_DDL_HANDLER->DELETE does not work for CDS views that reference other views
        " To drop any views regardless of reference, we use delnoref = false
        ls_deltab-objtyp  = 'DDLS'.
        ls_deltab-objname = ms_item-obj_name.
        APPEND ls_deltab TO lt_deltab.
    
        " protname in DDPRS is 40 chars long!
        lv_logname = |DEL_{ get_log_uuid( ) }|.
    
        IF ii_log IS NOT INITIAL.
          ii_log->add_info( |> Mass deletion 1 DDIC object| ).
          ii_log->add_info( |Log name: { lv_logname }| ).
        ENDIF.
    
        CALL FUNCTION 'DD_MASS_ACT_C3'
          EXPORTING
            ddmode         = 'O'
            inactive       = abap_true
            write_log      = abap_true
            logname        = lv_logname
            delall         = abap_true
            delnoref       = abap_false
            prid           = 1
          IMPORTING
            act_rc         = lv_rc
          TABLES
            gentab         = lt_gentab
            deltab         = lt_deltab
          EXCEPTIONS
            access_failure = 1
            no_objects     = 2
            locked         = 3
            OTHERS         = 4.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        corr_insert( iv_package ).
    
        " rebuild object list to delete remaining TADIR entry
        CALL FUNCTION 'WB_TREE_UPDATE_OBJECTLIST'
          EXPORTING
            p_object_type = 'DF'
            p_object_name = ms_item-obj_name
            p_operation   = 'DELETE'
          EXCEPTIONS
            OTHERS        = 0.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA:
          lr_data          TYPE REF TO data,
          lr_data_baseinfo TYPE REF TO data,
          lx_error         TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:
                            TYPE any,
                   TYPE any,
                          TYPE any,
                 TYPE any,
                TYPE any,
           TYPE any.
    
        CREATE DATA lr_data TYPE ('DDDDLSRCV').
        ASSIGN lr_data->* TO .
    
        TRY.
            io_xml->read( EXPORTING iv_name = 'DDLS'
                          CHANGING cg_data  =  ).
    
            ASSIGN COMPONENT 'SOURCE' OF STRUCTURE  TO .
            ASSERT sy-subrc = 0.
             = mo_files->read_string( 'asddls' ).
    
            ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE  TO .
            IF sy-subrc = 0.
              set_abap_language_version( CHANGING cv_abap_language_version =  ).
            ENDIF.
    
            IF is_baseinfo_supported( ) = abap_true.
              CREATE DATA lr_data_baseinfo TYPE ('IF_DD_DDL_TYPES=>TY_S_BASEINFO_STRING_SAVE').
              ASSIGN lr_data_baseinfo->* TO .
    
              ASSIGN COMPONENT 'BASEINFO_STRING' OF STRUCTURE  TO .
              ASSERT sy-subrc = 0.
    
               = read_baseinfo( ).
    
              ASSIGN COMPONENT 'DDLNAME' OF STRUCTURE  TO .
              ASSERT sy-subrc = 0.
               = ms_item-obj_name.
    
              TRY.
                  CALL METHOD mo_ddl_handler->('IF_DD_DDL_HANDLER~SAVE')
                    EXPORTING
                      name                  = ms_item-obj_name
                      put_state             = 'N'
                      ddddlsrcv_wa          = 
                      baseinfo_string       = 
                      save_language_version = abap_true.
                CATCH cx_sy_dyn_call_param_not_found.
                  CALL METHOD mo_ddl_handler->('IF_DD_DDL_HANDLER~SAVE')
                    EXPORTING
                      name            = ms_item-obj_name
                      put_state       = 'N'
                      ddddlsrcv_wa    = 
                      baseinfo_string = .
              ENDTRY.
            ELSE.
              CALL METHOD mo_ddl_handler->('IF_DD_DDL_HANDLER~SAVE')
                EXPORTING
                  name         = ms_item-obj_name
                  put_state    = 'N'
                  ddddlsrcv_wa = .
            ENDIF.
    
            CALL METHOD mo_ddl_handler->('IF_DD_DDL_HANDLER~WRITE_TADIR')
              EXPORTING
                objectname = ms_item-obj_name
                devclass   = iv_package
                prid       = 0.
    
            corr_insert( iv_package ).
    
          CATCH cx_root INTO lx_error.
            " Attempt clean-up but catch error if it doesn't work
            TRY.
                CALL METHOD mo_ddl_handler->('IF_DD_DDL_HANDLER~DELETE')
                  EXPORTING
                    name = ms_item-obj_name
                    prid = 0.
              CATCH cx_root ##NO_HANDLER.
            ENDTRY.
    
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_state TYPE objstate.
    
        TRY.
            CALL METHOD mo_ddl_handler->('IF_DD_DDL_HANDLER~READ')
              EXPORTING
                name      = ms_item-obj_name
              IMPORTING
                got_state = lv_state.
            rv_bool = boolc( NOT lv_state IS INITIAL ).
          CATCH cx_root.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESDICT'
                                                iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: lv_typename   TYPE typename.
        DATA: lv_ddtypekind TYPE ddtypekind.
        DATA lv_adt_jump_enabled TYPE abap_bool.
    
        lv_typename = ms_item-obj_name.
    
        CALL FUNCTION 'DDIF_TYPEINFO_GET'
          EXPORTING
            typename = lv_typename
          IMPORTING
            typekind = lv_ddtypekind.
    
        lv_adt_jump_enabled = /apmg/cl_apm_settings=>factory( )->get( )-gui_settings-adt_jump_enabled.
    
        IF lv_ddtypekind = 'STOB' AND lv_adt_jump_enabled = abap_true.
          open_adt_stob( ms_item-obj_name ).
          rv_exit = abap_true.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lr_data          TYPE REF TO data,
              lr_data_baseinfo TYPE REF TO data,
              lx_error         TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:           TYPE any,
                                TYPE any,
                        TYPE ANY TABLE,
                        TYPE any,
                              TYPE any,
                             TYPE any.
    
        CREATE DATA lr_data TYPE ('DDDDLSRCV').
        ASSIGN lr_data->* TO .
    
        TRY.
    
            IF is_baseinfo_supported( ) = abap_true.
              CREATE DATA lr_data_baseinfo TYPE ('IF_DD_DDL_TYPES=>TY_T_BASEINFO_STRING').
              ASSIGN lr_data_baseinfo->* TO .
    
              CALL METHOD mo_ddl_handler->('IF_DD_DDL_HANDLER~READ')
                EXPORTING
                  name            = ms_item-obj_name
                  get_state       = 'A'
                IMPORTING
                  ddddlsrcv_wa    = 
                  baseinfo_string = .
    
              LOOP AT  ASSIGNING .
                ASSIGN COMPONENT 'DDLNAME' OF STRUCTURE  TO .
                ASSERT sy-subrc = 0.
    
                ASSIGN COMPONENT 'AS4LOCAL' OF STRUCTURE  TO .
                ASSERT sy-subrc = 0.
    
                IF  = ms_item-obj_name AND  = 'A'.
                  ASSIGN COMPONENT 'BASEINFO_STRING' OF STRUCTURE  TO .
                  ASSERT sy-subrc = 0.
                  mo_files->add_string(
                    iv_ext    = 'baseinfo'
                    iv_string =  ).
                  EXIT.
                ENDIF.
              ENDLOOP.
            ELSE.
              CALL METHOD mo_ddl_handler->('IF_DD_DDL_HANDLER~READ')
                EXPORTING
                  name         = ms_item-obj_name
                  get_state    = 'A'
                IMPORTING
                  ddddlsrcv_wa = .
            ENDIF.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        clear_fields( CHANGING cg_data =  ).
    
        ASSIGN COMPONENT 'SOURCE' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
    
        format_source_before_serialize( CHANGING cv_string =  ).
    
        mo_files->add_string(
          iv_ext    = 'asddls'
          iv_string =  ).
    
        CLEAR .
    
        io_xml->add( iv_name = 'DDLS'
                     ig_data =  ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ddlx IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
            is_item        = is_item
            iv_language    = iv_language
            io_files       = io_files
            io_i18n_params = io_i18n_params ).
    
        mv_object_key = ms_item-obj_name.
    
        TRY.
            CREATE OBJECT mi_persistence
              TYPE ('CL_DDLX_ADT_OBJECT_PERSIST').
    
            CREATE OBJECT mi_data_model
              TYPE ('CL_DDLX_WB_OBJECT_DATA').
    
          CATCH cx_sy_create_object_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lr_data TYPE REF TO data.
    
        FIELD-SYMBOLS:
                 TYPE any,
           TYPE data.
    
        CREATE DATA lr_data
          TYPE ('CL_DDLX_WB_OBJECT_DATA=>TY_OBJECT_DATA').
        ASSIGN lr_data->* TO .
    
        TRY.
            mi_persistence->get(
              EXPORTING
                p_object_key  = mv_object_key
                p_version     = swbm_version_active
              CHANGING
                p_object_data = mi_data_model ).
          CATCH cx_swb_exception.
            rv_user = c_user_unknown.
            RETURN.
        ENDTRY.
    
        mi_data_model->get_data( IMPORTING p_data =  ).
    
        ASSIGN COMPONENT 'METADATA-CHANGED_BY' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
        rv_user = .
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lx_error TYPE REF TO cx_swb_exception.
    
        TRY.
            mi_persistence->delete( p_object_key = mv_object_key
                                    p_version    = swbm_version_active ).
    
          CATCH cx_swb_exception INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lr_data  TYPE REF TO data,
              lx_error TYPE REF TO cx_swb_exception.
    
        FIELD-SYMBOLS:                   TYPE any,
                                       TYPE data,
                                      TYPE data,
                                      TYPE data,
                                   TYPE syuname,
                                   TYPE xsddatetime_z,
                        TYPE data.
    
        CREATE DATA lr_data
          TYPE ('CL_DDLX_WB_OBJECT_DATA=>TY_OBJECT_DATA').
        ASSIGN lr_data->* TO .
    
        io_xml->read(
          EXPORTING
            iv_name = 'DDLX'
          CHANGING
            cg_data =  ).
    
        ASSIGN COMPONENT 'METADATA-ABAP_LANGU_VERSION' OF STRUCTURE  TO .
        IF sy-subrc = 0.
          set_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
    
        ASSIGN COMPONENT 'CONTENT-SOURCE' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
    
        TRY.
            " If the file doesn't exist that's ok, because previously
            " the source code was stored in the xml. We are downward compatible.
             = mo_files->read_string( 'asddlxs' ).
          CATCH zcx_abapgit_exception ##NO_HANDLER.
        ENDTRY.
    
        ASSIGN COMPONENT 'METADATA-VERSION' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
    
        " We have to always save as inactive. Standard activation below activates then
        " and also creates transport request entry if necessary
         = 'inactive'.
    
        "package needed to be able to determine ABAP language version
        ASSIGN COMPONENT 'METADATA-PACKAGE_REF-NAME' OF STRUCTURE  TO .
        IF  IS ASSIGNED.
           = iv_package.
        ENDIF.
    
        ASSIGN COMPONENT 'METADATA-CHANGED_BY' OF STRUCTURE  TO .
        IF  IS ASSIGNED.
           = sy-uname.
        ENDIF.
        ASSIGN COMPONENT 'METADATA-CHANGED_AT' OF STRUCTURE  TO .
        IF  IS ASSIGNED.
          GET TIME STAMP FIELD .
        ENDIF.
    
        mi_data_model->set_data(  ).
    
        TRY.
            mi_persistence->save( mi_data_model ).
          CATCH cx_swb_exception INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        tadir_insert( iv_package ).
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = abap_true.
    
        TRY.
            mi_persistence->get( p_object_key           = mv_object_key
                                 p_version              = swbm_version_active
                                 p_existence_check_only = abap_true ).
    
          CATCH cx_swb_exception.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESWB_EO'
                                                iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by ZCL_ABAPGIT_ADT_LINK=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lr_data  TYPE REF TO data,
              lx_error TYPE REF TO cx_swb_exception.
    
        FIELD-SYMBOLS:   TYPE any,
                        TYPE data.
    
        CREATE DATA lr_data
          TYPE ('CL_DDLX_WB_OBJECT_DATA=>TY_OBJECT_DATA').
        ASSIGN lr_data->* TO .
    
        TRY.
            IF zcl_abapgit_factory=>get_environment( )->compare_with_inactive( ) = abap_true.
              "Retrieve inactive version
              mi_persistence->get(
                EXPORTING
                  p_object_key  = mv_object_key
                  p_version     = swbm_version_inactive
                CHANGING
                  p_object_data = mi_data_model ).
              IF mi_data_model->get_object_name( ) IS INITIAL.
                "Fallback: retrieve active version
                mi_persistence->get(
                  EXPORTING
                    p_object_key  = mv_object_key
                    p_version     = swbm_version_active
                  CHANGING
                    p_object_data = mi_data_model ).
              ENDIF.
            ELSE.
              "Retrieve active version
              mi_persistence->get(
                EXPORTING
                  p_object_key  = mv_object_key
                  p_version     = swbm_version_active
                CHANGING
                  p_object_data = mi_data_model ).
            ENDIF.
    
          CATCH cx_swb_exception INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        mi_data_model->get_data( IMPORTING p_data =  ).
    
        clear_fields( CHANGING cg_data =  ).
    
        ASSIGN COMPONENT 'CONTENT-SOURCE' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
    
        mo_files->add_string(
          iv_ext    = 'asddlxs'
          iv_string =  ).
    
        CLEAR .
    
        io_xml->add( iv_name = 'DDLX'
                     ig_data =  ).
    
      ENDMETHOD.
    
      METHOD clear_fields.
    
        DATA:
          BEGIN OF ls_fields_to_clear,
            BEGIN OF metadata,
              changed_at    TYPE d,
              changed_by    TYPE c,
              created_at    TYPE d,
              created_by    TYPE c,
              responsible   TYPE c,
              BEGIN OF package_ref,
                name TYPE c,
              END OF package_ref,
              BEGIN OF container_ref,
                name TYPE c,
              END OF container_ref,
              version       TYPE c,
              master_system TYPE c,
            END OF metadata,
          END OF ls_fields_to_clear.
    
        FIELD-SYMBOLS:
           TYPE any.
    
        MOVE-CORRESPONDING ls_fields_to_clear TO cg_data.
    
        ASSIGN COMPONENT 'METADATA-ABAP_LANGUAGE_VERSION' OF STRUCTURE cg_data TO .
        IF sy-subrc = 0.
          clear_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_object_desd IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
        DATA lo_handler       TYPE REF TO object.
        DATA lx_error         TYPE REF TO cx_root.
        FIELD-SYMBOLS  TYPE any.
    
        TRY.
            lo_handler = _create_les_handler( ms_item-obj_name ).
            IF ms_item-inactive = abap_true.
              ASSIGN ('IF_DD_LES_PERSIST=>S_GET_STATE-NEWEST') TO .
            ELSE.
              ASSIGN ('IF_DD_LES_PERSIST=>S_GET_STATE-ACTIVE') TO .
            ENDIF.
            CALL METHOD lo_handler->('IF_DD_LES_HANDLER~GET_CHANGED_BY')
              EXPORTING
                iv_state      = 
              RECEIVING
                rv_changed_by = rv_user.
          CATCH cx_root INTO lx_error ##CATCH_ALL.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        INSERT zif_abapgit_object=>gc_step_id-ddic INTO TABLE rt_steps.
      ENDMETHOD.
    
      METHOD _create_les_handler.
        DATA lo_handler_fctry         TYPE REF TO object.
        DATA lo_dd_logger             TYPE REF TO object.
        DATA lr_data_of_logger_object TYPE REF TO data.
        DATA lr_logger_type_descr     TYPE REF TO cl_abap_typedescr.
        DATA lr_logger_ref_descr      TYPE REF TO cl_abap_refdescr.
        DATA lr_desd_name             TYPE REF TO data.
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE any.
    
        CALL METHOD ('CL_DD_LOG_FACTORY')=>('CREATE_RESTRICTED_LOGGER')
          EXPORTING
            iv_log_id = -1
          RECEIVING
            ro_logger = lo_dd_logger.
    
        lr_logger_type_descr = cl_abap_typedescr=>describe_by_name( 'CL_DD_LOG_ABS_LOGGER' ).
        lr_logger_ref_descr = cl_abap_refdescr=>get( lr_logger_type_descr ).
        CREATE DATA lr_data_of_logger_object TYPE HANDLE lr_logger_ref_descr.
        ASSIGN lr_data_of_logger_object->* TO .
         ?= lo_dd_logger.
    
        CREATE DATA lr_desd_name TYPE ('DD_LES_NAME').
        ASSIGN lr_desd_name->* TO .
         = iv_desd_name.
    
        CREATE OBJECT lo_handler_fctry TYPE ('CL_DD_LES_HANDLER_FACTORY').
        CALL METHOD lo_handler_fctry->('CREATE')
          EXPORTING
            io_logger = 
            iv_name   = 
          RECEIVING
            r_result  = ro_handler.
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_object_devc IMPLEMENTATION.
    
      METHOD adjust_sw_component.
    
        DATA:
          lv_namespace TYPE namespace,
          lv_comp_type TYPE c LENGTH 1.
    
        " Keep software component of a package for ABAP add-ons (customer and partner developments)...
        SELECT SINGLE comp_type FROM cvers INTO lv_comp_type WHERE component = cv_dlvunit.
        IF sy-subrc = 0 AND lv_comp_type = 'A'.
          " ... with a matching namespace (typical Add-on Assembly Kit scenario)
          lv_namespace = |/{ cv_dlvunit }/|.
          SELECT SINGLE namespace FROM trnspace INTO lv_namespace WHERE namespace = lv_namespace.
          IF sy-subrc <> 0.
            CLEAR cv_dlvunit.
          ENDIF.
        ELSE.
          CLEAR cv_dlvunit.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        IF is_item-devclass IS NOT INITIAL.
          mv_local_devclass = is_item-devclass.
        ELSE.
          mv_local_devclass = is_item-obj_name.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_package.
        IF zif_abapgit_object~exists( ) = abap_true.
          ri_package = load_package( mv_local_devclass ).
        ENDIF.
      ENDMETHOD.
    
      METHOD is_empty.
    
        DATA: lv_object_name TYPE tadir-obj_name,
              lt_subpackages TYPE zif_abapgit_sap_package=>ty_devclass_tt.
    
        lt_subpackages = zcl_abapgit_factory=>get_sap_package( iv_package_name )->list_subpackages( ).
    
        IF lines( lt_subpackages ) > 0.
          rv_is_empty = abap_false.
          RETURN.
        ENDIF.
    
        " Ignore the SOTR if is linked to the current SAP package (DEVC)
        SELECT SINGLE obj_name
               FROM tadir
               INTO lv_object_name
               WHERE pgmid = 'R3TR'
               AND NOT ( ( object = 'DEVC' OR object = 'SOTR' ) AND obj_name = iv_package_name )
               AND devclass = iv_package_name.
        rv_is_empty = boolc( sy-subrc <> 0 ).
    
      ENDMETHOD.
    
      METHOD is_local.
    
        DATA lv_dlvunit TYPE tdevc-dlvunit.
    
        SELECT SINGLE dlvunit FROM tdevc INTO lv_dlvunit
            WHERE devclass = iv_package_name AND intsys <> 'SAP'.
        IF sy-subrc = 0 AND lv_dlvunit = 'LOCAL'.
          rv_is_local = abap_true.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD load_package.
    
        cl_package_factory=>load_package(
          EXPORTING
            i_package_name             = iv_package_name
            i_force_reload             = abap_true
          IMPORTING
            e_package                  = ri_package
          EXCEPTIONS
            object_not_existing        = 1
            unexpected_error           = 2
            intern_err                 = 3
            no_access                  = 4
            object_locked_and_modified = 5
            OTHERS                     = 6 ).
        IF sy-subrc = 1.
          RETURN.
        ELSEIF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD remove_obsolete_tadir.
    
        DATA:
          lv_pack  TYPE devclass,
          lt_pack  TYPE STANDARD TABLE OF devclass,
          ls_tadir TYPE zif_abapgit_definitions=>ty_tadir,
          lt_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt,
          ls_item  TYPE zif_abapgit_definitions=>ty_item.
    
        " TADIR entries must remain for transportable packages
        IF is_local( iv_package_name ) = abap_false.
          RETURN.
        ENDIF.
    
        " Clean-up sub packages first
        SELECT devclass FROM tdevc INTO TABLE lt_pack
          WHERE parentcl = iv_package_name
          ORDER BY PRIMARY KEY.
    
        LOOP AT lt_pack INTO lv_pack.
          remove_obsolete_tadir( lv_pack ).
        ENDLOOP.
    
        " Remove TADIR entries for objects that do not exist anymore
        SELECT * FROM tadir INTO CORRESPONDING FIELDS OF TABLE lt_tadir
          WHERE devclass = iv_package_name
          ORDER BY PRIMARY KEY ##TOO_MANY_ITAB_FIELDS.
    
        LOOP AT lt_tadir INTO ls_tadir.
          ls_item-obj_type = ls_tadir-object.
          ls_item-obj_name = ls_tadir-obj_name.
    
          IF /apmg/cl_apm_abapgit_objects=>exists( ls_item ) = abap_false.
            zcl_abapgit_factory=>get_tadir( )->delete_single(
              iv_object    = ls_tadir-object
              iv_obj_name  = ls_tadir-obj_name ).
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD set_lock.
    
        DATA: lv_changeable TYPE abap_bool.
    
        ii_package->get_changeable( IMPORTING e_changeable = lv_changeable ).
        IF lv_changeable <> iv_lock.
          TRY.
              CALL METHOD ii_package->('SET_CHANGEABLE')
                EXPORTING
                  i_changeable                = iv_lock
                  i_suppress_dialog           = abap_true " Parameter missing in 702
                EXCEPTIONS
                  object_locked_by_other_user = 1
                  permission_failure          = 2
                  object_already_changeable   = 3
                  object_already_unlocked     = 4
                  object_just_created         = 5
                  object_deleted              = 6
                  object_modified             = 7
                  object_not_existing         = 8
                  object_invalid              = 9
                  unexpected_error            = 10
                  OTHERS                      = 11.
            CATCH cx_sy_dyn_call_param_not_found.
              ii_package->set_changeable(
                EXPORTING
                  i_changeable                = iv_lock
                EXCEPTIONS
                  object_locked_by_other_user = 1
                  permission_failure          = 2
                  object_already_changeable   = 3
                  object_already_unlocked     = 4
                  object_just_created         = 5
                  object_deleted              = 6
                  object_modified             = 7
                  object_not_existing         = 8
                  object_invalid              = 9
                  unexpected_error            = 10
                  OTHERS                      = 11 ).
          ENDTRY.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
        ENDIF.
    
        TRY.
            CALL METHOD ii_package->('SET_PERMISSIONS_CHANGEABLE')
              EXPORTING
                i_changeable                = iv_lock
                i_suppress_dialog           = abap_true " Parameter missing in 702
              EXCEPTIONS
                object_already_changeable   = 1
                object_already_unlocked     = 2
                object_locked_by_other_user = 3
                object_modified             = 4
                object_just_created         = 5
                object_deleted              = 6
                permission_failure          = 7
                object_invalid              = 8
                unexpected_error            = 9
                OTHERS                      = 10.
          CATCH cx_sy_dyn_call_param_not_found.
            ii_package->set_permissions_changeable(
              EXPORTING
                i_changeable                = iv_lock
              EXCEPTIONS
                object_already_changeable   = 1
                object_already_unlocked     = 2
                object_locked_by_other_user = 3
                object_modified             = 4
                object_just_created         = 5
                object_deleted              = 6
                permission_failure          = 7
                object_invalid              = 8
                unexpected_error            = 9
                OTHERS                      = 10 ).
        ENDTRY.
        IF ( sy-subrc = 1 AND iv_lock = abap_true ) OR ( sy-subrc = 2 AND iv_lock = abap_false ).
          " There's no getter to find out beforehand...
        ELSEIF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD unlock_and_raise_error.
    
        DATA ls_msg TYPE bal_s_msg.
    
        " Remember message since unlock overwrites it (for example with XT465)
        MOVE-CORRESPONDING sy TO ls_msg.
    
        set_lock( ii_package = ii_package
                  iv_lock    = abap_false ).
    
        zcx_abapgit_exception=>raise_t100(
          iv_msgid = ls_msg-msgid
          iv_msgno = ls_msg-msgno
          iv_msgv1 = ls_msg-msgv1
          iv_msgv2 = ls_msg-msgv2
          iv_msgv3 = ls_msg-msgv3
          iv_msgv4 = ls_msg-msgv4 ).
    
      ENDMETHOD.
    
      METHOD update_pinf_usages.
        DATA: lt_current_permissions TYPE tpak_permission_to_use_list,
              li_usage               TYPE REF TO if_package_permission_to_use,
              ls_data_sign           TYPE scomppsign,
              ls_add_permission_data TYPE pkgpermdat,
              lt_handled             TYPE SORTED TABLE OF i WITH UNIQUE KEY table_line.
        FIELD-SYMBOLS:  LIKE LINE OF it_usage_data.
    
        " Get the current permissions
        ii_package->get_permissions_to_use(
          IMPORTING
            e_permissions    = lt_current_permissions
          EXCEPTIONS
            object_invalid   = 1
            unexpected_error = 2
            OTHERS           = 3 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        ls_data_sign-err_sever = abap_true.
    
        " New permissions
        LOOP AT it_usage_data ASSIGNING .
          READ TABLE lt_current_permissions
               WITH KEY table_line->package_interface_name = -intf_name
               INTO li_usage.
    
          IF sy-subrc = 0 AND li_usage IS BOUND.
            INSERT sy-tabix INTO TABLE lt_handled.
    
            " Permission already exists, update attributes
            li_usage->set_all_attributes(
              EXPORTING
                i_permission_data     = 
                i_data_sign           = ls_data_sign
              EXCEPTIONS
                object_not_changeable = 1
                object_invalid        = 2
                intern_err            = 3
                OTHERS                = 4 ).
            IF sy-subrc <> 0.
              zcx_abapgit_exception=>raise_t100( ).
            ENDIF.
    
          ELSE.
            " Permission does not exist yet, add it
            MOVE-CORRESPONDING  TO ls_add_permission_data.
            ii_package->add_permission_to_use(
              EXPORTING
                i_pkg_permission_data   = ls_add_permission_data
              EXCEPTIONS
                object_not_changeable   = 1
                object_access_error     = 2
                object_already_existing = 3
                object_invalid          = 4
                unexpected_error        = 5
                OTHERS                  = 6 ).
            IF sy-subrc <> 0.
              zcx_abapgit_exception=>raise_t100( ).
            ENDIF.
    
          ENDIF.
    
          FREE li_usage.
        ENDLOOP.
    
        " Delete missing usages
        LOOP AT lt_current_permissions INTO li_usage.
          READ TABLE lt_handled WITH TABLE KEY table_line = sy-tabix TRANSPORTING NO FIELDS.
          IF sy-subrc = 0.
            CONTINUE.
          ENDIF.
    
          li_usage->delete(
            EXCEPTIONS
              object_not_changeable = 1
              object_invalid        = 2
    *          deletion_not_allowed  = 3 downport, does not exist in 7.30
              intern_err            = 4
              OTHERS                = 5 ).
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
        ENDLOOP.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        DATA li_package TYPE REF TO if_package.
    
        li_package = get_package( ).
        IF li_package IS BOUND.
          rv_user = li_package->changed_by.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: li_package TYPE REF TO if_package,
              lv_package TYPE devclass.
    
        " Package deletion is a bit tricky. A package can only be deleted if there are no objects
        " contained in it. This includes subpackages, so first the leaf packages need to be deleted.
        " Unfortunately deleted objects that are still contained in an unreleased transport request
        " also count towards the contained objects counter.
        " -> Currently we delete only empty packages
        "
        " If objects are deleted, the TADIR entry is deleted when the transport request is released.
        " So before we can delete the package, the transport which deletes the objects
        " in the package has to be released.
    
        lv_package = ms_item-obj_name.
    
        " Remove remaining OTR entries
        zcl_abapgit_sotr_handler=>delete_sotr_package( iv_package ).
    
        remove_obsolete_tadir( lv_package ).
    
        IF is_empty( lv_package ) = abap_true.
    
          li_package = load_package( lv_package ).
    
          IF li_package IS NOT BOUND.
            RETURN.
          ENDIF.
    
          IF lv_package(1) = '$'.
          ENDIF.
    
          set_lock( ii_package = li_package
                    iv_lock    = abap_true ).
    
          TRY.
              CALL METHOD li_package->('DELETE')
                EXPORTING
                  i_suppress_dialog     = abap_true  " Parameter missing in 702
                EXCEPTIONS
                  object_not_empty      = 1
                  object_not_changeable = 2
                  object_invalid        = 3
                  intern_err            = 4
                  OTHERS                = 5.
    
            CATCH cx_sy_dyn_call_param_not_found.
    
              li_package->delete(
                EXCEPTIONS
                  object_not_empty      = 1
                  object_not_changeable = 2
                  object_invalid        = 3
                  intern_err            = 4
                  OTHERS                = 5 ).
    
          ENDTRY.
    
          IF sy-subrc <> 0.
            unlock_and_raise_error( li_package ).
          ENDIF.
    
          TRY.
              CALL METHOD li_package->('SAVE')
                EXPORTING
                  i_suppress_dialog     = abap_true
                EXCEPTIONS
                  object_invalid        = 1
                  object_not_changeable = 2
                  cancelled_in_corr     = 3
                  permission_failure    = 4
                  unexpected_error      = 5
                  intern_err            = 6
                  OTHERS                = 7.
    
            CATCH cx_sy_dyn_call_param_not_found.
    
              li_package->save(
                EXCEPTIONS
                  object_invalid        = 1
                  object_not_changeable = 2
                  cancelled_in_corr     = 3
                  permission_failure    = 4
                  unexpected_error      = 5
                  intern_err            = 6
                  OTHERS                = 7 ).
    
          ENDTRY.
    
          IF sy-subrc <> 0.
            unlock_and_raise_error( li_package ).
          ENDIF.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: li_package      TYPE REF TO if_package,
              ls_package_data TYPE scompkdtln,
              ls_data_sign    TYPE scompksign,
              lt_usage_data   TYPE scomppdata,
              ls_save_sign    TYPE paksavsign.
    
        FIELD-SYMBOLS:  TYPE scomppdtln.
        FIELD-SYMBOLS:  TYPE any.
    
        mv_local_devclass = iv_package.
    
        io_xml->read(
          EXPORTING
            iv_name = 'DEVC'
          CHANGING
            cg_data = ls_package_data ).
    
        IF mv_local_devclass(1) = '$'.
          IF ls_package_data-mainpack = 'X'.
            zcx_abapgit_exception=>raise( |Main package { iv_package } cannot be used in local package| ).
          ELSEIF ls_package_data-mainpack = 'S'.
            zcx_abapgit_exception=>raise( |Structure package { iv_package } cannot be used in local package| ).
          ENDIF.
        ENDIF.
    
        li_package = get_package( ).
    
        " Swap out repository package name with the local installation package name
        ls_package_data-devclass = mv_local_devclass.
        IF li_package IS BOUND.
          ls_package_data-pdevclass = li_package->transport_layer.
        ENDIF.
    
        " For local packages store application component
        IF ls_package_data-devclass(1) = '$'.
        ENDIF.
    
        " Parent package is not changed. Assume the folder logic already created the package and set
        " the hierarchy before.
        CLEAR ls_package_data-parentcl.
    
        ASSIGN COMPONENT 'PACKKIND' OF STRUCTURE ls_package_data TO .
        IF sy-subrc = 0.
          set_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
        ASSIGN COMPONENT 'PACKKIND' OF STRUCTURE ls_data_sign TO .
        IF sy-subrc = 0.
           = abap_true.
        ENDIF.
    
    * Fields not set:
    * korrflag
    * parentcl
    * cli_check
    * intprefx
        IF ls_package_data-dlvunit IS NOT INITIAL.
          ls_data_sign-dlvunit = abap_true.
        ENDIF.
        ls_data_sign-ctext            = abap_true.
        ls_data_sign-as4user          = abap_true.
        ls_data_sign-pdevclass        = abap_true.
        ls_data_sign-comp_posid       = abap_true.
        ls_data_sign-component        = abap_true.
        ls_data_sign-perminher        = abap_true.
        ls_data_sign-packtype         = abap_true.
        ls_data_sign-restricted       = abap_true.
        ls_data_sign-mainpack         = abap_true.
        ls_data_sign-srv_check        = abap_true.
        ls_data_sign-ext_alias        = abap_true.
        ls_data_sign-project_guid     = abap_true.
        ls_data_sign-project_id       = abap_true.
        ls_data_sign-project_passdown = abap_true.
    
        IF ls_package_data-ctext IS INITIAL.
          ls_package_data-ctext = mv_local_devclass.
        ENDIF.
        IF ls_package_data-dlvunit IS INITIAL.
          ls_package_data-dlvunit = 'HOME'.
        ENDIF.
    
        ls_package_data-as4user = sy-uname.
    
        IF li_package IS BOUND.
          " Package already exists, change it
          set_lock( ii_package = li_package
                    iv_lock    = abap_true ).
    
          li_package->set_all_attributes(
            EXPORTING
              i_package_data             = ls_package_data
              i_data_sign                = ls_data_sign
            EXCEPTIONS
              object_not_changeable      = 1
              object_deleted             = 2
              object_invalid             = 3
              short_text_missing         = 4
              author_not_existing        = 5
              local_package              = 6
              software_component_invalid = 7
              layer_invalid              = 8
              korrflag_invalid           = 9
              component_not_existing     = 10
              component_missing          = 11
              authorize_failure          = 12
              prefix_in_use              = 13
              unexpected_error           = 14
              intern_err                 = 15
    *          wrong_mainpack_value       = 16  downport, does not exist in 7.30
    *          superpackage_invalid       = 17  downport, does not exist in 7.30
              OTHERS                     = 18 ).
          IF sy-subrc <> 0.
            unlock_and_raise_error( li_package ).
          ENDIF.
    
        ELSE.
          " Package does not exist yet, create it
          " This shouldn't really happen, because the folder logic initially creates the packages.
          cl_package_factory=>create_new_package(
            IMPORTING
              e_package                  = li_package
            CHANGING
              c_package_data             = ls_package_data
            EXCEPTIONS
              object_already_existing    = 1
              object_just_created        = 2
              not_authorized             = 3
              wrong_name_prefix          = 4
              undefined_name             = 5
              reserved_local_name        = 6
              invalid_package_name       = 7
              short_text_missing         = 8
              software_component_invalid = 9
              layer_invalid              = 10
              author_not_existing        = 11
              component_not_existing     = 12
              component_missing          = 13
              prefix_in_use              = 14
              unexpected_error           = 15
              intern_err                 = 16
              no_access                  = 17
    *          invalid_translation_depth  = 18 downport, does not exist in 7.30
    *          wrong_mainpack_value       = 19 downport, does not exist in 7.30
    *          superpackage_invalid       = 20 downport, does not exist in 7.30
    *          error_in_cts_checks        = 21 downport, does not exist in 7.31
              OTHERS                     = 22 ).
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
        ENDIF.
    
        " Load package interface usages
        TRY.
            io_xml->read(
              EXPORTING
                iv_name = 'PERMISSION'
              CHANGING
                cg_data = lt_usage_data ).
          CATCH zcx_abapgit_exception ##NO_HANDLER.
            " No permissions saved
        ENDTRY.
    
        LOOP AT lt_usage_data ASSIGNING .
          -client_pak = mv_local_devclass.
        ENDLOOP.
    
        update_pinf_usages( ii_package    = li_package
                            it_usage_data = lt_usage_data ).
    
        ls_save_sign-pack   = abap_true.
        ls_save_sign-permis = abap_true.
        ls_save_sign-elems  = abap_true.
        ls_save_sign-interf = abap_true.
    
        li_package->save_generic(
          EXPORTING
            i_save_sign           = ls_save_sign
            i_transport_request   = iv_transport
            i_suppress_dialog     = abap_true
          EXCEPTIONS
            cancelled_in_corr     = 1
            permission_failure    = 2
            object_not_changeable = 3
            object_invalid        = 4
            OTHERS                = 5 ).
        IF sy-subrc <> 0.
          unlock_and_raise_error( li_package ).
        ENDIF.
    
        set_lock( ii_package = li_package
                  iv_lock    = abap_false ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
        " Check remote package if deserialize has not been called before this
        IF mv_local_devclass IS INITIAL.
          rv_bool = abap_false.
        ELSE.
          cl_package_helper=>check_package_existence(
            EXPORTING
              i_package_name          = mv_local_devclass
            IMPORTING
              e_package_exists        = rv_bool
            EXCEPTIONS
              intern_err              = 1
              OTHERS                  = 2 ).
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'EEUDB'
                                                iv_argument    = ms_item-obj_name
                                                iv_prefix      = 'DV' ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
    
        IF iv_item_part_of_filename <> zcl_abapgit_filename_logic=>c_package_file-obj_name.
          zcx_abapgit_exception=>raise( |Unexpected filename for package { cs_item-obj_name }| ).
        ENDIF.
    
        " Try to get a unique package name for DEVC by using the path
        cs_item-obj_name = zcl_abapgit_folder_logic=>get_instance( )->path_to_package(
          iv_top                  = iv_package
          io_dot                  = io_dot
          iv_create_if_not_exists = abap_false
          iv_path                 = iv_path ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
    
        " Packages have a fixed filename so that the repository can be installed to a different
        " package(-hierarchy) on the client and not show up as a different package in the repo.
        cv_item_part_of_filename = zcl_abapgit_filename_logic=>c_package_file-obj_name.
        " use just obj_name ("package") so that e.g. translation files also have this first part
        " yet be able to modify the extension e.g. package.i18n.de.po
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
        DATA: ls_package_data TYPE scompkdtln,
              li_package      TYPE REF TO if_package,
              lt_intf_usages  TYPE tpak_permission_to_use_list,
              lt_usage_data   TYPE scomppdata,
              ls_usage_data   TYPE scomppdtln,
              li_usage        TYPE REF TO if_package_permission_to_use.
    
        FIELD-SYMBOLS:  TYPE any.
    
        li_package = get_package( ).
        IF li_package IS NOT BOUND.
          zcx_abapgit_exception=>raise( |Could not find package to serialize.| ).
        ENDIF.
    
        li_package->get_all_attributes(
          IMPORTING
            e_package_data  = ls_package_data
          EXCEPTIONS
            object_invalid  = 1
            package_deleted = 2
            intern_err      = 3
            OTHERS          = 4 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        " For local packages get application component
        IF is_local( ls_package_data-devclass ) = abap_true.
        ENDIF.
    
        CLEAR: ls_package_data-devclass,
               ls_package_data-parentcl.
    
        " Clear administrative data to prevent diffs
        CLEAR: ls_package_data-created_by,
               ls_package_data-created_on,
               ls_package_data-changed_by,
               ls_package_data-changed_on,
               ls_package_data-as4user.
    
        " Clear text descriptions that might be localized
        CLEAR: ls_package_data-comp_text,
               ls_package_data-dlvu_text,
               ls_package_data-layer_text.
    
        " Clear obsolete fields
        CLEAR: ls_package_data-intfprefx,
               ls_package_data-cli_check.
    
        " If software component is related to add-on and a valid namespace, then keep it
        adjust_sw_component( CHANGING cv_dlvunit = ls_package_data-dlvunit ).
    
        ASSIGN COMPONENT 'TRANSLATION_DEPTH_TEXT'
               OF STRUCTURE ls_package_data
               TO .
        IF sy-subrc = 0.
          CLEAR: .
        ENDIF.
    
        ASSIGN COMPONENT 'TRANSLATION_GRAPH_DEPTH_TEXT'
               OF STRUCTURE ls_package_data
               TO .
        IF sy-subrc = 0.
          CLEAR: .
        ENDIF.
    
        " Clear things related to local installation package
        CLEAR: ls_package_data-namespace,
               ls_package_data-dlvunit,
               ls_package_data-tpclass,
               ls_package_data-pdevclass.
    
        " Not usable on customer systems
        ASSIGN COMPONENT 'TRANSLATION_DEPTH'
               OF STRUCTURE ls_package_data
               TO .
        IF sy-subrc = 0.
          CLEAR: .
        ENDIF.
    
        ASSIGN COMPONENT 'TRANSLATION_GRAPH_DEPTH'
               OF STRUCTURE ls_package_data
               TO .
        IF sy-subrc = 0.
          CLEAR: .
        ENDIF.
    
        CLEAR: ls_package_data-korrflag.
    
        ASSIGN COMPONENT 'PACKKIND' OF STRUCTURE ls_package_data TO .
        IF sy-subrc = 0.
          clear_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
    
        io_xml->add( iv_name = 'DEVC'
                     ig_data = ls_package_data ).
    
        " Save package interface usages
        li_package->get_permissions_to_use(
          IMPORTING
            e_permissions    = lt_intf_usages
          EXCEPTIONS
            object_invalid   = 1
            unexpected_error = 2
            OTHERS           = 3 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        LOOP AT lt_intf_usages INTO li_usage.
          li_usage->get_all_attributes(
            IMPORTING
              e_permission_data = ls_usage_data
            EXCEPTIONS
              object_invalid    = 1
              intern_err        = 2
              OTHERS            = 3 ).
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          CLEAR: ls_usage_data-pack_name, ls_usage_data-client_pak.
    
          APPEND ls_usage_data TO lt_usage_data.
        ENDLOOP.
    
        IF lt_usage_data IS NOT INITIAL.
          io_xml->add( iv_name = 'PERMISSION'
                       ig_data = lt_usage_data ).
        ENDIF.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_dial IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
        rv_user = c_user_unknown. " not stored by SAP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: ls_bcdata TYPE bdcdata,
              lt_bcdata TYPE STANDARD TABLE OF bdcdata.
    
        ls_bcdata-program  = 'SAPMSDIA'.
        ls_bcdata-dynpro   = '1010'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam     = 'DIAPAR-DNAM'.
        ls_bcdata-fval     = ms_item-obj_name.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam     = 'RS38L-PARM'.
        ls_bcdata-fval     = abap_true.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam = 'BDC_OKCODE'.
        ls_bcdata-fval = '=DELF'.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-program  = 'SAPLSPO1'.
        ls_bcdata-dynpro   = '0100'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam = 'BDC_OKCODE'.
        ls_bcdata-fval = '=YES'.
        APPEND ls_bcdata TO lt_bcdata.
    
        ls_bcdata-program  = 'SAPMSDIA'.
        ls_bcdata-dynpro   = '1010'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam = 'BDC_OKCODE'.
        ls_bcdata-fval = '=BACK'.
        APPEND ls_bcdata TO lt_bcdata.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode      = 'SE35'
          it_bdcdata    = lt_bcdata
          iv_new_window = abap_false ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_dialog_module TYPE ty_dialog_module.
    
        " Prefill popup asking for package
        set_default_package( iv_package ).
    
        io_xml->read(
          EXPORTING
            iv_name = 'DIAL'
          CHANGING
            cg_data = ls_dialog_module ).
    
        CALL FUNCTION 'RS_DIALOG_CREATE'
          EXPORTING
            dialogname            = ls_dialog_module-tdct-dnam
            dynpronumber          = ls_dialog_module-tdct-dynr
            programname           = ls_dialog_module-tdct-prog
            suppress_corr_check   = abap_false
    *     It seems that dia_par parameter doesn't do anything, but we can't omit it
    *     Parameters are inserted below
          TABLES
            dia_par               = ls_dialog_module-dia_pars
          EXCEPTIONS
            dialog_already_exists = 1
            invalid_name          = 2
            OTHERS                = 3.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error deserializing dialogmodule { ms_item-obj_name }| ).
        ENDIF.
    
        " It seems that there's no API for diapar, therefore we manipulate it directly
        INSERT diapar FROM TABLE ls_dialog_module-dia_pars.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: ls_tdct TYPE tdct.
    
        ls_tdct = _read_tdct( ).
    
        rv_bool = boolc( ls_tdct IS NOT INITIAL ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
    
        rs_metadata = get_metadata( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: lv_objectname TYPE tdct-dnam.
    
        lv_objectname = ms_item-obj_name.
    
        CALL FUNCTION 'RS_DIALOG_SHOW'
          EXPORTING
            objectname       = lv_objectname
            type             = 'VW'
          EXCEPTIONS
            object_not_found = 1
            OTHERS           = 2.
    
        rv_exit = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA ls_dialog_module TYPE ty_dialog_module.
    
        ls_dialog_module-tdct = _read_tdct( ).
    
        SELECT * FROM diapar
          INTO TABLE ls_dialog_module-dia_pars
          WHERE dnam = ls_dialog_module-tdct-dnam
          ORDER BY PRIMARY KEY.
    
        io_xml->add( iv_name = 'DIAL'
                     ig_data = ls_dialog_module ).
    
      ENDMETHOD.
    
      METHOD _read_tdct.
    
        DATA: lv_dnam TYPE tdct-dnam.
    
        lv_dnam = ms_item-obj_name.
    
        SELECT SINGLE * FROM tdct
               INTO rs_tdct
               WHERE dnam = lv_dnam.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_doct IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mi_longtexts = zcl_abapgit_factory=>get_longtexts( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        rv_user = mi_longtexts->changed_by(
                      iv_object_name = ms_item-obj_name
                      iv_longtext_id = c_id ).
    
        IF rv_user IS INITIAL.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        mi_longtexts->delete(
            iv_object_name = ms_item-obj_name
            iv_longtext_id = c_id ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        mi_longtexts->deserialize(
          iv_longtext_name = c_name
          iv_object_name   = ms_item-obj_name
          iv_longtext_id   = c_id
          ii_xml           = io_xml
          iv_main_language = mv_language ).
    
        tadir_insert( iv_package ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_id     TYPE dokil-id,
              lv_object TYPE dokhl-object.
    
        lv_object = ms_item-obj_name.
    
        SELECT SINGLE id FROM dokil INTO lv_id
          WHERE id         = c_id
            AND object     = lv_object.     "#EC CI_GENBUFF "#EC CI_NOORDER
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: ls_dokentry TYPE dokentry,
              ls_bcdata   TYPE bdcdata,
              lt_bcdata   TYPE STANDARD TABLE OF bdcdata.
    
        " We need to modify dokentry directly, otherwise
        " Batch Input on SE61 wouldn't work because it stores
        " the last seen Document Class in this table. There's
        " no standard function to do this. SE61 does this
        " directly in its dialog modules
        ls_dokentry-username = sy-uname.
        ls_dokentry-langu    = mv_language.
        ls_dokentry-class    = c_id.
        MODIFY dokentry FROM ls_dokentry.
    
        ls_bcdata-program  = 'SAPMSDCU'.
        ls_bcdata-dynpro   = '0100'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam     = 'RSDCU-OBJECT7'.
        ls_bcdata-fval     = ms_item-obj_name.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam = 'BDC_OKCODE'.
        ls_bcdata-fval = '=SHOW'.
        APPEND ls_bcdata TO lt_bcdata.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'SE61'
          it_bdcdata = lt_bcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        mi_longtexts->serialize(
            iv_longtext_name = c_name
            iv_object_name = ms_item-obj_name
            iv_longtext_id = c_id
            io_i18n_params = mo_i18n_params
            ii_xml         = io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_docv IMPLEMENTATION.
    
      METHOD constructor.
    
        DATA: lv_prefix    TYPE namespace,
              lv_bare_name TYPE progname.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        IF ms_item-obj_name(2) <> 'DT' AND ms_item-obj_name NP '/*/DT*'. " IN, MO, UO, UP
          mv_id         = ms_item-obj_name(2).
          mv_doc_object = ms_item-obj_name+2.
        ELSE. " DT
          CALL FUNCTION 'RS_NAME_SPLIT_NAMESPACE'
            EXPORTING
              name_with_namespace    = ms_item-obj_name
            IMPORTING
              namespace              = lv_prefix
              name_without_namespace = lv_bare_name
            EXCEPTIONS
              delimiter_error        = 1
              OTHERS                 = 2.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( |Error determining namespace for { ms_item-obj_type } { ms_item-obj_name }| ).
          ENDIF.
    
          mv_id         = lv_bare_name(2).
          mv_doc_object = |{ lv_prefix }{ lv_bare_name+2 }|.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD read.
    
        CALL FUNCTION 'DOCU_READ'
          EXPORTING
            id       = mv_id
            langu    = mv_language
            object   = mv_doc_object
            typ      = c_typ
            version  = c_version
          IMPORTING
            doktitle = rs_data-doctitle
            head     = rs_data-head
          TABLES
            line     = rs_data-lines.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        rv_user = read( )-head-tdluser.
        IF rv_user IS INITIAL.
          rv_user = c_user_unknown.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        CALL FUNCTION 'DOCU_DEL'
          EXPORTING
            id       = mv_id
            langu    = mv_language
            object   = mv_doc_object
            typ      = c_typ
          EXCEPTIONS
            ret_code = 1
            OTHERS   = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_data TYPE ty_data.
    
        io_xml->read( EXPORTING iv_name = c_name
                      CHANGING cg_data = ls_data ).
    
        CALL FUNCTION 'DOCU_UPDATE'
          EXPORTING
            head    = ls_data-head
            state   = 'A'
            typ     = c_typ
            version = c_version
          TABLES
            line    = ls_data-lines.
    
        tadir_insert( iv_package ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        SELECT SINGLE id FROM dokil INTO mv_id
           WHERE id     = mv_id
             AND object = mv_doc_object.    "#EC CI_GENBUFF "#EC CI_NOORDER
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_data   TYPE ty_data.
    
        ls_data = read( ).
    
        CLEAR: ls_data-head-tdfuser,
               ls_data-head-tdfreles,
               ls_data-head-tdfdate,
               ls_data-head-tdftime,
               ls_data-head-tdluser,
               ls_data-head-tdlreles,
               ls_data-head-tdldate,
               ls_data-head-tdltime.
    
        io_xml->add( iv_name = c_name
                     ig_data = ls_data ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_doma IMPLEMENTATION.
    
      METHOD adjust_exit.
    
        DATA lv_function TYPE funcname.
    
        IF cv_exit IS NOT INITIAL.
          lv_function = |CONVERSION_EXIT_{ cv_exit }_INPUT|.
    
          " If exit function does not exist, remove it
          IF zcl_abapgit_factory=>get_function_module( )->function_exists( lv_function ) = abap_false.
            cv_exit = ''.
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD check_exit.
    
        DATA lv_exit TYPE dd01v-convexit.
    
        rv_done = abap_true.
    
        IF iv_exit IS NOT INITIAL.
          " Check if exit function is set correctly
          SELECT SINGLE convexit FROM dd01v INTO lv_exit WHERE domname = ms_item-obj_name.
          IF sy-subrc = 0 AND lv_exit <> iv_exit.
            rv_done = abap_false.
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD deserialize_texts.
    
        DATA: lv_name       TYPE ddobjname,
              lv_valpos     TYPE valpos,
              ls_dd01v_tmp  TYPE dd01v,
              lt_dd07v_tmp  TYPE TABLE OF dd07v,
              lt_i18n_langs TYPE TABLE OF langu,
              lt_dd01_texts TYPE ty_dd01_texts,
              lt_dd07_texts TYPE ty_dd07_texts.
    
        FIELD-SYMBOLS:       LIKE LINE OF lt_i18n_langs,
                            LIKE LINE OF it_dd07v,
                        LIKE LINE OF lt_dd01_texts,
                        LIKE LINE OF lt_dd07_texts.
    
        lv_name = ms_item-obj_name.
    
        ii_xml->read( EXPORTING iv_name = 'I18N_LANGS'
                      CHANGING  cg_data = lt_i18n_langs ).
    
        ii_xml->read( EXPORTING iv_name = 'DD01_TEXTS'
                      CHANGING  cg_data = lt_dd01_texts ).
    
        ii_xml->read( EXPORTING iv_name = 'DD07_TEXTS'
                      CHANGING  cg_data = lt_dd07_texts ).
    
        mo_i18n_params->trim_saplang_list( CHANGING ct_sap_langs = lt_i18n_langs ).
    
        SORT lt_i18n_langs.
        SORT lt_dd07_texts BY ddlanguage. " Optimization
    
        LOOP AT lt_i18n_langs ASSIGNING .
    
          " Domain description
          ls_dd01v_tmp = is_dd01v.
          READ TABLE lt_dd01_texts ASSIGNING  WITH KEY ddlanguage = .
          IF sy-subrc > 0.
            zcx_abapgit_exception=>raise( |DD01_TEXTS cannot find lang {  } in XML| ).
          ENDIF.
          MOVE-CORRESPONDING  TO ls_dd01v_tmp.
    
          " Domain values
          lt_dd07v_tmp = it_dd07v.
          LOOP AT lt_dd07v_tmp ASSIGNING .
            lv_valpos = -valpos.
            " it_dd07v was potentially renumbered so lookup by value
            READ TABLE lt_dd07_texts ASSIGNING 
              WITH KEY ddlanguage =  domvalue_l = -domvalue_l domvalue_h = -domvalue_h.
            IF sy-subrc = 0.
              MOVE-CORRESPONDING  TO .
              -valpos = lv_valpos.
              DELETE lt_dd07_texts INDEX sy-tabix. " Optimization
            ELSE.
              " no translation -> keep entry but clear texts
              -ddlanguage = .
              CLEAR: -ddtext, -domval_ld, -domval_hd.
            ENDIF.
          ENDLOOP.
    
          CALL FUNCTION 'DDIF_DOMA_PUT'
            EXPORTING
              name              = lv_name
              dd01v_wa          = ls_dd01v_tmp
            TABLES
              dd07v_tab         = lt_dd07v_tmp
            EXCEPTIONS
              doma_not_found    = 1
              name_inconsistent = 2
              doma_inconsistent = 3
              put_failure       = 4
              put_refused       = 5
              OTHERS            = 6.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD handle_dependencies.
    
        " For domains with dependency on conversion exit function, we use two phases:
        " 1) DDIC phase:
        "    - If function does not exit, remove the exit function
        " 2) LATE phase
        "    - If function was removed, change it to the correct exit function
        CASE iv_step.
          WHEN zif_abapgit_object=>gc_step_id-ddic.
            adjust_exit( CHANGING cv_exit = cv_exit ).
    
          WHEN zif_abapgit_object=>gc_step_id-late.
            cv_done = check_exit( cv_exit ).
    
          WHEN zif_abapgit_object=>gc_step_id-lxe.
            cv_done = abap_true.
    
          WHEN OTHERS.
            ASSERT 0 = 1.
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD serialize_texts.
    
        DATA: lv_name            TYPE ddobjname,
              lv_index           TYPE i,
              ls_dd01v           TYPE dd01v,
              lt_dd07v           TYPE TABLE OF dd07v,
              lt_i18n_langs      TYPE TABLE OF langu,
              lt_dd01_texts      TYPE ty_dd01_texts,
              lt_dd07_texts      TYPE ty_dd07_texts,
              lt_language_filter TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        FIELD-SYMBOLS:       LIKE LINE OF lt_i18n_langs,
                            LIKE LINE OF lt_dd07v,
                        LIKE LINE OF lt_dd07v,
                        LIKE LINE OF lt_dd01_texts,
                        LIKE LINE OF lt_dd07_texts.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          RETURN.
        ENDIF.
    
        lv_name = ms_item-obj_name.
    
        " Collect additional languages, skip main lang - it was serialized already
        lt_language_filter = mo_i18n_params->build_language_filter( ).
    
        SELECT DISTINCT ddlanguage AS langu INTO TABLE lt_i18n_langs
          FROM dd01v
          WHERE domname = lv_name
          AND ddlanguage IN lt_language_filter
          AND ddlanguage <> mv_language
          ORDER BY langu.                                     "#EC CI_SUBRC
    
        SELECT DISTINCT ddlanguage AS langu APPENDING TABLE lt_i18n_langs
          FROM dd07v
          WHERE domname = lv_name
          AND ddlanguage IN lt_language_filter
          AND ddlanguage <> mv_language
          ORDER BY langu.                                     "#EC CI_SUBRC
    
        SORT lt_i18n_langs.
        DELETE ADJACENT DUPLICATES FROM lt_i18n_langs.
    
        LOOP AT lt_i18n_langs ASSIGNING .
          lv_index = sy-tabix.
    
          CALL FUNCTION 'DDIF_DOMA_GET'
            EXPORTING
              name          = lv_name
              langu         = 
            IMPORTING
              dd01v_wa      = ls_dd01v
            TABLES
              dd07v_tab     = lt_dd07v
            EXCEPTIONS
              illegal_input = 1
              OTHERS        = 2.
          IF sy-subrc <> 0.
            DELETE lt_i18n_langs INDEX lv_index. " Don't save this lang
            CONTINUE.
          ENDIF.
    
          IF ls_dd01v-ddlanguage IS INITIAL.
            ls_dd01v-ddlanguage = .
          ENDIF.
    
          APPEND INITIAL LINE TO lt_dd01_texts ASSIGNING .
          MOVE-CORRESPONDING ls_dd01v TO .
    
          " Process main language entries and find corresponding translation
          LOOP AT it_dd07v ASSIGNING  WHERE NOT ddlanguage IS INITIAL.
            APPEND INITIAL LINE TO lt_dd07_texts ASSIGNING .
            READ TABLE lt_dd07v ASSIGNING 
              WITH KEY ddlanguage =  domvalue_l = -domvalue_l domvalue_h = -domvalue_h.
            IF sy-subrc = 0.
              MOVE-CORRESPONDING  TO .
            ELSE.
              " no translation -> keep entry but clear texts
              MOVE-CORRESPONDING  TO .
              -ddlanguage = .
              CLEAR: -ddtext, -domval_ld, -domval_hd.
            ENDIF.
          ENDLOOP.
    
        ENDLOOP.
    
        SORT lt_i18n_langs ASCENDING.
        SORT lt_dd01_texts BY ddlanguage ASCENDING.
        SORT lt_dd07_texts BY valpos ASCENDING ddlanguage ASCENDING.
    
        IF lines( lt_i18n_langs ) > 0.
          ii_xml->add( iv_name = 'I18N_LANGS'
                       ig_data = lt_i18n_langs ).
    
          ii_xml->add( iv_name = 'DD01_TEXTS'
                       ig_data = lt_dd01_texts ).
    
          ii_xml->add( iv_name = 'DD07_TEXTS'
                       ig_data = lt_dd07_texts ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE as4user FROM dd01l INTO rv_user
          WHERE domname = ms_item-obj_name
          AND as4local = 'A'
          AND as4vers = '0000'.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        delete_ddic( iv_objtype              = 'D'
                     iv_no_ask_delete_append = abap_true ).
    
        delete_longtexts( c_longtext_id_doma ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
    * package SEDD
    * package SDIC
    
        DATA: lv_name  TYPE ddobjname,
              lv_done  TYPE abap_bool,
              ls_dd01v TYPE dd01v,
              ls_extra TYPE ty_extra,
              lt_dd07v TYPE TABLE OF dd07v.
    
        FIELD-SYMBOLS  TYPE dd07v.
    
        io_xml->read( EXPORTING iv_name = 'DD01V'
                      CHANGING  cg_data = ls_dd01v ).
        io_xml->read( EXPORTING iv_name = 'DD07V_TAB'
                      CHANGING  cg_data = lt_dd07v ).
    
        handle_dependencies(
          EXPORTING
            iv_step = iv_step
          CHANGING
            cv_exit = ls_dd01v-convexit
            cv_done = lv_done ).
    
        IF lv_done = abap_true.
          RETURN.
        ENDIF.
    
        corr_insert( iv_package      = iv_package
                     ig_object_class = 'DICT' ).
    
        lv_name = ms_item-obj_name. " type conversion
    
        LOOP AT lt_dd07v ASSIGNING .
          -domname = lv_name.
          -valpos = sy-tabix.
        ENDLOOP.
    
        CALL FUNCTION 'DDIF_DOMA_PUT'
          EXPORTING
            name              = lv_name
            dd01v_wa          = ls_dd01v
          TABLES
            dd07v_tab         = lt_dd07v
          EXCEPTIONS
            doma_not_found    = 1
            name_inconsistent = 2
            doma_inconsistent = 3
            put_failure       = 4
            put_refused       = 5
            OTHERS            = 6.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        " Fields that are not part of dd01v
        io_xml->read( EXPORTING iv_name = 'DD01L_EXTRA'
                      CHANGING  cg_data = ls_extra ).
    
        TRY.
            set_abap_language_version( CHANGING cv_abap_language_version = ls_extra-abap_language_version ).
    
            UPDATE ('DD01L') SET abap_language_version = ls_extra-abap_language_version WHERE domname = lv_name.
          CATCH cx_sy_dynamic_osql_semantics ##NO_HANDLER.
        ENDTRY.
    
        IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
          deserialize_texts(
            ii_xml   = io_xml
            is_dd01v = ls_dd01v
            it_dd07v = lt_dd07v ).
        ENDIF.
    
        deserialize_longtexts( ii_xml         = io_xml
                               iv_longtext_id = c_longtext_id_doma ).
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA lv_domname TYPE dd01l-domname.
    
        SELECT SINGLE domname FROM dd01l INTO lv_domname
          WHERE domname = ms_item-obj_name.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESDICT'
                                                iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by ZCL_ABAPGIT_OBJECT=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_name    TYPE ddobjname,
              lv_state   TYPE ddgotstate,
              ls_dd01v   TYPE dd01v,
              ls_extra   TYPE ty_extra,
              lv_masklen TYPE c LENGTH 4,
              lt_dd07v   TYPE TABLE OF dd07v.
    
        FIELD-SYMBOLS  TYPE dd07v.
        FIELD-SYMBOLS  TYPE any.
    
        lv_name = ms_item-obj_name.
    
        CALL FUNCTION 'DDIF_DOMA_GET'
          EXPORTING
            name          = lv_name
            state         = 'A'
            langu         = mv_language
          IMPORTING
            gotstate      = lv_state
            dd01v_wa      = ls_dd01v
          TABLES
            dd07v_tab     = lt_dd07v
          EXCEPTIONS
            illegal_input = 1
            OTHERS        = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        IF ls_dd01v IS INITIAL OR lv_state <> 'A'.
          RETURN.
        ENDIF.
    
        CLEAR: ls_dd01v-as4user,
               ls_dd01v-as4date,
               ls_dd01v-as4time,
               ls_dd01v-appexist.
    
        ASSIGN COMPONENT 'ACTFLAG' OF STRUCTURE ls_dd01v TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
    
    * make sure XML serialization does not dump if the field contains invalid data
    * note that this is a N field, so '' is not valid
        IF ls_dd01v-authclass = ''.
          CLEAR ls_dd01v-authclass.
        ENDIF.
        lv_masklen = ls_dd01v-masklen.
        IF lv_masklen = '' OR NOT lv_masklen CO '0123456789'.
          CLEAR ls_dd01v-masklen.
        ENDIF.
    
        DELETE lt_dd07v WHERE appval = abap_true.
    
        SORT lt_dd07v BY
          valpos ASCENDING
          ddlanguage ASCENDING.
    
        LOOP AT lt_dd07v ASSIGNING .
          CLEAR -domname.
        ENDLOOP.
    
        io_xml->add( iv_name = 'DD01V'
                     ig_data = ls_dd01v ).
        io_xml->add( iv_name = 'DD07V_TAB'
                     ig_data = lt_dd07v ).
    
        ls_extra-abap_language_version = get_abap_language_version( ).
    
        io_xml->add( iv_name = 'DD01L_EXTRA'
                     ig_data = ls_extra ).
    
        IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
          serialize_texts(
            ii_xml   = io_xml
            it_dd07v = lt_dd07v ).
        ENDIF.
    
        serialize_longtexts( ii_xml         = io_xml
                             iv_longtext_id = c_longtext_id_doma ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_dras IMPLEMENTATION.
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_dras_handler          TYPE REF TO object,
              lo_dras_source_container TYPE REF TO object,
              lv_object_key            TYPE seu_objkey,
              lv_exists                TYPE abap_bool,
              lx_error                 TYPE REF TO cx_root.
    
        TRY.
            lv_object_key = ms_item-obj_name.
            CALL METHOD ('CL_DRAS_AFF_OBJECT_HANDLER')=>('GET_DDIC_HANDLER')
              EXPORTING
                object_key = lv_object_key
              RECEIVING
                handler    = lo_dras_handler.
    
            CALL METHOD lo_dras_handler->('IF_DD_DRAS_WB_HANDLER~CHECK_EXISTENCE')
              EXPORTING
                iv_as4local = 'A'
              RECEIVING
                rv_exists   = lv_exists.
    
            IF lv_exists = abap_true.
              CALL METHOD lo_dras_handler->('IF_DD_DRAS_WB_HANDLER~GET_SOURCE_CONTAINER')
                EXPORTING
                  iv_as4local = 'A'
                RECEIVING
                  ro_result   = lo_dras_source_container.
              CALL METHOD lo_dras_source_container->('IF_DD_DRAS_SOURCE_CONTAINER~GET_AS4USER')
                RECEIVING
                  rv_as4user = rv_user.
            ENDIF.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
      ENDMETHOD.
    
      METHOD get_additional_extensions.
        DATA ls_additional_extension LIKE LINE OF rv_additional_extensions.
        ls_additional_extension-extension = 'acds'.
        CALL METHOD ('CL_CDS_AFF_FILE_NAME_MAPPER')=>for_cds
          RECEIVING
            result = ls_additional_extension-file_name_mapper.
        APPEND ls_additional_extension TO rv_additional_extensions.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_drty IMPLEMENTATION.
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_drty_handler TYPE REF TO object,
              lv_object_key   TYPE seu_objkey,
              lx_error        TYPE REF TO cx_root.
    
        TRY.
            lv_object_key = ms_item-obj_name.
            CALL METHOD ('CL_DRTY_AFF_OBJECT_HANDLER')=>('GET_DDIC_HANDLER')
              EXPORTING
                object_key = lv_object_key
              RECEIVING
                handler    = lo_drty_handler.
    
            CALL METHOD lo_drty_handler->('GET_CHANGED_BY')
              RECEIVING
                rv_changed_by = rv_user.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
      ENDMETHOD.
    
      METHOD get_additional_extensions.
        DATA ls_additional_extension LIKE LINE OF rv_additional_extensions.
        ls_additional_extension-extension = 'acds'.
        CALL METHOD ('CL_CDS_AFF_FILE_NAME_MAPPER')=>for_cds
          RECEIVING
            result = ls_additional_extension-file_name_mapper.
        APPEND ls_additional_extension TO rv_additional_extensions.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_drul IMPLEMENTATION.
    
      METHOD clear_field.
    
        FIELD-SYMBOLS:  TYPE data.
    
        ASSIGN COMPONENT iv_fieldname OF STRUCTURE cs_dependency_rule
               TO .
        ASSERT sy-subrc = 0.
    
        CLEAR: .
    
      ENDMETHOD.
    
      METHOD clear_fields.
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-CREATED_AT'
          CHANGING
            cs_dependency_rule = cs_dependency_rule ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-CREATED_BY'
          CHANGING
            cs_dependency_rule = cs_dependency_rule ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-CHANGED_AT'
          CHANGING
            cs_dependency_rule = cs_dependency_rule ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-CHANGED_BY'
          CHANGING
            cs_dependency_rule = cs_dependency_rule ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-MASTER_LANGUAGE'
          CHANGING
            cs_dependency_rule = cs_dependency_rule ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-MASTER_SYSTEM'
          CHANGING
            cs_dependency_rule = cs_dependency_rule ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-RESPONSIBLE'
          CHANGING
            cs_dependency_rule = cs_dependency_rule ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-PACKAGE_REF'
          CHANGING
            cs_dependency_rule = cs_dependency_rule ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'CONTENT-SOURCE'
          CHANGING
            cs_dependency_rule = cs_dependency_rule ).
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_dependency_rule_key = ms_item-obj_name.
    
        TRY.
            CREATE DATA mr_dependency_rule TYPE ('CL_BLUE_SOURCE_OBJECT_DATA=>TY_OBJECT_DATA').
            CREATE OBJECT mi_persistence TYPE ('CL_DRUL_WB_OBJECT_PERSIST').
    
          CATCH cx_sy_create_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD fill_metadata_from_db.
    
        DATA:
          li_wb_object_operator          TYPE REF TO object,
          lr_dependency_rule_old         TYPE REF TO data,
          lv_drul_object_data_clas_exist TYPE c LENGTH 1.
    
        FIELD-SYMBOLS:
           TYPE any,
                    TYPE xsddatetime_z,
                    TYPE syuname,
                TYPE xsddatetime_z,
                TYPE syuname.
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        CREATE DATA lr_dependency_rule_old TYPE ('CL_BLUE_SOURCE_OBJECT_DATA2=>TY_OBJECT_DATA').
        CALL FUNCTION 'CHECK_EXIST_CLAS'
          EXPORTING
            name            = 'CL_DRUL_WB_OBJECT_DATA'
          IMPORTING
            exist           = lv_drul_object_data_clas_exist
          EXCEPTIONS
            tr_invalid_type = 1
            OTHERS          = 2.
    
        IF sy-subrc = 0 AND lv_drul_object_data_clas_exist = abap_true.
          CREATE DATA lr_dependency_rule_old TYPE ('CL_DRUL_WB_OBJECT_DATA=>TY_OBJECT_DATA').
        ELSE.
          CREATE DATA lr_dependency_rule_old TYPE ('CL_BLUE_SOURCE_OBJECT_DATA=>TY_OBJECT_DATA').
        ENDIF.
        ASSIGN lr_dependency_rule_old->* TO .
        ASSERT sy-subrc = 0.
    
        CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
          IMPORTING
            data = .
    
        ASSIGN COMPONENT 'METADATA-CREATED_BY' OF STRUCTURE cs_dependency_rule
               TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN COMPONENT 'METADATA-CREATED_AT' OF STRUCTURE cs_dependency_rule
               TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN COMPONENT 'METADATA-CREATED_BY' OF STRUCTURE 
               TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN COMPONENT 'METADATA-CREATED_AT' OF STRUCTURE 
               TO .
        ASSERT sy-subrc = 0.
    
         = .
         = .
    
      ENDMETHOD.
    
      METHOD get_wb_object_operator.
    
        DATA:
          ls_object_type TYPE wbobjtype,
          lx_error       TYPE REF TO cx_root.
    
        IF mi_wb_object_operator IS BOUND.
          ri_wb_object_operator = mi_wb_object_operator.
        ENDIF.
    
        ls_object_type-objtype_tr = 'DRUL'.
        ls_object_type-subtype_wb = 'DRL'.
    
        TRY.
            CALL METHOD ('CL_WB_OBJECT_OPERATOR')=>('CREATE_INSTANCE')
              EXPORTING
                object_type = ls_object_type
                object_key  = mv_dependency_rule_key
              RECEIVING
                result      = mi_wb_object_operator.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        ri_wb_object_operator = mi_wb_object_operator.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA:
          li_wb_object_operator TYPE REF TO object,
          li_object_data_model  TYPE REF TO if_wb_object_data_model,
          lx_error              TYPE REF TO cx_root.
    
        TRY.
            li_wb_object_operator = get_wb_object_operator( ).
    
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
              IMPORTING
                eo_object_data = li_object_data_model.
    
            rv_user = li_object_data_model->get_changed_by( ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA:
          lx_error              TYPE REF TO cx_root,
          li_wb_object_operator TYPE REF TO object.
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        TRY.
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~DELETE')
              EXPORTING
                transport_request = iv_transport.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA:
          li_object_data_model           TYPE REF TO if_wb_object_data_model,
          li_wb_object_operator          TYPE REF TO object,
          lx_error                       TYPE REF TO cx_root,
          lv_drul_object_data_clas_exist TYPE c LENGTH 1.
    
        FIELD-SYMBOLS:
           TYPE any,
                    TYPE data.
    
        ASSIGN mr_dependency_rule->* TO .
        ASSERT sy-subrc = 0.
    
        io_xml->read(
          EXPORTING
            iv_name = 'DRUL'
          CHANGING
            cg_data =  ).
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        TRY.
            CALL FUNCTION 'CHECK_EXIST_CLAS'
              EXPORTING
                name            = 'CL_DRUL_WB_OBJECT_DATA'
              IMPORTING
                exist           = lv_drul_object_data_clas_exist
              EXCEPTIONS
                tr_invalid_type = 1
                OTHERS          = 2.
    
            IF sy-subrc = 0 AND lv_drul_object_data_clas_exist = abap_true.
              CREATE OBJECT li_object_data_model TYPE ('CL_DRUL_WB_OBJECT_DATA').
            ELSE.
              CREATE OBJECT li_object_data_model TYPE ('CL_BLUE_SOURCE_OBJECT_DATA').
            ENDIF.
    
            ASSIGN COMPONENT 'CONTENT-SOURCE' OF STRUCTURE 
                   TO .
            ASSERT sy-subrc = 0.
    
             = mo_files->read_string( 'asdrul' ).
    
            tadir_insert( iv_package ).
    
            IF zif_abapgit_object~exists( ) = abap_true.
    
              " We need to populate created_at, created_by, because otherwise update  is not possible
              fill_metadata_from_db( CHANGING cs_dependency_rule =  ).
              li_object_data_model->set_data(  ).
    
              CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
                EXPORTING
                  io_object_data    = li_object_data_model
                  transport_request = iv_transport.
    
            ELSE.
    
              li_object_data_model->set_data(  ).
    
              CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~CREATE')
                EXPORTING
                  io_object_data    = li_object_data_model
                  data_selection    = 'P' " if_wb_object_data_selection_co=>c_properties
                  package           = iv_package
                  transport_request = iv_transport.
    
              CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
                EXPORTING
                  io_object_data    = li_object_data_model
                  data_selection    = 'D' " if_wb_object_data_selection_co=>c_data_content
                  transport_request = iv_transport.
    
            ENDIF.
    
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~ACTIVATE').
    
            corr_insert( iv_package ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        TRY.
            mi_persistence->get(
              p_object_key           = mv_dependency_rule_key
              p_version              = 'A'
              p_existence_check_only = abap_true ).
            rv_bool = abap_true.
    
          CATCH cx_swb_exception.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESWB_EO'
                                                iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          li_wb_object_operator TYPE REF TO object,
          li_object_data_model  TYPE REF TO if_wb_object_data_model,
          lx_error              TYPE REF TO cx_root,
          lv_source             TYPE string.
    
        FIELD-SYMBOLS:
           TYPE any,
                    TYPE string.
    
        ASSIGN mr_dependency_rule->* TO .
        ASSERT sy-subrc = 0.
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        TRY.
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
              EXPORTING
                version        = 'A'
              IMPORTING
                data           = 
                eo_object_data = li_object_data_model.
    
            ASSIGN COMPONENT 'CONTENT-SOURCE' OF STRUCTURE 
                   TO .
            ASSERT sy-subrc = 0.
    
            lv_source = .
    
            clear_fields( CHANGING cs_dependency_rule =  ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        io_xml->add(
          iv_name = 'DRUL'
          ig_data =  ).
    
        mo_files->add_string(
          iv_ext    = 'asdrul'
          iv_string = lv_source ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_dsfd IMPLEMENTATION.
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_dsfd_handler          TYPE REF TO object,
              lo_dsfd_source_container TYPE REF TO object,
              lv_object_key            TYPE seu_objkey,
              lv_exists                TYPE abap_bool,
              lx_error                 TYPE REF TO cx_root.
    
        TRY.
            lv_object_key = ms_item-obj_name.
            CALL METHOD ('CL_DSFD_AFF_OBJECT_HANDLER')=>('GET_DDIC_HANDLER')
              EXPORTING
                object_key = lv_object_key
              RECEIVING
                handler    = lo_dsfd_handler.
    
            CALL METHOD lo_dsfd_handler->('IF_DD_DSFD_WB_HANDLER~CHECK_EXISTENCE')
              EXPORTING
                iv_as4local = 'A'
              RECEIVING
                rv_exists   = lv_exists.
    
            IF lv_exists = abap_true.
              CALL METHOD lo_dsfd_handler->('IF_DD_DSFD_WB_HANDLER~GET_SOURCE_CONTAINER')
    EXPORTING
                  iv_as4local = 'A'
                RECEIVING
                  ro_result   = lo_dsfd_source_container.
    
              CALL METHOD lo_dsfd_source_container->('IF_DD_DSFD_CONTAINER_SRC~GET_AS4USER')
                RECEIVING
                  rv_as4user = rv_user.
            ENDIF.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
      ENDMETHOD.
    
      METHOD get_additional_extensions.
        DATA ls_additional_extension LIKE LINE OF rv_additional_extensions.
        ls_additional_extension-extension = 'acds'.
        CALL METHOD ('CL_CDS_AFF_FILE_NAME_MAPPER')=>for_cds
          RECEIVING
            result = ls_additional_extension-file_name_mapper.
        APPEND ls_additional_extension TO rv_additional_extensions.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_dsfi IMPLEMENTATION.
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_dsfi_handler          TYPE REF TO object,
              lo_dsfi_source_container TYPE REF TO object,
              lv_object_key            TYPE seu_objkey,
              lv_exists                TYPE abap_bool,
              lx_error                 TYPE REF TO cx_root.
        FIELD-SYMBOLS:  TYPE any.
    
        TRY.
            lv_object_key = ms_item-obj_name.
            CALL METHOD ('CL_DSFI_AFF_OBJECT_HANDLER')=>('GET_DDIC_HANDLER')
              EXPORTING
                object_key = lv_object_key
              RECEIVING
                handler    = lo_dsfi_handler.
    
            ASSIGN ('CE_DD_DSFI_AS4LOCAL=>EN_STATE-ACTIVE')
              TO .
            IF sy-subrc = 0.
              CALL METHOD lo_dsfi_handler->('IF_DD_DSFI_WB_HANDLER~CHECK_EXISTENCE')
                EXPORTING
                  iv_as4local = 
                RECEIVING
                  rv_exists   = lv_exists.
    
              IF lv_exists = abap_true.
                CALL METHOD lo_dsfi_handler->('IF_DD_DSFI_WB_HANDLER~GET_SOURCE_CONTAINER')
                  EXPORTING
                    iv_as4local = 
                  RECEIVING
                    ro_result   = lo_dsfi_source_container.
    
                CALL METHOD lo_dsfi_source_container->('IF_DD_DSFI_SRC_CONTAINER~GET_AS4USER')
                  RECEIVING
                    rv_result = rv_user.
              ENDIF.
            ENDIF.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_dsys IMPLEMENTATION.
    
      METHOD constructor.
    
        DATA: lv_prefix    TYPE namespace,
              lv_bare_name TYPE progname.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        IF ms_item-obj_name(1) = '/'.
    
          CALL FUNCTION 'RS_NAME_SPLIT_NAMESPACE'
            EXPORTING
              name_with_namespace    = ms_item-obj_name
            IMPORTING
              namespace              = lv_prefix
              name_without_namespace = lv_bare_name.
    
          mv_doc_object = |{ lv_bare_name+0(4) }{ lv_prefix }{ lv_bare_name+4(*) }|.
        ELSE.
    
          mv_doc_object = ms_item-obj_name.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD deserialize_dsys.
    
        DATA: ls_data      TYPE ty_data,
              ls_docu_info TYPE dokil,
              lv_version   TYPE dokvers,
              lv_doku_obj  TYPE doku_obj.
    
        lv_doku_obj = mv_doc_object.
        ii_xml->read( EXPORTING iv_name = 'DSYS'
                      CHANGING cg_data = ls_data ).
    
        CALL FUNCTION 'DOCU_INIT'
          EXPORTING
            id     = c_id
            langu  = mv_language
            object = lv_doku_obj
            typ    = c_typ
          IMPORTING
            xdokil = ls_docu_info.
    
        lv_version = ls_docu_info-version.
    
        CALL FUNCTION 'DOCU_UPDATE'
          EXPORTING
            head    = ls_data-head
            state   = 'A'
            typ     = c_typ
            version = lv_version
          TABLES
            line    = ls_data-lines.
    
      ENDMETHOD.
    
      METHOD get_main_lang.
    
        SELECT SINGLE langu FROM dokil INTO rv_language
          WHERE id = c_id
          AND object = mv_doc_object
          AND masterlang = abap_true.                       "#EC CI_NOORDER
    
        IF sy-subrc <> 0.
          rv_language = mv_language.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        rv_user = zcl_abapgit_factory=>get_longtexts( )->changed_by(
          iv_object_name = mv_doc_object
          iv_longtext_id = c_id ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        zcl_abapgit_factory=>get_longtexts( )->delete(
          iv_object_name = mv_doc_object
          iv_longtext_id = c_id ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_metadata TYPE zif_abapgit_definitions=>ty_metadata.
    
        ls_metadata = io_xml->get_metadata( ).
    
        CASE ls_metadata-version.
    
          WHEN 'v1.0.0'.
            deserialize_dsys( io_xml ).
    
          WHEN 'v2.0.0'.
            zcl_abapgit_factory=>get_longtexts( )->deserialize(
              ii_xml           = io_xml
              iv_object_name   = mv_doc_object
              iv_longtext_id   = c_id
              iv_main_language = mv_language ).
    
          WHEN OTHERS.
            zcx_abapgit_exception=>raise( 'unsupported DSYS version' ).
    
        ENDCASE.
    
        tadir_insert( iv_package ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_count TYPE i.
    
        SELECT SINGLE COUNT( * ) FROM dokil INTO lv_count
               WHERE id   = c_id
               AND object = mv_doc_object.  "#EC CI_GENBUFF "#EC CI_NOORDER
    
        rv_bool = boolc( lv_count > 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
        rs_metadata-version = 'v2.0.0'.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA lv_lang TYPE sy-langu.
    
        lv_lang = get_main_lang( ).
    
        CALL FUNCTION 'DSYS_EDIT'
          EXPORTING
            dokclass            = mv_doc_object+0(4)
            dokname             = mv_doc_object+4(*)
            doklangu            = lv_lang
          EXCEPTIONS
            not_hypertext_class = 1
            no_editor           = 2
            OTHERS              = 3.
    
        rv_exit = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        zcl_abapgit_factory=>get_longtexts( )->serialize(
          iv_object_name = mv_doc_object
          iv_longtext_id = c_id
          io_i18n_params = mo_i18n_params
          ii_xml         = io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_dtdc IMPLEMENTATION.
    
      METHOD clear_field.
    
        FIELD-SYMBOLS:  TYPE data.
    
        ASSIGN COMPONENT iv_fieldname OF STRUCTURE cs_dynamic_cache
               TO .
        IF sy-subrc = 0.
          CLEAR: .
        ENDIF.
    
      ENDMETHOD.
    
      METHOD clear_fields.
    
        clear_field(
          EXPORTING
            iv_fieldname     = 'METADATA-CREATED_AT'
          CHANGING
            cs_dynamic_cache = cs_dynamic_cache ).
    
        clear_field(
          EXPORTING
            iv_fieldname     = 'METADATA-CREATED_BY'
          CHANGING
            cs_dynamic_cache = cs_dynamic_cache ).
    
        clear_field(
          EXPORTING
            iv_fieldname     = 'METADATA-CHANGED_AT'
          CHANGING
            cs_dynamic_cache = cs_dynamic_cache ).
    
        clear_field(
          EXPORTING
            iv_fieldname     = 'METADATA-CHANGED_BY'
          CHANGING
            cs_dynamic_cache = cs_dynamic_cache ).
    
        clear_field(
          EXPORTING
            iv_fieldname     = 'METADATA-MASTER_LANGUAGE'
          CHANGING
            cs_dynamic_cache = cs_dynamic_cache ).
    
        clear_field(
          EXPORTING
            iv_fieldname     = 'METADATA-MASTER_SYSTEM'
          CHANGING
            cs_dynamic_cache = cs_dynamic_cache ).
    
        clear_field(
          EXPORTING
            iv_fieldname     = 'METADATA-RESPONSIBLE'
          CHANGING
            cs_dynamic_cache = cs_dynamic_cache ).
    
        clear_field(
          EXPORTING
            iv_fieldname     = 'METADATA-PACKAGE_REF'
          CHANGING
            cs_dynamic_cache = cs_dynamic_cache ).
    
        clear_field(
          EXPORTING
            iv_fieldname     = 'METADATA-ABAP_LANGUAGE_VERSION'
          CHANGING
            cs_dynamic_cache = cs_dynamic_cache ).
        clear_field(
          EXPORTING
            iv_fieldname     = 'METADATA-ABAP_LANGU_VERSION'
          CHANGING
            cs_dynamic_cache = cs_dynamic_cache ).
    
        clear_field(
          EXPORTING
            iv_fieldname     = 'CONTENT-SOURCE'
          CHANGING
            cs_dynamic_cache = cs_dynamic_cache ).
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_dynamic_cache_key = ms_item-obj_name.
        mv_has_own_wb_data_class = has_own_wb_data_class( ).
    
        TRY.
            IF mv_has_own_wb_data_class = abap_true.
              CREATE DATA mr_dynamic_cache TYPE ('CL_DTDC_WB_OBJECT_DATA=>TY_DTDC_OBJECT_DATA').
            ELSE.
              CREATE DATA mr_dynamic_cache TYPE ('CL_BLUE_SOURCE_OBJECT_DATA=>TY_OBJECT_DATA').
            ENDIF.
            CREATE OBJECT mi_persistence TYPE ('CL_DTDC_OBJECT_PERSIST').
    
          CATCH cx_sy_create_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD fill_metadata_from_db.
    
        DATA:
          li_wb_object_operator TYPE REF TO object,
          lr_dynamic_cache_old  TYPE REF TO data.
    
        FIELD-SYMBOLS:
           TYPE any,
                  TYPE xsddatetime_z,
                  TYPE syuname,
              TYPE xsddatetime_z,
              TYPE syuname.
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        IF mv_has_own_wb_data_class = abap_true.
          CREATE DATA lr_dynamic_cache_old TYPE ('CL_DTDC_WB_OBJECT_DATA=>TY_DTDC_OBJECT_DATA').
        ELSE.
          CREATE DATA lr_dynamic_cache_old TYPE ('CL_BLUE_SOURCE_OBJECT_DATA=>TY_OBJECT_DATA').
        ENDIF.
        ASSIGN lr_dynamic_cache_old->* TO .
        ASSERT sy-subrc = 0.
    
        CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
          IMPORTING
            data = .
    
        ASSIGN COMPONENT 'METADATA-CREATED_BY' OF STRUCTURE cs_dynamic_cache
               TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN COMPONENT 'METADATA-CREATED_AT' OF STRUCTURE cs_dynamic_cache
               TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN COMPONENT 'METADATA-CREATED_BY' OF STRUCTURE 
               TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN COMPONENT 'METADATA-CREATED_AT' OF STRUCTURE 
               TO .
        ASSERT sy-subrc = 0.
    
         = .
         = .
    
      ENDMETHOD.
    
      METHOD get_wb_object_operator.
    
        DATA:
          ls_object_type TYPE wbobjtype,
          lx_error       TYPE REF TO cx_root.
    
        IF mi_wb_object_operator IS BOUND.
          ri_wb_object_operator = mi_wb_object_operator.
        ENDIF.
    
        ls_object_type-objtype_tr = 'DTDC'.
        ls_object_type-subtype_wb = 'DF'.
    
        TRY.
            CALL METHOD ('CL_WB_OBJECT_OPERATOR')=>('CREATE_INSTANCE')
              EXPORTING
                object_type = ls_object_type
                object_key  = mv_dynamic_cache_key
              RECEIVING
                result      = mi_wb_object_operator.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        ri_wb_object_operator = mi_wb_object_operator.
    
      ENDMETHOD.
    
      METHOD has_own_wb_data_class.
    
        DATA:
          lr_own_type TYPE REF TO data,
          lx_error    TYPE REF TO cx_root.
    
        TRY.
            CREATE DATA lr_own_type TYPE ('CL_DTDC_WB_OBJECT_DATA=>TY_DTDC_OBJECT_DATA').
            rv_supported = abap_true.
          CATCH cx_root INTO lx_error.
            rv_supported = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA:
          li_object_data_model  TYPE REF TO if_wb_object_data_model,
          lx_error              TYPE REF TO cx_root,
          li_wb_object_operator TYPE REF TO object.
    
        TRY.
            li_wb_object_operator = get_wb_object_operator( ).
    
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
              IMPORTING
                eo_object_data = li_object_data_model.
    
            rv_user = li_object_data_model->get_changed_by( ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA:
          lx_error              TYPE REF TO cx_root,
          li_wb_object_operator TYPE REF TO object.
    
        TRY.
            li_wb_object_operator = get_wb_object_operator( ).
    
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~DELETE')
              EXPORTING
                transport_request = iv_transport.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA:
          li_object_data_model  TYPE REF TO if_wb_object_data_model,
          li_wb_object_operator TYPE REF TO object,
          lx_error              TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:
           TYPE uccheck,
                   TYPE any,
                          TYPE data.
    
        ASSIGN mr_dynamic_cache->* TO .
        ASSERT sy-subrc = 0.
    
        io_xml->read(
          EXPORTING
            iv_name = 'DTDC'
          CHANGING
            cg_data =  ).
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        TRY.
            IF mv_has_own_wb_data_class = abap_true.
              CREATE OBJECT li_object_data_model TYPE ('CL_DTDC_WB_OBJECT_DATA').
            ELSE.
              CREATE OBJECT li_object_data_model TYPE ('CL_BLUE_SOURCE_OBJECT_DATA').
            ENDIF.
    
            ASSIGN COMPONENT 'CONTENT-SOURCE' OF STRUCTURE 
                   TO .
            ASSERT sy-subrc = 0.
    
             = mo_files->read_string( 'asdtdc' ).
    
            tadir_insert( iv_package ).
    
            IF zif_abapgit_object~exists( ) = abap_true.
    
              " We need to populate created_at, created_by, because otherwise update  is not possible
              fill_metadata_from_db( CHANGING cs_dynamic_cache =  ).
              li_object_data_model->set_data(  ).
    
              CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
                EXPORTING
                  io_object_data    = li_object_data_model
                  transport_request = iv_transport.
    
            ELSE.
    
              li_object_data_model->set_data(  ).
    
              CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~CREATE')
                EXPORTING
                  io_object_data    = li_object_data_model
                  data_selection    = 'P' " if_wb_object_data_selection_co=>c_properties
                  package           = iv_package
                  transport_request = iv_transport.
    
              CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
                EXPORTING
                  io_object_data    = li_object_data_model
                  data_selection    = 'D' " if_wb_object_data_selection_co=>c_data_content
                  transport_request = iv_transport.
    
            ENDIF.
    
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~ACTIVATE').
    
            ASSIGN COMPONENT 'METADATA-ABAP_LANGUAGE_VERSION' OF STRUCTURE 
              TO .
            IF sy-subrc = 0.
              set_abap_language_version( CHANGING cv_abap_language_version =  ).
    
              TRY.
                  UPDATE ('DDDTDC_SOURCE') SET abap_language_version = 
                    WHERE dtdc_name = ms_item-obj_name.
                CATCH cx_sy_dynamic_osql_semantics ##NO_HANDLER.
              ENDTRY.
            ENDIF.
    
            corr_insert( iv_package ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        TRY.
            mi_persistence->get(
              p_object_key           = mv_dynamic_cache_key
              p_version              = 'A'
              p_existence_check_only = abap_true ).
            rv_bool = abap_true.
    
          CATCH cx_swb_exception.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESWB_EO'
                                                iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          li_wb_object_operator TYPE REF TO object,
          li_object_data_model  TYPE REF TO if_wb_object_data_model,
          lx_error              TYPE REF TO cx_root,
          lv_source             TYPE string.
    
        FIELD-SYMBOLS:
           TYPE uccheck,
                   TYPE any,
                          TYPE string.
    
        ASSIGN mr_dynamic_cache->* TO .
        ASSERT sy-subrc = 0.
    
        TRY.
            li_wb_object_operator = get_wb_object_operator( ).
    
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
              EXPORTING
                version        = 'A'
              IMPORTING
                data           = 
                eo_object_data = li_object_data_model.
    
            ASSIGN COMPONENT 'CONTENT-SOURCE' OF STRUCTURE 
                   TO .
            ASSERT sy-subrc = 0.
    
            lv_source = .
    
            clear_fields( CHANGING cs_dynamic_cache =  ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        ASSIGN COMPONENT 'METADATA-ABAP_LANGUAGE_VERSION' OF STRUCTURE  TO .
        IF sy-subrc = 0.
           = get_abap_language_version( ).
        ENDIF.
    
        io_xml->add(
          iv_name = 'DTDC'
          ig_data =  ).
    
        mo_files->add_string(
          iv_ext    = 'asdtdc'
          iv_string = lv_source ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_dteb IMPLEMENTATION.
    
      METHOD get_additional_extensions.
    
        DATA ls_additional_extension LIKE LINE OF rv_additional_extensions.
        ls_additional_extension-extension = 'acds'.
        CALL METHOD ('CL_CDS_AFF_FILE_NAME_MAPPER')=>for_cds
          RECEIVING
            result = ls_additional_extension-file_name_mapper.
        APPEND ls_additional_extension TO rv_additional_extensions.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_dteb_handler TYPE REF TO object,
              lv_object_key   TYPE seu_objkey,
              lx_error        TYPE REF TO cx_root.
    
        TRY.
            lv_object_key = ms_item-obj_name.
            CALL METHOD ('CL_DDIC_ADT_DT_UTILITY')=>('CREATE_DD_DTEB_HANDLER')
              EXPORTING
                object_key = lv_object_key
              RECEIVING
                handler = lo_dteb_handler.
    
            CALL METHOD lo_dteb_handler->('GET_CHANGED_BY')
              RECEIVING
                rv_changed_by = rv_user.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_dtel IMPLEMENTATION.
    
      METHOD deserialize_texts.
    
        DATA: lv_name       TYPE ddobjname,
              ls_dd04v_tmp  TYPE dd04v,
              lt_i18n_langs TYPE TABLE OF langu,
              lt_dd04_texts TYPE ty_dd04_texts.
    
        FIELD-SYMBOLS:       LIKE LINE OF lt_i18n_langs,
                        LIKE LINE OF lt_dd04_texts.
    
        lv_name = ms_item-obj_name.
    
        ii_xml->read( EXPORTING iv_name = 'I18N_LANGS'
                      CHANGING  cg_data = lt_i18n_langs ).
    
        ii_xml->read( EXPORTING iv_name = 'DD04_TEXTS'
                      CHANGING  cg_data = lt_dd04_texts ).
    
        mo_i18n_params->trim_saplang_list( CHANGING ct_sap_langs = lt_i18n_langs ).
    
        SORT lt_i18n_langs.
        SORT lt_dd04_texts BY ddlanguage. " Optimization
    
        LOOP AT lt_i18n_langs ASSIGNING .
    
          " Data element description
          ls_dd04v_tmp = is_dd04v.
          READ TABLE lt_dd04_texts ASSIGNING  WITH KEY ddlanguage = .
          IF sy-subrc > 0.
            zcx_abapgit_exception=>raise( |DD04_TEXTS cannot find lang {  } in XML| ).
          ENDIF.
          MOVE-CORRESPONDING  TO ls_dd04v_tmp.
          CALL FUNCTION 'DDIF_DTEL_PUT'
            EXPORTING
              name              = lv_name
              dd04v_wa          = ls_dd04v_tmp
            EXCEPTIONS
              dtel_not_found    = 1
              name_inconsistent = 2
              dtel_inconsistent = 3
              put_failure       = 4
              put_refused       = 5
              OTHERS            = 6.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD serialize_texts.
    
        DATA: lv_name            TYPE ddobjname,
              lv_index           TYPE i,
              ls_dd04v           TYPE dd04v,
              lt_dd04_texts      TYPE ty_dd04_texts,
              lt_i18n_langs      TYPE TABLE OF langu,
              lt_language_filter TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        FIELD-SYMBOLS:       LIKE LINE OF lt_i18n_langs,
                        LIKE LINE OF lt_dd04_texts.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          RETURN.
        ENDIF.
    
        lv_name = ms_item-obj_name.
    
        " Collect additional languages, skip main lang - it was serialized already
        lt_language_filter = mo_i18n_params->build_language_filter( ).
    
        SELECT DISTINCT ddlanguage AS langu INTO TABLE lt_i18n_langs
          FROM dd04v
          WHERE rollname = lv_name
          AND ddlanguage IN lt_language_filter
          AND ddlanguage <> mv_language
          ORDER BY langu.                                     "#EC CI_SUBRC
    
        LOOP AT lt_i18n_langs ASSIGNING .
          lv_index = sy-tabix.
          CALL FUNCTION 'DDIF_DTEL_GET'
            EXPORTING
              name          = lv_name
              langu         = 
            IMPORTING
              dd04v_wa      = ls_dd04v
            EXCEPTIONS
              illegal_input = 1
              OTHERS        = 2.
          IF sy-subrc <> 0 OR ls_dd04v-ddlanguage IS INITIAL.
            DELETE lt_i18n_langs INDEX lv_index. " Don't save this lang
            CONTINUE.
          ENDIF.
    
          APPEND INITIAL LINE TO lt_dd04_texts ASSIGNING .
          MOVE-CORRESPONDING ls_dd04v TO .
    
        ENDLOOP.
    
        SORT lt_i18n_langs ASCENDING.
        SORT lt_dd04_texts BY ddlanguage ASCENDING.
    
        IF lines( lt_i18n_langs ) > 0.
          ii_xml->add( iv_name = 'I18N_LANGS'
                       ig_data = lt_i18n_langs ).
    
          ii_xml->add( iv_name = 'DD04_TEXTS'
                       ig_data = lt_dd04_texts ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE as4user FROM dd04l INTO rv_user
          WHERE rollname = ms_item-obj_name
          AND as4local = 'A'
          AND as4vers = '0000'.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        delete_ddic( 'E' ).
    
        delete_longtexts( c_longtext_id_dtel ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_dd04v TYPE dd04v,
              ls_extra TYPE ty_extra,
              lv_name  TYPE ddobjname.
    
        io_xml->read( EXPORTING iv_name = 'DD04V'
                      CHANGING cg_data = ls_dd04v ).
    
        corr_insert( iv_package = iv_package
                     ig_object_class = 'DICT' ).
    
        lv_name = ms_item-obj_name. " type conversion
    
        CALL FUNCTION 'DDIF_DTEL_PUT'
          EXPORTING
            name              = lv_name
            dd04v_wa          = ls_dd04v
          EXCEPTIONS
            dtel_not_found    = 1
            name_inconsistent = 2
            dtel_inconsistent = 3
            put_failure       = 4
            put_refused       = 5
            OTHERS            = 6.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        " Fields that are not part of dd04v
        io_xml->read( EXPORTING iv_name = 'DD04L_EXTRA'
                      CHANGING  cg_data = ls_extra ).
    
        TRY.
            set_abap_language_version( CHANGING cv_abap_language_version = ls_extra-abap_language_version ).
    
            UPDATE ('DD04L') SET abap_language_version = ls_extra-abap_language_version WHERE rollname = lv_name.
          CATCH cx_sy_dynamic_osql_semantics ##NO_HANDLER.
        ENDTRY.
    
        IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
          deserialize_texts(
            ii_xml   = io_xml
            is_dd04v = ls_dd04v ).
        ENDIF.
    
        deserialize_longtexts( ii_xml         = io_xml
                               iv_longtext_id = c_longtext_id_dtel ).
    
        deserialize_longtexts( ii_xml           = io_xml
                               iv_longtext_name = 'LONGTEXTS_' && c_longtext_id_dtel_suppl
                               iv_longtext_id   = c_longtext_id_dtel_suppl ).
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_rollname TYPE dd04l-rollname.
    
        lv_rollname = ms_item-obj_name.
    
        " Check nametab because it's fast
        CALL FUNCTION 'DD_GET_NAMETAB_HEADER'
          EXPORTING
            tabname   = lv_rollname
          EXCEPTIONS
            not_found = 1
            OTHERS    = 2.
        IF sy-subrc <> 0.
          " Check for inactive or modified versions
          SELECT SINGLE rollname FROM dd04l INTO lv_rollname
            WHERE rollname = lv_rollname.
        ENDIF.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESDICT'
                                                iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by ZCL_ABAPGIT_OBJECT=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    * fm DDIF_DTEL_GET bypasses buffer, so SELECTs are
    * done directly from here
    
        DATA: lv_name  TYPE ddobjname,
              ls_extra TYPE ty_extra,
              ls_dd04v TYPE dd04v.
    
        FIELD-SYMBOLS  TYPE any.
    
        lv_name = ms_item-obj_name.
    
        SELECT SINGLE * FROM dd04l
          INTO CORRESPONDING FIELDS OF ls_dd04v
          WHERE rollname = lv_name
          AND as4local = 'A'
          AND as4vers = '0000'.
        IF sy-subrc <> 0 OR ls_dd04v IS INITIAL.
          RETURN.
        ENDIF.
    
        SELECT SINGLE * FROM dd04t
          INTO CORRESPONDING FIELDS OF ls_dd04v
          WHERE rollname = lv_name
          AND ddlanguage = mv_language
          AND as4local = 'A'
          AND as4vers = '0000'.
    
        CLEAR: ls_dd04v-as4user,
               ls_dd04v-as4date,
               ls_dd04v-as4time.
    
        IF ls_dd04v-refkind = 'D'.
    * clear values inherited from domain
          CLEAR: ls_dd04v-datatype,
                 ls_dd04v-leng,
                 ls_dd04v-decimals,
                 ls_dd04v-outputlen,
                 ls_dd04v-valexi,
                 ls_dd04v-lowercase,
                 ls_dd04v-signflag,
                 ls_dd04v-convexit,
                 ls_dd04v-entitytab.
        ENDIF.
    
        ASSIGN COMPONENT 'ACTFLAG' OF STRUCTURE ls_dd04v TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
        ASSIGN COMPONENT 'RESERVEDTE' OF STRUCTURE ls_dd04v TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
    
        IF ls_dd04v-routputlen = ''.
    * numeric field, make sure it is initial or XML serialization will dump
          CLEAR ls_dd04v-routputlen.
        ENDIF.
        IF ls_dd04v-authclass = ''.
          CLEAR ls_dd04v-authclass.
        ENDIF.
    
        io_xml->add( iv_name = 'DD04V'
                     ig_data = ls_dd04v ).
    
        ls_extra-abap_language_version = get_abap_language_version( ).
    
        io_xml->add( iv_name = 'DD04L_EXTRA'
                     ig_data = ls_extra ).
    
        IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
          serialize_texts( io_xml ).
        ENDIF.
    
        serialize_longtexts( ii_xml         = io_xml
                             iv_longtext_id = c_longtext_id_dtel ).
    
        serialize_longtexts( ii_xml           = io_xml
                             iv_longtext_name = 'LONGTEXTS_' && c_longtext_id_dtel_suppl
                             iv_longtext_id   = c_longtext_id_dtel_suppl ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ecatt_super IMPLEMENTATION.
    
      METHOD clear_attributes.
    
        DATA: li_element     TYPE REF TO if_ixml_element,
              lv_object_type TYPE etobj_type.
    
        lv_object_type = get_object_type( ).
    
        li_element = ci_document->find_from_name( |{ lv_object_type }| ).
        li_element->remove_attribute( |SAPRL| ).
        li_element->remove_attribute( |DOWNLOADDATE| ).
        li_element->remove_attribute( |DOWNLOADTIME| ).
    
      ENDMETHOD.
    
      METHOD clear_element.
    
        DATA: li_element TYPE REF TO if_ixml_element.
    
        li_element = ci_document->find_from_name( iv_name ).
    
        IF li_element IS BOUND.
          li_element->set_value( || ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD clear_elements.
    
        clear_element( EXPORTING iv_name     = |FUSER|
                       CHANGING  ci_document = ci_document ).
    
        clear_element( EXPORTING iv_name     = |FDATE|
                       CHANGING  ci_document = ci_document ).
    
        clear_element( EXPORTING iv_name     = |LUSER|
                       CHANGING  ci_document = ci_document ).
    
        clear_element( EXPORTING iv_name     = |LDATE|
                       CHANGING  ci_document = ci_document ).
    
        clear_element( EXPORTING iv_name     = |LTIME|
                       CHANGING  ci_document = ci_document ).
    
        clear_element( EXPORTING iv_name     = |TWB_RESP|
                       CHANGING  ci_document = ci_document ).
    
        clear_element( EXPORTING iv_name     = |DEVCLASS|
                       CHANGING  ci_document = ci_document ).
    
        clear_element( EXPORTING iv_name     = |TADIR_RESP|
                       CHANGING  ci_document = ci_document ).
    
        " Clearing just VAR_EXT_PATH will lead to diffs in batch
        clear_element( EXPORTING iv_name     = |ETVAR_EXT|
                       CHANGING  ci_document = ci_document ).
    
        " SORTLNR is part of ETPAR_VARI and causing diffs
        " We can clear it since it's automatically filled during deserialize
        clear_element_collection( EXPORTING iv_name     = |SORTLNR|
                                  CHANGING  ci_document = ci_document ).
    
      ENDMETHOD.
    
      METHOD clear_element_collection.
    
        DATA:
          lo_node_collection TYPE REF TO if_ixml_node_collection,
          lo_node            TYPE REF TO if_ixml_node,
          lv_index           TYPE i.
    
        lo_node_collection = ci_document->get_elements_by_tag_name( iv_name ).
    
        lv_index = 0.
        WHILE lv_index < lo_node_collection->get_length( ).
          lo_node = lo_node_collection->get_item( lv_index ).
          lo_node->set_value( '' ).
          lv_index = lv_index + 1.
        ENDWHILE.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_object_name = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD deserialize_version.
    
        DATA: ls_object   TYPE etmobjects,
              lo_upload   TYPE REF TO cl_apl_ecatt_upload,
              li_upload   TYPE REF TO zif_abapgit_ecatt_upload,
              lv_xml      TYPE xstring,
              li_document TYPE REF TO if_ixml_document,
              lv_version  TYPE string,
              lx_error    TYPE REF TO cx_ecatt.
    
        lv_version = get_version_from_node( ii_version_node ).
    
        IF lv_version IS INITIAL.
          RETURN.
        ENDIF.
    
        lo_upload  = get_upload( ).
        li_upload ?= lo_upload.
    
        li_document = cl_ixml=>create( )->create_document( ).
        li_document->append_child( ii_version_node->get_first_child( ) ).
    
        lv_xml = cl_ixml_80_20=>render_to_xstring( li_document ).
    
        li_upload->set_stream_for_upload( lv_xml ).
    
        ls_object-d_obj_name  = mv_object_name.
        ls_object-s_obj_type  = get_object_type( ).
        ls_object-d_devclass  = iv_package.
        ls_object-d_obj_ver   = lv_version.
        ls_object-d_overwrite = abap_true.
    
        TRY.
            lo_upload->upload( CHANGING ch_object = ls_object ).
    
          CATCH cx_ecatt INTO lx_error.
            zcx_abapgit_exception=>raise( lx_error->get_text( ) ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get_changed_by_user.
    
        rv_changed_by_user = ii_document->find_from_name( 'LUSER' )->get_value( ).
    
      ENDMETHOD.
    
      METHOD get_changed_date.
    
        DATA: lv_changed_date_external TYPE string.
    
        lv_changed_date_external = ii_document->find_from_name( 'LDATE' )->get_value( ).
    
        REPLACE ALL OCCURRENCES OF '-' IN lv_changed_date_external WITH ''.
        rv_changed_date = lv_changed_date_external.
    
      ENDMETHOD.
    
      METHOD get_changed_time.
    
        DATA: lv_changed_time_external TYPE string.
    
        lv_changed_time_external = ii_document->find_from_name( 'LTIME' )->get_value( ).
    
        REPLACE ALL OCCURRENCES OF ':' IN lv_changed_time_external WITH ''.
        rv_changed_time = lv_changed_time_external.
    
      ENDMETHOD.
    
      METHOD get_change_information.
    
        DATA: li_document    TYPE REF TO if_ixml_document,
              lv_xml         TYPE xstring,
              lo_download    TYPE REF TO cl_apl_ecatt_download,
              lv_object_type TYPE etobj_type.
    
        lo_download = get_download( ).
    
        lv_object_type = get_object_type( ).
    
        lv_xml = zcl_abapgit_ecatt_helper=>build_xml_of_object(
                     iv_object_name    = mv_object_name
                     iv_object_version = is_version_info-version
                     iv_object_type    = lv_object_type
                     io_download       = lo_download ).
    
        li_document = cl_ixml_80_20=>parse_to_document( stream_xstring = lv_xml ).
    
        rs_change_information-ldate = get_changed_date( li_document ).
        rs_change_information-ltime = get_changed_time( li_document ).
        rs_change_information-luser = get_changed_by_user( li_document ).
    
      ENDMETHOD.
    
      METHOD get_version_from_node.
    
        TRY.
            rv_version = ii_node->get_first_child(
                               )->get_first_child(
                               )->get_first_child(
                               )->get_first_child(
                               )->get_value( ).
    
          CATCH cx_sy_ref_is_initial.
            RETURN.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD is_change_more_recent_than.
    
        IF is_currently_changed-ldate > is_last_changed-ldate
          OR (     is_currently_changed-ldate = is_last_changed-ldate
               AND is_currently_changed-ltime > is_last_changed-ltime ).
    
          rv_is_change_more_recent = abap_true.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD serialize_version.
    
        DATA: li_document    TYPE REF TO if_ixml_document,
              lv_xml         TYPE xstring,
              li_node        TYPE REF TO if_ixml_element,
              lo_download    TYPE REF TO cl_apl_ecatt_download,
              lv_object_type TYPE etobj_type.
    
        lo_download = get_download( ).
    
        lv_object_type = get_object_type( ).
    
        lv_xml = zcl_abapgit_ecatt_helper=>build_xml_of_object(
                     iv_object_name    = mv_object_name
                     iv_object_version = iv_version
                     iv_object_type    = lv_object_type
                     io_download       = lo_download ).
    
        IF lv_xml IS INITIAL.
          zcx_abapgit_exception=>raise( |ECATT, empty xml, { mv_object_name }| ).
        ENDIF.
    
        li_document = cl_ixml_80_20=>parse_to_document( stream_xstring = lv_xml ).
    
        clear_attributes( CHANGING ci_document = li_document ).
    
        clear_elements( CHANGING ci_document = li_document ).
    
        li_node = li_document->create_element( c_name-version ).
        li_node->append_child( li_document->get_root_element( ) ).
    
        ci_node->append_child( li_node ).
    
      ENDMETHOD.
    
      METHOD serialize_versions.
    
        DATA: li_versions_node TYPE REF TO if_ixml_element.
        FIELD-SYMBOLS:  LIKE LINE OF it_version_info.
    
        li_versions_node = ci_document->create_element( c_name-versions ).
    
        IF lines( it_version_info ) > 0.
    
          LOOP AT it_version_info ASSIGNING .
    
            serialize_version(
              EXPORTING
                iv_version = -version
              CHANGING
                ci_node    = li_versions_node ).
    
          ENDLOOP.
    
        ELSE.
    
          serialize_version(
            EXPORTING
              iv_version = c_default_version
            CHANGING
              ci_node    = li_versions_node ).
    
        ENDIF.
    
        ci_document->append_child( li_versions_node ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: ls_last_changed      TYPE ty_last_changed,
              ls_currently_changed TYPE ty_last_changed,
              lt_version_info      TYPE etversinfo_tabtype,
              lx_error             TYPE REF TO cx_static_check,
              lv_text              TYPE string,
              lv_object_type       TYPE etobj_type.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_version_info.
    
        TRY.
            lv_object_type = get_object_type( ).
    
            cl_apl_ecatt_object=>get_version_info_object(
              EXPORTING
                im_name          = mv_object_name
                im_obj_type      = lv_object_type
              IMPORTING
                ex_version_info  = lt_version_info ).
    
            LOOP AT lt_version_info ASSIGNING .
    
              ls_currently_changed = get_change_information(  ).
    
              IF is_change_more_recent_than( is_currently_changed = ls_currently_changed
                                             is_last_changed      = ls_last_changed ) = abap_true.
                ls_last_changed = ls_currently_changed.
              ENDIF.
    
            ENDLOOP.
    
          CATCH cx_static_check INTO lx_error.
            lv_text = lx_error->get_text( ).
            MESSAGE lv_text TYPE 'S' DISPLAY LIKE 'E'.
        ENDTRY.
    
        IF ls_last_changed-luser IS NOT INITIAL.
          rv_user = ls_last_changed-luser.
        ELSE.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lx_error       TYPE REF TO cx_ecatt_apl,
              lv_text        TYPE string,
              lv_object_type TYPE etobj_type.
    
        lv_object_type = get_object_type( ).
    
        TRY.
            cl_apl_ecatt_object=>delete_object( im_obj_type            = lv_object_type
                                                im_name                = mv_object_name
                                                " we have to supply a version, so let's use the default version
                                                " and delete them all
                                                im_version             = c_default_version
                                                im_delete_all_versions = abap_true ).
    
          CATCH cx_ecatt_apl INTO lx_error.
            lv_text = lx_error->get_text( ).
            zcx_abapgit_exception=>raise( lv_text ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: li_document         TYPE REF TO if_ixml_document,
              li_versions         TYPE REF TO if_ixml_node_collection,
              li_version_iterator TYPE REF TO if_ixml_node_iterator,
              li_version_node     TYPE REF TO if_ixml_node.
    
        li_document = io_xml->get_raw( ).
    
        li_versions = li_document->get_elements_by_tag_name( depth = 0
                                                             name  = c_name-version ).
    
        li_version_iterator = li_versions->create_iterator( ).
    
        DO.
          li_version_node = li_version_iterator->get_next( ).
    
          IF li_version_node IS NOT BOUND.
            EXIT.
          ENDIF.
    
          deserialize_version( ii_version_node = li_version_node
                               iv_package      = iv_package ).
    
        ENDDO.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_object_type TYPE etobj_type.
    
        lv_object_type = get_object_type( ).
    
        TRY.
            rv_bool = cl_apl_ecatt_object=>existence_check_object( im_name               = mv_object_name
                                                                   im_version            = c_default_version
                                                                   im_obj_type           = lv_object_type
                                                                   im_exists_any_version = abap_true ).
    
          CATCH cx_ecatt.
            RETURN.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lv_object TYPE seqg3-garg.
    
        lv_object = ms_item-obj_name.
        OVERLAY lv_object WITH '                              '.
        lv_object = lv_object && '*'.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = get_lock_object( )
                                                iv_argument    = lv_object ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          lt_version_info TYPE etversinfo_tabtype,
          li_document     TYPE REF TO if_ixml_document,
          lx_error        TYPE REF TO cx_ecatt,
          lv_text         TYPE string,
          lv_object_type  TYPE etobj_type.
    
        lv_object_type = get_object_type( ).
    
        TRY.
            cl_apl_ecatt_object=>get_version_info_object(
              EXPORTING
                im_name         = mv_object_name
                im_obj_type     = lv_object_type
              IMPORTING
                ex_version_info = lt_version_info ).
    
            SORT lt_version_info BY version.
    
            li_document = cl_ixml=>create( )->create_document( ).
    
            serialize_versions(
              EXPORTING
                it_version_info = lt_version_info
              CHANGING
                ci_document     = li_document ).
    
            io_xml->set_raw( li_document->get_root_element( ) ).
    
          CATCH cx_ecatt INTO lx_error.
            lv_text = lx_error->get_text( ).
            MESSAGE lv_text TYPE 'S' DISPLAY LIKE 'E'.
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ecat IMPLEMENTATION.
    
      METHOD get_download.
    
        CREATE OBJECT ro_download TYPE zcl_abapgit_ecatt_script_downl.
    
      ENDMETHOD.
    
      METHOD get_lock_object.
    
        rv_lock_object = 'E_ECATT'.
    
      ENDMETHOD.
    
      METHOD get_object_type.
    
        rv_object_type = cl_apl_ecatt_const=>obj_type_test_script.
    
      ENDMETHOD.
    
      METHOD get_upload.
    
        CREATE OBJECT ro_upload TYPE zcl_abapgit_ecatt_script_upl.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ecsd IMPLEMENTATION.
    
      METHOD get_download.
    
        CREATE OBJECT ro_download TYPE zcl_abapgit_ecatt_system_downl.
    
      ENDMETHOD.
    
      METHOD get_lock_object.
    
        rv_lock_object = 'E_ECATT_SD'.
    
      ENDMETHOD.
    
      METHOD get_object_type.
    
        rv_object_type = cl_apl_ecatt_const=>obj_type_system_data.
    
      ENDMETHOD.
    
      METHOD get_upload.
    
        CREATE OBJECT ro_upload TYPE zcl_abapgit_ecatt_system_upl.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ecsp IMPLEMENTATION.
    
      METHOD get_download.
    
        CREATE OBJECT ro_download TYPE zcl_abapgit_ecatt_sp_download.
    
      ENDMETHOD.
    
      METHOD get_lock_object.
    
        rv_lock_object = 'E_ECATT_SP'.
    
      ENDMETHOD.
    
      METHOD get_object_type.
    
    * constant missing in 702, cl_apl_ecatt_const=>obj_type_start_profile
        rv_object_type = 'ECSP'.
    
      ENDMETHOD.
    
      METHOD get_upload.
    
        CREATE OBJECT ro_upload TYPE zcl_abapgit_ecatt_sp_upload.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ectc IMPLEMENTATION.
    
      METHOD get_download.
    
        CREATE OBJECT ro_download TYPE zcl_abapgit_ecatt_config_downl.
    
      ENDMETHOD.
    
      METHOD get_lock_object.
    
        rv_lock_object = 'E_ECATT_TC'.
    
      ENDMETHOD.
    
      METHOD get_object_type.
    
        rv_object_type = cl_apl_ecatt_const=>obj_type_test_config.
    
      ENDMETHOD.
    
      METHOD get_upload.
    
        CREATE OBJECT ro_upload TYPE zcl_abapgit_ecatt_config_upl.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ectd IMPLEMENTATION.
    
      METHOD get_download.
    
        CREATE OBJECT ro_download TYPE zcl_abapgit_ecatt_data_downl.
    
      ENDMETHOD.
    
      METHOD get_lock_object.
    
        rv_lock_object = 'E_ECATT_TD'.
    
      ENDMETHOD.
    
      METHOD get_object_type.
    
        rv_object_type = cl_apl_ecatt_const=>obj_type_test_data.
    
      ENDMETHOD.
    
      METHOD get_upload.
    
        CREATE OBJECT ro_upload TYPE zcl_abapgit_ecatt_data_upload.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ecvo IMPLEMENTATION.
    
      METHOD get_download.
    
        CREATE OBJECT ro_download TYPE zcl_abapgit_ecatt_val_obj_down.
    
      ENDMETHOD.
    
      METHOD get_lock_object.
    
        rv_lock_object = 'E_ECATT_TD'.
    
      ENDMETHOD.
    
      METHOD get_object_type.
    
    * constant missing in 702, cl_apl_ecatt_const=>obj_type_ecatt_vo
        rv_object_type = 'ECVO'.
    
      ENDMETHOD.
    
      METHOD get_upload.
    
        CREATE OBJECT ro_upload TYPE zcl_abapgit_ecatt_val_obj_upl.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_eeec IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lr_data             TYPE REF TO data,
              lo_registry_adapter TYPE REF TO object,
              lv_object_key       TYPE seu_objkey,
              lx_error            TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:    TYPE any,
                        TYPE any.
    
        TRY.
            CREATE OBJECT lo_registry_adapter TYPE ('/IWXBE/CL_EEEC_REG_ADAPTER').
            CREATE DATA lr_data TYPE ('/IWXBE/IF_REGISTRY_TYPES=>TY_S_CONSUMER').
            ASSIGN lr_data->* TO .
    
            lv_object_key = ms_item-obj_name.
    
            TRY.
                CALL METHOD lo_registry_adapter->('/IWXBE/IF_EEEC_REG_ADAPTER_WB~GET_METADATA')
                  EXPORTING
                    iv_object_key = lv_object_key
                    iv_state      = 'I'
                  RECEIVING
                    rs_consumer   = .
    
              CATCH cx_root.
                CALL METHOD lo_registry_adapter->('/IWXBE/IF_EEEC_REG_ADAPTER_WB~GET_METADATA')
                  EXPORTING
                    iv_object_key = lv_object_key
                    iv_state      = 'A'
                  RECEIVING
                    rs_consumer   = .
            ENDTRY.
    
            ASSIGN COMPONENT 'CHANGED_BY' OF STRUCTURE  TO .
            rv_user = .
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise( iv_text     = lx_error->get_text( )
                                          ix_previous = lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_enhc IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_composite_id = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE changedby INTO rv_user FROM enhcompheader
          WHERE enhcomposite = ms_item-obj_name AND version = 'A'.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lx_enh_root   TYPE REF TO cx_enh_root,
              li_enh_object TYPE REF TO if_enh_object.
    
        TRY.
            li_enh_object = cl_enh_factory=>load_enhancement_composite(
              name = mv_composite_id
              lock = abap_true ).
    
            li_enh_object->delete( nevertheless_delete = abap_true
                                   run_dark            = abap_true ).
            li_enh_object->unlock( ).
    
          CATCH cx_enh_root INTO lx_enh_root.
            zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lx_enh_root         TYPE REF TO cx_enh_root,
              li_enh_composite    TYPE REF TO if_enh_composite,
              lv_package          TYPE devclass,
              lt_composite_childs TYPE enhcompositename_it,
              lt_enh_childs       TYPE enhname_it,
              lv_longtext_id      TYPE enhdocuobject,
              lv_vers             TYPE enhcompheader-version,
              lv_shorttext        TYPE string.
    
        FIELD-SYMBOLS:  TYPE enhcompositename,
                              LIKE LINE OF lt_enh_childs.
    
        lv_package = iv_package.
    
        io_xml->read( EXPORTING iv_name = 'SHORTTEXT'
                      CHANGING  cg_data = lv_shorttext ).
        io_xml->read( EXPORTING iv_name = 'COMPOSITE_CHILDS'
                      CHANGING  cg_data = lt_composite_childs ).
        io_xml->read( EXPORTING iv_name = 'ENH_CHILDS'
                      CHANGING  cg_data = lt_enh_childs ).
        io_xml->read( EXPORTING iv_name = 'LONGTEXT_ID'
                      CHANGING  cg_data = lv_longtext_id ).
    
        SELECT SINGLE version FROM enhcompheader INTO lv_vers WHERE enhcomposite = ms_item-obj_name.
        IF sy-subrc = 0.
          " If object exists already, then set TADIR entry to deleted
          " otherwise create_enhancement_composite will fail
          tadir_delete( ).
        ENDIF.
    
        TRY.
            cl_enh_factory=>create_enhancement_composite(
              EXPORTING
                name      = mv_composite_id
                run_dark  = abap_true
              IMPORTING
                composite = li_enh_composite
              CHANGING
                devclass  = lv_package ).
    
            li_enh_composite->if_enh_object_docu~set_shorttext( lv_shorttext ).
    
            LOOP AT lt_composite_childs ASSIGNING .
              li_enh_composite->add_composite_child(  ).
            ENDLOOP.
    
            LOOP AT lt_enh_childs ASSIGNING .
              li_enh_composite->add_enh_child(  ).
            ENDLOOP.
    
            li_enh_composite->set_longtext_id( lv_longtext_id ).
    
            li_enh_composite->if_enh_object~save( ).
            li_enh_composite->if_enh_object~activate( ).
            li_enh_composite->if_enh_object~unlock( ).
    
          CATCH cx_enh_root INTO lx_enh_root.
            zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        TRY.
            cl_enh_factory=>load_enhancement_composite(
              name = mv_composite_id
              lock = abap_false ).
            rv_bool = abap_true.
          CATCH cx_enh_root.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lv_argument TYPE seqg3-garg.
    
        lv_argument = |{ mv_composite_id }|.
        OVERLAY lv_argument WITH '                                  '.
        lv_argument = |{ lv_argument }*|.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = |E_ENHANCE|
                                                iv_argument    = lv_argument ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lx_enh_root         TYPE REF TO cx_enh_root,
              li_enh_composite    TYPE REF TO if_enh_composite,
              lt_composite_childs TYPE enhcompositename_it,
              lt_enh_childs       TYPE enhname_it,
              lv_longtext_id      TYPE enhdocuobject,
              lv_shorttext        TYPE string.
    
        TRY.
            li_enh_composite = cl_enh_factory=>load_enhancement_composite(
              name = mv_composite_id
              lock = abap_false ).
    
            lv_shorttext = li_enh_composite->if_enh_object_docu~get_shorttext( ).
    
            lt_composite_childs = li_enh_composite->get_composite_childs( ).
            lt_enh_childs       = li_enh_composite->get_enh_childs( ).
            lv_longtext_id      = li_enh_composite->get_longtext_id( ).
    
            io_xml->add( iv_name = 'SHORTTEXT'
                         ig_data = lv_shorttext ).
            io_xml->add( iv_name = 'COMPOSITE_CHILDS'
                         ig_data = lt_composite_childs ).
            io_xml->add( iv_name = 'ENH_CHILDS'
                         ig_data = lt_enh_childs ).
            io_xml->add( iv_name = 'LONGTEXT_ID'
                         ig_data = lv_longtext_id ).
    
          CATCH cx_enh_root INTO lx_enh_root.
            zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_enho_badi IMPLEMENTATION.
    
      METHOD constructor.
        ms_item = is_item.
        mv_abap_language_version = iv_abap_language_version.
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enho~deserialize.
    
        DATA: lv_spot_name TYPE enhspotname,
              lv_shorttext TYPE string,
              lv_enhname   TYPE enhname,
              lo_badi      TYPE REF TO cl_enh_tool_badi_impl,
              li_tool      TYPE REF TO if_enh_tool,
              lv_package   TYPE devclass,
              lt_impl      TYPE enh_badi_impl_data_it,
              lx_enh_root  TYPE REF TO cx_enh_root.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_impl.
    
        ii_xml->read( EXPORTING iv_name = 'SHORTTEXT'
                      CHANGING  cg_data = lv_shorttext ).
        ii_xml->read( EXPORTING iv_name = 'SPOT_NAME'
                      CHANGING  cg_data = lv_spot_name ).
        ii_xml->read( EXPORTING iv_name = 'IMPL'
                      CHANGING  cg_data = lt_impl ).
    
        lv_enhname = ms_item-obj_name.
        lv_package = iv_package.
        TRY.
            TRY.
                CALL METHOD ('CL_ENH_FACTORY')=>create_enhancement
                  EXPORTING
                    enhname               = lv_enhname
                    enhtype               = cl_abstract_enh_tool_redef=>credefinition
                    enhtooltype           = cl_enh_tool_badi_impl=>tooltype
                    abap_language_version = mv_abap_language_version " not on lower releases
                  IMPORTING
                    enhancement           = li_tool
                  CHANGING
                    devclass              = lv_package.
              CATCH cx_root.
                cl_enh_factory=>create_enhancement(
                  EXPORTING
                    enhname     = lv_enhname
                    enhtype     = cl_abstract_enh_tool_redef=>credefinition
                    enhtooltype = cl_enh_tool_badi_impl=>tooltype
                  IMPORTING
                    enhancement = li_tool
                  CHANGING
                    devclass    = lv_package ).
            ENDTRY.
    
            lo_badi ?= li_tool.
    
            lo_badi->set_spot_name( lv_spot_name ).
            lo_badi->if_enh_object_docu~set_shorttext( lv_shorttext ).
            LOOP AT lt_impl ASSIGNING .
              lo_badi->add_implementation(  ).
            ENDLOOP.
            lo_badi->if_enh_object~save( run_dark = abap_true ).
            lo_badi->if_enh_object~unlock( ).
          CATCH cx_enh_root INTO lx_enh_root.
            TRY.
                lo_badi->if_enh_object~unlock( ).
              CATCH cx_sy_ref_is_initial cx_enh_mod_not_allowed ##NO_HANDLER.
            ENDTRY.
            zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enho~serialize.
    
        DATA: lo_badi_impl TYPE REF TO cl_enh_tool_badi_impl,
              lv_spot_name TYPE enhspotname,
              lv_shorttext TYPE string,
              lt_impl      TYPE enh_badi_impl_data_it.
    
        FIELD-SYMBOLS:    LIKE LINE OF lt_impl,
                        LIKE LINE OF -filter_values,
                        LIKE LINE OF -filters.
    
        lo_badi_impl ?= ii_enh_tool.
    
        lv_shorttext = lo_badi_impl->if_enh_object_docu~get_shorttext( ).
        lv_spot_name = lo_badi_impl->get_spot_name( ).
        lt_impl      = lo_badi_impl->get_implementations( ).
    
        LOOP AT lt_impl ASSIGNING .
    * make sure the XML serialization does not dump, field type = N
          LOOP AT -filter_values ASSIGNING .
            IF -filter_numeric_value1 CA space.
              CLEAR -filter_numeric_value1.
            ENDIF.
          ENDLOOP.
          LOOP AT -filters ASSIGNING .
            IF -filter_numeric_value1 CA space.
              CLEAR -filter_numeric_value1.
            ENDIF.
          ENDLOOP.
        ENDLOOP.
    
        ii_xml->add( iv_name = 'TOOL'
                     ig_data = ii_enh_tool->get_tool( ) ).
        ii_xml->add( ig_data = lv_shorttext
                     iv_name = 'SHORTTEXT' ).
        ii_xml->add( iv_name = 'SPOT_NAME'
                     ig_data = lv_spot_name ).
        ii_xml->add( iv_name = 'IMPL'
                     ig_data = lt_impl ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_enho_hook IMPLEMENTATION.
    
      METHOD add_sources.
    
        DATA lv_source TYPE string.
        DATA ls_file LIKE LINE OF ct_files.
    
        FIELD-SYMBOLS  LIKE LINE OF ct_enhancements.
    
        LOOP AT ct_enhancements ASSIGNING .
          " Use hash as filename since full_name is very long
          CLEAR ls_file.
          ls_file-name = -full_name.
          ls_file-file = substring(
            val = zcl_abapgit_hash=>sha1_string( -full_name )
            len = 8 ).
          INSERT ls_file INTO TABLE ct_files.
    
          " Add full name as comment and put code between enhancement statements
          lv_source = c_enhancement.
          REPLACE '*' IN lv_source WITH ms_item-obj_name.
          INSERT lv_source INTO -source INDEX 1.
    
          lv_source = |"Name: { -full_name }|.
          INSERT lv_source INTO -source INDEX 1.
    
          APPEND c_endenhancement TO -source.
    
    * some ENHO might be inconsistent, resulting in identical filenames added to mo_files, so do a check
          IF mo_files->contains_file( iv_extra = ls_file-file
                                      iv_ext = 'abap' ) = abap_true.
            zcx_abapgit_exception=>raise( |ENHO { ms_item-obj_name
              } contains enhancements with duplicate filenames (hash collision)| ).
          ENDIF.
    
          mo_files->add_abap( iv_extra = ls_file-file
                              it_abap  = -source ).
    
          CLEAR -source.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD constructor.
        ms_item = is_item.
        mo_files = io_files.
        mv_abap_language_version = iv_abap_language_version.
      ENDMETHOD.
    
      METHOD hook_impl_deserialize.
    
        FIELD-SYMBOLS:    LIKE LINE OF ct_impl,
                          TYPE string,
                         TYPE i,
                        LIKE LINE OF it_spaces.
    
        LOOP AT ct_impl ASSIGNING .
          READ TABLE it_spaces ASSIGNING  WITH KEY full_name = -full_name.
          IF sy-subrc = 0.
            LOOP AT -source ASSIGNING .
              READ TABLE -spaces ASSIGNING  INDEX sy-tabix.
              IF sy-subrc = 0 AND  > 0.
                DO  TIMES.
                  CONCATENATE space  INTO  RESPECTING BLANKS.
                ENDDO.
              ENDIF.
            ENDLOOP.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD read_sources.
    
        DATA lv_source TYPE string.
        DATA ls_file LIKE LINE OF ct_files.
        DATA lv_from TYPE i.
        DATA lv_to TYPE i.
    
        FIELD-SYMBOLS  LIKE LINE OF ct_enhancements.
    
        LOOP AT ct_enhancements ASSIGNING .
          READ TABLE ct_files INTO ls_file WITH TABLE KEY name = -full_name.
          IF sy-subrc = 0.
            -source = mo_files->read_abap( iv_extra = ls_file-file ).
            " Get code between enhancement statements
            LOOP AT -source INTO lv_source.
              IF lv_source CP c_enhancement.
                lv_from = sy-tabix.
              ENDIF.
              IF lv_source CP c_endenhancement.
                lv_to = sy-tabix.
              ENDIF.
            ENDLOOP.
            DELETE -source FROM lv_to.
            DELETE -source TO lv_from.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enho~deserialize.
    
        DATA: lv_shorttext       TYPE string,
              lo_hook_impl       TYPE REF TO cl_enh_tool_hook_impl,
              li_tool            TYPE REF TO if_enh_tool,
              lv_enhname         TYPE enhname,
              lv_package         TYPE devclass,
              ls_original_object TYPE enh_hook_admin,
              lt_spaces          TYPE ty_spaces_tt,
              lt_files           TYPE ty_files,
              lt_enhancements    TYPE enh_hook_impl_it,
              lx_enh_root        TYPE REF TO cx_enh_root.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_enhancements.
    
        ii_xml->read( EXPORTING iv_name = 'SHORTTEXT'
                      CHANGING  cg_data = lv_shorttext ).
        ii_xml->read( EXPORTING iv_name = 'ORIGINAL_OBJECT'
                      CHANGING  cg_data = ls_original_object ).
        ii_xml->read( EXPORTING iv_name = 'ENHANCEMENTS'
                      CHANGING  cg_data = lt_enhancements ).
        ii_xml->read( EXPORTING iv_name = 'FILES'
                      CHANGING  cg_data = lt_files ).
        ii_xml->read( EXPORTING iv_name = 'SPACES'
                      CHANGING  cg_data = lt_spaces ).
    
        " todo: kept for compatibility, remove after grace period #3680
        hook_impl_deserialize( EXPORTING it_spaces = lt_spaces
                               CHANGING  ct_impl   = lt_enhancements ).
    
        read_sources( CHANGING ct_enhancements = lt_enhancements
                               ct_files        = lt_files ).
    
        lv_enhname = ms_item-obj_name.
        lv_package = iv_package.
        TRY.
            TRY.
                CALL METHOD ('CL_ENH_FACTORY')=>create_enhancement
                  EXPORTING
                    enhname               = lv_enhname
                    enhtype               = cl_abstract_enh_tool_redef=>credefinition
                    enhtooltype           = cl_enh_tool_hook_impl=>tooltype
                    abap_language_version = mv_abap_language_version " not on lower releases
                  IMPORTING
                    enhancement           = li_tool
                  CHANGING
                    devclass              = lv_package.
              CATCH cx_root.
                cl_enh_factory=>create_enhancement(
                  EXPORTING
                    enhname     = lv_enhname
                    enhtype     = cl_abstract_enh_tool_redef=>credefinition
                    enhtooltype = cl_enh_tool_hook_impl=>tooltype
                  IMPORTING
                    enhancement = li_tool
                  CHANGING
                    devclass    = lv_package ).
            ENDTRY.
    
            lo_hook_impl ?= li_tool.
    
            lo_hook_impl->if_enh_object_docu~set_shorttext( lv_shorttext ).
            lo_hook_impl->set_original_object(
              pgmid     = ls_original_object-pgmid
              obj_name  = ls_original_object-org_obj_name
              obj_type  = ls_original_object-org_obj_type
              program   = ls_original_object-programname
              main_type = ls_original_object-org_main_type
              main_name = ls_original_object-org_main_name ).
            lo_hook_impl->set_include_bound( ls_original_object-include_bound ).
    
            LOOP AT lt_enhancements ASSIGNING .
              lo_hook_impl->add_hook_impl(
                overwrite        = -overwrite
                method           = -method
                enhmode          = -enhmode
                full_name        = -full_name
                source           = -source
                spot             = -spotname
                parent_full_name = -parent_full_name ).
            ENDLOOP.
            lo_hook_impl->if_enh_object~save( run_dark = abap_true ).
            lo_hook_impl->if_enh_object~unlock( ).
          CATCH cx_enh_root INTO lx_enh_root.
            TRY.
                lo_hook_impl->if_enh_object~unlock( ).
              CATCH cx_sy_ref_is_initial cx_enh_mod_not_allowed ##NO_HANDLER.
            ENDTRY.
            zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enho~serialize.
    
        DATA: lv_shorttext       TYPE string,
              lo_hook_impl       TYPE REF TO cl_enh_tool_hook_impl,
              ls_original_object TYPE enh_hook_admin,
              lt_spaces          TYPE ty_spaces_tt,
              lt_files           TYPE ty_files,
              ls_progdir         TYPE zif_abapgit_sap_report=>ty_progdir,
              lt_enhancements    TYPE enh_hook_impl_it.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_enhancements.
    
        lo_hook_impl ?= ii_enh_tool.
    
        lv_shorttext = lo_hook_impl->if_enh_object_docu~get_shorttext( ).
        lo_hook_impl->get_original_object(
          IMPORTING
            pgmid     = ls_original_object-pgmid
            obj_name  = ls_original_object-org_obj_name
            obj_type  = ls_original_object-org_obj_type
            main_type = ls_original_object-org_main_type
            main_name = ls_original_object-org_main_name
            program   = ls_original_object-programname ).
    
    * dont call method lo_hook_impl->get_include_bound( ), it might dump
    * if the PROG does not exists
        IF ls_original_object-org_main_type = 'PROG' OR ls_original_object-org_main_type = 'REPS'.
          TRY.
              ls_progdir = zcl_abapgit_factory=>get_sap_report( )->read_progdir( ls_original_object-org_main_name ).
              ls_original_object-include_bound = boolc( ls_progdir-subc = 'I' ).
            CATCH zcx_abapgit_exception.
              ls_original_object-include_bound = abap_false.
          ENDTRY.
        ENDIF.
    
        lt_enhancements = lo_hook_impl->get_hook_impls( ).
    
        LOOP AT lt_enhancements ASSIGNING .
          CLEAR: -extid,
                 -id.
        ENDLOOP.
    
        add_sources( CHANGING ct_enhancements = lt_enhancements
                              ct_files        = lt_files ).
    
        ii_xml->add( iv_name = 'TOOL'
                     ig_data = ii_enh_tool->get_tool( ) ).
        ii_xml->add( iv_name = 'SHORTTEXT'
                     ig_data = lv_shorttext ).
        ii_xml->add( iv_name = 'ORIGINAL_OBJECT'
                     ig_data = ls_original_object ).
        ii_xml->add( iv_name = 'ENHANCEMENTS'
                     ig_data = lt_enhancements ).
        ii_xml->add( iv_name = 'FILES'
                     ig_data = lt_files ).
        ii_xml->add( iv_name = 'SPACES'
                     ig_data = lt_spaces ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_enho_class IMPLEMENTATION.
    
      METHOD adjust_generated_comments.
    
        FIELD-SYMBOLS  LIKE LINE OF ct_source.
    
        " Enhancements contain comments that end in '.' or ' .' depending on release
        " This routine replaces the space-dot with just dot
        LOOP AT ct_source ASSIGNING .
          IF strlen(  ) > 2.
             = replace(
              val   = 
              regex = '^(\*".*) \.$'
              with  = '$1.' ) ##REGEX_POSIX.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD constructor.
        ms_item = is_item.
        mo_files = io_files.
        mv_abap_language_version = iv_abap_language_version.
      ENDMETHOD.
    
      METHOD deserialize_includes.
    
        DATA: lt_tab_methods TYPE enhnewmeth_tab,
              lv_editorder   TYPE n LENGTH 3,
              lv_methname    TYPE seocpdname,
              lt_abap        TYPE rswsourcet,
              lx_enh_root    TYPE REF TO cx_enh_root,
              lv_new_em      TYPE abap_bool,
              lt_files       TYPE zif_abapgit_git_definitions=>ty_files_tt.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_tab_methods,
                          TYPE zif_abapgit_git_definitions=>ty_file.
    
        ii_xml->read( EXPORTING iv_name = 'TAB_METHODS'
                      CHANGING  cg_data = lt_tab_methods ).
    
        lv_new_em = abap_false.
        lt_files = mo_files->get_files( ).
        LOOP AT lt_files ASSIGNING 
            WHERE filename CS 'enho.em_'.
          lv_new_em = abap_true.
          EXIT.
        ENDLOOP.
    
        SORT lt_tab_methods BY meth_header-editorder.
        LOOP AT lt_tab_methods ASSIGNING .
    
          lv_methname = -methkey-cmpname.
          IF lv_new_em = abap_true.
            lt_abap = mo_files->read_abap( iv_extra = 'em_' && lv_methname ).
          ELSE.
            " old way
            lv_editorder = -meth_header-editorder.
            lt_abap = mo_files->read_abap( iv_extra = 'em' && lv_editorder ).
          ENDIF.
    
          TRY.
              io_class->add_change_new_method_source(
                clsname    = -methkey-clsname
                methname   = lv_methname
                methsource = lt_abap ).
            CATCH cx_enh_root INTO lx_enh_root.
              zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
          ENDTRY.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD serialize_includes.
    
        DATA: lt_includes TYPE enhnewmeth_tabincl_plus_enha,
              lt_source   TYPE TABLE OF abaptxt255,
              lv_include  TYPE syrepid.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_includes.
    
        lt_includes = io_class->get_enh_method_includes( ).
        LOOP AT lt_includes ASSIGNING .
          lv_include = io_class->if_enh_tool~get_name( ).
          TRANSLATE lv_include USING ' ='.
          lv_include+30 = 'EM'.
          lv_include+32(8) = -includenr.
    
          CALL FUNCTION 'RPY_PROGRAM_READ'
            EXPORTING
              program_name     = lv_include
              with_includelist = abap_false
              with_lowercase   = abap_true
            TABLES
              source_extended  = lt_source
            EXCEPTIONS
              cancelled        = 1
              not_found        = 2
              permission_error = 3
              OTHERS           = 4.
          IF sy-subrc = 0.
            mo_files->add_abap( iv_extra = |EM_{ -cpdname }|
                                it_abap  = lt_source ).
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enho~deserialize.
    
        DATA: lo_enh_class TYPE REF TO cl_enh_tool_class,
              lt_owr       TYPE enhmeth_tabkeys,
              lt_pre       TYPE enhmeth_tabkeys,
              lt_post      TYPE enhmeth_tabkeys,
              lt_source    TYPE rswsourcet,
              li_tool      TYPE REF TO if_enh_tool,
              lv_shorttext TYPE string,
              lv_class     TYPE seoclsname,
              lv_enhname   TYPE enhname,
              lv_package   TYPE devclass,
              lx_enh_root  TYPE REF TO cx_enh_root.
    
        ii_xml->read( EXPORTING iv_name = 'SHORTTEXT'
                      CHANGING  cg_data = lv_shorttext ).
        ii_xml->read( EXPORTING iv_name = 'OWR_METHODS'
                      CHANGING  cg_data = lt_owr ).
        ii_xml->read( EXPORTING iv_name = 'PRE_METHODS'
                      CHANGING  cg_data = lt_pre ).
        ii_xml->read( EXPORTING iv_name = 'POST_METHODS'
                      CHANGING  cg_data = lt_post ).
        ii_xml->read( EXPORTING iv_name = 'CLASS'
                      CHANGING  cg_data = lv_class ).
        lt_source = mo_files->read_abap( ).
    
        lv_enhname = ms_item-obj_name.
        lv_package = iv_package.
        TRY.
            TRY.
                CALL METHOD ('CL_ENH_FACTORY')=>create_enhancement
                  EXPORTING
                    enhname               = lv_enhname
                    enhtype               = ''
                    enhtooltype           = cl_enh_tool_class=>tooltype
                    abap_language_version = mv_abap_language_version " not on lower releases
                  IMPORTING
                    enhancement           = li_tool
                  CHANGING
                    devclass              = lv_package.
              CATCH cx_root.
                cl_enh_factory=>create_enhancement(
                  EXPORTING
                    enhname     = lv_enhname
                    enhtype     = ''
                    enhtooltype = cl_enh_tool_class=>tooltype
                  IMPORTING
                    enhancement = li_tool
                  CHANGING
                    devclass    = lv_package ).
            ENDTRY.
    
            lo_enh_class ?= li_tool.
    
            lo_enh_class->if_enh_object_docu~set_shorttext( lv_shorttext ).
            lo_enh_class->set_class( lv_class ).
            lo_enh_class->set_owr_methods( version     = 'I'
                                           owr_methods = lt_owr ).
            lo_enh_class->set_pre_methods( version     = 'I'
                                           pre_methods = lt_pre ).
            lo_enh_class->set_post_methods( version      = 'I'
                                            post_methods = lt_post ).
            lo_enh_class->set_eimp_include( version     = 'I'
                                            eimp_source = lt_source ).
    
            zcl_abapgit_object_enho_clif=>deserialize(
              io_xml  = ii_xml
              io_clif = lo_enh_class ).
    
            deserialize_includes(
              ii_xml   = ii_xml
              io_class = lo_enh_class ).
    
            lo_enh_class->if_enh_object~save( run_dark = abap_true ).
            lo_enh_class->if_enh_object~unlock( ).
          CATCH cx_enh_root INTO lx_enh_root.
            TRY.
                lo_enh_class->if_enh_object~unlock( ).
              CATCH cx_sy_ref_is_initial cx_enh_mod_not_allowed ##NO_HANDLER.
            ENDTRY.
            zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enho~serialize.
    
        DATA: lo_enh_class TYPE REF TO cl_enh_tool_class,
              lt_owr       TYPE enhmeth_tabkeys,
              lt_pre       TYPE enhmeth_tabkeys,
              lt_post      TYPE enhmeth_tabkeys,
              lt_source    TYPE rswsourcet,
              lv_class     TYPE seoclsname,
              lv_shorttext TYPE string.
    
        lo_enh_class ?= ii_enh_tool.
    
        lv_shorttext = lo_enh_class->if_enh_object_docu~get_shorttext( ).
        lt_owr = lo_enh_class->get_owr_methods( ).
        lt_pre = lo_enh_class->get_pre_methods( ).
        lt_post = lo_enh_class->get_post_methods( ).
        lt_source = lo_enh_class->get_eimp_include( ).
        lo_enh_class->get_class( IMPORTING class_name = lv_class ).
    
        ii_xml->add( iv_name = 'TOOL'
                     ig_data = ii_enh_tool->get_tool( ) ).
        ii_xml->add( ig_data = lv_shorttext
                     iv_name = 'SHORTTEXT' ).
        ii_xml->add( iv_name = 'CLASS'
                     ig_data = lv_class ).
        ii_xml->add( iv_name = 'OWR_METHODS'
                     ig_data = lt_owr ).
        ii_xml->add( iv_name = 'PRE_METHODS'
                     ig_data = lt_pre ).
        ii_xml->add( iv_name = 'POST_METHODS'
                     ig_data = lt_post ).
    
        adjust_generated_comments( CHANGING ct_source = lt_source ).
    
        mo_files->add_abap( lt_source ).
    
        zcl_abapgit_object_enho_clif=>serialize(
          io_xml  = ii_xml
          io_clif = lo_enh_class ).
    
        serialize_includes( lo_enh_class ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_enho_intf IMPLEMENTATION.
    
      METHOD constructor.
        ms_item  = is_item.
        mo_files = io_files.
        mv_abap_language_version = iv_abap_language_version.
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enho~deserialize.
    
        DATA: lo_enh_intf  TYPE REF TO cl_enh_tool_intf,
              li_tool      TYPE REF TO if_enh_tool,
              lv_shorttext TYPE string,
              lv_class     TYPE seoclsname,
              lv_enhname   TYPE enhname,
              lv_package   TYPE devclass,
              lx_enh_root  TYPE REF TO cx_enh_root.
    
        ii_xml->read( EXPORTING iv_name = 'SHORTTEXT'
                      CHANGING  cg_data = lv_shorttext ).
        ii_xml->read( EXPORTING iv_name = 'CLASS'
                      CHANGING  cg_data = lv_class ).
    
        lv_enhname = ms_item-obj_name.
        lv_package = iv_package.
        TRY.
            TRY.
                CALL METHOD ('CL_ENH_FACTORY')=>create_enhancement
                  EXPORTING
                    enhname               = lv_enhname
                    enhtype               = ''
                    enhtooltype           = cl_enh_tool_intf=>tooltype
                    abap_language_version = mv_abap_language_version " not on lower releases
                  IMPORTING
                    enhancement           = li_tool
                  CHANGING
                    devclass              = lv_package.
              CATCH cx_root.
                cl_enh_factory=>create_enhancement(
                  EXPORTING
                    enhname     = lv_enhname
                    enhtype     = ''
                    enhtooltype = cl_enh_tool_intf=>tooltype
                  IMPORTING
                    enhancement = li_tool
                  CHANGING
                    devclass    = lv_package ).
            ENDTRY.
    
            lo_enh_intf ?= li_tool.
    
            lo_enh_intf->if_enh_object_docu~set_shorttext( lv_shorttext ).
            lo_enh_intf->set_class( lv_class ).
    
            zcl_abapgit_object_enho_clif=>deserialize(
              io_xml  = ii_xml
              io_clif = lo_enh_intf ).
    
            lo_enh_intf->if_enh_object~save( run_dark = abap_true ).
            lo_enh_intf->if_enh_object~unlock( ).
          CATCH cx_enh_root INTO lx_enh_root.
            TRY.
                lo_enh_intf->if_enh_object~unlock( ).
              CATCH cx_sy_ref_is_initial cx_enh_mod_not_allowed ##NO_HANDLER.
            ENDTRY.
            zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enho~serialize.
    
        DATA: lo_enh_intf  TYPE REF TO cl_enh_tool_intf,
              lv_class     TYPE seoclsname,
              lv_shorttext TYPE string.
    
        lo_enh_intf ?= ii_enh_tool.
    
        lv_shorttext = lo_enh_intf->if_enh_object_docu~get_shorttext( ).
        lo_enh_intf->get_class( IMPORTING class_name = lv_class ).
    
        ii_xml->add( iv_name = 'TOOL'
                     ig_data = ii_enh_tool->get_tool( ) ).
        ii_xml->add( ig_data = lv_shorttext
                     iv_name = 'SHORTTEXT' ).
        ii_xml->add( iv_name = 'CLASS'
                     ig_data = lv_class ).
    
        zcl_abapgit_object_enho_clif=>serialize(
          io_xml  = ii_xml
          io_clif = lo_enh_intf ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_enho_wdyc IMPLEMENTATION.
    
      METHOD constructor.
        ms_item = is_item.
        mv_abap_language_version = iv_abap_language_version.
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enho~deserialize.
    
        DATA: lv_enhname TYPE enhname,
              lo_wdyconf TYPE REF TO cl_wdr_cfg_enhancement,
              li_tool    TYPE REF TO if_enh_tool,
              ls_obj     TYPE wdy_config_key,
              lv_xml     TYPE string,
              lt_data    TYPE wdy_cfg_expl_data_tab,
              lv_package TYPE devclass.
    
        ii_xml->read( EXPORTING iv_name = 'ORIGINAL_OBJECT'
                      CHANGING  cg_data = ls_obj ).
    
        ii_xml->read( EXPORTING iv_name = 'ENHANCEMENT_DATA'
                      CHANGING  cg_data = lv_xml ).
    
        lv_enhname = ms_item-obj_name.
        lv_package = iv_package.
        TRY.
            TRY.
                CALL METHOD ('CL_ENH_FACTORY')=>create_enhancement
                  EXPORTING
                    enhname               = lv_enhname
                    enhtype               = ''
                    enhtooltype           = cl_wdr_cfg_enhancement=>tooltype
                    abap_language_version = mv_abap_language_version " not on lower releases
                  IMPORTING
                    enhancement           = li_tool
                  CHANGING
                    devclass              = lv_package.
              CATCH cx_root.
                cl_enh_factory=>create_enhancement(
                  EXPORTING
                    enhname     = lv_enhname
                    enhtype     = ''
                    enhtooltype = cl_wdr_cfg_enhancement=>tooltype
                  IMPORTING
                    enhancement = li_tool
                  CHANGING
                    devclass    = lv_package ).
            ENDTRY.
    
            lo_wdyconf ?= li_tool.
    
            CALL METHOD cl_wdr_cfg_persistence_utils=>('COMP_XML_TO_TABLES')
              EXPORTING
                xml_content   = lv_xml
              IMPORTING
                expl_data_tab = lt_data.
    
    * only works on new ABAP versions, parameters differ between versions
            CALL METHOD lo_wdyconf->('SET_ENHANCEMENT_DATA')
              EXPORTING
                p_enh_data = lt_data.
    
            lo_wdyconf->if_enh_object~save( run_dark = abap_true ).
            lo_wdyconf->if_enh_object~unlock( ).
          CATCH cx_enh_root cx_static_check.
            TRY.
                lo_wdyconf->if_enh_object~unlock( ).
              CATCH cx_sy_ref_is_initial cx_enh_mod_not_allowed ##NO_HANDLER.
            ENDTRY.
            zcx_abapgit_exception=>raise( 'error deserializing ENHO wdyconf' ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enho~serialize.
    
        DATA: lo_wdyconf  TYPE REF TO cl_wdr_cfg_enhancement,
              lt_data     TYPE wdy_cfg_expl_data_tab,
              ls_outline  TYPE wdy_cfg_outline_data,
              ls_obj      TYPE wdy_config_key,
              li_document TYPE REF TO if_ixml_document,
              li_element  TYPE REF TO if_ixml_element.
    
        lo_wdyconf ?= ii_enh_tool.
    
        ls_obj = lo_wdyconf->get_original_object( ).
        ii_xml->add( iv_name = 'TOOL'
                     ig_data = ii_enh_tool->get_tool( ) ).
        ii_xml->add( iv_name = 'ORIGINAL_OBJECT'
                     ig_data = ls_obj ).
    
    * only works on new ABAP versions, parameters differ between versions
        CALL METHOD lo_wdyconf->('GET_ENHANCEMENT_DATA')
          EXPORTING
            p_scope    = 1
          IMPORTING
            p_enh_data = lt_data.
    
        CALL METHOD cl_wdr_cfg_persistence_utils=>('COMP_TABLES_TO_XML')
          EXPORTING
            outline_data  = ls_outline
            expl_data_tab = lt_data
          IMPORTING
            element       = li_element
          CHANGING
            document      = li_document.
    
        ii_xml->add_xml( iv_name = 'ENHANCEMENT_DATA'
                         ii_xml  = li_element ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_enho_fugr IMPLEMENTATION.
    
      METHOD constructor.
        ms_item = is_item.
        mo_files = io_files.
        mv_abap_language_version = iv_abap_language_version.
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enho~deserialize.
    
        DATA: lo_fugrdata  TYPE REF TO cl_enh_tool_fugr,
              ls_enha_data TYPE enhfugrdata,
              li_tool      TYPE REF TO if_enh_tool,
              lv_tool      TYPE enhtooltype,
              lv_package   TYPE devclass,
              lx_enh_root  TYPE REF TO cx_enh_root.
    
        FIELD-SYMBOLS:  TYPE enhfugrfuncdata.
    
        ii_xml->read(
          EXPORTING
            iv_name = 'TOOL'
          CHANGING
            cg_data = lv_tool ).
    
        ii_xml->read(
          EXPORTING
            iv_name = 'FUGRDATA'
          CHANGING
            cg_data = ls_enha_data ).
    
        lv_package = iv_package.
    
        TRY.
            TRY.
                CALL METHOD ('CL_ENH_FACTORY')=>create_enhancement
                  EXPORTING
                    enhname               = |{ ms_item-obj_name }|
                    enhtype               = ''
                    enhtooltype           = lv_tool
                    abap_language_version = mv_abap_language_version " not on lower releases
                  IMPORTING
                    enhancement           = li_tool
                  CHANGING
                    devclass              = lv_package.
              CATCH cx_root.
                cl_enh_factory=>create_enhancement(
                  EXPORTING
                    enhname     = |{ ms_item-obj_name }|
                    enhtype     = ''
                    enhtooltype = lv_tool
                  IMPORTING
                    enhancement = li_tool
                  CHANGING
                    devclass    = lv_package ).
            ENDTRY.
    
            lo_fugrdata ?= li_tool.
    
            lo_fugrdata->set_fugr( ls_enha_data-fugr ).
    
            LOOP AT ls_enha_data-enh_fubas ASSIGNING .
    
              lo_fugrdata->set_func_data( func_name     = -fuba
                                          func_enhadata =  ).
    
            ENDLOOP.
    
            lo_fugrdata->if_enh_object~save( run_dark = abap_true ).
            lo_fugrdata->if_enh_object~unlock( ).
          CATCH cx_enh_root INTO lx_enh_root.
            TRY.
                lo_fugrdata->if_enh_object~unlock( ).
              CATCH cx_sy_ref_is_initial cx_enh_mod_not_allowed ##NO_HANDLER.
            ENDTRY.
            zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enho~serialize.
    
        DATA: lo_fugrdata  TYPE REF TO cl_enh_tool_fugr,
              lv_fugr_name TYPE rs38l-area,
              ls_enha_data TYPE enhfugrdata.
    
        FIELD-SYMBOLS:  TYPE enhfugrparamdocu.
    
        lo_fugrdata ?= ii_enh_tool.
    
        lo_fugrdata->get_fugr( IMPORTING fugr_name = lv_fugr_name ).
    
        TRY.
            lo_fugrdata->get_all_data_for_fugr(
              EXPORTING
                fugr_name = lv_fugr_name
              IMPORTING
                enha_data = ls_enha_data ).
    
            LOOP AT ls_enha_data-docuobjs ASSIGNING .
              CLEAR: -shorttext,
                     -longtext.
            ENDLOOP.
    
          CATCH cx_enh_not_found.
            zcx_abapgit_exception=>raise( |error deserializing ENHO fugrdata { ms_item-obj_name }| ).
        ENDTRY.
    
        ii_xml->add( iv_name = 'TOOL'
                     ig_data = lo_fugrdata->if_enh_tool~get_tool( ) ).
    
        ii_xml->add( iv_name = 'FUGRDATA'
                     ig_data = ls_enha_data ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_enho_wdyn IMPLEMENTATION.
    
      METHOD constructor.
        ms_item = is_item.
        mv_abap_language_version = iv_abap_language_version.
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enho~deserialize.
    
        DATA: ls_enh_data  TYPE enhwdyn,
              li_tool      TYPE REF TO if_enh_tool,
              lo_wdyn      TYPE REF TO cl_enh_tool_wdy,
              lv_tool_type TYPE enhtooltype,
              lv_package   TYPE devclass.
    
        FIELD-SYMBOLS:  TYPE enhwdyc,
                              TYPE enhwdyv.
    
        ii_xml->read(
          EXPORTING
            iv_name = 'TOOL'
          CHANGING
            cg_data = lv_tool_type ).
    
        ii_xml->read(
          EXPORTING
            iv_name = 'COMPONENT_DATA'
          CHANGING
            cg_data = ls_enh_data ).
    
        lv_package = iv_package.
    
        TRY.
            TRY.
                CALL METHOD ('CL_ENH_FACTORY')=>create_enhancement
                  EXPORTING
                    enhname               = |{ ms_item-obj_name }|
                    enhtype               = ''
                    enhtooltype           = lv_tool_type
                    abap_language_version = mv_abap_language_version " not on lower releases
                  IMPORTING
                    enhancement           = li_tool
                  CHANGING
                    devclass              = lv_package.
              CATCH cx_root.
                cl_enh_factory=>create_enhancement(
                  EXPORTING
                    enhname     = |{ ms_item-obj_name }|
                    enhtype     = ''
                    enhtooltype = lv_tool_type
                  IMPORTING
                    enhancement = li_tool
                  CHANGING
                    devclass    = lv_package ).
            ENDTRY.
    
            lo_wdyn ?= li_tool.
    
            lo_wdyn->initialize( ls_enh_data-component_name ).
    
            lo_wdyn->set_component_data( ls_enh_data-component_data ).
    
            LOOP AT ls_enh_data-controller_data ASSIGNING .
    
              lo_wdyn->set_controller_data( p_controller_name = -controller_name
                                            p_enh_data        =  ).
    
            ENDLOOP.
    
            LOOP AT ls_enh_data-view_data ASSIGNING .
    
              lo_wdyn->set_view_data( p_view_name = -view_name
                                      p_enh_data  =  ).
    
            ENDLOOP.
    
            lo_wdyn->if_enh_object~save( run_dark = abap_true ).
            lo_wdyn->if_enh_object~unlock( ).
    
          CATCH cx_root.
            TRY.
                lo_wdyn->if_enh_object~unlock( ).
              CATCH cx_sy_ref_is_initial cx_enh_mod_not_allowed ##NO_HANDLER.
            ENDTRY.
            zcx_abapgit_exception=>raise( |error deserializing ENHO wdyn { ms_item-obj_name }| ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enho~serialize.
    
        DATA: lo_wdyn           TYPE REF TO cl_enh_tool_wdy,
              lv_component_name TYPE wdy_component_name,
              ls_enh_data       TYPE enhwdyn.
    
        lo_wdyn ?= ii_enh_tool.
        lv_component_name = lo_wdyn->get_component_name( ).
    
        TRY.
            lo_wdyn->get_all_data_for_comp(
              EXPORTING
                p_component_name = lv_component_name
              IMPORTING
                p_enh_data       = ls_enh_data ).
    
            ii_xml->add( iv_name = 'TOOL'
                         ig_data = ii_enh_tool->get_tool( ) ).
    
            ii_xml->add( iv_name = 'COMPONENT_DATA'
                         ig_data = ls_enh_data ).
    
          CATCH cx_enh_not_found.
            zcx_abapgit_exception=>raise( |error serializing ENHO wdyn { ms_item-obj_name }| ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_enho IMPLEMENTATION.
    
      METHOD factory.
    
        CASE iv_tool.
          WHEN cl_enh_tool_badi_impl=>tooltype.
            CREATE OBJECT ri_enho TYPE zcl_abapgit_object_enho_badi
              EXPORTING
                is_item                  = ms_item
                iv_abap_language_version = iv_abap_language_version.
          WHEN cl_enh_tool_hook_impl=>tooltype.
            CREATE OBJECT ri_enho TYPE zcl_abapgit_object_enho_hook
              EXPORTING
                is_item                  = ms_item
                io_files                 = mo_files
                iv_abap_language_version = iv_abap_language_version.
          WHEN cl_enh_tool_class=>tooltype.
            CREATE OBJECT ri_enho TYPE zcl_abapgit_object_enho_class
              EXPORTING
                is_item                  = ms_item
                io_files                 = mo_files
                iv_abap_language_version = iv_abap_language_version.
          WHEN cl_enh_tool_intf=>tooltype.
            CREATE OBJECT ri_enho TYPE zcl_abapgit_object_enho_intf
              EXPORTING
                is_item                  = ms_item
                io_files                 = mo_files
                iv_abap_language_version = iv_abap_language_version.
          WHEN cl_wdr_cfg_enhancement=>tooltype.
            CREATE OBJECT ri_enho TYPE zcl_abapgit_object_enho_wdyc
              EXPORTING
                is_item                  = ms_item
                iv_abap_language_version = iv_abap_language_version.
          WHEN 'FUGRENH'.
            CREATE OBJECT ri_enho TYPE zcl_abapgit_object_enho_fugr
              EXPORTING
                is_item                  = ms_item
                io_files                 = mo_files
                iv_abap_language_version = iv_abap_language_version.
          WHEN 'WDYENH'.
            CREATE OBJECT ri_enho TYPE zcl_abapgit_object_enho_wdyn
              EXPORTING
                is_item                  = ms_item
                iv_abap_language_version = iv_abap_language_version.
          WHEN OTHERS.
            zcx_abapgit_exception=>raise( |Unsupported ENHO type { iv_tool }| ).
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lv_enh_id   TYPE enhname,
              lt_log      TYPE enh_log_it,
              li_log_obj  TYPE REF TO if_enh_log,
              ls_enhlog   TYPE enhlog,
              lv_lines    TYPE i,
              lt_enhlog   TYPE STANDARD TABLE OF enhlog WITH DEFAULT KEY,
              li_enh_tool TYPE REF TO if_enh_tool.
    
        lv_enh_id = ms_item-obj_name.
        TRY.
            li_enh_tool = cl_enh_factory=>get_enhancement(
              enhancement_id   = lv_enh_id
              run_dark         = abap_true
              bypassing_buffer = abap_true ).
          CATCH cx_enh_root.
            rv_user = c_user_unknown.
            RETURN.
        ENDTRY.
    
        lt_log = li_enh_tool->get_log( ).
    
        LOOP AT lt_log INTO li_log_obj.
          ls_enhlog = li_log_obj->get_enhlog( ).
          APPEND ls_enhlog TO lt_enhlog.
        ENDLOOP.
    
        lv_lines = lines( lt_enhlog ).
        READ TABLE lt_enhlog INTO ls_enhlog INDEX lv_lines.
        IF sy-subrc = 0.
          rv_user = ls_enhlog-loguser.
        ELSE.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_enh_id     TYPE enhname,
              li_enh_object TYPE REF TO if_enh_object,
              lx_enh_root   TYPE REF TO cx_enh_root,
              lv_corrnum    TYPE trkorr.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        zcl_abapgit_sotr_handler=>delete_sotr(
          iv_pgmid    = 'R3TR'
          iv_object   = ms_item-obj_type
          iv_obj_name = ms_item-obj_name ).
    
        zcl_abapgit_sots_handler=>delete_sots(
          iv_pgmid    = 'R3TR'
          iv_object   = ms_item-obj_type
          iv_obj_name = ms_item-obj_name ).
    
        lv_corrnum = iv_transport.
    
        lv_enh_id = ms_item-obj_name.
        TRY.
            li_enh_object = cl_enh_factory=>get_enhancement(
              enhancement_id = lv_enh_id
              run_dark       = abap_true
              lock           = abap_true ).
            li_enh_object->delete(
              EXPORTING
                nevertheless_delete = abap_true
                run_dark            = abap_true
              CHANGING
                trkorr              = lv_corrnum ).
            li_enh_object->unlock( ).
          CATCH cx_enh_root INTO lx_enh_root.
            zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_tool TYPE enhtooltype,
              li_enho TYPE REF TO zif_abapgit_object_enho.
        DATA lv_abap_language_version TYPE uccheck.
    
        IF zif_abapgit_object~exists( ) = abap_true.
          zif_abapgit_object~delete( iv_package   = iv_package
                                     iv_transport = iv_transport
                                     ii_log       = ii_log ).
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'TOOL'
                      CHANGING  cg_data = lv_tool ).
    
        io_xml->read( EXPORTING iv_name = 'ABAP_LANGUAGE_VERSION'
                      CHANGING  cg_data = lv_abap_language_version ).
    
        set_abap_language_version( CHANGING cv_abap_language_version = lv_abap_language_version ).
    
        li_enho = factory(
          iv_tool                  = lv_tool
          iv_abap_language_version = lv_abap_language_version ).
    
        li_enho->deserialize( ii_xml     = io_xml
                              iv_package = iv_package ).
    
        zcl_abapgit_sotr_handler=>create_sotr(
          iv_package = iv_package
          io_xml     = io_xml ).
    
        zcl_abapgit_sots_handler=>create_sots(
          iv_package = iv_package
          io_xml     = io_xml ).
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_enh_id TYPE enhname.
    
        lv_enh_id = ms_item-obj_name.
        TRY.
            cl_enh_factory=>get_enhancement(
              enhancement_id   = lv_enh_id
              run_dark         = abap_true
              bypassing_buffer = abap_true ).
            rv_bool = abap_true.
          CATCH cx_enh_root.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lv_object TYPE seqg3-garg.
    
        lv_object = |{ ms_item-obj_type }{ ms_item-obj_name }|.
        OVERLAY lv_object WITH '                                          '.
        lv_object = lv_object && '*'.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'E_ENHANCE'
                                                iv_argument    = lv_object ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_enh_id   TYPE enhname,
              li_enho     TYPE REF TO zif_abapgit_object_enho,
              li_enh_tool TYPE REF TO if_enh_tool,
              lx_enh_root TYPE REF TO cx_enh_root.
        DATA lv_abap_language_version TYPE uccheck.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        lv_enh_id = ms_item-obj_name.
        TRY.
            li_enh_tool = cl_enh_factory=>get_enhancement(
              enhancement_id   = lv_enh_id
              run_dark         = abap_true
              bypassing_buffer = abap_true ).
          CATCH cx_enh_root INTO lx_enh_root.
            zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
        ENDTRY.
    
        TRY.
            SELECT SINGLE ('ABAP_LANGUAGE_VERSION') FROM enhheader INTO lv_abap_language_version
              WHERE enhname = ms_item-obj_name AND version = 'A'.
            IF sy-subrc = 0.
              clear_abap_language_version( CHANGING cv_abap_language_version = lv_abap_language_version ).
    
              io_xml->add( iv_name = 'ABAP_LANGUAGE_VERSION'
                           ig_data = lv_abap_language_version ).
            ENDIF.
          CATCH cx_root ##NO_HANDLER.
        ENDTRY.
    
        li_enho = factory(
          iv_tool                  = li_enh_tool->get_tool( )
          iv_abap_language_version = lv_abap_language_version ).
    
        li_enho->serialize( ii_xml      = io_xml
                            ii_enh_tool = li_enh_tool ).
    
        zcl_abapgit_sotr_handler=>read_sotr(
          iv_pgmid       = 'R3TR'
          iv_object      = ms_item-obj_type
          iv_obj_name    = ms_item-obj_name
          io_i18n_params = mo_i18n_params
          io_xml         = io_xml ).
    
        zcl_abapgit_sots_handler=>read_sots(
          iv_pgmid       = 'R3TR'
          iv_object      = ms_item-obj_type
          iv_obj_name    = ms_item-obj_name
          io_i18n_params = mo_i18n_params
          io_xml         = io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_enho_clif IMPLEMENTATION.
    
      METHOD deserialize.
    
        DATA: lt_tab_attributes TYPE enhclasstabattrib,
              lt_tab_types      TYPE enhtype_tab,
              lt_tab_methods    TYPE enhnewmeth_tab,
              ls_type_line      TYPE vseotype,
              ls_header         TYPE vseomethod,
              ls_param          TYPE vseomepara,
              ls_exc            TYPE vseoexcep,
              lt_tab_eventdata  TYPE enhevent_tab,
              ls_event_line     TYPE vseoevent,
              ls_event_param    TYPE vseoeparam.
    
        FIELD-SYMBOLS:         LIKE LINE OF lt_tab_types,
                             LIKE LINE OF lt_tab_methods,
                              LIKE LINE OF -meth_param,
                              LIKE LINE OF lt_tab_eventdata,
                                LIKE LINE OF -meth_exc,
                        LIKE LINE OF -event_param.
    
        io_xml->read( EXPORTING iv_name = 'TAB_ATTRIBUTES'
                      CHANGING cg_data = lt_tab_attributes ).
        io_xml->read( EXPORTING iv_name = 'TAB_TYPES'
                      CHANGING cg_data = lt_tab_types ).
        io_xml->read( EXPORTING iv_name = 'TAB_METHODS'
                      CHANGING cg_data = lt_tab_methods ).
        io_xml->read( EXPORTING iv_name = 'TAB_EVENTDATA'
                      CHANGING cg_data = lt_tab_eventdata ).
    
        LOOP AT lt_tab_types ASSIGNING .
          MOVE-CORRESPONDING  TO ls_type_line.
          TRY.
              io_clif->add_change_enha_type( type_line = ls_type_line ).
            CATCH cx_enh_mod_not_allowed
            cx_enh_is_not_enhanceable ##NO_HANDLER.
              " TODO
          ENDTRY.
        ENDLOOP.
    
        io_clif->set_enhattributes( lt_tab_attributes ).
    
    * SAP standard SET_ENH_NEW_METHOS does not work
    
        LOOP AT lt_tab_methods ASSIGNING .
    
          MOVE-CORRESPONDING -meth_header TO ls_header.
    
          io_clif->add_change_new_enh_method(
            methkey       = -methkey
            method_header = ls_header ).
    
    * parameters
          LOOP AT -meth_param ASSIGNING .
            MOVE-CORRESPONDING  TO ls_param.
            io_clif->add_change_enh_methparam(
              methname   = -methkey-cmpname
              param_line = ls_param ).
          ENDLOOP.
    
    * exceptions
          LOOP AT -meth_exc ASSIGNING .
            MOVE-CORRESPONDING  TO ls_exc.
            io_clif->add_change_enh_methexc(
              methname    = -methkey-cmpname
              except_line = ls_exc ).
          ENDLOOP.
    
        ENDLOOP.
    
        " events are renumbered based on
        LOOP AT lt_tab_eventdata ASSIGNING .
    
          MOVE-CORRESPONDING -event_header TO ls_event_line.
    
          io_clif->add_change_enha_event(
            event_key  = -eventkey
            event_line = ls_event_line ).
    
    * parameters
          LOOP AT -event_param ASSIGNING .
            MOVE-CORRESPONDING  TO ls_event_param.
            io_clif->add_change_enh_eventparam(
              eventname   = -eventkey-cmpname
              event_param = ls_event_param ).
          ENDLOOP.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD serialize.
    
        DATA: lt_tab_attributes TYPE enhclasstabattrib,
              lt_tab_types      TYPE enhtype_tab,
              lt_tab_methods    TYPE enhnewmeth_tab,
              lt_tab_eventdata  TYPE enhevent_tab,
              lv_editorder      TYPE i.
    
        FIELD-SYMBOLS:         LIKE LINE OF lt_tab_attributes,
                               LIKE LINE OF lt_tab_types,
                               LIKE LINE OF lt_tab_methods,
                              LIKE LINE OF -meth_param,
                                LIKE LINE OF -meth_exc,
                              LIKE LINE OF lt_tab_eventdata,
                        LIKE LINE OF -event_param.
    
        io_clif->get_enhattributes( IMPORTING tab_attributes = lt_tab_attributes ).
    
        io_clif->get_enhatypes( IMPORTING tab_types = lt_tab_types ).
    
        io_clif->get_enh_new_methodes( IMPORTING tab_methodes = lt_tab_methods ).
    
        io_clif->get_enhevents( IMPORTING tab_eventdata = lt_tab_eventdata ).
    
        LOOP AT lt_tab_attributes ASSIGNING .
          CLEAR: -author,
                 -createdon,
                 -changedby,
                 -changedon,
                 -descript_id.
        ENDLOOP.
    
        LOOP AT lt_tab_types ASSIGNING .
          CLEAR: -author,
                 -createdon,
                 -changedby,
                 -changedon,
                 -descript_id.
        ENDLOOP.
    
        lv_editorder = 0.
        SORT lt_tab_methods BY meth_header-editorder.
        LOOP AT lt_tab_methods ASSIGNING .
          CLEAR: -meth_header-author,
                 -meth_header-createdon,
                 -meth_header-changedby,
                 -meth_header-changedon,
                 -meth_header-descript_id.
          lv_editorder = lv_editorder + 1.
          -meth_header-editorder = lv_editorder.
          LOOP AT -meth_param ASSIGNING .
            CLEAR: -author,
                   -createdon,
                   -changedby,
                   -changedon,
                   -descript_id.
          ENDLOOP.
          LOOP AT -meth_exc ASSIGNING .
            CLEAR: -author,
                   -createdon,
                   -changedby,
                   -changedon,
                   -descript_id.
          ENDLOOP.
        ENDLOOP.
    
        LOOP AT lt_tab_eventdata ASSIGNING .
          CLEAR: -event_header-author,
                 -event_header-createdon,
                 -event_header-changedby,
                 -event_header-changedon,
                 -event_header-descript_id.
          LOOP AT -event_param ASSIGNING .
            CLEAR: -author,
                   -createdon,
                   -changedby,
                   -changedon,
                   -descript_id.
          ENDLOOP.
        ENDLOOP.
    
        io_xml->add( iv_name = 'TAB_ATTRIBUTES'
                     ig_data = lt_tab_attributes ).
        io_xml->add( iv_name = 'TAB_TYPES'
                     ig_data = lt_tab_types ).
        io_xml->add( iv_name = 'TAB_METHODS'
                     ig_data = lt_tab_methods ).
        io_xml->add( iv_name = 'TAB_EVENTDATA'
                     ig_data = lt_tab_eventdata ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_enhs_badi_d IMPLEMENTATION.
    
      METHOD zif_abapgit_object_enhs~deserialize.
    
        DATA: lv_parent          TYPE enhspotcompositename,
              lt_enh_badi        TYPE enh_badi_data_it,
              lo_badidef_tool    TYPE REF TO cl_enh_tool_badi_def,
              lv_enh_shorttext   TYPE string,
              li_enh_object      TYPE REF TO if_enh_object,
              li_enh_object_docu TYPE REF TO if_enh_object_docu,
              lx_enh_root        TYPE REF TO cx_enh_root.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_enh_badi.
    
        ii_xml->read( EXPORTING iv_name = 'PARENT_COMP'
                      CHANGING  cg_data = lv_parent ).
    
        ii_xml->read( EXPORTING iv_name = 'BADI_DATA'
                      CHANGING  cg_data = lt_enh_badi ).
    
        ii_xml->read( EXPORTING iv_name = 'SHORTTEXT'
                      CHANGING  cg_data = lv_enh_shorttext ).
    
        li_enh_object ?= ii_enh_spot_tool.
        li_enh_object_docu ?= ii_enh_spot_tool.
    
        TRY.
            li_enh_object_docu->set_shorttext( lv_enh_shorttext ).
    
            lo_badidef_tool ?= ii_enh_spot_tool.
    
            LOOP AT lt_enh_badi ASSIGNING .
              lo_badidef_tool->add_badi_def(  ).
            ENDLOOP.
    
            li_enh_object->save( ).
            li_enh_object->activate( ).
            li_enh_object->unlock( ).
    
          CATCH cx_enh_root INTO lx_enh_root.
            zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enhs~serialize.
    
        DATA: lv_spot_name       TYPE enhspotname,
              lv_parent          TYPE enhspotcompositename,
              lt_enh_badi        TYPE enh_badi_data_it,
              lo_badidef_tool    TYPE REF TO cl_enh_tool_badi_def,
              lv_enh_shorttext   TYPE string,
              li_enh_object_docu TYPE REF TO if_enh_object_docu.
    
        lo_badidef_tool ?= ii_enh_spot_tool.
    
        li_enh_object_docu ?= ii_enh_spot_tool.
        lv_enh_shorttext = li_enh_object_docu->get_shorttext( ).
    
        "get parent = composite enhs (ENHC)
        lv_parent = cl_r3standard_persistence=>enh_find_parent_composite( lv_spot_name ).
        "get subsequent BADI definitions
        lt_enh_badi = lo_badidef_tool->get_badi_defs( ).
    
        ii_xml->add( ig_data = ii_enh_spot_tool->get_tool( )
                     iv_name = 'TOOL' ).
    
        ii_xml->add( ig_data = lv_enh_shorttext
                     iv_name = 'SHORTTEXT' ).
    
        ii_xml->add( ig_data = lv_parent
                     iv_name = 'PARENT_COMP' ).
    
        ii_xml->add( ig_data = lt_enh_badi
                     iv_name = 'BADI_DATA' ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_enhs_hook_d IMPLEMENTATION.
    
      METHOD zif_abapgit_object_enhs~deserialize.
    
        DATA: lv_enh_shorttext       TYPE string,
              ls_enh_hook_definition TYPE enh_hook_def,
              ls_hook_definition     TYPE ty_hook_defifnition,
              li_enh_object          TYPE REF TO if_enh_object,
              li_enh_object_docu     TYPE REF TO if_enh_object_docu,
              lo_hookdef_tool        TYPE REF TO cl_enh_tool_hook_def,
              lx_enh_root            TYPE REF TO cx_enh_root.
    
        FIELD-SYMBOLS:  TYPE enh_hook_def_ext.
    
        ii_xml->read( EXPORTING iv_name = 'SHORTTEXT'
                      CHANGING  cg_data = lv_enh_shorttext ).
    
        ii_xml->read( EXPORTING iv_name = 'BADI_DATA'
                      CHANGING  cg_data = ls_hook_definition ).
    
        li_enh_object ?= ii_enh_spot_tool.
        li_enh_object_docu ?= ii_enh_spot_tool.
    
        TRY.
            li_enh_object_docu->set_shorttext( lv_enh_shorttext ).
    
            lo_hookdef_tool ?= ii_enh_spot_tool.
    
            lo_hookdef_tool->set_original_object( pgmid     = ls_hook_definition-pgmid
                                                  obj_name  = ls_hook_definition-obj_name
                                                  obj_type  = ls_hook_definition-obj_type
                                                  program   = ls_hook_definition-program
                                                  main_type = ls_hook_definition-main_type
                                                  main_name = ls_hook_definition-main_name ).
    
            LOOP AT ls_hook_definition-def_hooks ASSIGNING .
              MOVE-CORRESPONDING  TO ls_enh_hook_definition.
              lo_hookdef_tool->add_hook_def( ls_enh_hook_definition ).
            ENDLOOP.
    
            li_enh_object->save( ).
            li_enh_object->activate( ).
            li_enh_object->unlock( ).
    
          CATCH cx_enh_root INTO lx_enh_root.
            zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object_enhs~serialize.
    
        DATA: lo_hookdef_tool    TYPE REF TO cl_enh_tool_hook_def,
              lv_enh_shorttext   TYPE string,
              li_enh_object_docu TYPE REF TO if_enh_object_docu,
              ls_hook_definition TYPE ty_hook_defifnition.
    
        lo_hookdef_tool ?= ii_enh_spot_tool.
    
        li_enh_object_docu ?= ii_enh_spot_tool.
        lv_enh_shorttext = li_enh_object_docu->get_shorttext( ).
    
        ls_hook_definition-def_hooks = lo_hookdef_tool->get_hook_defs( ).
    
        lo_hookdef_tool->get_original_object(
          IMPORTING
            pgmid     = ls_hook_definition-pgmid
            obj_name  = ls_hook_definition-obj_name
            obj_type  = ls_hook_definition-obj_type
            main_type = ls_hook_definition-main_type
            main_name = ls_hook_definition-main_name
            program   = ls_hook_definition-program ).
    
        ii_xml->add( ig_data = ii_enh_spot_tool->get_tool( )
                     iv_name = 'TOOL' ).
    
        ii_xml->add( ig_data = lv_enh_shorttext
                     iv_name = 'SHORTTEXT' ).
    
        ii_xml->add( ig_data = ls_hook_definition
                     iv_name = 'BADI_DATA' ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_enhs IMPLEMENTATION.
    
      METHOD factory.
    
        CASE iv_tool.
          WHEN cl_enh_tool_badi_def=>tooltype.
            CREATE OBJECT ri_enho TYPE zcl_abapgit_object_enhs_badi_d.
          WHEN cl_enh_tool_hook_def=>tool_type.
            CREATE OBJECT ri_enho TYPE zcl_abapgit_object_enhs_hook_d.
          WHEN OTHERS.
            zcx_abapgit_exception=>raise( |ENHS: Unsupported tool { iv_tool }| ).
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lv_spot_name TYPE enhspotname,
              li_spot_ref  TYPE REF TO if_enh_spot_tool.
    
        lv_spot_name = ms_item-obj_name.
    
        TRY.
            li_spot_ref = cl_enh_factory=>get_enhancement_spot( spot_name = lv_spot_name
                                                                run_dark  = abap_true ).
            li_spot_ref->get_attributes( IMPORTING changedby = rv_user ).
    
          CATCH cx_enh_root.
            rv_user = c_user_unknown.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_spot_name  TYPE enhspotname,
              lx_enh_root   TYPE REF TO cx_enh_root,
              li_enh_object TYPE REF TO if_enh_object.
    
        zcl_abapgit_sotr_handler=>delete_sotr(
          iv_pgmid    = 'R3TR'
          iv_object   = ms_item-obj_type
          iv_obj_name = ms_item-obj_name ).
    
        zcl_abapgit_sots_handler=>delete_sots(
          iv_pgmid    = 'R3TR'
          iv_object   = ms_item-obj_type
          iv_obj_name = ms_item-obj_name ).
    
        lv_spot_name  = ms_item-obj_name.
    
        TRY.
            li_enh_object ?= cl_enh_factory=>get_enhancement_spot( spot_name = lv_spot_name
                                                                   run_dark  = abap_true
                                                                   lock      = abap_true ).
    
            li_enh_object->delete( nevertheless_delete = abap_true
                                   run_dark            = abap_true ).
    
            li_enh_object->unlock( ).
    
          CATCH cx_enh_root INTO lx_enh_root.
            zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_parent    TYPE        enhspotcompositename,
              lv_spot_name TYPE        enhspotname,
              lv_tool      TYPE        enhspottooltype,
              lv_package   LIKE        iv_package,
              lx_enh_root  TYPE REF TO cx_enh_root,
              li_spot_ref  TYPE REF TO if_enh_spot_tool,
              li_enhs      TYPE REF TO zif_abapgit_object_enhs.
        DATA lv_abap_language_version TYPE uccheck.
    
        IF zif_abapgit_object~exists( ) = abap_true.
          zif_abapgit_object~delete( iv_package   = iv_package
                                     iv_transport = iv_transport
                                     ii_log       = ii_log ).
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'TOOL'
                      CHANGING  cg_data = lv_tool ).
    
        io_xml->read( EXPORTING iv_name = 'ABAP_LANGUAGE_VERSION'
                      CHANGING  cg_data = lv_abap_language_version ).
    
        set_abap_language_version( CHANGING cv_abap_language_version = lv_abap_language_version ).
    
        lv_spot_name = ms_item-obj_name.
        lv_package   = iv_package.
    
        TRY.
            TRY.
                CALL METHOD ('CL_ENH_FACTORY')=>create_enhancement_spot
                  EXPORTING
                    spot_name             = lv_spot_name
                    tooltype              = lv_tool
                    dark                  = abap_false
                    compositename         = lv_parent
                    abap_language_version = lv_abap_language_version " not on lower releases
                  IMPORTING
                    spot                  = li_spot_ref
                  CHANGING
                    devclass              = lv_package.
              CATCH cx_root.
                cl_enh_factory=>create_enhancement_spot(
                  EXPORTING
                    spot_name     = lv_spot_name
                    tooltype      = lv_tool
                    dark          = abap_false
                    compositename = lv_parent
                  IMPORTING
                    spot          = li_spot_ref
                  CHANGING
                    devclass      = lv_package ).
            ENDTRY.
          CATCH cx_enh_root INTO lx_enh_root.
            zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
        ENDTRY.
    
        li_enhs = factory( lv_tool ).
    
        li_enhs->deserialize( ii_xml           = io_xml
                              iv_package       = iv_package
                              ii_enh_spot_tool = li_spot_ref ).
    
        zcl_abapgit_sotr_handler=>create_sotr(
          iv_package = iv_package
          io_xml     = io_xml ).
    
        zcl_abapgit_sots_handler=>create_sots(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_spot_name TYPE enhspotname,
              li_spot_ref  TYPE REF TO if_enh_spot_tool.
    
        lv_spot_name = ms_item-obj_name.
    
        TRY.
            li_spot_ref = cl_enh_factory=>get_enhancement_spot( spot_name = lv_spot_name
                                                                run_dark  = abap_true ).
    
            rv_bool = abap_true.
    
          CATCH cx_enh_root.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_spot_name TYPE enhspotname,
              li_spot_ref  TYPE REF TO if_enh_spot_tool,
              li_enhs      TYPE REF TO zif_abapgit_object_enhs,
              lx_enh_root  TYPE REF TO cx_enh_root.
        DATA lv_abap_language_version TYPE uccheck.
    
        lv_spot_name = ms_item-obj_name.
    
        TRY.
            li_spot_ref = cl_enh_factory=>get_enhancement_spot( spot_name = lv_spot_name
                                                                run_dark  = abap_true ).
    
          CATCH cx_enh_root INTO lx_enh_root.
            zcx_abapgit_exception=>raise_with_text( lx_enh_root ).
        ENDTRY.
    
        TRY.
            SELECT SINGLE ('ABAP_LANGUAGE_VERSION') FROM enhspotheader INTO lv_abap_language_version
              WHERE enhspot = ms_item-obj_name AND version = 'A'.
            IF sy-subrc = 0.
              clear_abap_language_version( CHANGING cv_abap_language_version = lv_abap_language_version ).
    
              io_xml->add( iv_name = 'ABAP_LANGUAGE_VERSION'
                           ig_data = lv_abap_language_version ).
            ENDIF.
          CATCH cx_root ##NO_HANDLER.
        ENDTRY.
    
        li_enhs = factory( li_spot_ref->get_tool( ) ).
    
        li_enhs->serialize( ii_xml           = io_xml
                            ii_enh_spot_tool = li_spot_ref ).
    
        zcl_abapgit_sotr_handler=>read_sotr(
          iv_pgmid       = 'R3TR'
          iv_object      = ms_item-obj_type
          iv_obj_name    = ms_item-obj_name
          io_i18n_params = mo_i18n_params
          io_xml         = io_xml ).
    
        zcl_abapgit_sots_handler=>read_sots(
          iv_pgmid       = 'R3TR'
          iv_object      = ms_item-obj_type
          iv_obj_name    = ms_item-obj_name
          io_i18n_params = mo_i18n_params
          io_xml         = io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_enqu IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE as4user FROM dd25l
          INTO rv_user
          WHERE viewname = ms_item-obj_name
          AND as4local = 'A'
          AND as4vers  = '0000'.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        delete_ddic( 'L' ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_name  TYPE ddobjname,
              ls_dd25v TYPE dd25v,
              ls_extra TYPE ty_extra,
              lt_dd26e TYPE TABLE OF dd26e,
              lt_dd27p TYPE ty_dd27p.
    
        io_xml->read( EXPORTING iv_name = 'DD25V'
                      CHANGING cg_data = ls_dd25v ).
        io_xml->read( EXPORTING iv_name = 'DD26E_TABLE'
                      CHANGING cg_data = lt_dd26e ).
        io_xml->read( EXPORTING iv_name = 'DD27P_TABLE'
                      CHANGING cg_data = lt_dd27p ).
    
        corr_insert( iv_package = iv_package
                     ig_object_class = 'DICT' ).
    
        lv_name = ms_item-obj_name.
    
        CALL FUNCTION 'DDIF_ENQU_PUT'
          EXPORTING
            name              = lv_name
            dd25v_wa          = ls_dd25v
          TABLES
            dd26e_tab         = lt_dd26e
            dd27p_tab         = lt_dd27p
          EXCEPTIONS
            enqu_not_found    = 1
            name_inconsistent = 2
            enqu_inconsistent = 3
            put_failure       = 4
            put_refused       = 5
            OTHERS            = 6.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        " Fields that are not part of dd25v
        io_xml->read( EXPORTING iv_name = 'DD25L_EXTRA'
                      CHANGING  cg_data = ls_extra ).
    
        TRY.
            set_abap_language_version( CHANGING cv_abap_language_version = ls_extra-abap_language_version ).
    
            UPDATE ('DD25L') SET abap_language_version = ls_extra-abap_language_version WHERE viewname = lv_name.
          CATCH cx_sy_dynamic_osql_semantics ##NO_HANDLER.
        ENDTRY.
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_viewname TYPE dd25l-viewname.
    
        SELECT SINGLE viewname FROM dd25l INTO lv_viewname
          WHERE viewname = ms_item-obj_name.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by ZCL_ABAPGIT_OBJECT=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_name  TYPE ddobjname,
              lv_state TYPE ddgotstate,
              ls_dd25v TYPE dd25v,
              ls_extra TYPE ty_extra,
              lt_dd26e TYPE TABLE OF dd26e,
              lt_dd27p TYPE ty_dd27p.
    
        FIELD-SYMBOLS  TYPE any.
    
        lv_name = ms_item-obj_name.
    
        CALL FUNCTION 'DDIF_ENQU_GET'
          EXPORTING
            name          = lv_name
            state         = 'A'
            langu         = mv_language
          IMPORTING
            gotstate      = lv_state
            dd25v_wa      = ls_dd25v
          TABLES
            dd26e_tab     = lt_dd26e
            dd27p_tab     = lt_dd27p
          EXCEPTIONS
            illegal_input = 1
            OTHERS        = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        IF ls_dd25v IS INITIAL OR lv_state <> 'A'.
          RETURN.
        ENDIF.
    
        CLEAR: ls_dd25v-as4user,
               ls_dd25v-as4date,
               ls_dd25v-as4time,
               ls_dd25v-as4local,
               ls_dd25v-as4vers.
    
        ASSIGN COMPONENT 'ACTFLAG' OF STRUCTURE ls_dd25v TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
    
        _clear_dd27p_fields( CHANGING ct_dd27p = lt_dd27p ).
    
        io_xml->add( iv_name = 'DD25V'
                     ig_data = ls_dd25v ).
        io_xml->add( ig_data = lt_dd26e
                     iv_name = 'DD26E_TABLE' ).
        io_xml->add( ig_data = lt_dd27p
                     iv_name = 'DD27P_TABLE' ).
    
        ls_extra-abap_language_version = get_abap_language_version( ).
    
        io_xml->add( iv_name = 'DD25L_EXTRA'
                     ig_data = ls_extra ).
    
      ENDMETHOD.
    
      METHOD _clear_dd27p_fields.
    
        FIELD-SYMBOLS  TYPE dd27p.
    
        LOOP AT ct_dd27p ASSIGNING .
          "taken from table
          CLEAR -headlen.
          CLEAR -scrlen1.
          CLEAR -scrlen2.
          CLEAR -scrlen3.
          CLEAR -intlen.
          CLEAR -outputlen.
          CLEAR -flength.
          CLEAR -ddtext.
          CLEAR -reptext.
          CLEAR -scrtext_s.
          CLEAR -scrtext_m.
          CLEAR -scrtext_l.
          CLEAR -rollname.
          CLEAR -rollnamevi.
          CLEAR -entitytab.
          CLEAR -datatype.
          CLEAR -inttype.
          CLEAR -ddlanguage.
          CLEAR -domname.
          CLEAR -signflag.
        ENDLOOP.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ensc IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lv_spot_name TYPE enhspotcompositename,
              li_spot_ref  TYPE REF TO if_enh_spot_composite,
              lo_spot_ref  TYPE REF TO cl_enh_spot_composite.
    
        lv_spot_name = ms_item-obj_name.
    
        TRY.
            li_spot_ref = cl_enh_factory=>get_enhancement_spot_comp(
              lock     = ''
              run_dark = abap_true
              name     = lv_spot_name ).
    
            lo_spot_ref ?= li_spot_ref.
    
            lo_spot_ref->if_enh_spot_composite~get_change_attributes( IMPORTING changedby = rv_user ).
          CATCH cx_root.
            rv_user = c_user_unknown.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
        DATA: lv_spot_name TYPE enhspotcompositename,
              lv_message   TYPE string,
              lx_root      TYPE REF TO cx_root,
              li_spot_ref  TYPE REF TO if_enh_spot_composite.
    
        lv_spot_name = ms_item-obj_name.
    
        TRY.
            li_spot_ref = cl_enh_factory=>get_enhancement_spot_comp(
              lock     = abap_true
              run_dark = abap_true
              name     = lv_spot_name ).
    
            IF li_spot_ref IS BOUND.
              li_spot_ref->if_enh_object~delete(
                nevertheless_delete = abap_true
                run_dark            = abap_true ).
            ENDIF.
            li_spot_ref->if_enh_object~unlock( ).
          CATCH cx_enh_root INTO lx_root.
            lv_message = `Error occurred while deleting ENSC: `
              && lx_root->get_text( ).
            zcx_abapgit_exception=>raise( lv_message ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_spot_name  TYPE enhspotcompositename,
              lv_message    TYPE string,
              lv_enh_shtext TYPE string,
              lv_enh_spot   TYPE enhspotname,
              lt_enh_spots  TYPE enhspotname_it,
              lt_comp_spots TYPE enhspotname_it,
              lx_root       TYPE REF TO cx_root,
              lv_package    LIKE iv_package,
              li_spot_ref   TYPE REF TO if_enh_spot_composite,
              lo_spot_ref   TYPE REF TO cl_enh_spot_composite.
    
        lv_spot_name = ms_item-obj_name.
    
        io_xml->read( EXPORTING iv_name = 'SHORTTEXT'
                      CHANGING  cg_data = lv_enh_shtext ).
        io_xml->read( EXPORTING iv_name = 'ENH_SPOTS'     "Enhancement spots
                      CHANGING  cg_data = lt_enh_spots ).
        io_xml->read( EXPORTING iv_name = 'COMP_ENH_SPOTS' "Composite enhancement spots
                      CHANGING  cg_data = lt_comp_spots ).
    
        IF zif_abapgit_object~exists( ) = abap_true.
          zif_abapgit_object~delete( iv_package   = iv_package
                                     iv_transport = iv_transport
                                     ii_log       = ii_log ).
        ENDIF.
    
        lv_package = iv_package.
    
        TRY.
            cl_enh_factory=>create_enhancement_spot_comp(
              EXPORTING
                name      = lv_spot_name
                run_dark  = abap_true
              IMPORTING
                composite = li_spot_ref
              CHANGING
                devclass  = lv_package ).
    
            lo_spot_ref ?= li_spot_ref.
    
            lo_spot_ref->if_enh_object_docu~set_shorttext( lv_enh_shtext ).
            "Add subsequent enhancement spots
            LOOP AT lt_enh_spots INTO lv_enh_spot.
              lo_spot_ref->if_enh_spot_composite~add_enh_spot_child( lv_enh_spot ).
            ENDLOOP.
            "Add subsequent composite enhancement spots
            LOOP AT lt_comp_spots INTO lv_enh_spot.
              lo_spot_ref->if_enh_spot_composite~add_composite_child( lv_enh_spot ).
            ENDLOOP.
    
            lo_spot_ref->if_enh_object~save( ).
            lo_spot_ref->if_enh_object~activate( ).
            lo_spot_ref->if_enh_object~unlock( ).
    
            zcl_abapgit_sotr_handler=>create_sotr(
              iv_package = iv_package
              io_xml     = io_xml ).
    
          CATCH cx_enh_root INTO lx_root.
            lv_message = `Error occurred while deserializing ENSC: `
              && lx_root->get_text( ).
            zcx_abapgit_exception=>raise( lv_message ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_spot_name TYPE enhspotcompositename.
    
        lv_spot_name = ms_item-obj_name.
    
        TRY.
            cl_enh_factory=>get_enhancement_spot_comp(
              lock     = ''
              run_dark = abap_true
              name     = lv_spot_name ).
            rv_bool = abap_true.
          CATCH cx_enh_root.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_spot_name  TYPE enhspotcompositename,
              lv_message    TYPE string,
              lv_enh_shtext TYPE string,
              lt_enh_spots  TYPE enhspotname_it,
              lt_comp_spots TYPE enhspotname_it,
              lx_root       TYPE REF TO cx_root,
              li_spot_ref   TYPE REF TO if_enh_spot_composite,
              lo_spot_ref   TYPE REF TO cl_enh_spot_composite.
    
        lv_spot_name = ms_item-obj_name.
    
        TRY.
            li_spot_ref = cl_enh_factory=>get_enhancement_spot_comp(
              lock     = ''
              run_dark = abap_true
              name     = lv_spot_name ).
    
            lo_spot_ref ?= li_spot_ref.
    
            lv_enh_shtext = li_spot_ref->if_enh_object_docu~get_shorttext( ).
            "find subsequent enhancement spots
            lt_enh_spots = lo_spot_ref->if_enh_spot_composite~get_enh_spot_childs( ).
            "find subsequent composite enhancement spots
            lt_comp_spots = lo_spot_ref->if_enh_spot_composite~get_composite_childs( ).
    
            io_xml->add( ig_data = lv_enh_shtext
                         iv_name = 'SHORTTEXT' ).
            io_xml->add( ig_data = lt_enh_spots
                         iv_name = 'ENH_SPOTS' ).         "Enhancement spots
            io_xml->add( ig_data = lt_comp_spots
                         iv_name = 'COMP_ENH_SPOTS' ).    "Composite enhancement spots
    
            zcl_abapgit_sotr_handler=>read_sotr(
              iv_pgmid    = 'R3TR'
              iv_object   = ms_item-obj_type
              iv_obj_name = ms_item-obj_name
              io_i18n_params = mo_i18n_params
              io_xml      = io_xml ).
    
          CATCH cx_enh_root INTO lx_root.
            lv_message = `Error occurred while serializing ENSC: `
              && lx_root->get_text( ).
            zcx_abapgit_exception=>raise( lv_message ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_evtb IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lv_user  TYPE string,
              lx_error TYPE REF TO cx_root.
    
        TRY.
    
            SELECT SINGLE changed_by INTO lv_user
                FROM (c_table_name)
                WHERE evtb_name = ms_item-obj_name AND version = 'I'.
    
            IF lv_user IS INITIAL.
              SELECT SINGLE changed_by INTO lv_user
                FROM (c_table_name)
                WHERE evtb_name = ms_item-obj_name AND version = 'A'.
            ENDIF.
    
            rv_user = lv_user.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_fdt0 IMPLEMENTATION.
    
      METHOD before_xml_deserialize.
    
        DATA lv_application_id TYPE fdt_admn_0000s-application_id.
        DATA lv_timestamp TYPE timestamp.
        DATA lv_transport TYPE string.
        DATA lv_dlvunit TYPE tdevc-dlvunit.
        DATA lo_node_local TYPE REF TO if_ixml_element.
        DATA lo_node_package TYPE REF TO if_ixml_element.
        DATA lo_node_id TYPE REF TO if_ixml_element.
        DATA lo_xml_element TYPE REF TO if_ixml_element.
        DATA lv_count TYPE i.
    
        lo_node_local = co_dom_tree->find_from_name( name      = 'Local'
                                                     namespace = 'FDTNS' ).
    
        IF lo_node_local IS BOUND.
          ev_is_local = lo_node_local->get_value( ).
        ENDIF.
    
        lo_node_package = co_dom_tree->find_from_name(
          name      = 'DevelopmentPackage'
          namespace = 'FDTNS' ).
        IF lo_node_package IS BOUND.
          lo_node_package->set_value( |{ iv_package }| ).
        ENDIF.
    
        lo_node_id = co_dom_tree->find_from_name(
          name      = 'ApplicationId'
          namespace = 'FDTNS' ).
        IF lo_node_id IS BOUND.
          lv_application_id = lo_node_id->get_value( ).
          SELECT COUNT( * ) FROM fdt_admn_0000s INTO lv_count
            WHERE object_type = 'AP'
            AND id = lv_application_id
            AND deleted = ''.
          ev_create = boolc( lv_count = 0 ).
        ENDIF.
    
        " Fill in user/time/system-specific fields
        GET TIME STAMP FIELD lv_timestamp.
        lv_transport = |${ sy-sysid }0000000000000001|.
    
        lo_xml_element = co_dom_tree->get_root_element( ).
    
        IF ev_create = abap_true.
          set_field(
            EXPORTING
              iv_name         = 'CreationUser'
              iv_value        = |{ sy-uname }|
            CHANGING
              co_ixml_element = lo_xml_element ).
    
          set_field(
            EXPORTING
              iv_name         = 'CreationTimestamp'
              iv_value        = |{ lv_timestamp }|
            CHANGING
              co_ixml_element = lo_xml_element ).
        ENDIF.
    
        set_field(
          EXPORTING
            iv_name         = 'ChangeUser'
            iv_value        = |{ sy-uname }|
          CHANGING
            co_ixml_element = lo_xml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'ChangeTimestamp'
            iv_value        = |{ lv_timestamp }|
          CHANGING
            co_ixml_element = lo_xml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'User'
            iv_value        = |{ sy-uname }|
          CHANGING
            co_ixml_element = lo_xml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'Timestamp'
            iv_value        = |{ lv_timestamp }|
          CHANGING
            co_ixml_element = lo_xml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'Trrequest'
            iv_value        = lv_transport
          CHANGING
            co_ixml_element = lo_xml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'Trversion'
            iv_value        = '000001'
          CHANGING
            co_ixml_element = lo_xml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'Trtimestamp'
            iv_value        = |{ lv_timestamp }|
          CHANGING
            co_ixml_element = lo_xml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'Trsysid'
            iv_value        = |{ sy-sysid }|
          CHANGING
            co_ixml_element = lo_xml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'Trclient'
            iv_value        = |{ sy-mandt }|
          CHANGING
            co_ixml_element = lo_xml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'OversId'
            iv_value        = |{ lv_application_id }|
          CHANGING
            co_ixml_element = lo_xml_element ).
    
        SELECT SINGLE dlvunit FROM tdevc INTO lv_dlvunit WHERE devclass = iv_package.
        IF sy-subrc = 0.
          set_field(
            EXPORTING
              iv_name         = 'SoftwareComponent'
              iv_value        = |{ lv_dlvunit }|
            CHANGING
              co_ixml_element = lo_xml_element ).
        ENDIF.
    
        lo_xml_element->set_attribute(
          name  = 'Client'
          value = |{ sy-mandt }| ).
        lo_xml_element->set_attribute(
          name  = 'Date'
          value = |{ sy-datum }| ).
        lo_xml_element->set_attribute(
          name  = 'SAPRelease'
          value = |{ sy-saprl }| ).
        lo_xml_element->set_attribute(
          name  = 'Server'
          value = |{ sy-host }| ).
        lo_xml_element->set_attribute(
          name  = 'SourceExportReqID'
          value = lv_transport ).
        lo_xml_element->set_attribute(
          name  = 'SystemID'
          value = |{ sy-sysid }| ).
        lo_xml_element->set_attribute(
          name  = 'Time'
          value = |{ sy-uzeit }| ).
        lo_xml_element->set_attribute(
          name  = 'User'
          value = |{ sy-uname }| ).
    
      ENDMETHOD.
    
      METHOD check_is_local.
    
        SELECT SINGLE local_object FROM fdt_admn_0000s INTO rv_is_local
          WHERE object_type = 'AP'
          AND name = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD filter_xml_serialize.
    
        DATA lo_components_node TYPE REF TO if_ixml_element.
    
        lo_components_node = co_ixml_element->find_from_name( name      = 'ComponentReleases'
                                                              namespace = 'FDTNS' ).
        IF lo_components_node IS BOUND.
          co_ixml_element->remove_child( lo_components_node ).
        ENDIF.
    
        " Clear user/time/system-specific fields
        set_field(
          EXPORTING
            iv_name         = 'CreationUser'
          CHANGING
            co_ixml_element = co_ixml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'CreationTimestamp'
          CHANGING
            co_ixml_element = co_ixml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'ChangeUser'
          CHANGING
            co_ixml_element = co_ixml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'ChangeTimestamp'
          CHANGING
            co_ixml_element = co_ixml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'User'
          CHANGING
            co_ixml_element = co_ixml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'Timestamp'
          CHANGING
            co_ixml_element = co_ixml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'Trrequest'
          CHANGING
            co_ixml_element = co_ixml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'Trversion'
          CHANGING
            co_ixml_element = co_ixml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'Trtimestamp'
          CHANGING
            co_ixml_element = co_ixml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'Trsysid'
          CHANGING
            co_ixml_element = co_ixml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'Trclient'
          CHANGING
            co_ixml_element = co_ixml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'OversId'
          CHANGING
            co_ixml_element = co_ixml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'SoftwareComponent'
          CHANGING
            co_ixml_element = co_ixml_element ).
    
        set_field(
          EXPORTING
            iv_name         = 'DevelopmentPackage'
          CHANGING
            co_ixml_element = co_ixml_element ).
    
        " Clear attributes of root FDTNS:Fdt node
        co_ixml_element->set_attribute(
          name  = 'Client'
          value = '' ).
        co_ixml_element->set_attribute(
          name  = 'Date'
          value = '' ).
        co_ixml_element->set_attribute(
          name  = 'SAPRelease'
          value = '' ).
        co_ixml_element->set_attribute(
          name  = 'Server'
          value = '' ).
        co_ixml_element->set_attribute(
          name  = 'SourceExportReqID'
          value = '' ).
        co_ixml_element->set_attribute(
          name  = 'SystemID'
          value = '' ).
        co_ixml_element->set_attribute(
          name  = 'Time'
          value = '' ).
        co_ixml_element->set_attribute(
          name  = 'User'
          value = '' ).
    
      ENDMETHOD.
    
      METHOD get_application_id.
    
        SELECT SINGLE application_id FROM fdt_admn_0000s INTO rv_application_id
          WHERE object_type = 'AP'
          AND name = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD set_field.
    
        DATA:
          lo_node_collection TYPE REF TO if_ixml_node_collection,
          lo_node            TYPE REF TO if_ixml_node,
          lv_index           TYPE i.
    
        lo_node_collection = co_ixml_element->get_elements_by_tag_name(
          namespace = 'FDTNS'
          name      = iv_name ).
    
        lv_index = 0.
        WHILE lv_index < lo_node_collection->get_length( ).
          lo_node = lo_node_collection->get_item( lv_index ).
          lo_node->set_value( iv_value ).
          lv_index = lv_index + 1.
        ENDWHILE.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA lv_ch_user TYPE fdt_admn_0000s-ch_user.
    
        SELECT SINGLE ch_user FROM fdt_admn_0000s INTO lv_ch_user
          WHERE object_type = 'AP'
          AND name = ms_item-obj_name.
    
        rv_user = lv_ch_user.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA lv_is_local TYPE abap_bool.
        DATA lt_application_id TYPE TABLE OF fdt_admn_0000s-application_id.
        DATA ls_object_category_sel TYPE if_fdt_query=>s_object_category_sel.
        DATA lv_failure TYPE abap_bool.
        DATA lx_fdt_input TYPE REF TO cx_fdt_input.
    
        lv_is_local = check_is_local( ).
    
        SELECT application_id FROM fdt_admn_0000s INTO TABLE lt_application_id
          WHERE object_type = 'AP'
          AND name = ms_item-obj_name
          ORDER BY application_id.
    
        ls_object_category_sel-system_objects = 'X'.
    
        TRY.
            IF lv_is_local = abap_true.
    
              cl_fdt_delete_handling=>mark_for_delete_via_job(
                EXPORTING
                  is_object_category_sel     = ls_object_category_sel
                  ita_application_id         = lt_application_id
                  iv_background              = abap_true
                  iv_local_option            = '1'
                  iv_appl_transported_option = '2'
                  iv_obj_transported_option  = '2'
                IMPORTING
                  ev_failure                 = lv_failure ).
              IF lv_failure IS INITIAL.
                cl_fdt_delete_handling=>delete_logical_via_job(
                  EXPORTING
                    is_object_category_sel     = ls_object_category_sel
                    ita_application_id         = lt_application_id
                    iv_retention_time          = 0
                    iv_background              = abap_true
                    iv_local_option            = '1'
                    iv_appl_transported_option = '2'
                    iv_obj_transported_option  = '2'
                  IMPORTING
                    ev_failure                 = lv_failure ).
                IF lv_failure IS INITIAL.
                  cl_fdt_delete_handling=>delete_physical_via_job(
                    EXPORTING
                      is_object_category_sel     = ls_object_category_sel
                      ita_application_id         = lt_application_id
                      iv_retention_time          = 0
                      iv_background              = abap_true
                      iv_local_option            = '1'
                      iv_appl_transported_option = '2'
                    IMPORTING
                      ev_failure                 = lv_failure ).
                ENDIF.
              ENDIF.
    
            ELSE.
    
              tadir_insert( iv_package ).
    
              corr_insert( iv_package ).
    
              cl_fdt_delete_handling=>mark_for_delete_via_job(
                EXPORTING
                  is_object_category_sel     = ls_object_category_sel
                  ita_application_id         = lt_application_id
                  iv_background              = abap_true
                  iv_local_option            = '2'
                  iv_appl_transported_option = '1'
                  iv_obj_transported_option  = '1'
                IMPORTING
                  ev_failure                 = lv_failure ).
              IF lv_failure IS INITIAL.
                cl_fdt_delete_handling=>delete_logical_via_job(
                  EXPORTING
                    is_object_category_sel     = ls_object_category_sel
                    ita_application_id         = lt_application_id
                    iv_retention_time          = 0
                    iv_background              = abap_true
                    iv_local_option            = '2'
                    iv_appl_transported_option = '1'
                    iv_obj_transported_option  = '1'
                  IMPORTING
                    ev_failure                 = lv_failure ).
                IF lv_failure IS INITIAL.
                  cl_fdt_delete_handling=>delete_physical_via_job(
                    EXPORTING
                      is_object_category_sel     = ls_object_category_sel
                      ita_application_id         = lt_application_id
                      iv_retention_time          = 0
                      iv_background              = abap_true
                      iv_local_option            = '2'
                      iv_appl_transported_option = '1'
                    IMPORTING
                      ev_failure                 = lv_failure ).
                ENDIF.
              ENDIF.
    
            ENDIF.
    
            IF lv_failure = abap_true.
              zcx_abapgit_exception=>raise( |Error deleting { ms_item-obj_type } { ms_item-obj_name }| ).
            ENDIF.
    
          CATCH cx_fdt_input INTO lx_fdt_input.
            zcx_abapgit_exception=>raise_with_text( lx_fdt_input ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA lo_dexc TYPE REF TO if_fdt_data_exchange.
        DATA lx_fdt_input TYPE REF TO cx_fdt_input.
        DATA lo_dom_tree TYPE REF TO if_ixml_document.
        DATA lv_transportable_package TYPE abap_bool.
        DATA lv_is_local TYPE abap_bool.
        DATA lt_message TYPE if_fdt_types=>t_message.
        DATA lv_create TYPE abap_bool.
    
        FIELD-SYMBOLS  TYPE if_fdt_types=>s_message.
    
        lo_dom_tree = io_xml->get_raw( ).
    
        before_xml_deserialize(
          EXPORTING
            iv_package  = iv_package
          IMPORTING
            ev_create   = lv_create
            ev_is_local = lv_is_local
          CHANGING
            co_dom_tree = lo_dom_tree ).
    
        lv_transportable_package = zcl_abapgit_factory=>get_sap_package( iv_package )->are_changes_recorded_in_tr_req( ).
    
        IF lv_transportable_package = abap_true AND lv_is_local = abap_true.
          zcx_abapgit_exception=>raise( 'Local applications can only be imported into a local package' ).
        ELSEIF lv_transportable_package = abap_false AND lv_is_local = abap_false.
          zcx_abapgit_exception=>raise( 'Transportable application can only be imported into transportable package' ).
        ENDIF.
    
        lo_dexc = cl_fdt_factory=>if_fdt_factory~get_instance( )->get_data_exchange( ).
    
        TRY.
    
            IF lv_is_local = abap_true. "Local Object
    
              lo_dexc->import_xml(
                EXPORTING
                  io_dom_tree = lo_dom_tree
                  iv_create   = lv_create
                  iv_activate = abap_true
                  iv_simulate = abap_false
                IMPORTING
                  et_message  = lt_message ).
    
            ELSE. "Transportable Object
    
              tadir_insert( iv_package ).
    
              corr_insert( iv_package ).
    
              lo_dexc->import_xml(
                EXPORTING
                  io_dom_tree            = lo_dom_tree
                  iv_create              = lv_create
                  iv_activate            = abap_true
                  iv_simulate            = abap_false
                  iv_workbench_trrequest = iv_transport
                IMPORTING
                  et_message             = lt_message ).
    
            ENDIF.
    
            LOOP AT lt_message ASSIGNING .
              ii_log->add(
                iv_msg  = -text
                iv_type = -msgty ).
            ENDLOOP.
    
          CATCH cx_fdt_input INTO lx_fdt_input.
            zcx_abapgit_exception=>raise_with_text( lx_fdt_input ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA lv_count TYPE i.
    
        SELECT COUNT( * ) FROM fdt_admn_0000s INTO lv_count
          WHERE object_type = 'AP'
          AND name = ms_item-obj_name
          AND deleted = ''.
    
        rv_bool = boolc( lv_count > 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
    
        DATA lv_application_id TYPE fdt_admn_0000s-application_id.
        DATA lx_fdt_input TYPE REF TO cx_fdt_input.
        DATA lo_instance TYPE REF TO if_fdt_admin_data.
        DATA lt_version TYPE if_fdt_admin_data=>ts_version.
        DATA lv_index TYPE sy-tabix.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_version.
    
        lv_application_id = get_application_id( ).
        IF lv_application_id IS INITIAL.
          rv_active = abap_false.
          RETURN.
        ENDIF.
    
        TRY.
            cl_fdt_factory=>get_instance_generic(
              EXPORTING
                iv_id       = lv_application_id
              IMPORTING
                eo_instance = lo_instance ).
    
          CATCH cx_fdt_input INTO lx_fdt_input.
            zcx_abapgit_exception=>raise_with_text( lx_fdt_input ).
        ENDTRY.
    
        lo_instance->get_versions( IMPORTING ets_version = lt_version ).
        lv_index = lines( lt_version ).
        READ TABLE lt_version ASSIGNING  INDEX lv_index.
    
        rv_active = boolc( -state = 'A' ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA lv_application_id TYPE string.
    
        lv_application_id = get_application_id( ).
    
        rv_is_locked = exists_a_lock_entry_for(
          iv_lock_object = 'FDT_ENQUEUE_ID'
          iv_argument    = |$ST{ lv_application_id }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA lv_application_id TYPE fdt_admn_0000s-application_id.
        DATA lx_root TYPE REF TO cx_root.
        DATA lo_fdt_wd TYPE REF TO if_fdt_wd_factory.
    
        lv_application_id = get_application_id( ).
    
        IF lv_application_id IS NOT INITIAL.
          TRY.
              lo_fdt_wd = cl_fdt_wd_factory=>if_fdt_wd_factory~get_instance( ).
              lo_fdt_wd->get_ui_execution( )->execute_workbench( iv_id = lv_application_id ).
            CATCH cx_root INTO lx_root.
              zcx_abapgit_exception=>raise_with_text( lx_root ).
          ENDTRY.
        ELSE.
          zcx_abapgit_exception=>raise( 'Could not open BRF+ Workbench' ).
        ENDIF.
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA lo_dexc TYPE REF TO if_fdt_data_exchange.
        DATA lv_application_id TYPE fdt_admn_0000s-application_id.
        DATA lx_root TYPE REF TO cx_root.
        DATA lv_xml_fdt0_application TYPE string.
        DATA lo_xml_document TYPE REF TO if_ixml_document.
        DATA lo_xml_element TYPE REF TO if_ixml_element.
    
        lv_application_id = get_application_id( ).
    
        lo_dexc = cl_fdt_factory=>if_fdt_factory~get_instance( )->get_data_exchange( ).
    
        TRY.
            lo_dexc->export_xml_application(
              EXPORTING
                iv_application_id = lv_application_id
                iv_schema         = if_fdt_data_exchange=>gc_xml_schema_type_external
                iv_xml_version    = if_fdt_data_exchange=>gc_xml_version
              IMPORTING
                ev_string         = lv_xml_fdt0_application ).
    
            IF lv_xml_fdt0_application IS INITIAL.
              zcx_abapgit_exception=>raise( 'FDT0, empty application' ).
            ENDIF.
    
            lo_xml_document = cl_ixml_80_20=>parse_to_document( stream_string = lv_xml_fdt0_application ).
            lo_xml_element = lo_xml_document->get_root_element( ).
    
            filter_xml_serialize( CHANGING co_ixml_element = lo_xml_element ).
    
            io_xml->set_raw( lo_xml_element ).
    
          CATCH cx_fdt_input INTO lx_root.
            zcx_abapgit_exception=>raise_with_text( lx_root ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_form IMPLEMENTATION.
    
      METHOD build_extra_from_header.
    
        DATA lv_tdspras TYPE laiso.
    
        lv_tdspras = zcl_abapgit_convert=>conversion_exit_isola_output( is_header-tdspras ).
    
        rv_result = c_objectname_tdlines && '_' && lv_tdspras.
    
      ENDMETHOD.
    
      METHOD build_extra_from_header_old.
        rv_result = c_objectname_tdlines && '_' && is_header-tdspras.
      ENDMETHOD.
    
      METHOD compress_lines.
    
        DATA lv_string TYPE string.
        DATA li_xml TYPE REF TO zif_abapgit_xml_output.
    
        CREATE OBJECT li_xml TYPE zcl_abapgit_xml_output.
        li_xml->add( iv_name = c_objectname_tdlines
                     ig_data = it_lines ).
        lv_string = li_xml->render( ).
        IF lv_string IS NOT INITIAL.
          mo_files->add_string( iv_extra  =
                        build_extra_from_header( is_form_data-form_header )
                                iv_ext    = c_extension_xml
                                iv_string = lv_string ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_form_name = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD extract_tdlines.
    
        DATA lv_string TYPE string.
        DATA li_xml TYPE REF TO zif_abapgit_xml_input.
    
        TRY.
            lv_string = mo_files->read_string( iv_extra =
                                       build_extra_from_header( is_form_data-form_header )
                                               iv_ext   = c_extension_xml ).
          CATCH zcx_abapgit_exception.
    
            lv_string = mo_files->read_string( iv_extra =
                                   build_extra_from_header_old( is_form_data-form_header )
                                               iv_ext   = c_extension_xml ).
    
        ENDTRY.
    
        CREATE OBJECT li_xml TYPE zcl_abapgit_xml_input EXPORTING iv_xml = lv_string.
        li_xml->read( EXPORTING iv_name = c_objectname_tdlines
                      CHANGING  cg_data = rt_lines ).
    
      ENDMETHOD.
    
      METHOD find_form.
    
        DATA: lv_text_name TYPE thead-tdname.
    
        lv_text_name = iv_object_name.
    
        CALL FUNCTION 'SELECT_TEXT'
          EXPORTING
            database_only = abap_true
            id            = 'TXT'
            language      = '*'
            name          = lv_text_name
            object        = c_objectname_form
          TABLES
            selections    = rt_text_header
          EXCEPTIONS
            OTHERS        = 1 ##FM_SUBRC_OK.  "#EC CI_SUBRC
    
      ENDMETHOD.
    
      METHOD get_last_changes.
    
        DATA: lv_form_name         TYPE thead-tdform.
    
        CLEAR rs_last_changed.
    
        lv_form_name = iv_form_name.
    
        CALL FUNCTION 'READ_FORM'
          EXPORTING
            form             = lv_form_name
            read_only_header = abap_true
          IMPORTING
            form_header      = rs_last_changed.
    
      ENDMETHOD.
    
      METHOD order_check_and_insert.
    
        DATA: lv_order TYPE e071k-trkorr.
    
        CALL FUNCTION 'SAPSCRIPT_ORDER_CHECK'
          EXPORTING
            objecttype           = ms_item-obj_type
            form                 = mv_form_name
          EXCEPTIONS
            invalid_input        = 1
            object_locked        = 2
            object_not_available = 3
            OTHERS               = 4.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CALL FUNCTION 'SAPSCRIPT_ORDER_INSERT'
          EXPORTING
            objecttype     = ms_item-obj_type
            form           = mv_form_name
            masterlang     = mv_language
          CHANGING
            order          = lv_order
          EXCEPTIONS
            invalid_input  = 1
            order_canceled = 2
            OTHERS         = 3.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: ls_last_changed TYPE ty_s_form_header.
    
        ls_last_changed = get_last_changes( ms_item-obj_name ).
    
        IF ls_last_changed-tdluser IS NOT INITIAL.
          rv_user = ls_last_changed-tdluser.
        ELSE.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        CALL FUNCTION 'DELETE_FORM'
          EXPORTING
            form     = mv_form_name
            language = '*'.
    
        order_check_and_insert( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lt_form_data            TYPE ty_t_form_data.
        DATA: lt_lines TYPE ty_t_lines.
        FIELD-SYMBOLS:  TYPE LINE OF ty_t_form_data.
    
        io_xml->read( EXPORTING iv_name = c_objectname_form
                      CHANGING  cg_data = lt_form_data ).
    
        LOOP AT lt_form_data ASSIGNING .
    
          lt_lines = extract_tdlines(  ).
    
          _save_form( EXPORTING it_lines     = lt_lines
                      CHANGING  cs_form_data =  ).
    
        ENDLOOP.
    
        CALL FUNCTION 'SAPSCRIPT_DELETE_LOAD'
          EXPORTING
            delete = abap_true
            form   = '*'
            write  = space.
    
        tadir_insert( iv_package ).
    
        order_check_and_insert( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA lv_lang TYPE sy-langu.
    
    * this will try to read the FORM in language EN
    * if it exists in other language, then "found" will be set to abap_false
    * so check the "olanguage" to see if the FORM exists
        CALL FUNCTION 'READ_FORM'
          EXPORTING
            form             = mv_form_name
            read_only_header = abap_true
          IMPORTING
            olanguage        = lv_lang.
    
        rv_bool = boolc( lv_lang IS NOT INITIAL ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lv_object TYPE seqg3-garg.
    
        " example lock entry
        "'001FORM      ZTEST_SAPSCRIPT                                                       TXT'
        lv_object = |{ sy-mandt }{ ms_item-obj_type }      { ms_item-obj_name }|.
        OVERLAY lv_object WITH '                                                                                   '.
        lv_object = lv_object && '*'.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESSFORM'
                                                iv_argument    = lv_object ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: lt_bdcdata TYPE TABLE OF bdcdata.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_bdcdata.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -program  = 'SAPMSSCF'.
        -dynpro   = '1102'.
        -dynbegin = abap_true.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'BDC_OKCODE'.
        -fval = '=SHOW'.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'RSSCF-TDFORM'.
        -fval = ms_item-obj_name.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'SE71'
          it_bdcdata = lt_bdcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lt_form_data              TYPE ty_t_form_data.
        DATA: ls_form_data              TYPE ty_s_form_data.
        DATA: lt_text_header            TYPE ty_t_text_header.
        DATA: lt_lines                  TYPE ty_t_lines.
        DATA: lv_form_found             TYPE abap_bool.
        FIELD-SYMBOLS:  LIKE LINE OF lt_text_header.
    
        lt_text_header = find_form( ms_item-obj_name ).
    
        LOOP AT lt_text_header ASSIGNING .
          CLEAR lt_lines.
          CLEAR ls_form_data.
    
          _read_form( EXPORTING is_text_header = 
                      IMPORTING ev_form_found = lv_form_found
                                es_form_data  = ls_form_data
                                et_lines      = lt_lines ).
    
          IF lv_form_found = abap_true.
    
            _clear_changed_fields( CHANGING cs_form_data = ls_form_data ).
    
            compress_lines( is_form_data = ls_form_data
                            it_lines     = lt_lines ).
    
            INSERT ls_form_data INTO TABLE lt_form_data.
    
          ENDIF.
    
        ENDLOOP.
    
        IF lt_form_data IS NOT INITIAL.
    
          io_xml->add( iv_name = c_objectname_form
                       ig_data = lt_form_data ).
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD _clear_changed_fields.
    
        CLEAR: cs_form_data-form_header-tdfuser,
               cs_form_data-form_header-tdfdate,
               cs_form_data-form_header-tdftime,
               cs_form_data-form_header-tdfreles,
               cs_form_data-form_header-tdluser,
               cs_form_data-form_header-tdldate,
               cs_form_data-form_header-tdltime,
               cs_form_data-form_header-tdlreles.
        CLEAR: cs_form_data-text_header-tdfuser,
               cs_form_data-text_header-tdfdate,
               cs_form_data-text_header-tdftime,
               cs_form_data-text_header-tdfreles,
               cs_form_data-text_header-tdluser,
               cs_form_data-text_header-tdldate,
               cs_form_data-text_header-tdltime,
               cs_form_data-text_header-tdlreles.
    
      ENDMETHOD.
    
      METHOD _read_form.
    
        CLEAR es_form_data.
    
        CALL FUNCTION 'READ_FORM'
          EXPORTING
            form         = is_text_header-tdform
            language     = is_text_header-tdspras
            status       = ' '
          IMPORTING
            form_header  = es_form_data-form_header
            found        = ev_form_found
            header       = es_form_data-text_header
            olanguage    = es_form_data-orig_language
          TABLES
            form_lines   = et_lines
            pages        = es_form_data-pages
            page_windows = es_form_data-page_windows
            paragraphs   = es_form_data-paragraphs
            strings      = es_form_data-strings
            tabs         = es_form_data-tabs
            windows      = es_form_data-windows.
    
        _sort_tdlines_by_windows( CHANGING ct_form_windows  = es_form_data-windows
                                           ct_lines         = et_lines ).
    
        es_form_data-form_header-tdversion = '00001'.
        es_form_data-text_header-tdversion = '00001'.
    
      ENDMETHOD.
    
      METHOD _save_form.
    
        CALL FUNCTION 'SAVE_FORM'
          EXPORTING
            form_header  = cs_form_data-form_header
          TABLES
            form_lines   = it_lines
            pages        = cs_form_data-pages
            page_windows = cs_form_data-page_windows
            paragraphs   = cs_form_data-paragraphs
            strings      = cs_form_data-strings
            tabs         = cs_form_data-tabs
            windows      = cs_form_data-windows.
    
        CALL FUNCTION 'SAPSCRIPT_CHANGE_OLANGUAGE'
          EXPORTING
            forced    = abap_true
            name      = cs_form_data-text_header-tdname
            object    = cs_form_data-text_header-tdobject
            olanguage = cs_form_data-orig_language
          EXCEPTIONS
            OTHERS    = 1 ##FM_SUBRC_OK.                                                   "#EC CI_SUBRC
    
      ENDMETHOD.
    
      METHOD _sort_tdlines_by_windows.
        DATA lt_lines        TYPE ty_t_lines.
        DATA ls_lines        LIKE LINE OF lt_lines.
        DATA ls_form_windows LIKE LINE OF ct_form_windows.
        DATA lv_elt_windows  TYPE tdformat VALUE '/W'.
        DATA lv_firstloop    TYPE abap_bool.
    
        lt_lines = ct_lines.
        CLEAR ct_lines.
    
        SORT ct_form_windows BY tdwindow.
    
        LOOP AT ct_form_windows INTO ls_form_windows.
          lv_firstloop = abap_true.
          READ TABLE lt_lines INTO ls_lines WITH KEY tdformat = lv_elt_windows
                                                     tdline   = ls_form_windows-tdwindow.
          IF sy-subrc <> 0.
            CONTINUE. " current loop
          ENDIF.
          LOOP AT lt_lines INTO ls_lines FROM sy-tabix.
            IF lv_firstloop = abap_false AND
               ls_lines-tdformat = lv_elt_windows.
              EXIT.
            ENDIF.
            APPEND ls_lines TO ct_lines.
            lv_firstloop = abap_false.
          ENDLOOP.
        ENDLOOP.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ftgl IMPLEMENTATION.
    
      METHOD clear_field.
    
        FIELD-SYMBOLS:  TYPE data.
    
        ASSIGN
          COMPONENT iv_fieldname
          OF STRUCTURE cg_header
          TO .
        ASSERT sy-subrc = 0.
    
        CLEAR: .
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_toggle_id = ms_item-obj_name.
    
        TRY.
            CREATE DATA mr_toggle TYPE ('FTGL_S_WB_FEATURE_TOGGLE').
          CATCH cx_root.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE changedby FROM ('FTGL_ID') INTO rv_user
          WHERE feature_id = ms_item-obj_name AND version = 'A'.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA:
          lv_return_code TYPE i.
    
        CALL METHOD ('CL_FEATURE_TOGGLE_OBJECT')=>delete
          EXPORTING
            iv_toggle_id = mv_toggle_id
          RECEIVING
            rv_rc        = lv_return_code.
    
        IF lv_return_code <> 0.
          zcx_abapgit_exception=>raise( |Cannot delete feature toggle { mv_toggle_id }. |
                                     && |Error { sy-subrc } from cl_feature_toggle_object=>delete| ).
        ENDIF.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA:
          lo_toggle TYPE REF TO object,
          lx_error  TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:  TYPE data.
    
        ASSIGN mr_toggle->* TO .
        ASSERT sy-subrc = 0.
    
        io_xml->read(
          EXPORTING
            iv_name = 'FTGL'
          CHANGING
            cg_data =  ).
    
        TRY.
            CALL METHOD ('CL_FEATURE_TOGGLE_OBJECT')=>create_toggle_by_content
              EXPORTING
                is_content = 
              RECEIVING
                ro_toggle  = lo_toggle.
    
            CALL METHOD lo_toggle->('SAVE').
    
            tadir_insert( iv_package ).
            corr_insert( iv_package ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        CALL METHOD ('CL_FEATURE_TOGGLE')=>is_defined
          EXPORTING
            iv_toggle_id = mv_toggle_id
          RECEIVING
            rc_exists    = rv_bool.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'E_FTGL'
                                                iv_argument    = |{ mv_toggle_id }*| ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          lx_error  TYPE REF TO cx_root,
          lo_toggle TYPE REF TO object.
    
        FIELD-SYMBOLS:  TYPE data.
    
        ASSIGN mr_toggle->* TO .
        ASSERT sy-subrc = 0.
    
        TRY.
            CALL METHOD ('CL_FEATURE_TOGGLE_OBJECT')=>create_toggle_by_id
              EXPORTING
                iv_toggle_id = mv_toggle_id
              RECEIVING
                ro_toggle    = lo_toggle.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        CALL METHOD lo_toggle->('GET_CONTENT')
          RECEIVING
            rs_content = .
    
        clear_field( EXPORTING iv_fieldname = 'HEADER-OWNER'        CHANGING cg_header =  ).
        clear_field( EXPORTING iv_fieldname = 'HEADER-CREATED_DATE' CHANGING cg_header =  ).
        clear_field( EXPORTING iv_fieldname = 'HEADER-CREATED_TIME' CHANGING cg_header =  ).
        clear_field( EXPORTING iv_fieldname = 'HEADER-CHANGEDBY   ' CHANGING cg_header =  ).
        clear_field( EXPORTING iv_fieldname = 'HEADER-CHANGED_DATE' CHANGING cg_header =  ).
        clear_field( EXPORTING iv_fieldname = 'HEADER-CHANGED_TIME' CHANGING cg_header =  ).
    
        io_xml->add(
            iv_name = 'FTGL'
            ig_data =  ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_fugr IMPLEMENTATION.
    
      METHOD check_rfc_parameters.
    
    * function module RS_FUNCTIONMODULE_INSERT does the same deep down, but the right error
    * message is not returned to the user, this is a workaround to give a proper error
    * message to the user
    
        DATA: ls_parameter TYPE rsfbpara,
              lt_fupa      TYPE rsfb_param,
              ls_fupa      LIKE LINE OF lt_fupa.
    
        IF is_function-remote_call = 'R'.
          cl_fb_parameter_conversion=>convert_parameter_old_to_fupa(
            EXPORTING
              functionname = is_function-funcname
              import       = is_function-import
              export       = is_function-export
              change       = is_function-changing
              tables       = is_function-tables
              except       = is_function-exception
            IMPORTING
              fupararef    = lt_fupa ).
    
          LOOP AT lt_fupa INTO ls_fupa WHERE paramtype = 'I' OR paramtype = 'E' OR paramtype = 'C' OR paramtype = 'T'.
            cl_fb_parameter_conversion=>convert_intern_to_extern(
              EXPORTING
                parameter_db  = ls_fupa
              IMPORTING
                parameter_vis = ls_parameter ).
    
            CALL FUNCTION 'RS_FB_CHECK_PARAMETER_REMOTE'
              EXPORTING
                parameter             = ls_parameter
                basxml_enabled        = is_function-remote_basxml
              EXCEPTIONS
                not_remote_compatible = 1
                OTHERS                = 2.
            IF sy-subrc <> 0.
              zcx_abapgit_exception=>raise_t100( ).
            ENDIF.
          ENDLOOP.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD deserialize_functions.
    
        DATA: lv_include   TYPE rs38l-include,
              lv_area      TYPE rs38l-area,
              lv_group     TYPE rs38l-area,
              lv_namespace TYPE rs38l-namespace,
              lt_source    TYPE TABLE OF abaptxt255,
              lv_msg       TYPE string,
              lx_error     TYPE REF TO zcx_abapgit_exception.
    
        FIELD-SYMBOLS:  LIKE LINE OF it_functions.
    
        LOOP AT it_functions ASSIGNING .
    
          lt_source = mo_files->read_abap( iv_extra = -funcname ).
    
          lv_area = ms_item-obj_name.
    
          CALL FUNCTION 'FUNCTION_INCLUDE_SPLIT'
            EXPORTING
              complete_area = lv_area
            IMPORTING
              namespace     = lv_namespace
              group         = lv_group
            EXCEPTIONS
              OTHERS        = 12.
    
          IF sy-subrc <> 0.
            MESSAGE ID sy-msgid TYPE 'S' NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 INTO lv_msg.
            ii_log->add_error( iv_msg  = |Function module { -funcname }: { lv_msg }|
                               is_item = ms_item ).
            CONTINUE. "with next function module
          ENDIF.
    
          IF zcl_abapgit_factory=>get_function_module( )->function_exists( -funcname ) = abap_true.
    * delete the function module to make sure the parameters are updated
    * haven't found a nice way to update the parameters
            CALL FUNCTION 'FUNCTION_DELETE'
              EXPORTING
                funcname                 = -funcname
                suppress_success_message = abap_true
              EXCEPTIONS
                error_message            = 1
                OTHERS                   = 2.
            IF sy-subrc <> 0.
              MESSAGE ID sy-msgid TYPE 'S' NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 INTO lv_msg.
              ii_log->add_error( iv_msg  = |Function module { -funcname }: { lv_msg }|
                                 is_item = ms_item ).
              CONTINUE. "with next function module
            ENDIF.
          ENDIF.
    
          TRY.
              check_rfc_parameters(  ).
            CATCH zcx_abapgit_exception INTO lx_error.
              ii_log->add_error(
                iv_msg  = |Function module { -funcname }: { lx_error->get_text( ) }|
                is_item = ms_item ).
              CONTINUE. "with next function module
          ENDTRY.
    
          TRY.
              CALL FUNCTION 'RS_FUNCTIONMODULE_INSERT'
                EXPORTING
                  funcname                = -funcname
                  function_pool           = lv_group
                  interface_global        = -global_flag
                  remote_call             = -remote_call
                  short_text              = -short_text
                  update_task             = -update_task
                  exception_class         = -exception_classes
                  namespace               = lv_namespace
                  remote_basxml_supported = -remote_basxml
                  corrnum                 = iv_transport
                  rfcscope                = -rfcscope " not on lower releases
                  rfcvers                 = -rfcvers " not on lower releases
                IMPORTING
                  function_include        = lv_include
                TABLES
                  import_parameter        = -import
                  export_parameter        = -export
                  tables_parameter        = -tables
                  changing_parameter      = -changing
                  exception_list          = -exception
                  parameter_docu          = -documentation
                EXCEPTIONS
                  double_task             = 1
                  error_message           = 2
                  function_already_exists = 3
                  invalid_function_pool   = 4
                  invalid_name            = 5
                  too_many_functions      = 6
                  no_modify_permission    = 7
                  no_show_permission      = 8
                  enqueue_system_failure  = 9
                  canceled_in_corr        = 10
                  OTHERS                  = 11.
            CATCH cx_sy_dyn_call_param_not_found.
              CALL FUNCTION 'RS_FUNCTIONMODULE_INSERT'
                EXPORTING
                  funcname                = -funcname
                  function_pool           = lv_group
                  interface_global        = -global_flag
                  remote_call             = -remote_call
                  short_text              = -short_text
                  update_task             = -update_task
                  exception_class         = -exception_classes
                  namespace               = lv_namespace
                  remote_basxml_supported = -remote_basxml
                  corrnum                 = iv_transport
                IMPORTING
                  function_include        = lv_include
                TABLES
                  import_parameter        = -import
                  export_parameter        = -export
                  tables_parameter        = -tables
                  changing_parameter      = -changing
                  exception_list          = -exception
                  parameter_docu          = -documentation
                EXCEPTIONS
                  double_task             = 1
                  error_message           = 2
                  function_already_exists = 3
                  invalid_function_pool   = 4
                  invalid_name            = 5
                  too_many_functions      = 6
                  no_modify_permission    = 7
                  no_show_permission      = 8
                  enqueue_system_failure  = 9
                  canceled_in_corr        = 10
                  OTHERS                  = 11.
          ENDTRY.
          IF sy-subrc <> 0.
            MESSAGE ID sy-msgid TYPE 'S' NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 INTO lv_msg.
            ii_log->add_error( iv_msg  = |Function module { -funcname }: { lv_msg }|
                               is_item = ms_item ).
            CONTINUE.  "with next function module
          ENDIF.
    
          zcl_abapgit_factory=>get_sap_report( )->insert_report(
            iv_name    = lv_include
            iv_package = iv_package
            iv_version = iv_version
            it_source  = lt_source ).
    
          ii_log->add_success( iv_msg  = |Function module { -funcname } imported|
                               is_item = ms_item ).
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD deserialize_function_docs.
    
        FIELD-SYMBOLS  LIKE LINE OF it_functions.
    
        zcl_abapgit_factory=>get_longtexts( )->deserialize(
          iv_longtext_id   = c_longtext_id_prog
          iv_object_name   = iv_prog_name
          ii_xml           = ii_xml
          iv_main_language = mv_language ).
    
        LOOP AT it_functions ASSIGNING .
          zcl_abapgit_factory=>get_longtexts( )->deserialize(
            iv_longtext_name = |LONGTEXTS_{ -funcname }|
            iv_longtext_id   = c_longtext_id_func
            iv_object_name   = -funcname
            ii_xml           = ii_xml
            iv_main_language = mv_language ).
          zcl_abapgit_factory=>get_longtexts( )->deserialize(
            iv_longtext_name = |LONGTEXTS_{ -funcname }___EXC|
            iv_longtext_id   = c_longtext_id_func_exc
            iv_object_name   = -funcname
            ii_xml           = ii_xml
            iv_main_language = mv_language ).
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD deserialize_includes.
    
        DATA: lo_xml       TYPE REF TO zif_abapgit_xml_input,
              ls_progdir   TYPE zif_abapgit_sap_report=>ty_progdir,
              lt_includes  TYPE ty_sobj_name_tt,
              lt_tpool     TYPE textpool_table,
              lt_tpool_ext TYPE zif_abapgit_lang_definitions=>ty_tpool_tt,
              lt_source    TYPE TABLE OF abaptxt255,
              lx_exc       TYPE REF TO zcx_abapgit_exception.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_includes.
    
        tadir_insert( iv_package ).
    
        ii_xml->read( EXPORTING iv_name = 'INCLUDES'
                      CHANGING  cg_data = lt_includes ).
    
        LOOP AT lt_includes ASSIGNING .
    
          "ignore simple transformation includes (as long as they remain in existing repositories)
          IF strlen(  ) = 33 AND +30(3) = 'XTI'.
            ii_log->add_warning( iv_msg  = |Simple Transformation include {  } ignored|
                                 is_item = ms_item ).
            CONTINUE.
          ENDIF.
    
          TRY.
              lt_source = mo_files->read_abap( iv_extra =  ).
    
              lo_xml = mo_files->read_xml(  ).
    
              lo_xml->read( EXPORTING iv_name = 'PROGDIR'
                            CHANGING  cg_data = ls_progdir ).
    
              set_abap_language_version( CHANGING cv_abap_language_version = ls_progdir-uccheck ).
    
              lo_xml->read( EXPORTING iv_name = 'TPOOL'
                            CHANGING  cg_data = lt_tpool_ext ).
              lt_tpool = read_tpool( lt_tpool_ext ).
    
              deserialize_program( is_progdir = ls_progdir
                                   it_source  = lt_source
                                   it_tpool   = lt_tpool
                                   iv_package = iv_package ).
    
              deserialize_textpool( iv_program    = 
                                    it_tpool      = lt_tpool
                                    iv_is_include = abap_true ).
    
              ii_log->add_success( iv_msg  = |Include { ls_progdir-name } imported|
                                   is_item = ms_item ).
    
            CATCH zcx_abapgit_exception INTO lx_exc.
              ii_log->add_exception( ix_exc  = lx_exc
                                     is_item = ms_item ).
              CONTINUE.
          ENDTRY.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD deserialize_texts.
        DATA: lt_tpool_i18n TYPE zif_abapgit_lang_definitions=>ty_i18n_tpools,
              lt_tpool      TYPE textpool_table.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_tpool_i18n.
        ii_xml->read( EXPORTING iv_name = 'I18N_TPOOL'
                      CHANGING  cg_data = lt_tpool_i18n ).
    
        LOOP AT lt_tpool_i18n ASSIGNING .
          lt_tpool = read_tpool( -textpool ).
          deserialize_textpool( iv_program  = iv_prog_name
                                iv_language = -language
                                it_tpool    = lt_tpool ).
        ENDLOOP.
      ENDMETHOD.
    
      METHOD deserialize_xml.
    
        DATA: lv_complete  TYPE rs38l-area,
              lv_namespace TYPE rs38l-namespace,
              lv_areat     TYPE tlibt-areat,
              lv_stext     TYPE tftit-stext,
              lv_group     TYPE rs38l-area.
    
        lv_complete = ms_item-obj_name.
    
        CALL FUNCTION 'FUNCTION_INCLUDE_SPLIT'
          EXPORTING
            complete_area                = lv_complete
          IMPORTING
            namespace                    = lv_namespace
            group                        = lv_group
          EXCEPTIONS
            include_not_exists           = 1
            group_not_exists             = 2
            no_selections                = 3
            no_function_include          = 4
            no_function_pool             = 5
            delimiter_wrong_position     = 6
            no_customer_function_group   = 7
            no_customer_function_include = 8
            reserved_name_customer       = 9
            namespace_too_long           = 10
            area_length_error            = 11
            OTHERS                       = 12.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        ii_xml->read( EXPORTING iv_name = 'AREAT'
                      CHANGING  cg_data = lv_areat ).
        lv_stext = lv_areat.
    
        CALL FUNCTION 'RS_FUNCTION_POOL_INSERT'
          EXPORTING
            function_pool           = lv_group
            short_text              = lv_stext
            namespace               = lv_namespace
            devclass                = iv_package
            unicode_checks          = iv_version
            corrnum                 = iv_transport
            suppress_corr_check     = abap_false
          EXCEPTIONS
            name_already_exists     = 1
            name_not_correct        = 2
            function_already_exists = 3
            invalid_function_pool   = 4
            invalid_name            = 5
            too_many_functions      = 6
            no_modify_permission    = 7
            no_show_permission      = 8
            enqueue_system_failure  = 9
            canceled_in_corr        = 10
            undefined_error         = 11
            OTHERS                  = 12.
    
        CASE sy-subrc.
          WHEN 0.
            " Everything is ok
          WHEN 1 OR 3.
            " If the function group exists we need to manually update the short text
            update_func_group_short_text( iv_group      = lv_group
                                          iv_short_text = lv_stext ).
          WHEN OTHERS.
            zcx_abapgit_exception=>raise_t100( ).
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD functions.
    
        DATA: lv_area    TYPE rs38l-area,
              lt_enlfdir TYPE STANDARD TABLE OF enlfdir.
        DATA lv_index TYPE i.
    
        FIELD-SYMBOLS:  TYPE LINE OF ty_rs38l_incl_tt,
                        TYPE enlfdir.
    
        lv_area = ms_item-obj_name.
    
        CALL FUNCTION 'RS_FUNCTION_POOL_CONTENTS'
          EXPORTING
            function_pool           = lv_area
          TABLES
            functab                 = rt_functab
          EXCEPTIONS
            function_pool_not_found = 1
            OTHERS                  = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        "FM is not reliable if Function Group is inconsistent, so cross-check results (#7147)
        SELECT * FROM enlfdir
          INTO TABLE lt_enlfdir
          WHERE area = ms_item-obj_name
            AND active = abap_true
          ORDER BY funcname.                                  "#EC CI_SUBRC
    
        LOOP AT lt_enlfdir ASSIGNING .
          TRANSLATE -funcname TO UPPER CASE.
        ENDLOOP.
    
        SORT lt_enlfdir BY funcname ASCENDING.
    
        "Remove anything not in FM attributes table
        LOOP AT rt_functab ASSIGNING .
          TRANSLATE  TO UPPER CASE.
          lv_index = sy-tabix.
          READ TABLE lt_enlfdir WITH KEY funcname = -funcname TRANSPORTING NO FIELDS.
          IF sy-subrc <> 0.
            DELETE rt_functab INDEX lv_index.
          ENDIF.
        ENDLOOP.
    
        SORT rt_functab BY funcname ASCENDING.
        DELETE ADJACENT DUPLICATES FROM rt_functab COMPARING funcname.
    
      ENDMETHOD.
    
      METHOD get_abap_version.
    
        DATA: lt_includes TYPE ty_sobj_name_tt,
              ls_progdir  TYPE zif_abapgit_sap_report=>ty_progdir,
              lo_xml      TYPE REF TO zif_abapgit_xml_input.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_includes.
    
        ii_xml->read( EXPORTING iv_name = 'INCLUDES'
                      CHANGING  cg_data = lt_includes ).
    
        LOOP AT lt_includes ASSIGNING .
    
          lo_xml = mo_files->read_xml(  ).
    
          lo_xml->read( EXPORTING iv_name = 'PROGDIR'
                        CHANGING  cg_data = ls_progdir ).
    
          IF ls_progdir-uccheck IS INITIAL.
            CONTINUE.
          ELSEIF rv_abap_version IS INITIAL.
            rv_abap_version = ls_progdir-uccheck.
            CONTINUE.
          ELSEIF rv_abap_version <> ls_progdir-uccheck.
    *** All includes need to have the same ABAP language version
            zcx_abapgit_exception=>raise( 'different ABAP Language Versions' ).
          ENDIF.
        ENDLOOP.
    
        IF rv_abap_version IS INITIAL.
          set_abap_language_version( CHANGING cv_abap_language_version = rv_abap_version ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD includes.
    
        TYPES: BEGIN OF ty_reposrc,
                 progname TYPE reposrc-progname,
               END OF ty_reposrc.
    
        DATA: lt_reposrc        TYPE STANDARD TABLE OF ty_reposrc WITH DEFAULT KEY,
              ls_reposrc        LIKE LINE OF lt_reposrc,
              lv_program        TYPE program,
              lv_maintviewname  LIKE LINE OF rt_includes,
              lv_offset_ns      TYPE i,
              lv_tabix          LIKE sy-tabix,
              lt_functab        TYPE ty_rs38l_incl_tt,
              lt_tadir_includes TYPE HASHED TABLE OF objname WITH UNIQUE KEY table_line.
    
        FIELD-SYMBOLS:  LIKE LINE OF rt_includes,
                           LIKE LINE OF lt_functab.
    
        IF lines( mt_includes_cache ) > 0.
          rt_includes = mt_includes_cache.
          RETURN.
        ENDIF.
    
        lv_program = main_name( ).
        lt_functab = functions( ).
    
        CALL FUNCTION 'RS_GET_ALL_INCLUDES'
          EXPORTING
            program      = lv_program
          TABLES
            includetab   = rt_includes
          EXCEPTIONS
            not_existent = 1
            no_program   = 2
            OTHERS       = 3.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'Error from RS_GET_ALL_INCLUDES' ).
        ENDIF.
    
        LOOP AT lt_functab ASSIGNING .
          DELETE TABLE rt_includes FROM -include.
        ENDLOOP.
    
    * handle generated maintenance views
        IF ms_item-obj_name(1) <> '/'.
          "FGroup name does not contain a namespace
          lv_maintviewname = |L{ ms_item-obj_name }T00|.
        ELSE.
          "FGroup name contains a namespace
          lv_offset_ns = find( val = ms_item-obj_name+1
                               sub = '/' ).
          lv_offset_ns = lv_offset_ns + 2.
          lv_maintviewname = |{ ms_item-obj_name(lv_offset_ns) }L{ ms_item-obj_name+lv_offset_ns }T00|.
        ENDIF.
    
        READ TABLE rt_includes WITH KEY table_line = lv_maintviewname TRANSPORTING NO FIELDS.
        IF sy-subrc <> 0.
          APPEND lv_maintviewname TO rt_includes.
        ENDIF.
    
        SORT rt_includes.
        IF lines( rt_includes ) > 0.
          " check which includes have their own tadir entry
          " these includes might reside in a different package or might be shared between multiple function groups
          " or other programs and are hence no part of the to serialized FUGR object
          " they will be handled as individual objects when serializing their package
          " in addition, referenced XTI includes referencing (simple) transformations must be ignored
          SELECT obj_name
            INTO TABLE lt_tadir_includes
            FROM tadir
            FOR ALL ENTRIES IN rt_includes
            WHERE pgmid      = 'R3TR'
                  AND object = 'PROG'
                  AND obj_name = rt_includes-table_line.
          LOOP AT rt_includes ASSIGNING .
            " skip autogenerated includes from Table Maintenance Generator
            IF  CP 'LSVIM*'.
              DELETE rt_includes INDEX sy-tabix.
              CONTINUE.
            ENDIF.
            READ TABLE lt_tadir_includes WITH KEY table_line =  TRANSPORTING NO FIELDS.
            IF sy-subrc = 0.
              DELETE rt_includes.
              CONTINUE.
            ENDIF.
            IF strlen(  ) = 33 AND +30(3) = 'XTI'.
              "ignore referenced (simple) transformation includes
              DELETE rt_includes.
              CONTINUE.
            ENDIF.
          ENDLOOP.
    
          IF lines( rt_includes ) > 0.
            SELECT progname FROM reposrc
              INTO TABLE lt_reposrc
              FOR ALL ENTRIES IN rt_includes
              WHERE progname = rt_includes-table_line
              AND r3state = 'A'.
          ENDIF.
          SORT lt_reposrc BY progname ASCENDING.
        ENDIF.
    
        LOOP AT rt_includes ASSIGNING .
          lv_tabix = sy-tabix.
    
    * make sure the include exists
          READ TABLE lt_reposrc INTO ls_reposrc
            WITH KEY progname =  BINARY SEARCH.
          IF sy-subrc <> 0.
            DELETE rt_includes INDEX lv_tabix.
            CONTINUE.
          ENDIF.
    
          "Make sure that the include does not belong to another function group
          IF is_part_of_other_fugr(  ) = abap_true.
            DELETE rt_includes.
          ENDIF.
        ENDLOOP.
    
        APPEND lv_program TO rt_includes.
        SORT rt_includes.
    
        mt_includes_cache = rt_includes.
    
      ENDMETHOD.
    
      METHOD is_any_function_module_locked.
    
        DATA: lt_functions TYPE ty_rs38l_incl_tt.
    
        FIELD-SYMBOLS:  TYPE rs38l_incl.
    
        TRY.
            lt_functions = functions( ).
          CATCH zcx_abapgit_exception.
            RETURN.
        ENDTRY.
    
        LOOP AT lt_functions ASSIGNING .
    
          IF exists_a_lock_entry_for( iv_lock_object = 'ESFUNCTION'
                                      iv_argument    = |{ -funcname }| ) = abap_true.
            rv_any_function_module_locked = abap_true.
            EXIT.
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD is_any_include_locked.
    
        DATA: lt_includes TYPE ty_sobj_name_tt.
        FIELD-SYMBOLS:  TYPE sobj_name.
    
        TRY.
            lt_includes = includes( ).
          CATCH zcx_abapgit_exception.
            RETURN.
        ENDTRY.
    
        LOOP AT lt_includes ASSIGNING .
    
          IF exists_a_lock_entry_for( iv_lock_object = 'ESRDIRE'
                                      iv_argument    = |{  }| ) = abap_true.
            rv_is_any_include_locked = abap_true.
            EXIT.
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD is_function_group_locked.
        rv_is_functions_group_locked = exists_a_lock_entry_for( iv_lock_object = 'EEUDB'
                                                                iv_argument    = ms_item-obj_name
                                                                iv_prefix      = 'FG' ).
      ENDMETHOD.
    
      METHOD is_part_of_other_fugr.
        " make sure that the include belongs to the function group
        " like in LSEAPFAP Form TADIR_MAINTENANCE
        DATA ls_tadir TYPE tadir.
        DATA lv_namespace TYPE rs38l-namespace.
        DATA lv_function_group TYPE rs38l-area.
        DATA lv_include TYPE rs38l-include.
        DATA ls_item_key TYPE zif_abapgit_definitions=>ty_item.
    
        rv_belongs_to_other_fugr = abap_false.
        IF iv_include(1) = 'L' OR iv_include+1 CS '/L'.
          lv_include = iv_include.
          ls_tadir-object = 'FUGR'.
    
          CALL FUNCTION 'FUNCTION_INCLUDE_SPLIT'
            IMPORTING
              namespace = lv_namespace
              group     = lv_function_group
            CHANGING
              include   = lv_include
            EXCEPTIONS
              OTHERS    = 1 ##FM_SUBRC_OK.
    
          IF lv_function_group(1) = 'X'.    " "EXIT"-function-module
            ls_tadir-object = 'FUGS'.
          ENDIF.
    
          IF sy-subrc = 0.
    
            CONCATENATE lv_namespace lv_function_group INTO ls_tadir-obj_name.
            ls_item_key-obj_type = ls_tadir-object.
            ls_item_key-obj_name = ls_tadir-obj_name.
    
            " compare complete tadir key to distinguish between regular and exit function groups
            IF ( ls_tadir-obj_name <> ms_item-obj_name OR ls_tadir-object <> ms_item-obj_type ) AND
               /apmg/cl_apm_abapgit_objects=>exists( ls_item_key ) = abap_true.
              rv_belongs_to_other_fugr = abap_true.
            ENDIF.
          ENDIF.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD main_name.
    
        DATA: lv_area      TYPE rs38l-area,
              lv_namespace TYPE rs38l-namespace,
              lv_group     TYPE rs38l-area.
    
        lv_area = ms_item-obj_name.
    
        CALL FUNCTION 'FUNCTION_INCLUDE_SPLIT'
          EXPORTING
            complete_area                = lv_area
          IMPORTING
            namespace                    = lv_namespace
            group                        = lv_group
          EXCEPTIONS
            include_not_exists           = 1
            group_not_exists             = 2
            no_selections                = 3
            no_function_include          = 4
            no_function_pool             = 5
            delimiter_wrong_position     = 6
            no_customer_function_group   = 7
            no_customer_function_include = 8
            reserved_name_customer       = 9
            namespace_too_long           = 10
            area_length_error            = 11
            OTHERS                       = 12.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CONCATENATE lv_namespace 'SAPL' lv_group INTO rv_program.
    
      ENDMETHOD.
    
      METHOD serialize_functions.
    
        DATA:
          lt_source     TYPE TABLE OF rssource,
          lt_functab    TYPE ty_rs38l_incl_tt,
          lt_new_source TYPE rsfb_source,
          ls_function   LIKE LINE OF rt_functions.
    
        FIELD-SYMBOLS:           LIKE LINE OF lt_functab,
                        TYPE LINE OF ty_function-documentation.
    
        lt_functab = functions( ).
    
        LOOP AT lt_functab ASSIGNING .
    * fm RPY_FUNCTIONMODULE_READ does not support source code
    * lines longer than 72 characters
          CLEAR ls_function.
          MOVE-CORRESPONDING  TO ls_function.
    
          CLEAR lt_new_source.
          CLEAR lt_source.
    
          CALL FUNCTION 'RPY_FUNCTIONMODULE_READ_NEW'
            EXPORTING
              functionname            = -funcname
            IMPORTING
              global_flag             = ls_function-global_flag
              remote_call             = ls_function-remote_call
              update_task             = ls_function-update_task
              short_text              = ls_function-short_text
              remote_basxml_supported = ls_function-remote_basxml
            TABLES
              import_parameter        = ls_function-import
              changing_parameter      = ls_function-changing
              export_parameter        = ls_function-export
              tables_parameter        = ls_function-tables
              exception_list          = ls_function-exception
              documentation           = ls_function-documentation
              source                  = lt_source
            CHANGING
              new_source              = lt_new_source
            EXCEPTIONS
              error_message           = 1
              function_not_found      = 2
              invalid_name            = 3
              OTHERS                  = 4.
          IF sy-subrc = 2.
            CONTINUE.
          ELSEIF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'Error from RPY_FUNCTIONMODULE_READ_NEW' ).
          ENDIF.
    
          LOOP AT ls_function-documentation ASSIGNING .
            CLEAR -index.
          ENDLOOP.
    
          SELECT SINGLE exten3 INTO ls_function-exception_classes FROM enlfdir
            WHERE funcname = -funcname.              "#EC CI_SUBRC
    
          " Scope and Interface Contract only for 7.55 or higher
          TRY.
              SELECT SINGLE rfcscope rfcvers INTO CORRESPONDING FIELDS OF ls_function FROM ('TFDIR')
                WHERE funcname = -funcname.          "#EC CI_SUBRC
            CATCH cx_sy_dynamic_osql_semantics ##NO_HANDLER.
          ENDTRY.
    
          APPEND ls_function TO rt_functions.
    
          IF NOT lt_new_source IS INITIAL.
            strip_generation_comments( CHANGING ct_source = lt_new_source ).
            mo_files->add_abap(
              iv_extra = -funcname
              it_abap  = lt_new_source ).
          ELSE.
            strip_generation_comments( CHANGING ct_source = lt_source ).
            mo_files->add_abap(
              iv_extra = -funcname
              it_abap  = lt_source ).
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD serialize_function_docs.
    
        FIELD-SYMBOLS  LIKE LINE OF it_functions.
    
        zcl_abapgit_factory=>get_longtexts( )->serialize(
          iv_longtext_id = c_longtext_id_prog
          iv_object_name = iv_prog_name
          io_i18n_params = mo_i18n_params
          ii_xml         = ii_xml ).
    
        LOOP AT it_functions ASSIGNING .
          zcl_abapgit_factory=>get_longtexts( )->serialize(
            iv_longtext_name = |LONGTEXTS_{ -funcname }|
            iv_longtext_id   = c_longtext_id_func
            iv_object_name   = -funcname
            io_i18n_params   = mo_i18n_params
            ii_xml           = ii_xml ).
          zcl_abapgit_factory=>get_longtexts( )->serialize(
            iv_longtext_name = |LONGTEXTS_{ -funcname }___EXC|
            iv_longtext_id   = c_longtext_id_func_exc
            iv_object_name   = -funcname
            io_i18n_params   = mo_i18n_params
            ii_xml           = ii_xml ).
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD serialize_includes.
    
        DATA: lt_includes TYPE ty_sobj_name_tt.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_includes.
    
        lt_includes = includes( ).
    
        LOOP AT lt_includes ASSIGNING .
    
    * todo, filename is not correct, a include can be used in several programs
          serialize_program( is_item    = ms_item
                             io_files   = mo_files
                             iv_program = 
                             iv_extra   =  ).
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD serialize_texts.
        DATA: lt_tpool_i18n TYPE zif_abapgit_lang_definitions=>ty_i18n_tpools,
              lt_tpool      TYPE textpool_table.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_tpool_i18n.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          RETURN.
        ENDIF.
    
        " Table d010tinf stores info. on languages in which program is maintained
        " Select all active translations of program texts
        " Skip main language - it was already serialized
        SELECT DISTINCT language
          INTO CORRESPONDING FIELDS OF TABLE lt_tpool_i18n
          FROM d010tinf
          WHERE r3state = 'A'
          AND prog = iv_prog_name
          AND language <> mv_language
          ORDER BY language ##TOO_MANY_ITAB_FIELDS.
    
        mo_i18n_params->trim_saplang_keyed_table(
          EXPORTING
            iv_lang_field_name = 'LANGUAGE'
          CHANGING
            ct_tab             = lt_tpool_i18n ).
    
        SORT lt_tpool_i18n BY language ASCENDING.
        LOOP AT lt_tpool_i18n ASSIGNING .
          READ TEXTPOOL iv_prog_name
            LANGUAGE -language
            INTO lt_tpool.
          -textpool = add_tpool( lt_tpool ).
        ENDLOOP.
    
        IF lines( lt_tpool_i18n ) > 0.
          ii_xml->add( iv_name = 'I18N_TPOOL'
                       ig_data = lt_tpool_i18n ).
        ENDIF.
      ENDMETHOD.
    
      METHOD serialize_xml.
    
        DATA: lt_includes TYPE ty_sobj_name_tt,
              lv_areat    TYPE tlibt-areat.
    
        SELECT SINGLE areat INTO lv_areat
          FROM tlibt
          WHERE spras = mv_language
          AND area = ms_item-obj_name.        "#EC CI_GENBUFF "#EC CI_SUBRC
    
        lt_includes = includes( ).
    
        ii_xml->add( iv_name = 'AREAT'
                     ig_data = lv_areat ).
        ii_xml->add( iv_name = 'INCLUDES'
                     ig_data = lt_includes ).
    
      ENDMETHOD.
    
      METHOD update_func_group_short_text.
    
        " We update the short text directly.
        " SE80 does the same in
        "   Program SAPLSEUF / LSEUFF07
        "   FORM GROUP_CHANGE
    
        UPDATE tlibt SET areat = iv_short_text
          WHERE spras = mv_language AND area = iv_group.
    
      ENDMETHOD.
    
      METHOD update_where_used.
    * make extra sure the where-used list is updated after deletion
    * Experienced some problems with the T00 include
    * this method just tries to update everything
    
        DATA: lv_include LIKE LINE OF it_includes,
              lo_cross   TYPE REF TO cl_wb_crossreference.
    
        LOOP AT it_includes INTO lv_include.
    
          CREATE OBJECT lo_cross
            EXPORTING
              p_name    = lv_include
              p_include = lv_include.
    
          lo_cross->index_actualize( ).
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        TYPES: BEGIN OF ty_stamps,
                 user TYPE syuname,
                 date TYPE d,
                 time TYPE t,
               END OF ty_stamps.
    
        DATA:
          lt_stamps    TYPE STANDARD TABLE OF ty_stamps WITH DEFAULT KEY,
          lv_program   TYPE program,
          lv_found     TYPE abap_bool,
          lt_functions TYPE ty_rs38l_incl_tt.
    
        FIELD-SYMBOLS:
           LIKE LINE OF lt_functions,
            LIKE LINE OF mt_includes_all,
              LIKE LINE OF lt_stamps.
    
        lv_program = main_name( ).
    
        IF mt_includes_all IS INITIAL.
          CALL FUNCTION 'RS_GET_ALL_INCLUDES'
            EXPORTING
              program      = lv_program
            TABLES
              includetab   = mt_includes_all
            EXCEPTIONS
              not_existent = 1
              no_program   = 2
              OTHERS       = 3.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'Error from RS_GET_ALL_INCLUDES' ).
          ENDIF.
        ENDIF.
    
        " Check if changed_by for include object was requested
        LOOP AT mt_includes_all ASSIGNING  WHERE table_line = to_upper( iv_extra ).
          lv_program = .
          lv_found   = abap_true.
          EXIT.
        ENDLOOP.
    
        " Check if changed_by for function module was requested
        lt_functions = functions( ).
    
        LOOP AT lt_functions ASSIGNING  WHERE funcname = to_upper( iv_extra ).
          lv_program = -include.
          lv_found   = abap_true.
          EXIT.
        ENDLOOP.
    
        SELECT unam AS user udat AS date utime AS time FROM reposrc
          APPENDING CORRESPONDING FIELDS OF TABLE lt_stamps
          WHERE progname = lv_program
          AND r3state = 'A'
          ORDER BY PRIMARY KEY.                               "#EC CI_SUBRC
    
        IF mt_includes_all IS NOT INITIAL AND lv_found = abap_false.
          SELECT unam AS user udat AS date utime AS time FROM reposrc
            APPENDING CORRESPONDING FIELDS OF TABLE lt_stamps
            FOR ALL ENTRIES IN mt_includes_all
            WHERE progname = mt_includes_all-table_line
            AND r3state = 'A'.                                "#EC CI_SUBRC
        ENDIF.
    
        SELECT unam AS user udat AS date utime AS time FROM repotext " Program text pool
          APPENDING CORRESPONDING FIELDS OF TABLE lt_stamps
          WHERE progname = lv_program
          AND r3state = 'A'
          ORDER BY PRIMARY KEY.                               "#EC CI_SUBRC
    
        SELECT vautor AS user vdatum AS date vzeit AS time FROM eudb         " GUI
          APPENDING CORRESPONDING FIELDS OF TABLE lt_stamps
          WHERE relid = 'CU'
          AND name = lv_program
          AND srtf2 = 0
          ORDER BY PRIMARY KEY ##TOO_MANY_ITAB_FIELDS.
    
    * Screens: username not stored in D020S database table
    
        SORT lt_stamps BY date DESCENDING time DESCENDING.
    
        READ TABLE lt_stamps INDEX 1 ASSIGNING .
        IF sy-subrc = 0.
          rv_user = -user.
        ELSE.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_area     TYPE rs38l-area,
              lt_includes TYPE ty_sobj_name_tt.
    
        " FUGR related to change documents will be deleted by CHDO
        SELECT SINGLE fgrp FROM tcdrps INTO lv_area WHERE fgrp = ms_item-obj_name.
        IF sy-subrc = 0.
          RETURN.
        ENDIF.
    
        lt_includes = includes( ).
    
        lv_area = ms_item-obj_name.
    
        CALL FUNCTION 'RS_FUNCTION_POOL_DELETE'
          EXPORTING
            area                   = lv_area
            suppress_popups        = abap_true
            skip_progress_ind      = abap_true
            corrnum                = iv_transport
          EXCEPTIONS
            canceled_in_corr       = 1
            enqueue_system_failure = 2
            function_exist         = 3
            not_executed           = 4
            no_modify_permission   = 5
            no_show_permission     = 6
            permission_failure     = 7
            pool_not_exist         = 8
            cancelled              = 9
            OTHERS                 = 10.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        update_where_used( lt_includes ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_program_name TYPE syrepid,
              lv_abap_version TYPE trdir-uccheck,
              lt_functions    TYPE ty_function_tt,
              lt_dynpros      TYPE ty_dynpro_tt,
              ls_cua          TYPE ty_cua.
    
        lv_abap_version = get_abap_version( io_xml ).
    
        deserialize_xml(
          ii_xml       = io_xml
          iv_version   = lv_abap_version
          iv_package   = iv_package
          iv_transport = iv_transport ).
    
        io_xml->read( EXPORTING iv_name = 'FUNCTIONS'
                      CHANGING  cg_data = lt_functions ).
    
        deserialize_functions(
          it_functions = lt_functions
          ii_log       = ii_log
          iv_version   = lv_abap_version
          iv_package   = iv_package
          iv_transport = iv_transport ).
    
        deserialize_includes(
          ii_xml     = io_xml
          iv_package = iv_package
          ii_log     = ii_log ).
    
        lv_program_name = main_name( ).
    
        IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
          deserialize_texts( iv_prog_name = lv_program_name
                             ii_xml       = io_xml ).
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'DYNPROS'
                      CHANGING  cg_data = lt_dynpros ).
    
        deserialize_dynpros( lt_dynpros ).
    
        io_xml->read( EXPORTING iv_name = 'CUA'
                      CHANGING  cg_data = ls_cua ).
    
        deserialize_cua( iv_program_name = lv_program_name
                         is_cua          = ls_cua ).
    
        deserialize_function_docs(
          iv_prog_name = lv_program_name
          it_functions = lt_functions
          ii_xml       = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_pool  TYPE tlibg-area.
    
        lv_pool = ms_item-obj_name.
        CALL FUNCTION 'RS_FUNCTION_POOL_EXISTS'
          EXPORTING
            function_pool   = lv_pool
          EXCEPTIONS
            pool_not_exists = 1.
        rv_bool = boolc( sy-subrc <> 1 ).
    
        " Skip FUGR generated by CHDO
        IF rv_bool = abap_true.
          SELECT SINGLE fgrp FROM tcdrp INTO lv_pool WHERE fgrp = lv_pool.
          IF sy-subrc = 0.
            rv_bool = abap_false.
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lv_program TYPE program.
    
        lv_program = main_name( ).
    
        IF is_function_group_locked( )        = abap_true
        OR is_any_include_locked( )           = abap_true
        OR is_any_function_module_locked( )   = abap_true
        OR is_any_dynpro_locked( lv_program ) = abap_true
        OR is_cua_locked( lv_program )        = abap_true
        OR is_text_locked( lv_program )       = abap_true.
    
          rv_is_locked = abap_true.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA:
          ls_item      TYPE zif_abapgit_definitions=>ty_item,
          lt_functions TYPE ty_rs38l_incl_tt,
          lt_includes  TYPE ty_sobj_name_tt.
    
        FIELD-SYMBOLS:
           LIKE LINE OF lt_functions,
            LIKE LINE OF lt_includes.
    
        ls_item-obj_type = 'PROG'.
        ls_item-obj_name = to_upper( iv_extra ).
    
        lt_functions = functions( ).
    
        LOOP AT lt_functions ASSIGNING  WHERE funcname = ls_item-obj_name.
          ls_item-obj_name = -include.
          rv_exit = zcl_abapgit_objects_factory=>get_gui_jumper( )->jump( ls_item ).
          IF rv_exit = abap_true.
            RETURN.
          ENDIF.
        ENDLOOP.
    
        lt_includes = includes( ).
    
        LOOP AT lt_includes ASSIGNING  WHERE table_line = ls_item-obj_name.
          rv_exit = zcl_abapgit_objects_factory=>get_gui_jumper( )->jump( ls_item ).
          IF rv_exit = abap_true.
            RETURN.
          ENDIF.
        ENDLOOP.
    
        " Otherwise covered by /apmg/cl_apm_abapgit_objects=>JUMP
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
    * function group SEUF
    * function group SIFP
    * function group SUNI
    
        DATA: lt_functions    TYPE ty_function_tt,
              ls_progdir      TYPE zif_abapgit_sap_report=>ty_progdir,
              lv_program_name TYPE syrepid,
              lt_dynpros      TYPE ty_dynpro_tt,
              ls_cua          TYPE ty_cua.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        serialize_xml( io_xml ).
    
        lt_functions = serialize_functions( ).
    
        io_xml->add( iv_name = 'FUNCTIONS'
                     ig_data = lt_functions ).
    
        serialize_includes( ).
    
        lv_program_name = main_name( ).
    
        ls_progdir = zcl_abapgit_factory=>get_sap_report( )->read_progdir( lv_program_name ).
    
        IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
          serialize_texts(
            iv_prog_name = lv_program_name
            ii_xml       = io_xml ).
        ENDIF.
    
        IF ls_progdir-subc = 'F'.
          lt_dynpros = serialize_dynpros( lv_program_name ).
          io_xml->add( iv_name = 'DYNPROS'
                       ig_data = lt_dynpros ).
    
          ls_cua = serialize_cua( lv_program_name ).
          io_xml->add( iv_name = 'CUA'
                       ig_data = ls_cua ).
        ENDIF.
    
        serialize_function_docs( iv_prog_name = lv_program_name
                                 it_functions = lt_functions
                                 ii_xml       = io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_fugs IMPLEMENTATION.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_g4ba IMPLEMENTATION.
    
      METHOD get_field_rules.
    
        ro_result = zcl_abapgit_field_rules=>create( ).
        ro_result->add(
          iv_table     = '/IWBEP/I_V4_MSGR'
          iv_field     = 'CREATED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_V4_MSGR'
          iv_field     = 'CHANGED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_V4_MSGR'
          iv_field     = 'CREATED_TS'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp
        )->add(
          iv_table     = '/IWBEP/I_V4_MSGR'
          iv_field     = 'CHANGED_TS'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp
        )->add(
          iv_table     = '/IWBEP/I_V4_MSGA'
          iv_field     = 'CREATED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_V4_MSGA'
          iv_field     = 'CHANGED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_V4_MSGA'
          iv_field     = 'CREATED_TS'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp
        )->add(
          iv_table     = '/IWBEP/I_V4_MSGA'
          iv_field     = 'CHANGED_TS'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp ).
    
        IF ms_item-abap_language_version = zcl_abapgit_abap_language_vers=>c_no_abap_language_version.
          ro_result->add(
            iv_table     = '/IWBEP/I_V4_MSGR'
            iv_field     = 'ABAP_LANGUAGE_VERSION'
            iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-abap_language_version ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_generic.
    
        CREATE OBJECT ro_generic
          EXPORTING
            io_field_rules = get_field_rules( )
            is_item        = ms_item
            iv_language    = mv_language.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        DATA lv_created TYPE sy-uname.
        DATA lv_changed TYPE sy-uname.
    
        SELECT SINGLE created_by changed_by INTO (lv_created, lv_changed) FROM ('/IWBEP/I_V4_MSGR')
          WHERE group_id = ms_item-obj_name.
    
        rv_user = lv_changed.
        IF lv_changed IS INITIAL.
          rv_user = lv_created.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        get_generic( )->delete( iv_package ).
    
    * SUSH object type checks if the G4BA exists, and blocks deletion if the TADIR exists without deletion flag
        UPDATE tadir SET delflag = abap_true WHERE pgmid = 'R3TR' AND object = 'G4BA' AND obj_name = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        get_generic( )->serialize( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_g4bs IMPLEMENTATION.
    
      METHOD get_field_rules.
    
        ro_result = zcl_abapgit_field_rules=>create( ).
        ro_result->add(
          iv_table     = '/IWBEP/I_V4_MSRV'
          iv_field     = 'CREATED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_V4_MSRV'
          iv_field     = 'CHANGED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_V4_MSRV'
          iv_field     = 'CREATED_TS'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp
        )->add(
          iv_table     = '/IWBEP/I_V4_MSRV'
          iv_field     = 'CHANGED_TS'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp
        )->add(
          iv_table     = '/IWBEP/I_V4_MSRT'
          iv_field     = 'CREATED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_V4_MSRT'
          iv_field     = 'CHANGED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_V4_MSRT'
          iv_field     = 'CREATED_TS'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp
        )->add(
          iv_table     = '/IWBEP/I_V4_MSRT'
          iv_field     = 'CHANGED_TS'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp ).
    
        IF ms_item-abap_language_version = zcl_abapgit_abap_language_vers=>c_no_abap_language_version.
          ro_result->add(
            iv_table     = '/IWBEP/I_V4_MSRV'
            iv_field     = 'ABAP_LANGUAGE_VERSION'
            iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-abap_language_version ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_generic.
    
        CREATE OBJECT ro_generic
          EXPORTING
            io_field_rules = get_field_rules( )
            is_item        = ms_item
            iv_language    = mv_language.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA lv_created TYPE sy-uname.
        DATA lv_changed TYPE sy-uname.
    
        " Get entry with highest version
        SELECT created_by changed_by INTO (lv_created, lv_changed) FROM ('/IWBEP/I_V4_MSRV')
          WHERE service_id = ms_item-obj_name ORDER BY PRIMARY KEY.
          rv_user = lv_changed.
          IF lv_changed IS INITIAL.
            rv_user = lv_created.
          ENDIF.
        ENDSELECT.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        get_generic( )->delete( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        get_generic( )->serialize( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_OBJECT_GSMP IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA lv_name TYPE c LENGTH 180.
        DATA lv_user  TYPE string.
        DATA lx_root TYPE REF TO cx_root.
    
        TRY.
            lv_name = ms_item-obj_name.
    
            SELECT SINGLE changed_by INTO lv_user
              FROM ('GSM_MD_PRV_W')
              WHERE provider_id = lv_name AND version = 'I'.
    
            IF lv_user IS INITIAL.
              SELECT SINGLE changed_by INTO lv_user
                FROM ('GSM_MD_PRV_W')
                WHERE provider_id = lv_name AND version = 'A'.
            ENDIF.
    
            rv_user = lv_user.
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise( iv_text     = lx_root->get_text( )
                                         ix_previous = lx_root ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_http IMPLEMENTATION.
    
      METHOD constructor.
    
        DATA: lr_dummy TYPE REF TO data.
    
        super->constructor(
            is_item        = is_item
            iv_language    = iv_language
            io_files       = io_files
            io_i18n_params = io_i18n_params ).
    
        TRY.
            CREATE DATA lr_dummy TYPE ('UCONHTTPSERVHEAD').
          CATCH cx_root.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE changedby FROM ('UCONHTTPSERVHEAD') INTO rv_user WHERE id = ms_item-obj_name.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA lv_name TYPE c LENGTH 30.
    
        lv_name = ms_item-obj_name.
    
        CALL METHOD ('CL_UCON_API_FACTORY')=>('DELETE_HTTP_SERVICE')
          EXPORTING
            name     = lv_name
            devclass = iv_package.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_http_servid       TYPE c LENGTH 30,
              lt_handler           TYPE TABLE OF ty_handler,
              ls_handler           LIKE LINE OF lt_handler,
              ls_description       TYPE ty_uconhttpservtext,
              lv_check_object_name TYPE c LENGTH 40,
              lx_root              TYPE REF TO cx_root,
              lo_http              TYPE REF TO object,
              ls_abap_lang         TYPE ty_gs_object_version,
              lo_instance          TYPE REF TO object,
              lv_icfnode           TYPE ty_icf_node.
    
        TRY.
            io_xml->read(
              EXPORTING iv_name = 'HTTPID'
              CHANGING  cg_data = lv_http_servid ).
            io_xml->read(
              EXPORTING iv_name = 'HTTPTEXT'
              CHANGING  cg_data = ls_description ).
            io_xml->read(
              EXPORTING iv_name = 'HTTPHDL'
              CHANGING  cg_data = lt_handler ).
    
            TRY.
                "link to icf node (in releases older than 757, a http service requires a icf node to function)
                io_xml->read(
                  EXPORTING iv_name = 'HTTPICFNODE'
                  CHANGING  cg_data = lv_icfnode ).
              CATCH cx_root.
            ENDTRY.
    
            SELECT COUNT(*) FROM ('UCONHTTPSERVHEAD') WHERE id = lv_http_servid.
            IF sy-dbcnt > 0.
              "update
              CALL METHOD ('CL_UCON_API_FACTORY')=>('GET_HTTP_SERVICE')
                EXPORTING
                  name          = lv_http_servid
                  no_auth_check = abap_true
                RECEIVING
                  http_service  = lo_http.
            ELSE.
              "create
              CALL METHOD ('CL_UCON_API_FACTORY')=>('NEW_HTTP_SERVICE')
                EXPORTING
                  name         = lv_http_servid
                RECEIVING
                  http_service = lo_http.
            ENDIF.
    
            CALL METHOD lo_http->('IF_UCON_API_HTTP_SERVICE~SET_HANDLER')
              EXPORTING
                handler = lt_handler.
            IF lt_handler IS NOT INITIAL.
              READ TABLE lt_handler INTO ls_handler INDEX 1.
              "get language version from abap class
    
              lv_check_object_name = ls_handler-servicehandler.
              IF lv_check_object_name IS NOT INITIAL.
                TRY.
                    CALL METHOD ('CL_ABAP_LANGUAGE_VERSION')=>('GET_INSTANCE')
                      RECEIVING
                        ro_version_handler = lo_instance.
                    CALL METHOD lo_instance->('IF_ABAP_LANGUAGE_VERSION~GET_VERSION_OF_OBJECT')
                      EXPORTING
                        iv_object_type    = 'CLAS'
                        iv_object_name    = lv_check_object_name
                      RECEIVING
                        rs_object_version = ls_abap_lang.
    
                    IF ls_abap_lang-id = 'X'. "language version X not supported, use space instead
                      ls_abap_lang-id = space.
                    ENDIF.
    
                    CALL METHOD lo_http->('IF_UCON_API_HTTP_SERVICE~SET_LANGUAGE_VERSION')
                      EXPORTING
                        iv_langu_version = ls_abap_lang-id.
                  CATCH cx_root ##NO_HANDLER.
                    " ABAP language version not supported in this system
                ENDTRY.
              ENDIF.
            ENDIF.
    
            CALL METHOD lo_http->('IF_UCON_API_HTTP_SERVICE~SET_DESCRIPTION')
              EXPORTING
                texts = ls_description.
            CALL METHOD lo_http->('IF_UCON_API_HTTP_SERVICE~SET_ICF_SERVICE')
              EXPORTING
                iv_icfservice = lv_icfnode.
            CALL METHOD lo_http->('IF_UCON_API_HTTP_SERVICE~ACTIVATE').
            CALL METHOD lo_http->('IF_UCON_API_HTTP_SERVICE~SAVE')
              EXPORTING
                run_dark  = abap_true
                dev_class = iv_package
                korrnum   = iv_transport.
            CALL METHOD lo_http->('IF_UCON_API_HTTP_SERVICE~FREE').
    
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise_with_text( lx_root ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        SELECT COUNT(*) FROM ('UCONHTTPSERVHEAD') WHERE id = ms_item-obj_name.
        rv_bool = boolc( sy-dbcnt > 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_http_srv_id TYPE c LENGTH 30,
              lo_serv        TYPE REF TO object, "if_ucon_api_http_service
              lt_handler     TYPE TABLE OF ty_uconservhttphandler,
              ls_description TYPE ty_uconhttpservtext,
              lx_root        TYPE REF TO cx_root,
              lv_icfnode     TYPE ty_icf_node,
              lv_name        TYPE c LENGTH 30.
    
        TRY.
            lv_http_srv_id = ms_item-obj_name.
            "read http service object
            CALL METHOD ('CL_UCON_API_FACTORY')=>('GET_HTTP_SERVICE')
              EXPORTING
                name          = lv_http_srv_id
                no_auth_check = abap_true
              RECEIVING
                http_service  = lo_serv.
    
            CALL METHOD lo_serv->('IF_UCON_API_HTTP_SERVICE~GET_HANDLER') RECEIVING handler = lt_handler.
            CALL METHOD lo_serv->('IF_UCON_API_HTTP_SERVICE~GET_DESCRIPTION')
              EXPORTING
                lang = sy-langu
              RECEIVING
                text = ls_description.
    
            "add data to output
            CALL METHOD lo_serv->('IF_UCON_API_HTTP_SERVICE~GET_NAME') RECEIVING name = lv_name.
    
            io_xml->add(
              iv_name = 'HTTPID'
              ig_data = lv_name ).
    
            io_xml->add(
              iv_name = 'HTTPTEXT'
              ig_data = ls_description ).
    
            io_xml->add(
              iv_name = 'HTTPHDL'
              ig_data = lt_handler ).
    
            TRY.
                "link to icf node (in releases older than 757, a http service requires a icf node to function)
                CALL METHOD lo_serv->('IF_UCON_API_HTTP_SERVICE~GET_ICF_SERVICE') IMPORTING ev_icfservice = lv_icfnode.
                io_xml->add(
                  iv_name = 'HTTPICFNODE'
                  ig_data = lv_icfnode ).
              CATCH cx_root.
            ENDTRY.
    
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise_with_text( lx_root ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_iamu IMPLEMENTATION.
    
      METHOD get_extension.
    
        CONSTANTS:
          lc_jpg TYPE xstring VALUE 'FFD8FF',
          lc_png TYPE xstring VALUE '89504E470D0A1A0A',
          lc_gif TYPE xstring VALUE '47494638',
          lc_bmp TYPE xstring VALUE '424D'.
    
        DATA lv_len TYPE i.
    
        " Try to derive type of MIME object from the long name
        FIND REGEX '\.(\w)$' IN iv_name SUBMATCHES rv_extension ##REGEX_POSIX.
        IF sy-subrc = 0.
          rv_extension = to_lower( rv_extension ).
        ELSEIF zcl_abapgit_utils=>is_binary( iv_data ) = abap_true.
          " Use magic numbers to detect common file types
          lv_len = xstrlen( iv_data ).
          IF lv_len > 3 AND iv_data(3) = lc_jpg.
            rv_extension = 'jpg'.
          ELSEIF lv_len > 8 AND iv_data(8) = lc_png.
            rv_extension = 'png'.
          ELSEIF lv_len > 4 AND iv_data(4) = lc_gif.
            rv_extension = 'git'.
          ELSEIF lv_len > 2 AND iv_data(2) = lc_bmp.
            rv_extension = 'bmp'.
          ELSE.
            rv_extension = 'bin'.
          ENDIF.
        ELSE.
          rv_extension = 'txt'.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD load_mime_api.
    
        DATA: ls_mime_name TYPE iacikeym.
    
        ls_mime_name = ms_item-obj_name.
    
        cl_w3_api_mime=>if_w3_api_mime~load(
          EXPORTING
            p_mime_name         = ls_mime_name
          IMPORTING
            p_mime              = mi_mime_api
          EXCEPTIONS
            object_not_existing = 1
            permission_failure  = 2
            data_corrupt        = 3
            error_occured       = 4
            OTHERS              = 6 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error from if_w3_api_mime~load' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD lock.
    
        " As a side effect this method removes also existing locks
        mi_mime_api->if_w3_api_object~set_changeable(
          EXPORTING
            p_changeable                 = iv_changable
          EXCEPTIONS
            action_cancelled             = 1
            object_locked_by_other_user  = 2
            permission_failure           = 3
            object_already_changeable    = 4
            object_already_unlocked      = 5
            object_just_created          = 6
            object_deleted               = 7
            object_modified              = 8
            object_not_existing          = 9
            object_invalid               = 10
            error_occured                = 11
            OTHERS                       = 12 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~set_changeable| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD read.
    
        load_mime_api( ).
    
        mi_mime_api->get_attributes(
          IMPORTING
            p_attributes   = rs_internet_appl_comp_binary-attributes
          EXCEPTIONS
            object_invalid = 1
            mime_deleted   = 2
            error_occured  = 3
            OTHERS         = 4 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~get_attributes| ).
        ENDIF.
    
        CLEAR: rs_internet_appl_comp_binary-attributes-chname,
               rs_internet_appl_comp_binary-attributes-tdate,
               rs_internet_appl_comp_binary-attributes-ttime,
               rs_internet_appl_comp_binary-attributes-devclass.
    
        mi_mime_api->get_source(
          IMPORTING
            p_source       = rs_internet_appl_comp_binary-source
            p_datalength   = rs_internet_appl_comp_binary-length
          EXCEPTIONS
            object_invalid = 1
            mime_deleted   = 2
            error_occured  = 3
            OTHERS         = 4 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~get_source| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD save.
    
        IF zif_abapgit_object~exists( ) = abap_true.
          load_mime_api( ).
          lock( abap_true ).
    
          mi_mime_api->set_source(
            EXPORTING
              p_source     = is_internet_appl_comp_binary-source
              p_datalength = is_internet_appl_comp_binary-length
            EXCEPTIONS
              object_not_changeable = 1
              object_deleted        = 2
              object_invalid        = 3
              authorize_failure     = 4
              invalid_content       = 5
              error_occured         = 6
              OTHERS                = 7 ).
    
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( |Error { sy-subrc } from set_source| ).
          ENDIF.
        ELSE.
          cl_w3_api_mime=>if_w3_api_mime~create_new(
            EXPORTING
              p_mime_data             = is_internet_appl_comp_binary-attributes
              p_mime_content          = is_internet_appl_comp_binary-source
              p_datalength            = is_internet_appl_comp_binary-length
            IMPORTING
              p_mime                  = mi_mime_api
            EXCEPTIONS
              object_already_existing = 1
              object_just_created     = 2
              not_authorized          = 3
              undefined_name          = 4
              author_not_existing     = 5
              action_cancelled        = 6
              error_occured           = 7
              OTHERS                  = 8 ).
    
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( |Error { sy-subrc } from create_new| ).
          ENDIF.
        ENDIF.
    
        " Create_new does not update text, so set attributes explicitly
        mi_mime_api->set_attributes(
          EXPORTING
            p_attributes          = is_internet_appl_comp_binary-attributes
          EXCEPTIONS
            object_not_changeable = 1
            object_deleted        = 2
            object_invalid        = 3
            author_not_existing   = 4
            authorize_failure     = 5
            error_occured         = 6
            OTHERS                = 7 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error { sy-subrc } from set_attributes| ).
        ENDIF.
    
        mi_mime_api->if_w3_api_object~save(
          EXCEPTIONS
            object_invalid        = 1
            object_not_changeable = 2
            action_cancelled      = 3
            permission_failure    = 4
            not_changed           = 5
            data_invalid          = 6
            error_occured         = 7
            OTHERS                = 8 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error { sy-subrc } from save| ).
        ENDIF.
    
        lock( abap_false ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        rv_user = read( )-attributes-chname.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        load_mime_api( ).
    
        mi_mime_api->if_w3_api_object~set_changeable(
          EXPORTING
            p_changeable                 = abap_true
          EXCEPTIONS
            action_cancelled             = 1
            object_locked_by_other_user  = 2
            permission_failure           = 3
            object_already_changeable    = 4
            object_already_unlocked      = 5
            object_just_created          = 6
            object_deleted               = 7
            object_modified              = 8
            object_not_existing          = 9
            object_invalid               = 10
            error_occured                = 11
            OTHERS                       = 12 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~set_changeable| ).
        ENDIF.
    
        mi_mime_api->if_w3_api_object~delete(
          EXCEPTIONS
            object_not_empty      = 1
            object_not_changeable = 2
            object_invalid        = 3
            error_occured         = 4
            OTHERS                = 5 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~delete| ).
        ENDIF.
    
        mi_mime_api->if_w3_api_object~save(
          EXCEPTIONS
            object_invalid        = 1
            object_not_changeable = 2
            action_cancelled      = 3
            permission_failure    = 4
            not_changed           = 5
            data_invalid          = 6
            error_occured         = 7
            OTHERS                = 8 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~save| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_internet_appl_comp_binary TYPE ty_internet_appl_comp_binary.
        DATA lv_xstring TYPE xstring.
    
        io_xml->read(
          EXPORTING
            iv_name = 'IAMU'
          CHANGING
            cg_data = ls_internet_appl_comp_binary ).
    
        ls_internet_appl_comp_binary-attributes-devclass = iv_package.
    
        IF io_xml->get_metadata( )-version = 'v2.0.0'.
          lv_xstring = mo_files->read_raw( ls_internet_appl_comp_binary-extension ).
    
          zcl_abapgit_convert=>xstring_to_bintab(
            EXPORTING
              iv_xstr   = lv_xstring
            IMPORTING
              et_bintab = ls_internet_appl_comp_binary-source
              ev_size   = ls_internet_appl_comp_binary-length ).
        ENDIF.
    
        save( ls_internet_appl_comp_binary ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: ls_mime_name TYPE iacikeym.
    
        ls_mime_name = ms_item-obj_name.
    
        cl_w3_api_mime=>s_check_exist(
          EXPORTING
            p_mime_name = ls_mime_name
          IMPORTING
            p_exists    = rv_bool ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata         = get_metadata( ).
        rs_metadata-version = 'v2.0.0'. " Serialization v2, separate data file
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_internet_appl_comp_binary TYPE ty_internet_appl_comp_binary.
        DATA lv_xstring TYPE xstring.
    
        FIELD-SYMBOLS:  LIKE LINE OF ls_internet_appl_comp_binary-source.
    
        ls_internet_appl_comp_binary = read( ).
    
        " Serialization v2, separate data file
        LOOP AT ls_internet_appl_comp_binary-source ASSIGNING .
          lv_xstring = lv_xstring && -line.
        ENDLOOP.
        lv_xstring = lv_xstring(ls_internet_appl_comp_binary-length).
    
        CLEAR: ls_internet_appl_comp_binary-source, ls_internet_appl_comp_binary-length.
    
        ls_internet_appl_comp_binary-extension = get_extension(
          iv_name = ls_internet_appl_comp_binary-attributes-longname
          iv_data = lv_xstring ).
    
        mo_files->add_raw(
          iv_data = lv_xstring
          iv_ext  = ls_internet_appl_comp_binary-extension ).
    
        io_xml->add( iv_name = 'IAMU'
                     ig_data = ls_internet_appl_comp_binary ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_iarp IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        ms_name = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD read.
    
        DATA: li_resource TYPE REF TO if_w3_api_resource.
    
        li_resource = w3_api_load( ).
        es_attributes = w3_api_get_attributes( li_resource ).
    
        CLEAR: es_attributes-chname,
               es_attributes-tdate,
               es_attributes-ttime,
               es_attributes-devclass.
    
        et_parameters = w3_api_get_parameters( li_resource ).
    
      ENDMETHOD.
    
      METHOD save.
    
        DATA: li_resource TYPE REF TO if_w3_api_resource.
    
        li_resource = w3_api_create_new( is_attributes ).
    
        w3_api_set_attributes(
            ii_resource   = li_resource
            is_attributes = is_attributes ).
    
        w3_api_set_parameters(
            ii_resource   = li_resource
            it_parameters = it_parameters ).
    
        w3_api_save( li_resource ).
    
        " Release locks
        w3_api_set_changeable(
          ii_resource   = li_resource
          iv_changeable = abap_false ).
    
      ENDMETHOD.
    
      METHOD w3_api_create_new.
    
        cl_w3_api_resource=>if_w3_api_resource~create_new(
          EXPORTING
            p_resource_data         = is_attributes
          IMPORTING
            p_resource              = ri_resource
          EXCEPTIONS
            object_already_existing = 1
            object_just_created     = 2
            not_authorized          = 3
            undefined_name          = 4
            author_not_existing     = 5
            action_cancelled        = 6
            error_occured           = 7
            OTHERS                  = 8 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error from if_w3_api_resource~create_new. Subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_delete.
    
        ii_resource->if_w3_api_object~delete(
          EXCEPTIONS
            object_not_empty      = 1
            object_not_changeable = 2
            object_invalid        = 3
            error_occured         = 4
            OTHERS                = 5 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error from if_w3_api_object~delete. Subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_get_attributes.
    
        ii_resource->get_attributes(
          IMPORTING
            p_attributes     = rs_attributes
          EXCEPTIONS
            object_invalid   = 1
            resource_deleted = 2
            error_occured    = 3
            OTHERS           = 4 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error from if_w3_api_resource~get_attributes. Subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_get_parameters.
    
        ii_resource->get_parameters(
          IMPORTING
            p_parameters     = rt_parameters
          EXCEPTIONS
            object_invalid   = 1
            resource_deleted = 2
            error_occured    = 3
            OTHERS           = 4 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error from if_w3_api_resource~get_parameters. Subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_load.
    
        cl_w3_api_resource=>if_w3_api_resource~load(
          EXPORTING
            p_resource_name     = ms_name
          IMPORTING
            p_resource          = ri_resource
          EXCEPTIONS
            object_not_existing = 1
            permission_failure  = 2
            error_occured       = 3
            OTHERS              = 4 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error from w3api_resource~load. Subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_save.
    
        ii_resource->if_w3_api_object~save(
          EXCEPTIONS
            object_invalid        = 1
            object_not_changeable = 2
            action_cancelled      = 3
            permission_failure    = 4
            not_changed           = 5
            data_invalid          = 6
            error_occured         = 7
            OTHERS                = 8 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error from if_w3_api_object~save. Subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_set_attributes.
    
        ii_resource->set_attributes(
          EXPORTING
            p_attributes          = is_attributes
          EXCEPTIONS
            object_not_changeable = 1
            object_deleted        = 2
            object_invalid        = 3
            author_not_existing   = 4
            authorize_failure     = 5
            error_occured         = 6
            OTHERS                = 7 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error from if_w3_api_resource~set_attributes. Subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_set_changeable.
    
        ii_resource->if_w3_api_object~set_changeable(
          EXPORTING
            p_changeable                 = iv_changeable
          EXCEPTIONS
            action_cancelled             = 1
            object_locked_by_other_user  = 2
            permission_failure           = 3
            object_already_changeable    = 4
            object_already_unlocked      = 5
            object_just_created          = 6
            object_deleted               = 7
            object_modified              = 8
            object_not_existing          = 9
            object_invalid               = 10
            error_occured                = 11
            OTHERS                       = 12 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error from if_w3_api_object~set_changeable. Subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_set_parameters.
    
        ii_resource->set_parameters(
          EXPORTING
            p_parameters          = it_parameters
          EXCEPTIONS
            object_not_changeable = 1
            object_deleted        = 2
            object_invalid        = 3
            authorize_failure     = 4
            invalid_parameter     = 5
            error_occured         = 6
            OTHERS                = 7 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error from if_w3_api_resource~set_parameters. Subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA ls_attributes TYPE w3resoattr.
    
        read( IMPORTING es_attributes = ls_attributes ).
    
        rv_user = ls_attributes-chname.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: li_resource TYPE REF TO if_w3_api_resource.
    
        li_resource = w3_api_load( ).
        w3_api_set_changeable( li_resource ).
        w3_api_delete( li_resource ).
        w3_api_save( li_resource ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_attr       TYPE w3resoattr,
              lt_parameters TYPE w3resopara_tabletype.
    
        io_xml->read( EXPORTING iv_name = 'ATTR'
                      CHANGING cg_data = ls_attr ).
        io_xml->read( EXPORTING iv_name = 'PARAMETERS'
                      CHANGING cg_data = lt_parameters ).
    
        ls_attr-devclass = iv_package.
        save( is_attributes       = ls_attr
              it_parameters = lt_parameters ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lx_error TYPE REF TO zcx_abapgit_exception.
    
        TRY.
            w3_api_load( ).
            rv_bool = abap_true.
    
          CATCH zcx_abapgit_exception INTO lx_error.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_attr       TYPE w3resoattr,
              lt_parameters TYPE w3resopara_tabletype.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        read( IMPORTING es_attributes       = ls_attr
                        et_parameters = lt_parameters ).
    
        io_xml->add( iv_name = 'ATTR'
                     ig_data = ls_attr ).
        io_xml->add( iv_name = 'PARAMETERS'
                     ig_data = lt_parameters ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_iasp IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_name = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD read.
    
        DATA: li_service TYPE REF TO if_w3_api_service.
    
        li_service = w3_api_load( ).
        es_attr = w3_api_get_attributes( li_service ).
    
        CLEAR: es_attr-chname,
               es_attr-tdate,
               es_attr-ttime,
               es_attr-devclass.
    
        et_parameters = w3_api_get_parameters( li_service ).
    
      ENDMETHOD.
    
      METHOD save.
    
        DATA: li_service TYPE REF TO if_w3_api_service.
    
        li_service = w3_api_create_new( is_attr ).
    
        w3_api_set_attributes(
            ii_service    = li_service
            is_attributes = is_attr ).
    
        w3_api_set_parameters(
            ii_service    = li_service
            it_parameters = it_parameters ).
    
        w3_api_save( li_service ).
    
        " Release locks
        w3_api_set_changeable(
          ii_service    = li_service
          iv_changeable = abap_false ).
    
      ENDMETHOD.
    
      METHOD w3_api_create_new.
    
        cl_w3_api_service=>if_w3_api_service~create_new(
          EXPORTING
            p_service_data = is_attributes
          IMPORTING
            p_service      = ri_service
          EXCEPTIONS
            object_already_existing = 1
            object_just_created     = 2
            not_authorized          = 3
            undefined_name          = 4
            author_not_existing     = 5
            action_cancelled        = 6
            error_occured           = 7
            invalid_parameter       = 8
            OTHERS                  = 9 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error from if_w3_api_service~create_new. Subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_delete.
    
        ii_service->if_w3_api_object~delete(
          EXCEPTIONS
            object_not_empty      = 1
            object_not_changeable = 2
            object_invalid        = 3
            error_occured         = 4
            OTHERS                = 5 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error from if_w3_api_object~delete. Subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_get_attributes.
    
        ii_service->get_attributes( IMPORTING p_attributes = rs_attributes ).
    
      ENDMETHOD.
    
      METHOD w3_api_get_parameters.
    
        ii_service->get_parameters( IMPORTING p_parameters = rt_parameters ).
    
      ENDMETHOD.
    
      METHOD w3_api_load.
    
        cl_w3_api_service=>if_w3_api_service~load(
          EXPORTING
            p_service_name     = mv_name
          IMPORTING
            p_service          = ri_service
          EXCEPTIONS
            object_not_existing = 1
            permission_failure  = 2
            error_occured       = 3
            OTHERS              = 4 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error from w3api_service~load' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_save.
    
        ii_service->if_w3_api_object~save(
          EXCEPTIONS
            object_invalid        = 1
            object_not_changeable = 2
            action_cancelled      = 3
            permission_failure    = 4
            not_changed           = 5
            data_invalid          = 6
            error_occured         = 7
            OTHERS                = 8 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error from if_w3_api_object~save. Subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_set_attributes.
    
        ii_service->set_attributes(
          EXPORTING
            p_attributes          = is_attributes
          EXCEPTIONS
            object_not_changeable = 1
            object_deleted        = 2
            object_invalid        = 3
            author_not_existing   = 4
            authorize_failure     = 5
            error_occured         = 6
            OTHERS                = 7 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error from if_w3_api_service~set_attributes. Subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_set_changeable.
    
        ii_service->if_w3_api_object~set_changeable(
          EXPORTING
            p_changeable                 = iv_changeable
          EXCEPTIONS
            action_cancelled             = 1
            object_locked_by_other_user  = 2
            permission_failure           = 3
            object_already_changeable    = 4
            object_already_unlocked      = 5
            object_just_created          = 6
            object_deleted               = 7
            object_modified              = 8
            object_not_existing          = 9
            object_invalid               = 10
            error_occured                = 11
            OTHERS                       = 12 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error from if_w3_api_object~set_changeable. Subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_set_parameters.
    
        ii_service->set_parameters(
          EXPORTING
            p_parameters          = it_parameters
          EXCEPTIONS
            object_not_changeable = 1
            object_deleted        = 2
            object_invalid        = 3
            authorize_failure     = 4
            invalid_parameter     = 5
            error_occured         = 6
            OTHERS                = 7 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error from if_w3_api_service~set_parameters. Subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        rv_user = c_user_unknown. " todo
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: li_service TYPE REF TO if_w3_api_service.
    
        li_service = w3_api_load( ).
    
        w3_api_set_changeable( li_service ).
        w3_api_delete( li_service ).
        w3_api_save( li_service ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_attr       TYPE w3servattr,
              lt_parameters TYPE w3servpara_tabletype.
    
        io_xml->read( EXPORTING iv_name = 'ATTR'
                      CHANGING cg_data = ls_attr ).
        io_xml->read( EXPORTING iv_name = 'PARAMETERS'
                      CHANGING cg_data = lt_parameters ).
    
        ls_attr-devclass = iv_package.
        save( is_attr       = ls_attr
              it_parameters = lt_parameters ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lx_error TYPE REF TO zcx_abapgit_exception.
    
        TRY.
            w3_api_load( ).
            rv_bool = abap_true.
    
          CATCH zcx_abapgit_exception INTO lx_error.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_attr       TYPE w3servattr,
              lt_parameters TYPE w3servpara_tabletype.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        read( IMPORTING es_attr       = ls_attr
                        et_parameters = lt_parameters ).
    
        io_xml->add( iv_name = 'ATTR'
                     ig_data = ls_attr ).
        io_xml->add( iv_name = 'PARAMETERS'
                     ig_data = lt_parameters ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_iatu IMPLEMENTATION.
    
      METHOD read.
    
        DATA: li_template TYPE REF TO if_w3_api_template,
              lt_source   TYPE w3htmltabtype,
              ls_name     TYPE iacikeyt.
    
        ls_name = ms_item-obj_name.
    
        li_template = w3_api_load( ls_name ).
    
        es_attr = w3_api_get_attributes( li_template ).
    
        CLEAR: es_attr-chname,
               es_attr-tdate,
               es_attr-ttime,
               es_attr-devclass.
    
        lt_source = w3_api_get_source( li_template ).
    
        CONCATENATE LINES OF lt_source INTO ev_source RESPECTING BLANKS.
    
      ENDMETHOD.
    
      METHOD save.
    
        DATA: lt_source   TYPE w3htmltabtype,
              lv_source   TYPE string,
              li_template TYPE REF TO if_w3_api_template.
    
        li_template = w3_api_create_new( is_attr ).
    
        w3_api_set_attributes( ii_template = li_template
                               is_attr     = is_attr ).
    
        lv_source = iv_source.
        WHILE strlen( lv_source ) >= 255.
          APPEND lv_source(255) TO lt_source.
          lv_source = lv_source+255.
        ENDWHILE.
        IF NOT lv_source IS INITIAL.
          APPEND lv_source TO lt_source.
        ENDIF.
    
        w3_api_set_source( ii_template = li_template
                           it_source   = lt_source ).
    
        w3_api_save( li_template ).
    
        " Release locks
        w3_api_set_changeable(
          ii_template   = li_template
          iv_changeable = abap_false ).
    
      ENDMETHOD.
    
      METHOD w3_api_create_new.
    
        cl_w3_api_template=>if_w3_api_template~create_new(
          EXPORTING
            p_template_data          = is_template_data
            p_program_name           = is_template_data-programm
          IMPORTING
            p_template               = ri_template
          EXCEPTIONS
            object_already_existing  = 1
            object_just_created      = 2
            not_authorized           = 3
            undefined_name           = 4
            author_not_existing      = 5
            action_cancelled         = 6
            error_occured            = 7
            user_error               = 8
            OTHERS                   = 9 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from w3_api_template~create_new subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_delete.
    
        ii_template->if_w3_api_object~delete(
          EXCEPTIONS
            object_not_empty      = 1
            object_not_changeable = 2
            object_invalid        = 3
            error_occured         = 4
            OTHERS                = 5 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from w3_api_template~delete subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_get_attributes.
    
        ii_template->get_attributes(
          IMPORTING
            p_attributes     = rs_attributes
          EXCEPTIONS
            object_invalid   = 1
            template_deleted = 2
            error_occured    = 3
            OTHERS           = 4 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from w3_api_template~get_attributes subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_get_source.
    
        ii_template->get_source(
          IMPORTING
            p_source         = rt_source
          EXCEPTIONS
            object_invalid   = 1
            template_deleted = 2
            error_occured    = 3
            OTHERS           = 4 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from w3_api_template~get_source subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_load.
    
        cl_w3_api_template=>if_w3_api_template~load(
          EXPORTING
            p_template_name     = is_name
          IMPORTING
            p_template          = ri_template
          EXCEPTIONS
            object_not_existing = 1
            permission_failure  = 2
            error_occured       = 3
            OTHERS              = 4 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from if_w3_api_template~load subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_save.
    
        ii_template->if_w3_api_object~save(
          EXCEPTIONS
            object_invalid        = 1
            object_not_changeable = 2
            action_cancelled      = 3
            permission_failure    = 4
            not_changed           = 5
            data_invalid          = 6
            error_occured         = 7
            OTHERS                = 8 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from w3_api_template~save subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_set_attributes.
    
        ii_template->set_attributes(
          EXPORTING
            p_attributes          = is_attr
          EXCEPTIONS
            object_not_changeable = 1
            object_deleted        = 2
            object_invalid        = 3
            author_not_existing   = 4
            authorize_failure     = 5
            error_occured         = 6
            OTHERS                = 7 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from w3_api_template~set_attributes subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_set_changeable.
    
        ii_template->if_w3_api_object~set_changeable(
          EXPORTING
            p_changeable                 = iv_changeable
          EXCEPTIONS
            action_cancelled             = 1
            object_locked_by_other_user  = 2
            permission_failure           = 3
            object_already_changeable    = 4
            object_already_unlocked      = 5
            object_just_created          = 6
            object_deleted               = 7
            object_modified              = 8
            object_not_existing          = 9
            object_invalid               = 10
            error_occured                = 11
            OTHERS                       = 12 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from w3_api_template~set_changeable subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_set_source.
    
        ii_template->set_source(
          EXPORTING
            p_source              = it_source
          EXCEPTIONS
            object_not_changeable = 1
            object_deleted        = 2
            object_invalid        = 3
            authorize_failure     = 4
            invalid_parameter     = 5
            error_occured         = 6
            OTHERS                = 7 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from w3_api_template~set_source subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA ls_attributes TYPE w3tempattr.
    
        read( IMPORTING es_attr = ls_attributes ).
    
        rv_user = ls_attributes-chname.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: li_template TYPE REF TO if_w3_api_template,
              ls_name     TYPE iacikeyt.
    
        ls_name = ms_item-obj_name.
    
        li_template = w3_api_load( ls_name ).
    
        w3_api_set_changeable( ii_template   = li_template
                               iv_changeable = abap_true ).
    
        w3_api_delete( li_template ).
    
        w3_api_save( li_template ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_attr   TYPE w3tempattr,
              lv_source TYPE string.
    
        io_xml->read( EXPORTING iv_name = 'ATTR'
                      CHANGING cg_data = ls_attr ).
    
        lv_source = mo_files->read_string( 'html' ).
    
        ls_attr-devclass = iv_package.
        save( is_attr   = ls_attr
              iv_source = lv_source ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: ls_name TYPE iacikeyt.
    
        ls_name = ms_item-obj_name.
    
        cl_w3_api_template=>s_check_exist( EXPORTING p_template_name = ls_name
                                           IMPORTING p_exists        = rv_bool ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_attr   TYPE w3tempattr,
              lv_source TYPE string.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        read( IMPORTING es_attr   = ls_attr
                        ev_source = lv_source ).
    
        io_xml->add( iv_name = 'ATTR'
                     ig_data = ls_attr ).
    
        mo_files->add_string(
          iv_ext    = 'html'
          iv_string = lv_source ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_iaxu IMPLEMENTATION.
    
      METHOD read.
    
        DATA: ls_name TYPE iacikeyt.
    
        ls_name = ms_item-obj_name.
    
        w3_api_load( EXPORTING is_name = ls_name
                     IMPORTING es_attr = rs_attr ).
    
        CLEAR: rs_attr-chname,
               rs_attr-tdate,
               rs_attr-ttime,
               rs_attr-devclass.
    
      ENDMETHOD.
    
      METHOD save.
    
        DATA: lo_xml_api TYPE REF TO object.
    
        lo_xml_api = w3_api_create_new( is_attr ).
    
        w3_api_save( lo_xml_api ).
    
        w3_api_set_changeable( io_xml_api    = lo_xml_api
                               iv_changeable = abap_false ).
    
      ENDMETHOD.
    
      METHOD w3_api_create_new.
    
        DATA: lr_xml_api TYPE REF TO data.
    
        FIELD-SYMBOLS:  TYPE any.
    
        CREATE DATA lr_xml_api TYPE REF TO ('CL_W3_API_XML3').
        ASSIGN lr_xml_api->* TO .
        ASSERT sy-subrc = 0.
    
        CALL METHOD ('CL_W3_API_XML3')=>create_new
          EXPORTING
            p_source_style_2006     = mv_source_style_2006
            p_xml_data              = is_attr
            p_generator_class       = mv_generator_class
            p_program_name          = is_attr-programm
          IMPORTING
            p_xml                   = 
          EXCEPTIONS
            undefined_name          = 1
            error_occured           = 2
            object_already_existing = 3
            not_authorized          = 4
            action_cancelled        = 5
            OTHERS                  = 6.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from w3_api_xml3~create_new subrc={ sy-subrc }| ).
        ENDIF.
    
        ro_xml_api ?= .
    
      ENDMETHOD.
    
      METHOD w3_api_delete.
    
        CALL METHOD io_xml_api->('IF_W3_API_OBJECT~DELETE')
          EXCEPTIONS
            object_not_empty      = 1
            object_not_changeable = 2
            object_invalid        = 3
            error_occured         = 4
            OTHERS                = 5.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from w3_api_xml3~delete subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_load.
    
        DATA: lr_xml_api TYPE REF TO data.
    
        FIELD-SYMBOLS:  TYPE any.
    
        CREATE DATA lr_xml_api TYPE REF TO ('CL_W3_API_XML3').
        ASSIGN lr_xml_api->* TO .
        ASSERT sy-subrc = 0.
    
        CALL METHOD ('CL_W3_API_XML3')=>load
          EXPORTING
            p_xml_name          = is_name
          IMPORTING
            p_attributes        = es_attr
            p_xml               = 
          EXCEPTIONS
            object_not_existing = 1
            permission_failure  = 2
            data_corrupt        = 3
            error_occured       = 4
            OTHERS              = 5.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from w3_api_xml3~load subrc={ sy-subrc }| ).
        ENDIF.
    
        eo_xml_api ?= .
    
      ENDMETHOD.
    
      METHOD w3_api_save.
    
        CALL METHOD io_xml_api->('IF_W3_API_OBJECT~SAVE')
          EXCEPTIONS
            object_invalid        = 1
            object_not_changeable = 2
            action_cancelled      = 3
            permission_failure    = 4
            not_changed           = 5
            data_invalid          = 6
            error_occured         = 7
            OTHERS                = 8.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from w3_api_xml3~save subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD w3_api_set_changeable.
    
        CALL METHOD io_xml_api->('IF_W3_API_OBJECT~SET_CHANGEABLE')
          EXPORTING
            p_changeable                = iv_changeable
          EXCEPTIONS
            action_cancelled            = 1
            object_locked_by_other_user = 2
            permission_failure          = 3
            object_already_changeable   = 4
            object_already_unlocked     = 5
            object_just_created         = 6
            object_deleted              = 7
            object_modified             = 8
            object_not_existing         = 9
            object_invalid              = 10
            error_occured               = 11
            OTHERS                      = 12.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from w3_api_xml3~set_changeable subrc={ sy-subrc }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        rv_user = read( )-chname.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lo_xml_api TYPE REF TO object,
              ls_name    TYPE iacikeyt.
    
        ls_name = ms_item-obj_name.
    
        w3_api_load( EXPORTING is_name    = ls_name
                     IMPORTING eo_xml_api = lo_xml_api ).
    
        w3_api_set_changeable( io_xml_api    = lo_xml_api
                               iv_changeable = abap_true ).
    
        w3_api_delete( lo_xml_api ).
    
        w3_api_save( lo_xml_api ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_attr TYPE w3tempattr.
    
        io_xml->read( EXPORTING iv_name = 'ATTR'
                      CHANGING  cg_data = ls_attr ).
    
        ls_attr-devclass = iv_package.
    
        IF zif_abapgit_object~exists( ) = abap_true.
          zif_abapgit_object~delete( iv_package   = iv_package
                                     iv_transport = iv_transport
                                     ii_log       = ii_log ).
        ENDIF.
    
        save( ls_attr ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: ls_name  TYPE iacikeyt.
    
        ls_name = ms_item-obj_name.
    
        CALL METHOD ('CL_W3_API_XML3')=>s_check_exist
          EXPORTING
            p_xml_name = ls_name
          IMPORTING
            p_exists   = rv_bool.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_attr TYPE w3tempattr.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        ls_attr = read( ).
    
        io_xml->add( iv_name = 'ATTR'
                     ig_data = ls_attr ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_idoc IMPLEMENTATION.
    
      METHOD clear_idoc_segement_field.
    
        FIELD-SYMBOLS  TYPE any.
    
        ASSIGN COMPONENT iv_fieldname OF STRUCTURE cg_structure TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
    
      ENDMETHOD.
    
      METHOD clear_idoc_segement_fields.
    
        clear_idoc_segement_field( EXPORTING iv_fieldname = 'DEVC'
                                   CHANGING  cg_structure = cg_structure ).
        clear_idoc_segement_field( EXPORTING iv_fieldname = 'PLAST'
                                   CHANGING  cg_structure = cg_structure ).
        clear_idoc_segement_field( EXPORTING iv_fieldname = 'PWORK'
                                   CHANGING  cg_structure = cg_structure ).
        clear_idoc_segement_field( EXPORTING iv_fieldname = 'PRESP'
                                   CHANGING  cg_structure = cg_structure ).
        clear_idoc_segement_field( EXPORTING iv_fieldname = 'CREDATE'
                                   CHANGING  cg_structure = cg_structure ).
        clear_idoc_segement_field( EXPORTING iv_fieldname = 'CRETIME'
                                   CHANGING  cg_structure = cg_structure ).
        clear_idoc_segement_field( EXPORTING iv_fieldname = 'LDATE'
                                   CHANGING  cg_structure = cg_structure ).
        clear_idoc_segement_field( EXPORTING iv_fieldname = 'LTIME'
                                   CHANGING  cg_structure = cg_structure ).
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_idoctyp = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD is_closed.
    
        DATA ls_idoc TYPE ty_idoc.
    
        CALL FUNCTION 'IDOCTYPE_READ'
          EXPORTING
            pi_idoctyp       = mv_idoctyp
          IMPORTING
            pe_attributes    = ls_idoc-attributes
          EXCEPTIONS
            object_not_found = 1
            db_error         = 2
            no_authority     = 3
            OTHERS           = 4.
        rv_closed = boolc( sy-subrc = 0 AND ls_idoc-attributes-closed = abap_true ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: ls_attributes TYPE edi_iapi01.
    
        CALL FUNCTION 'IDOCTYPE_READ'
          EXPORTING
            pi_idoctyp       = mv_idoctyp
          IMPORTING
            pe_attributes    = ls_attributes
          EXCEPTIONS
            object_not_found = 1
            db_error         = 2
            no_authority     = 3
            OTHERS           = 4.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        rv_user = ls_attributes-plast.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        CALL FUNCTION 'IDOCTYPE_DELETE'
          EXPORTING
            pi_idoctyp          = mv_idoctyp
          EXCEPTIONS
            object_not_found    = 1
            lock_error          = 2
            action_not_possible = 3
            transport_error     = 4
            db_error            = 5
            no_authority        = 6
            OTHERS              = 7.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_idoc       TYPE ty_idoc,
              lv_transport  TYPE trkorr,
              ls_edbas      TYPE edbas,
              ls_attributes TYPE edi_iapi05.
    
        io_xml->read(
          EXPORTING
            iv_name = 'IDOC'
          CHANGING
            cg_data = ls_idoc ).
    
        MOVE-CORRESPONDING ls_idoc-attributes TO ls_attributes.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          " Avoid popup asking for package
          tadir_insert( iv_package ).
    
          CALL FUNCTION 'IDOCTYPE_CREATE'
            EXPORTING
              pi_idoctyp       = mv_idoctyp
              pi_devclass      = iv_package
              pi_attributes    = ls_attributes
            TABLES
              pt_syntax        = ls_idoc-t_syntax
            EXCEPTIONS
              object_not_found = 1
              object_exists    = 2
              syntax_error     = 3
              segment_error    = 4
              transport_error  = 5
              db_error         = 6
              no_authority     = 7
              OTHERS           = 8.
        ELSE.
          IF is_closed( ) = abap_true.
            CALL FUNCTION 'IDOCTYPE_UNCLOSE'
              EXPORTING
                pi_idoctyp          = mv_idoctyp
              EXCEPTIONS
                object_not_found    = 1
                action_not_possible = 2
                db_error            = 3
                no_authority        = 4
                OTHERS              = 5.
            IF sy-subrc <> 0.
              zcx_abapgit_exception=>raise_t100( ).
            ENDIF.
          ENDIF.
    
          CALL FUNCTION 'IDOCTYPE_UPDATE'
            EXPORTING
              pi_idoctyp       = mv_idoctyp
              pi_attributes    = ls_attributes
            TABLES
              pt_syntax        = ls_idoc-t_syntax
            EXCEPTIONS
              object_not_found = 1
              object_exists    = 2
              syntax_error     = 3
              segment_error    = 4
              transport_error  = 5
              db_error         = 6
              no_authority     = 7
              OTHERS           = 8.
        ENDIF.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        IF ls_idoc-attributes-closed = abap_true.
          IF iv_transport IS NOT INITIAL.
            lv_transport = iv_transport.
    
            CALL FUNCTION 'IDOCTYPE_CLOSE'
              EXPORTING
                pi_idoctyp          = mv_idoctyp
              CHANGING
                pc_order            = lv_transport
              EXCEPTIONS
                object_not_found    = 1
                action_not_possible = 2
                db_error            = 3
                no_authority        = 4
                OTHERS              = 5.
            IF sy-subrc <> 0.
              zcx_abapgit_exception=>raise_t100( ).
            ENDIF.
          ENDIF.
    
          " IDOCTYPE_CLOSE saves current release but it should be same as in repo
          SELECT SINGLE * FROM edbas INTO ls_edbas WHERE idoctyp = mv_idoctyp.
          ls_edbas-released = ls_idoc-attributes-released.
          ls_edbas-applrel  = ls_idoc-attributes-applrel.
          ls_edbas-closed   = ls_idoc-attributes-closed.
          UPDATE edbas FROM ls_edbas.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( |Error updating IDOC { mv_idoctyp }| ).
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        CALL FUNCTION 'IDOCTYPE_EXISTENCE_CHECK'
          EXPORTING
            pi_idoctyp       = mv_idoctyp
          EXCEPTIONS
            object_not_found = 1
            db_error         = 2
            OTHERS           = 3.
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: lt_bdcdata TYPE TABLE OF bdcdata.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_bdcdata.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -program  = 'SAPMSED5'.
        -dynpro   = '0010'.
        -dynbegin = abap_true.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'SED5STRUC-OBJECT'.
        -fval = ms_item-obj_name.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'SED5STRUC-SELECT_ORG'.
        -fval = abap_true.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'BDC_OKCODE'.
        -fval = '=DISP'.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'WE30'
          it_bdcdata = lt_bdcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_idoc TYPE ty_idoc.
    
        CALL FUNCTION 'IDOCTYPE_READ'
          EXPORTING
            pi_idoctyp       = mv_idoctyp
          IMPORTING
            pe_attributes    = ls_idoc-attributes
          TABLES
            pt_syntax        = ls_idoc-t_syntax
          EXCEPTIONS
            object_not_found = 1
            db_error         = 2
            no_authority     = 3
            OTHERS           = 4.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        clear_idoc_segement_fields( CHANGING cg_structure = ls_idoc-attributes ).
    
        io_xml->add( iv_name = 'IDOC'
                     ig_data = ls_idoc ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_iext IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_extension = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: ls_attributes TYPE edi_iapi01.
    
        CALL FUNCTION 'EXTTYPE_READ'
          EXPORTING
            pi_cimtyp     = mv_extension
          IMPORTING
            pe_attributes = ls_attributes
          EXCEPTIONS
            OTHERS        = 1.
        IF sy-subrc = 0.
          rv_user = ls_attributes-plast.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        CALL FUNCTION 'EXTTYPE_DELETE'
          EXPORTING
            pi_cimtyp = mv_extension
          EXCEPTIONS
            OTHERS    = 1.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_extension  TYPE ty_extention,
              ls_attributes TYPE edi_iapi05.
    
        io_xml->read( EXPORTING iv_name = c_dataname_iext
                      CHANGING  cg_data = ls_extension ).
    
        MOVE-CORRESPONDING ls_extension-attributes TO ls_attributes.
        ls_attributes-presp = sy-uname.
        ls_attributes-pwork = ls_attributes-presp.
    
        IF zif_abapgit_object~exists( ) = abap_true.
          CALL FUNCTION 'EXTTYPE_UPDATE'
            EXPORTING
              pi_cimtyp     = mv_extension
              pi_attributes = ls_attributes
            TABLES
              pt_syntax     = ls_extension-t_syntax
            EXCEPTIONS
              OTHERS        = 1.
        ELSE.
          " Avoid popup asking for package
          tadir_insert( iv_package ).
    
          CALL FUNCTION 'EXTTYPE_CREATE'
            EXPORTING
              pi_cimtyp     = mv_extension
              pi_devclass   = iv_package
              pi_attributes = ls_attributes
            TABLES
              pt_syntax     = ls_extension-t_syntax
            EXCEPTIONS
              OTHERS        = 1.
        ENDIF.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        CALL FUNCTION 'EXTTYPE_READ'
          EXPORTING
            pi_cimtyp = mv_extension
          EXCEPTIONS
            OTHERS    = 1.
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: lt_bdcdata TYPE TABLE OF bdcdata.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_bdcdata.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -program  = 'SAPMSED5'.
        -dynpro   = '0010'.
        -dynbegin = abap_true.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'SED5STRUC-OBJECT'.
        -fval = ms_item-obj_name.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'SED5STRUC-SELECT_EXT'.
        -fval = abap_true.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'BDC_OKCODE'.
        -fval = '=DISP'.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'WE30'
          it_bdcdata = lt_bdcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA ls_extension           TYPE ty_extention.
    
        CALL FUNCTION 'EXTTYPE_READ'
          EXPORTING
            pi_cimtyp     = mv_extension
          IMPORTING
            pe_attributes = ls_extension-attributes
          TABLES
            pt_syntax     = ls_extension-t_syntax
          EXCEPTIONS
            OTHERS        = 1.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        zcl_abapgit_object_idoc=>clear_idoc_segement_fields( CHANGING cg_structure = ls_extension-attributes ).
    
        io_xml->add( iv_name = c_dataname_iext
                     ig_data = ls_extension ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS lcl_aff_helper DEFINITION.
      PUBLIC SECTION.
        CLASS-METHODS:
          get_descriptions_compo_subco
            IMPORTING iv_language          TYPE sy-langu
                      iv_clif_name         TYPE seoclsname
            RETURNING VALUE(rs_properties) TYPE zif_abapgit_aff_oo_types_v1=>ty_descriptions ,
          get_descr_comp_subc_w_exposure
            IMPORTING iv_language          TYPE sy-langu
                      iv_clif_name         TYPE seoclsname
                      iv_exposure          TYPE seoexpose DEFAULT seoc_exposure_public
            RETURNING VALUE(rs_properties) TYPE zif_abapgit_aff_oo_types_v1=>ty_descriptions ,
          set_descriptions_compo_subco
            IMPORTING iv_clif_name  TYPE seoclsname
                      iv_language   TYPE langu
                      is_properties TYPE zif_abapgit_aff_oo_types_v1=>ty_descriptions .
      PRIVATE SECTION.
        TYPES:
          BEGIN OF ty_component,
            visibility TYPE seoexpose,
            cmpname    TYPE seocmpname,
            descript   TYPE seodescr,
            cmptype    TYPE seocmptype,
          END OF ty_component,
          BEGIN OF ty_sub_component,
            cmpname  TYPE seocmpname,
            sconame  TYPE seosconame,
            descript TYPE seodescr,
            scotype  TYPE seoscotype,
          END OF ty_sub_component,
          ty_compontents     TYPE SORTED TABLE OF ty_component WITH UNIQUE DEFAULT KEY,
          ty_sub_compontents TYPE SORTED TABLE OF ty_sub_component WITH UNIQUE DEFAULT KEY.
    
        CLASS-METHODS:
          get_attributes
            IMPORTING is_components    TYPE ty_compontents
            RETURNING VALUE(rs_result) TYPE zif_abapgit_aff_oo_types_v1=>ty_component_descriptions,
          get_methods
            IMPORTING is_components     TYPE ty_compontents
                      is_sub_components TYPE ty_sub_compontents
            RETURNING VALUE(rs_result)  TYPE zif_abapgit_aff_oo_types_v1=>ty_methods,
          get_types
            IMPORTING is_components    TYPE ty_compontents
            RETURNING VALUE(rs_result) TYPE zif_abapgit_aff_oo_types_v1=>ty_component_descriptions,
          get_events
            IMPORTING is_components     TYPE ty_compontents
                      is_sub_components TYPE ty_sub_compontents
            RETURNING VALUE(rs_result)  TYPE zif_abapgit_aff_oo_types_v1=>ty_events,
          set_methods
            IMPORTING iv_clif_name  TYPE seoclsname
                      iv_language   TYPE langu
                      is_properties TYPE zif_abapgit_aff_oo_types_v1=>ty_descriptions,
          set_attributes
            IMPORTING iv_clif_name  TYPE seoclsname
                      iv_language   TYPE langu
                      is_properties TYPE zif_abapgit_aff_oo_types_v1=>ty_descriptions,
          set_events
            IMPORTING iv_clif_name  TYPE seoclsname
                      iv_language   TYPE langu
                      is_properties TYPE zif_abapgit_aff_oo_types_v1=>ty_descriptions,
          set_types
            IMPORTING iv_clif_name  TYPE seoclsname
                      iv_language   TYPE langu
                      is_properties TYPE zif_abapgit_aff_oo_types_v1=>ty_descriptions .
    ENDCLASS.
    
    CLASS lcl_aff_helper IMPLEMENTATION.
    
      METHOD get_descr_comp_subc_w_exposure.
        DATA:
          lt_components     TYPE ty_compontents,
          lt_sub_components TYPE ty_sub_compontents.
    
        SELECT df~exposure AS visibility component~cmpname component_text~descript component~cmptype
          INTO TABLE lt_components
          FROM seocompo AS component
          LEFT OUTER JOIN seocompotx AS component_text
          ON component~cmpname = component_text~cmpname AND component~clsname = component_text~clsname AND
             component_text~langu = iv_language
          INNER JOIN seocompodf AS df
          ON component~clsname = df~clsname AND
             component~cmpname = df~cmpname
          WHERE component~clsname = iv_clif_name AND
                df~exposure       = iv_exposure.           "#EC CI_BUFFJOIN
    
        SELECT sub_component~cmpname sub_component~sconame sub_component_text~descript sub_component~scotype
          INTO TABLE lt_sub_components
          FROM seosubco AS sub_component JOIN seosubcotx AS sub_component_text
          ON sub_component~clsname = sub_component_text~clsname AND
             sub_component~cmpname = sub_component_text~cmpname AND
             sub_component~sconame = sub_component_text~sconame
          INNER JOIN seocompodf AS df
          ON sub_component~clsname = df~clsname AND
             sub_component~cmpname = df~cmpname
          WHERE sub_component~clsname    = iv_clif_name
            AND df~exposure              = iv_exposure
            AND sub_component_text~langu = iv_language
            AND sub_component_text~descript <> space.      "#EC CI_BUFFJOIN
    
        rs_properties-attributes = get_attributes( lt_components ).
        rs_properties-methods = get_methods( is_components     = lt_components
                                             is_sub_components = lt_sub_components ).
        rs_properties-events = get_events( is_components     = lt_components
                                           is_sub_components = lt_sub_components ).
        rs_properties-types = get_types( lt_components ).
      ENDMETHOD.
    
      METHOD get_descriptions_compo_subco.
        TYPES:
          BEGIN OF ty_helper_type,
            cmpname  TYPE seocmpname,
            descript TYPE seodescr,
            cmptype  TYPE seocmptype,
          END OF ty_helper_type.
        DATA:
          lt_components     TYPE STANDARD TABLE OF ty_helper_type,
          lt_sub_components TYPE ty_sub_compontents,
          lt_components_exp TYPE ty_compontents,
          ls_component_exp  LIKE LINE OF lt_components_exp.
        FIELD-SYMBOLS:
           LIKE LINE OF lt_components.
    
        SELECT component~cmpname component_text~descript component~cmptype
          INTO TABLE lt_components
          FROM seocompo AS component
          LEFT OUTER JOIN seocompotx AS component_text
          ON component~cmpname = component_text~cmpname AND component~clsname    = component_text~clsname
                                                        AND component_text~langu = iv_language
          WHERE component~clsname = iv_clif_name
          ORDER BY component~cmpname.                      "#EC CI_BUFFJOIN
    
        SELECT sub_component~cmpname sub_component~sconame sub_component_text~descript sub_component~scotype
          INTO TABLE lt_sub_components
          FROM seosubco AS sub_component JOIN seosubcotx AS sub_component_text
          ON sub_component~clsname      = sub_component_text~clsname
              AND sub_component~cmpname = sub_component_text~cmpname
              AND sub_component~sconame = sub_component_text~sconame
          WHERE sub_component~clsname    = iv_clif_name
            AND sub_component_text~langu = iv_language
            AND sub_component_text~descript <> space.      "#EC CI_BUFFJOIN
    
        LOOP AT lt_components ASSIGNING .
          CLEAR ls_component_exp.
          MOVE-CORRESPONDING  TO ls_component_exp.
          INSERT ls_component_exp INTO TABLE lt_components_exp.
        ENDLOOP.
    
        rs_properties-attributes = get_attributes( lt_components_exp ).
        rs_properties-methods = get_methods( is_components     = lt_components_exp
                                             is_sub_components = lt_sub_components ).
        rs_properties-events = get_events( is_components     = lt_components_exp
                                           is_sub_components = lt_sub_components ).
        rs_properties-types = get_types( lt_components_exp ).
    
      ENDMETHOD.
    
      METHOD get_attributes.
        DATA:
          lo_component TYPE zif_abapgit_aff_oo_types_v1=>ty_component_description.
        FIELD-SYMBOLS  TYPE ty_component.
    
        LOOP AT is_components ASSIGNING  WHERE cmptype = seoo_cmptype_attribute AND descript IS NOT INITIAL.
          lo_component-name = -cmpname.
          lo_component-description = -descript.
          INSERT lo_component INTO TABLE rs_result.
        ENDLOOP.
      ENDMETHOD.
    
      METHOD get_methods.
        DATA:
          lo_method    TYPE zif_abapgit_aff_oo_types_v1=>ty_method,
          lo_exception TYPE zif_abapgit_aff_oo_types_v1=>ty_component_description,
          lo_parameter TYPE zif_abapgit_aff_oo_types_v1=>ty_component_description.
    
        FIELD-SYMBOLS  TYPE ty_sub_component.
        FIELD-SYMBOLS  TYPE ty_component.
    
        LOOP AT is_components ASSIGNING  WHERE cmptype = seoo_cmptype_method.
          lo_method-name = -cmpname.
          lo_method-description = -descript.
    
          LOOP AT is_sub_components ASSIGNING  WHERE cmpname = -cmpname.
            CASE -scotype.
              WHEN seos_scotype_parameter.
                lo_parameter-name = -sconame.
                lo_parameter-description = -descript.
                INSERT lo_parameter INTO TABLE lo_method-parameters.
              WHEN seos_scotype_exception.
                lo_exception-name = -sconame.
                lo_exception-description = -descript.
                INSERT lo_exception INTO TABLE lo_method-exceptions.
            ENDCASE.
          ENDLOOP.
    
          IF lo_method-description IS NOT INITIAL
              OR lo_method-exceptions IS NOT INITIAL
              OR lo_method-parameters IS NOT INITIAL.
            INSERT lo_method INTO TABLE rs_result.
          ENDIF.
        ENDLOOP.
      ENDMETHOD.
    
      METHOD get_types.
        DATA:
            lo_type TYPE zif_abapgit_aff_oo_types_v1=>ty_component_description.
        FIELD-SYMBOLS:  TYPE ty_component.
    
        LOOP AT is_components ASSIGNING 
            WHERE cmptype = seoo_cmptype_type AND descript IS NOT INITIAL.
          lo_type-name = -cmpname.
          lo_type-description = -descript.
          INSERT lo_type INTO TABLE rs_result.
        ENDLOOP.
      ENDMETHOD.
    
      METHOD get_events.
        DATA:
          lo_parameter TYPE zif_abapgit_aff_oo_types_v1=>ty_component_description,
          lo_event     TYPE zif_abapgit_aff_oo_types_v1=>ty_event.
        FIELD-SYMBOLS  TYPE ty_component.
        FIELD-SYMBOLS  TYPE ty_sub_component.
    
        LOOP AT is_components ASSIGNING  WHERE cmptype = seoo_cmptype_event.
          lo_event-name = -cmpname.
          lo_event-description = -descript.
    
          LOOP AT is_sub_components ASSIGNING  WHERE cmpname = -cmpname.
            lo_parameter-name = -sconame.
            lo_parameter-description = -descript.
            INSERT lo_parameter INTO TABLE lo_event-parameters.
          ENDLOOP.
    
          IF lo_event-description IS NOT INITIAL OR lo_event-parameters IS NOT INITIAL.
            INSERT lo_event INTO TABLE rs_result.
          ENDIF.
        ENDLOOP.
      ENDMETHOD.
    
      METHOD set_attributes.
        DATA:
          lo_attribute TYPE seocompotx.
        FIELD-SYMBOLS:  TYPE zif_abapgit_aff_oo_types_v1=>ty_component_description.
    
        LOOP AT is_properties-attributes ASSIGNING .
          lo_attribute-clsname  = iv_clif_name.
          lo_attribute-cmpname  = -name.
          lo_attribute-langu    = iv_language.
          lo_attribute-descript = -description.
          MODIFY seocompotx FROM lo_attribute.
        ENDLOOP.
      ENDMETHOD.
    
      METHOD set_methods.
        DATA:
          lo_method           TYPE seocompotx,
          lo_method_exception TYPE seosubcotx,
          lo_method_parameter TYPE seosubcotx.
        FIELD-SYMBOLS:     TYPE zif_abapgit_aff_oo_types_v1=>ty_method,
                        TYPE zif_abapgit_aff_oo_types_v1=>ty_component_description,
                        TYPE zif_abapgit_aff_oo_types_v1=>ty_component_description.
    
        LOOP AT is_properties-methods ASSIGNING .
          lo_method-clsname  = iv_clif_name.
          lo_method-cmpname  = -name.
          lo_method-langu    = iv_language.
          lo_method-descript = -description.
          MODIFY seocompotx FROM lo_method.
    
          LOOP AT -parameters ASSIGNING .
            lo_method_parameter-clsname  = iv_clif_name.
            lo_method_parameter-cmpname  = -name.
            lo_method_parameter-sconame  = -name.
            lo_method_parameter-langu    = iv_language.
            lo_method_parameter-descript = -description.
            MODIFY seosubcotx FROM lo_method_parameter.
          ENDLOOP.
    
          LOOP AT -exceptions ASSIGNING .
            lo_method_exception-clsname  = iv_clif_name.
            lo_method_exception-cmpname  = -name.
            lo_method_exception-sconame  = -name.
            lo_method_exception-langu    = iv_language.
            lo_method_exception-descript = -description.
            MODIFY seosubcotx FROM lo_method_exception.
          ENDLOOP.
        ENDLOOP.
      ENDMETHOD.
    
      METHOD set_events.
        DATA:
          lo_event_parameter TYPE seosubcotx,
          lo_event           TYPE seocompotx.
        FIELD-SYMBOLS:      TYPE zif_abapgit_aff_oo_types_v1=>ty_event,
                        TYPE zif_abapgit_aff_oo_types_v1=>ty_component_description.
    
        LOOP AT is_properties-events ASSIGNING .
          lo_event-clsname  = iv_clif_name.
          lo_event-cmpname  = -name.
          lo_event-langu    = iv_language.
          lo_event-descript = -description.
          MODIFY seocompotx FROM lo_event.
    
          LOOP AT -parameters ASSIGNING .
            lo_event_parameter-clsname  = iv_clif_name.
            lo_event_parameter-cmpname  = -name.
            lo_event_parameter-sconame  = -name.
            lo_event_parameter-langu    = iv_language.
            lo_event_parameter-descript = -description.
            MODIFY seosubcotx FROM lo_event_parameter.
          ENDLOOP.
        ENDLOOP.
      ENDMETHOD.
    
      METHOD set_types.
        DATA:
          lo_type TYPE seocompotx.
        FIELD-SYMBOLS:  TYPE zif_abapgit_aff_oo_types_v1=>ty_component_description.
    
        LOOP AT is_properties-types ASSIGNING .
          lo_type-clsname  = iv_clif_name.
          lo_type-cmpname  = -name.
          lo_type-langu    = iv_language.
          lo_type-descript = -description.
          MODIFY seocompotx FROM lo_type.
        ENDLOOP.
      ENDMETHOD.
    
      METHOD set_descriptions_compo_subco.
        set_attributes( is_properties = is_properties
                        iv_clif_name  = iv_clif_name
                        iv_language   = iv_language ).
        set_methods( is_properties = is_properties
                     iv_clif_name  = iv_clif_name
                     iv_language   = iv_language ).
        set_events( is_properties = is_properties
                    iv_clif_name  = iv_clif_name
                    iv_language   = iv_language ).
        set_types( is_properties = is_properties
                   iv_clif_name  = iv_clif_name
                   iv_language   = iv_language ).
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS lcl_aff_type_mapping DEFINITION.
      PUBLIC SECTION.
        INTERFACES zif_abapgit_aff_type_mapping.
      PRIVATE SECTION.
        METHODS set_abapgit_descriptions
          IMPORTING is_clsname          TYPE seoclsname
                    is_intf_aff         TYPE zif_abapgit_aff_intf_v1=>ty_main
          EXPORTING et_descriptions     TYPE zif_abapgit_oo_object_fnc=>ty_seocompotx_tt
                    et_descriptions_sub TYPE zif_abapgit_oo_object_fnc=>ty_seosubcotx_tt.
    ENDCLASS.
    
    CLASS lcl_aff_type_mapping IMPLEMENTATION.
    
      METHOD zif_abapgit_aff_type_mapping~to_aff.
        DATA:
          ls_data_abapgit TYPE zcl_abapgit_object_intf=>ty_intf,
          ls_data_aff     TYPE zif_abapgit_aff_intf_v1=>ty_main.
    
        ls_data_abapgit = iv_data.
    
        ls_data_aff-format_version = '1'.
    
        " get header
        ls_data_aff-header-description = ls_data_abapgit-vseointerf-descript.
        ls_data_aff-header-abap_language_version = ls_data_abapgit-vseointerf-unicode.
        ls_data_aff-header-original_language = ls_data_abapgit-vseointerf-langu.
    
        " get category and proxy
        ls_data_aff-category = ls_data_abapgit-vseointerf-category.
        ls_data_aff-proxy = ls_data_abapgit-vseointerf-clsproxy.
    
        " get descriptions
        ls_data_aff-descriptions = lcl_aff_helper=>get_descriptions_compo_subco(
          iv_language  = ls_data_aff-header-original_language
          iv_clif_name = ls_data_abapgit-vseointerf-clsname ).
    
        es_data = ls_data_aff.
      ENDMETHOD.
    
      METHOD zif_abapgit_aff_type_mapping~to_abapgit.
        DATA:
          ls_data_abapgit TYPE zcl_abapgit_object_intf=>ty_intf,
          ls_data_aff     TYPE zif_abapgit_aff_intf_v1=>ty_main,
          lv_classname    TYPE seoclsname.
    
        ls_data_aff = iv_data.
    
        lv_classname = to_upper( iv_object_name ).
    
        set_abapgit_descriptions( EXPORTING is_clsname          = lv_classname
                                            is_intf_aff         = ls_data_aff
                                  IMPORTING et_descriptions     = ls_data_abapgit-description
                                            et_descriptions_sub = ls_data_abapgit-description_sub ).
    
        ls_data_abapgit-vseointerf-clsname  = lv_classname.
        ls_data_abapgit-vseointerf-descript = ls_data_aff-header-description.
        ls_data_abapgit-vseointerf-category = ls_data_aff-category.
        ls_data_abapgit-vseointerf-unicode  = ls_data_aff-header-abap_language_version.
        ls_data_abapgit-vseointerf-langu    = ls_data_aff-header-original_language.
        ls_data_abapgit-vseointerf-clsproxy = ls_data_aff-proxy.
        ls_data_abapgit-vseointerf-exposure = seoc_exposure_public.
        ls_data_abapgit-vseointerf-state    = seoc_state_implemented.
    
        es_data = ls_data_abapgit.
    
      ENDMETHOD.
    
      METHOD set_abapgit_descriptions.
    
        DATA ls_description       TYPE seocompotx.
        DATA ls_description_subco TYPE seosubcotx.
        FIELD-SYMBOLS       TYPE zif_abapgit_aff_oo_types_v1=>ty_component_description.
        FIELD-SYMBOLS  TYPE zif_abapgit_aff_oo_types_v1=>ty_method.
        FIELD-SYMBOLS   TYPE zif_abapgit_aff_oo_types_v1=>ty_event.
    
        LOOP AT is_intf_aff-descriptions-types ASSIGNING .
          ls_description-clsname  = is_clsname.
          ls_description-cmpname  = -name.
          ls_description-langu    = is_intf_aff-header-original_language.
          ls_description-descript = -description.
          APPEND ls_description TO et_descriptions.
        ENDLOOP.
    
        LOOP AT is_intf_aff-descriptions-attributes ASSIGNING .
          ls_description-clsname  = is_clsname.
          ls_description-cmpname  = -name.
          ls_description-langu    = is_intf_aff-header-original_language.
          ls_description-descript = -description.
          APPEND ls_description TO et_descriptions.
        ENDLOOP.
    
        LOOP AT is_intf_aff-descriptions-methods ASSIGNING .
          ls_description-clsname  = is_clsname.
          ls_description-cmpname  = -name.
          ls_description-langu    = is_intf_aff-header-original_language.
          ls_description-descript = -description.
          APPEND ls_description TO et_descriptions.
    
          LOOP AT -parameters ASSIGNING .
            ls_description_subco-clsname  = ls_description-clsname.
            ls_description_subco-cmpname  = ls_description-cmpname.
            ls_description_subco-langu    = ls_description-langu.
            ls_description_subco-sconame  = -name.
            ls_description_subco-descript = -description.
            APPEND ls_description_subco TO et_descriptions_sub.
          ENDLOOP.
    
          LOOP AT -exceptions ASSIGNING .
            ls_description_subco-clsname  = ls_description-clsname.
            ls_description_subco-cmpname  = ls_description-cmpname.
            ls_description_subco-langu    = ls_description-langu.
            ls_description_subco-sconame  = -name.
            ls_description_subco-descript = -description.
            APPEND ls_description_subco TO et_descriptions_sub.
          ENDLOOP.
        ENDLOOP.
    
        LOOP AT is_intf_aff-descriptions-events ASSIGNING .
          ls_description-clsname  = is_clsname.
          ls_description-cmpname  = -name.
          ls_description-langu    = is_intf_aff-header-original_language.
          ls_description-descript = -description.
          APPEND ls_description TO et_descriptions.
    
          LOOP AT -parameters ASSIGNING .
            ls_description_subco-clsname  = ls_description-clsname.
            ls_description_subco-cmpname  = ls_description-cmpname.
            ls_description_subco-langu    = ls_description-langu.
            ls_description_subco-sconame  = -name.
            ls_description_subco-descript = -description.
            APPEND ls_description_subco TO et_descriptions_sub.
          ENDLOOP.
        ENDLOOP.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS lcl_aff_metadata_handler DEFINITION.
      PUBLIC SECTION.
    
        CLASS-METHODS serialize
          IMPORTING is_intf          TYPE zcl_abapgit_object_intf=>ty_intf
          RETURNING VALUE(rv_result) TYPE xstring
          RAISING   zcx_abapgit_exception.
        CLASS-METHODS serialize_translations
          IMPORTING is_intf          TYPE zcl_abapgit_object_intf=>ty_intf
                    it_language      TYPE zif_abapgit_definitions=>ty_languages
          RETURNING VALUE(rt_result) TYPE zif_abapgit_i18n_file=>ty_table_of
          RAISING   zcx_abapgit_exception.
        CLASS-METHODS deserialize
          IMPORTING iv_data          TYPE string
          RETURNING VALUE(rv_result) TYPE zif_abapgit_aff_intf_v1=>ty_main
          RAISING   zcx_abapgit_exception.
        CLASS-METHODS deserialize_translation
          IMPORTING io_files           TYPE REF TO zcl_abapgit_objects_files
                    is_item            TYPE zif_abapgit_definitions=>ty_item
          EXPORTING et_description_int TYPE zcl_abapgit_object_intf=>ty_intf-description_int
                    et_description     TYPE zcl_abapgit_object_intf=>ty_intf-description
                    et_description_sub TYPE zcl_abapgit_object_intf=>ty_intf-description_sub
          RAISING   zcx_abapgit_exception.
      PRIVATE SECTION.
        CLASS-METHODS:
          "! For serialization
          "! @parameter rt_result | Map/table that associates ABAP values to JSON values (enums)
          get_mappings
            RETURNING VALUE(rt_result) TYPE zcl_abapgit_json_handler=>ty_enum_mappings,
          "! For serialization
          "! @parameter rt_result | Paths that will not be serialized (depending on value)
          get_paths_to_skip
            RETURNING VALUE(rt_result) TYPE zcl_abapgit_json_handler=>ty_skip_paths,
          fill_translation
            IMPORTING iv_name          TYPE seoclsname
                      iv_language      TYPE laiso
            RETURNING VALUE(rt_result) TYPE zif_abapgit_aff_intf_v1=>ty_main.
    ENDCLASS.
    
    CLASS lcl_aff_metadata_handler IMPLEMENTATION.
    
      METHOD serialize.
        DATA:
          ls_data_aff      TYPE zif_abapgit_aff_intf_v1=>ty_main,
          lx_exception     TYPE REF TO cx_root,
          lo_aff_handler   TYPE REF TO zcl_abapgit_json_handler,
          lo_aff_mapper    TYPE REF TO zif_abapgit_aff_type_mapping,
          lt_enum_mappings TYPE zcl_abapgit_json_handler=>ty_enum_mappings,
          lt_paths_to_skip TYPE zcl_abapgit_json_handler=>ty_skip_paths.
    
        CREATE OBJECT lo_aff_mapper TYPE lcl_aff_type_mapping.
        lo_aff_mapper->to_aff( EXPORTING iv_data = is_intf
                               IMPORTING es_data = ls_data_aff ).
    
        lt_enum_mappings = get_mappings( ).
        lt_paths_to_skip = get_paths_to_skip( ).
    
        CREATE OBJECT lo_aff_handler.
        TRY.
            rv_result = lo_aff_handler->serialize( iv_data          = ls_data_aff
                                                   iv_enum_mappings = lt_enum_mappings
                                                   iv_skip_paths    = lt_paths_to_skip ).
          CATCH cx_root INTO lx_exception.
            zcx_abapgit_exception=>raise_with_text( lx_exception ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get_mappings.
        DATA:
          ls_category_mapping   TYPE zcl_abapgit_json_handler=>ty_enum_mapping,
          ls_json_abap_mapping  TYPE zcl_abapgit_json_handler=>ty_json_abap_mapping,
          lt_json_abap_mappings TYPE zcl_abapgit_json_handler=>ty_json_abap_mappings.
    
        ls_json_abap_mapping-abap = zif_abapgit_aff_intf_v1=>co_category-general.
        ls_json_abap_mapping-json = 'standard'.
        APPEND ls_json_abap_mapping TO lt_json_abap_mappings.
        ls_json_abap_mapping-abap = zif_abapgit_aff_intf_v1=>co_category-classic_badi.
        ls_json_abap_mapping-json = 'classicBadi'.
        APPEND ls_json_abap_mapping TO lt_json_abap_mappings.
        ls_json_abap_mapping-abap = zif_abapgit_aff_intf_v1=>co_category-business_static_components.
        ls_json_abap_mapping-json = 'businessStaticComponents'.
        APPEND ls_json_abap_mapping TO lt_json_abap_mappings.
        ls_json_abap_mapping-abap = zif_abapgit_aff_intf_v1=>co_category-db_procedure_proxy.
        ls_json_abap_mapping-json = 'dbProcedureProxy'.
        APPEND ls_json_abap_mapping TO lt_json_abap_mappings.
        ls_json_abap_mapping-abap = zif_abapgit_aff_intf_v1=>co_category-web_dynpro_runtime.
        ls_json_abap_mapping-json = 'webDynproRuntime'.
        APPEND ls_json_abap_mapping TO lt_json_abap_mappings.
        ls_json_abap_mapping-abap = zif_abapgit_aff_intf_v1=>co_category-enterprise_service.
        ls_json_abap_mapping-json = 'enterpriseService'.
        APPEND ls_json_abap_mapping TO lt_json_abap_mappings.
    
        ls_category_mapping-path = '/category'.
        ls_category_mapping-mappings = lt_json_abap_mappings.
    
        APPEND ls_category_mapping TO rt_result.
      ENDMETHOD.
    
      METHOD get_paths_to_skip.
        DATA:
          ls_path_to_skipp TYPE zcl_abapgit_json_handler=>ty_path_value_pair.
    
        ls_path_to_skipp-path  = '/category'.
        ls_path_to_skipp-value = 'standard'.
    
        APPEND ls_path_to_skipp TO rt_result.
      ENDMETHOD.
    
      METHOD deserialize.
        DATA:
          lo_ajson                      TYPE REF TO zcl_abapgit_json_handler,
          lx_exception                  TYPE REF TO cx_static_check,
          lt_enum_mappings              TYPE zcl_abapgit_json_handler=>ty_enum_mappings,
          lt_default_abap_langu_version TYPE zcl_abapgit_json_handler=>ty_path_value_pair,
          lt_values_for_initial         TYPE zcl_abapgit_json_handler=>ty_skip_paths.
    
        lt_values_for_initial = get_paths_to_skip( ).
    
        lt_default_abap_langu_version-path  = '/header/abap_language_version'.
        lt_default_abap_langu_version-value = zif_abapgit_dot_abapgit=>c_abap_language_version-standard.
        APPEND lt_default_abap_langu_version TO lt_values_for_initial.
    
        lt_enum_mappings = get_mappings( ).
    
        CREATE OBJECT lo_ajson.
        TRY.
            lo_ajson->deserialize(
              EXPORTING
                iv_content       = iv_data
                iv_defaults      = lt_values_for_initial
                iv_enum_mappings = lt_enum_mappings
              IMPORTING
                ev_data          = rv_result ).
          CATCH cx_static_check INTO lx_exception.
            zcx_abapgit_exception=>raise_with_text( lx_exception ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD serialize_translations.
        DATA: ls_data        TYPE zif_abapgit_aff_intf_v1=>ty_main,
              lv_langu       TYPE laiso,
              lv_json        TYPE string,
              lo_ajson       TYPE REF TO /apmg/if_apm_ajson,
              lo_json_path   TYPE REF TO zcl_abapgit_json_path,
              lt_translation TYPE string_table,
              lx_exception   TYPE REF TO /apmg/cx_apm_ajson_error,
              lo_trans_file  TYPE REF TO zcl_abapgit_properties_file.
    
        LOOP AT it_language INTO lv_langu.
    
          ls_data = fill_translation( iv_name  = is_intf-vseointerf-clsname
                                      iv_language = lv_langu ).
    
          " convert AFF type to JSON
          TRY.
              lo_ajson = /apmg/cl_apm_ajson=>new( iv_keep_item_order = abap_true
                )->set( iv_path = '/'
                        iv_val  = ls_data
                )->map( /apmg/cl_apm_ajson_mapping=>create_to_camel_case( )
                )->filter( /apmg/cl_apm_ajson_filter_lib=>create_empty_filter( ) ).
              " remove manually the non-primitive types that are initial or not relevant for translation
              lo_ajson->delete( '/category/' ).
              lo_ajson->delete( '/proxy/' ).
              lv_json = lo_ajson->stringify( ).
            CATCH /apmg/cx_apm_ajson_error INTO lx_exception.
              zcx_abapgit_exception=>raise_with_text( lx_exception ).
          ENDTRY.
    
          CREATE OBJECT lo_json_path.
          lt_translation = lo_json_path->serialize( lv_json ).
    
          CREATE OBJECT lo_trans_file
            EXPORTING iv_lang = lv_langu.
    
          lo_trans_file->push_text_pairs( lt_translation ).
    
          APPEND lo_trans_file TO rt_result.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD fill_translation.
        DATA: lv_langu_sap1 TYPE sy-langu.
    
        lv_langu_sap1 = zcl_abapgit_convert=>language_sap2_to_sap1( iv_language ).
    
        rt_result-descriptions = lcl_aff_helper=>get_descriptions_compo_subco(
          iv_clif_name = iv_name
          iv_language  = lv_langu_sap1 ).
    
        SELECT SINGLE descript FROM seoclasstx INTO rt_result-header-description
        WHERE clsname = iv_name AND
              langu   = lv_langu_sap1.
    
      ENDMETHOD.
    
      METHOD deserialize_translation.
        DATA: lo_properties_file  TYPE REF TO zcl_abapgit_properties_file,
              lt_description_int  LIKE LINE OF et_description_int,
              lt_translation_file TYPE zif_abapgit_i18n_file=>ty_table_of,
              li_translation_file LIKE LINE OF lt_translation_file,
              ls_aff_data         TYPE zif_abapgit_aff_intf_v1=>ty_main,
              lo_type_mapper      TYPE REF TO zif_abapgit_aff_type_mapping,
              ls_ag_data          TYPE zcl_abapgit_object_intf=>ty_intf,
              lv_sap1             TYPE sy-langu.
    
        lt_translation_file = io_files->read_i18n_files( ).
    
        LOOP AT lt_translation_file INTO li_translation_file.
    
          CLEAR ls_ag_data.
    
          lo_properties_file ?= li_translation_file.
          lo_properties_file->get_translations( IMPORTING ev_data = ls_aff_data ).
    
          lv_sap1 = zcl_abapgit_convert=>language_sap2_to_sap1( li_translation_file->lang( ) ).
          ls_aff_data-header-original_language = lv_sap1.
    
          CREATE OBJECT lo_type_mapper TYPE lcl_aff_type_mapping.
          lo_type_mapper->to_abapgit(
            EXPORTING
              iv_data        = ls_aff_data
              iv_object_name = is_item-obj_name
            IMPORTING
              es_data        = ls_ag_data ).
    
          lt_description_int-clsname  = ls_ag_data-vseointerf-clsname.
          lt_description_int-langu    = ls_ag_data-vseointerf-langu.
          lt_description_int-descript = ls_ag_data-vseointerf-descript.
    
          APPEND lt_description_int TO et_description_int.
          APPEND LINES OF ls_ag_data-description     TO et_description.
          APPEND LINES OF ls_ag_data-description_sub TO et_description_sub.
    
        ENDLOOP.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_intf IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mi_object_oriented_object_fct = zcl_abapgit_oo_factory=>get_by_type( ms_item-obj_type ).
    
        mv_aff_enabled = zcl_abapgit_aff_factory=>get_registry( )->is_supported_object_type( 'INTF' ).
    
      ENDMETHOD.
    
      METHOD deserialize_descr_class.
        DATA ls_clskey TYPE seoclskey.
        ls_clskey-clsname = ms_item-obj_name.
    
        mi_object_oriented_object_fct->update_descriptions_class(
          is_key          = ls_clskey
          iv_language     = mv_language
          it_descriptions = it_description ).
      ENDMETHOD.
    
      METHOD deserialize_descr_compo.
        DATA ls_clskey TYPE seoclskey.
        ls_clskey-clsname = ms_item-obj_name.
    
        mi_object_oriented_object_fct->update_descriptions_compo(
          is_key          = ls_clskey
          it_descriptions = it_description ).
      ENDMETHOD.
    
      METHOD deserialize_descr_subco.
        DATA ls_clskey TYPE seoclskey.
        ls_clskey-clsname = ms_item-obj_name.
    
        mi_object_oriented_object_fct->update_descriptions_subco(
          is_key          = ls_clskey
          it_descriptions = it_description ).
      ENDMETHOD.
    
      METHOD deserialize_docu.
        DATA: lv_object     TYPE dokhl-object,
              ls_i18n_lines TYPE zif_abapgit_lang_definitions=>ty_i18n_line.
    
        lv_object = ms_item-obj_name.
    
        IF lines( is_docu-lines ) = 0.
          mi_object_oriented_object_fct->delete_documentation(
            iv_id          = c_longtext_id-interface
            iv_object_name = lv_object
            iv_language    = mv_language ).
          RETURN.
        ENDIF.
    
        mi_object_oriented_object_fct->create_documentation(
          it_lines       = is_docu-lines
          iv_id          = c_longtext_id-interface
          iv_object_name = lv_object
          iv_language    = mv_language ).
    
        LOOP AT is_docu-i18n_lines INTO ls_i18n_lines.
          mi_object_oriented_object_fct->create_documentation(
            it_lines         = ls_i18n_lines-lines
            iv_id            = c_longtext_id-interface
            iv_object_name   = lv_object
            iv_language      = ls_i18n_lines-language
            iv_no_masterlang = abap_true ).
        ENDLOOP.
    
        deserialize_longtexts(
          ii_xml           = ii_xml
          iv_longtext_name = c_longtext_name-attributes
          iv_longtext_id   = c_longtext_id-attributes ).
    
        deserialize_longtexts(
          ii_xml           = ii_xml
          iv_longtext_name = c_longtext_name-methods
          iv_longtext_id   = c_longtext_id-methods ).
    
        deserialize_longtexts(
          ii_xml           = ii_xml
          iv_longtext_name = c_longtext_name-events
          iv_longtext_id   = c_longtext_id-events ).
    
      ENDMETHOD.
    
      METHOD deserialize_pre_ddic.
    
        DATA ls_intf TYPE ty_intf.
    
        IF mv_aff_enabled = abap_true.
          ls_intf = read_json( ).
        ELSE.
          ii_xml->read( EXPORTING iv_name = 'VSEOINTERF'
                        CHANGING  cg_data = ls_intf-vseointerf ).
        ENDIF.
    
        set_abap_language_version( CHANGING cv_abap_language_version = ls_intf-vseointerf-unicode ).
    
        mi_object_oriented_object_fct->create(
          EXPORTING
            iv_check      = abap_false
            iv_package    = iv_package
          CHANGING
            cg_properties = ls_intf-vseointerf ).
    
      ENDMETHOD.
    
      METHOD deserialize_proxy.
    
        DATA: lv_transport    TYPE trkorr,
              li_proxy_object TYPE REF TO if_px_main,
              lv_name         TYPE prx_r3name,
              lx_proxy_fault  TYPE REF TO cx_proxy_fault.
    
        lv_name = ms_item-obj_name.
    
        lv_transport = iv_transport.
    
        TRY.
            li_proxy_object = cl_pxn_factory=>create(
                                  application  = 'PROXY_UI'
                                  display_only = abap_false
                                  saveable     = abap_true
                              )->if_pxn_factory~load_by_abap_name(
                                  object   = ms_item-obj_type
                                  obj_name = lv_name ).
    
            li_proxy_object->activate(
              EXPORTING
                activate_all     = abap_true
              CHANGING
                transport_number = lv_transport ).
    
            li_proxy_object->dequeue( ).
    
          CATCH cx_proxy_fault INTO lx_proxy_fault.
            IF li_proxy_object IS BOUND.
              TRY.
                  li_proxy_object->dequeue( ).
                CATCH cx_proxy_fault ##NO_HANDLER.
              ENDTRY.
            ENDIF.
            zcx_abapgit_exception=>raise_with_text( lx_proxy_fault ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD extract_languages_for_transl.
        DATA: lv_desc              TYPE seocompotx,
              lv_desc_int          TYPE seoclasstx,
              lv_desc_sub          TYPE seosubcotx,
              lv_unique            TYPE sy-langu,
              lv_sap2              TYPE string,
              lt_unique_language   TYPE STANDARD TABLE OF sy-langu,
              lv_original_language TYPE sy-langu.
    
        lv_original_language = mo_i18n_params->ms_params-main_language.
    
        LOOP AT is_intf-description INTO lv_desc WHERE langu <> lv_original_language.
          APPEND lv_desc-langu TO lt_unique_language.
        ENDLOOP.
    
        LOOP AT is_intf-description_int INTO lv_desc_int WHERE langu <> lv_original_language.
          APPEND lv_desc_int-langu TO lt_unique_language.
        ENDLOOP.
    
        LOOP AT is_intf-description_sub INTO lv_desc_sub WHERE langu <> lv_original_language.
          APPEND lv_desc_sub-langu TO lt_unique_language.
        ENDLOOP.
    
        SORT lt_unique_language ASCENDING.
        DELETE ADJACENT DUPLICATES FROM lt_unique_language.
    
        LOOP AT lt_unique_language INTO lv_unique.
          lv_sap2 = zcl_abapgit_convert=>language_sap1_to_sap2( lv_unique ).
          APPEND lv_sap2 TO rs_result.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD read_json.
        DATA lv_json_data TYPE string.
        DATA ls_intf_aff TYPE zif_abapgit_aff_intf_v1=>ty_main.
        DATA lo_aff_mapper TYPE REF TO zif_abapgit_aff_type_mapping.
    
        lv_json_data = mo_files->read_string( 'json' ).
        ls_intf_aff = lcl_aff_metadata_handler=>deserialize( lv_json_data ).
    
        CREATE OBJECT lo_aff_mapper TYPE lcl_aff_type_mapping.
        lo_aff_mapper->to_abapgit( EXPORTING iv_data        = ls_intf_aff
                                             iv_object_name = ms_item-obj_name
                                   IMPORTING es_data        = rs_intf ).
      ENDMETHOD.
    
      METHOD read_xml.
        ii_xml->read( EXPORTING iv_name = 'VSEOINTERF'
                      CHANGING  cg_data = rs_intf-vseointerf ).
        ii_xml->read( EXPORTING iv_name = 'DESCRIPTIONS_INTERFACE'
                      CHANGING  cg_data = rs_intf-description_int ).
        ii_xml->read( EXPORTING iv_name = 'DESCRIPTIONS'
                      CHANGING  cg_data = rs_intf-description ).
        ii_xml->read( EXPORTING iv_name = 'DESCRIPTIONS_SUB'
                      CHANGING  cg_data = rs_intf-description_sub ).
        ii_xml->read( EXPORTING iv_name = 'LINES'
                      CHANGING  cg_data = rs_intf-docu-lines ).
        ii_xml->read( EXPORTING iv_name = 'I18N_LINES'
                      CHANGING  cg_data = rs_intf-docu-i18n_lines ).
      ENDMETHOD.
    
      METHOD serialize_descr_class.
    
        DATA: lt_descriptions    TYPE zif_abapgit_oo_object_fnc=>ty_seoclasstx_tt,
              lt_language_filter TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        " Main language is already in VSEOCLASS so we serialize only translations
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          RETURN.
        ENDIF.
    
        lt_descriptions = mi_object_oriented_object_fct->read_descriptions_class(
          iv_object_name = iv_clsname
          iv_language    = mv_language ).
    
        " Remove technical languages
        lt_language_filter = mo_i18n_params->build_language_filter( ).
        DELETE lt_descriptions WHERE NOT langu IN lt_language_filter AND langu <> mv_language.
    
        IF lines( lt_descriptions ) = 0.
          RETURN.
        ENDIF.
    
        rs_description = lt_descriptions.
    
      ENDMETHOD.
    
      METHOD serialize_descr_compo.
    
        DATA: lt_descriptions    TYPE zif_abapgit_oo_object_fnc=>ty_seocompotx_tt,
              lv_language        TYPE spras,
              lt_language_filter TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          lv_language = mv_language.
        ENDIF.
    
        lt_descriptions = mi_object_oriented_object_fct->read_descriptions_compo(
          iv_object_name = iv_clsname
          iv_language    = lv_language ).
    
        " Remove technical languages
        lt_language_filter = mo_i18n_params->build_language_filter( ).
        DELETE lt_descriptions WHERE NOT langu IN lt_language_filter AND langu <> mv_language.
    
        IF lines( lt_descriptions ) = 0.
          RETURN.
        ENDIF.
    
        rs_description = lt_descriptions.
    
      ENDMETHOD.
    
      METHOD serialize_descr_subco.
    
        DATA: lt_descriptions    TYPE zif_abapgit_oo_object_fnc=>ty_seosubcotx_tt,
              lv_language        TYPE spras,
              lt_language_filter TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          lv_language = mv_language.
        ENDIF.
    
        lt_descriptions = mi_object_oriented_object_fct->read_descriptions_subco(
          iv_object_name = iv_clsname
          iv_language    = lv_language ).
    
        " Remove technical languages
        lt_language_filter = mo_i18n_params->build_language_filter( ).
        DELETE lt_descriptions WHERE NOT langu IN lt_language_filter AND langu <> mv_language.
    
        IF lines( lt_descriptions ) = 0.
          RETURN.
        ENDIF.
    
        rs_description = lt_descriptions.
    
      ENDMETHOD.
    
      METHOD serialize_docu.
    
        DATA: lt_lines      TYPE tlinetab,
              lv_object     TYPE dokhl-object,
              lv_langu      TYPE sy-langu,
              lt_i18n_lines TYPE zif_abapgit_lang_definitions=>ty_i18n_lines,
              ls_i18n_lines TYPE zif_abapgit_lang_definitions=>ty_i18n_line.
    
        lv_object = iv_clsname.
    
        lt_lines = mi_object_oriented_object_fct->read_documentation(
          iv_id          = c_longtext_id-interface
          iv_object_name = lv_object
          iv_language    = mv_language ).
    
        rs_docu-lines = lt_lines.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          RETURN.
        ENDIF.
    
        LOOP AT it_langu_additional INTO lv_langu.
    
          lt_lines = mi_object_oriented_object_fct->read_documentation(
            iv_id          = c_longtext_id-interface
            iv_object_name = lv_object
            iv_language    = lv_langu ).
    
          IF lines( lt_lines ) > 0.
            CLEAR ls_i18n_lines.
            ls_i18n_lines-language = lv_langu.
            ls_i18n_lines-lines    = lt_lines.
            INSERT ls_i18n_lines INTO TABLE lt_i18n_lines.
          ENDIF.
    
        ENDLOOP.
    
        rs_docu-i18n_lines = lt_i18n_lines.
    
      ENDMETHOD.
    
      METHOD serialize_xml.
    
        DATA:
          ls_intf                      TYPE ty_intf,
          ls_clskey                    TYPE seoclskey,
          lv_serialized_data           TYPE xstring,
          lt_langu_additional          TYPE zif_abapgit_lang_definitions=>ty_langus,
          lt_i18n_file                 TYPE zif_abapgit_i18n_file=>ty_table_of,
          lo_i18n_file                 TYPE REF TO zif_abapgit_i18n_file,
          lt_languages_for_translation TYPE zif_abapgit_definitions=>ty_languages.
    
        ls_clskey-clsname = ms_item-obj_name.
    
        ls_intf-vseointerf = mi_object_oriented_object_fct->get_interface_properties( ls_clskey ).
    
        clear_abap_language_version( CHANGING cv_abap_language_version = ls_intf-vseointerf-unicode ).
    
        " Select all active translations of documentation
        " Skip main language - it was already serialized
        SELECT DISTINCT langu
          INTO TABLE lt_langu_additional
          FROM dokhl
          WHERE id     = c_longtext_id-interface
            AND object = ls_clskey-clsname
            AND langu  <> mv_language
          ORDER BY langu.
    
        ls_intf-docu = serialize_docu(
          iv_clsname          = ls_clskey-clsname
          it_langu_additional = lt_langu_additional ).
    
        ls_intf-description_int = serialize_descr_class( ls_clskey-clsname ).
        ls_intf-description     = serialize_descr_compo( ls_clskey-clsname ).
        ls_intf-description_sub = serialize_descr_subco( ls_clskey-clsname ).
    
        " HERE: switch with feature flag for XML or JSON file format
        IF mv_aff_enabled = abap_true.
          lv_serialized_data = lcl_aff_metadata_handler=>serialize( ls_intf ).
          mo_files->add_raw( iv_ext  = 'json'
                             iv_data = lv_serialized_data ).
    
          lt_languages_for_translation = extract_languages_for_transl( ls_intf ).
    
          lt_i18n_file = lcl_aff_metadata_handler=>serialize_translations(
            is_intf     = ls_intf
            it_language = lt_languages_for_translation ).
    
          LOOP AT lt_i18n_file INTO lo_i18n_file.
            mo_files->add_i18n_file( lo_i18n_file ).
          ENDLOOP.
        ELSE.
          io_xml->add( iv_name = 'VSEOINTERF'
                       ig_data = ls_intf-vseointerf ).
          io_xml->add( iv_name = 'DESCRIPTIONS_INTERFACE'
                       ig_data = ls_intf-description_int ).
          io_xml->add( iv_name = 'DESCRIPTIONS'
                       ig_data = ls_intf-description ).
          io_xml->add( iv_name = 'DESCRIPTIONS_SUB'
                       ig_data = ls_intf-description_sub ).
          io_xml->add( iv_name = 'LINES'
                       ig_data = ls_intf-docu-lines ).
          io_xml->add( iv_name = 'I18N_LINES'
                       ig_data = ls_intf-docu-i18n_lines ).
    
          serialize_longtexts(
            ii_xml           = io_xml
            iv_longtext_name = c_longtext_name-attributes
            iv_longtext_id   = c_longtext_id-attributes ).
    
          serialize_longtexts(
            ii_xml           = io_xml
            iv_longtext_name = c_longtext_name-methods
            iv_longtext_id   = c_longtext_id-methods ).
    
          serialize_longtexts(
            ii_xml           = io_xml
            iv_longtext_name = c_longtext_name-events
            iv_longtext_id   = c_longtext_id-events ).
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        TYPES: BEGIN OF ty_includes,
                 programm TYPE syrepid,
               END OF ty_includes.
    
        TYPES: BEGIN OF ty_reposrc,
                 unam  TYPE reposrc-unam,
                 udat  TYPE reposrc-udat,
                 utime TYPE reposrc-utime,
               END OF ty_reposrc.
    
        DATA: lt_reposrc  TYPE STANDARD TABLE OF ty_reposrc,
              ls_reposrc  LIKE LINE OF lt_reposrc,
              lt_includes TYPE STANDARD TABLE OF ty_includes.
    
        lt_includes = mi_object_oriented_object_fct->get_includes( ms_item-obj_name ).
        ASSERT lines( lt_includes ) > 0.
    
        SELECT unam udat utime FROM reposrc
          INTO TABLE lt_reposrc
          FOR ALL ENTRIES IN lt_includes
          WHERE progname = lt_includes-programm
          AND r3state = 'A'.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ELSE.
          SORT lt_reposrc BY udat DESCENDING utime DESCENDING.
          READ TABLE lt_reposrc INDEX 1 INTO ls_reposrc.
          ASSERT sy-subrc = 0.
          rv_user = ls_reposrc-unam.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
        DATA: ls_clskey     TYPE seoclskey,
              ls_vseointerf TYPE vseointerf.
    
        ls_clskey-clsname = ms_item-obj_name.
        ls_vseointerf = mi_object_oriented_object_fct->get_interface_properties( ls_clskey ).
    
        IF ls_vseointerf-clsproxy = abap_true.
          " Proxy interfaces are managed via SPRX
          RETURN.
        ENDIF.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        corr_insert( iv_package ).
    
        mi_object_oriented_object_fct->delete( ls_clskey ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
        DATA:
          lt_source          TYPE rswsourcet,
          ls_clskey          TYPE seoclskey,
          ls_intf            TYPE ty_intf,
          lt_description     TYPE zif_abapgit_oo_object_fnc=>ty_seocompotx_tt,
          lt_description_int TYPE zif_abapgit_oo_object_fnc=>ty_seoclasstx_tt,
          lt_description_sub TYPE zif_abapgit_oo_object_fnc=>ty_seosubcotx_tt.
    
        IF iv_step = zif_abapgit_object=>gc_step_id-abap.
          " HERE: switch with feature flag between XML and JSON file format
          IF mv_aff_enabled = abap_true.
            ls_intf = read_json( ).
    
            lcl_aff_metadata_handler=>deserialize_translation(
              EXPORTING
                io_files           = mo_files
                is_item            = ms_item
              IMPORTING
                et_description     = lt_description
                et_description_int = lt_description_int
                et_description_sub = lt_description_sub ).
    
            APPEND LINES OF lt_description TO ls_intf-description.
            APPEND LINES OF lt_description_int TO ls_intf-description_int.
            APPEND LINES OF lt_description_sub TO ls_intf-description_sub.
    
          ELSE.
            ls_intf = read_xml( io_xml ).
          ENDIF.
    
          set_abap_language_version( CHANGING cv_abap_language_version = ls_intf-vseointerf-unicode ).
    
          IF ls_intf-vseointerf-clsproxy = abap_true.
            " Proxy interfaces are managed via SPRX
            deserialize_proxy( iv_transport ).
    
          ELSE.
            mi_object_oriented_object_fct->create(
              EXPORTING
                iv_check      = abap_true
                iv_package    = iv_package
              CHANGING
                cg_properties = ls_intf-vseointerf ).
    
            ls_clskey-clsname = ms_item-obj_name.
            lt_source = mo_files->read_abap( ).
    
            mi_object_oriented_object_fct->deserialize_source(
              is_key     = ls_clskey
              iv_package = iv_package
              iv_version = ls_intf-vseointerf-unicode
              it_source  = lt_source ).
    
            deserialize_descr_class( ls_intf-description_int ).
    
            deserialize_descr_compo( ls_intf-description ).
    
            deserialize_descr_subco( ls_intf-description_sub ).
    
            deserialize_docu(
              is_docu = ls_intf-docu
              ii_xml  = io_xml ).
    
            mi_object_oriented_object_fct->add_to_activation_list( ms_item ).
          ENDIF.
    
        ELSEIF iv_step = zif_abapgit_object=>gc_step_id-early.
    
          " If interface does not exist, create it
          " so DDIC that depends on it does not fail activation
          IF zif_abapgit_object~exists( ) = abap_false.
            deserialize_pre_ddic(
              ii_xml     = io_xml
              iv_package = iv_package ).
          ELSE.
            corr_insert( iv_package ).
          ENDIF.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: ls_class_key TYPE seoclskey,
              lv_category  TYPE seoclassdf-category.
    
        ls_class_key-clsname = ms_item-obj_name.
    
        rv_bool = mi_object_oriented_object_fct->exists( ls_class_key-clsname ).
    
        IF rv_bool = abap_true.
          SELECT SINGLE category FROM seoclassdf INTO lv_category
            WHERE clsname = ls_class_key-clsname
            AND ( version = '1'
            OR version = '0' ) ##WARN_OK.                   "#EC CI_GENBUFF
          IF sy-subrc = 0 AND lv_category = seoc_category_webdynpro_class.
            rv_bool = abap_false.
          ELSE.
            SELECT SINGLE obj_name FROM sproxhdr INTO ls_class_key-clsname
              WHERE object = 'INTF' AND obj_name = ls_class_key-clsname.
            IF sy-subrc = 0.
              " generated by proxy
              rv_bool = abap_false.
            ENDIF.
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-early TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lv_object TYPE eqegraarg.
    
        lv_object = |{ ms_item-obj_name }|.
        OVERLAY lv_object WITH '==============================P'.
        lv_object = lv_object && '*'.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESEOCLASS'
                                                iv_argument    = lv_object ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lt_source        TYPE seop_source_string,
              ls_interface_key TYPE seoclskey.
    
        ls_interface_key-clsname = ms_item-obj_name.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        CALL FUNCTION 'SEO_BUFFER_REFRESH'
          EXPORTING
            version = seoc_version_active
            force   = abap_true.
        CALL FUNCTION 'SEO_BUFFER_REFRESH'
          EXPORTING
            version = seoc_version_inactive
            force   = abap_true.
    
        lt_source = mi_object_oriented_object_fct->serialize_abap( ls_interface_key ).
    
        mo_files->add_abap( lt_source ).
    
        serialize_xml( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_iobj IMPLEMENTATION.
    
      METHOD constructor.
    
        DATA lr_viobj TYPE REF TO data.
    
        super->constructor(
            is_item        = is_item
            iv_language    = iv_language
            io_files       = io_files
            io_i18n_params = io_i18n_params ).
    
        TRY.
            CREATE DATA lr_viobj TYPE ('RSD_S_VIOBJ').
          CATCH cx_sy_create_data_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD clear_field.
    
        FIELD-SYMBOLS:  TYPE data.
    
        ASSIGN COMPONENT iv_fieldname
               OF STRUCTURE cg_metadata
               TO .
        ASSERT sy-subrc = 0.
    
        CLEAR: .
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lv_objna TYPE c LENGTH 30,
              lr_viobj TYPE REF TO data.
    
        FIELD-SYMBOLS:
           TYPE any,
            TYPE any.
    
        lv_objna = ms_item-obj_name.
    
        CREATE DATA lr_viobj TYPE ('RSD_S_VIOBJ').
        ASSIGN lr_viobj->* TO .
    
        CALL FUNCTION 'RSD_IOBJ_GET'
          EXPORTING
            i_iobjnm         = lv_objna
            i_objvers        = 'A'
          IMPORTING
            e_s_viobj        = 
          EXCEPTIONS
            iobj_not_found   = 1
            illegal_input    = 2
            bct_comp_invalid = 3
    *       not_authorized   = 4 " not in lower releases
            OTHERS           = 5.
        IF sy-subrc = 0.
          ASSIGN COMPONENT 'TSTPNM' OF STRUCTURE  TO .
          rv_user = .
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        TYPES: BEGIN OF ty_iobj,
                 objnm TYPE c LENGTH 30.
        TYPES END OF ty_iobj.
    
        DATA: lt_iobjname TYPE STANDARD TABLE OF ty_iobj,
              lv_subrc    TYPE sy-subrc.
    
        APPEND ms_item-obj_name TO lt_iobjname.
    
        CALL FUNCTION 'RSDG_IOBJ_MULTI_DELETE'
          EXPORTING
            i_t_iobjnm        = lt_iobjname
            i_check_dependent = abap_false
            i_manual          = abap_false
          IMPORTING
            e_subrc           = lv_subrc.
    
        IF lv_subrc <> 0.
          zcx_abapgit_exception=>raise( |Error when deleting InfoObject { ms_item-obj_name }| ).
        ENDIF.
    
        corr_insert( iv_package ).
    
        TRY.
            " In case of IOBJ dependencies, tadir entry might be leftover so we remove it
            tadir_delete( ).
          CATCH zcx_abapgit_exception ##NO_HANDLER.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA:
          lr_details                  TYPE REF TO data,
          lr_infoobj                  TYPE REF TO data,
          ls_return                   TYPE bapiret2,
          lt_return                   TYPE STANDARD TABLE OF bapiret2,
          lr_compounds                TYPE REF TO data,
          lr_attributes               TYPE REF TO data,
          lr_navigationattributes     TYPE REF TO data,
          lr_atrnavinfoprovider       TYPE REF TO data,
          lr_hierarchycharacteristics TYPE REF TO data,
          lr_elimination              TYPE REF TO data,
          lr_hanafieldsmapping        TYPE REF TO data,
          lr_xxlattributes            TYPE REF TO data.
    
        FIELD-SYMBOLS:
                            TYPE any,
                          TYPE STANDARD TABLE,
                         TYPE STANDARD TABLE,
               TYPE STANDARD TABLE,
                 TYPE STANDARD TABLE,
           TYPE STANDARD TABLE,
                        TYPE STANDARD TABLE,
                  TYPE STANDARD TABLE,
                      TYPE STANDARD TABLE,
                         TYPE data,
                        TYPE STANDARD TABLE.
    
        CREATE DATA lr_details TYPE ('BAPI6108').
        CREATE DATA lr_compounds TYPE STANDARD TABLE OF ('BAPI6108CM').
        CREATE DATA lr_attributes TYPE STANDARD TABLE OF ('BAPI6108AT').
        CREATE DATA lr_navigationattributes TYPE STANDARD TABLE OF ('BAPI6108AN').
        CREATE DATA lr_atrnavinfoprovider TYPE STANDARD TABLE OF ('BAPI6108NP').
        CREATE DATA lr_hierarchycharacteristics TYPE STANDARD TABLE OF ('BAPI6108HC').
        CREATE DATA lr_elimination TYPE STANDARD TABLE OF ('BAPI6108IE').
        CREATE DATA lr_hanafieldsmapping TYPE STANDARD TABLE OF ('BAPI6108HANA_MAP').
        CREATE DATA lr_xxlattributes TYPE STANDARD TABLE OF ('BAPI6108ATXXL').
        CREATE DATA lr_infoobj TYPE STANDARD TABLE OF ('BAPI6108').
    
        ASSIGN lr_details->* TO .
        ASSIGN lr_compounds->* TO .
        ASSIGN lr_attributes->* TO .
        ASSIGN lr_navigationattributes->* TO .
        ASSIGN lr_atrnavinfoprovider->* TO .
        ASSIGN lr_hierarchycharacteristics->* TO .
        ASSIGN lr_elimination->* TO .
        ASSIGN lr_hanafieldsmapping->* TO .
        ASSIGN lr_xxlattributes->* TO .
        ASSIGN lr_infoobj->* TO .
    
        io_xml->read( EXPORTING iv_name = 'IOBJ'
                      CHANGING cg_data =  ).
    
        io_xml->read( EXPORTING iv_name = 'COMPOUNDS'
                      CHANGING  cg_data =  ).
    
        io_xml->read( EXPORTING iv_name = 'ATTRIBUTES'
                      CHANGING  cg_data =  ).
    
        io_xml->read( EXPORTING iv_name = 'NAVIGATION_ATTRIBUTES'
                      CHANGING  cg_data =  ).
    
        io_xml->read( EXPORTING iv_name = 'ATTR_NAVIGATION'
                      CHANGING  cg_data =  ).
    
        io_xml->read( EXPORTING iv_name = 'HIERARCHY'
                      CHANGING  cg_data =  ).
    
        io_xml->read( EXPORTING iv_name = 'ELIMINATION'
                      CHANGING  cg_data =  ).
    
        io_xml->read( EXPORTING iv_name = 'HANA_FIELDS_MAPPING'
                      CHANGING  cg_data =  ).
    
        io_xml->read( EXPORTING iv_name = 'XXL_ATTRIBUTES'
                      CHANGING  cg_data =  ).
    
        " Number ranges are local (should not have been serialized)
        clear_field( EXPORTING iv_fieldname = 'NUMBRANR'
                     CHANGING  cg_metadata  =  ).
    
        TRY.
    
            ASSIGN
              COMPONENT 'INFOOBJECT'
              OF STRUCTURE 
              TO .
            ASSERT sy-subrc = 0.
    
            IF zif_abapgit_object~exists( ) = abap_false.
              TRY.
                  CALL FUNCTION 'BAPI_IOBJ_CREATE'
                    EXPORTING
                      details                  = 
                    IMPORTING
                      return                   = ls_return
                    TABLES
                      compounds                = 
                      attributes               = 
                      navigationattributes     = 
                      atrnavinfoprovider       = 
                      hierarchycharacteristics = 
                      elimination              = 
                      hanafieldsmapping        = 
                      xxlattributes            =  ##ARG_OK.
                CATCH cx_sy_dyn_call_param_not_found.
                  CALL FUNCTION 'BAPI_IOBJ_CREATE'
                    EXPORTING
                      details                  = 
                    IMPORTING
                      return                   = ls_return
                    TABLES
                      compounds                = 
                      attributes               = 
                      navigationattributes     = 
                      atrnavinfoprovider       = 
                      hierarchycharacteristics = 
                      elimination              = .
              ENDTRY.
            ELSE.
              TRY.
                  CALL FUNCTION 'BAPI_IOBJ_CHANGE'
                    EXPORTING
                      infoobject               = 
                      details                  = 
                    IMPORTING
                      return                   = ls_return
                    TABLES
                      compounds                = 
                      attributes               = 
                      navigationattributes     = 
                      atrnavinfoprovider       = 
                      hierarchycharacteristics = 
                      elimination              = 
                      hanafieldsmapping        = 
                      xxlattributes            =  ##ARG_OK.
                CATCH cx_sy_dyn_call_param_not_found.
                  CALL FUNCTION 'BAPI_IOBJ_CHANGE'
                    EXPORTING
                      infoobject               = 
                      details                  = 
                    IMPORTING
                      return                   = ls_return
                    TABLES
                      compounds                = 
                      attributes               = 
                      navigationattributes     = 
                      atrnavinfoprovider       = 
                      hierarchycharacteristics = 
                      elimination              = .
              ENDTRY.
            ENDIF.
    
            IF ls_return-type = 'E'.
              zcx_abapgit_exception=>raise( |Error when creating iobj: { ls_return-message }| ).
            ENDIF.
    
            APPEND  TO .
    
            CALL FUNCTION 'BAPI_IOBJ_ACTIVATE_MULTIPLE'
              TABLES
                infoobjects = 
                return      = lt_return.
    
            READ TABLE lt_return WITH KEY type = 'E' INTO ls_return.
            IF sy-subrc = 0.
              zcx_abapgit_exception=>raise( |Error when activating iobj: { ls_return-message }| ).
            ENDIF.
    
          CATCH cx_sy_dyn_call_illegal_func.
            zcx_abapgit_exception=>raise( |Necessary BW function modules not found| ).
        ENDTRY.
    
        tadir_insert( iv_package ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_iobjnm TYPE c LENGTH 30.
    
        SELECT SINGLE iobjnm
          FROM ('RSDIOBJ')
          INTO lv_iobjnm
          WHERE iobjnm = ms_item-obj_name.
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
    
        DATA: lv_objna TYPE c LENGTH 30,
              lr_viobj TYPE REF TO data.
    
        FIELD-SYMBOLS:
           TYPE any,
             TYPE any.
    
        lv_objna = ms_item-obj_name.
    
        CREATE DATA lr_viobj TYPE ('RSD_S_VIOBJ').
    
        ASSIGN lr_viobj->* TO .
    
        CALL FUNCTION 'RSD_IOBJ_GET'
          EXPORTING
            i_iobjnm  = lv_objna
            i_objvers = 'A'
          IMPORTING
            e_s_viobj = .
    
        ASSIGN COMPONENT 'OBJSTAT' OF STRUCTURE  TO .
    
        IF  = 'ACT' AND sy-subrc = 0.
          rv_active = abap_true.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lv_object TYPE eqegraarg.
    
        lv_object = ms_item-obj_name.
        OVERLAY lv_object WITH '                                          '.
        lv_object = lv_object && '*'.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'E_BIW_PROV'
                                                iv_argument    = lv_object ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          lv_iobjnam                  TYPE rsiobjnm,
          ls_return                   TYPE bapiret2,
          lr_details                  TYPE REF TO data,
          lr_compounds                TYPE REF TO data,
          lr_attributes               TYPE REF TO data,
          lr_navigationattributes     TYPE REF TO data,
          lr_atrnavinfoprovider       TYPE REF TO data,
          lr_hierarchycharacteristics TYPE REF TO data,
          lr_elimination              TYPE REF TO data,
          lr_hanafieldsmapping        TYPE REF TO data,
          lr_xxlattributes            TYPE REF TO data.
    
        FIELD-SYMBOLS:
                            TYPE any,
                          TYPE STANDARD TABLE,
                         TYPE STANDARD TABLE,
               TYPE STANDARD TABLE,
                 TYPE STANDARD TABLE,
           TYPE STANDARD TABLE,
                        TYPE STANDARD TABLE,
                  TYPE STANDARD TABLE,
                      TYPE STANDARD TABLE.
    
        CREATE DATA lr_details TYPE ('BAPI6108').
        CREATE DATA lr_compounds TYPE STANDARD TABLE OF ('BAPI6108CM').
        CREATE DATA lr_attributes TYPE STANDARD TABLE OF ('BAPI6108AT').
        CREATE DATA lr_navigationattributes TYPE STANDARD TABLE OF ('BAPI6108AN').
        CREATE DATA lr_atrnavinfoprovider TYPE STANDARD TABLE OF ('BAPI6108NP').
        CREATE DATA lr_hierarchycharacteristics TYPE STANDARD TABLE OF ('BAPI6108HC').
        CREATE DATA lr_elimination TYPE STANDARD TABLE OF ('BAPI6108IE').
        CREATE DATA lr_hanafieldsmapping TYPE STANDARD TABLE OF ('BAPI6108HANA_MAP').
        CREATE DATA lr_xxlattributes TYPE STANDARD TABLE OF ('BAPI6108ATXXL').
    
        ASSIGN lr_details->* TO .
        ASSIGN lr_compounds->* TO .
        ASSIGN lr_attributes->* TO .
        ASSIGN lr_navigationattributes->* TO .
        ASSIGN lr_atrnavinfoprovider->* TO .
        ASSIGN lr_hierarchycharacteristics->* TO .
        ASSIGN lr_elimination->* TO .
        ASSIGN lr_hanafieldsmapping->* TO .
        ASSIGN lr_xxlattributes->* TO .
    
        lv_iobjnam = ms_item-obj_name.
    
        TRY.
            CALL FUNCTION 'BAPI_IOBJ_GETDETAIL'
              EXPORTING
                infoobject               = lv_iobjnam
              IMPORTING
                details                  = 
                return                   = ls_return
              TABLES
                compounds                = 
                attributes               = 
                navigationattributes     = 
                atrnavinfoprovider       = 
                hierarchycharacteristics = 
                elimination              = 
                hanafieldsmapping        = 
                xxlattributes            =  ##ARG_OK.
          CATCH cx_sy_dyn_call_param_not_found.
            CALL FUNCTION 'BAPI_IOBJ_GETDETAIL'
              EXPORTING
                infoobject               = lv_iobjnam
              IMPORTING
                details                  = 
                return                   = ls_return
              TABLES
                compounds                = 
                attributes               = 
                navigationattributes     = 
                atrnavinfoprovider       = 
                hierarchycharacteristics = 
                elimination              = .
        ENDTRY.
    
        IF ls_return-type = 'E'.
          zcx_abapgit_exception=>raise( |Error getting details of InfoObject: { ls_return-message }| ).
        ENDIF.
    
        clear_field( EXPORTING iv_fieldname = 'TSTPNM'
                     CHANGING  cg_metadata  =  ).
    
        clear_field( EXPORTING iv_fieldname = 'TIMESTMP'
                     CHANGING  cg_metadata  =  ).
    
        clear_field( EXPORTING iv_fieldname = 'DBROUTID'
                     CHANGING  cg_metadata  =  ).
    
        " Number ranges are local
        clear_field( EXPORTING iv_fieldname = 'NUMBRANR'
                     CHANGING  cg_metadata  =  ).
    
        io_xml->add( iv_name = 'IOBJ'
                     ig_data =  ).
    
        io_xml->add( iv_name = 'COMPOUNDS'
                     ig_data =  ).
    
        io_xml->add( iv_name = 'ATTRIBUTES'
                     ig_data =  ).
    
        io_xml->add( iv_name = 'NAVIGATION_ATTRIBUTES'
                     ig_data =  ).
    
        io_xml->add( iv_name = 'ATTR_NAVIGATION'
                      ig_data =  ).
    
        io_xml->add( iv_name = 'HIERARCHY'
                     ig_data =  ).
    
        io_xml->add( iv_name = 'ELIMINATION'
                     ig_data =  ).
    
        io_xml->add( iv_name = 'HANA_FIELDS_MAPPING'
                     ig_data =  ).
    
        io_xml->add( iv_name = 'XXL_ATTRIBUTES'
                     ig_data =  ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_iwmo IMPLEMENTATION.
    
      METHOD get_field_rules.
        ro_result = zcl_abapgit_field_rules=>create( ).
        ro_result->add(
          iv_table     = '/IWBEP/I_MGW_OHD'
          iv_field     = 'CREATED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_MGW_OHD'
          iv_field     = 'CREATED_TIMESTMP'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp
        )->add(
          iv_table     = '/IWBEP/I_MGW_OHD'
          iv_field     = 'CHANGED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_MGW_OHD'
          iv_field     = 'CHANGED_TIMESTMP'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp ).
    
        IF ms_item-abap_language_version = zcl_abapgit_abap_language_vers=>c_no_abap_language_version.
          ro_result->add(
            iv_table     = '/IWBEP/I_MGW_OHD'
            iv_field     = 'ABAP_LANGUAGE_VERSION'
            iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-abap_language_version ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_generic.
    
        CREATE OBJECT ro_generic
          EXPORTING
            io_field_rules = get_field_rules( )
            is_item        = ms_item
            iv_language    = mv_language.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA lv_created TYPE sy-uname.
        DATA lv_changed TYPE sy-uname.
    
        " Get entry with highest version
        SELECT created_by changed_by INTO (lv_created, lv_changed) FROM ('/IWBEP/I_MGW_OHD')
          WHERE technical_name = ms_item-obj_name
          ORDER BY PRIMARY KEY.
          rv_user = lv_changed.
          IF lv_changed IS INITIAL.
            rv_user = lv_created.
          ENDIF.
        ENDSELECT.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        get_generic( )->delete( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: lv_mdl_technical_name TYPE c LENGTH 32,
              lv_version            TYPE bdc_fval,
              lt_bdcdata            TYPE TABLE OF bdcdata.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_bdcdata.
    
        lv_mdl_technical_name = ms_item-obj_name.
        lv_version = ms_item-obj_name+32(4).
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -program  = '/IWBEP/R_DST_MODEL_BUILDER'.
        -dynpro   = '0100'.
        -dynbegin = 'X'.
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'GS_MODEL_SCREEN_100-TECHNICAL_NAME'.
        -fval = lv_mdl_technical_name.
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'GS_MODEL_SCREEN_100-VERSION'.
        -fval = lv_version.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = '/IWBEP/REG_MODEL'
          it_bdcdata = lt_bdcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        get_generic( )->serialize( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_iwom IMPLEMENTATION.
    
      METHOD get_field_rules.
        ro_result = zcl_abapgit_field_rules=>create( ).
        ro_result->add(
          iv_table     = '/IWFND/I_MED_OHD'
          iv_field     = 'CREATED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWFND/I_MED_OHD'
          iv_field     = 'CREATED_TIMESTMP'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp
        )->add(
          iv_table     = '/IWFND/I_MED_OHD'
          iv_field     = 'CHANGED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWFND/I_MED_OHD'
          iv_field     = 'CHANGED_TIMESTMP'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp ).
    
        IF ms_item-abap_language_version = zcl_abapgit_abap_language_vers=>c_no_abap_language_version.
          ro_result->add(
            iv_table     = '/IWFND/I_MED_OHD'
            iv_field     = 'ABAP_LANGUAGE_VERSION'
            iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-abap_language_version ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_generic.
    
        CREATE OBJECT ro_generic
          EXPORTING
            io_field_rules = get_field_rules( )
            is_item        = ms_item
            iv_language    = mv_language.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE changed_by FROM ('/IWFND/I_MED_OHD') INTO rv_user
          WHERE model_identifier = ms_item-obj_name.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        get_generic( )->delete( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        get_generic( )->serialize( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_iwpr IMPLEMENTATION.
    
      METHOD get_field_rules.
        ro_result = zcl_abapgit_field_rules=>create( ).
        ro_result->add(
          iv_table     = '/IWBEP/I_SBD_GA'
          iv_field     = 'CREATION_USER_ID'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_SBD_GA'
          iv_field     = 'CREATION_TIME'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp
        )->add(
          iv_table     = '/IWBEP/I_SBD_GA'
          iv_field     = 'LAST_CHG_USER_ID'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_SBD_GA'
          iv_field     = 'LAST_CHG_TIME'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp ).
      ENDMETHOD.
    
      METHOD get_generic.
    
        CREATE OBJECT ro_generic
          EXPORTING
            io_field_rules = get_field_rules( )
            is_item        = ms_item
            iv_language    = mv_language.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE last_chg_user_id FROM ('/IWBEP/I_SBD_PR') INTO rv_user
          WHERE project = ms_item-obj_name.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        get_generic( )->delete( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA lv_prog TYPE progname.
    
        lv_prog = '/IWBEP/R_SBUI_SERVICE_BUILDER'.
    
        SUBMIT (lv_prog)
          WITH i_prname = ms_item-obj_name
          AND RETURN.
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        get_generic( )->serialize( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_iwsg IMPLEMENTATION.
    
      METHOD get_field_rules.
    
        ro_result = zcl_abapgit_field_rules=>create( ).
        ro_result->add(
          iv_table     = '/IWFND/I_MED_SRH'
          iv_field     = 'CREATED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWFND/I_MED_SRH'
          iv_field     = 'CREATED_TIMESTMP'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp
        )->add(
          iv_table     = '/IWFND/I_MED_SRH'
          iv_field     = 'CHANGED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWFND/I_MED_SRH'
          iv_field     = 'CHANGED_TIMESTMP'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp ).
    
        IF ms_item-abap_language_version = zcl_abapgit_abap_language_vers=>c_no_abap_language_version.
          ro_result->add(
            iv_table     = '/IWFND/I_MED_SRH'
            iv_field     = 'ABAP_LANGUAGE_VERSION'
            iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-abap_language_version ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_generic.
    
        CREATE OBJECT ro_generic
          EXPORTING
            io_field_rules = get_field_rules( )
            is_item        = ms_item
            iv_language    = mv_language.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE changed_by FROM ('/IWFND/I_MED_SRH') INTO rv_user
          WHERE srv_identifier = ms_item-obj_name.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        get_generic( )->delete( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        get_generic( )->serialize( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_iwsv IMPLEMENTATION.
    
      METHOD get_field_rules.
        ro_result = zcl_abapgit_field_rules=>create( ).
        ro_result->add(
          iv_table     = '/IWBEP/I_MGW_SRH'
          iv_field     = 'CREATED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_MGW_SRH'
          iv_field     = 'CREATED_TIMESTMP'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp
        )->add(
          iv_table     = '/IWBEP/I_MGW_SRH'
          iv_field     = 'CHANGED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_MGW_SRH'
          iv_field     = 'CHANGED_TIMESTMP'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp ).
    
        IF ms_item-abap_language_version = zcl_abapgit_abap_language_vers=>c_no_abap_language_version.
          ro_result->add(
            iv_table     = '/IWBEP/I_MGW_SRH'
            iv_field     = 'ABAP_LANGUAGE_VERSION'
            iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-abap_language_version ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_generic.
    
        CREATE OBJECT ro_generic
          EXPORTING
            io_field_rules = get_field_rules( )
            is_item        = ms_item
            iv_language    = mv_language.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA lv_created TYPE sy-uname.
        DATA lv_changed TYPE sy-uname.
    
        " Get entry with highest version
        SELECT created_by changed_by INTO (lv_created, lv_changed) FROM ('/IWBEP/I_MGW_SRH')
          WHERE technical_name = ms_item-obj_name
          ORDER BY PRIMARY KEY.
          rv_user = lv_changed.
          IF lv_changed IS INITIAL.
            rv_user = lv_created.
          ENDIF.
        ENDSELECT.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        get_generic( )->delete( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: lv_technical_name TYPE c LENGTH 35,
              lv_version        TYPE bdc_fval,
              lt_bdcdata        TYPE TABLE OF bdcdata.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_bdcdata.
    
        lv_technical_name = ms_item-obj_name(36).
        lv_version = ms_item-obj_name+36(4).
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -program  = '/IWBEP/R_DST_SERVICE_BUILDER'.
        -dynpro   = '0100'.
        -dynbegin = 'X'.
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'GS_SCREEN_100-TECHNICAL_NAME'.
        -fval = lv_technical_name.
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'GS_SCREEN_100-VERSION'.
        -fval = lv_version.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = '/IWBEP/REG_SERVICE'
          it_bdcdata = lt_bdcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        get_generic( )->serialize( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_iwvb IMPLEMENTATION.
    
      METHOD get_field_rules.
        ro_result = zcl_abapgit_field_rules=>create( ).
        ro_result->add(
          iv_table     = '/IWBEP/I_MGW_VAH'
          iv_field     = 'CREATED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_MGW_VAH'
          iv_field     = 'CREATED_TIMESTMP'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp
        )->add(
          iv_table     = '/IWBEP/I_MGW_VAH'
          iv_field     = 'CHANGED_BY'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = '/IWBEP/I_MGW_VAH'
          iv_field     = 'CHANGED_TIMESTMP'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-timestamp ).
    
        IF ms_item-abap_language_version = zcl_abapgit_abap_language_vers=>c_no_abap_language_version.
          ro_result->add(
            iv_table     = '/IWBEP/I_MGW_VAH'
            iv_field     = 'ABAP_LANGUAGE_VERSION'
            iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-abap_language_version ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_generic.
    
        CREATE OBJECT ro_generic
          EXPORTING
            io_field_rules = get_field_rules( )
            is_item        = ms_item
            iv_language    = mv_language.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA lv_created TYPE sy-uname.
        DATA lv_changed TYPE sy-uname.
    
        " Get entry with highest version
        SELECT created_by changed_by INTO (lv_created, lv_changed) FROM ('/IWBEP/I_MGW_VAH')
          WHERE technical_name = ms_item-obj_name
          ORDER BY PRIMARY KEY.
          rv_user = lv_changed.
          IF lv_changed IS INITIAL.
            rv_user = lv_created.
          ENDIF.
        ENDSELECT.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        get_generic( )->delete( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA lv_prog TYPE progname.
    
        lv_prog = '/IWBEP/R_DST_VOCAN_REGISTER'.
    
        SUBMIT (lv_prog)
          WITH ip_aname = ms_item-obj_name
          WITH ip_avers = ms_item-obj_name+32(4)
          AND RETURN.
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        get_generic( )->serialize( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_jobd IMPLEMENTATION.
    
      METHOD constructor.
    
        DATA: lr_job_definition TYPE REF TO data.
    
        super->constructor(
            is_item        = is_item
            iv_language    = iv_language
            io_files       = io_files
            io_i18n_params = io_i18n_params ).
    
        TRY.
            CREATE DATA lr_job_definition TYPE ('CL_JR_JOB_DEFINITION=>TY_JOB_DEFINITION').
          CATCH cx_sy_ref_creation.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lr_job_definition TYPE REF TO data,
              lo_job_definition TYPE REF TO object,
              lv_name           TYPE ty_jd_name.
    
        FIELD-SYMBOLS:  TYPE any,
                                 TYPE any.
    
        lv_name = ms_item-obj_name.
    
        TRY.
            CREATE DATA lr_job_definition TYPE ('CL_JR_JOB_DEFINITION=>TY_JOB_DEFINITION').
            ASSIGN lr_job_definition->* TO .
            ASSERT sy-subrc = 0.
    
            CREATE OBJECT lo_job_definition TYPE ('CL_JR_JOB_DEFINITION')
              EXPORTING
                im_jd_name = lv_name.
    
            CALL METHOD lo_job_definition->('GET_JD_ATTRIBUTES')
              IMPORTING
                ex_jd_attributes = .
    
            ASSIGN COMPONENT 'CHANGED_BY' OF STRUCTURE  TO .
            IF sy-subrc = 0.
              rv_user = .
            ENDIF.
    
          CATCH cx_root ##NO_HANDLER.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lo_job_definition TYPE REF TO object,
              lv_name           TYPE c LENGTH 32.
    
        lv_name = ms_item-obj_name.
    
        TRY.
            CREATE OBJECT lo_job_definition TYPE ('CL_JR_JOB_DEFINITION')
              EXPORTING
                im_jd_name = lv_name.
    
            CALL METHOD lo_job_definition->('DELETE_JD').
    
          CATCH cx_root.
            zcx_abapgit_exception=>raise( |Error deleting JOBD| ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lr_job_definition TYPE REF TO data,
              lo_job_definition TYPE REF TO object,
              lx_error          TYPE REF TO cx_root,
              lv_name           TYPE ty_jd_name.
    
        FIELD-SYMBOLS:  TYPE any,
                                 TYPE any.
    
        lv_name = ms_item-obj_name.
    
        TRY.
            CREATE DATA lr_job_definition TYPE ('CL_JR_JOB_DEFINITION=>TY_JOB_DEFINITION').
            ASSIGN lr_job_definition->* TO .
            ASSERT sy-subrc = 0.
    
            io_xml->read(
              EXPORTING
                iv_name = 'JOBD'
              CHANGING
                cg_data =  ).
    
            CREATE OBJECT lo_job_definition TYPE ('CL_JR_JOB_DEFINITION')
              EXPORTING
                im_jd_name = lv_name.
    
            ASSIGN COMPONENT 'JDPACKAGE' OF STRUCTURE  TO .
    
             = iv_package.
    
            CALL METHOD lo_job_definition->('CREATE_JD')
              EXPORTING
                im_jd_attributes = .
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_name TYPE ty_jd_name.
    
        lv_name = ms_item-obj_name.
    
        CALL METHOD ('CL_JR_JD_MANAGER')=>('CHECK_JD_EXISTENCE')
          EXPORTING
            im_jd_name     = lv_name
          IMPORTING
            ex_is_existing = rv_bool.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: lv_obj_name TYPE e071-obj_name.
    
        lv_obj_name = ms_item-obj_name.
    
        CALL FUNCTION 'TR_OBJECT_JUMP_TO_TOOL'
          EXPORTING
            iv_pgmid          = 'R3TR'
            iv_object         = ms_item-obj_type
            iv_obj_name       = lv_obj_name
            iv_action         = 'SHOW'
          EXCEPTIONS
            jump_not_possible = 1
            OTHERS            = 2.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lr_job_definition TYPE REF TO data,
              lo_job_definition TYPE REF TO object,
              lv_name           TYPE ty_jd_name.
    
        FIELD-SYMBOLS:  TYPE any,
                                 TYPE any.
    
        lv_name = ms_item-obj_name.
    
        TRY.
            CREATE DATA lr_job_definition TYPE ('CL_JR_JOB_DEFINITION=>TY_JOB_DEFINITION').
            ASSIGN lr_job_definition->* TO .
            ASSERT sy-subrc = 0.
    
            CREATE OBJECT lo_job_definition TYPE ('CL_JR_JOB_DEFINITION')
              EXPORTING
                im_jd_name = lv_name.
    
            CALL METHOD lo_job_definition->('GET_JD_ATTRIBUTES')
              IMPORTING
                ex_jd_attributes = .
    
            ASSIGN COMPONENT 'JDPACKAGE' OF STRUCTURE  TO .
            CLEAR .
    
            ASSIGN COMPONENT 'BTCJOB_USER' OF STRUCTURE  TO .
            CLEAR .
    
            ASSIGN COMPONENT 'OWNER' OF STRUCTURE  TO .
            CLEAR .
    
            ASSIGN COMPONENT 'CREATED_DATE' OF STRUCTURE  TO .
            CLEAR .
    
            ASSIGN COMPONENT 'CREATED_TIME' OF STRUCTURE  TO .
            CLEAR .
    
            ASSIGN COMPONENT 'CHANGEDBY' OF STRUCTURE  TO .
            CLEAR .
    
            ASSIGN COMPONENT 'CHANGED_DATE' OF STRUCTURE  TO .
            CLEAR .
    
            ASSIGN COMPONENT 'CHANGED_TIME' OF STRUCTURE  TO .
            CLEAR .
    
            io_xml->add( iv_name = 'JOBD'
                         ig_data =  ).
    
          CATCH cx_root.
            zcx_abapgit_exception=>raise( |Error serializing JOBD| ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_msag IMPLEMENTATION.
    
      METHOD delete_documentation.
        DATA: lv_key_s TYPE dokhl-object.
    
        CLEAR lv_key_s.
        CALL FUNCTION 'DOCU_OBJECT_NAME_CONCATENATE'
          EXPORTING
            docu_id  = c_longtext_id_msag
            element  = iv_message_id
            addition = '   '
          IMPORTING
            object   = lv_key_s.
    
        CALL FUNCTION 'DOKU_DELETE_ALL'
          EXPORTING
            doku_id                        = c_longtext_id_msag
            doku_object                    = lv_key_s
            generic_use                    = 'X'
            suppress_authority             = space
            suppress_enqueue               = space
            suppress_transport             = space
          EXCEPTIONS
            header_without_text            = 1
            index_without_header           = 2
            no_authority_for_devclass_xxxx = 3
            no_docu_found                  = 4
            object_is_already_enqueued     = 5
            object_is_enqueued_by_corr     = 6
            user_break                     = 7
            OTHERS                         = 8.
        IF sy-subrc <> 0 AND sy-subrc <> 4.
          zcx_abapgit_exception=>raise( 'Error deleting longtext for message' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD delete_msgid.
    
        delete_documentation( iv_message_id ).
    
        DELETE FROM t100a WHERE arbgb = iv_message_id.
        IF sy-subrc = 0 OR sy-subrc = 4.
          CALL FUNCTION 'RS_TREE_OBJECT_PLACEMENT'
            EXPORTING
              object    = iv_message_id
              operation = 'DELETE'
              program   = space
              type      = 'CN'.
          DELETE FROM t100o WHERE arbgb = iv_message_id.
          DELETE FROM t100t WHERE arbgb = iv_message_id.    "#EC CI_NOFIRST
          DELETE FROM t100u WHERE arbgb = iv_message_id.
          DELETE FROM t100x WHERE arbgb = iv_message_id.
          DELETE FROM t100 WHERE arbgb = iv_message_id.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD deserialize_texts.
    
        DATA: lv_msg_id     TYPE rglif-message_id,
              ls_t100       TYPE t100,
              lt_t100t      TYPE TABLE OF t100t,
              lt_t100_texts TYPE ty_t100_texts,
              lt_t100u      TYPE TABLE OF t100u.
    
        FIELD-SYMBOLS:  TYPE ty_t100_text.
    
        lv_msg_id = ms_item-obj_name.
    
        SELECT * FROM t100u INTO TABLE lt_t100u
          WHERE arbgb = lv_msg_id ORDER BY PRIMARY KEY.     "#EC CI_GENBUFF
    
        ii_xml->read( EXPORTING iv_name = 'T100_TEXTS'
                      CHANGING  cg_data = lt_t100_texts ).
    
        ii_xml->read( EXPORTING iv_name = 'T100T'
                      CHANGING  cg_data = lt_t100t ).
    
        mo_i18n_params->trim_saplang_keyed_table(
          EXPORTING
            iv_lang_field_name = 'SPRSL'
          CHANGING
            ct_tab = lt_t100_texts ).
        mo_i18n_params->trim_saplang_keyed_table(
          EXPORTING
            iv_lang_field_name = 'SPRSL'
          CHANGING
            ct_tab = lt_t100t ).
    
        MODIFY t100t FROM TABLE lt_t100t.                     "#EC CI_SUBRC
    
        LOOP AT lt_t100_texts ASSIGNING .
          "check if message exists
          READ TABLE lt_t100u TRANSPORTING NO FIELDS
            WITH KEY arbgb = lv_msg_id msgnr = -msgnr BINARY SEARCH.
          CHECK sy-subrc = 0. "if original message doesn't exist no translations added
    
          MOVE-CORRESPONDING  TO ls_t100.
          ls_t100-arbgb = lv_msg_id.
          MODIFY t100 FROM ls_t100.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'MSAG: Table T100 modify failed' ).
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD free_access_permission.
        CALL FUNCTION 'RS_ACCESS_PERMISSION'
          EXPORTING
            mode         = 'FREE'
            object       = iv_message_id
            object_class = 'T100'.
      ENDMETHOD.
    
      METHOD serialize_longtexts_msag.
    
        DATA: lv_doku_object_name  TYPE dokhl-object,
              lt_doku_object_names TYPE STANDARD TABLE OF dokhl-object
                              WITH NON-UNIQUE DEFAULT KEY,
              lt_dokil             TYPE zif_abapgit_definitions=>ty_dokil_tt,
              ls_dokil             LIKE LINE OF lt_dokil,
              lt_language_filter   TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        FIELD-SYMBOLS:   TYPE t100.
    
        IF lines( it_t100 ) = 0.
          RETURN.
        ENDIF.
    
        LOOP AT it_t100 ASSIGNING .
    
          lv_doku_object_name = -arbgb && -msgnr.
          INSERT lv_doku_object_name INTO TABLE lt_doku_object_names.
    
        ENDLOOP.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          SELECT * FROM dokil
            INTO TABLE lt_dokil
            FOR ALL ENTRIES IN lt_doku_object_names
            WHERE id = c_longtext_id_msag
            AND object = lt_doku_object_names-table_line
            AND masterlang = abap_true
            ORDER BY PRIMARY KEY.
        ELSE.
          lt_language_filter = mo_i18n_params->build_language_filter( ).
          SELECT * FROM dokil
            INTO TABLE lt_dokil
            FOR ALL ENTRIES IN lt_doku_object_names
            WHERE id = c_longtext_id_msag
            AND object = lt_doku_object_names-table_line
            AND langu IN lt_language_filter
            ORDER BY PRIMARY KEY.
        ENDIF.
    
        CLEAR ls_dokil-dokstate.
        MODIFY lt_dokil FROM ls_dokil TRANSPORTING dokstate WHERE dokstate IS NOT INITIAL.
    
        IF lines( lt_dokil ) > 0.
          serialize_longtexts( ii_xml   = ii_xml
                               it_dokil = lt_dokil ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD serialize_texts.
    
        DATA: lv_msg_id          TYPE rglif-message_id,
              lt_t100_texts      TYPE ty_t100_texts,
              lt_t100t           TYPE TABLE OF t100t,
              lt_i18n_langs      TYPE TABLE OF langu,
              lt_language_filter TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        lv_msg_id = ms_item-obj_name.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          RETURN. " skip
        ENDIF.
    
        " Collect additional languages
        " Skip main lang - it has been already serialized and also technical languages
        lt_language_filter = mo_i18n_params->build_language_filter( ).
    
        SELECT DISTINCT sprsl AS langu INTO TABLE lt_i18n_langs
          FROM t100t
          WHERE arbgb = lv_msg_id
          AND sprsl IN lt_language_filter
          AND sprsl <> mv_language
          ORDER BY langu.                    "#EC CI_BYPASS "#EC CI_GENBUFF
    
        SORT lt_i18n_langs ASCENDING.
    
        IF lines( lt_i18n_langs ) > 0.
    
          SELECT * FROM t100t INTO CORRESPONDING FIELDS OF TABLE lt_t100t
            WHERE sprsl IN lt_language_filter
            AND sprsl <> mv_language
            AND arbgb = lv_msg_id
            ORDER BY PRIMARY KEY.                           "#EC CI_GENBUFF
    
          SELECT * FROM t100 INTO CORRESPONDING FIELDS OF TABLE lt_t100_texts
            WHERE sprsl IN lt_language_filter
            AND sprsl <> mv_language
            AND arbgb = lv_msg_id
            ORDER BY PRIMARY KEY.             "#EC CI_SUBRC "#EC CI_GENBUFF
    
          SORT lt_t100t BY sprsl ASCENDING.
          SORT lt_t100_texts BY sprsl msgnr ASCENDING.
    
          ii_xml->add( iv_name = 'I18N_LANGS'
                       ig_data = lt_i18n_langs ).
    
          ii_xml->add( iv_name = 'T100T'
                       ig_data = lt_t100t ).
    
          ii_xml->add( iv_name = 'T100_TEXTS'
                       ig_data = lt_t100_texts ).
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE lastuser FROM t100a INTO rv_user
          WHERE arbgb = ms_item-obj_name.                   "#EC CI_GENBUFF
        IF sy-subrc <> 0 OR rv_user = ''.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
        DATA: ls_t100a      TYPE t100a,
              lv_frozen     TYPE abap_bool,
              lv_message_id TYPE arbgb.
    
    * parameter SUPPRESS_DIALOG doesn't exist in all versions of FM RS_DELETE_MESSAGE_ID
    * replaced with a copy
        lv_message_id = ms_item-obj_name.
        IF ms_item-obj_name = space.
          zcx_abapgit_exception=>raise( 'Error from (copy of) RS_DELETE_MESSAGE_ID' )."blank message id
        ENDIF.
    
        SELECT SINGLE * FROM t100a INTO ls_t100a WHERE arbgb = ms_item-obj_name.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'Error from (copy of) RS_DELETE_MESSAGE_ID' )."not found
        ENDIF.
    
        CLEAR lv_frozen.
        CALL FUNCTION 'RS_ACCESS_PERMISSION'
          EXPORTING
            authority_check = 'X'
            global_lock     = 'X'
            mode            = 'MODIFY'
            object          = lv_message_id
            object_class    = 'T100'
          IMPORTING
            frozen          = lv_frozen
          EXCEPTIONS
            OTHERS          = 1.
    
        IF sy-subrc <> 0 OR lv_frozen <> space.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        zcl_abapgit_factory=>get_cts_api( )->insert_transport_object(
          iv_object   = 'MSAG'
          iv_obj_name = lv_message_id
          iv_package  = iv_package
          iv_language = mv_language
          iv_mode     = zif_abapgit_cts_api=>c_transport_mode-delete ).
    
        delete_msgid( lv_message_id ).
    
        free_access_permission( lv_message_id ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    * fm RPY_MESSAGE_ID_INSERT almost works, but not in older versions
    
        DATA: ls_t100a  TYPE t100a,
              ls_t100t  TYPE t100t,
              ls_t100u  TYPE t100u,
              lt_t100   TYPE TABLE OF t100,
              lt_before TYPE TABLE OF t100u.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_t100.
    
        io_xml->read( EXPORTING iv_name = 'T100A'
                      CHANGING cg_data = ls_t100a ).
        io_xml->read( EXPORTING iv_name = 'T100'
                      CHANGING cg_data = lt_t100 ).
    
        corr_insert( iv_package ).
    
        SELECT * FROM t100u INTO TABLE lt_before
          WHERE arbgb = ls_t100a-arbgb ORDER BY msgnr. "#EC CI_GENBUFF "#EC CI_BYPASS
    
        LOOP AT lt_t100 ASSIGNING .
          DELETE lt_before WHERE msgnr = -msgnr.
          MODIFY t100 FROM .
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'MSAG: Table T100 modify failed' ).
          ENDIF.
          CLEAR ls_t100u.
          MOVE-CORRESPONDING  TO ls_t100u ##ENH_OK.
          ls_t100u-name    = sy-uname.
          ls_t100u-datum   = sy-datum.
          ls_t100u-selfdef = '3'.
          MODIFY t100u FROM ls_t100u.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'MSAG: Table T100U modify failed' ).
          ENDIF.
        ENDLOOP.
    
        ls_t100a-masterlang = mv_language.
        ls_t100a-lastuser = sy-uname.
        ls_t100a-respuser = sy-uname.
        ls_t100a-ldate = sy-datum.
        ls_t100a-ltime = sy-uzeit.
        MODIFY t100a FROM ls_t100a.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'MSAG: Table T100A modify failed' ).
        ENDIF.
    
        ls_t100t-sprsl = mv_language.
        ls_t100t-arbgb = ls_t100a-arbgb.
        ls_t100t-stext = ls_t100a-stext.
        MODIFY t100t FROM ls_t100t.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'MSAG: Table T100T modify failed' ).
        ENDIF.
    
        LOOP AT lt_before INTO ls_t100u.
          DELETE FROM t100 WHERE arbgb = ls_t100u-arbgb
            AND msgnr = ls_t100u-msgnr.                       "#EC CI_SUBRC
    
          DELETE FROM t100u WHERE arbgb = ls_t100u-arbgb
            AND msgnr = ls_t100u-msgnr.                       "#EC CI_SUBRC
        ENDLOOP.
    
        deserialize_longtexts( ii_xml         = io_xml
                               iv_longtext_id = c_longtext_id_msag ).
    
        IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
          deserialize_texts( io_xml ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_arbgb TYPE t100a-arbgb.
    
        SELECT SINGLE arbgb FROM t100a INTO lv_arbgb
          WHERE arbgb = ms_item-obj_name.                   "#EC CI_GENBUFF
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lv_argument TYPE seqg3-garg.
    
        lv_argument   = |{ ms_item-obj_name }|.
        OVERLAY lv_argument WITH '                     '.
        lv_argument = lv_argument && '*'.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = |ES_MSGSI|
                                                iv_argument    = lv_argument ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_msg_id TYPE rglif-message_id,
              ls_inf    TYPE t100a,
              lt_source TYPE ty_t100s.
    
        lv_msg_id = ms_item-obj_name.
    
        SELECT SINGLE * FROM t100a INTO ls_inf
          WHERE arbgb = lv_msg_id.                          "#EC CI_GENBUFF
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
        CLEAR ls_inf-respuser.
    
        SELECT * FROM t100 INTO TABLE lt_source
          WHERE sprsl = mv_language
          AND arbgb = lv_msg_id
          ORDER BY PRIMARY KEY.               "#EC CI_SUBRC "#EC CI_GENBUFF
    
        CLEAR: ls_inf-lastuser,
               ls_inf-ldate,
               ls_inf-ltime.
    
        io_xml->add( iv_name = 'T100A'
                     ig_data = ls_inf ).
        io_xml->add( ig_data = lt_source
                     iv_name = 'T100' ).
    
        serialize_longtexts_msag( it_t100 = lt_source
                                  ii_xml  = io_xml ).
    
        IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
          serialize_texts( io_xml ).
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_nont IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
        DATA: lv_user  TYPE string,
              lx_error TYPE REF TO cx_root.
    
        TRY.
    
            SELECT SINGLE changed_by INTO lv_user
                FROM (c_table_name)
                WHERE nont_name = ms_item-obj_name AND version = 'I'.
    
            IF lv_user IS INITIAL.
              SELECT SINGLE changed_by INTO lv_user
                FROM (c_table_name)
                WHERE nont_name = ms_item-obj_name AND version = 'A'.
            ENDIF.
    
            rv_user = lv_user.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_nrob IMPLEMENTATION.
    
      METHOD delete_intervals.
    
        DATA: lv_error    TYPE c LENGTH 1,
              ls_error    TYPE inrer,
              lt_list     TYPE STANDARD TABLE OF inriv WITH DEFAULT KEY,
              lt_error_iv TYPE STANDARD TABLE OF inriv WITH DEFAULT KEY.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_list.
    
        CALL FUNCTION 'NUMBER_RANGE_INTERVAL_LIST'
          EXPORTING
            object                     = iv_object
          TABLES
            interval                   = lt_list
          EXCEPTIONS
            nr_range_nr1_not_found     = 1
            nr_range_nr1_not_intern    = 2
            nr_range_nr2_must_be_space = 3
            nr_range_nr2_not_extern    = 4
            nr_range_nr2_not_found     = 5
            object_not_found           = 6
            subobject_must_be_space    = 7
            subobject_not_found        = 8
            OTHERS                     = 9.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        IF lines( lt_list ) = 0.
          RETURN.
        ENDIF.
    
        LOOP AT lt_list ASSIGNING .
          CLEAR -nrlevel.
          -procind = 'D'.
        ENDLOOP.
    
        CALL FUNCTION 'NUMBER_RANGE_INTERVAL_UPDATE'
          EXPORTING
            object           = iv_object
          IMPORTING
            error            = ls_error
            error_occured    = lv_error
          TABLES
            error_iv         = lt_error_iv
            interval         = lt_list
          EXCEPTIONS
            object_not_found = 1
            OTHERS           = 2.
        IF sy-subrc <> 0 OR lv_error = abap_true.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CALL FUNCTION 'NUMBER_RANGE_UPDATE_CLOSE'
          EXPORTING
            object                 = iv_object
          EXCEPTIONS
            no_changes_made        = 1
            object_not_initialized = 2
            OTHERS                 = 3.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lv_objectid TYPE cdhdr-objectid,
              lt_cdhdr    TYPE cdhdr_tab.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_cdhdr.
    
        lv_objectid = ms_item-obj_name.
    
        CALL FUNCTION 'CHANGEDOCUMENT_READ_HEADERS'
          EXPORTING
            objectclass                = 'NRKROBJ'
            objectid                   = lv_objectid
          TABLES
            i_cdhdr                    = lt_cdhdr
          EXCEPTIONS
            no_position_found          = 1
            wrong_access_to_archive    = 2
            time_zone_conversion_error = 3
            OTHERS                     = 4.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
          RETURN.
        ENDIF.
    
        SORT lt_cdhdr BY udate DESCENDING utime DESCENDING.
    
        READ TABLE lt_cdhdr INDEX 1 ASSIGNING .
        ASSERT sy-subrc = 0.
    
        rv_user = -username.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_object TYPE tnro-object.
    
        lv_object = ms_item-obj_name.
    
        delete_intervals( lv_object ).
    
        CALL FUNCTION 'NUMBER_RANGE_OBJECT_DELETE'
          EXPORTING
            language           = mv_language
            object             = lv_object
          EXCEPTIONS
            delete_not_allowed = 1
            object_not_found   = 2
            wrong_indicator    = 3
            OTHERS             = 4.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lt_errors     TYPE TABLE OF inoer,
              ls_attributes TYPE tnro,
              ls_text       TYPE tnrot.
    
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE uccheck.
    
        io_xml->read( EXPORTING iv_name = 'ATTRIBUTES'
                      CHANGING  cg_data = ls_attributes ).
        io_xml->read( EXPORTING iv_name = 'TEXT'
                      CHANGING  cg_data = ls_text ).
    
        ASSIGN COMPONENT 'CHANGED_AT' OF STRUCTURE ls_attributes TO .
        IF sy-subrc = 0.
          GET TIME STAMP FIELD .
        ENDIF.
        ASSIGN COMPONENT 'CHANGED_BY' OF STRUCTURE ls_attributes TO .
        IF sy-subrc = 0.
           = sy-uname.
        ENDIF.
        ASSIGN COMPONENT 'ENAME' OF STRUCTURE ls_attributes TO .
        IF sy-subrc = 0.
           = sy-uname.
        ENDIF.
        ASSIGN COMPONENT 'EDATE' OF STRUCTURE ls_attributes TO .
        IF sy-subrc = 0.
           = sy-datum.
        ENDIF.
        ASSIGN COMPONENT 'ETIME' OF STRUCTURE ls_attributes TO .
        IF sy-subrc = 0.
           = sy-uzeit.
        ENDIF.
    
        ASSIGN COMPONENT 'UNAME' OF STRUCTURE ls_text TO .
        IF sy-subrc = 0.
           = sy-uname.
        ENDIF.
        ASSIGN COMPONENT 'UDATE' OF STRUCTURE ls_text TO .
        IF sy-subrc = 0.
           = sy-datum.
        ENDIF.
        ASSIGN COMPONENT 'UTIME' OF STRUCTURE ls_text TO .
        IF sy-subrc = 0.
           = sy-uzeit.
        ENDIF.
        ASSIGN COMPONENT 'ENAME' OF STRUCTURE ls_text TO .
        IF sy-subrc = 0.
           = sy-uname.
        ENDIF.
        ASSIGN COMPONENT 'EDATE' OF STRUCTURE ls_text TO .
        IF sy-subrc = 0.
           = sy-datum.
        ENDIF.
        ASSIGN COMPONENT 'ETIME' OF STRUCTURE ls_text TO .
        IF sy-subrc = 0.
           = sy-uzeit.
        ENDIF.
    
        ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE ls_attributes TO .
        IF sy-subrc = 0.
          set_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
    
        CALL FUNCTION 'NUMBER_RANGE_OBJECT_UPDATE'
          EXPORTING
            indicator                 = 'I'
            object_attributes         = ls_attributes
            object_text               = ls_text
          TABLES
            errors                    = lt_errors
          EXCEPTIONS
            object_already_exists     = 1
            object_attributes_missing = 2
            object_not_found          = 3
            object_text_missing       = 4
            wrong_indicator           = 5
            OTHERS                    = 6.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        tadir_insert( iv_package ).
        corr_insert( iv_package ).
    
        CALL FUNCTION 'NUMBER_RANGE_OBJECT_CLOSE'
          EXPORTING
            object                 = ls_attributes-object
          EXCEPTIONS
            object_not_initialized = 1.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_object TYPE tnro-object.
    
        SELECT SINGLE object FROM tnro INTO lv_object
          WHERE object = ms_item-obj_name.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: ls_bcdata TYPE bdcdata,
              lt_bcdata TYPE STANDARD TABLE OF bdcdata.
    
        ls_bcdata-program  = 'SAPMSNRO'.
        ls_bcdata-dynpro   = '0150'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam     = 'NRIV-OBJECT'.
        ls_bcdata-fval     = ms_item-obj_name.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam = 'BDC_OKCODE'.
        ls_bcdata-fval = '=DISP'.
        APPEND ls_bcdata TO lt_bcdata.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'SNRO'
          it_bdcdata = lt_bcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_object     TYPE tnro-object,
              ls_attributes TYPE tnro,
              ls_text       TYPE tnrot.
    
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE uccheck.
    
        lv_object = ms_item-obj_name.
    
        CALL FUNCTION 'NUMBER_RANGE_OBJECT_READ'
          EXPORTING
            language          = mv_language
            object            = lv_object
          IMPORTING
            object_attributes = ls_attributes
            object_text       = ls_text
          EXCEPTIONS
            object_not_found  = 1
            OTHERS            = 2.
        IF sy-subrc = 1.
          RETURN.
        ELSEIF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        ASSIGN COMPONENT 'CHANGED_AT' OF STRUCTURE ls_attributes TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
        ASSIGN COMPONENT 'CHANGED_BY' OF STRUCTURE ls_attributes TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
        ASSIGN COMPONENT 'ENAME' OF STRUCTURE ls_attributes TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
        ASSIGN COMPONENT 'EDATE' OF STRUCTURE ls_attributes TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
        ASSIGN COMPONENT 'ETIME' OF STRUCTURE ls_attributes TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
    
        ASSIGN COMPONENT 'UNAME' OF STRUCTURE ls_text TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
        ASSIGN COMPONENT 'UDATE' OF STRUCTURE ls_text TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
        ASSIGN COMPONENT 'UTIME' OF STRUCTURE ls_text TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
        ASSIGN COMPONENT 'ENAME' OF STRUCTURE ls_text TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
        ASSIGN COMPONENT 'EDATE' OF STRUCTURE ls_text TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
        ASSIGN COMPONENT 'ETIME' OF STRUCTURE ls_text TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
    
        ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE ls_attributes TO .
        IF sy-subrc = 0.
          clear_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
    
        io_xml->add( iv_name = 'ATTRIBUTES'
                     ig_data = ls_attributes ).
        io_xml->add( iv_name = 'TEXT'
                     ig_data = ls_text ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_nspc IMPLEMENTATION.
    
      METHOD add_to_transport.
    
        DATA: li_sap_package TYPE REF TO zif_abapgit_sap_package.
    
        li_sap_package = zcl_abapgit_factory=>get_sap_package( iv_package ).
    
        IF li_sap_package->are_changes_recorded_in_tr_req( ) = abap_true.
          corr_insert( iv_package ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_component = replace( val  = is_item-obj_name
                                sub  = '/'
                                with = ''
                                occ  = 0 ).
    
      ENDMETHOD.
    
      METHOD deserialize_sw_component.
    
        DATA:
          ls_cvers_old TYPE cvers,
          ls_cvers_new TYPE cvers,
          ls_cvers_ref TYPE cvers_ref.
    
        ii_xml->read( EXPORTING iv_name = 'CVERS'
                      CHANGING  cg_data = ls_cvers_new ).
    
        ii_xml->read( EXPORTING iv_name = 'CVERS_REF'
                      CHANGING  cg_data = ls_cvers_ref ).
    
        IF ls_cvers_new IS NOT INITIAL.
          SELECT SINGLE * FROM cvers INTO ls_cvers_old WHERE component = mv_component.
          IF sy-subrc = 0.
            IF ls_cvers_old <> ls_cvers_new.
              zcx_abapgit_exception=>raise( `Update of software component not supported.`
                && ` Use Software Update Manager (SUM)` ).
            ENDIF.
          ELSE.
            INSERT cvers FROM ls_cvers_new.
          ENDIF.
        ENDIF.
    
        IF ls_cvers_ref IS NOT INITIAL.
          MODIFY cvers_ref FROM ls_cvers_ref.
          IF sy-subrc <> 0.
            INSERT cvers_ref FROM ls_cvers_ref.
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD deserialize_texts.
    
        DATA:
          ls_trnspacett TYPE trnspacett,
          lt_i18n_langs TYPE TABLE OF langu,
          lt_cvers_refs TYPE TABLE OF cvers_ref,
          ls_cvers_ref  TYPE cvers_ref,
          lt_nspc_texts TYPE ty_nspc_texts.
    
        FIELD-SYMBOLS:
                LIKE LINE OF lt_i18n_langs,
           LIKE LINE OF lt_nspc_texts.
    
        ii_xml->read( EXPORTING iv_name = 'I18N_LANGS'
                      CHANGING  cg_data = lt_i18n_langs ).
    
        ii_xml->read( EXPORTING iv_name = 'NSPC_TEXTS'
                      CHANGING  cg_data = lt_nspc_texts ).
    
        ii_xml->read( EXPORTING iv_name = 'CVERS_REFS'
                      CHANGING  cg_data = lt_cvers_refs ).
    
        SORT lt_i18n_langs.
        SORT lt_nspc_texts BY spras. " Optimization
        SORT lt_cvers_refs BY langu. " Optimization
    
        LOOP AT lt_i18n_langs ASSIGNING .
          ls_trnspacett-namespace = iv_namespace.
          READ TABLE lt_nspc_texts ASSIGNING  WITH KEY spras = .
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( |Cannot find language {  } in XML| ).
          ENDIF.
          MOVE-CORRESPONDING  TO ls_trnspacett.
    
          MODIFY trnspacett FROM ls_trnspacett.
          IF sy-subrc <> 0.
            INSERT trnspacett FROM ls_trnspacett.
          ENDIF.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( |Error upserting text for namespace| ).
          ENDIF.
    
          READ TABLE lt_cvers_refs INTO ls_cvers_ref WITH KEY langu = .
          IF sy-subrc = 0.
            MODIFY cvers_ref FROM ls_cvers_ref.
            IF sy-subrc <> 0.
              INSERT cvers_ref FROM ls_cvers_ref.
            ENDIF.
            IF sy-subrc <> 0.
              zcx_abapgit_exception=>raise( |Error upserting text for software component| ).
            ENDIF.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD serialize_sw_component.
    
        DATA:
          ls_cvers     TYPE cvers,
          ls_cvers_ref TYPE cvers_ref.
    
        SELECT SINGLE * FROM cvers INTO ls_cvers WHERE component = mv_component.
        IF sy-subrc = 0.
          ii_xml->add( iv_name = 'CVERS'
                       ig_data = ls_cvers ).
        ENDIF.
    
        SELECT SINGLE * FROM cvers_ref INTO ls_cvers_ref WHERE component = mv_component AND langu = mv_language.
        IF sy-subrc = 0.
          ii_xml->add( iv_name = 'CVERS_REF'
                       ig_data = ls_cvers_ref ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD serialize_texts.
    
        DATA:
          ls_trnspacett TYPE trnspacett,
          lt_nspc_texts TYPE ty_nspc_texts,
          lt_cvers_refs TYPE TABLE OF cvers_ref,
          lt_i18n_langs TYPE TABLE OF langu.
    
        FIELD-SYMBOLS:
                LIKE LINE OF lt_i18n_langs,
           LIKE LINE OF lt_nspc_texts.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          RETURN.
        ENDIF.
    
        " Collect additional languages, skip main lang - it was serialized already
        SELECT DISTINCT spras AS langu FROM trnspacett INTO TABLE lt_i18n_langs
          WHERE namespace = ms_item-obj_name AND spras <> mv_language
          ORDER BY langu.                                     "#EC CI_SUBRC
    
        LOOP AT lt_i18n_langs ASSIGNING .
          SELECT SINGLE * FROM trnspacett INTO ls_trnspacett
            WHERE namespace = ms_item-obj_name AND spras = .
          IF sy-subrc = 0.
            APPEND INITIAL LINE TO lt_nspc_texts ASSIGNING .
            MOVE-CORRESPONDING ls_trnspacett TO .
          ENDIF.
    
          SELECT * FROM cvers_ref APPENDING TABLE lt_cvers_refs
            WHERE component = mv_component AND langu = 
            ORDER BY PRIMARY KEY.
        ENDLOOP.
    
        SORT lt_i18n_langs ASCENDING.
        SORT lt_nspc_texts BY spras ASCENDING.
        SORT lt_cvers_refs.
    
        IF lines( lt_i18n_langs ) > 0.
          ii_xml->add( iv_name = 'I18N_LANGS'
                       ig_data = lt_i18n_langs ).
    
          ii_xml->add( iv_name = 'NSPC_TEXTS'
                       ig_data = lt_nspc_texts ).
    
          ii_xml->add( iv_name = 'CVERS_REFS'
                       ig_data = lt_cvers_refs ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        SELECT SINGLE changeuser FROM trnspacet INTO rv_user
           WHERE namespace = ms_item-obj_name.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
        RETURN. " not supported
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA:
          ls_nspc       TYPE ty_nspc,
          ls_nspc_text  TYPE ty_nspc_text,
          lv_modifiable TYPE abap_bool,
          ls_trnspacet  TYPE trnspacet,
          ls_trnspacett TYPE trnspacett.
    
        io_xml->read( EXPORTING iv_name = 'NSPC'
                      CHANGING  cg_data = ls_nspc ).
    
        io_xml->read( EXPORTING iv_name = 'NSPC_TEXT'
                      CHANGING  cg_data = ls_nspc_text ).
    
        add_to_transport( iv_package ).
    
        SELECT SINGLE * FROM trnspacet INTO ls_trnspacet WHERE namespace = ls_nspc-namespace.
        IF sy-subrc = 0.
          " For existing namespace, check if it's modifiable (SE03)
          SELECT SINGLE editflag FROM trnspace INTO lv_modifiable WHERE namespace = ls_nspc-namespace.
          IF sy-subrc = 0 AND lv_modifiable = abap_false.
            zcx_abapgit_exception=>raise( |Namespace is not modifiable| ).
          ENDIF.
    
          " keep existing role
          ls_trnspacet-replicense = ls_nspc-replicense.
          ls_trnspacet-sscrflag   = ls_nspc-sscrflag.
          ls_trnspacet-sapflag    = ls_nspc-sapflag.
          ls_trnspacet-gen_only   = ls_nspc-gen_only.
          ls_trnspacet-changeuser = sy-uname.
          ls_trnspacet-changedate = sy-datum.
          MODIFY trnspacet FROM ls_trnspacet.
        ELSE.
          MOVE-CORRESPONDING ls_nspc TO ls_trnspacet.
          ls_trnspacet-role       = 'C'. " customer repair license
          ls_trnspacet-changeuser = sy-uname.
          ls_trnspacet-changedate = sy-datum.
          INSERT trnspacet FROM ls_trnspacet.
        ENDIF.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error upserting namespace| ).
        ENDIF.
    
        SELECT SINGLE * FROM trnspacett INTO ls_trnspacett
          WHERE namespace = ls_nspc-namespace AND spras = mv_language.
        IF sy-subrc = 0.
          ls_trnspacett-descriptn = ls_nspc_text-descriptn.
          ls_trnspacett-owner     = ls_nspc_text-owner.
          MODIFY trnspacett FROM ls_trnspacett.
        ELSE.
          MOVE-CORRESPONDING ls_nspc_text TO ls_trnspacett.
          ls_trnspacett-namespace = ls_nspc-namespace.
          INSERT trnspacett FROM ls_trnspacett.
        ENDIF.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error upserting text for namespace| ).
        ENDIF.
    
        deserialize_texts( ii_xml       = io_xml
                           iv_namespace = ls_nspc-namespace ).
    
        deserialize_sw_component( io_xml ).
    
        " Fill trnspace and trnspacel tables
        CALL FUNCTION 'TR_ACTIVATE_NAMESPACE'
          EXPORTING
            iv_namespace         = ls_nspc-namespace
          EXCEPTIONS
            deletion_not_allowed = 1
            OTHERS               = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error activating namespace| ).
        ENDIF.
    
        " Make namespace modifiable
        UPDATE trnspace SET editflag = abap_true WHERE namespace = ls_nspc-namespace.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA lv_namespace TYPE trnspace-namespace.
    
        lv_namespace = ms_item-obj_name.
    
        CALL FUNCTION 'TR_CHECK_NAMESPACE'
          EXPORTING
            iv_namespace        = lv_namespace
          EXCEPTIONS
            namespace_not_valid = 1
            OTHERS              = 2.
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = zif_abapgit_object~exists( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Launch general maintenance for namespaces
        CALL FUNCTION 'VIEW_MAINTENANCE_CALL'
          EXPORTING
            action                       = 'S'
            view_name                    = 'V_TRNSPACE'
            no_warning_for_clientindep   = 'X'
            variant_for_selection        = 'STANDARD'
          EXCEPTIONS
            client_reference             = 1
            foreign_lock                 = 2
            invalid_action               = 3
            no_clientindependent_auth    = 4
            no_database_function         = 5
            no_editor_function           = 6
            no_show_auth                 = 7
            no_tvdir_entry               = 8
            no_upd_auth                  = 9
            only_show_allowed            = 10
            system_failure               = 11
            unknown_field_in_dba_sellist = 12
            view_not_found               = 13
            OTHERS                       = 14.
    
        rv_exit = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          ls_nspc      TYPE ty_nspc,
          ls_nspc_text TYPE ty_nspc_text.
    
        SELECT SINGLE * FROM trnspacet INTO CORRESPONDING FIELDS OF ls_nspc
          WHERE namespace = ms_item-obj_name.
    
        SELECT SINGLE * FROM trnspacett INTO CORRESPONDING FIELDS OF ls_nspc_text
          WHERE namespace = ms_item-obj_name AND spras = mv_language.
    
        io_xml->add( iv_name = 'NSPC'
                     ig_data = ls_nspc ).
    
        io_xml->add( iv_name = 'NSPC_TEXT'
                     ig_data = ls_nspc_text ).
    
        serialize_texts( io_xml ).
    
        serialize_sw_component( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_oa2p IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_profile = is_item-obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_persist     TYPE REF TO object,
              lr_wb          TYPE REF TO data,
              lo_profile     TYPE REF TO object,
              lv_profile_key TYPE seu_objkey.
    
        FIELD-SYMBOLS:  TYPE any.
    
        lv_profile_key = mv_profile.
        CREATE OBJECT lo_persist TYPE ('CL_OA2P_OBJECT_PERSIST').
    
        CREATE OBJECT lo_profile TYPE ('CL_OA2P_OBJECT_DATA').
        CREATE DATA lr_wb TYPE REF TO ('IF_WB_OBJECT_DATA_MODEL').
        ASSIGN lr_wb->* TO .
         ?= lo_profile.
    
        TRY.
            CALL METHOD lo_persist->('IF_WB_OBJECT_PERSIST~GET')
              EXPORTING
                p_object_key  = lv_profile_key    " Object Key
                p_version     = 'A'    " Version (Active/Inactive)
              CHANGING
                p_object_data = .  " Object Data
          CATCH cx_swb_object_does_not_exist.
            zcx_abapgit_exception=>raise( |OAuth2 Profile { lv_profile_key } doesn't exist.| ).
          CATCH cx_swb_exception.
            zcx_abapgit_exception=>raise( |Error when getting details of OAuth2 Profile { lv_profile_key }.| ).
        ENDTRY.
    
        lo_profile = .
        CALL METHOD lo_profile->('IF_WB_OBJECT_DATA_MODEL~GET_CHANGED_BY')
          RECEIVING
            p_user_name = rv_user.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        CONSTANTS: lc_actvt TYPE c LENGTH 2 VALUE `06`.
    
        DATA: lo_persist     TYPE REF TO object,
              lv_profile_key TYPE seu_objkey.
    
        "authority check
        AUTHORITY-CHECK OBJECT 'S_OA2C_ADM'
          ID 'ACTVT' FIELD lc_actvt ##AUTH_OBJ_OK.
        IF sy-subrc <> 0.
          MESSAGE e463(01) WITH mv_profile INTO zcx_abapgit_exception=>null.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        "delete profile
        lv_profile_key = mv_profile.
        CREATE OBJECT lo_persist TYPE ('CL_OA2P_OBJECT_PERSIST').
    
        TRY.
            CALL METHOD lo_persist->('IF_WB_OBJECT_PERSIST~DELETE')
              EXPORTING
                p_object_key = lv_profile_key.   " Object Key
          CATCH cx_swb_object_does_not_exist ##NO_HANDLER.
          CATCH cx_swb_exception.
            zcx_abapgit_exception=>raise( |Error when deleting OAuth2 Profile { lv_profile_key }.| ).
        ENDTRY.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lo_persist      TYPE REF TO object,
              lo_profile      TYPE REF TO object,
              lr_wb           TYPE REF TO data,
              lr_profile_data TYPE REF TO data.
        FIELD-SYMBOLS:  TYPE data,
                                  TYPE any.
    
        CREATE DATA lr_profile_data TYPE ('OA2C_SX_OA2P_OBJECT_DATA').
        ASSIGN lr_profile_data->* TO .
    
        io_xml->read( EXPORTING iv_name = 'PROFILE'
                      CHANGING cg_data =  ).
    
        CREATE OBJECT lo_profile TYPE ('CL_OA2P_OBJECT_DATA').
        CREATE DATA lr_wb TYPE REF TO ('IF_WB_OBJECT_DATA_MODEL').
        ASSIGN lr_wb->* TO .
         ?= lo_profile.
    
        CALL METHOD lo_profile->('IF_WB_OBJECT_DATA_MODEL~SET_DATA')
          EXPORTING
            p_data = .
    
        CREATE OBJECT lo_persist TYPE ('CL_OA2P_OBJECT_PERSIST').
        TRY.
            CALL METHOD lo_persist->('IF_WB_OBJECT_PERSIST~SAVE')
              EXPORTING
                p_object_data = .   " Object Data
          CATCH cx_swb_exception.
            zcx_abapgit_exception=>raise( |Error deserialize profile { mv_profile }.| ).
        ENDTRY.
    
        tadir_insert( iv_package ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        CALL METHOD ('CL_OA2P_OBJECT_PERSIST')=>('CHECK_EXISTS_ON_DB')
          EXPORTING
            i_profile = mv_profile
          RECEIVING
            r_exists  = rv_bool.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lv_profile_name TYPE eqegraarg,
              lv_lock_number  TYPE i,
              lt_locks        TYPE STANDARD TABLE OF seqg3.
    
        lv_profile_name = mv_profile.
    
        CALL FUNCTION 'ENQUEUE_READ'
          EXPORTING
            gclient = sy-mandt    " Client
            gname   = 'OA2C_PROFILES'    " Granularity name (-> table name)
            garg    = lv_profile_name    " Granularity value(->values of key fields)
          IMPORTING
            number  = lv_lock_number
          TABLES
            enq     = lt_locks.    " Number of chosen lock entries
    
        rv_is_locked = boolc( lv_lock_number > 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lo_persist      TYPE REF TO object,
              lo_profile      TYPE REF TO object,
              lv_profile_key  TYPE seu_objkey,
              lr_profile_data TYPE REF TO data,
              lr_wb           TYPE REF TO data.
    
        FIELD-SYMBOLS:  TYPE data,
                           TYPE any,
                                  TYPE any.
    
        CREATE DATA lr_profile_data TYPE ('OA2C_SX_OA2P_OBJECT_DATA').
        ASSIGN lr_profile_data->* TO .
    
        lv_profile_key = mv_profile.
        CREATE OBJECT lo_persist TYPE ('CL_OA2P_OBJECT_PERSIST').
        CREATE OBJECT lo_profile TYPE ('CL_OA2P_OBJECT_DATA').
        CREATE DATA lr_wb TYPE REF TO ('IF_WB_OBJECT_DATA_MODEL').
        ASSIGN lr_wb->* TO .
         ?= lo_profile.
    
        TRY.
            CALL METHOD lo_persist->('IF_WB_OBJECT_PERSIST~GET')
              EXPORTING
                p_object_key  = lv_profile_key    " Object Key
                p_version     = 'A'    " Version (Active/Inactive)
              CHANGING
                p_object_data = .  " Object Data
          CATCH cx_swb_object_does_not_exist.
            zcx_abapgit_exception=>raise( |OAuth2 Profile { lv_profile_key } doesn't exist.| ).
          CATCH cx_swb_exception.
            zcx_abapgit_exception=>raise( |Error when getting details of OAuth2 Profile { lv_profile_key }.| ).
        ENDTRY.
    
        "remove system specific information
        lo_profile = .
        CALL METHOD lo_profile->('IF_WB_OBJECT_DATA_MODEL~SET_CHANGED_BY')
          EXPORTING
            p_user_name = ''.
        CALL METHOD lo_profile->('IF_WB_OBJECT_DATA_MODEL~SET_CHANGED_ON')
          EXPORTING
            p_date = '00000000'
            p_time = '000000'.
        CALL METHOD lo_profile->('IF_WB_OBJECT_DATA_MODEL~SET_CREATED_BY')
          EXPORTING
            p_user_name = ''.
        CALL METHOD lo_profile->('IF_WB_OBJECT_DATA_MODEL~SET_CREATED_ON')
          EXPORTING
            p_date = '00000000'
            p_time = '000000'.
    
        CALL METHOD lo_profile->('IF_WB_OBJECT_DATA_MODEL~GET_DATA')
          IMPORTING
            p_data = .
    
        "remove runtime information
        ASSIGN COMPONENT 'O_SPECIFICS' OF STRUCTURE  TO .
        CLEAR .
    
        io_xml->add( iv_name = 'PROFILE'
                     ig_data =  ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_odso IMPLEMENTATION.
    
      METHOD constructor.
    
        DATA: lr_details TYPE REF TO data.
    
        super->constructor(
            is_item        = is_item
            iv_language    = iv_language
            io_files       = io_files
            io_i18n_params = io_i18n_params ).
    
        TRY.
            CREATE DATA lr_details TYPE ('BAPI6116').
          CATCH cx_sy_create_data_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD clear_field.
    
        FIELD-SYMBOLS:  TYPE data.
    
        ASSIGN COMPONENT iv_fieldname
               OF STRUCTURE cg_metadata
               TO .
        ASSERT sy-subrc = 0.
    
        CLEAR: .
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lv_dsonam  TYPE c LENGTH 30,
              ls_return  TYPE bapiret2,
              lr_details TYPE REF TO data.
    
        FIELD-SYMBOLS:  TYPE any,
                         TYPE any.
    
        CREATE DATA lr_details TYPE ('BAPI6116').
    
        ASSIGN lr_details->* TO .
    
        lv_dsonam = ms_item-obj_name.
    
        CALL FUNCTION 'BAPI_ODSO_GETDETAIL'
          EXPORTING
            odsobject = lv_dsonam
          IMPORTING
            details   = 
            return    = ls_return.
    
        IF ls_return-type = 'E'.
          zcx_abapgit_exception=>raise( |Error when getting changed by of ODSO: { ls_return-message }| ).
        ENDIF.
    
        ASSIGN COMPONENT 'TSTPNM' OF STRUCTURE  TO .
    
        rv_user = .
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_odsonam    TYPE c LENGTH 30,
              lv_objname    TYPE sobj_name,
              lo_collection TYPE REF TO object,
              lt_msg        TYPE STANDARD TABLE OF bal_s_msg,
              ls_msg        TYPE bal_s_msg.
    
        CREATE OBJECT lo_collection TYPE ('CL_RSD_ODSO_COLLECTION').
    
        lv_odsonam = ms_item-obj_name.
        lv_objname = ms_item-obj_name.
    
        TRY.
            CALL METHOD lo_collection->('ADD_TLOGO')
              EXPORTING
                i_objnm  = lv_objname
                i_modify = abap_true
                i_delete = abap_true.
    
            CALL METHOD lo_collection->('DELETE').
    
            CALL METHOD ('CL_RSO_APPLICATION_LOG')=>('APPL_LOG_MSG_READ')
              IMPORTING
                e_t_msg = lt_msg.
    
            READ TABLE lt_msg WITH KEY msgty = 'E' INTO ls_msg.
            IF sy-subrc = 0.
              zcx_abapgit_exception=>raise(
              |Error when deleting ODSO: { ms_item-obj_name } { ls_msg-msgv1 } { ls_msg-msgv2 }| ).
            ENDIF.
    
          CATCH cx_root.
            zcx_abapgit_exception=>raise( |Canceled deletion of ODSO: { ms_item-obj_name }| ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_dsonam      TYPE c LENGTH 30,
              lr_details     TYPE REF TO data,
              lr_infoobjects TYPE REF TO data,
              lr_navigation  TYPE REF TO data,
              lr_indexes     TYPE REF TO data,
              lr_index_iobj  TYPE REF TO data,
              lt_return      TYPE STANDARD TABLE OF bapiret2,
              ls_return      TYPE bapiret2.
    
        FIELD-SYMBOLS:
               TYPE any,
             TYPE any,
           TYPE STANDARD TABLE,
            TYPE STANDARD TABLE,
               TYPE STANDARD TABLE,
            TYPE STANDARD TABLE.
    
        CREATE DATA lr_details     TYPE ('BAPI6116').
        CREATE DATA lr_infoobjects TYPE STANDARD TABLE OF ('BAPI6116IO').
        CREATE DATA lr_navigation  TYPE STANDARD TABLE OF ('BAPI6116NA').
        CREATE DATA lr_indexes     TYPE STANDARD TABLE OF ('BAPI6116IN').
        CREATE DATA lr_index_iobj  TYPE STANDARD TABLE OF ('BAPI6116II').
    
        ASSIGN lr_details->* TO .
        ASSIGN lr_infoobjects->* TO .
        ASSIGN lr_navigation->* TO .
        ASSIGN lr_indexes->* TO .
        ASSIGN lr_index_iobj->* TO .
    
        io_xml->read( EXPORTING iv_name = 'ODSO'
                      CHANGING  cg_data =  ).
    
        io_xml->read( EXPORTING iv_name = 'INFOOBJECTS'
                      CHANGING  cg_data =  ).
    
        io_xml->read( EXPORTING iv_name = 'NAVIGATION'
                      CHANGING  cg_data =  ).
    
        io_xml->read( EXPORTING iv_name = 'INDEXES'
                      CHANGING  cg_data =  ).
    
        io_xml->read( EXPORTING iv_name = 'INDEX_IOBJ'
                      CHANGING  cg_data =  ).
        TRY.
    
            ASSIGN COMPONENT 'ODSOBJECT' OF STRUCTURE  TO .
            ASSERT sy-subrc = 0.
    
            IF zif_abapgit_object~exists( ) = abap_false.
              CALL FUNCTION 'BAPI_ODSO_CREATE'
                EXPORTING
                  details              = 
                IMPORTING
                  odsobject            = lv_dsonam
                TABLES
                  infoobjects          = 
                  navigationattributes = 
                  indexes              = 
                  indexesinfoobjects   = 
                  return               = lt_return.
            ELSE.
              CALL FUNCTION 'BAPI_ODSO_CHANGE'
                EXPORTING
                  odsobject            = 
                  details              = 
                TABLES
                  infoobjects          = 
                  navigationattributes = 
                  indexes              = 
                  indexesinfoobjects   = 
                  return               = lt_return.
            ENDIF.
    
          CATCH cx_sy_dyn_call_illegal_func.
            zcx_abapgit_exception=>raise( |Necessary BW function modules not found or object not supported| ).
        ENDTRY.
    
        READ TABLE lt_return WITH KEY type = 'E' INTO ls_return.
        IF sy-subrc = 0.
          zcx_abapgit_exception=>raise( |Error when creating ODSO: { ls_return-message }| ).
        ENDIF.
    
        CALL FUNCTION 'BAPI_ODSO_ACTIVATE'
          EXPORTING
            odsobject = 
          TABLES
            return    = lt_return.
    
        READ TABLE lt_return WITH KEY type = 'E' INTO ls_return.
        IF sy-subrc = 0.
          zcx_abapgit_exception=>raise( |Error when activating ODSO: { ls_return-message }| ).
        ENDIF.
    
        tadir_insert( iv_package ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_iobjnm TYPE c LENGTH 30.
    
        SELECT SINGLE odsobject
        FROM ('RSDODSO')
        INTO lv_iobjnm
        WHERE odsobject = ms_item-obj_name.
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
    
        DATA: lv_dsona TYPE c LENGTH 30,
              lo_odso  TYPE REF TO object,
              lv_isact TYPE abap_bool.
    
        lv_dsona = ms_item-obj_name.
    
        CALL METHOD ('CL_RSD_ODSO')=>('FACTORY')
          EXPORTING
            i_odsobject = lv_dsona
          RECEIVING
            r_r_odso    = lo_odso.
    
        CALL METHOD lo_odso->('IS_ACTIVE')
          RECEIVING
            r_is_active = lv_isact.
    
        rv_active = lv_isact.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lv_object TYPE eqegraarg.
    
        lv_object = ms_item-obj_name.
        OVERLAY lv_object WITH '                                          '.
        lv_object = lv_object && '*'.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'RSD_S_PROV'
                                                iv_argument    = lv_object ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_dsonam      TYPE c LENGTH 30,
              lr_details     TYPE REF TO data,
              lr_infoobjects TYPE REF TO data,
              lr_navigation  TYPE REF TO data,
              lr_indexes     TYPE REF TO data,
              lr_index_iobj  TYPE REF TO data,
              ls_return      TYPE bapiret2.
    
        FIELD-SYMBOLS:
               TYPE any,
           TYPE STANDARD TABLE,
            TYPE STANDARD TABLE,
               TYPE STANDARD TABLE,
            TYPE STANDARD TABLE.
    
        CREATE DATA lr_details     TYPE ('BAPI6116').
        CREATE DATA lr_infoobjects TYPE STANDARD TABLE OF ('BAPI6116IO').
        CREATE DATA lr_navigation  TYPE STANDARD TABLE OF ('BAPI6116NA').
        CREATE DATA lr_indexes     TYPE STANDARD TABLE OF ('BAPI6116IN').
        CREATE DATA lr_index_iobj  TYPE STANDARD TABLE OF ('BAPI6116II').
    
        ASSIGN lr_details->* TO .
        ASSIGN lr_infoobjects->* TO .
        ASSIGN lr_navigation->* TO .
        ASSIGN lr_indexes->* TO .
        ASSIGN lr_index_iobj->* TO .
    
        lv_dsonam = ms_item-obj_name.
    
        CALL FUNCTION 'BAPI_ODSO_GETDETAIL'
          EXPORTING
            odsobject            = lv_dsonam
          IMPORTING
            details              = 
            return               = ls_return
          TABLES
            infoobjects          = 
            navigationattributes = 
            indexes              = 
            indexesinfoobjects   = .
    
        IF ls_return-type = 'E'.
          zcx_abapgit_exception=>raise( |Error when getting details of ODSO: { ls_return-message }| ).
        ENDIF.
    
        clear_field( EXPORTING iv_fieldname = 'TSTPNM'
                     CHANGING  cg_metadata  =  ).
    
        clear_field( EXPORTING iv_fieldname = 'TIMESTMP'
                     CHANGING  cg_metadata  =  ).
    
        clear_field( EXPORTING iv_fieldname = 'CONTTIMESTMP'
                     CHANGING  cg_metadata  =  ).
    
        clear_field( EXPORTING iv_fieldname = 'OWNER'
                     CHANGING  cg_metadata  =  ).
    
        io_xml->add( iv_name = 'ODSO'
                     ig_data =  ).
    
        io_xml->add( iv_name = 'INFOOBJECTS'
                     ig_data =  ).
    
        io_xml->add( iv_name = 'NAVIGATION'
                     ig_data =  ).
    
        io_xml->add( iv_name = 'INDEXES'
                     ig_data =  ).
    
        io_xml->add( iv_name = 'INDEX_IOBJ'
                     ig_data =  ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_otgr IMPLEMENTATION.
    
      METHOD instantiate_and_lock_otgr.
        DATA:
          lv_new   TYPE abap_bool,
          lv_name  TYPE cls_attribute_name,
          lv_state TYPE cls_type_group-activation_state.
    
        SELECT SINGLE name FROM cls_type_group INTO lv_name WHERE name = ms_item-obj_name.
        IF sy-subrc = 0.
          lv_new   = abap_false.
          lv_state = cl_pak_wb_domains=>co_activation_state-invalid.
        ELSE.
          lv_new   = abap_true.
          lv_state = cl_pak_wb_domains=>co_activation_state-active.
        ENDIF.
        lv_name = ms_item-obj_name.
    
        TRY.
            CREATE OBJECT ro_otgr
              EXPORTING
                im_name             = lv_name
                im_new              = lv_new
                im_activation_state = lv_state.
          CATCH cx_pak_invalid_data
              cx_pak_not_authorized
              cx_pak_invalid_state
              cx_pak_wb_object_locked.
            zcx_abapgit_exception=>raise( |OTGR { lv_name }: error while instantiating CL_CLS_OBJECT_TYPE_GROUP| ).
        ENDTRY.
    
        IF lv_new = abap_false.
          TRY.
              ro_otgr->if_pak_wb_object~lock_and_refresh( ).
            CATCH cx_pak_invalid_data
                cx_pak_not_authorized
                cx_pak_invalid_state
                cx_pak_wb_object_locked.
              zcx_abapgit_exception=>raise( |OTGR { lv_name }: could not acquire lock| ).
          ENDTRY.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        SELECT SINGLE changed_by FROM cls_type_group INTO rv_user
          WHERE name = ms_item-obj_name
          AND activation_state = cl_pak_wb_domains=>co_activation_state-active.
    
        IF rv_user IS INITIAL.
          SELECT SINGLE created_by FROM cls_type_group INTO rv_user
            WHERE name = ms_item-obj_name
            AND activation_state = cl_pak_wb_domains=>co_activation_state-active.
        ENDIF.
    
        IF rv_user IS INITIAL.
          rv_user = c_user_unknown.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
        DATA: lo_otgr      TYPE REF TO cl_cls_object_type_group,
              lx_pak_error TYPE REF TO cx_root,
              lv_text      TYPE string.
    
        lo_otgr = instantiate_and_lock_otgr( ).
    
        TRY.
            lo_otgr->if_pak_wb_object~delete( ).
            lo_otgr->if_pak_wb_object~save( ).
            lo_otgr->unlock( ).
    
          CATCH cx_pak_invalid_state cx_pak_invalid_data cx_pak_not_authorized INTO lx_pak_error.
            lo_otgr->unlock( ).
    
            lv_text = lx_pak_error->get_text( ).
            zcx_abapgit_exception=>raise( |OTGR { ms_item-obj_name }: delete: { lv_text }| ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
        DATA: ls_otgr      TYPE ty_otgr,
              lo_otgr      TYPE REF TO cl_cls_object_type_group,
              lx_pak_error TYPE REF TO cx_root,
              lv_text      TYPE string,
              lv_main_lang TYPE sy-langu,
              lo_parents   TYPE REF TO data.
    
        FIELD-SYMBOLS:   LIKE LINE OF ls_otgr-texts,
                        LIKE LINE OF ls_otgr-elements,
                          TYPE any,
                         TYPE any,
                        TYPE ANY TABLE.
    
        io_xml->read( EXPORTING iv_name = 'OTGR'
                      CHANGING  cg_data = ls_otgr ).
    
        LOOP AT ls_otgr-texts ASSIGNING .
          -activation_state = cl_pak_wb_domains=>co_activation_state-inactive.
          " Removed in the method serialize.
          -name = ms_item-obj_name.
        ENDLOOP.
    
        " Parents (cls_tygr_parent) does not exist in lower releases
        TRY.
            CREATE DATA lo_parents TYPE TABLE OF ('CLS_TYGR_PARENT').
            ASSIGN lo_parents->* TO .
          CATCH cx_sy_create_data_error ##NO_HANDLER.
        ENDTRY.
    
        IF  IS ASSIGNED.
          io_xml->read( EXPORTING iv_name = 'PARENTS'
                        CHANGING  cg_data =  ).
    
          LOOP AT  ASSIGNING .
            ASSIGN COMPONENT 'ACTIVATION_STATE' OF STRUCTURE  TO .
            IF sy-subrc = 0.
               = cl_pak_wb_domains=>co_activation_state-inactive.
            ENDIF.
            ASSIGN COMPONENT 'OBJ_TYPE_GROUP' OF STRUCTURE  TO .
            IF sy-subrc = 0.
              " Removed in the method serialize.
               = ms_item-obj_name.
            ENDIF.
          ENDLOOP.
        ENDIF.
    
        LOOP AT ls_otgr-elements ASSIGNING .
          -activation_state = cl_pak_wb_domains=>co_activation_state-inactive.
          " Removed in the method serialize.
          -obj_type_group = ms_item-obj_name.
        ENDLOOP.
    
        tadir_insert( iv_package ).
    
        lo_otgr = instantiate_and_lock_otgr( ).
    
        TRY.
            lo_otgr->if_cls_object_type_group~set_proxy_filter( ls_otgr-cls_type_group-proxy_flag ).
            lo_otgr->if_cls_object_type_group~set_elements( ls_otgr-elements ).
    
            IF  IS ASSIGNED.
              CALL METHOD lo_otgr->('IF_CLS_OBJECT_TYPE_GROUP~SET_PARENT_GROUPS')
                EXPORTING
                  im_parent_groups = .
            ENDIF.
    
            lv_main_lang = lo_otgr->if_pak_wb_object~get_master_language( ).
            READ TABLE ls_otgr-texts WITH KEY langu = lv_main_lang ASSIGNING .
            IF sy-subrc = 0.
              lo_otgr->set_description( -text ).
              " ELSE.
              "   Do we want to clear the main language description if not present in the XML content?
              "   Main language is non-deterministic - it depends on sy-langu, so rather don't touch
              "   description if the main language is not present
              "   Perhaps, we can display some sort of a message but how?
            ENDIF.
    
            set_default_package( iv_package ).
    
            lo_otgr->if_pak_wb_object~save( ).
    
            lo_otgr->if_pak_wb_object~activate( ).
            lo_otgr->unlock( ).
    
          CATCH cx_pak_invalid_state cx_pak_invalid_data cx_pak_not_authorized INTO lx_pak_error.
            lo_otgr->unlock( ).
    
            lv_text = lx_pak_error->get_text( ).
            zcx_abapgit_exception=>raise( |OTGR { ms_item-obj_name }: deserialize: { lv_text }| ).
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
        rv_bool = cl_cls_object_type_group=>exists_object_type_group( ms_item-obj_name ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ECLS_ATTRIBUTE'
                                                iv_argument    = |{ ms_item-obj_name }*| ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
        DATA: lv_text      TYPE string,
              lv_name      TYPE ty_otgr-cls_type_group,
              ls_otgr      TYPE ty_otgr,
              lo_otgr      TYPE REF TO cl_cls_object_type_group,
              lx_pak_error TYPE REF TO cx_root,
              lo_parents   TYPE REF TO data.
    
        FIELD-SYMBOLS:   LIKE LINE OF ls_otgr-texts,
                        LIKE LINE OF ls_otgr-elements,
                          TYPE any,
                         TYPE any,
                        TYPE ANY TABLE.
    
        lo_otgr = instantiate_and_lock_otgr( ).
    
    *   Description part 1:
    *   Dealing with Description of OTGR objects is problematic.
    *   The API supports setting of main language only and
    *   if we want to save also translations we would have to implement
    *   our own logic for merging and activation. To keep it simple stupid
    *   the current version focuses on the main language only.
    *   If anybody ever runs into the need to version also translation,
    *   ask the maintainers of CL_CLS_OBJECT_TYPE_GROUP to add a method for it.
    *
    *   However, the XML content will pretend we support also translations,
    *   so if someone adds support for them in future, there will be no format change.
        APPEND INITIAL LINE TO ls_otgr-texts ASSIGNING .
    
        " Parents (cls_tygr_parent) does not exist in lower releases
        TRY.
            CREATE DATA lo_parents TYPE TABLE OF ('CLS_TYGR_PARENT').
            ASSIGN lo_parents->* TO .
          CATCH cx_sy_create_data_error ##NO_HANDLER.
        ENDTRY.
    
        TRY.
            ls_otgr-cls_type_group-name = lo_otgr->if_cls_object_type_group~get_name( ).
            ls_otgr-cls_type_group-proxy_flag = lo_otgr->if_cls_object_type_group~get_proxy_filter( ).
    
            TRY.
                CALL METHOD lo_otgr->('GET_ELEMENTS')
                  EXPORTING
                    im_explicit_elements_only = abap_true " doesn't exist on lower releases. Eg. 752 SP04
                  IMPORTING
                    ex_elements               = ls_otgr-elements.
    
              CATCH cx_sy_dyn_call_param_not_found.
    
                lo_otgr->get_elements( IMPORTING ex_elements = ls_otgr-elements ).
    
            ENDTRY.
    
            " Remove children since they are created automatically (by the child group)
            LOOP AT ls_otgr-elements ASSIGNING .
              SELECT SINGLE name FROM cls_type_group INTO lv_name WHERE name = -type.
              IF sy-subrc = 0.
                DELETE ls_otgr-elements.
              ENDIF.
            ENDLOOP.
    
            IF  IS ASSIGNED.
              CALL METHOD lo_otgr->('IF_CLS_OBJECT_TYPE_GROUP~GET_PARENT_GROUPS')
                EXPORTING
                  im_explicit_parents_only = abap_true
                IMPORTING
                  ex_parent_groups         = .
            ENDIF.
    
            " Beware: the following method returns the main language description only if the object is locked!
            -text = lo_otgr->if_cls_object_type_group~get_description( ).
            -langu = lo_otgr->if_pak_wb_object~get_master_language( ).
    
            lo_otgr->unlock( ).
    
          CATCH cx_pak_invalid_state cx_pak_invalid_data cx_pak_not_authorized INTO lx_pak_error.
            lo_otgr->unlock( ).
    
            lv_text = lx_pak_error->get_text( ).
            zcx_abapgit_exception=>raise( |OTGR { ms_item-obj_name }: serialize: { lv_text }| ).
        ENDTRY.
    
        CLEAR: ls_otgr-cls_type_group-created_by,
               ls_otgr-cls_type_group-created_on,
               ls_otgr-cls_type_group-changed_by,
               ls_otgr-cls_type_group-changed_on.
    
    *    Description part 2:
    *
    * lt_lang_sel  TYPE RANGE OF langu,
    * ls_lang_sel  LIKE LINE OF lt_lang_sel,
    *
    *    IF io_xml->i18n_params( )-main_language_only = abap_true.
    *      ls_lang_sel-low = mv_language.
    *      ls_lang_sel-sign = 'I'.
    *      ls_lang_sel-option = 'EQ'.
    *    ENDIF.
    *
    *    SELECT * FROM cls_type_groupt INTO TABLE ls_otgr-texts
    *      WHERE name = ms_item-obj_name
    *        AND activation_state = 'A'
    *        AND langu in lt_lang_sel.
    *
    *   Description ideas end
    
        LOOP AT ls_otgr-texts ASSIGNING .
          " Not necessary as we serialize only Active
          CLEAR -activation_state.
          " Not necessary as we have it in the root XML node
          CLEAR -name.
        ENDLOOP.
    
        LOOP AT ls_otgr-elements ASSIGNING .
          " Not necessary as we serialize only Active
          CLEAR -activation_state.
          " Not necessary as we have it in the root XML node
          CLEAR -obj_type_group.
        ENDLOOP.
    
        io_xml->add( iv_name = 'OTGR'
                     ig_data = ls_otgr ).
    
        IF  IS ASSIGNED.
          LOOP AT  ASSIGNING .
            ASSIGN COMPONENT 'ACTIVATION_STATE' OF STRUCTURE  TO .
            IF sy-subrc = 0.
              " Not necessary as we serialize only Active
              CLEAR .
            ENDIF.
            ASSIGN COMPONENT 'OBJ_TYPE_GROUP' OF STRUCTURE  TO .
            IF sy-subrc = 0.
              " Not necessary as we have it in the root XML node
              CLEAR .
            ENDIF.
          ENDLOOP.
    
          io_xml->add( iv_name = 'PARENTS'
                       ig_data =  ).
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_para IMPLEMENTATION.
    
      METHOD unlock.
    
        CALL FUNCTION 'RS_ACCESS_PERMISSION'
          EXPORTING
            mode         = 'FREE'
            object       = iv_paramid
            object_class = 'PARA'.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    * looks like "changed by user" is not stored in the database
        rv_user = c_user_unknown.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        " We can't use FM RS_PARAMETER_DELETE because of the popup to confirm
        "Therefore we have to reimplement most of the FMs logic
    
        DATA lv_paramid TYPE tpara-paramid.
    
        lv_paramid = ms_item-obj_name.
    
        CALL FUNCTION 'RS_ACCESS_PERMISSION'
          EXPORTING
            global_lock              = abap_true
            language_upd_exit        = 'RS_PARAMETER_LANGUAGE_EXIT'    " Name FuBa for maintenance language change
            object                   = lv_paramid
            object_class             = ms_item-obj_type
            suppress_language_check  = space
          EXCEPTIONS
            canceled_in_corr         = 1
            enqueued_by_user         = 2
            enqueue_system_failure   = 3
            illegal_parameter_values = 4
            locked_by_author         = 5
            no_modify_permission     = 6
            no_show_permission       = 7
            permission_failure       = 8
            request_language_denied  = 9
            OTHERS                   = 10.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        SELECT COUNT(*) FROM cross
          WHERE ( type = 'P' OR type = 'Q' ) AND name = lv_paramid.
        IF sy-subrc = 0.
          unlock( lv_paramid ).
          zcx_abapgit_exception=>raise( 'PARA: Parameter is still used' ).
        ELSE.
          SELECT COUNT(*) FROM dd04l BYPASSING BUFFER
            WHERE memoryid = lv_paramid
            AND as4local = 'A'.
          IF sy-subrc = 0.
            unlock( lv_paramid ).
            zcx_abapgit_exception=>raise( 'PARA: Parameter is still used' ).
          ENDIF.
        ENDIF.
    
        unlock( lv_paramid ).
    
        zcl_abapgit_factory=>get_cts_api( )->insert_transport_object(
          iv_object   = 'PARA'
          iv_obj_name = lv_paramid
          iv_package  = iv_package
          iv_language = mv_language
          iv_mode     = zif_abapgit_cts_api=>c_transport_mode-delete ).
    
        DELETE FROM tpara WHERE paramid = lv_paramid.
        DELETE FROM tparat WHERE paramid = lv_paramid.
    
        CALL FUNCTION 'RS_TREE_OBJECT_PLACEMENT'
          EXPORTING
            object    = lv_paramid
            operation = 'DELETE'
            type      = 'CR'.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    * see fm RS_PARAMETER_ADD and RS_PARAMETER_EDIT
    
        DATA: lv_mode   TYPE c LENGTH 1,
              ls_tpara  TYPE tpara,
              ls_tparat TYPE tparat.
    
        SELECT SINGLE * FROM tpara INTO ls_tpara
          WHERE paramid = ms_item-obj_name.                 "#EC CI_GENBUFF
        IF sy-subrc = 0.
          lv_mode = 'M'.
        ELSE.
          lv_mode = 'I'.
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'TPARA'
                      CHANGING cg_data = ls_tpara ).
    
        CALL FUNCTION 'RS_CORR_INSERT'
          EXPORTING
            object              = ms_item-obj_name
            object_class        = 'PARA'
            mode                = lv_mode
            global_lock         = abap_true
            devclass            = iv_package
            master_language     = mv_language
            suppress_dialog     = abap_true
          EXCEPTIONS
            cancelled           = 1
            permission_failure  = 2
            unknown_objectclass = 3
            OTHERS              = 4.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        MODIFY tpara FROM ls_tpara.                           "#EC CI_SUBRC
        ASSERT sy-subrc = 0.
    
        io_xml->read(
          EXPORTING iv_name = 'TPARAT'
          CHANGING  cg_data = ls_tparat ).
    
        MODIFY tparat FROM ls_tparat.                         "#EC CI_SUBRC
        ASSERT sy-subrc = 0.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_paramid TYPE tpara-paramid.
    
        SELECT SINGLE paramid FROM tpara INTO lv_paramid
          WHERE paramid = ms_item-obj_name.                 "#EC CI_GENBUFF
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'EEUDB'
                                                iv_argument    = ms_item-obj_name
                                                iv_prefix      = 'PA' ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_tpara  TYPE tpara,
              ls_tparat TYPE tparat.
    
        SELECT SINGLE * FROM tpara INTO ls_tpara
          WHERE paramid = ms_item-obj_name.                 "#EC CI_GENBUFF
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        SELECT SINGLE * FROM tparat INTO ls_tparat
          WHERE paramid = ms_item-obj_name
          AND sprache = mv_language.          "#EC CI_GENBUFF "#EC CI_SUBRC
    
        io_xml->add( iv_name = 'TPARA'
                     ig_data = ls_tpara ).
    
        io_xml->add(
          iv_name = 'TPARAT'
          ig_data = ls_tparat ).
        " Here only the original language is serialized,
        " so it should be present for the moment. LXEs are just translations
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_pdxx_super IMPLEMENTATION.
    
      METHOD check_subrc_for.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( iv_call && ' returned ' && sy-subrc ).
        ENDIF.
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        ms_objkey-otype = is_item-obj_type+2(2).
        ms_objkey-objid = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE uname
          INTO rv_user
          FROM hrs1201
          WHERE otype = ms_item-obj_type AND
                objid = ms_item-obj_name.
    
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        CALL FUNCTION 'RH_HRSOBJECT_DELETE'
          EXPORTING
            act_otype           = ms_objkey-otype
            act_objid           = ms_objkey-objid
            no_confirmation_msg = abap_true
          EXCEPTIONS
            enqueue_failed      = 1
            object_not_deleted  = 2
            object_not_found    = 3
            OTHERS              = 4 ##FM_SUBRC_OK.
    
        check_subrc_for( `RH_HRSOBJECT_DELETE` ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
        ASSERT 1 = 2. "Must be redefined
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        CALL FUNCTION 'RH_READ_OBJECT'
          EXPORTING
            plvar     = '01'
            otype     = ms_objkey-otype
            objid     = ms_objkey-objid
            istat     = '1'
            begda     = sy-datum
            endda     = '99991231'
            ointerval = 'X'
            read_db   = 'X'
          EXCEPTIONS
            not_found = 1
            OTHERS    = 2.
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = abap_true.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'HRSOBJECT'
                                                iv_argument    = ms_objkey-otype && ms_objkey-objid ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
        ASSERT 1 = 2. "Must be redefined
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_pers IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_pers_key = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD get_personalization_object.
    
        CREATE OBJECT ro_personalization_object
          EXPORTING
            p_create                = iv_create
            p_pers_key              = mv_pers_key
            p_view_only             = iv_view_only
          EXCEPTIONS
            pers_key_already_exists = 1
            pers_key_does_not_exist = 2
            transport_view_only     = 3
            transport_canceled      = 4
            OTHERS                  = 5.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE author FROM spers_reg INTO rv_user
          WHERE pers_key = ms_item-obj_name.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lo_personalization_object TYPE REF TO cl_pers_reg.
    
        lo_personalization_object = get_personalization_object( ).
    
        lo_personalization_object->delete(
          EXPORTING
            p_no_confirm       = abap_true
          EXCEPTIONS
            deletion_canceled  = 1
            deletion_failed    = 2
            transport_canceled = 3
            OTHERS             = 4 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA:
          ls_personalization_object TYPE ty_personalization_object,
          lo_personalization_object TYPE REF TO cl_pers_reg.
    
        io_xml->read(
          EXPORTING
            iv_name = 'PERS'
          CHANGING
            cg_data = ls_personalization_object ).
    
        tadir_insert( iv_package ).
    
        lo_personalization_object = get_personalization_object( iv_create = abap_true ).
    
        lo_personalization_object->set_reg_data(
            p_pers_reg      = ls_personalization_object-pers_reg
            p_pers_reg_text = ls_personalization_object-pers_reg_text ).
    
        lo_personalization_object->save(
          EXPORTING
            no_check           = abap_true
          EXCEPTIONS
            data_not_saved     = 1
            transport_canceled = 2
            OTHERS             = 3 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        cl_pers_reg=>exists(
          EXPORTING
            p_pers_key              = mv_pers_key
          EXCEPTIONS
            pers_key_does_not_exist = 1
            OTHERS                  = 2 ).
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        " There's no object specific locking. Just a global one.
        rv_is_locked = exists_a_lock_entry_for( 'E_SPERSREG' ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: ls_bcdata TYPE bdcdata,
              lt_bcdata TYPE STANDARD TABLE OF bdcdata.
    
        ls_bcdata-program  = 'SAPLSPERS_REG_DIALOG'.
        ls_bcdata-dynpro   = '0100'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam     = 'SPERS_REG-PERS_KEY'.
        ls_bcdata-fval     = ms_item-obj_name.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam = 'BDC_OKCODE'.
        ls_bcdata-fval = '=PERSDISPLAY'.
        APPEND ls_bcdata TO lt_bcdata.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'PERSREG'
          it_bdcdata = lt_bcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          lo_personalization_object TYPE REF TO cl_pers_reg,
          ls_personalization_object TYPE ty_personalization_object.
    
        lo_personalization_object = get_personalization_object( iv_view_only = abap_true ).
    
        lo_personalization_object->get_reg_data(
          IMPORTING
            p_pers_reg      = ls_personalization_object-pers_reg
            p_pers_reg_text = ls_personalization_object-pers_reg_text ).
    
        CLEAR:
          ls_personalization_object-pers_reg-author,
          ls_personalization_object-pers_reg-fdate,
          ls_personalization_object-pers_reg-ftime.
    
        io_xml->add( iv_name = 'PERS'
                     ig_data = ls_personalization_object ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS lcl_package_interface_facade IMPLEMENTATION.
    
      METHOD constructor.
    
        mi_interface = ii_interface.
    
      ENDMETHOD.
    
      METHOD lif_package_interface_facade~get_elements.
    
        mi_interface->get_elements(
          IMPORTING
            e_elements     = rt_elements
          EXCEPTIONS
            object_invalid = 1
            intern_err     = 2
            OTHERS         = 3 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD lif_package_interface_facade~set_elements_changeable.
    
        mi_interface->set_elements_changeable(
          EXPORTING
            i_changeable                = iv_changeable
          EXCEPTIONS
            object_already_changeable   = 1
            object_already_unlocked     = 2
            object_locked_by_other_user = 3
            object_modified             = 4
            object_just_created         = 5
            object_deleted              = 6
            permission_failure          = 7
            object_invalid              = 8
            unexpected_error            = 9
            OTHERS                      = 10 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD lif_package_interface_facade~save_elements.
    
        mi_interface->save_elements(
          EXCEPTIONS
            object_not_changeable = 1
            object_invalid        = 2
            cancelled_in_corr     = 3
            permission_failure    = 4
            unexpected_error      = 5
            intern_err            = 6
            OTHERS                = 7 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD lif_package_interface_facade~get_all_attributes.
    
        mi_interface->get_all_attributes(
          IMPORTING
            e_package_interface_data = rs_package_interface_data
          EXCEPTIONS
            object_invalid           = 1
            OTHERS                   = 2 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD lif_package_interface_facade~set_changeable.
    
        mi_interface->set_changeable(
          EXPORTING
            i_changeable                 = iv_changeable
          EXCEPTIONS
            object_locked_by_other_user  = 1
            permission_failure           = 2
            object_already_changeable    = 3
            object_already_unlocked      = 4
            object_just_created          = 5
            object_deleted               = 6
            object_modified              = 7
            object_not_existing          = 8
            object_invalid               = 9
            unexpected_error             = 10
            OTHERS                       = 11 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD lif_package_interface_facade~delete.
    
        mi_interface->delete(
          EXCEPTIONS
            object_not_empty      = 1
            object_not_changeable = 2
            object_invalid        = 3
            intern_err            = 4
            OTHERS                = 5 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD lif_package_interface_facade~save.
    
        mi_interface->save(
          EXCEPTIONS
            short_text_missing    = 1
            object_not_changeable = 2
            object_invalid        = 3
            cancelled_in_corr     = 4
            permission_failure    = 5
            unexpected_error      = 6
            intern_err            = 7
            OTHERS                = 8 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD lif_package_interface_facade~remove_elements.
    
        mi_interface->remove_elements(
          EXPORTING
            i_elements            = it_elements
          EXCEPTIONS
            object_deleted        = 1
            object_invalid        = 2
            object_not_changeable = 3
            element_not_contained = 4
            intern_err            = 5
            OTHERS                = 6 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD lif_package_interface_facade~add_elements.
    
        DATA:
          lt_mismatched TYPE scomeldata,
          ls_mismatched LIKE LINE OF lt_mismatched.
    
        mi_interface->add_elements(
          EXPORTING
            i_elements_data        = it_elements_data
          IMPORTING
            e_mismatched_elem_data = lt_mismatched
          EXCEPTIONS
            object_invalid         = 1
            intern_err             = 2
            OTHERS                 = 3 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        LOOP AT lt_mismatched INTO ls_mismatched.
          zcx_abapgit_exception=>raise( |Object { ls_mismatched-elem_type } { ls_mismatched-elem_key } | &&
                                        |from different package { ls_mismatched-elem_pack }| ).
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD lif_package_interface_facade~set_all_attributes.
    
        mi_interface->set_all_attributes(
          EXPORTING
            i_package_interface_data     = is_package_interface_data
            i_data_sign                  = is_data_sign
          EXCEPTIONS
            object_deleted               = 1
            object_not_changeable        = 2
            interface_not_empty          = 3
            acl_not_empty                = 4
            author_not_existing          = 5
            object_type_mismatch         = 6
            object_invalid               = 7
            OTHERS                       = 8 ).
    * Downport: exception "logical_package_types_differ"
    * does not exist in lower versions
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD lif_package_interface_facade~get_changeable.
    
        mi_interface->get_changeable(
          IMPORTING
            e_changeable   = rv_changeable
          EXCEPTIONS
            object_invalid = 1
            OTHERS         = 2 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_object_pinf IMPLEMENTATION.
    
      METHOD create_facade.
    
        CREATE OBJECT ri_facade TYPE lcl_package_interface_facade
          EXPORTING
            ii_interface = ii_interface.
    
      ENDMETHOD.
    
      METHOD create_or_load.
    
        DATA: li_interface          TYPE REF TO if_package_interface,
              lv_pkg_interface_data TYPE scompidtln.
    
        lv_pkg_interface_data-default_if = is_pinf-attributes-default_if.
        lv_pkg_interface_data-tadir_devc = iv_package.
    
        "Important if the package name comes from another package
        IF is_pinf-attributes-pack_name IS INITIAL.
          lv_pkg_interface_data-pack_name = iv_package.
        ELSE.
          lv_pkg_interface_data-pack_name = is_pinf-attributes-pack_name.
        ENDIF.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          cl_package_interface=>create_new_package_interface(
            EXPORTING
              i_pkg_interface_name    = is_pinf-attributes-intf_name
              i_publisher_pkg_name    = lv_pkg_interface_data-pack_name
              i_pkg_interface_data    = lv_pkg_interface_data
            IMPORTING
              e_package_interface     = li_interface
            EXCEPTIONS
              object_already_existing = 1
              object_just_created     = 2
              interface_name_invalid  = 3
              unexpected_error        = 4
              OTHERS                  = 7 ).
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'error creating new package interface' ).
          ENDIF.
    
          ri_interface = create_facade( li_interface ).
    
        ELSE.
    
          ri_interface = load( is_pinf-attributes-intf_name ).
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD delete_elements.
    
        DATA: lt_elements TYPE ty_elements.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_elements.
    
        ii_interface->set_elements_changeable( abap_true ).
    
        lt_elements = ii_interface->get_elements( ).
    
        LOOP AT lt_elements ASSIGNING .
          ->delete( ).
        ENDLOOP.
    
        ii_interface->save_elements( ).
    
      ENDMETHOD.
    
      METHOD load.
    
        DATA: li_interface TYPE REF TO  if_package_interface.
    
        cl_package_interface=>load_package_interface(
          EXPORTING
            i_package_interface_name = iv_name
            i_force_reload           = abap_true
          IMPORTING
            e_package_interface      = li_interface ).
    
        ri_interface = create_facade( li_interface ).
    
      ENDMETHOD.
    
      METHOD update_attributes.
    
        DATA: ls_sign       TYPE scompisign,
              lv_changeable TYPE abap_bool.
    
        lv_changeable = ii_interface->get_changeable( ).
        IF lv_changeable = abap_false.
    * at creation the object is already in change mode
          ii_interface->set_changeable( abap_true ).
        ENDIF.
    
        ls_sign-descript       = abap_true.
        ls_sign-pinftype       = abap_true.
        ls_sign-restricted     = abap_true.
        ls_sign-default_if     = abap_true.
        ls_sign-def_sever      = abap_true.
        ls_sign-acl_flag       = abap_true.
        ls_sign-pifstablty     = abap_true.
        ls_sign-release_status = abap_true.
    
        ii_interface->set_all_attributes(
          is_package_interface_data = is_pinf-attributes
          is_data_sign              = ls_sign ).
    
        set_default_package( iv_package ).
    * looks like setting "i_suppress_dialog = abap_true" will make
    * it fail for local($) packages
        ii_interface->save( ).
    
        ii_interface->set_changeable( abap_false ).
    
      ENDMETHOD.
    
      METHOD update_elements.
    
        DATA: lt_existing TYPE ty_elements,
              ls_element  LIKE LINE OF is_pinf-elements,
              lt_add      TYPE scomeldata,
              lv_index    TYPE i,
              lv_found    TYPE abap_bool,
              ls_attr     TYPE scomeldtln.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_existing.
    
        ii_interface->set_elements_changeable( abap_true ).
    
        lt_existing = ii_interface->get_elements( ).
    
        LOOP AT is_pinf-elements INTO ls_element.
    
          lv_found = abap_false.
          LOOP AT lt_existing ASSIGNING .
            lv_index = sy-tabix.
            ->get_all_attributes( IMPORTING e_element_data = ls_attr ).
            IF ls_element-elem_type = ls_attr-elem_type
                AND ls_element-elem_key = ls_attr-elem_key.
              DELETE lt_existing INDEX lv_index.
              CONTINUE. " current loop
            ENDIF.
          ENDLOOP.
    
          IF lv_found = abap_false.
            ls_element-elem_pack = iv_package.
            APPEND ls_element TO lt_add.
          ENDIF.
        ENDLOOP.
    
        ii_interface->remove_elements( lt_existing ).
    
        ii_interface->add_elements( lt_add ).
    
        ii_interface->save_elements( ).
    
        ii_interface->set_elements_changeable( abap_false ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE changed_by FROM intf INTO rv_user
          WHERE intf_name = ms_item-obj_name.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: li_interface TYPE REF TO lif_package_interface_facade.
    
        corr_insert( iv_package ).
    
        li_interface = load( |{ ms_item-obj_name }| ).
    
    * elements must be deleted before the package interface
    * can be deleted
        delete_elements( li_interface ).
    
        li_interface->set_changeable( abap_true ).
    
        li_interface->delete( ).
    
        li_interface->save( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: li_interface TYPE REF TO lif_package_interface_facade,
              ls_pinf      TYPE ty_pinf.
    
        io_xml->read( EXPORTING iv_name = 'PINF'
                      CHANGING cg_data = ls_pinf ).
    
        "needed for update_attributes
        ls_pinf-attributes-tadir_devc = iv_package.
    
        li_interface = create_or_load(
          is_pinf    = ls_pinf
          iv_package = iv_package ).
    
        update_attributes(
          iv_package   = iv_package
          is_pinf      = ls_pinf
          ii_interface = li_interface ).
    
        update_elements(
          iv_package   = iv_package
          is_pinf      = ls_pinf
          ii_interface = li_interface ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_pack_name TYPE intf-pack_name,
              lv_main_pack TYPE tdevc-mainpack.
    
        SELECT SINGLE pack_name FROM intf INTO lv_pack_name
          WHERE intf_name = ms_item-obj_name.
        rv_bool = boolc( sy-subrc = 0 ).
    
        IF rv_bool = abap_true.
          SELECT SINGLE mainpack FROM tdevc INTO lv_main_pack
            WHERE devclass = lv_pack_name.                  "#EC CI_GENBUFF
          rv_bool = boolc( sy-subrc = 0 ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'EEUDB'
                                                iv_argument    = ms_item-obj_name
                                                iv_prefix      = 'PF' ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_pinf      TYPE ty_pinf,
              lt_elements  TYPE ty_elements,
              li_interface TYPE REF TO lif_package_interface_facade.
    
        FIELD-SYMBOLS:      TYPE any,
                        LIKE LINE OF lt_elements,
                        LIKE LINE OF ls_pinf-elements.
    
        li_interface = load( |{ ms_item-obj_name }| ).
    
        ls_pinf-attributes = li_interface->get_all_attributes( ).
    
        "Delete the package name if it comes from the same package
        IF ls_pinf-attributes-tadir_devc = ls_pinf-attributes-pack_name OR
          ms_item-devclass = ls_pinf-attributes-pack_name.
          CLEAR ls_pinf-attributes-pack_name.
        ENDIF.
    
        CLEAR: ls_pinf-attributes-author,
               ls_pinf-attributes-created_by,
               ls_pinf-attributes-created_on,
               ls_pinf-attributes-changed_by,
               ls_pinf-attributes-changed_on,
               ls_pinf-attributes-tadir_devc.
    
    * fields does not exist in older SAP versions
        ASSIGN COMPONENT 'SW_COMP_LOGICAL_PACKAGE' OF STRUCTURE ls_pinf-attributes TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
        ASSIGN COMPONENT 'SW_COMP_TADIR_PACKAGE' OF STRUCTURE ls_pinf-attributes TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
    
        lt_elements = li_interface->get_elements( ).
    
        LOOP AT lt_elements ASSIGNING .
          APPEND INITIAL LINE TO ls_pinf-elements ASSIGNING .
          ->get_all_attributes( IMPORTING e_element_data =  ).
          CLEAR -elem_pack.
        ENDLOOP.
    
        io_xml->add( ig_data = ls_pinf
                     iv_name = 'PINF' ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_prag IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
        rv_user = c_user_unknown. " not stored by SAP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lo_pragma TYPE REF TO cl_abap_pragma,
              lx_error  TYPE REF TO cx_root.
    
        TRY.
            lo_pragma = cl_abap_pragma=>get_ref( ms_item-obj_name ).
    
            lo_pragma->delete( ).
            lo_pragma->leave_change( ). "unlock
    
          CATCH cx_root INTO lx_error.
            IF lo_pragma IS BOUND.
              lo_pragma->leave_change( ).
            ENDIF.
            zcx_abapgit_exception=>raise( lx_error->get_text( ) ).
        ENDTRY.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_pragma TYPE ty_pragma,
              lo_pragma TYPE REF TO cl_abap_pragma,
              lx_error  TYPE REF TO cx_root.
    
        tadir_insert( iv_package ).
    
        TRY.
            io_xml->read(
              EXPORTING
                iv_name = 'PRAG'
              CHANGING
                cg_data = ls_pragma ).
    
            lo_pragma = cl_abap_pragma=>create( p_pragma  = ms_item-obj_name
                                                p_package = iv_package ).
    
            lo_pragma->set_info( p_description = ls_pragma-description
                                 p_signature   = ls_pragma-signature
                                 p_extension   = ls_pragma-extension ).
    
            lo_pragma->save( ).
            lo_pragma->leave_change( ). "unlock
          CATCH cx_root INTO lx_error.
            IF lo_pragma IS BOUND.
              lo_pragma->leave_change( ).
            ENDIF.
            zcx_abapgit_exception=>raise( lx_error->get_text( ) ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        TRY.
            cl_abap_pragma=>get_ref( ms_item-obj_name ).
    
          CATCH cx_abap_pragma_not_exists.
            rv_bool = abap_false.
            RETURN.
        ENDTRY.
    
        rv_bool = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lo_pragma TYPE REF TO cl_abap_pragma,
              ls_pragma TYPE ty_pragma.
    
        TRY.
            lo_pragma = cl_abap_pragma=>get_ref( ms_item-obj_name ).
    
            ls_pragma-pragma      = lo_pragma->pragma.
            ls_pragma-extension   = lo_pragma->extension.
            ls_pragma-signature   = lo_pragma->signature.
            ls_pragma-description = lo_pragma->description.
    
            io_xml->add( iv_name = 'PRAG'
                         ig_data = ls_pragma ).
    
          CATCH cx_abap_pragma_not_exists.
            zcx_abapgit_exception=>raise( |Pragma { ms_item-obj_name } doesn't exist| ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_prog IMPLEMENTATION.
    
      METHOD deserialize_texts.
    
        DATA: lt_tpool_i18n TYPE zif_abapgit_lang_definitions=>ty_i18n_tpools,
              lt_tpool      TYPE textpool_table.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_tpool_i18n.
    
        ii_xml->read( EXPORTING iv_name = 'I18N_TPOOL'
                      CHANGING  cg_data = lt_tpool_i18n ).
    
        LOOP AT lt_tpool_i18n ASSIGNING .
          lt_tpool = read_tpool( -textpool ).
          deserialize_textpool( iv_program  = ms_item-obj_name
                                iv_language = -language
                                it_tpool    = lt_tpool ).
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD deserialize_with_ext.
    
        " Special treatment for extensions
        " If the program name exceeds 30 characters it is not a usual ABAP program but might be
        " some extension, which requires the internal addition EXTENSION TYPE
        " https://help.sap.com/doc/abapdocu_755_index_htm/7.55/en-US/index.htm?file=abapinsert_report_internal.htm
        " This e.g. occurs in case of transportable Code Inspector variants (ending with ===VC)
    
        zcl_abapgit_factory=>get_sap_report( )->insert_report(
          iv_name           = is_progdir-name
          iv_package        = iv_package
          it_source         = it_source
          iv_state          = 'I'
          iv_version        = is_progdir-uccheck
          iv_program_type   = is_progdir-subc
          iv_extension_type = is_progdir-name+30 ).
    
        zcl_abapgit_factory=>get_sap_report( )->update_progdir(
          is_progdir = is_progdir
          iv_state   = 'I'
          iv_package = iv_package ).
    
        zcl_abapgit_objects_activation=>add(
          iv_type = 'REPS'
          iv_name = is_progdir-name ).
    
      ENDMETHOD.
    
      METHOD is_program_locked.
    
        rv_is_program_locked = exists_a_lock_entry_for( iv_lock_object = 'ESRDIRE'
                                                        iv_argument    = |{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD serialize_texts.
    
        DATA: lt_tpool_i18n      TYPE zif_abapgit_lang_definitions=>ty_i18n_tpools,
              lt_tpool           TYPE textpool_table,
              lt_language_filter TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_tpool_i18n.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          RETURN.
        ENDIF.
    
        " Table d010tinf stores info. on languages in which program is maintained
        " Select all active translations of program texts
        " Skip main language - it was already serialized
        lt_language_filter = mo_i18n_params->build_language_filter( ).
    
        SELECT DISTINCT language
          INTO CORRESPONDING FIELDS OF TABLE lt_tpool_i18n
          FROM d010tinf
          WHERE r3state = 'A'
          AND prog = ms_item-obj_name
          AND language <> mv_language
          AND language IN lt_language_filter
          ORDER BY language ##TOO_MANY_ITAB_FIELDS.
    
        SORT lt_tpool_i18n BY language ASCENDING.
        LOOP AT lt_tpool_i18n ASSIGNING .
          READ TEXTPOOL ms_item-obj_name
            LANGUAGE -language
            INTO lt_tpool.
          -textpool = add_tpool( lt_tpool ).
        ENDLOOP.
    
        IF lines( lt_tpool_i18n ) > 0.
          ii_xml->add( iv_name = 'I18N_TPOOL'
                       ig_data = lt_tpool_i18n ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        SELECT SINGLE unam FROM reposrc INTO rv_user
          WHERE progname = ms_item-obj_name
          AND r3state = 'A'.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA:
          lv_program  LIKE sy-repid,
          lv_obj_name TYPE e071-obj_name.
    
        lv_program = ms_item-obj_name.
    
        CALL FUNCTION 'RS_DELETE_PROGRAM'
          EXPORTING
            corrnumber                 = iv_transport
            program                    = lv_program
            suppress_popup             = abap_true
            mass_delete_call           = abap_true
            tadir_devclass             = iv_package
            force_delete_used_includes = abap_true
          EXCEPTIONS
            enqueue_lock               = 1
            object_not_found           = 2
            permission_failure         = 3
            reject_deletion            = 4
            OTHERS                     = 5.
        IF sy-subrc = 2.
          " Drop also any inactive code that is left in REPOSRC
          zcl_abapgit_factory=>get_sap_report( )->delete_report( lv_program ).
    
          " Remove inactive objects from work area
          lv_obj_name = lv_program.
    
          CALL FUNCTION 'RS_DELETE_FROM_WORKING_AREA'
            EXPORTING
              object                 = 'REPS'
              obj_name               = lv_obj_name
              immediate              = 'X'
              actualize_working_area = 'X'.
    
          CALL FUNCTION 'RS_DELETE_FROM_WORKING_AREA'
            EXPORTING
              object                 = 'REPT'
              obj_name               = lv_obj_name
              immediate              = 'X'
              actualize_working_area = 'X'.
        ELSEIF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        delete_longtexts( c_longtext_id_prog ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_program_name TYPE syrepid,
              ls_progdir      TYPE zif_abapgit_sap_report=>ty_progdir,
              lt_tpool        TYPE textpool_table,
              lt_dynpros      TYPE ty_dynpro_tt,
              lt_tpool_ext    TYPE zif_abapgit_lang_definitions=>ty_tpool_tt,
              ls_cua          TYPE ty_cua,
              lt_source       TYPE abaptxt255_tab.
    
        " Add R3TR PROG to transport first, otherwise we get several LIMUs
        corr_insert( iv_package ).
    
        lv_program_name = ms_item-obj_name.
    
        lt_source = mo_files->read_abap( ).
    
        io_xml->read( EXPORTING iv_name = 'TPOOL'
                      CHANGING cg_data = lt_tpool_ext ).
        lt_tpool = read_tpool( lt_tpool_ext ).
    
        io_xml->read( EXPORTING iv_name = 'PROGDIR'
                      CHANGING cg_data  = ls_progdir ).
    
        set_abap_language_version( CHANGING cv_abap_language_version = ls_progdir-uccheck ).
    
        IF strlen( lv_program_name ) > 30.
    
          " Objects with extension for example transportable Code Inspector variants (ending with ===VC)
          deserialize_with_ext( is_progdir = ls_progdir
                                iv_package = iv_package
                                it_source  = lt_source ).
    
        ELSE.
    
          deserialize_program( is_progdir = ls_progdir
                               it_source  = lt_source
                               it_tpool   = lt_tpool
                               iv_package = iv_package ).
    
          io_xml->read( EXPORTING iv_name = 'DYNPROS'
                        CHANGING cg_data  = lt_dynpros ).
          deserialize_dynpros( lt_dynpros ).
    
          io_xml->read( EXPORTING iv_name = 'CUA'
                        CHANGING cg_data  = ls_cua ).
          deserialize_cua( iv_program_name = lv_program_name
                           is_cua = ls_cua ).
    
          " Texts deserializing (English)
          deserialize_textpool( iv_program = lv_program_name
                                it_tpool   = lt_tpool ).
    
          " Texts deserializing (translations)
          IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
            deserialize_texts( io_xml ).
          ENDIF.
    
          deserialize_longtexts( ii_xml         = io_xml
                                 iv_longtext_id = c_longtext_id_prog ).
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_progname TYPE reposrc-progname.
    
        SELECT SINGLE progname FROM reposrc INTO lv_progname
          WHERE progname = ms_item-obj_name.
        rv_bool = boolc( sy-subrc = 0 ).
    
        " Skip PROG generated by CHDO
        IF rv_bool = abap_true.
          FIND REGEX '^F.*CD[C|F|T|V]' IN ms_item-obj_name ##REGEX_POSIX.
          IF sy-subrc <> 0.
            FIND REGEX '^/.*/F.*CD[C|F|T|V]' IN ms_item-obj_name ##REGEX_POSIX.
          ENDIF.
          rv_bool = boolc( sy-subrc <> 0 ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        IF is_program_locked( ) = abap_true
            OR is_any_dynpro_locked( ms_item-obj_name ) = abap_true
            OR is_cua_locked( ms_item-obj_name ) = abap_true
            OR is_text_locked( ms_item-obj_name ) = abap_true.
    
          rv_is_locked = abap_true.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        serialize_program( io_xml   = io_xml
                           is_item  = ms_item
                           io_files = mo_files ).
    
        " Texts serializing (translations)
        IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
          serialize_texts( io_xml ).
        ENDIF.
    
        serialize_longtexts( ii_xml         = io_xml
                             iv_longtext_id = c_longtext_id_prog ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ront IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
        DATA: lv_user  TYPE string,
              lx_error TYPE REF TO cx_root.
    
        TRY.
    
            SELECT SINGLE changed_by INTO lv_user
                FROM (c_table_name)
                WHERE ront_name = ms_item-obj_name AND version = 'I'.
    
            IF lv_user IS INITIAL.
              SELECT SINGLE changed_by INTO lv_user
                FROM (c_table_name)
                WHERE ront_name = ms_item-obj_name AND version = 'A'.
            ENDIF.
    
            rv_user = lv_user.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_OBJECT_SAJC IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        CONSTANTS lc_table_name TYPE tabname VALUE 'APJ_W_JCE_ROOT'.
    
        SELECT SINGLE lst_ch_user_acct
          FROM (lc_table_name)
          INTO rv_user
          WHERE job_catalog_entry_name = ms_item-obj_name
            AND job_catalog_entry_version = 'I'.
    
        IF rv_user IS INITIAL.
          SELECT SINGLE lst_ch_user_acct
            FROM (lc_table_name)
            INTO rv_user
            WHERE job_catalog_entry_name = ms_item-obj_name
              AND job_catalog_entry_version = 'A'.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sajt IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        CONSTANTS lc_table_name TYPE tabname VALUE 'APJ_W_JT_ROOT'.
    
        SELECT SINGLE lst_ch_user_acct
          FROM (lc_table_name)
          INTO rv_user
          WHERE job_template_name = ms_item-obj_name
            AND job_template_version = 'I'.
    
        IF rv_user IS INITIAL.
          SELECT SINGLE lst_ch_user_acct
            FROM (lc_table_name)
            INTO rv_user
            WHERE job_template_name = ms_item-obj_name
              AND job_template_version = 'A'.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_saxx_super IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
            is_item        = is_item
            iv_language    = iv_language
            io_files       = io_files
            io_i18n_params = io_i18n_params ).
    
        mv_object_key = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD create_channel_objects.
    
        get_names( ).
    
        TRY.
            CREATE OBJECT mi_appl_obj_data TYPE (mv_appl_obj_cls_name).
            CREATE OBJECT mi_persistence TYPE (mv_persistence_cls_name).
    
          CATCH cx_sy_create_object_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = ms_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get_data.
    
        DATA: lx_error TYPE REF TO cx_swb_exception.
    
        TRY.
            mi_persistence->get(
              EXPORTING
                p_object_key  = mv_object_key
                p_version     = 'A'
              CHANGING
                p_object_data = mi_appl_obj_data ).
    
          CATCH cx_swb_exception INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        mi_appl_obj_data->get_data( IMPORTING p_data = eg_data ).
    
      ENDMETHOD.
    
      METHOD get_names.
    
        IF mv_data_structure_name IS INITIAL.
          mv_data_structure_name  = get_data_structure_name( ).
        ENDIF.
    
        IF mv_appl_obj_cls_name IS INITIAL.
          mv_appl_obj_cls_name    = get_data_class_name( ).
        ENDIF.
    
        IF mv_persistence_cls_name IS INITIAL.
          mv_persistence_cls_name = get_persistence_class_name( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD lock.
    
        DATA: lv_objname TYPE trobj_name,
              lv_objtype TYPE trobjtype.
    
        lv_objname = ms_item-obj_name.
        lv_objtype = ms_item-obj_type.
    
        mi_persistence->lock(
          EXPORTING
            p_objname_tr   = lv_objname
            p_object_key   = mv_object_key
            p_objtype_tr   = lv_objtype
          EXCEPTIONS
            foreign_lock   = 1
            error_occurred = 2
            OTHERS         = 3 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error occurred while locking { ms_item-obj_type } { lv_objname }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD unlock.
    
        DATA: lv_objname TYPE trobj_name,
              lv_objtype TYPE trobjtype.
    
        lv_objname = ms_item-obj_name.
        lv_objtype = ms_item-obj_type.
    
        mi_persistence->unlock( p_objname_tr = lv_objname
                                p_object_key = mv_object_key
                                p_objtype_tr = lv_objtype ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lr_data TYPE REF TO data.
    
        FIELD-SYMBOLS:        TYPE any,
                        TYPE any.
    
        CREATE DATA lr_data TYPE (mv_data_structure_name).
        ASSIGN lr_data->* TO .
    
        get_data( IMPORTING eg_data =  ).
    
        ASSIGN COMPONENT 'HEADER-CHANGED_BY' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
    
        IF  IS NOT INITIAL.
          rv_user = .
        ELSE.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        TRY.
            lock( ).
            mi_persistence->delete( mv_object_key ).
            unlock( ).
    
          CATCH cx_swb_exception.
            zcx_abapgit_exception=>raise( |Error occurred while deleting { ms_item-obj_type }| ).
        ENDTRY.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lr_data TYPE REF TO data.
    
        FIELD-SYMBOLS:  TYPE any.
    
        CREATE DATA lr_data TYPE (mv_data_structure_name).
        ASSIGN lr_data->* TO .
    
        io_xml->read(
          EXPORTING
            iv_name = ms_item-obj_type
          CHANGING
            cg_data =  ).
    
        IF zif_abapgit_object~exists( ) = abap_true.
          zif_abapgit_object~delete( iv_package   = iv_package
                                     iv_transport = iv_transport
                                     ii_log       = ii_log ).
        ENDIF.
    
        TRY.
            lock( ).
            corr_insert( iv_package ).
            mi_appl_obj_data->set_data(  ).
            mi_persistence->save( mi_appl_obj_data ).
            unlock( ).
    
          CATCH cx_swb_exception.
            zcx_abapgit_exception=>raise( |Error occurred while creating { ms_item-obj_type }| ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        TRY.
            mi_persistence->get( p_object_key           = mv_object_key
                                 p_version              = 'A'
                                 p_existence_check_only = abap_true ).
    
          CATCH cx_swb_exception.
            rv_bool = abap_false.
            RETURN.
        ENDTRY.
    
        rv_bool = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lv_argument    TYPE seqg3-garg,
              lv_lock_object TYPE string.
    
        lv_lock_object = get_lock_object( ).
    
        lv_argument = mv_object_key.
        OVERLAY lv_argument WITH '                              '.
        lv_argument = lv_argument && '*'.
    
        rv_is_locked = exists_a_lock_entry_for(
                         iv_lock_object = lv_lock_object
                         iv_argument    = lv_argument ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lr_data TYPE REF TO data.
    
        FIELD-SYMBOLS:    TYPE any,
                        TYPE any,
                         TYPE any.
    
        CREATE DATA lr_data TYPE (mv_data_structure_name).
        ASSIGN lr_data->* TO .
    
        get_data( IMPORTING eg_data =  ).
    
        ASSIGN COMPONENT 'HEADER' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN COMPONENT 'CHANGED_ON' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
        CLEAR .
    
        ASSIGN COMPONENT 'CHANGED_BY' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
        CLEAR .
    
        ASSIGN COMPONENT 'CHANGED_AT' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
        CLEAR .
    
        ASSIGN COMPONENT 'CHANGED_CLNT' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
        CLEAR .
    
        ASSIGN COMPONENT 'CREATED_ON' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
        CLEAR .
    
        ASSIGN COMPONENT 'CREATED_BY' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
        CLEAR .
    
        ASSIGN COMPONENT 'CREATED_AT' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
        CLEAR .
    
        ASSIGN COMPONENT 'CREATED_CLNT' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
        CLEAR .
    
        io_xml->add( iv_name = ms_item-obj_type
                     ig_data =  ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_samc IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
            is_item        = is_item
            iv_language    = iv_language
            io_files       = io_files
            io_i18n_params = io_i18n_params ).
    
        create_channel_objects( ).
    
      ENDMETHOD.
    
      METHOD get_data_class_name.
    
        rv_data_class_name = 'CL_AMC_APPLICATION_OBJ_DATA'.
    
      ENDMETHOD.
    
      METHOD get_data_structure_name.
    
        rv_data_structure_name = 'AMC_APPLICATION_COMPLETE'.
    
      ENDMETHOD.
    
      METHOD get_persistence_class_name.
    
        rv_persistence_class_name = 'CL_AMC_APPLICATION_OBJ_PERS'.
    
      ENDMETHOD.
    
      METHOD get_lock_object.
    
        rv_lock_object = 'E_AMC_APPL'.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sapc IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
            is_item        = is_item
            iv_language    = iv_language
            io_files       = io_files
            io_i18n_params = io_i18n_params ).
    
        create_channel_objects( ).
    
      ENDMETHOD.
    
      METHOD get_data_class_name.
    
        rv_data_class_name = 'CL_APC_APPLICATION_OBJ_DATA'.
    
      ENDMETHOD.
    
      METHOD get_data_structure_name.
    
        rv_data_structure_name = 'APC_APPLICATION_COMPLETE'.
    
      ENDMETHOD.
    
      METHOD get_persistence_class_name.
    
        rv_persistence_class_name = 'CL_APC_APPLICATION_OBJ_PERS'.
    
      ENDMETHOD.
    
      METHOD get_lock_object.
    
        rv_lock_object = 'E_APC_APPL'.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_object_scp1 IMPLEMENTATION.
    
      METHOD adjust_inbound.
    
        FIELD-SYMBOLS:  TYPE scprvals,
                        TYPE scprreca,
                        TYPE scprvall.
    
    * back to internal format
        LOOP AT cs_scp1-scprvals ASSIGNING .
          SHIFT -recnumber RIGHT DELETING TRAILING space.
        ENDLOOP.
        LOOP AT cs_scp1-scprreca ASSIGNING .
          SHIFT -recnumber RIGHT DELETING TRAILING space.
        ENDLOOP.
        LOOP AT cs_scp1-scprvall ASSIGNING .
          SHIFT -recnumber RIGHT DELETING TRAILING space.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD adjust_outbound.
    
        FIELD-SYMBOLS:  TYPE scprvals,
                        TYPE scprreca,
                        TYPE scprvall.
    
    * normalize the XML
        LOOP AT cs_scp1-scprvals ASSIGNING .
          CONDENSE -recnumber.
        ENDLOOP.
        LOOP AT cs_scp1-scprreca ASSIGNING .
          CONDENSE -recnumber.
        ENDLOOP.
        LOOP AT cs_scp1-scprvall ASSIGNING .
          CONDENSE -recnumber.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD call_delete_fms.
    
        CONSTANTS:
          lc_version_new      TYPE c VALUE 'N', "Include SCPRINTCONST version_new
          lc_operation_delete TYPE c VALUE 'D'.
    
        DATA:
          lv_profile_type   TYPE scprattr-type,
          lt_fatherprofiles TYPE STANDARD TABLE OF scproprof WITH DEFAULT KEY,
          ls_fatherprofile  TYPE scproprof.
    
        CALL FUNCTION 'SCPR_DB_ATTR_GET_DETAIL'
          EXPORTING
            profid   = iv_profile_id
            version  = lc_version_new
          IMPORTING
            proftype = lv_profile_type
          EXCEPTIONS
            OTHERS   = 0.
    
        CALL FUNCTION 'SCPR_PRSET_DB_USED_IN'
          EXPORTING
            profid   = iv_profile_id
            version  = lc_version_new
          TABLES
            profiles = lt_fatherprofiles.
    
        ls_fatherprofile-id = iv_profile_id.
        APPEND ls_fatherprofile TO lt_fatherprofiles.
        CALL FUNCTION 'SCPR_CT_TRANSPORT_ENTRIES'
          TABLES
            profids                  = lt_fatherprofiles
          EXCEPTIONS
            error_in_transport_layer = 1
            user_abort               = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error while deleting SCP1 - TRANSPORT, { sy-subrc }| ).
        ENDIF.
    
        CALL FUNCTION 'SCPR_PRSET_DB_DELETE_ALL'
          EXPORTING
            profid      = iv_profile_id
            proftype    = lv_profile_type
          TABLES
            fatherprofs = lt_fatherprofiles
          EXCEPTIONS
            user_abort  = 1.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |error while deleting SCP1 - DB_DELETE, { sy-subrc }| ).
        ENDIF.
    
        CALL FUNCTION 'SCPR_MEM_SCPR_ACTIONS_ADD'
          EXPORTING
            bcset_id  = iv_profile_id
            operation = lc_operation_delete.
    
      ENDMETHOD.
    
      METHOD dequeue.
    
        DATA: lv_id TYPE scpr_id.
    
        lv_id = ms_item-obj_name.
    
        CALL FUNCTION 'SCPR_SV_DEQUEUE_BCSET'
          EXPORTING
            bcset_id = lv_id.
    
      ENDMETHOD.
    
      METHOD enqueue.
    
        DATA: lv_id TYPE scpr_id.
    
        lv_id = ms_item-obj_name.
    
        CALL FUNCTION 'SCPR_SV_ENQUEUE_BCSET'
          EXPORTING
            bcset_id          = lv_id
          EXCEPTIONS
            is_already_locked = 1
            system_failure    = 2
            OTHERS            = 3.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD load.
    
        CALL FUNCTION 'SCPR_TEMPL_DB_VALS_GET_DETAIL'
          EXPORTING
            profid   = cs_scp1-scprattr-id
            category = cs_scp1-scprattr-category
          TABLES
            values   = cs_scp1-scprvals
            valuesl  = cs_scp1-scprvall
            recattr  = cs_scp1-scprreca.
    
        CALL FUNCTION 'SCPR_TEMPL_DB_FLDTXTVAR_GET'
          EXPORTING
            bcset_id = cs_scp1-scprattr-id
            category = cs_scp1-scprattr-category
          TABLES
            it_fldv  = cs_scp1-scprfldv.
    
      ENDMETHOD.
    
      METHOD load_hier.
    
        CALL FUNCTION 'SCPR_PRSET_DB_SUBP_GET_DETAIL'
          EXPORTING
            profid   = cs_scp1-scprattr-id
            category = cs_scp1-scprattr-category
          TABLES
            subprofs = cs_scp1-subprofs.
    
      ENDMETHOD.
    
      METHOD save.
    
        DATA: ls_scp1 TYPE ty_scp1,
              ls_text TYPE scprtext.
    
    * copy everything to local, the function module changes the values
        ls_scp1 = is_scp1.
    
        READ TABLE ls_scp1-scprtext INTO ls_text WITH KEY langu = mv_language. "#EC CI_SUBRC
    
        CALL FUNCTION 'SCPR_TEMPL_MN_TEMPLATE_SAVE'
          EXPORTING
            profid                    = ls_scp1-scprattr-id
            proftext                  = ls_text-text
            category                  = ls_scp1-scprattr-category
            cli_dep                   = ls_scp1-scprattr-cli_dep
            cli_cas                   = ls_scp1-scprattr-cli_cas
            reftype                   = ls_scp1-scprattr-reftype
            refname                   = ls_scp1-scprattr-refname
            orgid                     = ls_scp1-scprattr-orgid
            component                 = ls_scp1-scprattr-component
            minrelease                = ls_scp1-scprattr-minrelease
            maxrelease                = ls_scp1-scprattr-maxrelease
            act_info                  = ls_scp1-scprattr-act_info
            bcset_type                = ls_scp1-scprattr-type
            fldtxtvar_supplied        = 'YES'
            with_transp_insert        = abap_false
            with_progress_indicator   = abap_false
            remove_denied_data        = abap_true
            ask_for_cont_after_remove = abap_true
          TABLES
            values                    = ls_scp1-scprvals
            valuesl                   = ls_scp1-scprvall
            recattr                   = ls_scp1-scprreca
            it_fldv                   = ls_scp1-scprfldv
            texts                     = ls_scp1-scprtext
          EXCEPTIONS
            user_abort                = 1
            error_in_transport_layer  = 2
            inconsistent_data         = 3
            database_error            = 4
            OTHERS                    = 5.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD save_hier.
    
        DATA: ls_scp1  TYPE ty_scp1,
              ls_profs LIKE LINE OF ls_scp1-subprofs,
              lt_sub   TYPE STANDARD TABLE OF scproprof WITH DEFAULT KEY,
              ls_sub   LIKE LINE OF lt_sub,
              ls_text  TYPE scprtext.
    
    * copy everything to local, the function module changes the values
        ls_scp1 = is_scp1.
    
        READ TABLE ls_scp1-scprtext INTO ls_text WITH KEY langu = mv_language. "#EC CI_SUBRC
    
    * see fm SCPR_PRSET_DB_STORE, only this field and sequence is used
        LOOP AT ls_scp1-subprofs INTO ls_profs.
          ls_sub-id = ls_profs-subprofile.
          APPEND ls_sub TO lt_sub.
        ENDLOOP.
    
        CALL FUNCTION 'SCPR_PRSET_MN_BCSET_SAVE'
          EXPORTING
            profid                   = ls_scp1-scprattr-id
            proftext                 = ls_text-text
            category                 = ls_scp1-scprattr-category
            cli_dep                  = ls_scp1-scprattr-cli_dep
            cli_cas                  = ls_scp1-scprattr-cli_cas
            reftype                  = ls_scp1-scprattr-reftype
            refname                  = ls_scp1-scprattr-refname
            orgid                    = ls_scp1-scprattr-orgid
            component                = ls_scp1-scprattr-component
            minrelease               = ls_scp1-scprattr-minrelease
            maxrelease               = ls_scp1-scprattr-maxrelease
            act_info                 = ls_scp1-scprattr-act_info
            with_transp_insert       = abap_false
            with_progress_indicator  = abap_false
          TABLES
            subprofs                 = lt_sub
            texts                    = ls_scp1-scprtext
          EXCEPTIONS
            user_abort               = 1
            error_in_transport_layer = 2
            OTHERS                   = 3.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE modifier INTO rv_user FROM scprattr
          WHERE id = ms_item-obj_name
          AND version = 'N'.
        IF sy-subrc <> 0 OR rv_user IS INITIAL.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_profile_id TYPE scpr_id.
    
        lv_profile_id = ms_item-obj_name.
    
        enqueue( ).
        call_delete_fms( lv_profile_id ).
        dequeue( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_scp1 TYPE ty_scp1.
    
        io_xml->read(
          EXPORTING iv_name = 'SCP1'
          CHANGING  cg_data = ls_scp1 ).
    
        adjust_inbound( CHANGING cs_scp1 = ls_scp1 ).
    
        IF ls_scp1-scprattr-type = 'TMP'.
          save_hier( ls_scp1 ).
        ELSE.
          save( ls_scp1 ).
        ENDIF.
    
        dequeue( ).
    
        tadir_insert( iv_package ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_rc     TYPE sy-subrc,
              lv_profid TYPE scprattr-id.
    
        lv_profid = ms_item-obj_name.
    
        CALL FUNCTION 'SCPR_BCSET_EXISTS'
          EXPORTING
            profid = lv_profid
          IMPORTING
            rc     = lv_rc.
        rv_bool = boolc( lv_rc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
    
        rs_metadata = get_metadata( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: lv_display_only TYPE scpr_txt20,
              lv_bcset_id     TYPE scpr_id.
    
        lv_display_only = abap_false.
        lv_bcset_id     = ms_item-obj_name.
    
        EXPORT scpr3_display_only = lv_display_only
               scpr3_bcset_id     = lv_bcset_id
            TO MEMORY ID 'SCPR3_PARAMETER'.
    
        SUBMIT scpr3 AND RETURN.
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_scp1 TYPE ty_scp1.
    
        ls_scp1-scprattr-id = ms_item-obj_name.
    
        CALL FUNCTION 'SCPR_DB_ATTR_GET_DETAIL'
          EXPORTING
            profid     = ls_scp1-scprattr-id
          IMPORTING
            proftype   = ls_scp1-scprattr-type
            cli_dep    = ls_scp1-scprattr-cli_dep
            cli_cas    = ls_scp1-scprattr-cli_cas
            reftype    = ls_scp1-scprattr-reftype
            refname    = ls_scp1-scprattr-refname
            component  = ls_scp1-scprattr-component
            minrelease = ls_scp1-scprattr-minrelease
            maxrelease = ls_scp1-scprattr-maxrelease
            orgid      = ls_scp1-scprattr-orgid
            act_info   = ls_scp1-scprattr-act_info.
    
        CALL FUNCTION 'SCPR_TEXT_GET'
          EXPORTING
            profid        = ls_scp1-scprattr-id
            category      = ls_scp1-scprattr-category
          TABLES
            texts         = ls_scp1-scprtext
          EXCEPTIONS
            no_text_found = 1
            OTHERS        = 2 ##FM_SUBRC_OK.
    
        IF ls_scp1-scprattr-type = 'TMP'.
          load_hier( CHANGING cs_scp1 = ls_scp1 ).
        ELSE.
          load( CHANGING cs_scp1 = ls_scp1 ).
        ENDIF.
    
        adjust_outbound( CHANGING cs_scp1 = ls_scp1 ).
    
        io_xml->add(
          iv_name = 'SCP1'
          ig_data  = ls_scp1 ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_scvi IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lv_screen_variant TYPE scvariant.
    
        lv_screen_variant = ms_item-obj_name.
    
        SELECT SINGLE chuser
        FROM shdsvci
        INTO rv_user
        WHERE scvariant = lv_screen_variant.
        IF sy-subrc <> 0
        OR rv_user IS INITIAL.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_screen_variant TYPE scvariant.
    
        lv_screen_variant = ms_item-obj_name.
    
        CALL FUNCTION 'RS_HDSYS_DELETE_SC_VARIANT'
          EXPORTING
            scvariant        = lv_screen_variant
          EXCEPTIONS
            variant_enqueued = 1
            no_correction    = 2
            scvariant_used   = 3
            OTHERS           = 4.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_screen_variant TYPE ty_screen_variant.
    
        io_xml->read(
          EXPORTING
            iv_name = 'SCVI'
          CHANGING
            cg_data = ls_screen_variant ).
    
        CALL FUNCTION 'ENQUEUE_ESSCVARCIU'
          EXPORTING
            scvariant = ls_screen_variant-shdsvci-scvariant
          EXCEPTIONS
            OTHERS    = 1.
        IF sy-subrc <> 0.
          MESSAGE e413(ms) WITH ls_screen_variant-shdsvci-scvariant INTO zcx_abapgit_exception=>null.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        corr_insert( iv_package ).
    
    *   Populate user details
        ls_screen_variant-shdsvci-crdate = sy-datum.
        ls_screen_variant-shdsvci-cruser = sy-uname.
        ls_screen_variant-shdsvci-chdate = sy-datum.
        ls_screen_variant-shdsvci-chuser = sy-uname.
    
        MODIFY shdsvci    FROM ls_screen_variant-shdsvci.
        MODIFY shdsvtxci  FROM TABLE ls_screen_variant-shdsvtxci[].
        MODIFY shdsvfvci  FROM TABLE ls_screen_variant-shdsvfvci[].
        MODIFY shdguixt   FROM TABLE ls_screen_variant-shdguixt[].
        MODIFY shdgxtcode FROM TABLE ls_screen_variant-shdgxtcode[].
    
        CALL FUNCTION 'DEQUEUE_ESSCVARCIU'
          EXPORTING
            scvariant = ls_screen_variant-shdsvci-scvariant.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_screen_variant TYPE scvariant.
    
        lv_screen_variant = ms_item-obj_name.
    
        CALL FUNCTION 'RS_HDSYS_READ_SC_VARIANT_DB'
          EXPORTING
            scvariant  = lv_screen_variant
          EXCEPTIONS
            no_variant = 1
            OTHERS     = 2.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
    
        rs_metadata = get_metadata( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
    
        rv_active = is_active( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_screen_variant TYPE ty_screen_variant.
    
        ls_screen_variant-shdsvci-scvariant = ms_item-obj_name.
    
        CALL FUNCTION 'RS_HDSYS_READ_SC_VARIANT_DB'
          EXPORTING
            scvariant        = ls_screen_variant-shdsvci-scvariant
          IMPORTING
            header_scvariant = ls_screen_variant-shdsvci
          TABLES
            values_scvariant = ls_screen_variant-shdsvfvci[]
            guixt_scripts    = ls_screen_variant-shdguixt[]
          EXCEPTIONS
            no_variant       = 1
            OTHERS           = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        SORT ls_screen_variant-shdsvfvci ASCENDING.
        SORT ls_screen_variant-shdguixt ASCENDING.
    
    *   Clear all user details
        CLEAR: ls_screen_variant-shdsvci-crdate,
               ls_screen_variant-shdsvci-cruser,
               ls_screen_variant-shdsvci-chdate,
               ls_screen_variant-shdsvci-chuser.
    
        SELECT *
        FROM shdsvtxci
        INTO TABLE ls_screen_variant-shdsvtxci[]
        WHERE scvariant = ls_screen_variant-shdsvci-scvariant
        ORDER BY PRIMARY KEY.
    
        SELECT *
        FROM shdgxtcode
        INTO TABLE ls_screen_variant-shdgxtcode[]
        WHERE scvariant = ls_screen_variant-shdsvci-scvariant
        ORDER BY PRIMARY KEY.
    
        io_xml->add( iv_name = 'SCVI'
                     ig_data = ls_screen_variant ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sfbf IMPLEMENTATION.
    
      METHOD activate.
    
        DATA: lt_bfuncts TYPE sfw_bftab,
              lt_msgtab  TYPE sprot_u_tab.
    
        IF zif_abapgit_object~is_active( ) = abap_true.
          RETURN.
        ENDIF.
    
        APPEND mv_bf TO lt_bfuncts.
    
        cl_sfw_activate=>activate_sfbf(
          EXPORTING
            p_bfuncts = lt_bfuncts
            p_version = 'I'
          IMPORTING
            p_msgtab  = lt_msgtab ).
    
        READ TABLE lt_msgtab WITH KEY severity = 'E' TRANSPORTING NO FIELDS.
        IF sy-subrc = 0.
          zcx_abapgit_exception=>raise( 'Error activating SFBF' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_bf = is_item-obj_name.
    
      ENDMETHOD.
    
      METHOD create.
    
        TRY.
            " make sure to clear cache
            ro_bf = cl_sfw_bf=>create_bf( mv_bf ).
            ro_bf->free( ).
            ro_bf = cl_sfw_bf=>create_bf( mv_bf ).
          CATCH cx_pak_invalid_data cx_pak_invalid_state cx_pak_not_authorized.
            zcx_abapgit_exception=>raise( 'Error from CL_SFW_BF=>CREATE_BF' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get.
    
        TRY.
            " make sure to clear cache, method GET_BF_FROM_DB does not exist in 702
            ro_bf = cl_sfw_bf=>get_bf( mv_bf ).
            ro_bf->free( ).
            ro_bf = cl_sfw_bf=>get_bf( mv_bf ).
          CATCH cx_pak_invalid_data cx_pak_invalid_state cx_pak_not_authorized.
            zcx_abapgit_exception=>raise( 'Error from CL_SFW_BF=>GET_BF' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD unlock.
    
        CALL FUNCTION 'DEQUEUE_EEUDB'
          EXPORTING
            relid     = 'SF'
            name      = ms_item-obj_name
            _synchron = 'X'
            _scope    = '1'
            mode_eudb = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: ls_data TYPE sfw_bf.
    
        ls_data = get( )->get_header_data( ).
    
        rv_user = ls_data-changedby.
    
        IF rv_user IS INITIAL.
          rv_user = ls_data-author.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lt_delete TYPE sfw_bftab,
              lt_msgtab TYPE sprot_u_tab.
    
        APPEND mv_bf TO lt_delete.
    
        cl_sfw_activate=>delete_sfbf( EXPORTING p_bfuncts = lt_delete
                                      IMPORTING p_msgtab = lt_msgtab ).
    
        READ TABLE lt_msgtab WITH KEY severity = 'E' TRANSPORTING NO FIELDS.
        IF sy-subrc = 0.
          zcx_abapgit_exception=>raise( 'Error deleting SFBF' ).
        ENDIF.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lo_bf                TYPE REF TO cl_sfw_bf,
              ls_header            TYPE sfw_bf,
              lv_name_32           TYPE sfw_name32,
              lv_name_80           TYPE sfw_name80,
              lt_assigned_switches TYPE sfw_swbf_outtab,
              lt_dependancies      TYPE sfw_depend_outtab,
              ls_sfw_bfc_kw        TYPE sfw_bfc_kw,
              ls_sfw_bfc_tc        TYPE sfw_bfc_tc,
              ls_sfw_bfc_rn        TYPE sfw_bfc_rn,
              lt_parent_bfs        TYPE sfw_bs_bf_outtab.
    
        IF iv_step = zif_abapgit_object=>gc_step_id-late.
          activate( ).
          RETURN.
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'HEADER'
                      CHANGING cg_data = ls_header ).
        io_xml->read( EXPORTING iv_name = 'NAME32'
                      CHANGING cg_data = lv_name_32 ).
        io_xml->read( EXPORTING iv_name = 'NAME80'
                      CHANGING cg_data = lv_name_80 ).
    
        io_xml->read( EXPORTING iv_name = 'ASSIGNED_SWITCHES'
                      CHANGING cg_data = lt_assigned_switches ).
        io_xml->read( EXPORTING iv_name = 'DEPENDANCIES'
                      CHANGING cg_data = lt_dependancies ).
        io_xml->read( EXPORTING iv_name = 'CONTENT_KW'
                      CHANGING cg_data = ls_sfw_bfc_kw ).
        io_xml->read( EXPORTING iv_name = 'CONTENT_TC'
                      CHANGING cg_data = ls_sfw_bfc_tc ).
        io_xml->read( EXPORTING iv_name = 'CONTENT_RN'
                      CHANGING cg_data = ls_sfw_bfc_rn ).
        io_xml->read( EXPORTING iv_name = 'PARENT_BFS'
                      CHANGING cg_data = lt_parent_bfs ).
    
        TRY.
            IF zif_abapgit_object~exists( ) = abap_true.
              lo_bf = get( ).
            ELSE.
              lo_bf = create( ).
            ENDIF.
          CATCH cx_pak_not_authorized cx_pak_invalid_state cx_pak_invalid_data.
            zcx_abapgit_exception=>raise( 'error in CL_SFW_BF=>CREATE_BF' ).
        ENDTRY.
    
        ls_header-author = sy-uname.
        ls_header-createdon = sy-datum.
    
        " Get component from package
        SELECT SINGLE dlvunit FROM tdevc INTO ls_header-component WHERE devclass = iv_package.
    
        lo_bf->set_header_data( ls_header ).
    
        lo_bf->set_texts( p_32 = lv_name_32
                          p_80 = lv_name_80 ).
    
        lo_bf->set_assigned_switches( lt_assigned_switches ).
        lo_bf->set_excluded_bf( lt_dependancies ).
        lo_bf->set_content_data(
            im_sfw_bfc_kw = ls_sfw_bfc_kw
            im_sfw_bfc_rn = ls_sfw_bfc_rn
            im_sfw_bfc_tc = ls_sfw_bfc_tc ).
        lo_bf->set_parent_bfs( lt_parent_bfs ).
    
        set_default_package( iv_package ).
        tadir_insert( iv_package ).
    
        lo_bf->save_all( ).
    
        unlock( ).
    
        deserialize_longtexts( ii_xml         = io_xml
                               iv_longtext_id = c_longtext_id_sfbf ).
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: ls_tadir TYPE tadir,
              lv_bf    TYPE sfw_bfunction.
    
        lv_bf = ms_item-obj_name.
        IF cl_sfw_bf=>check_existence( lv_bf ) = abap_false.
          RETURN.
        ENDIF.
    
        SELECT SINGLE * FROM tadir INTO ls_tadir
          WHERE pgmid = 'R3TR'
          AND object = ms_item-obj_type
          AND obj_name = ms_item-obj_name.
        IF ls_tadir IS INITIAL.
          RETURN.
        ENDIF.
    
        rv_bool = abap_true.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'EEUDB'
                                                iv_argument    = ms_item-obj_name
                                                iv_prefix      = 'SF' ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lo_bf                TYPE REF TO cl_sfw_bf,
              ls_header            TYPE sfw_bf,
              lv_name_32           TYPE sfw_name32,
              lv_name_80           TYPE sfw_name80,
              lt_assigned_switches TYPE sfw_swbf_outtab,
              lt_dependancies      TYPE sfw_depend_outtab,
              ls_sfw_bfc_kw        TYPE sfw_bfc_kw,
              ls_sfw_bfc_tc        TYPE sfw_bfc_tc,
              ls_sfw_bfc_rn        TYPE sfw_bfc_rn,
              lt_parent_bfs        TYPE sfw_bs_bf_outtab.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        lo_bf = get( ).
    
        ls_header = lo_bf->get_header_data( ).
        CLEAR: ls_header-author,
               ls_header-version,
               ls_header-component,
               ls_header-createdon,
               ls_header-changedby,
               ls_header-changedon,
               ls_header-timestamp.
    
        lo_bf->get_texts(
          IMPORTING
            p_32 = lv_name_32
            p_80 = lv_name_80 ).
    
        lt_assigned_switches = lo_bf->get_assigned_switches( ).
        lt_dependancies = lo_bf->get_excluded_bf( ).
        lo_bf->get_content_data(
          IMPORTING
            ex_sfw_bfc_kw = ls_sfw_bfc_kw
            ex_sfw_bfc_tc = ls_sfw_bfc_tc
            ex_sfw_bfc_rn = ls_sfw_bfc_rn ).
        lt_parent_bfs = lo_bf->get_parent_bfs( ).
    
        io_xml->add( ig_data = ls_header
                     iv_name = 'HEADER' ).
        io_xml->add( ig_data = lv_name_32
                     iv_name = 'NAME32' ).
        io_xml->add( ig_data = lv_name_80
                     iv_name = 'NAME80' ).
    
        io_xml->add( ig_data = lt_assigned_switches
                     iv_name = 'ASSIGNED_SWITCHES' ).
        io_xml->add( ig_data = lt_dependancies
                     iv_name = 'DEPENDANCIES' ).
        io_xml->add( ig_data = ls_sfw_bfc_kw
                     iv_name = 'CONTENT_KW' ).
        io_xml->add( ig_data = ls_sfw_bfc_tc
                     iv_name = 'CONTENT_TC' ).
        io_xml->add( ig_data = ls_sfw_bfc_rn
                     iv_name = 'CONTENT_RN' ).
        io_xml->add( ig_data = lt_parent_bfs
                     iv_name = 'PARENT_BFS' ).
    
        serialize_longtexts( ii_xml         = io_xml
                             iv_longtext_id = c_longtext_id_sfbf ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sfbs IMPLEMENTATION.
    
      METHOD activate.
    
        DATA: lt_bfsets TYPE sfw_bstab,
              lt_msgtab TYPE sprot_u_tab.
    
        IF zif_abapgit_object~is_active( ) = abap_true.
          RETURN.
        ENDIF.
    
        APPEND mv_bfset TO lt_bfsets.
    
        cl_sfw_activate=>activate_sfbs(
          EXPORTING
            p_bsets   = lt_bfsets
            p_version = 'I'
          IMPORTING
            p_msgtab  = lt_msgtab ).
    
        READ TABLE lt_msgtab WITH KEY severity = 'E' TRANSPORTING NO FIELDS.
        IF sy-subrc = 0.
          zcx_abapgit_exception=>raise( 'Error activating SFBS' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_bfset = is_item-obj_name.
    
      ENDMETHOD.
    
      METHOD create.
    
        TRY.
            " make sure to clear cache
            ro_bfs = cl_sfw_bfs=>create_bfs( mv_bfset ).
            ro_bfs->free( ).
            ro_bfs = cl_sfw_bfs=>create_bfs( mv_bfset ).
          CATCH cx_pak_invalid_data cx_pak_invalid_state cx_pak_not_authorized.
            zcx_abapgit_exception=>raise( 'Error from CL_SFW_BFS=>CREATE_BFS' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get.
    
        TRY.
            " make sure to clear cache
            ro_bfs = cl_sfw_bfs=>get_bfs( mv_bfset ).
            ro_bfs->free( ).
            ro_bfs = cl_sfw_bfs=>get_bfs( mv_bfset ).
          CATCH cx_pak_invalid_data cx_pak_invalid_state cx_pak_not_authorized.
            zcx_abapgit_exception=>raise( 'Error from CL_SFW_BFS=>GET_BFS' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD unlock.
    
        CALL FUNCTION 'DEQUEUE_EEUDB'
          EXPORTING
            relid     = 'SS'
            name      = ms_item-obj_name
            _synchron = 'X'
            _scope    = '1'
            mode_eudb = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: ls_data TYPE sfw_bs.
    
        ls_data = get( )->get_header_data( ).
    
        rv_user = ls_data-changedby.
    
        IF rv_user IS INITIAL.
          rv_user = ls_data-author.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lt_delete TYPE sfw_bstab,
              lt_msgtab TYPE sprot_u_tab.
    
        APPEND mv_bfset TO lt_delete.
    
        cl_sfw_activate=>delete_sfbs( EXPORTING p_bsets = lt_delete
                                      IMPORTING p_msgtab = lt_msgtab ).
    
        READ TABLE lt_msgtab WITH KEY severity = 'E' TRANSPORTING NO FIELDS.
        IF sy-subrc = 0.
          zcx_abapgit_exception=>raise( 'Error deleting SFBS' ).
        ENDIF.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lo_bfs         TYPE REF TO cl_sfw_bfs,
              ls_header      TYPE sfw_bs,
              lv_name_32     TYPE sfw_name32,
              lv_name_80     TYPE sfw_name80,
              lt_assigned_bf TYPE sfw_bfbs_outtab,
              lt_nested_bfs  TYPE sfw_bsbs_outtab,
              lt_parent_bfs  TYPE sfw_bs_bs_parent_outtab.
    
        IF iv_step = zif_abapgit_object=>gc_step_id-late.
          activate( ).
          RETURN.
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'HEADER'
                      CHANGING cg_data = ls_header ).
        io_xml->read( EXPORTING iv_name = 'NAME32'
                      CHANGING cg_data = lv_name_32 ).
        io_xml->read( EXPORTING iv_name = 'NAME80'
                      CHANGING cg_data = lv_name_80 ).
    
        io_xml->read( EXPORTING iv_name = 'ASSIGNED_BF'
                      CHANGING cg_data = lt_assigned_bf ).
        io_xml->read( EXPORTING iv_name = 'NESTED_BFS'
                      CHANGING cg_data = lt_nested_bfs ).
        io_xml->read( EXPORTING iv_name = 'PARENT_BFS'
                      CHANGING cg_data = lt_parent_bfs ).
    
        TRY.
            IF zif_abapgit_object~exists( ) = abap_true.
              lo_bfs = get( ).
            ELSE.
              lo_bfs = create( ).
            ENDIF.
          CATCH cx_pak_not_authorized cx_pak_invalid_state cx_pak_invalid_data.
            zcx_abapgit_exception=>raise( 'error in CL_SFW_BFS=>CREATE_BFS' ).
        ENDTRY.
    
        ls_header-author = sy-uname.
        ls_header-createdon = sy-datum.
        lo_bfs->set_header_data( ls_header ).
    
        lo_bfs->set_texts( p_32 = lv_name_32
                           p_80 = lv_name_80 ).
    
        lo_bfs->set_assigned_bf( lt_assigned_bf ).
        lo_bfs->set_assigned_bfs( lt_nested_bfs ).
        lo_bfs->set_nested_parent( lt_parent_bfs ).
    
        set_default_package( iv_package ).
        tadir_insert( iv_package ).
    
        lo_bfs->save_all( ).
    
        unlock( ).
    
        deserialize_longtexts( ii_xml         = io_xml
                               iv_longtext_id = c_longtext_id_sfbs ).
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA ls_tadir TYPE tadir.
    
        IF cl_sfw_bfs=>check_existence( mv_bfset ) = abap_false.
          RETURN.
        ENDIF.
    
        SELECT SINGLE * FROM tadir INTO ls_tadir
          WHERE pgmid = 'R3TR'
          AND object = ms_item-obj_type
          AND obj_name = ms_item-obj_name.
        IF ls_tadir IS INITIAL.
          RETURN.
        ENDIF.
    
        rv_bool = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'EEUDB'
                                                iv_argument    = ms_item-obj_name
                                                iv_prefix      = 'SS' ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lo_bfs         TYPE REF TO cl_sfw_bfs,
              ls_header      TYPE sfw_bs,
              lv_name_32     TYPE sfw_name32,
              lv_name_80     TYPE sfw_name80,
              lt_assigned_bf TYPE sfw_bfbs_outtab,
              lt_nested_bfs  TYPE sfw_bsbs_outtab,
              lt_parent_bfs  TYPE sfw_bs_bs_parent_outtab.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        lo_bfs = get( ).
    
        ls_header = lo_bfs->get_header_data( ).
        CLEAR: ls_header-author,
               ls_header-version,
               ls_header-createdon,
               ls_header-changedby,
               ls_header-changedon,
               ls_header-timestamp.
    
        lo_bfs->get_texts(
          IMPORTING
            p_32 = lv_name_32
            p_80 = lv_name_80 ).
    
        lt_assigned_bf = lo_bfs->get_assigned_bf( ).
        lt_nested_bfs = lo_bfs->get_nested_bfs( ).
        lt_parent_bfs = lo_bfs->get_nested_parent( ).
    
        io_xml->add( ig_data = ls_header
                     iv_name = 'HEADER' ).
        io_xml->add( ig_data = lv_name_32
                     iv_name = 'NAME32' ).
        io_xml->add( ig_data = lv_name_80
                     iv_name = 'NAME80' ).
    
        io_xml->add( ig_data = lt_assigned_bf
                     iv_name = 'ASSIGNED_BF' ).
        io_xml->add( ig_data = lt_nested_bfs
                     iv_name = 'NESTED_BFS' ).
        io_xml->add( ig_data = lt_parent_bfs
                     iv_name = 'PARENT_BFS' ).
    
        serialize_longtexts( ii_xml         = io_xml
                             iv_longtext_id = c_longtext_id_sfbs ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sfpf IMPLEMENTATION.
    
      METHOD fix_oref.
    
    * During serialization of a SFPF / SFPI object the interface hierarchy
    * is represented by attributes "id" and "href", where the id looks
    * like "o" and href like "#o". Every run of
    * serialization generates a new  in these  attributes, that
    * leads to differences even by comparing of untouched forms.
    * The purpose of this method is to renumber the id's consequentially
    * and therefore to avoid fictive differences.
    
    * NB: As the method iterator->get_next() works quite slowly,
    *     it is better to collect all attributes in a cache table
    *     instead of implementing of a nested loop using get_next().
    
        DATA:
          li_iterator TYPE REF TO if_ixml_node_iterator,
          li_elem     TYPE REF TO if_ixml_element,
          lv_new      TYPE string,
          lv_old      TYPE string,
          lv_count    TYPE i,
          BEGIN OF ls_attr_href,
            val  TYPE string,
            attr TYPE REF TO if_ixml_attribute,
          END OF ls_attr_href,
          lt_attr_href LIKE SORTED TABLE OF ls_attr_href WITH NON-UNIQUE KEY val.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_attr_href.
    
    *   Collect all attributes href='#o...' in the cache table
        li_iterator = ii_document->create_iterator_filtered(
          ii_document->create_filter_and(
            filter1 = ii_document->create_filter_node_type( if_ixml_node=>co_node_element )
            filter2 = ii_document->create_filter_attribute( 'href' ) ) ).
        li_elem ?= li_iterator->get_next( ).
        WHILE li_elem IS NOT INITIAL.
          ls_attr_href-attr = li_elem->get_attribute_node( 'href' ).
          ls_attr_href-val = ls_attr_href-attr->get_value( ).
          IF ls_attr_href-val CP '##o*'.
            INSERT ls_attr_href INTO TABLE lt_attr_href.
          ENDIF.
          li_elem ?= li_iterator->get_next( ).
        ENDWHILE.
    
    *   Renumber id='o...' attributes
        li_iterator = ii_document->create_iterator_filtered(
          ii_document->create_filter_and(
            filter1 = ii_document->create_filter_node_type( if_ixml_node=>co_node_element )
            filter2 = ii_document->create_filter_attribute( 'id' ) ) ).
        li_elem ?= li_iterator->get_next( ).
        WHILE li_elem IS NOT INITIAL.
          lv_old = li_elem->get_attribute( 'id' ).
          IF lv_old CP 'o*'.
            lv_count = lv_count + 1.
            lv_new = |o{ lv_count }|.
    *       Rewrite id
            IF li_elem->set_attribute( name = 'id'
                                       value = lv_new ) IS NOT INITIAL.
              zcx_abapgit_exception=>raise( 'SFPF error, FIX_OREF' ).
            ENDIF.
    *       Update references
            LOOP AT lt_attr_href ASSIGNING  WHERE val = '#' && lv_old.
              IF -attr->set_value( '#' && lv_new ) IS NOT INITIAL.
                zcx_abapgit_exception=>raise( 'SFPF error, FIX_OREF' ).
              ENDIF.
            ENDLOOP.
          ENDIF.
          li_elem ?= li_iterator->get_next( ).
        ENDWHILE.
    
      ENDMETHOD.
    
      METHOD form_to_xstring.
    
        CONSTANTS: lc_empty_data TYPE xstring VALUE ''.
    
        DATA: li_fp_form     TYPE REF TO if_fp_form,
              li_wb_form     TYPE REF TO if_fp_wb_form,
              li_fp_layout   TYPE REF TO if_fp_layout,
              lx_fp_err      TYPE REF TO cx_fp_api,
              lx_fp_conv_err TYPE REF TO cx_fp_api,
              lv_layout_data TYPE xstring.
    
        li_wb_form = load( ).
        li_fp_form ?= li_wb_form->get_object( ).
        li_fp_layout = li_fp_form->get_layout( ).
        lv_layout_data = li_fp_layout->get_layout_data( ).
    
        mo_files->add_raw(
          iv_ext  = c_layout_file_ext
          iv_data = lv_layout_data ).
    
        TRY.
            li_fp_layout->set_layout_data( i_layout_data   = lc_empty_data
                                           i_set_xliff_ids = abap_false ).
          CATCH cx_fp_api INTO lx_fp_err.
            zcx_abapgit_exception=>raise( |SFPF remove layout: { lx_fp_err->get_text( ) }| ).
        ENDTRY.
    
        TRY.
            rv_xstr = cl_fp_helper=>convert_form_to_xstring( li_fp_form ).
          CATCH cx_fp_api INTO lx_fp_conv_err ##NO_HANDLER.
            " Pass - the exception is handled below!
        ENDTRY.
    
        TRY.
            li_fp_layout->set_layout_data( i_layout_data   = lv_layout_data
                                           i_set_xliff_ids = abap_false ).
          CATCH cx_fp_api INTO lx_fp_err.
            " Be aware that there might be another exception
            " raised by cl_fp_helper=>convert_form_to_xstring( )
            zcx_abapgit_exception=>raise( |SFPF recover layout: { lx_fp_err->get_text( ) }| ).
        ENDTRY.
    
        IF lx_fp_conv_err IS BOUND.
          " This statement handles the exception raised from cl_fp_helper=>convert_form_to_xstring( )
          zcx_abapgit_exception=>raise( |SFPF convert_form_to_xstring: { lx_fp_conv_err->get_text( ) }| ).
        ENDIF.
      ENDMETHOD.
    
      METHOD load.
    
        DATA: lv_name TYPE fpname.
    
        lv_name = ms_item-obj_name.
    
        TRY.
            ri_wb_form = cl_fp_wb_form=>load( lv_name ).
          CATCH cx_fp_api.
            zcx_abapgit_exception=>raise( 'SFPF error, load' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE lastuser FROM fplayout
          INTO rv_user
          WHERE name = ms_item-obj_name
          AND state = 'A'.
        IF rv_user IS INITIAL.
          SELECT SINGLE firstuser FROM fplayout
            INTO rv_user
            WHERE name = ms_item-obj_name
            AND state = 'A'.
        ENDIF.
        IF rv_user IS INITIAL.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_name TYPE fpname.
    
        lv_name = ms_item-obj_name.
    
        TRY.
            TRY.
                CALL METHOD cl_fp_wb_form=>('DELETE')
                  EXPORTING
                    i_name     = lv_name
                    i_ordernum = iv_transport
                    i_dark     = abap_true. " > 740
              CATCH cx_sy_dyn_call_error.
                cl_fp_wb_form=>delete(
                  i_name     = lv_name
                  i_ordernum = iv_transport ).
            ENDTRY.
          CATCH cx_fp_api.
            zcx_abapgit_exception=>raise( 'SFPI error, delete' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_xstr      TYPE xstring,
              lv_layout    TYPE xstring,
              lv_name      TYPE fpname,
              li_wb_object TYPE REF TO if_fp_wb_form,
              li_form      TYPE REF TO if_fp_form,
              lx_fp_err    TYPE REF TO cx_fp_api.
    
        lv_name = ms_item-obj_name.
        lv_xstr = cl_ixml_80_20=>render_to_xstring( io_xml->get_raw( ) ).
    
        TRY.
            li_form = cl_fp_helper=>convert_xstring_to_form( lv_xstr ).
    
            IF mo_files->contains_file( c_layout_file_ext ) = abap_true.
              lv_layout = mo_files->read_raw( c_layout_file_ext ).
              li_form->get_layout( )->set_layout_data( lv_layout ).
            ENDIF.
    
            IF zif_abapgit_object~exists( ) = abap_true.
              TRY.
                  CALL METHOD cl_fp_wb_form=>('DELETE')
                    EXPORTING
                      i_name     = lv_name
                      i_ordernum = iv_transport
                      i_dark     = abap_true. " > 740
                CATCH cx_sy_dyn_call_error.
                  cl_fp_wb_form=>delete(
                    i_name     = lv_name
                    i_ordernum = iv_transport ).
              ENDTRY.
            ENDIF.
    
            tadir_insert( iv_package ).
    
            TRY.
                CALL METHOD cl_fp_wb_form=>('CREATE')
                  EXPORTING
                    i_name     = lv_name
                    i_form     = li_form
                    i_ordernum = iv_transport
                    i_dark     = abap_true " > 740
                  RECEIVING
                    r_wb_form  = li_wb_object.
              CATCH cx_sy_dyn_call_error.
                li_wb_object = cl_fp_wb_form=>create(
                  i_name     = lv_name
                  i_form     = li_form
                  i_ordernum = iv_transport ).
            ENDTRY.
    
            li_wb_object->save( ).
            li_wb_object->free( ).
          CATCH cx_fp_api INTO lx_fp_err.
            zcx_abapgit_exception=>raise( |SFPF deserialization error: { lx_fp_err->get_text( ) }| ).
        ENDTRY.
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_name TYPE fpname.
    
        " Check for any state
        SELECT SINGLE name FROM fplayout
          INTO lv_name
          WHERE name = ms_item-obj_name.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lv_object TYPE seqg3-garg.
    
        lv_object = |{ ms_item-obj_name }|.
        OVERLAY lv_object WITH '                              '.
        lv_object = lv_object && '*'.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'EFPFORM'
                                                iv_argument    = lv_object ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_xstr            TYPE xstring,
              li_document        TYPE REF TO if_ixml_document,
              li_node_collection TYPE REF TO if_ixml_node_collection,
              li_node_iter       TYPE REF TO if_ixml_node_iterator,
              li_node            TYPE REF TO if_ixml_node,
              li_node_new        TYPE REF TO if_ixml_node,
              li_node_parent     TYPE REF TO if_ixml_node.
    
        lv_xstr = form_to_xstring( ).
        li_document = cl_ixml_80_20=>parse_to_document( stream_xstring = lv_xstr ).
    
    *   Clear CACHE_INFO
        li_node_collection = li_document->get_elements_by_tag_name_ns( 'CACHE_INFO' ).
        IF li_node_collection IS NOT INITIAL.
          li_node_iter = li_node_collection->create_iterator( ).
          DO.
            li_node = li_node_iter->get_next( ).
            IF li_node IS INITIAL.
              EXIT.
            ENDIF.
            li_node_new = li_document->create_element_ns( 'CACHE_INFO' ).
            li_node_parent = li_node->get_parent( ).
            li_node_parent->replace_child( new_child = li_node_new
                                           old_child = li_node ).
          ENDDO.
        ENDIF.
    
        fix_oref( li_document ).
        io_xml->set_raw( li_document->get_root_element( ) ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sfpi IMPLEMENTATION.
    
      METHOD interface_to_xstring.
    
        DATA: li_fp_interface TYPE REF TO if_fp_interface,
              li_wb_interface TYPE REF TO if_fp_wb_interface.
    
        TRY.
            li_wb_interface = load( ).
            li_fp_interface ?= li_wb_interface->get_object( ).
            rv_xstr = cl_fp_helper=>convert_interface_to_xstring( li_fp_interface ).
          CATCH cx_fp_api.
            zcx_abapgit_exception=>raise( 'SFPI error, interface_to_xstring' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD load.
    
        DATA: lv_name TYPE fpname.
    
        lv_name = ms_item-obj_name.
    
        TRY.
            ri_wb_interface = cl_fp_wb_interface=>load( lv_name ).
          CATCH cx_fp_api.
            zcx_abapgit_exception=>raise( 'SFPI error, load' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE lastuser FROM fpinterface
          INTO rv_user
          WHERE name = ms_item-obj_name
          AND state = 'A'.
        IF rv_user IS INITIAL.
          SELECT SINGLE firstuser FROM fpinterface
            INTO rv_user
            WHERE name = ms_item-obj_name
            AND state = 'A'.
        ENDIF.
        IF rv_user IS INITIAL.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_name         TYPE fpname,
              lo_wb_interface TYPE REF TO cl_fp_wb_interface.
    
        lo_wb_interface ?= load( ).
    
        lv_name = ms_item-obj_name.
    
        TRY.
            lo_wb_interface->delete( lv_name ).
          CATCH cx_fp_api.
            zcx_abapgit_exception=>raise( 'SFPI error, delete' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_xstr      TYPE xstring,
              lv_name      TYPE fpname,
              li_wb_object TYPE REF TO if_fp_wb_interface,
              li_interface TYPE REF TO if_fp_interface.
    
        lv_name = ms_item-obj_name.
        lv_xstr = cl_ixml_80_20=>render_to_xstring( io_xml->get_raw( ) ).
    
        IF zif_abapgit_object~exists( ) = abap_true.
          zif_abapgit_object~delete( iv_package   = iv_package
                                     iv_transport = iv_transport
                                     ii_log       = ii_log ).
        ENDIF.
    
        TRY.
            li_interface = cl_fp_helper=>convert_xstring_to_interface( lv_xstr ).
            tadir_insert( iv_package ).
            li_wb_object = cl_fp_wb_interface=>create( i_name      = lv_name
                                                       i_interface = li_interface ).
            li_wb_object->save( ).
            li_wb_object->free( ).
          CATCH cx_fp_api.
            zcx_abapgit_exception=>raise( 'SFPI error, deserialize' ).
        ENDTRY.
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_name TYPE fpinterface-name.
    
        SELECT SINGLE name FROM fpinterface
          INTO lv_name
          WHERE name = ms_item-obj_name.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lv_object TYPE seqg3-garg.
    
        lv_object = |{ ms_item-obj_name }|.
        OVERLAY lv_object WITH '                              '.
        lv_object = lv_object && '*'.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'EFPINTERFACE'
                                                iv_argument    = lv_object ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_xstr     TYPE xstring,
              li_document TYPE REF TO if_ixml_document.
    
        lv_xstr = interface_to_xstring( ).
        li_document = cl_ixml_80_20=>parse_to_document( stream_xstring = lv_xstr ).
        zcl_abapgit_object_sfpf=>fix_oref( li_document ).
        io_xml->set_raw( li_document->get_root_element( ) ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_OBJECT_SFSW IMPLEMENTATION.
    
      METHOD activate.
    
        DATA: lt_switches TYPE sfw_switchtab,
              lt_msgtab   TYPE sprot_u_tab.
    
        IF zif_abapgit_object~is_active( ) = abap_true.
          RETURN.
        ENDIF.
    
        APPEND mv_switch TO lt_switches.
    
        cl_sfw_activate=>activate_sfsw(
          EXPORTING
            p_switches = lt_switches
            p_version  = 'I'
          IMPORTING
            p_msgtab   = lt_msgtab ).
    
        READ TABLE lt_msgtab WITH KEY severity = 'E' TRANSPORTING NO FIELDS.
        IF sy-subrc = 0.
          zcx_abapgit_exception=>raise( 'Error activating SFBS' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_switch = is_item-obj_name.
    
      ENDMETHOD.
    
      METHOD create.
    
        TRY.
            " make sure to clear cache
            ro_switch = cl_sfw_sw=>create_switch( mv_switch ).
            ro_switch->free( ).
            ro_switch = cl_sfw_sw=>create_switch( mv_switch ).
          CATCH cx_pak_invalid_data cx_pak_invalid_state cx_pak_not_authorized.
            zcx_abapgit_exception=>raise( 'Error from CL_SFW_SW=>CREATE_SWITCH' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get.
    
        TRY.
            " make sure to clear cache
            ro_switch = cl_sfw_sw=>get_switch( mv_switch ).
            ro_switch->free( ).
            ro_switch = cl_sfw_sw=>get_switch( mv_switch ).
          CATCH cx_pak_invalid_data cx_pak_invalid_state cx_pak_not_authorized.
            zcx_abapgit_exception=>raise( 'Error from CL_SFW_SW=>GET_SWITCH' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD unlock.
    
        CALL FUNCTION 'DEQUEUE_EEUDB'
          EXPORTING
            relid     = 'SW'
            name      = ms_item-obj_name
            _synchron = 'X'
            _scope    = '1'
            mode_eudb = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: ls_data TYPE sfw_switch.
    
        ls_data = get( )->get_header_data( ).
    
        rv_user = ls_data-changedby.
        IF rv_user IS INITIAL.
          rv_user = ls_data-author.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lt_delete TYPE sfw_switchtab,
              lt_msgtab TYPE sprot_u_tab.
    
        APPEND mv_switch TO lt_delete.
    
        cl_sfw_activate=>delete_sfsw( EXPORTING p_switches = lt_delete
                                      IMPORTING p_msgtab = lt_msgtab ).
    
        READ TABLE lt_msgtab WITH KEY severity = 'E' TRANSPORTING NO FIELDS.
        IF sy-subrc = 0.
          zcx_abapgit_exception=>raise( 'Error deleting SFSW' ).
        ENDIF.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lo_switch    TYPE REF TO cl_sfw_sw,
              ls_header    TYPE sfw_switch,
              lv_name_32   TYPE sfw_name32,
              lv_name_80   TYPE sfw_name80,
              lt_parent_bf TYPE sfw_bf_sw_outtab,
              lt_conflicts TYPE sfw_confl_outtab,
              lt_packages  TYPE sfw_devcl_outtab.
    
        IF iv_step = zif_abapgit_object=>gc_step_id-late.
          activate( ).
          RETURN.
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'HEADER'
                      CHANGING cg_data = ls_header ).
        io_xml->read( EXPORTING iv_name = 'NAME32'
                      CHANGING cg_data = lv_name_32 ).
        io_xml->read( EXPORTING iv_name = 'NAME80'
                      CHANGING cg_data = lv_name_80 ).
    
        io_xml->read( EXPORTING iv_name = 'PARENT_BF'
                      CHANGING cg_data = lt_parent_bf ).
        io_xml->read( EXPORTING iv_name = 'CONFLICTS'
                      CHANGING cg_data = lt_conflicts ).
        io_xml->read( EXPORTING iv_name = 'PACKAGES'
                      CHANGING cg_data = lt_packages ).
    
        TRY.
            IF zif_abapgit_object~exists( ) = abap_true.
              lo_switch = get( ).
            ELSE.
              lo_switch = create( ).
            ENDIF.
          CATCH cx_pak_not_authorized cx_pak_invalid_state cx_pak_invalid_data.
            zcx_abapgit_exception=>raise( 'error in CL_SFW_SW=>CREATE_SWITCH' ).
        ENDTRY.
    
        ls_header-author = sy-uname.
        ls_header-createdon = sy-datum.
        lo_switch->set_header_data( ls_header ).
    
        lo_switch->set_texts( p_32 = lv_name_32
                              p_80 = lv_name_80 ).
    
        lo_switch->set_parent_bf( lt_parent_bf ).
        lo_switch->set_conflicts( lt_conflicts ).
        lo_switch->set_assigned_packages( lt_packages ).
    
        set_default_package( iv_package ).
        tadir_insert( iv_package ).
    
        lo_switch->save_all(
          EXCEPTIONS
            not_saved = 1
            OTHERS    = 2 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error in CL_SFW_SW->SAVE_ALL' ).
        ENDIF.
    
        unlock( ).
    
        deserialize_longtexts( ii_xml         = io_xml
                               iv_longtext_id = c_longtext_id_sfsw ).
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA ls_tadir TYPE tadir.
    
        IF cl_sfw_sw=>check_existence( mv_switch ) = abap_false.
          RETURN.
        ENDIF.
    
        SELECT SINGLE * FROM tadir INTO ls_tadir
          WHERE pgmid = 'R3TR'
          AND object = ms_item-obj_type
          AND obj_name = ms_item-obj_name.
        IF ls_tadir IS INITIAL.
          RETURN.
        ENDIF.
    
        rv_bool = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'EEUDB'
                                                iv_argument    = ms_item-obj_name
                                                iv_prefix      = 'SW' ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lo_switch    TYPE REF TO cl_sfw_sw,
              ls_header    TYPE sfw_switch,
              lv_name_32   TYPE sfw_name32,
              lv_name_80   TYPE sfw_name80,
              lt_parent_bf TYPE sfw_bf_sw_outtab,
              lt_conflicts TYPE sfw_confl_outtab,
              lt_packages  TYPE sfw_devcl_outtab.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        lo_switch = get( ).
    
        ls_header = lo_switch->get_header_data( ).
        CLEAR: ls_header-author,
               ls_header-version,
               ls_header-createdon,
               ls_header-changedby,
               ls_header-changedon,
               ls_header-timestamp.
    
        lo_switch->get_texts(
          IMPORTING
            p_32 = lv_name_32
            p_80 = lv_name_80 ).
    
        lt_parent_bf = lo_switch->get_parent_bf( ).
        lt_conflicts = lo_switch->get_conflicts( ).
        lt_packages  = lo_switch->get_assigned_packages( ).
    
        io_xml->add( ig_data = ls_header
                     iv_name = 'HEADER' ).
        io_xml->add( ig_data = lv_name_32
                     iv_name = 'NAME32' ).
        io_xml->add( ig_data = lv_name_80
                     iv_name = 'NAME80' ).
    
        io_xml->add( ig_data = lt_parent_bf
                     iv_name = 'PARENT_BF' ).
        io_xml->add( ig_data = lt_conflicts
                     iv_name = 'CONFLICTS' ).
        io_xml->add( ig_data = lt_packages
                     iv_name = 'PACKAGES' ).
    
        serialize_longtexts( ii_xml         = io_xml
                             iv_longtext_id = c_longtext_id_sfsw ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_OBJECT_SHI3 IMPLEMENTATION.
    
      METHOD clear_fields.
    
        FIELD-SYMBOLS  LIKE LINE OF ct_nodes.
    
        CLEAR: cs_head-luser, cs_head-ldate, cs_head-ltime.
        CLEAR: cs_head-fuser, cs_head-fdate, cs_head-ftime.
        CLEAR: cs_head-frelease, cs_head-lrelease.
        CLEAR: cs_head-responsibl.
    
        LOOP AT ct_nodes ASSIGNING .
          CLEAR: -luser, -ldate, -ltime.
          CLEAR: -fuser, -fdate, -ftime.
          CLEAR: -frelease, -lrelease.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_tree_id = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD delete_tree_structure.
        CALL FUNCTION 'STREE_EXTERNAL_DELETE'
          EXPORTING
            structure_id          = iv_structure_id
            no_confirmation_popup = abap_true.
      ENDMETHOD.
    
      METHOD has_authorization.
    
        AUTHORITY-CHECK OBJECT 'S_DEVELOP'
          ID 'DEVCLASS'  FIELD iv_devclass
          ID 'OBJTYPE'   FIELD 'MENU'
          ID 'OBJNAME'   FIELD iv_structure_id
          ID 'P_GROUP'   DUMMY
          ID 'ACTVT'     FIELD iv_activity.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( iv_msgid = 'S#'
                                             iv_msgno = '203' ).
        ENDIF.
      ENDMETHOD.
    
      METHOD insert_transport.
    
        DATA:
          ls_msg     TYPE hier_mess,
          ls_object  TYPE e071,
          lt_objects TYPE TABLE OF e071,
          lt_keys    TYPE TABLE OF e071k,
          ls_ko200   TYPE ko200,
          lt_ko200   TYPE TABLE OF ko200.
    
        " This function shows a popup so get objects and keys and insert
        " them into transport below
        CALL FUNCTION 'STREE_INSERT_ALL_IN_TRANSPORT'
          EXPORTING
            structure_id               = mv_tree_id
            iv_return_objects_and_keys = abap_true
          IMPORTING
            message                    = ls_msg
          TABLES
            et_objects                 = lt_objects
            et_keys                    = lt_keys.
        IF ls_msg-msgty = 'E'.
          MESSAGE ID ls_msg-msgid TYPE ls_msg-msgty NUMBER ls_msg-msgno
            WITH ls_msg-msgv1 ls_msg-msgv2 ls_msg-msgv3 ls_msg-msgv4 INTO zcx_abapgit_exception=>null.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        LOOP AT lt_objects INTO ls_object.
          MOVE-CORRESPONDING ls_object TO ls_ko200.
          INSERT ls_ko200 INTO TABLE lt_ko200.
        ENDLOOP.
    
        CALL FUNCTION 'TR_RECORD_OBJ_CHANGE_TO_REQ'
          EXPORTING
            iv_request = iv_transport
            it_objects = lt_ko200
            it_keys    = lt_keys
          EXCEPTIONS
            cancel     = 1
            OTHERS     = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD is_used.
    
        DATA: lt_used_in_structures TYPE STANDARD TABLE OF ttree WITH DEFAULT KEY.
    
        CALL FUNCTION 'STREE_GET_STRUCTURE_USAGE'
          EXPORTING
            structure_id       = iv_structure_id
          TABLES
            used_in_structures = lt_used_in_structures.
    
        IF lt_used_in_structures IS NOT INITIAL.
          zcx_abapgit_exception=>raise( |IMG structure ID { iv_structure_id } is still used| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD jump_sbach04.
        DATA: ls_message      TYPE hier_mess,
              lv_structure_id TYPE hier_treeg.
    
        lv_structure_id = ms_item-obj_name.
    
        CALL FUNCTION 'STREE_EXTERNAL_EDIT'
          EXPORTING
            structure_id   = lv_structure_id
            language       = mv_language
            edit_structure = abap_false
            no_commit_work = abap_false
            activity       = 'D'
          IMPORTING
            message        = ls_message.
        IF ls_message IS NOT INITIAL.
          zcx_abapgit_exception=>raise_t100(
            iv_msgid = ls_message-msgid
            iv_msgno = ls_message-msgno
            iv_msgv1 = ls_message-msgv1
            iv_msgv2 = ls_message-msgv2
            iv_msgv3 = ls_message-msgv3
            iv_msgv4 = ls_message-msgv4 ).
        ENDIF.
      ENDMETHOD.
    
      METHOD jump_se43.
    
        DATA: lt_bdcdata TYPE TABLE OF bdcdata.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_bdcdata.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -program  = 'SAPLBMEN'.
        -dynpro   = '0200'.
        -dynbegin = abap_true.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'BDC_OKCODE'.
        -fval = '=SHOW'.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'BMENUNAME-ID'.
        -fval = ms_item-obj_name.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'SE43'
          it_bdcdata = lt_bdcdata ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: ls_head TYPE ttree.
    
        CALL FUNCTION 'STREE_STRUCTURE_READ'
          EXPORTING
            structure_id     = mv_tree_id
          IMPORTING
            structure_header = ls_head.
    
        rv_user = ls_head-luser.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        CONSTANTS lc_activity_delete_06 TYPE activ_auth VALUE '06'.
    
        TRY.
            IF zif_abapgit_object~exists( ) = abap_false.
              RETURN.
            ENDIF.
          CATCH zcx_abapgit_exception.
            RETURN.
        ENDTRY.
    
        has_authorization( iv_structure_id = mv_tree_id
                           iv_devclass     = ms_item-devclass
                           iv_activity     = lc_activity_delete_06 ).
    
        is_used( mv_tree_id ).
    
        delete_tree_structure( mv_tree_id ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_msg    TYPE hier_mess,
              ls_head   TYPE ttree,
              ls_ttree  TYPE ttree,
              lt_titles TYPE TABLE OF ttreet,
              lt_nodes  TYPE TABLE OF hier_iface,
              lt_texts  TYPE TABLE OF hier_texts,
              lt_refs   TYPE TABLE OF hier_ref.
    
        io_xml->read( EXPORTING iv_name = 'TREE_HEAD'
                      CHANGING  cg_data = ls_head ).
        io_xml->read( EXPORTING iv_name = 'TREE_TITLES'
                      CHANGING  cg_data = lt_titles ).
        io_xml->read( EXPORTING iv_name = 'TREE_NODES'
                      CHANGING  cg_data = lt_nodes ).
        io_xml->read( EXPORTING iv_name = 'TREE_REFS'
                      CHANGING  cg_data = lt_refs ).
        io_xml->read( EXPORTING iv_name = 'TREE_TEXTS'
                      CHANGING  cg_data = lt_texts ).
    
        mo_i18n_params->trim_saplang_keyed_table(
          EXPORTING
            iv_lang_field_name = 'SPRAS'
            iv_keep_master_lang = abap_true
          CHANGING
            ct_tab = lt_titles ).
        mo_i18n_params->trim_saplang_keyed_table(
          EXPORTING
            iv_lang_field_name = 'SPRAS'
            iv_keep_master_lang = abap_true
          CHANGING
            ct_tab = lt_texts ).
    
        IF zif_abapgit_object~exists( ) = abap_true.
          delete_tree_structure( mv_tree_id ).
        ENDIF.
    
        CALL FUNCTION 'STREE_HIERARCHY_SAVE'
          EXPORTING
            structure_id             = mv_tree_id
            structure_type           = ls_head-type
            structure_description    = space
            structure_masterlanguage = mv_language
            structure_responsible    = sy-uname
            structure_buffermode     = ls_head-buffermode
            development_class        = iv_package
          IMPORTING
            message                  = ls_msg
          TABLES
            list_of_nodes            = lt_nodes
            list_of_references       = lt_refs
            list_of_texts            = lt_texts
            structure_descriptions   = lt_titles
          EXCEPTIONS
            no_nodes_given           = 1
            OTHERS                   = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ELSEIF ls_msg-msgty = 'E'.
          MESSAGE ID ls_msg-msgid TYPE ls_msg-msgty NUMBER ls_msg-msgno
            WITH ls_msg-msgv1 ls_msg-msgv2 ls_msg-msgv3 ls_msg-msgv4 INTO zcx_abapgit_exception=>null.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        " Set buffer mode for menus (see function BMENU_CREATE_TREE)
        SELECT SINGLE * FROM ttree INTO ls_ttree
          WHERE type = 'BMENU' AND id = mv_tree_id.
        IF sy-subrc = 0.
          ls_ttree-buffermode = ls_head-buffermode.
          ls_ttree-buffervar  = ls_head-buffervar.
          MODIFY ttree FROM ls_ttree.
        ENDIF.
    
        IF zcl_abapgit_factory=>get_sap_package( iv_package )->are_changes_recorded_in_tr_req( ) = abap_true.
          " Add necessary SHI6, SHI7, and TABU entries to transport (SAP Note 455542)
          insert_transport( iv_transport ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: ls_msg    TYPE hier_mess,
              ls_header TYPE ttree,
              ls_tadir  TYPE tadir.
    
        " Ignore buffer and get state from DB
        CALL FUNCTION 'STREE_STRUCTURE_EXIST'
          EXPORTING
            structure_id         = mv_tree_id
            read_from_database   = abap_true
            do_not_read_devclass = abap_false
          IMPORTING
            message              = ls_msg
            structure_header     = ls_header
            structure_tadir      = ls_tadir.
    
        rv_bool = boolc( ls_header-id IS NOT INITIAL ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: ls_head TYPE ttree.
    
        CALL FUNCTION 'STREE_STRUCTURE_READ'
          EXPORTING
            structure_id     = mv_tree_id
          IMPORTING
            structure_header = ls_head.
    
        CASE ls_head-type.
          WHEN 'BMENU'.
            jump_se43( ).
            rv_exit = abap_true.
          WHEN 'GHIER'.
            jump_sbach04( ).
            rv_exit = abap_true.
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_msg           TYPE hier_mess,
              ls_head          TYPE ttree,
              lt_titles        TYPE TABLE OF ttreet,
              lt_nodes         TYPE TABLE OF hier_iface,
              lt_texts         TYPE TABLE OF hier_texts,
              lt_refs          TYPE TABLE OF hier_ref,
              lv_all_languages TYPE abap_bool.
    
        CALL FUNCTION 'STREE_STRUCTURE_READ'
          EXPORTING
            structure_id     = mv_tree_id
          IMPORTING
            message          = ls_msg
            structure_header = ls_head
          TABLES
            description      = lt_titles.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true OR mo_i18n_params->is_lxe_applicable( ) = abap_true.
          lv_all_languages = abap_false.
          DELETE lt_titles WHERE spras <> mv_language.
        ELSE.
          lv_all_languages = abap_true.
          mo_i18n_params->trim_saplang_keyed_table(
            EXPORTING
                iv_lang_field_name = 'SPRAS'
                iv_keep_master_lang = abap_true
              CHANGING
                ct_tab = lt_titles ).
        ENDIF.
    
        CALL FUNCTION 'STREE_HIERARCHY_READ'
          EXPORTING
            structure_id       = mv_tree_id
            read_also_texts    = abap_true
            all_languages      = lv_all_languages
            language           = mv_language
          IMPORTING
            message            = ls_msg
          TABLES
            list_of_nodes      = lt_nodes
            list_of_references = lt_refs
            list_of_texts      = lt_texts.
    
        clear_fields( CHANGING cs_head  = ls_head
                               ct_nodes = lt_nodes ).
    
        SORT lt_titles BY id.
        DELETE ADJACENT DUPLICATES FROM lt_titles COMPARING spras id.
    
        SORT lt_texts BY spras node_id.
        DELETE ADJACENT DUPLICATES FROM lt_texts COMPARING spras node_id.
    
        mo_i18n_params->trim_saplang_keyed_table(
          EXPORTING
            iv_lang_field_name = 'SPRAS'
            iv_keep_master_lang = abap_true
          CHANGING
            ct_tab = lt_texts ).
    
        io_xml->add( iv_name = 'TREE_HEAD'
                     ig_data = ls_head ).
        io_xml->add( iv_name = 'TREE_TITLES'
                     ig_data = lt_titles ).
        io_xml->add( iv_name = 'TREE_NODES'
                     ig_data = lt_nodes ).
        io_xml->add( iv_name = 'TREE_REFS'
                     ig_data = lt_refs ).
        io_xml->add( iv_name = 'TREE_TEXTS'
                     ig_data = lt_texts ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_shi5 IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_extension = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        rv_user = c_user_unknown. " not stored by SAP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA:
          ls_msg              TYPE hier_mess,
          lv_found_users      TYPE hier_yesno,
          ls_check_extensions TYPE treenamesp,
          lt_check_extensions TYPE TABLE OF treenamesp,
          lv_obj_name         TYPE ko200-obj_name.
    
        " STREE_EXTENSION_DELETE shows a popup so do the same here
    
        ls_check_extensions-extension = mv_extension.
        INSERT ls_check_extensions INTO TABLE lt_check_extensions.
    
        CALL FUNCTION 'STREE_CHECK_EXTENSION'
          IMPORTING
            message         = ls_msg
          TABLES
            check_extension = lt_check_extensions.
    
        READ TABLE lt_check_extensions INTO ls_check_extensions INDEX 1.
        IF ls_check_extensions-original = abap_false.
          zcx_abapgit_exception=>raise( 'Delete enhancement ID in your source system' ).
        ENDIF.
    
        lv_obj_name = mv_extension.
    
        CALL FUNCTION 'STREE_TRANSPORT_CHECK'
          EXPORTING
            object   = 'SHI5'
            obj_name = lv_obj_name
          IMPORTING
            message  = ls_msg.
    
        IF ls_msg-msgty = 'E'.
          MESSAGE ID ls_msg-msgid TYPE ls_msg-msgty NUMBER ls_msg-msgno
            WITH ls_msg-msgv1 ls_msg-msgv2 ls_msg-msgv3 ls_msg-msgv4 INTO zcx_abapgit_exception=>null.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CALL FUNCTION 'STREE_EXTENSION_USAGE'
          EXPORTING
            extension         = mv_extension
            no_display        = abap_true
          IMPORTING
            message           = ls_msg
            extension_is_used = lv_found_users.
    
        IF ls_msg-msgty = 'E'.
          MESSAGE ID ls_msg-msgid TYPE ls_msg-msgty NUMBER ls_msg-msgno
            WITH ls_msg-msgv1 ls_msg-msgv2 ls_msg-msgv3 ls_msg-msgv4 INTO zcx_abapgit_exception=>null.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        IF lv_found_users = abap_true.
          zcx_abapgit_exception=>raise( 'Enhancement ID is still used' ).
        ENDIF.
    
        CALL FUNCTION 'STREE_TRANSPORT_INSERT'
          EXPORTING
            object   = 'SHI5'
            obj_name = lv_obj_name
          IMPORTING
            message  = ls_msg.
    
        IF ls_msg-msgty = 'E'.
          MESSAGE ID ls_msg-msgid TYPE ls_msg-msgty NUMBER ls_msg-msgno
            WITH ls_msg-msgv1 ls_msg-msgv2 ls_msg-msgv3 ls_msg-msgv4 INTO zcx_abapgit_exception=>null.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        DELETE FROM ttree_ext WHERE extension = mv_extension.
        DELETE FROM ttree_extt WHERE extension = mv_extension.
    
        IF ls_check_extensions-transport = abap_false.
          " no transportable Devclass -> delete TADIR
          tadir_delete( ).
        ENDIF.
    
        " reset some internal tables
        CALL FUNCTION 'STREE_RESET_FUGR_SHI5_TABLES'.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        " We cannot use STREE_EXTENSION_NAME_CREATE
        " the create logic is directly tied to the UI
        "
        " Do it like here LSHI20F01 -> SAVE_DATA
    
        DATA: ls_extension TYPE ty_extension.
    
        io_xml->read(
          EXPORTING
            iv_name = 'SHI5'
          CHANGING
            cg_data = ls_extension ).
    
        INSERT ttree_ext  FROM ls_extension-header.
    
        DELETE FROM ttrees WHERE extension = ls_extension-header-extension.
        MODIFY ttrees FROM TABLE ls_extension-sequences.
    
        DELETE FROM ttree_extt WHERE extension = ls_extension-header-extension.
        MODIFY ttree_extt FROM TABLE ls_extension-texts.
    
        corr_insert( iv_package ).
    
        tadir_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: ls_extension_header TYPE ttree_ext.
    
        CALL FUNCTION 'STREE_EXTENSION_EXISTS'
          EXPORTING
            extension        = mv_extension
          IMPORTING
            extension_header = ls_extension_header.
    
        rv_bool = boolc( ls_extension_header IS NOT INITIAL ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        DATA: lt_extension TYPE STANDARD TABLE OF ttree_ext.
        FIELD-SYMBOLS:  LIKE LINE OF lt_extension.
    
        INSERT INITIAL LINE INTO TABLE lt_extension ASSIGNING .
        -extension = mv_extension.
    
        CALL FUNCTION 'STREE_EXTENSION_NAME_F4'
          EXPORTING
            originals_only       = abap_true
          TABLES
            show_only_extensions = lt_extension.
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_extension TYPE ty_extension.
    
        CALL FUNCTION 'STREE_EXTENSION_EXISTS'
          EXPORTING
            extension        = mv_extension
          IMPORTING
            extension_header = ls_extension-header.
    
        SELECT * FROM ttree_extt
                 INTO TABLE ls_extension-texts
                 WHERE extension = mv_extension ORDER BY PRIMARY KEY.
    
        SELECT * FROM ttrees
                INTO TABLE ls_extension-sequences
                WHERE extension = mv_extension ORDER BY PRIMARY KEY.
    
        io_xml->add( iv_name = 'SHI5'
                     ig_data = ls_extension ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_shi8 IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_assignment_id = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        rv_user = c_user_unknown. " not stored by SAP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_deleted TYPE abap_bool,
              ls_message TYPE hier_mess.
    
        CALL FUNCTION 'STREE_SFW_ASSIGNMENT_DELETE'
          EXPORTING
            assignment_id = mv_assignment_id
          IMPORTING
            id_deleted    = lv_deleted
            message       = ls_message.
    
        IF lv_deleted = abap_false.
          zcx_abapgit_exception=>raise( |{ ls_message-msgtxt }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_assignment_data TYPE ttree_sfw_nodes,
              ls_node_data       TYPE hier_iface,
              lv_saved           TYPE abap_bool,
              ls_message         TYPE hier_mess.
    
        io_xml->read(
          EXPORTING
            iv_name = 'SHI8'
          CHANGING
            cg_data = ls_assignment_data ).
    
        ls_node_data-tree_id = ls_assignment_data-tree_id.
        ls_node_data-node_id = ls_assignment_data-node_id.
    
        tadir_insert( iv_package ).
    
        CALL FUNCTION 'STREE_SFW_ASSIGNMENT_SAVE'
          EXPORTING
            assignment_id = ls_assignment_data-sfw_ass_id
            switch_id     = ls_assignment_data-switch_id
            reaction      = ls_assignment_data-reaction
            node_data     = ls_node_data
          IMPORTING
            data_saved    = lv_saved
            message       = ls_message.
    
        IF lv_saved = abap_false.
          zcx_abapgit_exception=>raise( |{ ls_message-msgtxt }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        CALL FUNCTION 'STREE_SFW_ASSIGNMENT_ID_EXISTS'
          EXPORTING
            assignment_id = mv_assignment_id
          IMPORTING
            exists        = rv_bool.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lt_assignments     TYPE STANDARD TABLE OF hier_sfw_assignment_id,
              ls_assignment      LIKE LINE OF lt_assignments,
              lt_assignment_data TYPE STANDARD TABLE OF ttree_sfw_nodes,
              ls_assignment_data LIKE LINE OF lt_assignment_data.
    
        ls_assignment-sfw_ass_id = mv_assignment_id.
        INSERT ls_assignment INTO TABLE lt_assignments.
    
        CALL FUNCTION 'STREE_SFW_ASSIGNMENT_READ'
          TABLES
            it_assignments     = lt_assignments
            et_assignment_data = lt_assignment_data.
    
        READ TABLE lt_assignment_data INTO ls_assignment_data
                                      INDEX 1.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error serializing { ms_item-obj_type } { ms_item-obj_name  }| ).
        ENDIF.
    
        io_xml->add( iv_name = 'SHI8'
                     ig_data = ls_assignment_data ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_shlp IMPLEMENTATION.
    
      METHOD adjust_exit.
    
        CONSTANTS lc_standard_exit TYPE dd30v-selmexit VALUE 'RS_DD_SELMEXIT'.
    
        IF cv_exit IS NOT INITIAL
        AND zcl_abapgit_factory=>get_function_module( )->function_exists( cv_exit ) = abap_false.
          " If exit function does not exist, replace it with standard SAP function
          " which exists in 7.02 and higher
          cv_exit = lc_standard_exit.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD check_exit.
    
        DATA lv_exit TYPE dd30v-selmexit.
    
        rv_done = abap_true.
    
        IF iv_exit IS NOT INITIAL.
          " Check if exit function is set correctly
          SELECT SINGLE selmexit FROM dd30v INTO lv_exit WHERE shlpname = ms_item-obj_name.
          IF sy-subrc = 0 AND lv_exit <> iv_exit.
            rv_done = abap_false.
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD handle_dependencies.
    
        " For search helps with dependency on exit function, we use two phases:
        " 1) DDIC phase:
        "    - If function does not exit, replace it with a standard SAP function
        " 2) LATE phase
        "    - If function was replaced, change it to the correct exit function
        CASE iv_step.
          WHEN zif_abapgit_object=>gc_step_id-ddic.
            adjust_exit( CHANGING cv_exit = cv_exit ).
    
          WHEN zif_abapgit_object=>gc_step_id-late.
            cv_done = check_exit( cv_exit ).
    
          WHEN zif_abapgit_object=>gc_step_id-lxe.
            cv_done = abap_true.
    
          WHEN OTHERS.
            ASSERT 0 = 1.
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE as4user FROM dd30l INTO rv_user
          WHERE shlpname = ms_item-obj_name
          AND as4local = 'A'.                               "#EC CI_GENBUFF
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        delete_ddic( 'H' ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_name  TYPE ddobjname,
              lv_done  TYPE abap_bool,
              ls_dd30v TYPE dd30v,
              lt_dd31v TYPE TABLE OF dd31v,
              lt_dd32p TYPE TABLE OF dd32p,
              lt_dd33v TYPE TABLE OF dd33v.
    
        io_xml->read( EXPORTING iv_name = 'DD30V'
                      CHANGING cg_data = ls_dd30v ).
    
        handle_dependencies(
          EXPORTING
            iv_step = iv_step
          CHANGING
            cv_exit = ls_dd30v-selmexit
            cv_done = lv_done ).
    
        IF lv_done = abap_true.
          RETURN.
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'DD31V_TABLE'
                      CHANGING cg_data = lt_dd31v ).
        io_xml->read( EXPORTING iv_name = 'DD32P_TABLE'
                      CHANGING cg_data = lt_dd32p ).
        io_xml->read( EXPORTING iv_name = 'DD33V_TABLE'
                      CHANGING cg_data = lt_dd33v ).
    
        corr_insert( iv_package = iv_package
                     ig_object_class = 'DICT' ).
    
        lv_name = ms_item-obj_name.
    
        CALL FUNCTION 'DDIF_SHLP_PUT'
          EXPORTING
            name              = lv_name
            dd30v_wa          = ls_dd30v
          TABLES
            dd31v_tab         = lt_dd31v
            dd32p_tab         = lt_dd32p
            dd33v_tab         = lt_dd33v
          EXCEPTIONS
            shlp_not_found    = 1
            name_inconsistent = 2
            shlp_inconsistent = 3
            put_failure       = 4
            put_refused       = 5
            OTHERS            = 6.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_shlpname TYPE dd30l-shlpname.
    
        SELECT SINGLE shlpname FROM dd30l INTO lv_shlpname
          WHERE shlpname = ms_item-obj_name.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by ZCL_ABAPGIT_OBJECT=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_name  TYPE ddobjname,
              lv_state TYPE ddgotstate,
              ls_dd30v TYPE dd30v,
              lt_dd31v TYPE TABLE OF dd31v,
              lt_dd32p TYPE TABLE OF dd32p,
              lt_dd33v TYPE TABLE OF dd33v.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_dd32p.
        FIELD-SYMBOLS  TYPE any.
    
        lv_name = ms_item-obj_name.
    
        CALL FUNCTION 'DDIF_SHLP_GET'
          EXPORTING
            name          = lv_name
            state         = 'A'
            langu         = mv_language
          IMPORTING
            gotstate      = lv_state
            dd30v_wa      = ls_dd30v
          TABLES
            dd31v_tab     = lt_dd31v
            dd32p_tab     = lt_dd32p
            dd33v_tab     = lt_dd33v
          EXCEPTIONS
            illegal_input = 1
            OTHERS        = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        IF ls_dd30v IS INITIAL OR lv_state <> 'A'.
          RETURN.
        ENDIF.
    
        CLEAR: ls_dd30v-as4user,
               ls_dd30v-as4date,
               ls_dd30v-as4time.
    
        ASSIGN COMPONENT 'ACTFLAG' OF STRUCTURE ls_dd30v TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
    
        LOOP AT lt_dd32p ASSIGNING .
    * clear information inherited from domain
          CLEAR: -domname,
            -headlen,
            -scrlen1,
            -scrlen2,
            -datatype,
            -leng,
            -outputlen,
            -decimals,
            -lowercase,
            -signflag,
            -convexit.
        ENDLOOP.
    
        io_xml->add( iv_name = 'DD30V'
                     ig_data = ls_dd30v ).
        io_xml->add( ig_data = lt_dd31v
                     iv_name = 'DD31V_TABLE' ).
        io_xml->add( ig_data = lt_dd32p
                     iv_name = 'DD32P_TABLE' ).
        io_xml->add( ig_data = lt_dd33v
                     iv_name = 'DD33V_TABLE' ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_shma IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE chg_user
          FROM shma_attributes
          INTO rv_user
          WHERE area_name = ms_item-obj_name.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        " We can't use FM SHMA_DELETE_AREA because it depends
        " on the corresponding class, but in abapGit it has its own
        " lifecycle. Therefore we have to reimplement most of the
        " FMs logic
    
        CONSTANTS: lc_request_delete TYPE i VALUE 4.
    
        DATA: lv_request   TYPE i,
              lv_area_name TYPE shm_area_name,
              lv_order     TYPE e070-trkorr,
              lv_task      TYPE e070-trkorr,
              lv_append    TYPE abap_bool,
              ls_tdevc     TYPE tdevc,
              lo_cts_if    TYPE REF TO object.
    
        lv_area_name = ms_item-obj_name.
    
        TRY.
            CALL FUNCTION 'ENQUEUE_E_SHM_AREA'
              EXPORTING
                mode_shma_attributes = 'E'
                area_name            = lv_area_name
                x_area_name          = ' '
                _scope               = '2'
                _wait                = ' '
                _collect             = ' '
              EXCEPTIONS
                foreign_lock         = 1
                system_failure       = 2
                OTHERS               = 3.
    
            IF sy-subrc <> 0.
              zcx_abapgit_exception=>raise_t100( ).
            ENDIF.
    
            CALL METHOD ('\PROGRAM=SAPMSHM_MONITOR\CLASS=LCL_SHMM')=>('FREE_AREA_BY_NAME')
              EXPORTING
                area_name     = lv_area_name
                affect_server = cl_shm_area=>affect_all_servers.
    
            CREATE OBJECT lo_cts_if TYPE ('\FUNCTION-POOL=SHMA\CLASS=LCL_CTS_INTERFACE')
              EXPORTING
                area = lv_area_name.
    
            CALL METHOD lo_cts_if->('CHECK_AREA')
              EXPORTING
                request     = lc_request_delete
              IMPORTING
                access_mode = lv_request
                appendable  = lv_append.
    
            IF lv_request <> lc_request_delete.
              zcx_abapgit_exception=>raise( |Error deleting SHMA { ms_item-obj_name }| ).
            ENDIF.
    
            CALL METHOD lo_cts_if->('INSERT_AREA')
              EXPORTING
                request = lc_request_delete
              IMPORTING
                order   = lv_order
                task    = lv_task.
    
            DELETE FROM shma_attributes  WHERE area_name = lv_area_name.
            DELETE FROM shma_start       WHERE area_name = lv_area_name.
    
            CALL FUNCTION 'TR_DEVCLASS_GET'
              EXPORTING
                iv_devclass = iv_package
              IMPORTING
                es_tdevc    = ls_tdevc
              EXCEPTIONS
                OTHERS      = 1.
    
            IF sy-subrc = 0 AND ls_tdevc-korrflag IS INITIAL.
    
              " TADIR entries for local objects must be deleted 'by hand'
              tadir_delete( ).
    
            ENDIF.
    
            CALL METHOD ('\PROGRAM=SAPLSHMA\CLASS=LCL_SHMA_HELPER')=>('DELETE_RUNTIME_SETTINGS')
              EXPORTING
                area_name = lv_area_name.
    
            CALL FUNCTION 'DEQUEUE_E_SHM_AREA'
              EXPORTING
                mode_shma_attributes = 'E'
                area_name            = lv_area_name
                x_area_name          = ' '
                _scope               = '3'
                _synchron            = ' '
                _collect             = ' '.
    
          CATCH cx_root.
            zcx_abapgit_exception=>raise( |Error deleting SHMA { ms_item-obj_name }| ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_area_name       TYPE shm_area_name,
              ls_area_attributes TYPE shma_attributes.
    
        lv_area_name = ms_item-obj_name.
    
        io_xml->read(
          EXPORTING
            iv_name = 'AREA_ATTRIBUTES'
          CHANGING
            cg_data = ls_area_attributes ).
    
        tadir_insert( iv_package ).
    
        TRY.
            CALL METHOD ('\PROGRAM=SAPLSHMA\CLASS=LCL_SHMA_HELPER')=>('INSERT_AREA')
              EXPORTING
                area_name           = lv_area_name
                attributes          = ls_area_attributes
                force_overwrite     = abap_true
                no_class_generation = abap_false
                silent_mode         = abap_true.
    
          CATCH cx_root.
            zcx_abapgit_exception=>raise( |Error serializing SHMA { ms_item-obj_name }| ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_area_name TYPE shm_area_name.
    
        SELECT SINGLE area_name
               FROM shma_attributes
               INTO lv_area_name
               WHERE area_name = ms_item-obj_name.
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
    
        rs_metadata = get_metadata( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: ls_bcdata TYPE bdcdata,
              lt_bcdata TYPE STANDARD TABLE OF bdcdata.
    
        ls_bcdata-program  = 'SAPLSHMA'.
        ls_bcdata-dynpro   = '0100'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam = 'SHMA_ATTRIBUTES-AREA_NAME'.
        ls_bcdata-fval = ms_item-obj_name.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam = 'BDC_OKCODE'.
        ls_bcdata-fval = '=SHOW'.
        APPEND ls_bcdata TO lt_bcdata.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'SHMA'
          it_bdcdata = lt_bcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_area_name       TYPE shm_area_name,
              ls_area_attributes TYPE shma_attributes.
    
        lv_area_name = ms_item-obj_name.
    
        TRY.
            CALL METHOD ('\PROGRAM=SAPLSHMA\CLASS=LCL_SHMA_HELPER')=>('READ_AREA_ATTRIBUTES_ALL')
              EXPORTING
                area_name       = lv_area_name
              IMPORTING
                area_attributes = ls_area_attributes.
    
            CLEAR: ls_area_attributes-chg_user,
                   ls_area_attributes-chg_date,
                   ls_area_attributes-chg_time,
                   ls_area_attributes-cls_gen_user,
                   ls_area_attributes-cls_gen_date,
                   ls_area_attributes-cls_gen_time.
    
            io_xml->add( iv_name = 'AREA_ATTRIBUTES'
                         ig_data = ls_area_attributes ).
    
          CATCH cx_root.
            zcx_abapgit_exception=>raise( |Error serializing SHMA { ms_item-obj_name }| ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_SOTS_HANDLER IMPLEMENTATION.
    
      METHOD create_sots.
    
        DATA:
          lt_sots     TYPE ty_sots_tt,
          lt_sots_use TYPE ty_sots_use_tt.
    
        io_xml->read( EXPORTING iv_name = 'SOTS'
                      CHANGING  cg_data = lt_sots ).
        io_xml->read( EXPORTING iv_name = 'SOTS_USE'
                      CHANGING  cg_data = lt_sots_use ).
    
        create_sots_from_data(
          iv_package  = iv_package
          it_sots     = lt_sots
          it_sots_use = lt_sots_use ).
    
      ENDMETHOD.
    
      METHOD create_sots_from_data.
    
        DATA:
          lt_objects         TYPE sotr_objects,
          lv_object          LIKE LINE OF lt_objects,
          lv_subrc           TYPE sy-subrc,
          ls_header          TYPE btfr_head,
          lt_text_tab        TYPE sotr_text_tt,
          lt_string_tab      TYPE sotr_textl_tt,
          ls_entry           LIKE LINE OF lt_string_tab,
          lv_concept         TYPE sotr_conc,
          lv_concept_default TYPE sotr_conc.
    
        FIELD-SYMBOLS  LIKE LINE OF it_sots.
    
        LOOP AT it_sots ASSIGNING .
    
          CALL FUNCTION 'SOTR_OBJECT_GET_OBJECTS'
            EXPORTING
              object_vector    = -header-objid_vec
            IMPORTING
              objects          = lt_objects
            EXCEPTIONS
              object_not_found = 1
              OTHERS           = 2.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          READ TABLE lt_objects INDEX 1 INTO lv_object.
          ASSERT sy-subrc = 0.
    
          " Reimplementation of SOTR_STRING_CREATE_CONCEPT because we can't supply
          " concept and it would then be generated.
    
          LOOP AT -entries INTO ls_entry.
            ls_entry-langu   = -header-crea_lan.
            ls_entry-concept = -header-concept.
            INSERT ls_entry INTO TABLE lt_string_tab.
          ENDLOOP.
    
          MOVE-CORRESPONDING -header TO ls_header.
          ls_header-paket = iv_package.
    
          lv_concept = -header-concept.
    
          PERFORM btfr_create IN PROGRAM saplsotr_db_string
            USING lv_object
                  sy-langu
                  abap_false
                  abap_true
            CHANGING lt_text_tab
                     lt_string_tab
                     ls_header
                     lv_concept
                     lv_concept_default
                     lv_subrc.
    
          CASE lv_subrc.
            WHEN 1.
              MESSAGE e100(sotr_mess) INTO zcx_abapgit_exception=>null.
            WHEN 2.
              MESSAGE e101(sotr_mess) INTO zcx_abapgit_exception=>null.
            WHEN 3.
              MESSAGE i305(sotr_mess) INTO zcx_abapgit_exception=>null.
            WHEN 4.
              " The concept will be created in the non-original system (not an error)
            WHEN 5.
              MESSAGE e504(sotr_mess) INTO zcx_abapgit_exception=>null.
            WHEN 6.
              MESSAGE e035(sotr_mess) INTO zcx_abapgit_exception=>null.
            WHEN 7.
              MESSAGE e170(sotr_mess) INTO zcx_abapgit_exception=>null.
            WHEN 9.
              MESSAGE e102(sotr_mess) INTO zcx_abapgit_exception=>null.
          ENDCASE.
    
          IF lv_subrc <> 0 AND lv_subrc <> 4.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
        ENDLOOP.
    
        CALL FUNCTION 'SOTR_USAGE_STRING_MODIFY'
          EXPORTING
            sotr_usage = it_sots_use.
    
      ENDMETHOD.
    
      METHOD delete_sots.
    
        DATA lt_sots_use TYPE ty_sots_use_tt.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_sots_use.
    
        lt_sots_use = get_sots_usage( iv_pgmid    = iv_pgmid
                                      iv_object   = iv_object
                                      iv_obj_name = iv_obj_name ).
    
        " Remove any usage to ensure deletion, see function module BTFR_CHECK
        DELETE sotr_useu FROM TABLE lt_sots_use ##SUBRC_OK.
    
        LOOP AT lt_sots_use ASSIGNING  WHERE concept IS NOT INITIAL.
    
          CALL FUNCTION 'BTFR_DELETE_SINGLE_TEXT'
            EXPORTING
              concept             = -concept
              flag_string         = abap_true
            EXCEPTIONS
              text_not_found      = 1 "ok
              invalid_package     = 3
              text_not_changeable = 4
              text_enqueued       = 5
              no_correction       = 6
              parameter_error     = 7
              OTHERS              = 8.
          IF sy-subrc > 2.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD get_sots_4_concept.
    
        DATA: ls_header  TYPE ty_sots-header,
              lt_entries TYPE ty_sots-entries.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_entries.
    
        CALL FUNCTION 'SOTR_STRING_GET_CONCEPT'
          EXPORTING
            concept        = iv_concept
          IMPORTING
            header         = ls_header
            entries        = lt_entries
          EXCEPTIONS
            no_entry_found = 1
            OTHERS         = 2.
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        CLEAR: ls_header-paket,
               ls_header-crea_name,
               ls_header-crea_tstut,
               ls_header-chan_name,
               ls_header-chan_tstut,
               ls_header-system_id.
    
        LOOP AT lt_entries ASSIGNING .
          CLEAR: -version,
                 -crea_name,
                 -crea_tstut,
                 -chan_name,
                 -chan_tstut.
        ENDLOOP.
    
        rs_sots-header  = ls_header.
        rs_sots-entries = lt_entries.
    
      ENDMETHOD.
    
      METHOD get_sots_usage.
    
        DATA: lv_obj_name TYPE trobj_name.
    
        lv_obj_name = iv_obj_name.
    
        " Objects with multiple components
        IF iv_pgmid = 'LIMU' AND ( iv_object CP 'WDY*' OR iv_object = 'WAPP' ).
          lv_obj_name+30 = '%'.
        ENDIF.
    
        CALL FUNCTION 'SOTR_USAGE_STRING_READ'
          EXPORTING
            pgmid          = iv_pgmid
            object         = iv_object
            obj_name       = lv_obj_name
          IMPORTING
            sotr_usage     = rt_sots_use
          EXCEPTIONS
            no_entry_found = 1
            error_in_pgmid = 2
            OTHERS         = 3.
        IF sy-subrc = 0.
          SORT rt_sots_use.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD read_sots.
    
        FIELD-SYMBOLS  LIKE LINE OF et_sots_use.
    
        DATA ls_sots TYPE ty_sots.
    
        " OTR long text (string) usage: see TABLE BTFR_OBJ_IDS
        " LIMU: CPUB, WAPP
        " R3TR: ENHO, ENHS, SICF, SMIF, WEBI, XSLT
    
        et_sots_use = get_sots_usage( iv_pgmid    = iv_pgmid
                                      iv_object   = iv_object
                                      iv_obj_name = iv_obj_name ).
    
        LOOP AT et_sots_use ASSIGNING  WHERE concept IS NOT INITIAL.
          ls_sots = get_sots_4_concept( -concept ).
    
          IF io_i18n_params->ms_params-main_language_only = abap_true.
            DELETE ls_sots-entries WHERE langu <> io_i18n_params->ms_params-main_language.
            CHECK ls_sots-entries IS NOT INITIAL.
          ENDIF.
    
          INSERT ls_sots INTO TABLE et_sots.
        ENDLOOP.
    
        IF io_xml IS BOUND.
          io_xml->add( iv_name = 'SOTS'
                       ig_data = et_sots ).
          io_xml->add( iv_name = 'SOTS_USE'
                       ig_data = et_sots_use ).
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sicf IMPLEMENTATION.
    
      METHOD change_sicf.
    
        DATA: lt_icfhndlist TYPE icfhndlist,
              lt_existing   TYPE TABLE OF icfhandler,
              ls_icfserdesc TYPE icfserdesc.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_existing.
    
        lt_icfhndlist = to_icfhndlist( it_icfhandler ).
    
        " Do not add handlers if they already exist, it will make the below
        " call to SAP standard code raise an exception
        SELECT * FROM icfhandler INTO TABLE lt_existing
          WHERE icf_name = is_icfservice-icf_name
          ORDER BY PRIMARY KEY.
        LOOP AT lt_existing ASSIGNING .
          DELETE TABLE lt_icfhndlist FROM -icfhandler.
        ENDLOOP.
    
        MOVE-CORRESPONDING is_icfservice TO ls_icfserdesc.
    
        cl_icf_tree=>if_icf_tree~change_node(
          EXPORTING
            icf_name                  = is_icfservice-orig_name
            icfaltnme                 = get_icfaltname( is_icfservice )
            icfparguid                = iv_parent
            icfdocu                   = is_icfdocu
            doculang                  = mv_language
            icfhandlst                = lt_icfhndlist
            package                   = iv_package
            application               = space
            icfserdesc                = ls_icfserdesc
            icfactive                 = abap_true
          EXCEPTIONS
            empty_icf_name            = 1
            no_new_virtual_host       = 2
            special_service_error     = 3
            parent_not_existing       = 4
            enqueue_error             = 5
            node_already_existing     = 6
            empty_docu                = 7
            doculang_not_installed    = 8
            security_info_error       = 9
            user_password_error       = 10
            password_encryption_error = 11
            invalid_url               = 12
            invalid_otr_concept       = 13
            formflg401_error          = 14
            handler_error             = 15
            transport_error           = 16
            tadir_error               = 17
            package_not_found         = 18
            wrong_application         = 19
            not_allow_application     = 20
            no_application            = 21
            invalid_icfparguid        = 22
            alt_name_invalid          = 23
            alternate_name_exist      = 24
            wrong_icf_name            = 25
            no_authority              = 26
            OTHERS                    = 27 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD deserialize_otr.
    
        DATA:
          lt_sots     TYPE zcl_abapgit_sots_handler=>ty_sots_tt,
          lt_sots_use TYPE zcl_abapgit_sots_handler=>ty_sots_use_tt.
    
        FIELD-SYMBOLS:
           LIKE LINE OF lt_sots_use.
    
        io_xml->read( EXPORTING iv_name = 'SOTS'
                      CHANGING  cg_data = lt_sots ).
        io_xml->read( EXPORTING iv_name = 'SOTS_USE'
                      CHANGING  cg_data = lt_sots_use ).
    
        LOOP AT lt_sots_use ASSIGNING .
          -obj_name = ms_item-obj_name.
        ENDLOOP.
    
        zcl_abapgit_sots_handler=>create_sots_from_data(
          iv_package  = iv_package
          it_sots     = lt_sots
          it_sots_use = lt_sots_use ).
    
      ENDMETHOD.
    
      METHOD find_parent.
    
        cl_icf_tree=>if_icf_tree~service_from_url(
          EXPORTING
            url                   = iv_url
            hostnumber            = 0
          IMPORTING
            icfnodguid            = rv_parent
          EXCEPTIONS
            wrong_application     = 1
            no_application        = 2
            not_allow_application = 3
            wrong_url             = 4
            no_authority          = 5
            OTHERS                = 6 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_hash_from_object.
    
        DATA:
          lv_icfnodguid TYPE icfservice-icfnodguid,
          lv_url        TYPE icfurlbuf,
          lv_ext_url    TYPE string.
    
        SELECT SINGLE icfnodguid FROM icfservice INTO lv_icfnodguid
          WHERE icf_name = iv_obj_name(15)
          AND icfparguid = iv_obj_name+15.
    
        IF sy-subrc = 0.
          CALL FUNCTION 'HTTP_GET_URL_FROM_NODGUID'
            EXPORTING
              nodguid      = lv_icfnodguid
            IMPORTING
              url          = lv_url
              extended_url = lv_ext_url
            EXCEPTIONS
              icf_inconst  = 1
              OTHERS       = 2.
          IF sy-subrc = 0.
            " It's possible that the URL contains the system id, for example for WD applications with names
            " longer than 15 characters. In that case, use the extended URL to generate the hash (#5064)
            IF lv_ext_url <> lv_url.
              rv_hash = zcl_abapgit_hash=>sha1_raw( zcl_abapgit_convert=>string_to_xstring_utf8( |{ lv_ext_url }| ) ).
            ELSE.
              rv_hash = zcl_abapgit_hash=>sha1_raw( zcl_abapgit_convert=>string_to_xstring_utf8( |{ lv_url }| ) ).
            ENDIF.
          ENDIF.
        ELSE.
          rv_hash = to_lower( iv_obj_name+15 ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_icfaltname.
    
        rv_icfaltnme = is_icfservice-icfaltnme.
        " If the original name is different (lower vs upper case), it needs to be deserialized
        IF is_icfservice-icfaltnme <> is_icfservice-icfaltnme_orig.
          rv_icfaltnme = is_icfservice-icfaltnme_orig.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD insert_sicf.
    
        DATA: lt_icfhndlist TYPE icfhndlist,
              ls_icfserdesc TYPE icfserdesc,
              ls_icfdocu    TYPE icfdocu,
              lv_icfnodguid TYPE icfnodguid,
              lv_parent     TYPE icfparguid.
    
        lt_icfhndlist = to_icfhndlist( it_icfhandler ).
        lv_parent = find_parent( iv_url ).
    
        " Nice, it seems that the structure should be mistreated
        ls_icfdocu = is_icfdocu-icf_docu.
    
        MOVE-CORRESPONDING is_icfservice TO ls_icfserdesc.
    
        cl_icf_tree=>if_icf_tree~insert_node(
          EXPORTING
            icf_name                  = is_icfservice-orig_name
            icfparguid                = lv_parent
            icfdocu                   = ls_icfdocu
            doculang                  = mv_language
            icfhandlst                = lt_icfhndlist
            package                   = iv_package
            application               = space
            icfserdesc                = ls_icfserdesc
            icfactive                 = abap_true
            icfaltnme                 = get_icfaltname( is_icfservice )
          IMPORTING
            icfnodguid                = lv_icfnodguid
          EXCEPTIONS
            empty_icf_name            = 1
            no_new_virtual_host       = 2
            special_service_error     = 3
            parent_not_existing       = 4
            enqueue_error             = 5
            node_already_existing     = 6
            empty_docu                = 7
            doculang_not_installed    = 8
            security_info_error       = 9
            user_password_error       = 10
            password_encryption_error = 11
            invalid_url               = 12
            invalid_otr_concept       = 13
            formflg401_error          = 14
            handler_error             = 15
            transport_error           = 16
            tadir_error               = 17
            package_not_found         = 18
            wrong_application         = 19
            not_allow_application     = 20
            no_application            = 21
            invalid_icfparguid        = 22
            alt_name_invalid          = 23
            alternate_name_exist      = 24
            wrong_icf_name            = 25
            no_authority              = 26
            OTHERS                    = 27 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        " Update item with name assigned by system
        SELECT SINGLE icfparguid INTO ms_item-obj_name+15 FROM icfservice
          WHERE icfnodguid = lv_icfnodguid.
    
      ENDMETHOD.
    
      METHOD read.
    
        DATA: lt_serv_info TYPE icfservtbl,
              ls_serv_info LIKE LINE OF lt_serv_info,
              ls_key       TYPE ty_sicf_key.
    
        FIELD-SYMBOLS:  LIKE LINE OF et_icfhandler.
    
        CLEAR es_icfservice.
        CLEAR es_icfdocu.
        CLEAR et_icfhandler.
        CLEAR ev_url.
    
        ls_key-icf_name   = ms_item-obj_name(15).
        ls_key-icfparguid = ms_item-obj_name+15.
    
        cl_icf_tree=>if_icf_tree~get_info_from_serv(
          EXPORTING
            icf_name          = ls_key-icf_name
            icfparguid        = ls_key-icfparguid
            icf_langu         = mv_language
          IMPORTING
            serv_info         = lt_serv_info
            icfdocu           = es_icfdocu
            url               = ev_url
          EXCEPTIONS
            wrong_name        = 1
            wrong_parguid     = 2
            incorrect_service = 3
            no_authority      = 4
            OTHERS            = 5 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        ASSERT lines( lt_serv_info ) = 1.
        READ TABLE lt_serv_info INDEX 1 INTO ls_serv_info.
        ASSERT sy-subrc = 0.
    
        MOVE-CORRESPONDING ls_serv_info-service TO es_icfservice.
        IF iv_clear = abap_true.
          CLEAR es_icfservice-icf_cuser.
          CLEAR es_icfservice-icf_cdate.
          CLEAR es_icfservice-icf_muser.
          CLEAR es_icfservice-icf_mdate.
        ENDIF.
    
        CLEAR es_icfdocu-icfparguid.
    
        APPEND LINES OF ls_serv_info-handlertbl TO et_icfhandler.
        LOOP AT et_icfhandler ASSIGNING .
          CLEAR -icfparguid.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD serialize_otr.
    
        DATA:
          lt_sots     TYPE zcl_abapgit_sots_handler=>ty_sots_tt,
          lt_sots_use TYPE zcl_abapgit_sots_handler=>ty_sots_use_tt.
    
        FIELD-SYMBOLS:
           LIKE LINE OF lt_sots_use.
    
        zcl_abapgit_sots_handler=>read_sots(
          EXPORTING
            iv_object      = ms_item-obj_type
            iv_obj_name    = ms_item-obj_name
            io_i18n_params = mo_i18n_params
          IMPORTING
            et_sots        = lt_sots
            et_sots_use    = lt_sots_use ).
    
        LOOP AT lt_sots_use ASSIGNING .
          CLEAR -obj_name.
        ENDLOOP.
    
        io_xml->add( iv_name = 'SOTS'
                     ig_data = lt_sots ).
        io_xml->add( iv_name = 'SOTS_USE'
                     ig_data = lt_sots_use ).
    
      ENDMETHOD.
    
      METHOD to_icfhndlist.
    
        FIELD-SYMBOLS:  LIKE LINE OF it_list.
    
        " Convert to sorted table
        LOOP AT it_list ASSIGNING .
          INSERT -icfhandler INTO TABLE rt_list.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: ls_icfservice TYPE icfservice.
    
        read( EXPORTING iv_clear      = abap_false
              IMPORTING es_icfservice = ls_icfservice ).
    
        rv_user = ls_icfservice-icf_muser.
    
        IF rv_user IS INITIAL.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA ls_icfservice TYPE icfservice.
    
        read( IMPORTING es_icfservice = ls_icfservice ).
    
        IF ls_icfservice IS INITIAL.
          " It seems that the ICF service doesn't exist anymore.
          " But that's ok, because some objects like SAPC manage
          " the lifecycle of its ICF service by itself and already
          " deleted the service.
          RETURN.
        ENDIF.
    
        IF ls_icfservice-icfparguid CO '0'.
          " not supported by the SAP standard API
          zcx_abapgit_exception=>raise( 'SICF - cannot delete root node, delete node manually' ).
        ENDIF.
    
        " OTR long texts
        zcl_abapgit_sots_handler=>delete_sots(
          iv_object   = ms_item-obj_type
          iv_obj_name = ms_item-obj_name ).
    
        " Delete Application Customizing Data the hard way, as it isn't done by the API.
        " If we wouldn't we would get errors from the API if entries exist.
        " Transaction SICF does the same.
        DELETE FROM icfapplcust
          WHERE icf_name = ls_icfservice-icf_name
          AND icfparguid = ls_icfservice-icfparguid.
    
        cl_icf_tree=>if_icf_tree~delete_node(
          EXPORTING
            icfparguid                  = ls_icfservice-icfparguid
          CHANGING
            icf_name                    = ls_icfservice-icf_name
          EXCEPTIONS
            no_virtual_host_delete      = 1
            special_service_error       = 2
            enqueue_error               = 3
            node_not_existing           = 4
            node_has_childs             = 5
            node_is_aliased             = 6
            node_not_in_original_system = 7
            transport_error             = 8
            tadir_error                 = 9
            db_error                    = 10
            no_authority                = 11
            OTHERS                      = 12 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_icfservice TYPE icfservice,
              ls_read       TYPE icfservice,
              ls_icfdocu    TYPE icfdocu,
              lv_url        TYPE string,
              lv_exists     TYPE abap_bool,
              lt_icfhandler TYPE TABLE OF icfhandler.
    
        io_xml->read( EXPORTING iv_name = 'URL'
                      CHANGING  cg_data = lv_url ).
        io_xml->read( EXPORTING iv_name = 'ICFSERVICE'
                      CHANGING  cg_data = ls_icfservice ).
        io_xml->read( EXPORTING iv_name = 'ICFDOCU'
                      CHANGING  cg_data = ls_icfdocu ).
        io_xml->read( EXPORTING iv_name = 'ICFHANDLER_TABLE'
                      CHANGING  cg_data = lt_icfhandler ).
    
        lv_exists = zif_abapgit_object~exists( ).
        IF lv_exists = abap_false.
          insert_sicf( is_icfservice = ls_icfservice
                       is_icfdocu    = ls_icfdocu
                       it_icfhandler = lt_icfhandler
                       iv_package    = iv_package
                       iv_url        = lv_url ).
        ELSE.
          read( IMPORTING es_icfservice = ls_read ).
          change_sicf( is_icfservice = ls_icfservice
                       is_icfdocu    = ls_icfdocu
                       it_icfhandler = lt_icfhandler
                       iv_package    = iv_package
                       iv_parent     = ls_read-icfparguid ).
        ENDIF.
    
        " OTR long texts
        deserialize_otr(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA ls_key TYPE ty_sicf_key.
    
        SELECT SINGLE icfaltnme FROM icfservice INTO ls_key-icf_name
          WHERE icf_name = ms_item-obj_name(15)
          AND icfparguid = ms_item-obj_name+15.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lv_argument TYPE seqg3-garg.
    
        lv_argument = ms_item-obj_name(15).
        lv_argument+15(1) = '*'.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESICFSER'
                                                iv_argument    = lv_argument ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: ls_bcdata TYPE bdcdata,
              lt_bcdata TYPE STANDARD TABLE OF bdcdata.
    
        ls_bcdata-program  = 'RSICFTREE'.
        ls_bcdata-dynpro   = '1000'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO lt_bcdata.
    
        ls_bcdata-dynpro   = space.
        ls_bcdata-dynbegin = space.
        ls_bcdata-fnam     = 'ICF_SERV'.
        ls_bcdata-fval     = ms_item-obj_name.
        APPEND ls_bcdata TO lt_bcdata.
    
        ls_bcdata-fnam = 'BDC_OKCODE'.
        ls_bcdata-fval = '=ONLI'.
        APPEND ls_bcdata TO lt_bcdata.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'SICF'
          it_bdcdata = lt_bcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
    
        DATA:
          lt_tadir    TYPE zif_abapgit_definitions=>ty_tadir_tt,
          lv_hash     TYPE ty_hash,
          lv_obj_name TYPE tadir-obj_name.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_tadir.
    
        lv_obj_name = to_upper( iv_item_part_of_filename(15) ) && '%'.
        lv_hash     = iv_item_part_of_filename+15(25).
    
        SELECT * FROM tadir INTO CORRESPONDING FIELDS OF TABLE lt_tadir
          WHERE pgmid = 'R3TR'
          AND object  = 'SICF'
          AND obj_name LIKE lv_obj_name
          ORDER BY PRIMARY KEY ##TOO_MANY_ITAB_FIELDS.      "#EC CI_GENBUFF
    
        LOOP AT lt_tadir ASSIGNING .
          IF get_hash_from_object( -obj_name ) = lv_hash.
            cs_item-obj_name = -obj_name.
            RETURN.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
    
        cv_item_part_of_filename = |{ cv_item_part_of_filename(15) }{ get_hash_from_object( is_item-obj_name ) }|.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_icfservice TYPE icfservice,
              ls_icfdocu    TYPE icfdocu,
              lv_url        TYPE string,
              lt_icfhandler TYPE TABLE OF icfhandler.
    
        read( IMPORTING es_icfservice = ls_icfservice
                        es_icfdocu    = ls_icfdocu
                        et_icfhandler = lt_icfhandler
                        ev_url        = lv_url ).
    
        IF ls_icfservice IS INITIAL.
          RETURN.
        ENDIF.
    
        CLEAR ls_icfservice-icf_mandt.
        CLEAR ls_icfservice-icfnodguid.
        CLEAR ls_icfservice-icfparguid.
        CLEAR ls_icfservice-icfchildno.
        CLEAR ls_icfservice-icfaliasno.
        CLEAR ls_icfservice-icf_user.
        CLEAR ls_icfservice-icf_cclnt.
        CLEAR ls_icfservice-icf_mclnt.
        " If the original name is different (lower vs upper case), it needs to be serialized
        IF ls_icfservice-icfaltnme = ls_icfservice-icfaltnme_orig.
          CLEAR ls_icfservice-icfaltnme_orig.
        ENDIF.
        CLEAR ls_icfservice-icfbitmap.
    
        " Clear settings that are only relevant for redirects
        IF ls_icfservice-kind401 <> 'X'.
          CLEAR: ls_icfservice-httpcde401, ls_icfservice-url401, ls_icfservice-formflg401.
        ENDIF.
        IF ls_icfservice-kind500 <> 'X'.
          CLEAR: ls_icfservice-httpcde500, ls_icfservice-url500.
        ENDIF.
        IF ls_icfservice-kindlpag <> 'X'.
          CLEAR: ls_icfservice-httpcdelpag, ls_icfservice-urllpag.
        ENDIF.
        IF ls_icfservice-kindnfpag <> 'X'.
          CLEAR: ls_icfservice-httpcdenfpag, ls_icfservice-urlnfpag.
        ENDIF.
    
        io_xml->add( iv_name = 'URL'
                     ig_data = lv_url ).
        io_xml->add( iv_name = 'ICFSERVICE'
                     ig_data = ls_icfservice ).
        io_xml->add( iv_name = 'ICFDOCU'
                     ig_data = ls_icfdocu ).
        io_xml->add( iv_name = 'ICFHANDLER_TABLE'
                     ig_data = lt_icfhandler ).
    
        " OTR long texts
        serialize_otr( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sktd IMPLEMENTATION.
    
      METHOD clear_field.
    
        FIELD-SYMBOLS  TYPE data.
    
        ASSIGN COMPONENT iv_fieldname OF STRUCTURE cs_data TO .
        ASSERT sy-subrc = 0.
    
        CLEAR .
    
      ENDMETHOD.
    
      METHOD clear_fields.
    
        clear_field(
          EXPORTING
            iv_fieldname = 'METADATA-NAME'
          CHANGING
            cs_data      = cs_data ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'METADATA-TYPE'
          CHANGING
            cs_data      = cs_data ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'METADATA-MASTER_SYSTEM'
          CHANGING
            cs_data      = cs_data ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'METADATA-VERSION'
          CHANGING
            cs_data      = cs_data ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'REF_OBJECT-URI'
          CHANGING
            cs_data      = cs_data ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'REF_OBJECT-DESCRIPTION'
          CHANGING
            cs_data      = cs_data ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'METADATA-CREATED_AT'
          CHANGING
            cs_data      = cs_data ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'METADATA-CREATED_BY'
          CHANGING
            cs_data      = cs_data ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'METADATA-CHANGED_AT'
          CHANGING
            cs_data      = cs_data ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'METADATA-CHANGED_BY'
          CHANGING
            cs_data      = cs_data ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'METADATA-MASTER_LANGUAGE'
          CHANGING
            cs_data      = cs_data ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'METADATA-RESPONSIBLE'
          CHANGING
            cs_data      = cs_data ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'METADATA-PACKAGE_REF'
          CHANGING
            cs_data      = cs_data ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'METADATA-LINKS'
          CHANGING
            cs_data      = cs_data ).
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_object_key = ms_item-obj_name.
    
        TRY.
            CREATE DATA mr_data TYPE ('CL_KTD_OBJECT_DATA=>TY_KTD_DATA').
            CREATE OBJECT mi_persistence TYPE ('CL_KTD_OBJECT_PERSIST').
    
          CATCH cx_sy_create_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get_wb_object_operator.
    
        DATA:
          ls_object_type TYPE wbobjtype,
          lx_error       TYPE REF TO cx_root.
    
        IF mi_wb_object_operator IS BOUND.
          ri_wb_object_operator = mi_wb_object_operator.
        ENDIF.
    
        ls_object_type-objtype_tr = 'SKTD'.
        ls_object_type-subtype_wb = 'TYP'.
    
        TRY.
            CALL METHOD ('CL_WB_OBJECT_OPERATOR')=>('CREATE_INSTANCE')
              EXPORTING
                object_type = ls_object_type
                object_key  = mv_object_key
              RECEIVING
                result      = mi_wb_object_operator.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        ri_wb_object_operator = mi_wb_object_operator.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA:
          li_wb_object_operator TYPE REF TO object,
          li_object_data_model  TYPE REF TO if_wb_object_data_model,
          lx_error              TYPE REF TO cx_root.
    
        TRY.
            li_wb_object_operator = get_wb_object_operator( ).
    
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
              IMPORTING
                eo_object_data = li_object_data_model.
    
            rv_user = li_object_data_model->get_changed_by( ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA:
          lx_error              TYPE REF TO cx_root,
          li_wb_object_operator TYPE REF TO object.
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        TRY.
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~DELETE')
              EXPORTING
                transport_request = iv_transport.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA li_wb_object_operator TYPE REF TO object.
        DATA li_object_data_model  TYPE REF TO if_wb_object_data_model.
    
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE syuname.
        FIELD-SYMBOLS  TYPE p.
        FIELD-SYMBOLS  TYPE uccheck.
    
        ASSIGN mr_data->* TO .
        ASSERT sy-subrc = 0.
    
        io_xml->read(
          EXPORTING
            iv_name = 'SKTD'
          CHANGING
            cg_data =  ).
    
        " update( ) requires created_at and created_by to be set
        ASSIGN COMPONENT 'METADATA' OF STRUCTURE  TO .
        IF sy-subrc = 0.
          ASSIGN COMPONENT 'CREATED_AT' OF STRUCTURE  TO .
          IF sy-subrc = 0 AND  IS INITIAL.
            GET TIME STAMP FIELD .
          ENDIF.
          ASSIGN COMPONENT 'CREATED_BY' OF STRUCTURE  TO .
          IF sy-subrc = 0 AND  IS INITIAL.
             = sy-uname.
          ENDIF.
    
          ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE  TO .
          IF sy-subrc = 0.
            set_abap_language_version( CHANGING cv_abap_language_version =  ).
          ENDIF.
        ENDIF.
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        CREATE OBJECT li_object_data_model TYPE ('CL_KTD_OBJECT_DATA').
        li_object_data_model->set_data(  ).
    
        tadir_insert( iv_package ).
    
        IF zif_abapgit_object~exists( ) = abap_true.
    
          CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
            EXPORTING
              io_object_data    = li_object_data_model
              transport_request = iv_transport.
    
        ELSE.
    
          CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~CREATE')
            EXPORTING
              io_object_data    = li_object_data_model
              data_selection    = 'P' " if_wb_object_data_selection_co=>c_properties
              package           = iv_package
              transport_request = iv_transport.
    
          CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
            EXPORTING
              io_object_data    = li_object_data_model
              data_selection    = 'D' " if_wb_object_data_selection_co=>c_data_content
              transport_request = iv_transport.
        ENDIF.
    
        CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~ACTIVATE').
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        TRY.
            mi_persistence->get(
              p_object_key           = mv_object_key
              p_version              = 'A'
              p_existence_check_only = abap_true ).
            rv_bool = abap_true.
    
          CATCH cx_swb_exception.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for(
          iv_lock_object = 'WBS_ENQUEUE_STRU'
          iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          li_wb_object_operator TYPE REF TO object,
          li_object_data_model  TYPE REF TO if_wb_object_data_model,
          lx_error              TYPE REF TO cx_root.
    
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE uccheck.
    
        ASSIGN mr_data->* TO .
        ASSERT sy-subrc = 0.
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        TRY.
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
              EXPORTING
                version        = 'A'
              IMPORTING
                data           = 
                eo_object_data = li_object_data_model.
    
            clear_fields( CHANGING cs_data =  ).
    
            ASSIGN COMPONENT 'METADATA-ABAP_LANGUAGE_VERSION' OF STRUCTURE  TO .
            IF sy-subrc = 0.
              clear_abap_language_version( CHANGING cv_abap_language_version =  ).
            ENDIF.
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        io_xml->add(
          iv_name = 'SKTD'
          ig_data =  ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_smbc IMPLEMENTATION.
    
      METHOD constructor.
    
        DATA: lo_handler TYPE REF TO object,
              lo_db_api  TYPE REF TO object,
              lr_data    TYPE REF TO data.
    
        super->constructor(
            is_item        = is_item
            iv_language    = iv_language
            io_files       = io_files
            io_i18n_params = io_i18n_params ).
    
        TRY.
            CREATE OBJECT lo_handler TYPE ('CL_SMBC_AFF_OBJECT_HANDLER').
            CREATE OBJECT lo_db_api TYPE ('CL_MBC_BUSINESS_CONFIG_DB').
            CREATE DATA lr_data TYPE ('SMBC_CONFIG').
          CATCH cx_sy_create_object_error
                cx_sy_create_data_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_handler      TYPE REF TO object,
              lo_db_api       TYPE REF TO object,
              lr_data         TYPE REF TO data,
              lv_technical_id TYPE c LENGTH 30.
    
        FIELD-SYMBOLS:      TYPE any,
                        TYPE any.
    
        CREATE OBJECT lo_handler TYPE ('CL_SMBC_AFF_OBJECT_HANDLER').
        CREATE OBJECT lo_db_api TYPE ('CL_MBC_BUSINESS_CONFIG_DB').
        CREATE DATA lr_data TYPE ('SMBC_CONFIG').
        ASSIGN lr_data->* TO .
    
        lv_technical_id = ms_item-obj_name.
        CALL METHOD lo_db_api->('IF_MBC_BUSINESS_CONFIG_DB~READ')
          EXPORTING
            iv_technical_id = lv_technical_id
            version         = 'I'
          RECEIVING
            rs_config       = .
        IF  IS INITIAL.
          CALL METHOD lo_db_api->('IF_MBC_BUSINESS_CONFIG_DB~READ')
            EXPORTING
              iv_technical_id = lv_technical_id
              version         = 'A'
            RECEIVING
              rs_config       = .
        ENDIF.
        ASSIGN COMPONENT 'CHANGED_BY' OF STRUCTURE  TO .
        rv_user = .
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_smim IMPLEMENTATION.
    
      METHOD build_filename.
    
        CONCATENATE ms_item-obj_name ms_item-obj_type iv_filename INTO rv_filename SEPARATED BY '.'.
        TRANSLATE rv_filename TO LOWER CASE.
    
      ENDMETHOD.
    
      METHOD find_content.
    
        DATA: lv_filename TYPE string,
              lt_files    TYPE zif_abapgit_git_definitions=>ty_files_tt.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_files.
    
        lt_files = mo_files->get_files( ).
    
        READ TABLE lt_files ASSIGNING 
            WITH KEY file
            COMPONENTS filename = iv_filename.
        IF sy-subrc <> 0.
          " Fallback to getting file name from URL
          lv_filename = build_filename( get_filename( iv_url ) ).
    
          READ TABLE lt_files ASSIGNING 
              WITH KEY file
              COMPONENTS filename = lv_filename.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'SMIM, file not found' ).
          ENDIF.
        ENDIF.
    
        rv_content = -data.
    
      ENDMETHOD.
    
      METHOD get_filename.
    
        DATA: lv_lines   TYPE i,
              lt_strings TYPE TABLE OF string.
    
        SPLIT iv_url AT '/' INTO TABLE lt_strings.
        lv_lines = lines( lt_strings ).
        ASSERT lv_lines > 0.
        READ TABLE lt_strings INDEX lv_lines INTO rv_filename.
        ASSERT sy-subrc = 0.
    
      ENDMETHOD.
    
      METHOD get_filename_and_mimetype.
    
        DATA ls_phio TYPE skwf_io.
        DATA lt_phios TYPE STANDARD TABLE OF skwf_io WITH DEFAULT KEY.
    
        " Get file name with extension which is important for importing object correctly
        CALL FUNCTION 'SKWF_LOIO_ALL_PHIOS_GET'
          EXPORTING
            loio  = is_loio
          TABLES
            phios = lt_phios.
    
        LOOP AT lt_phios INTO ls_phio.
          SELECT SINGLE file_name mimetype FROM smimphf INTO CORRESPONDING FIELDS OF cs_extra
            WHERE langu = sy-langu AND loio_id = is_loio-objid AND phio_id = ls_phio-objid.
          IF sy-subrc = 0.
            EXIT.
          ENDIF.
        ENDLOOP.
    
        IF cs_extra-mimetype IS INITIAL OR cs_extra-file_name IS INITIAL.
          zcx_abapgit_exception=>raise( 'File name or mimetype not found' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_properties.
    
        DATA ls_loio_prop TYPE sdokpropty.
        DATA lt_loio_props TYPE STANDARD TABLE OF sdokpropty WITH DEFAULT KEY.
    
        CALL FUNCTION 'SKWF_IO_PROPERTIES_GET'
          EXPORTING
            io                = is_loio
          TABLES
            properties_result = lt_loio_props.
    
        READ TABLE lt_loio_props INTO ls_loio_prop WITH KEY name = c_prop_description.
        IF sy-subrc = 0.
          cs_extra-description = ls_loio_prop-value.
        ENDIF.
    
        READ TABLE lt_loio_props INTO ls_loio_prop WITH KEY name = c_prop_folder_id.
        IF sy-subrc = 0.
          cs_extra-parent_folder_id = ls_loio_prop-value.
        ENDIF.
    
        READ TABLE lt_loio_props INTO ls_loio_prop WITH KEY name = c_prop_abap_langu_vers.
        IF sy-subrc = 0.
          cs_extra-abap_language_version = ls_loio_prop-value.
          clear_abap_language_version( CHANGING cv_abap_language_version = cs_extra-abap_language_version ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_url_for_io.
    
        DATA ls_smimloio TYPE smimloio.
    
        CLEAR: ev_url, ev_is_folder, es_io.
    
        SELECT SINGLE * FROM smimloio INTO ls_smimloio
          WHERE loio_id = ms_item-obj_name.                 "#EC CI_GENBUFF
        IF sy-subrc <> 0.
          RAISE EXCEPTION TYPE zcx_abapgit_not_found.
        ENDIF.
    
        IF ls_smimloio-lo_class = wbmr_c_skwf_folder_class.
          ev_is_folder = abap_true.
          es_io-objtype = skwfc_obtype_folder.
        ELSE.
          es_io-objtype = skwfc_obtype_loio.
        ENDIF.
        es_io-class = ls_smimloio-lo_class.
        es_io-objid = ls_smimloio-loio_id.
    
        CALL FUNCTION 'SKWF_NMSPC_IO_ADDRESS_GET'
          EXPORTING
            io  = es_io
          IMPORTING
            url = ev_url.
    
      ENDMETHOD.
    
      METHOD set_filename_and_mimetype.
    
        DATA ls_phio TYPE skwf_io.
        DATA lt_phios TYPE STANDARD TABLE OF skwf_io WITH DEFAULT KEY.
    
        IF is_extra-mimetype IS INITIAL OR is_extra-file_name IS INITIAL.
          RETURN.
        ENDIF.
    
        CALL FUNCTION 'SKWF_LOIO_ALL_PHIOS_GET'
          EXPORTING
            loio  = is_loio
          TABLES
            phios = lt_phios.
    
        LOOP AT lt_phios INTO ls_phio.
          UPDATE smimphf SET mimetype = is_extra-mimetype file_name = is_extra-file_name
            WHERE langu = sy-langu AND loio_id = is_loio-objid AND phio_id = ls_phio-objid.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'Error updating file name and mimetype' ).
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD set_properties.
    
        DATA ls_property TYPE sdokpropty.
        DATA lt_properties TYPE STANDARD TABLE OF sdokpropty WITH DEFAULT KEY.
        DATA lv_abap_language_version TYPE uccheck.
    
        ls_property-name  = c_prop_description.
        ls_property-value = is_extra-description.
        INSERT ls_property INTO TABLE lt_properties.
    
        lv_abap_language_version = is_extra-abap_language_version.
        set_abap_language_version( CHANGING cv_abap_language_version = lv_abap_language_version ).
    
        ls_property-name  = c_prop_abap_langu_vers.
        ls_property-value = lv_abap_language_version.
        INSERT ls_property INTO TABLE lt_properties.
    
        CALL FUNCTION 'SKWF_IO_PROPERTIES_SET'
          EXPORTING
            io         = is_loio
          TABLES
            properties = lt_properties.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lv_loio TYPE sdok_docid.
    
        lv_loio = ms_item-obj_name.
    
        SELECT SINGLE chng_user FROM smimloio INTO rv_user
          WHERE loio_id = lv_loio.                          "#EC CI_GENBUFF
        IF sy-subrc <> 0 OR rv_user IS INITIAL.
          SELECT SINGLE crea_user FROM smimloio INTO rv_user
            WHERE loio_id = lv_loio.                        "#EC CI_GENBUFF
          IF sy-subrc <> 0 OR rv_user IS INITIAL.
            rv_user = c_user_unknown.
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: li_api TYPE REF TO if_mr_api,
              lv_url TYPE skwf_url.
    
        TRY.
            get_url_for_io( IMPORTING ev_url = lv_url ).
          CATCH zcx_abapgit_not_found.
            " Deleted already (maybe by "folder with children") but record deletion in transport
            corr_insert( iv_package ).
            RETURN.
        ENDTRY.
    
        li_api = cl_mime_repository_api=>if_mr_api~get_api( ).
        li_api->delete(
          EXPORTING
            i_url              = lv_url
            i_delete_children  = abap_true
          EXCEPTIONS
            parameter_missing  = 1
            error_occured      = 2
            cancelled          = 3
            permission_failure = 4
            not_found          = 5
            OTHERS             = 6 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_url      TYPE skwf_url,
              lv_folder   TYPE abap_bool,
    
              lv_content  TYPE xstring,
              ls_extra    TYPE ty_extra,
              ls_loio     TYPE skwf_io,
              li_api      TYPE REF TO if_mr_api.
    
        li_api = cl_mime_repository_api=>if_mr_api~get_api( ).
        ls_loio-objid = ms_item-obj_name.
    
        io_xml->read( EXPORTING iv_name = 'URL'
                      CHANGING  cg_data = lv_url ).
        io_xml->read( EXPORTING iv_name = 'FOLDER'
                      CHANGING  cg_data = lv_folder ).
        io_xml->read( EXPORTING iv_name = 'CLASS'
                      CHANGING  cg_data = ls_loio-class ).
        io_xml->read( EXPORTING iv_name = 'EXTRA'
                      CHANGING  cg_data = ls_extra ).
    
        IF lv_folder = abap_true.
          ls_loio-objtype = skwfc_obtype_folder.
    
          li_api->create_folder(
            EXPORTING
              i_url              = lv_url
              i_language         = mv_language
              i_dev_package      = iv_package
              i_folder_loio      = ls_loio
            EXCEPTIONS
              parameter_missing  = 1
              error_occured      = 2
              cancelled          = 3
              permission_failure = 4
              folder_exists      = 5
              OTHERS             = 6 ).
          IF sy-subrc <> 5 AND sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
        ELSE.
          ls_loio-objtype = skwfc_obtype_loio.
    
          lv_content = find_content(
            iv_url      = lv_url
            iv_filename = ls_extra-file_name ).
    
          " This PUT is using function SDOK_MIMETYPE_GET to derive the mimetype from the file extension of the URL
          " If there's no extension, it defaults to 'application/octet-stream'. Therefore, the correct file name
          li_api->put(
            EXPORTING
              i_url                   = lv_url
              i_content               = lv_content
              i_dev_package           = iv_package
              i_new_loio              = ls_loio
              i_language              = mv_language
            EXCEPTIONS
              parameter_missing       = 1
              error_occured           = 2
              cancelled               = 3
              permission_failure      = 4
              data_inconsistency      = 5
              new_loio_already_exists = 6
              is_folder               = 7
              OTHERS                  = 8 ).
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          set_filename_and_mimetype(
            is_loio  = ls_loio
            is_extra = ls_extra ).
        ENDIF.
    
        set_properties(
          is_loio  = ls_loio
          is_extra = ls_extra ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_loio TYPE sdok_docid.
    
        lv_loio = ms_item-obj_name.
    
        SELECT SINGLE loio_id FROM smimloio INTO lv_loio
          WHERE loio_id = lv_loio.                          "#EC CI_GENBUFF
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        " TODO: Use parent folder ID here (#4783)
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_url     TYPE skwf_url,
              lv_folder  TYPE abap_bool,
              ls_file    TYPE zif_abapgit_git_definitions=>ty_file,
              lv_content TYPE xstring,
              ls_extra   TYPE ty_extra,
              li_api     TYPE REF TO if_mr_api,
              ls_loio    TYPE skwf_io.
    
        TRY.
            get_url_for_io(
              IMPORTING
                ev_url       = lv_url
                ev_is_folder = lv_folder
                es_io        = ls_loio ).
          CATCH zcx_abapgit_not_found.
            RETURN.
        ENDTRY.
    
        IF lv_folder = abap_false.
          li_api = cl_mime_repository_api=>if_mr_api~get_api( ).
    
          li_api->get(
            EXPORTING
              i_url              = lv_url
            IMPORTING
              e_content          = lv_content
            EXCEPTIONS
              parameter_missing  = 1
              error_occured      = 2
              not_found          = 3
              permission_failure = 4
              OTHERS             = 5 ).
          IF sy-subrc <> 0 AND sy-subrc <> 2 AND sy-subrc <> 3.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          get_filename_and_mimetype(
            EXPORTING
              is_loio  = ls_loio
            CHANGING
              cs_extra = ls_extra ).
    
          CLEAR ls_file.
          ls_file-filename = build_filename( ls_extra-file_name ).
          ls_file-path     = '/'.
          ls_file-data     = lv_content.
          mo_files->add( ls_file ).
        ENDIF.
    
        get_properties(
          EXPORTING
            is_loio  = ls_loio
          CHANGING
            cs_extra = ls_extra ).
    
        io_xml->add( iv_name = 'URL'
                     ig_data = lv_url ).
        io_xml->add( iv_name = 'FOLDER'
                     ig_data = lv_folder ).
        io_xml->add( iv_name = 'CLASS'
                     ig_data = ls_loio-class ).
        io_xml->add( iv_name = 'EXTRA'
                     ig_data = ls_extra ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_smtg IMPLEMENTATION.
    
      METHOD add_component.
    
        DATA:
          ls_component LIKE LINE OF ct_components,
          lo_typedescr TYPE REF TO cl_abap_typedescr.
    
        cl_abap_structdescr=>describe_by_name(
          EXPORTING
            p_name         = iv_structure_name
          RECEIVING
            p_descr_ref    = lo_typedescr
          EXCEPTIONS
            type_not_found = 1
            OTHERS         = 2 ).
        IF sy-subrc <> 0.
          RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = ms_item-obj_type.
        ENDIF.
    
        ls_component-name = iv_fielname.
        ls_component-type ?= lo_typedescr.
        INSERT ls_component INTO TABLE ct_components.
    
      ENDMETHOD.
    
      METHOD clear_field.
    
        FIELD-SYMBOLS:  TYPE data.
    
        ASSIGN
          COMPONENT iv_fieldname
          OF STRUCTURE cg_header
          TO .
        ASSERT sy-subrc = 0.
    
        CLEAR: .
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_template_id = ms_item-obj_name.
        mo_structdescr = get_structure( ).
    
      ENDMETHOD.
    
      METHOD get_structure.
    
        DATA: lt_components TYPE abap_component_tab.
    
        add_component(
          EXPORTING
            iv_fielname       = `HEADER`
            iv_structure_name = `IF_SMTG_EMAIL_TEMPLATE=>TY_GS_TMPL_HDR`
          CHANGING
            ct_components     = lt_components ).
    
        add_component(
          EXPORTING
            iv_fielname       = `HEADER_T`
            iv_structure_name = `IF_SMTG_EMAIL_TEMPLATE=>TY_GT_TMPL_HDR_T`
          CHANGING
            ct_components     = lt_components ).
    
        add_component(
          EXPORTING
            iv_fielname       = `CONTENT`
            iv_structure_name = `IF_SMTG_EMAIL_TEMPLATE=>TY_GT_TMPL_CONT`
          CHANGING
            ct_components     = lt_components ).
    
        ro_structdescr = cl_abap_structdescr=>create( lt_components ).
    
      ENDMETHOD.
    
      METHOD get_template.
    
        DATA:
          lr_template TYPE REF TO data,
          lx_error    TYPE REF TO cx_root,
          lo_template TYPE REF TO object.
    
        FIELD-SYMBOLS:
           TYPE data,
             TYPE data,
             TYPE INDEX TABLE,
            TYPE INDEX TABLE.
    
        CREATE DATA lr_template TYPE HANDLE mo_structdescr.
        ASSIGN lr_template->* TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN
          COMPONENT 'HEADER'
          OF STRUCTURE 
          TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN
          COMPONENT 'HEADER_T'
          OF STRUCTURE 
          TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN
          COMPONENT 'CONTENT'
          OF STRUCTURE 
          TO .
        ASSERT sy-subrc = 0.
    
        TRY.
            CALL METHOD ('CL_SMTG_EMAIL_TEMPLATE')=>get
              EXPORTING
                iv_id       = mv_template_id
              RECEIVING
                ro_instance = lo_template.
    
            CALL METHOD lo_template->('IF_SMTG_EMAIL_TEMPLATE~GET_TMPL_HDR')
              RECEIVING
                rs_tmpl_hdr = .
    
            CALL METHOD lo_template->('IF_SMTG_EMAIL_TEMPLATE~GET_TMPL_HDR_T_ALL')
              RECEIVING
                rt_tmpl_hdr_t = .
    
            CALL METHOD lo_template->('IF_SMTG_EMAIL_TEMPLATE~GET_TMPL_CONT_ALL')
              RECEIVING
                rt_tmpl_cont = .
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        es_template = .
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA:
          lr_template TYPE REF TO data.
    
        FIELD-SYMBOLS:
                    TYPE data,
           TYPE data.
    
        CREATE DATA lr_template TYPE HANDLE mo_structdescr.
        ASSIGN lr_template->* TO .
        ASSERT sy-subrc = 0.
    
        get_template( IMPORTING es_template =  ).
    
        ASSIGN
          COMPONENT 'HEADER-LST_CH_USER_ACCT'
          OF STRUCTURE 
          TO .
        ASSERT sy-subrc = 0.
    
        rv_user = .
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lx_error TYPE REF TO cx_root.
    
        TRY.
            CALL METHOD ('CL_SMTG_EMAIL_TEMPLATE')=>delete
              EXPORTING
                iv_id = mv_template_id.
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA:
          lr_template TYPE REF TO data,
          lx_error    TYPE REF TO cx_root,
          lo_template TYPE REF TO object.
    
        FIELD-SYMBOLS:
              TYPE data,
                TYPE data,
                TYPE INDEX TABLE,
               TYPE INDEX TABLE,
                  TYPE data,
           TYPE data,
           TYPE data.
    
        CREATE DATA lr_template TYPE HANDLE mo_structdescr.
        ASSIGN lr_template->* TO .
        ASSERT sy-subrc = 0.
    
        io_xml->read(
          EXPORTING
            iv_name = 'SMTG'
          CHANGING
            cg_data =  ).
    
        ASSIGN
          COMPONENT 'HEADER'
          OF STRUCTURE 
          TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN
          COMPONENT 'HEADER_T'
          OF STRUCTURE 
          TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN
          COMPONENT 'CONTENT'
          OF STRUCTURE 
          TO .
        ASSERT sy-subrc = 0.
    
        TRY.
            IF zif_abapgit_object~exists( ) = abap_true.
              CALL METHOD ('CL_SMTG_EMAIL_TEMPLATE')=>get
                EXPORTING
                  iv_id       = mv_template_id
                RECEIVING
                  ro_instance = lo_template.
            ELSE.
              CALL METHOD ('CL_SMTG_EMAIL_TEMPLATE')=>create
                EXPORTING
                  is_tmpl_hdr       = 
                RECEIVING
                  ro_email_template = lo_template.
            ENDIF.
    
            CALL METHOD lo_template->('IF_SMTG_EMAIL_TEMPLATE~SET_TMPL_CONT_ALL')
              EXPORTING
                it_tmpl_cont = .
    
            READ TABLE  ASSIGNING 
                                   INDEX 1.
            IF sy-subrc = 0.
              ASSIGN
                COMPONENT 'NAME'
                OF STRUCTURE 
                TO .
              ASSERT sy-subrc = 0.
    
              ASSIGN
                COMPONENT 'DESCRIPTION'
                OF STRUCTURE 
                TO .
              ASSERT sy-subrc = 0.
    
              CALL METHOD lo_template->('IF_SMTG_EMAIL_TEMPLATE~SET_TEXT')
                EXPORTING
                  iv_name        = 
                  iv_description = .
            ENDIF.
    
            tadir_insert( iv_package ).
            corr_insert( iv_package ).
    
            CALL METHOD lo_template->('IF_SMTG_EMAIL_TEMPLATE~SAVE')
              EXPORTING
                iv_lock   = abap_true
                iv_commit = abap_true
                iv_wait   = abap_true.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        TRY.
            CALL METHOD ('CL_SMTG_EMAIL_TEMPLATE')=>get
              EXPORTING
                iv_id = mv_template_id.
    
            rv_bool = abap_true.
    
          CATCH cx_root.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'E_SMTG'
                                                iv_argument    = |{ mv_template_id }| ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          lr_template TYPE REF TO data,
          lx_error    TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:
           TYPE data,
             TYPE data.
    
        CREATE DATA lr_template TYPE HANDLE mo_structdescr.
        ASSIGN lr_template->* TO .
        ASSERT sy-subrc = 0.
    
        get_template( IMPORTING es_template =  ).
    
        ASSIGN
          COMPONENT 'HEADER'
          OF STRUCTURE 
          TO .
        ASSERT sy-subrc = 0.
    
        TRY.
            clear_field( EXPORTING iv_fieldname = 'CREA_DATE_TIME'   CHANGING cg_header =  ).
            clear_field( EXPORTING iv_fieldname = 'CREA_USER_ACCT'   CHANGING cg_header =  ).
            clear_field( EXPORTING iv_fieldname = 'LST_CH_DATE_TIME' CHANGING cg_header =  ).
            clear_field( EXPORTING iv_fieldname = 'LST_CH_USER_ACCT' CHANGING cg_header =  ).
    
            io_xml->add(
                iv_name = 'SMTG'
                ig_data =  ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sobj IMPLEMENTATION.
    
      METHOD get_field_rules.
    
        ri_rules = zcl_abapgit_field_rules=>create( ).
        ri_rules->add(
          iv_table     = 'TOJTB'
          iv_field     = 'CREA_USER'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = 'TOJTB'
          iv_field     = 'CREA_DATE'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-date
        )->add(
          iv_table     = 'TOJTB'
          iv_field     = 'CREA_TIME'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-time
        )->add(
          iv_table     = 'TOJTB'
          iv_field     = 'CHAN_USER'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = 'TOJTB'
          iv_field     = 'CHAN_DATE'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-date
        )->add(
          iv_table     = 'TOJTB'
          iv_field     = 'CHAN_TIME'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-time
        )->add(
          iv_table     = 'TOJTB'
          iv_field     = 'ACTV_USER'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = 'TOJTB'
          iv_field     = 'ACTV_DATE'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-date
        )->add(
          iv_table     = 'TOJTB'
          iv_field     = 'ACTV_TIME'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-time
        )->add(
          iv_table     = 'TOJTB'
          iv_field     = 'REL_USER'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user
        )->add(
          iv_table     = 'TOJTB'
          iv_field     = 'REL_DATE'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-date
        )->add(
          iv_table     = 'TOJTB'
          iv_field     = 'REL_TIME'
          iv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-time ).
    
      ENDMETHOD.
    
      METHOD get_generic.
    
        CREATE OBJECT ro_generic
          EXPORTING
            io_field_rules = get_field_rules( )
            is_item        = ms_item
            iv_language    = mv_language.
    
      ENDMETHOD.
    
      METHOD get_program.
        SELECT SINGLE progname INTO rv_program FROM tojtb WHERE name = ms_item-obj_name.
      ENDMETHOD.
    
      METHOD is_locked.
        rv_is_locked = boolc( is_objtype_locked( ) = abap_true OR is_program_locked( ) = abap_true ).
      ENDMETHOD.
    
      METHOD is_objtype_locked.
        CONSTANTS lc_tabname TYPE tabname VALUE 'SWOTBASDAT'.
        DATA lv_varkey TYPE vim_enqkey.
    
        rv_is_locked = abap_false.
        lv_varkey = ms_item-obj_name.
    
        CALL FUNCTION 'ENQUEUE_E_TABLE'
          EXPORTING
            tabname      = lc_tabname
            varkey       = lv_varkey
          EXCEPTIONS
            foreign_lock = 1
            OTHERS       = 999.
        IF sy-subrc IS NOT INITIAL.
          rv_is_locked = abap_true.
        ELSE.
          CALL FUNCTION 'DEQUEUE_E_TABLE'
            EXPORTING
              tabname = lc_tabname
              varkey  = lv_varkey.
        ENDIF.
      ENDMETHOD.
    
      METHOD is_program_locked.
        CONSTANTS lc_enqueue_exclusive TYPE enqmode VALUE 'X'.
        DATA lv_progname TYPE progname.
    
        rv_is_locked = abap_false.
        lv_progname = get_program( ).
    
        IF lv_progname IS NOT INITIAL.
          CALL FUNCTION 'ENQUEUE_ESRDIRE'
            EXPORTING
              mode_trdir   = lc_enqueue_exclusive
              name         = lv_progname
            EXCEPTIONS
              foreign_lock = 1
              OTHERS       = 999.
          IF sy-subrc IS NOT INITIAL.
            rv_is_locked = abap_true.
          ELSE.
            CALL FUNCTION 'DEQUEUE_ESRDIRE'
              EXPORTING
                mode_trdir = lc_enqueue_exclusive
                name       = lv_progname.
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        DATA: BEGIN OF ls_userinfo,
                crea_user TYPE tojtb-crea_user,
                chan_user TYPE tojtb-chan_user,
              END   OF ls_userinfo.
    
        SELECT SINGLE
            crea_user
            chan_user
        INTO (ls_userinfo-crea_user, ls_userinfo-chan_user)
        FROM tojtb WHERE name = ms_item-obj_name.
    
        IF ls_userinfo-chan_user IS INITIAL.
          ls_userinfo-chan_user = ls_userinfo-crea_user.
        ENDIF.
        rv_user = ls_userinfo-chan_user.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        get_generic( )->delete( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = is_locked( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        "No need as GENERIC class already handles it
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        get_generic( )->serialize( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sod1 IMPLEMENTATION.
    
      METHOD clear_content_fields.
    
        FIELD-SYMBOLS  TYPE any.
    
        ASSIGN COMPONENT 'CONTENT' OF STRUCTURE cs_data TO .
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CHANGE_USER'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CHANGE_TIMESTAMP'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CREATE_USER'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CREATE_TIMESTAMP'
          CHANGING
            cs_metadata  =  ).
    
      ENDMETHOD.
    
      METHOD clear_field.
    
        FIELD-SYMBOLS:  TYPE data.
    
        ASSIGN COMPONENT iv_fieldname OF STRUCTURE cs_metadata TO .
        IF sy-subrc = 0.
          CLEAR: .
        ENDIF.
    
      ENDMETHOD.
    
      METHOD clear_metadata_fields.
    
        FIELD-SYMBOLS  TYPE any.
    
        ASSIGN COMPONENT 'METADATA' OF STRUCTURE cs_data TO .
    
        clear_field(
          EXPORTING
            iv_fieldname = 'VERSION'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CREATED_AT'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CREATED_BY'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CHANGED_AT'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CHANGED_BY'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'RESPONSIBLE'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'PACKAGE_REF'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'MASTER_SYSTEM'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'DT_UUID'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'ABAP_LANGU_VERSION'
          CHANGING
            cs_metadata  =  ).
        clear_field(
          EXPORTING
            iv_fieldname = 'ABAP_LANGU_VERSION'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'LINKS'
          CHANGING
            cs_metadata  =  ).
    
      ENDMETHOD.
    
      METHOD constructor.
    
        DATA lo_data_model TYPE REF TO object.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        TRY.
            CREATE OBJECT lo_data_model TYPE (c_data_model_class_name).
          CATCH cx_root.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD create_wb_object_operator.
    
        DATA lx_error TYPE REF TO cx_root.
    
        TRY.
    
            CALL METHOD ('CL_WB_OBJECT_OPERATOR_FACTORY')=>('CREATE_OBJECT_OPERATOR')
              EXPORTING
                object_type       = is_object_type
                object_key        = iv_object_key
                transport_request = iv_transport_request
                do_commits        = iv_do_commits
                run_in_test_mode  = iv_run_in_test_mode
              RECEIVING
                result            = ro_wb_object_operator.
    
          CATCH cx_root INTO lx_error.
    
            zcx_abapgit_exception=>raise_with_text( lx_error ).
    
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get_wb_object_operator.
    
        DATA lx_error TYPE REF TO cx_root.
    
        TRY.
    
            CALL METHOD ('CL_WB_OBJECT_OPERATOR_FACTORY')=>('GET_OBJECT_OPERATOR')
              EXPORTING
                object_type       = is_object_type
                object_key        = iv_object_key
                transport_request = iv_transport_request
              RECEIVING
                result            = ro_wb_object_operator.
    
          CATCH cx_root INTO lx_error.
    
            zcx_abapgit_exception=>raise_with_text( lx_error ).
    
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_data_model  TYPE REF TO if_wb_object_data_model,
              lo_factory     TYPE REF TO object,
              ls_object_type TYPE wbobjtype,
              lv_object_key  TYPE seu_objkey,
              lx_error       TYPE REF TO cx_root.
    
        TRY.
    
            ls_object_type-objtype_tr = ms_item-obj_type.
            lv_object_key             = ms_item-obj_name.
    
            lo_factory = create_wb_object_operator( is_object_type = ls_object_type
                                                    iv_object_key  = lv_object_key ).
    
            CALL METHOD lo_factory->('IF_WB_OBJECT_OPERATOR~READ')
              IMPORTING
                eo_object_data = lo_data_model.
    
            rv_user = lo_data_model->get_changed_by( ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: ls_object_type TYPE wbobjtype,
              lv_object_key  TYPE seu_objkey,
              lo_factory     TYPE REF TO object,
              lx_error       TYPE REF TO cx_root.
    
        ls_object_type-objtype_tr = ms_item-obj_type.
        lv_object_key             = ms_item-obj_name.
    
        TRY.
    
            lo_factory = get_wb_object_operator( is_object_type       = ls_object_type
                                                 iv_object_key        = lv_object_key
                                                 iv_transport_request = iv_transport ).
    
            CALL METHOD lo_factory->('IF_WB_OBJECT_OPERATOR~DELETE').
    
          CATCH cx_root INTO lx_error.
    
            zcx_abapgit_exception=>raise_with_text( lx_error ).
    
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lo_factory               TYPE REF TO object,
              lo_data_model            TYPE REF TO if_wb_object_data_model,
              lv_data_type_name        TYPE string,
              ls_data                  TYPE REF TO data,
              ls_object_type           TYPE wbobjtype,
              lv_object_key            TYPE seu_objkey,
              lo_logger                TYPE REF TO cl_wb_checklist,
              lx_create_error          TYPE REF TO cx_root,
              lx_error                 TYPE REF TO cx_root,
              lt_msgs                  TYPE TABLE OF string,
              lt_error_msgs_create     TYPE swbme_error_tab,
              ls_error_msg_create      LIKE LINE OF lt_error_msgs_create,
              lv_error_msg             TYPE string,
              lv_abap_language_version TYPE c LENGTH 1. " abap_language_version
    
        FIELD-SYMBOLS  TYPE any.
    
        CREATE OBJECT lo_data_model TYPE (c_data_model_class_name).
    
        " if_wb_object_data_selection_co=>c_all_data
        CALL METHOD lo_data_model->('GET_DATATYPE_NAME')
          EXPORTING
            p_data_selection = 'AL'
          RECEIVING
            result           = lv_data_type_name.
    
        CREATE DATA ls_data TYPE (lv_data_type_name).
        ASSIGN ls_data->* TO .
    
        io_xml->read(
          EXPORTING
            iv_name = c_xml_transformation_name
          CHANGING
            cg_data =  ).
    
        CALL METHOD lo_data_model->('SET_SELECTED_DATA')
          EXPORTING
            p_data_selection = 'AL' " if_wb_object_data_selection_co=>c_all_data
            p_data           = .
    
        TRY.
    
            ls_object_type-objtype_tr = ms_item-obj_type.
            lv_object_key             = ms_item-obj_name.
    
            lo_factory = get_wb_object_operator( is_object_type = ls_object_type
                                                 iv_object_key  = lv_object_key ).
    
            IF zif_abapgit_object~exists( ) = abap_true.
    
              CALL METHOD lo_factory->('IF_WB_OBJECT_OPERATOR~UPDATE')
                EXPORTING
                  io_object_data    = lo_data_model
                  version           = 'A'
                  transport_request = iv_transport.
    
            ELSE.
    
              TRY.
    
                  CALL METHOD lo_data_model->('GET_ABAP_LANGUAGE_VERSION')
                    RECEIVING
                      result = lv_abap_language_version.
    
                  CALL METHOD lo_factory->('IF_WB_OBJECT_OPERATOR~CREATE')
                    EXPORTING
                      io_object_data        = lo_data_model
                      version               = 'A'
                      package               = iv_package
                      abap_language_version = lv_abap_language_version
                      transport_request     = iv_transport
                    IMPORTING
                      logger                = lo_logger.
    
                CATCH cx_root INTO lx_create_error.
    
                  " Check for error messages from Workbench API to provide more error infos to user
                  lo_logger->get_error_messages( IMPORTING p_error_tab = lt_error_msgs_create ).
    
                  IF lt_error_msgs_create IS NOT INITIAL.
    
                    LOOP AT lt_error_msgs_create INTO ls_error_msg_create.
    
                      APPEND LINES OF ls_error_msg_create-mtext TO lt_msgs.
    
                    ENDLOOP.
    
                    CONCATENATE LINES OF lt_msgs INTO lv_error_msg SEPARATED BY '; '.
                    zcx_abapgit_exception=>raise( iv_text     = lv_error_msg
                                                  ix_previous = lx_create_error ).
    
                  ELSE.
    
                    zcx_abapgit_exception=>raise_with_text( lx_create_error ).
    
                  ENDIF.
    
              ENDTRY.
    
            ENDIF.
    
          CATCH cx_root INTO lx_error.
    
            zcx_abapgit_exception=>raise_with_text( lx_error ).
    
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lo_factory     TYPE REF TO object,
              ls_object_type TYPE wbobjtype,
              lv_object_key  TYPE seu_objkey,
              lx_error       TYPE REF TO cx_root.
    
        TRY.
    
            ls_object_type-objtype_tr = ms_item-obj_type.
            lv_object_key             = ms_item-obj_name.
    
            lo_factory = get_wb_object_operator( is_object_type = ls_object_type
                                                 iv_object_key  = lv_object_key ).
    
            CALL METHOD lo_factory->('IF_WB_OBJECT_OPERATOR~CHECK_EXISTENCE')
              RECEIVING
                r_result = rv_bool.
    
          CATCH cx_root INTO lx_error.
    
            zcx_abapgit_exception=>raise_with_text( lx_error ).
    
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESWB_EO'
                                                iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }*| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lo_data_model     TYPE REF TO if_wb_object_data_model,
              lv_data_type_name TYPE string,
              lo_factory        TYPE REF TO object,
              ls_object_type    TYPE wbobjtype,
              lv_object_key     TYPE seu_objkey,
              lx_error          TYPE REF TO cx_root.
    
        DATA ls_data TYPE REF TO data.
        FIELD-SYMBOLS  TYPE any.
    
        TRY.
    
            ls_object_type-objtype_tr = ms_item-obj_type.
            lv_object_key             = ms_item-obj_name.
    
            lo_factory = create_wb_object_operator( is_object_type = ls_object_type
                                                    iv_object_key  = lv_object_key ).
    
            CALL METHOD lo_factory->('IF_WB_OBJECT_OPERATOR~READ')
              IMPORTING
                eo_object_data = lo_data_model.
    
            " if_wb_object_data_selection_co=>c_all_data
            CALL METHOD lo_data_model->('GET_DATATYPE_NAME')
              EXPORTING
                p_data_selection = 'AL'
              RECEIVING
                result           = lv_data_type_name.
    
            CREATE DATA ls_data TYPE (lv_data_type_name).
            ASSIGN ls_data->* TO .
    
            CALL METHOD lo_data_model->('GET_SELECTED_DATA')
              EXPORTING
                p_data_selection = 'AL' " if_wb_object_data_selection_co=>c_all_data
              IMPORTING
                p_data           = .
    
            clear_metadata_fields( CHANGING cs_data =  ).
            clear_content_fields( CHANGING cs_data =  ).
            clear_field( EXPORTING iv_fieldname = 'PLUGIN_CONFIG'
                         CHANGING  cs_metadata  =  ).
    
            io_xml->add( iv_name = c_xml_transformation_name
                         ig_data =  ).
    
          CATCH cx_root INTO lx_error.
    
            zcx_abapgit_exception=>raise_with_text( lx_error ).
    
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sod2 IMPLEMENTATION.
    
      METHOD clear_content_fields.
    
        FIELD-SYMBOLS  TYPE any.
    
        ASSIGN COMPONENT 'CONTENT' OF STRUCTURE cs_data TO .
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CHANGE_USER'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CHANGE_TIMESTAMP'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CREATE_USER'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CREATE_TIMESTAMP'
          CHANGING
            cs_metadata  =  ).
    
      ENDMETHOD.
    
      METHOD clear_field.
    
        FIELD-SYMBOLS:  TYPE data.
    
        ASSIGN COMPONENT iv_fieldname OF STRUCTURE cs_metadata TO .
        IF sy-subrc = 0.
          CLEAR: .
        ENDIF.
    
      ENDMETHOD.
    
      METHOD clear_metadata_fields.
    
        FIELD-SYMBOLS  TYPE any.
    
        ASSIGN COMPONENT 'METADATA' OF STRUCTURE cs_data TO .
    
        clear_field(
          EXPORTING
            iv_fieldname = 'VERSION'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CREATED_AT'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CREATED_BY'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CHANGED_AT'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CHANGED_BY'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'RESPONSIBLE'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'PACKAGE_REF'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'MASTER_SYSTEM'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'DT_UUID'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'ABAP_LANGU_VERSION'
          CHANGING
            cs_metadata  =  ).
        clear_field(
          EXPORTING
            iv_fieldname = 'ABAP_LANGU_VERSION'
          CHANGING
            cs_metadata  =  ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'LINKS'
          CHANGING
            cs_metadata  =  ).
    
      ENDMETHOD.
    
      METHOD constructor.
    
        DATA lo_data_model TYPE REF TO object.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        TRY.
            CREATE OBJECT lo_data_model TYPE (c_data_model_class_name).
          CATCH cx_root.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD create_wb_object_operator.
    
        DATA lx_error TYPE REF TO cx_root.
    
        TRY.
    
            CALL METHOD ('CL_WB_OBJECT_OPERATOR_FACTORY')=>('CREATE_OBJECT_OPERATOR')
              EXPORTING
                object_type       = is_object_type
                object_key        = iv_object_key
                transport_request = iv_transport_request
                do_commits        = iv_do_commits
                run_in_test_mode  = iv_run_in_test_mode
              RECEIVING
                result            = ro_wb_object_operator.
    
          CATCH cx_root INTO lx_error.
    
            zcx_abapgit_exception=>raise_with_text( lx_error ).
    
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get_wb_object_operator.
    
        DATA lx_error TYPE REF TO cx_root.
    
        TRY.
    
            CALL METHOD ('CL_WB_OBJECT_OPERATOR_FACTORY')=>('GET_OBJECT_OPERATOR')
              EXPORTING
                object_type       = is_object_type
                object_key        = iv_object_key
                transport_request = iv_transport_request
              RECEIVING
                result            = ro_wb_object_operator.
    
          CATCH cx_root INTO lx_error.
    
            zcx_abapgit_exception=>raise_with_text( lx_error ).
    
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_data_model  TYPE REF TO if_wb_object_data_model,
              lo_factory     TYPE REF TO object,
              ls_object_type TYPE wbobjtype,
              lv_object_key  TYPE seu_objkey,
              lx_error       TYPE REF TO cx_root.
    
        TRY.
    
            ls_object_type-objtype_tr = ms_item-obj_type.
            lv_object_key             = ms_item-obj_name.
    
            lo_factory = create_wb_object_operator( is_object_type = ls_object_type
                                                    iv_object_key  = lv_object_key ).
    
            CALL METHOD lo_factory->('IF_WB_OBJECT_OPERATOR~READ')
              IMPORTING
                eo_object_data = lo_data_model.
    
            rv_user = lo_data_model->get_changed_by( ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: ls_object_type TYPE wbobjtype,
              lv_object_key  TYPE seu_objkey,
              lo_factory     TYPE REF TO object,
              lx_error       TYPE REF TO cx_root.
    
        ls_object_type-objtype_tr = ms_item-obj_type.
        lv_object_key             = ms_item-obj_name.
    
        TRY.
    
            lo_factory = get_wb_object_operator( is_object_type       = ls_object_type
                                                 iv_object_key        = lv_object_key
                                                 iv_transport_request = iv_transport ).
    
            CALL METHOD lo_factory->('IF_WB_OBJECT_OPERATOR~DELETE').
    
          CATCH cx_root INTO lx_error.
    
            zcx_abapgit_exception=>raise_with_text( lx_error ).
    
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lo_factory               TYPE REF TO object,
              lo_data_model            TYPE REF TO if_wb_object_data_model,
              lv_data_type_name        TYPE string,
              ls_data                  TYPE REF TO data,
              ls_object_type           TYPE wbobjtype,
              lv_object_key            TYPE seu_objkey,
              lo_logger                TYPE REF TO cl_wb_checklist,
              lx_create_error          TYPE REF TO cx_root,
              lx_error                 TYPE REF TO cx_root,
              lt_msgs                  TYPE TABLE OF string,
              lt_error_msgs_create     TYPE swbme_error_tab,
              ls_error_msg_create      LIKE LINE OF lt_error_msgs_create,
              lv_error_msg             TYPE string,
              lv_abap_language_version TYPE c LENGTH 1. " abap_language_version
    
        FIELD-SYMBOLS  TYPE any.
    
        CREATE OBJECT lo_data_model TYPE (c_data_model_class_name).
    
        " if_wb_object_data_selection_co=>c_all_data
        CALL METHOD lo_data_model->('GET_DATATYPE_NAME')
          EXPORTING
            p_data_selection = 'AL'
          RECEIVING
            result           = lv_data_type_name.
    
        CREATE DATA ls_data TYPE (lv_data_type_name).
        ASSIGN ls_data->* TO .
    
        io_xml->read(
          EXPORTING
            iv_name = c_xml_transformation_name
          CHANGING
            cg_data =  ).
    
        CALL METHOD lo_data_model->('SET_SELECTED_DATA')
          EXPORTING
            p_data_selection = 'AL' " if_wb_object_data_selection_co=>c_all_data
            p_data           = .
    
        TRY.
    
            ls_object_type-objtype_tr = ms_item-obj_type.
            lv_object_key             = ms_item-obj_name.
    
            lo_factory = get_wb_object_operator( is_object_type = ls_object_type
                                                 iv_object_key  = lv_object_key ).
    
            IF zif_abapgit_object~exists( ) = abap_true.
    
              CALL METHOD lo_factory->('IF_WB_OBJECT_OPERATOR~UPDATE')
                EXPORTING
                  io_object_data    = lo_data_model
                  version           = 'A'
                  transport_request = iv_transport.
    
            ELSE.
    
              TRY.
    
                  CALL METHOD lo_data_model->('GET_ABAP_LANGUAGE_VERSION')
                    RECEIVING
                      result = lv_abap_language_version.
    
                  CALL METHOD lo_factory->('IF_WB_OBJECT_OPERATOR~CREATE')
                    EXPORTING
                      io_object_data        = lo_data_model
                      version               = 'A'
                      package               = iv_package
                      abap_language_version = lv_abap_language_version
                      transport_request     = iv_transport
                    IMPORTING
                      logger                = lo_logger.
    
                CATCH cx_root INTO lx_create_error.
    
                  " Check for error messages from Workbench API to provide more error infos to user
                  lo_logger->get_error_messages( IMPORTING p_error_tab = lt_error_msgs_create ).
    
                  IF lt_error_msgs_create IS NOT INITIAL.
    
                    LOOP AT lt_error_msgs_create INTO ls_error_msg_create.
    
                      APPEND LINES OF ls_error_msg_create-mtext TO lt_msgs.
    
                    ENDLOOP.
    
                    CONCATENATE LINES OF lt_msgs INTO lv_error_msg SEPARATED BY '; '.
                    zcx_abapgit_exception=>raise( iv_text     = lv_error_msg
                                                  ix_previous = lx_create_error ).
    
                  ELSE.
    
                    zcx_abapgit_exception=>raise_with_text( lx_create_error ).
    
                  ENDIF.
    
              ENDTRY.
    
            ENDIF.
    
          CATCH cx_root INTO lx_error.
    
            zcx_abapgit_exception=>raise_with_text( lx_error ).
    
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lo_factory     TYPE REF TO object,
              ls_object_type TYPE wbobjtype,
              lv_object_key  TYPE seu_objkey,
              lx_error       TYPE REF TO cx_root.
    
        TRY.
    
            ls_object_type-objtype_tr = ms_item-obj_type.
            lv_object_key             = ms_item-obj_name.
    
            lo_factory = get_wb_object_operator( is_object_type = ls_object_type
                                                 iv_object_key  = lv_object_key ).
    
            CALL METHOD lo_factory->('IF_WB_OBJECT_OPERATOR~CHECK_EXISTENCE')
              RECEIVING
                r_result = rv_bool.
    
          CATCH cx_root INTO lx_error.
    
            zcx_abapgit_exception=>raise_with_text( lx_error ).
    
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESWB_EO'
                                                iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }*| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lo_data_model     TYPE REF TO if_wb_object_data_model,
              lv_data_type_name TYPE string,
              lo_factory        TYPE REF TO object,
              ls_object_type    TYPE wbobjtype,
              lv_object_key     TYPE seu_objkey,
              lx_error          TYPE REF TO cx_root.
    
        DATA ls_data TYPE REF TO data.
        FIELD-SYMBOLS  TYPE any.
    
        TRY.
    
            ls_object_type-objtype_tr = ms_item-obj_type.
            lv_object_key             = ms_item-obj_name.
    
            lo_factory = create_wb_object_operator( is_object_type = ls_object_type
                                                    iv_object_key  = lv_object_key ).
    
            CALL METHOD lo_factory->('IF_WB_OBJECT_OPERATOR~READ')
              IMPORTING
                eo_object_data = lo_data_model.
    
            " if_wb_object_data_selection_co=>c_all_data
            CALL METHOD lo_data_model->('GET_DATATYPE_NAME')
              EXPORTING
                p_data_selection = 'AL'
              RECEIVING
                result           = lv_data_type_name.
    
            CREATE DATA ls_data TYPE (lv_data_type_name).
            ASSIGN ls_data->* TO .
    
            CALL METHOD lo_data_model->('GET_SELECTED_DATA')
              EXPORTING
                p_data_selection = 'AL' " if_wb_object_data_selection_co=>c_all_data
              IMPORTING
                p_data           = .
    
            clear_metadata_fields( CHANGING cs_data =  ).
            clear_content_fields( CHANGING cs_data =  ).
            clear_field( EXPORTING iv_fieldname = 'PLUGIN_CONFIG'
                         CHANGING  cs_metadata  =  ).
    
            io_xml->add( iv_name = c_xml_transformation_name
                         ig_data =  ).
    
          CATCH cx_root INTO lx_error.
    
            zcx_abapgit_exception=>raise_with_text( lx_error ).
    
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sots IMPLEMENTATION.
    
      METHOD create_sots.
    
        " Reimplementation of SOTR_STRING_CREATE_CONCEPT because we can't supply
        " concept and it would then be generated.
    
        DATA: lv_subrc                 TYPE sy-subrc,
              lv_source_langu          TYPE spras,
              ls_header                TYPE btfr_head,
              lv_flag_is_string        TYPE btfr_flag VALUE abap_true,
              lt_text_tab              TYPE sotr_text_tt,
              lv_concept_default       TYPE sotr_conc,
              lt_entries               TYPE sotr_textl_tt,
              lv_concept               LIKE is_sots-header-concept,
              lv_flag_correction_entry TYPE abap_bool VALUE abap_true.
    
        lt_entries = is_sots-entries.
    
        ls_header-paket          = iv_package.
        ls_header-crea_lan       = mv_language.
        ls_header-alias_name     = is_sots-header-alias_name.
        lv_source_langu          = mv_language.
        lv_concept               = is_sots-header-concept.
    
        PERFORM btfr_create
          IN PROGRAM saplsotr_db_string
          USING iv_object
                lv_source_langu
                lv_flag_correction_entry
                lv_flag_is_string
          CHANGING lt_text_tab
                   lt_entries
                   ls_header
                   lv_concept
                   lv_concept_default
                   lv_subrc.
    
        CASE lv_subrc.
          WHEN 1.
            zcx_abapgit_exception=>raise( |No entry found| ).
          WHEN 2.
            zcx_abapgit_exception=>raise( |OTR concept not found| ).
          WHEN 3.
            zcx_abapgit_exception=>raise( |Enter a permitted object type| ).
          WHEN 4.
            "The concept will be created in the non-original system (not an error)
            RETURN.
          WHEN 5.
            zcx_abapgit_exception=>raise( |Invalid alias| ).
          WHEN 6.
            zcx_abapgit_exception=>raise( |No correction entry has been created| ).
          WHEN 7.
            zcx_abapgit_exception=>raise( |Error in database operation| ).
          WHEN 9.
            zcx_abapgit_exception=>raise( |Action canceled by user| ).
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD get_raw_text_filename.
    
        DATA lv_langu TYPE string.
    
        " Lower case language codes can cause duplicate filenames therefore add suffix to make them unique
        " Note: Using ISO code would be better but is not compatible with existing files
        lv_langu = is_entry-langu.
        IF lv_langu = to_lower( lv_langu ).
          lv_langu = lv_langu && '-'.
        ENDIF.
    
        rv_filename =
            to_lower( |{ is_entry-concept }_|
                   && |{ lv_langu         }_|
                   && |{ is_entry-object  }_|
                   && |{ is_entry-lfd_num }| ).
    
      ENDMETHOD.
    
      METHOD read_sots.
    
        DATA: lt_sotr_head TYPE STANDARD TABLE OF sotr_headu,
              lt_objects   TYPE sotr_objects,
              lv_object    LIKE LINE OF lt_objects,
              ls_sots      LIKE LINE OF rt_sots.
    
        FIELD-SYMBOLS:  TYPE sotr_head,
                            LIKE LINE OF ls_sots-entries.
    
        SELECT * FROM sotr_headu
                 INTO TABLE lt_sotr_head
                 WHERE paket = ms_item-obj_name
                 ORDER BY PRIMARY KEY.
    
        LOOP AT lt_sotr_head ASSIGNING .
    
          CALL FUNCTION 'SOTR_OBJECT_GET_OBJECTS'
            EXPORTING
              object_vector    = -objid_vec
            IMPORTING
              objects          = lt_objects
            EXCEPTIONS
              object_not_found = 1
              OTHERS           = 2.
          IF sy-subrc <> 0.
            CONTINUE.
          ENDIF.
    
          READ TABLE lt_objects INDEX 1 INTO lv_object.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'SOTS: No objects found from SOTR_OBJECT_GET_OBJECTS' ).
          ENDIF.
    
          " Handled by object serializer
          CHECK lv_object <> 'SICF' AND lv_object <> 'CPUB'.
    
          CLEAR: ls_sots.
    
          CALL FUNCTION 'SOTR_STRING_GET_CONCEPT'
            EXPORTING
              concept        = -concept
            IMPORTING
              header         = ls_sots-header
              entries        = ls_sots-entries
            EXCEPTIONS
              no_entry_found = 1
              OTHERS         = 2.
    
          IF sy-subrc <> 0.
            CONTINUE.
          ENDIF.
    
          CLEAR:
            ls_sots-header-paket,
            ls_sots-header-crea_name,
            ls_sots-header-crea_tstut,
            ls_sots-header-chan_name,
            ls_sots-header-chan_tstut.
    
          LOOP AT ls_sots-entries ASSIGNING .
            CLEAR: -version,
                   -crea_name,
                   -crea_tstut,
                   -chan_name,
                   -chan_tstut.
          ENDLOOP.
    
          INSERT ls_sots INTO TABLE rt_sots.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        SELECT SINGLE chan_name FROM sotr_headu INTO rv_user
          WHERE paket = ms_item-obj_name.                   "#EC CI_NOORDER
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lt_sots TYPE ty_sots_tt.
    
        FIELD-SYMBOLS:  TYPE ty_sots.
    
        lt_sots = read_sots( ).
    
        LOOP AT lt_sots ASSIGNING .
          " Remove any usage to ensure deletion, see function module BTFR_CHECK
          DELETE FROM sotr_useu WHERE concept = -header-concept.
    
          CALL FUNCTION 'BTFR_DELETE_SINGLE_TEXT'
            EXPORTING
              concept             = -header-concept
              flag_string         = abap_true
            EXCEPTIONS
              text_not_found      = 1
              invalid_package     = 2
              text_not_changeable = 3
              text_enqueued       = 4
              no_correction       = 5
              parameter_error     = 6
              OTHERS              = 7.
    
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lt_sots    TYPE ty_sots_tt,
              lt_objects TYPE sotr_objects,
              lv_object  LIKE LINE OF lt_objects.
    
        FIELD-SYMBOLS:   TYPE ty_sots,
                        LIKE LINE OF -entries.
    
        io_xml->read(
          EXPORTING
            iv_name = 'SOTS'
          CHANGING
            cg_data = lt_sots ).
    
        tadir_insert( iv_package ).
    
        LOOP AT lt_sots ASSIGNING .
    
          CLEAR: lt_objects.
    
          CALL FUNCTION 'SOTR_OBJECT_GET_OBJECTS'
            EXPORTING
              object_vector    = -header-objid_vec
            IMPORTING
              objects          = lt_objects
            EXCEPTIONS
              object_not_found = 1
              OTHERS           = 2.
    
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'error from SOTR_OBJECT_GET_OBJECTS' ).
          ENDIF.
    
          READ TABLE lt_objects INDEX 1 INTO lv_object.
          ASSERT sy-subrc = 0.
    
          LOOP AT -entries ASSIGNING .
    
            TRY.
                -text = mo_files->read_string(
                  iv_extra = get_raw_text_filename(  )
                  iv_ext   = 'txt' ).
    
              CATCH zcx_abapgit_exception.
                " Most probably file not found -> ignore
                CONTINUE.
            ENDTRY.
    
          ENDLOOP.
    
          create_sots(
              is_sots    = 
              iv_package = iv_package
              iv_object  = lv_object ).
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_object_type TYPE trobjtype,
              lv_object_name TYPE trobj_name.
    
        lv_object_type = ms_item-obj_type.
        lv_object_name = ms_item-obj_name.
    
        CALL FUNCTION 'SOTR_WBO_OBJECTS_CHECK'
          EXPORTING
            pgmid          = 'R3TR'
            object         = lv_object_type
            obj_name       = lv_object_name
          IMPORTING
            object_exist   = rv_bool
          EXCEPTIONS
            unknown_object = 1
            OTHERS         = 2.
    
        IF sy-subrc <> 0.
          rv_bool = abap_false.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lt_sots TYPE ty_sots_tt.
    
        FIELD-SYMBOLS:   TYPE ty_sots,
                        TYPE sotr_textl.
    
        lt_sots = read_sots( ).
    
        LOOP AT lt_sots ASSIGNING .
    
          LOOP AT -entries ASSIGNING .
    
            mo_files->add_string(
              iv_extra  = get_raw_text_filename(  )
              iv_ext    = 'txt'
              iv_string = -text ).
    
            CLEAR: -text.
    
          ENDLOOP.
    
        ENDLOOP.
    
        io_xml->add( iv_name = 'SOTS'
                     ig_data = lt_sots ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_splo IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE chgname1 FROM tsp1d INTO rv_user
          WHERE papart = ms_item-obj_name.
        IF sy-subrc <> 0 OR rv_user IS INITIAL.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DELETE FROM tsp1t WHERE papart = ms_item-obj_name. "#EC CI_NOFIRST "#EC CI_SUBRC
        DELETE FROM tsp1d WHERE papart = ms_item-obj_name.    "#EC CI_SUBRC
        DELETE FROM tsp0p WHERE pdpaper = ms_item-obj_name.   "#EC CI_SUBRC
    
        set_default_transport( iv_transport ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_tsp1t TYPE tsp1t,
              ls_tsp1d TYPE tsp1d,
              ls_tsp0p TYPE tsp0p.
    
        io_xml->read( EXPORTING iv_name = 'TSPLT'
                      CHANGING cg_data = ls_tsp1t ).
        io_xml->read( EXPORTING iv_name = 'TSPLD'
                      CHANGING cg_data = ls_tsp1d ).
        io_xml->read( EXPORTING iv_name = 'TSP0P'
                      CHANGING cg_data = ls_tsp0p ).
    
        MODIFY tsp1t FROM ls_tsp1t.                           "#EC CI_SUBRC
        MODIFY tsp1d FROM ls_tsp1d.                           "#EC CI_SUBRC
        MODIFY tsp0p FROM ls_tsp0p.                           "#EC CI_SUBRC
    
        set_default_transport( iv_transport ).
    
        tadir_insert( iv_package ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_papart TYPE tsp1d-papart.
    
        SELECT SINGLE papart INTO lv_papart FROM tsp1d
          WHERE papart = ms_item-obj_name.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_tsp1t TYPE tsp1t,
              ls_tsp1d TYPE tsp1d,
              ls_tsp0p TYPE tsp0p.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        SELECT SINGLE * FROM tsp1t INTO ls_tsp1t
          WHERE papart = ms_item-obj_name
          AND spras = mv_language.            "#EC CI_GENBUFF "#EC CI_SUBRC
        SELECT SINGLE * FROM tsp1d INTO ls_tsp1d
          WHERE papart = ms_item-obj_name.                    "#EC CI_SUBRC
        SELECT SINGLE * FROM tsp0p INTO ls_tsp0p
          WHERE pdpaper = ms_item-obj_name.                   "#EC CI_SUBRC
    
        CLEAR: ls_tsp1d-chgname1,
               ls_tsp1d-chgtstmp1,
               ls_tsp1d-chgsaprel1,
               ls_tsp1d-chgsapsys1.
    
        io_xml->add( iv_name = 'TSPLT'
                     ig_data = ls_tsp1t ).
        io_xml->add( iv_name = 'TSPLD'
                     ig_data = ls_tsp1d ).
        io_xml->add( iv_name = 'TSP0P'
                     ig_data = ls_tsp0p ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sppf IMPLEMENTATION.
    
      METHOD get_generic.
    
        CREATE OBJECT ro_generic
          EXPORTING
            is_item     = ms_item
            iv_language = mv_language.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        rv_user = c_user_unknown. " not stored by SAP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        set_default_transport( iv_transport ).
    
        get_generic( )->delete( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        set_default_transport( iv_transport ).
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        get_generic( )->serialize( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sprx IMPLEMENTATION.
    
      METHOD check_sprx_tadir.
    
        DATA: lt_abap_keys TYPE prx_abapobjects,
              ls_abap_key  LIKE LINE OF lt_abap_keys,
              lx_error     TYPE REF TO cx_proxy_gen_error.
    
        ls_abap_key-object   = mv_object.
        ls_abap_key-obj_name = mv_obj_name.
        APPEND ls_abap_key TO lt_abap_keys.
    
        TRY.
            cl_proxy_utils=>check_sprx_tadir(
              objects = lt_abap_keys
              repair  = abap_true ).
    
          CATCH cx_proxy_gen_error INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        get_object_and_name(
          IMPORTING
            ev_object   = mv_object
            ev_obj_name = mv_obj_name ).
    
      ENDMETHOD.
    
      METHOD delta_handling.
    
        DATA: lo_proxy   TYPE REF TO cl_proxy,
              lt_delta   TYPE sprx_t_delta,
              ls_db_data TYPE sprx_db_data.
    
        "add Delta-Handling to avoid that single objects created without the dependent objects.
        "Thereby the dependent objects will be deleted
        TRY.
            lo_proxy = cl_proxy_fact=>load_by_abap_name(
              object   = mv_object
              obj_name = mv_obj_name ).
    
            lt_delta = lo_proxy->get_delta_all( ).
    
            ls_db_data = cl_proxy_db=>serialize(
              proxy    = lo_proxy
              inactive = abap_false
              delta    = lt_delta ).
    
            et_sproxhdr_new = ls_db_data-sproxhdr.
            et_sproxdat_new = ls_db_data-sproxdat.
    
          CATCH cx_proxy_gen_error.
            "No delta for this object -> create
    
            ii_xml->read(
              EXPORTING
                iv_name = c_proxy-header
              CHANGING
                cg_data = et_sproxhdr_new ).
    
            IF et_sproxhdr_new IS INITIAL.
              zcx_abapgit_exception=>raise( |SPRX - error deserialize: { ms_item-obj_name }| ).
            ENDIF.
    
            ii_xml->read(
              EXPORTING
                iv_name = c_proxy-data
              CHANGING
                cg_data = et_sproxdat_new ).
    
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get_object_and_name.
    
        ev_object   = ms_item-obj_name(4).
        ev_obj_name = ms_item-obj_name+4.
    
      ENDMETHOD.
    
      METHOD load_db.
    
    * method cl_proxy_db=>load_by_abap_name does not exist in lower releases
    
        DATA: lt_packages TYPE prx_t_namespace_package,
              ls_package  LIKE LINE OF lt_packages,
              ls_hdr      TYPE prx_s_proxy_hdr,
              lv_package  TYPE tadir-devclass,
              lt_ids      TYPE prx_ids.
    
        cl_proxy_query=>get_hdr_by_abap_name(
          EXPORTING
            object   = mv_object
            obj_name = mv_obj_name
          IMPORTING
            hdr      = ls_hdr ).
        APPEND ls_hdr-id TO lt_ids.
    
        IF ls_hdr-gen_appl = 'WEBSERVICES'.
          cl_proxy_utils=>get_package(
            EXPORTING
              object   = mv_object
              obj_name = mv_obj_name
            RECEIVING
              rval     = lv_package
            EXCEPTIONS
              OTHERS   = 0 ).
    
          ls_package-namespace = ls_hdr-esr_nspce.
          ls_package-prefix    = ls_hdr-prefix.
          ls_package-package   = lv_package.
          APPEND ls_package TO lt_packages.
        ENDIF.
    
        rs_data = cl_proxy_db=>load(
          inactive               = abap_false
          ids                    = lt_ids
          generating_application = ls_hdr-gen_appl
          packages               = lt_packages ).
    
      ENDMETHOD.
    
      METHOD save.
    
        DATA:
          lt_sproxhdr_old  TYPE sprx_hdr_t,
          lt_sproxdat_old  TYPE sprx_dat_t,
          lt_sproxsvar_old TYPE sprx_svar_t,
          lt_sproxintf_old TYPE sprx_matchintf_t,
          lt_sproxsvar_new TYPE sprx_svar_t,
          lt_sproxintf_new TYPE sprx_matchintf_t.
    
        cl_proxy_data=>db_save(
          sproxhdr_old  = lt_sproxhdr_old
          sproxdat_old  = lt_sproxdat_old
          sproxsvar_old = lt_sproxsvar_old
          sproxintf_old = lt_sproxintf_old
          sproxhdr_new  = it_sproxhdr_new
          sproxdat_new  = it_sproxdat_new
          sproxsvar_new = lt_sproxsvar_new
          sproxintf_new = lt_sproxintf_new ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA lv_changed_by TYPE sproxhdr-changed_by.
    
        rv_user = c_user_unknown.
    
        SELECT SINGLE changed_by
          FROM sproxhdr
          INTO lv_changed_by
          WHERE object = mv_object
          AND obj_name = mv_obj_name
          AND inactive = abap_false.
    
        IF sy-subrc = 0 AND lv_changed_by IS NOT INITIAL.
          rv_user = lv_changed_by.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA:
          lv_object      TYPE sproxhdr-object,
          lv_obj_name    TYPE sproxhdr-obj_name,
          lv_transp_flag TYPE abap_bool,
          lv_return_code TYPE i,
          lt_log         TYPE sprx_log_t.
    
        IF iv_package(1) <> '$'.
          lv_transp_flag = abap_true.
        ENDIF.
    
        get_object_and_name(
          IMPORTING
            ev_object   = lv_object
            ev_obj_name = lv_obj_name ).
    
        TRY.
            CALL METHOD ('CL_PROXY_DATA')=>('DELETE_SINGLE_PROXY')
              EXPORTING
                object           = lv_object
                obj_name         = lv_obj_name
                i_transport      = lv_transp_flag
                suppress_dialogs = abap_true
              CHANGING
                c_return_code    = lv_return_code
                ct_log           = lt_log.
          CATCH cx_root.
            cl_proxy_data=>delete_single_proxy(
              EXPORTING
                object        = lv_object
                obj_name      = lv_obj_name
                i_transport   = lv_transp_flag
              CHANGING
                c_return_code = lv_return_code
                ct_log        = lt_log ).
        ENDTRY.
        IF lv_return_code <> 0.
          zcx_abapgit_exception=>raise( 'SPRX: Error from DELETE_SINGLE_PROXY' ).
        ENDIF.
    
        corr_insert( iv_package ).
    
        tadir_delete( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lt_sproxhdr_new TYPE sprx_hdr_t,
              lt_sproxdat_new TYPE sprx_dat_t.
    
        tadir_insert( iv_package ).
    
        corr_insert( iv_package ).
    
        delta_handling(
          EXPORTING
            ii_xml          = io_xml
          IMPORTING
            et_sproxhdr_new = lt_sproxhdr_new
            et_sproxdat_new = lt_sproxdat_new ).
    
        save(
          it_sproxhdr_new = lt_sproxhdr_new
          it_sproxdat_new = lt_sproxdat_new ).
    
        COMMIT WORK.
    
        check_sprx_tadir( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA lv_status TYPE prx_status.
    
        cl_proxy_data=>db_get_status(
          EXPORTING
            object   = mv_object
            obj_name = mv_obj_name
          IMPORTING
            status   = lv_status ).
    
        rv_bool = boolc( lv_status = if_proxy=>c_state_active OR lv_status = if_proxy=>c_state_inactive ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
    
        DATA lv_status TYPE prx_status.
    
        cl_proxy_data=>db_get_status(
          EXPORTING
            object   = mv_object
            obj_name = mv_obj_name
          IMPORTING
            status   = lv_status ).
    
        rv_active = boolc( lv_status = if_proxy=>c_state_active ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          ls_sprx_db_data TYPE sprx_db_data.
    
        FIELD-SYMBOLS:
           LIKE LINE OF ls_sprx_db_data-sproxhdr,
              LIKE LINE OF ls_sprx_db_data-sproxdat.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        ls_sprx_db_data = load_db( ).
    
        DELETE ls_sprx_db_data-sproxhdr WHERE object <> mv_object OR obj_name <> mv_obj_name.
        DELETE ls_sprx_db_data-sproxdat WHERE object <> mv_object OR obj_name <> mv_obj_name.
        DELETE ls_sprx_db_data-sproxsvar WHERE object <> mv_object OR obj_name <> mv_obj_name.
        DELETE ls_sprx_db_data-sproxpck WHERE object <> mv_object OR obj_name <> mv_obj_name.
        DELETE ls_sprx_db_data-sproxintf WHERE object <> mv_object OR obj_name <> mv_obj_name.
    
        IF lines( ls_sprx_db_data-sproxhdr ) <> 1.
          zcx_abapgit_exception=>raise( |SPRX, no header found, { mv_object }, { mv_obj_name }| ).
        ENDIF.
    
        LOOP AT ls_sprx_db_data-sproxhdr ASSIGNING .
    
          CLEAR:
            -created_by,
            -created_on,
            -changed_by,
            -changed_on.
    
        ENDLOOP.
    
        LOOP AT ls_sprx_db_data-sproxdat ASSIGNING .
    
          CLEAR -warnings.
    
        ENDLOOP.
    
        io_xml->add(
          iv_name = c_proxy-header
          ig_data = ls_sprx_db_data-sproxhdr ).
    
        io_xml->add(
          iv_name = c_proxy-data
          ig_data = ls_sprx_db_data-sproxdat ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sqsc IMPLEMENTATION.
    
      METHOD constructor.
    
        FIELD-SYMBOLS:  TYPE ty_abap_name.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        TRY.
            CREATE OBJECT mo_proxy
              TYPE ('CL_DDIC_WB_DBPROC_PROXY').
    
            ASSIGN ('MO_PROXY->IF_DDIC_WB_DBPROC_PROXY~DBPROXYNAME')
                TO .
            ASSERT sy-subrc = 0.
    
          CATCH cx_root.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
         = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD delete_interface_if_it_exists.
    
        DATA: ls_item      TYPE zif_abapgit_definitions=>ty_item,
              lo_interface TYPE REF TO zcl_abapgit_object_intf.
    
        " The interface is managed by the proxy. If abapGit
        " has created it before we have to delete it. Otherwise
        " if_dbproc_proxy_ui~create will throw errors.
    
        ls_item-obj_name = iv_interface.
        ls_item-obj_type = 'INTF'.
    
        IF /apmg/cl_apm_abapgit_objects=>exists( ls_item ) = abap_true.
    
          CREATE OBJECT lo_interface
            EXPORTING
              is_item        = ls_item
              iv_language    = mv_language
              io_files       = mo_files
              io_i18n_params = mo_i18n_params.
    
          lo_interface->zif_abapgit_object~delete( iv_package   = iv_package
                                                   iv_transport = iv_transport
                                                   ii_log       = ii_log ).
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA lx_error TYPE REF TO cx_root.
    
        TRY.
            CALL METHOD mo_proxy->('IF_DBPROC_PROXY_UI~READ_FROM_SOURCE')
              EXPORTING
                if_version     = 'A'
              IMPORTING
                ef_change_user = rv_user.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lx_error TYPE REF TO cx_root.
    
        TRY.
            CALL METHOD mo_proxy->('IF_DBPROC_PROXY_UI~DELETE')
              EXPORTING
                if_transport_req = iv_transport.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_proxy TYPE ty_proxy,
              lx_error TYPE REF TO cx_root.
    
        io_xml->read(
          EXPORTING
            iv_name = 'SQSC'
          CHANGING
            cg_data = ls_proxy ).
    
        IF zif_abapgit_object~exists( ) = abap_false.
    
          delete_interface_if_it_exists(
            iv_package   = iv_package
            iv_transport = iv_transport
            iv_interface = ls_proxy-header-interface_pool
            ii_log       = ii_log ).
    
          CALL METHOD mo_proxy->('IF_DBPROC_PROXY_UI~CREATE')
            EXPORTING
              if_interface_pool = ls_proxy-header-interface_pool
              if_transport_req  = iv_transport
              if_package        = iv_package
              if_langu          = mv_language.
    
        ENDIF.
    
        TRY.
            CALL METHOD mo_proxy->('IF_DBPROC_PROXY_UI~WRITE_TO_SOURCE')
              EXPORTING
                if_transport_req  = iv_transport
                is_header         = ls_proxy-header
                it_parameter      = ls_proxy-parameters
                it_parameter_type = ls_proxy-parameter_types.
    
            CALL METHOD mo_proxy->('IF_DBPROC_PROXY_UI~WRITE_DESCR')
              EXPORTING
                if_langu = mv_language
                if_descr = ls_proxy-description.
    
            CALL METHOD mo_proxy->('IF_DBPROC_PROXY_UI~ACTIVATE').
    
            tadir_insert( iv_package ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        CALL METHOD mo_proxy->('IF_DBPROC_PROXY_UI~EXISTS')
          RECEIVING
            ef_exists = rv_bool.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by ZCL_ABAPGIT_ADT_LINK=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_proxy TYPE ty_proxy,
              lx_error TYPE REF TO cx_root.
    
        TRY.
            CALL METHOD mo_proxy->('IF_DBPROC_PROXY_UI~READ_FROM_SOURCE')
              EXPORTING
                if_version        = 'A'
              IMPORTING
                es_header         = ls_proxy-header
                et_parameter      = ls_proxy-parameters
                et_parameter_type = ls_proxy-parameter_types.
    
            CALL METHOD mo_proxy->('IF_DBPROC_PROXY_UI~READ_DESCR')
              EXPORTING
                if_langu   = mv_language
                if_version = 'A'
              IMPORTING
                ef_descr   = ls_proxy-description.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        io_xml->add( iv_name = 'SQSC'
                     ig_data = ls_proxy ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_srfc IMPLEMENTATION.
    
      METHOD constructor.
    
        DATA li_srfc_persist TYPE REF TO if_wb_object_persist.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        TRY.
            CREATE OBJECT li_srfc_persist TYPE ('CL_UCONRFC_OBJECT_PERSIST').
          CATCH cx_root.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: li_object_data  TYPE REF TO if_wb_object_data_model,
              li_srfc_persist TYPE REF TO if_wb_object_persist,
              lr_srfc_data    TYPE REF TO data,
              lx_error        TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:  TYPE any,
                              TYPE any.
    
        TRY.
            CREATE DATA lr_srfc_data TYPE ('UCONRFCSERV_COMPLETE').
            ASSIGN lr_srfc_data->* TO .
            ASSERT sy-subrc = 0.
    
            CREATE OBJECT li_srfc_persist TYPE ('CL_UCONRFC_OBJECT_PERSIST').
    
            li_srfc_persist->get(
              EXPORTING
                p_object_key  = |{ ms_item-obj_name }|
                p_version     = 'A'
              CHANGING
                p_object_data = li_object_data ).
    
            li_object_data->get_data( IMPORTING p_data =  ).
    
            ASSIGN COMPONENT 'HEADER-CHANGEDBY' OF STRUCTURE  TO .
            IF sy-subrc = 0 AND  IS NOT INITIAL.
              rv_user = .
            ELSE.
              rv_user = c_user_unknown.
            ENDIF.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: li_srfc_persist TYPE REF TO if_wb_object_persist,
              lx_error        TYPE REF TO cx_root.
    
        TRY.
            CREATE OBJECT li_srfc_persist TYPE ('CL_UCONRFC_OBJECT_PERSIST').
    
            li_srfc_persist->delete( p_object_key = |{ ms_item-obj_name }|
                                     p_version    = 'A' ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: li_srfc_persist TYPE REF TO if_wb_object_persist,
              li_object_data  TYPE REF TO if_wb_object_data_model,
              lr_srfc_data    TYPE REF TO data,
              lx_error        TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:  TYPE any,
                              TYPE any.
    
        TRY.
            CREATE DATA lr_srfc_data TYPE ('UCONRFCSERV_COMPLETE').
            ASSIGN lr_srfc_data->* TO .
            ASSERT sy-subrc = 0.
    
            ASSIGN COMPONENT 'HEADER-CREATEDBY' OF STRUCTURE  TO .
            IF sy-subrc = 0.
               = sy-uname.
            ENDIF.
    
            ASSIGN COMPONENT 'HEADER-CREATEDON' OF STRUCTURE  TO .
            IF sy-subrc = 0.
               = sy-datum.
            ENDIF.
    
            ASSIGN COMPONENT 'HEADER-CREATEDAT' OF STRUCTURE  TO .
            IF sy-subrc = 0.
               = sy-uzeit.
            ENDIF.
    
            io_xml->read(
              EXPORTING
                iv_name = 'SRFC'
              CHANGING
                cg_data =  ).
    
            CREATE OBJECT li_srfc_persist TYPE ('CL_UCONRFC_OBJECT_PERSIST').
            CREATE OBJECT li_object_data TYPE ('CL_UCONRFC_OBJECT_DATA').
    
            li_object_data->set_data(  ).
    
            li_srfc_persist->save( li_object_data ).
    
            tadir_insert( iv_package ).
    
            corr_insert( iv_package ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: li_object_data  TYPE REF TO if_wb_object_data_model,
              li_srfc_persist TYPE REF TO if_wb_object_persist.
    
        TRY.
            CREATE OBJECT li_srfc_persist TYPE ('CL_UCONRFC_OBJECT_PERSIST').
    
            li_srfc_persist->get(
              EXPORTING
                p_object_key  = |{ ms_item-obj_name }|
                p_version     = 'A'
              CHANGING
                p_object_data = li_object_data ).
    
          CATCH cx_root.
            rv_bool = abap_false.
            RETURN.
        ENDTRY.
    
        rv_bool = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: li_object_data  TYPE REF TO if_wb_object_data_model,
              li_srfc_persist TYPE REF TO if_wb_object_persist,
              lr_srfc_data    TYPE REF TO data,
              lx_error        TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:  TYPE any,
                              TYPE any.
    
        TRY.
            CREATE DATA lr_srfc_data TYPE ('UCONRFCSERV_COMPLETE').
            ASSIGN lr_srfc_data->* TO .
            ASSERT sy-subrc = 0.
    
            CREATE OBJECT li_srfc_persist TYPE ('CL_UCONRFC_OBJECT_PERSIST').
    
            li_srfc_persist->get(
              EXPORTING
                p_object_key  = |{ ms_item-obj_name }|
                p_version     = 'A'
              CHANGING
                p_object_data = li_object_data ).
    
            li_object_data->get_data( IMPORTING p_data =  ).
    
            ASSIGN COMPONENT 'HEADER-CREATEDBY' OF STRUCTURE  TO .
            IF sy-subrc = 0.
              CLEAR .
            ENDIF.
    
            ASSIGN COMPONENT 'HEADER-CREATEDON' OF STRUCTURE  TO .
            IF sy-subrc = 0.
              CLEAR .
            ENDIF.
    
            ASSIGN COMPONENT 'HEADER-CREATEDAT' OF STRUCTURE  TO .
            IF sy-subrc = 0.
              CLEAR .
            ENDIF.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        io_xml->add( iv_name = 'SRFC'
                     ig_data =  ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_srvb IMPLEMENTATION.
    
      METHOD clear_field.
    
        FIELD-SYMBOLS:  TYPE data.
    
        ASSIGN COMPONENT iv_fieldname OF STRUCTURE cs_service_binding
               TO .
        ASSERT sy-subrc = 0.
    
        CLEAR: .
    
      ENDMETHOD.
    
      METHOD clear_fields.
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-VERSION'
          CHANGING
            cs_service_binding = cs_service_binding ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-CREATED_AT'
          CHANGING
            cs_service_binding = cs_service_binding ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-CREATED_BY'
          CHANGING
            cs_service_binding = cs_service_binding ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-CHANGED_AT'
          CHANGING
            cs_service_binding = cs_service_binding ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-CHANGED_BY'
          CHANGING
            cs_service_binding = cs_service_binding ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-LANGUAGE'
          CHANGING
            cs_service_binding = cs_service_binding ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-PACKAGE_REF'
          CHANGING
            cs_service_binding = cs_service_binding ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-MASTER_SYSTEM'
          CHANGING
            cs_service_binding = cs_service_binding ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-LINKS'
          CHANGING
            cs_service_binding = cs_service_binding ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-RESPONSIBLE'
          CHANGING
            cs_service_binding = cs_service_binding ).
    
        clear_field(
          EXPORTING
            iv_fieldname       = 'METADATA-MASTER_LANGUAGE'
          CHANGING
            cs_service_binding = cs_service_binding ).
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_service_binding_key = ms_item-obj_name.
    
        TRY.
            CREATE DATA mr_service_binding TYPE ('CL_SRVB_OBJECT_DATA=>TY_OBJECT_DATA').
            CREATE OBJECT mi_persistence TYPE ('CL_SRVB_OBJECT_PERSIST').
    
          CATCH cx_sy_create_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
        mv_is_inactive_supported = is_ai_supported( ).
    
      ENDMETHOD.
    
      METHOD get_object_data.
    
        FIELD-SYMBOLS:
           TYPE uccheck,
                 TYPE any,
                        TYPE data.
    
        ASSIGN mr_service_binding->* TO .
        ASSERT sy-subrc = 0.
    
        io_xml->read(
          EXPORTING
            iv_name = 'SRVB'
          CHANGING
            cg_data =  ).
    
        " We have to set the language explicitly,
        " because otherwise the description isn't stored
        ASSIGN COMPONENT 'METADATA-LANGUAGE' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
         = mv_language.
    
        ASSIGN COMPONENT 'METADATA-ABAP_LANGU_VERSION' OF STRUCTURE  TO .
        IF sy-subrc = 0.
          set_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
    
        CREATE OBJECT ro_object_data TYPE ('CL_SRVB_OBJECT_DATA').
        ro_object_data->set_data( p_data =  ).
    
      ENDMETHOD.
    
      METHOD get_wb_object_operator.
    
        DATA:
          ls_object_type TYPE wbobjtype,
          lx_error       TYPE REF TO cx_root.
    
        IF mo_object_operator IS BOUND.
          ro_object_operator = mo_object_operator.
        ENDIF.
    
        ls_object_type-objtype_tr = 'SRVB'.
        ls_object_type-subtype_wb = 'SVB'.
    
        TRY.
            CALL METHOD ('CL_WB_OBJECT_OPERATOR')=>('CREATE_INSTANCE')
              EXPORTING
                object_type = ls_object_type
                object_key  = mv_service_binding_key
              RECEIVING
                result      = mo_object_operator.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        ro_object_operator = mo_object_operator.
    
      ENDMETHOD.
    
      METHOD is_ai_supported.
        TRY.
            CREATE OBJECT mr_srvb_svrs_config TYPE ('CL_SRVB_SVRS_CONFIG')
              EXPORTING iv_objtype = 'SRVB'.
          CATCH cx_sy_create_error.
            rv_ai_supported = abap_false.
        ENDTRY.
        CALL METHOD mr_srvb_svrs_config->('HAS_INACTIVE_VERSION')
          RECEIVING
            rv_has_inactive = rv_ai_supported.
    
      ENDMETHOD.
    
      METHOD merge_object_data.
    
        DATA:
          lo_object_data        TYPE REF TO object,
          lo_object_data_old    TYPE REF TO if_wb_object_data_model,
          lr_new                TYPE REF TO data,
          lr_old                TYPE REF TO data,
          lo_wb_object_operator TYPE REF TO object.
    
        FIELD-SYMBOLS:
                 TYPE any,
                 TYPE any,
           TYPE any,
           TYPE any.
    
        CREATE OBJECT lo_object_data TYPE ('CL_SRVB_OBJECT_DATA').
        lo_object_data = io_object_data.
    
        CREATE DATA lr_new TYPE ('CL_SRVB_OBJECT_DATA=>TY_OBJECT_DATA').
        ASSIGN lr_new->* TO .
        ASSERT sy-subrc = 0.
    
        CREATE DATA lr_old TYPE ('CL_SRVB_OBJECT_DATA=>TY_OBJECT_DATA').
        ASSIGN lr_old->* TO .
        ASSERT sy-subrc = 0.
    
        CALL METHOD lo_object_data->('IF_WB_OBJECT_DATA_MODEL~GET_DATA')
          EXPORTING
            p_metadata_only  = abap_false
            p_data_selection = 'AL'
          IMPORTING
            p_data           = .
    
        lo_wb_object_operator = get_wb_object_operator( ).
    
        CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
          EXPORTING
            data_selection = 'AL' " if_wb_object_data_selection_co=>c_all_data
          IMPORTING
            eo_object_data = lo_object_data_old.
    
        CALL METHOD lo_object_data_old->('GET_DATA')
          EXPORTING
            p_metadata_only  = abap_false
            p_data_selection = 'AL' " if_wb_object_data_selection_co=>c_all_data
          IMPORTING
            p_data           = .
    
        ASSIGN COMPONENT 'METADATA-DESCRIPTION' OF STRUCTURE  TO .
        ASSIGN COMPONENT 'METADATA-DESCRIPTION' OF STRUCTURE  TO .
         = .
    
        CREATE OBJECT ro_object_data_merged TYPE ('CL_SRVB_OBJECT_DATA').
    
        CALL METHOD ro_object_data_merged->('SET_DATA')
          EXPORTING
            p_data = .
    
      ENDMETHOD.
    
      METHOD publish.
    
        DATA lx_error TYPE REF TO cx_root.
        DATA lr_create_info TYPE REF TO data.
        DATA lo_publishing_config TYPE REF TO object.
    
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE any.
    
        ASSIGN mr_service_binding->* TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN COMPONENT 'PUBLISHED' OF STRUCTURE  TO .
        IF sy-subrc <> 0 OR  <> abap_true.
          RETURN.
        ENDIF.
    
        TRY.
            CREATE DATA lr_create_info TYPE ('/IWFND/IF_V4_PUBLISHING_TYPES=>TY_S_CREATE_GROUP_INFO').
            ASSIGN lr_create_info->* TO .
            ASSERT sy-subrc = 0.
    
            ASSIGN COMPONENT 'LANGUAGE' OF STRUCTURE  TO .
             = mv_language.
            ASSIGN COMPONENT 'DESCRIPTION' OF STRUCTURE  TO .
            ASSIGN COMPONENT 'METADATA-DESCRIPTION' OF STRUCTURE  TO .
             = .
            ASSIGN COMPONENT 'GROUP_ID' OF STRUCTURE  TO .
             = |{ ms_item-obj_name }|.
    
            CALL METHOD ('/IWFND/CL_V4_PUBLISHING_CONFIG')=>('GET_INSTANCE')
              RECEIVING
                ro_publishing_config = lo_publishing_config.
    
            CALL METHOD lo_publishing_config->('PUBLISH_GROUP')
              EXPORTING
                is_create_info      = 
                iv_system_alias     = 'LOCAL'
                iv_suppress_dialog  = abap_true
                iv_do_not_transport = abap_true.
    
            " Do we use OAuth 2.0 scope?
            " CALL METHOD ('/IWFND/CL_V4_COF_FACADE')=>('CREATE_OAUTH2_SCOPE')
            "   EXPORTING
            "     iv_service_group_id = 
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD unpublish.
    
        DATA lo_publishing_config TYPE REF TO object.
    
        " If it's published, we unpublish it
        TRY.
            CALL METHOD ('/IWFND/CL_V4_PUBLISHING_DBA')=>('CHECK_IS_GROUP_PUBLISHED')
              EXPORTING
                iv_group_id = |{ ms_item-obj_name }|.
    
            CALL METHOD ('/IWFND/CL_V4_PUBLISHING_CONFIG')=>('GET_INSTANCE')
              RECEIVING
                ro_publishing_config = lo_publishing_config.
    
            CALL METHOD lo_publishing_config->('DELETE_GROUP')
              EXPORTING
                iv_group_id         = |{ ms_item-obj_name }|
                iv_suppress_dialog  = abap_true
                iv_do_not_transport = abap_true.
    
            " Do we use OAuth 2.0 scope?
            " CALL METHOD ('/IWFND/CL_V4_COF_FACADE')=>('DELETE_OAUTH2_SCOPE')
            "   EXPORTING
            "     iv_service_group_id = |{ ms_item-obj_name }|
    
          CATCH cx_root ##NO_HANDLER.
            " not published i.e. good to go
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA:
          li_object_data_model TYPE REF TO if_wb_object_data_model.
    
        TRY.
            mi_persistence->get(
              EXPORTING
                p_object_key  = mv_service_binding_key
                p_version     = 'A'
              CHANGING
                p_object_data = li_object_data_model ).
    
            rv_user = li_object_data_model->get_changed_by( ).
    
          CATCH cx_swb_exception.
            rv_user = c_user_unknown.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lx_error TYPE REF TO cx_swb_exception.
    
        unpublish( ).
    
        TRY.
            mi_persistence->delete( mv_service_binding_key ).
    
          CATCH cx_swb_exception INTO lx_error.
            CALL FUNCTION 'DEQUEUE_ESWB_EO'
              EXPORTING
                objtype = ms_item-obj_type
                objname = ms_item-obj_name.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA:
          lo_object_data        TYPE REF TO if_wb_object_data_model,
          lx_error              TYPE REF TO cx_root,
          lo_wb_object_operator TYPE REF TO object,
          lo_merged_data_all    TYPE REF TO if_wb_object_data_model,
          lv_version            TYPE r3state.
    
        " To make changes, object must be unpublished
        unpublish( ).
    
        TRY.
            lo_object_data = get_object_data( io_xml ).
            lo_wb_object_operator = get_wb_object_operator( ).
    
            IF mv_is_inactive_supported = abap_true.
              lv_version = 'I'.
            ELSE.
              lv_version = 'A'.
            ENDIF.
    
            tadir_insert( iv_package ).
    
            IF zif_abapgit_object~exists( ) = abap_false.
              "if_wb_adt_plugin_resource_co=>co_sfs_res_category_atomic.
              CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~CREATE')
                EXPORTING
                  io_object_data    = lo_object_data
                  data_selection    = 'AL' "if_wb_object_data_selection_co=>c_all_data
                  version           = lv_version
                  package           = iv_package
                  transport_request = iv_transport.
    
            ELSE.
    
              lo_merged_data_all = merge_object_data( lo_object_data ).
              CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
                EXPORTING
                  io_object_data    = lo_merged_data_all
                  data_selection    = 'AL' "if_wb_object_data_selection_co=>c_all_data
                  version           = lv_version
                  transport_request = iv_transport.
    
            ENDIF.
    
            corr_insert( iv_package ).
    
          CATCH cx_root INTO lx_error.
            CALL FUNCTION 'DEQUEUE_ESWB_EO'
              EXPORTING
                objtype = ms_item-obj_type
                objname = ms_item-obj_name.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        " Publish service binding
        publish( ).
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA lo_object_data TYPE REF TO if_wb_object_data_model.
    
        TRY.
            IF mv_is_inactive_supported = abap_true.
              TRY.
                  mi_persistence->get(
                    EXPORTING
                      p_object_key     = mv_service_binding_key
                      p_version        = 'I'
                      p_data_selection = 'ST'
                    CHANGING
                      p_object_data    = lo_object_data ).
    
                CATCH cx_root.
                  mi_persistence->get(
                    EXPORTING
                      p_object_key     = mv_service_binding_key
                      p_version        = 'A'
                      p_data_selection = 'ST'
                    CHANGING
                      p_object_data    = lo_object_data ).
    
              ENDTRY.
            ELSE.
    
              mi_persistence->get(
                EXPORTING
                  p_object_key     = mv_service_binding_key
                  p_version        = 'A'
                  p_data_selection = 'ST'
                CHANGING
                  p_object_data    = lo_object_data ).
    
            ENDIF.
            rv_bool = boolc( lo_object_data IS NOT INITIAL AND lo_object_data->get_object_key( ) IS NOT INITIAL ).
          CATCH cx_root.
            rv_bool = abap_false.
        ENDTRY.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESWB_EO'
                                                iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          li_object_data_model  TYPE REF TO if_wb_object_data_model,
          li_wb_object_operator TYPE REF TO object,
          lx_error              TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:
           TYPE uccheck,
                 TYPE any.
    
        ASSIGN mr_service_binding->* TO .
        ASSERT sy-subrc = 0.
    
        TRY.
            li_wb_object_operator = get_wb_object_operator( ).
    
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
              EXPORTING
                version        = 'A'
                data_selection = 'AL'
              IMPORTING
                eo_object_data = li_object_data_model.
    
            li_object_data_model->get_data( IMPORTING p_data =  ).
    
            clear_fields( CHANGING cs_service_binding =  ).
    
            ASSIGN COMPONENT 'METADATA-ABAP_LANGU_VERSION'
              OF STRUCTURE  TO .
            IF sy-subrc = 0.
              clear_abap_language_version( CHANGING cv_abap_language_version =  ).
            ENDIF.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        io_xml->add(
          iv_name = 'SRVB'
          ig_data =  ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_srvd IMPLEMENTATION.
    
      METHOD clear_field.
    
        FIELD-SYMBOLS:  TYPE data.
    
        ASSIGN COMPONENT iv_fieldname OF STRUCTURE cs_metadata TO .
        IF sy-subrc = 0.
          CLEAR: .
        ENDIF.
    
      ENDMETHOD.
    
      METHOD clear_fields.
    
        clear_field(
          EXPORTING
            iv_fieldname = 'VERSION'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CREATED_AT'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CREATED_BY'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CHANGED_AT'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'CHANGED_BY'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'RESPONSIBLE'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'PACKAGE_REF'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'MASTER_SYSTEM'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'DT_UUID'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'ABAP_LANGUAGE_VERSION'
          CHANGING
            cs_metadata  = cs_metadata ).
        clear_field(
          EXPORTING
            iv_fieldname = 'ABAP_LANGU_VERSION'
          CHANGING
            cs_metadata  = cs_metadata ).
    
        clear_field(
          EXPORTING
            iv_fieldname = 'LINKS'
          CHANGING
            cs_metadata  = cs_metadata ).
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_service_definition_key = ms_item-obj_name.
    
        TRY.
            CREATE DATA mr_service_definition TYPE ('CL_SRVD_WB_OBJECT_DATA=>TY_SRVD_OBJECT_DATA').
    
          CATCH cx_sy_create_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get_object_data.
    
        DATA:
          lr_metadata TYPE REF TO data,
          lr_data     TYPE REF TO data.
    
        FIELD-SYMBOLS:
           TYPE uccheck,
                   TYPE any,
                        TYPE any,
                          TYPE any,
                            TYPE any.
    
        CREATE DATA lr_data TYPE ('CL_SRVD_WB_OBJECT_DATA=>TY_SRVD_OBJECT_DATA').
        ASSIGN lr_data->* TO .
        ASSERT sy-subrc = 0.
    
        ASSIGN COMPONENT 'METADATA' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
    
        CREATE DATA lr_metadata  TYPE ('CL_SRVD_WB_OBJECT_DATA=>TY_METADATA_EXTENDED').
        ASSIGN lr_metadata->* TO .
        ASSERT sy-subrc = 0.
    
        io_xml->read(
          EXPORTING
            iv_name = c_xml_parent_name
          CHANGING
            cg_data =  ).
    
         = .
    
        ASSIGN COMPONENT 'ABAP_LANGU_VERSION' OF STRUCTURE  TO .
        IF sy-subrc = 0.
          set_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
    
        ASSIGN COMPONENT 'CONTENT-SOURCE' OF STRUCTURE  TO .
        ASSERT sy-subrc = 0.
    
         = mo_files->read_string( c_source_file ).
        IF  IS INITIAL.
           = mo_files->read_string( 'assrvd' ).
        ENDIF.
    
        CREATE OBJECT ro_object_data TYPE ('CL_SRVD_WB_OBJECT_DATA').
        ro_object_data->set_data( p_data =  ).
    
      ENDMETHOD.
    
      METHOD get_wb_object_operator.
    
        DATA:
          ls_object_type TYPE wbobjtype,
          lx_error       TYPE REF TO cx_root.
    
        IF mo_object_operator IS BOUND.
          ro_object_operator = mo_object_operator.
        ENDIF.
    
        ls_object_type-objtype_tr = 'SRVD'.
        ls_object_type-subtype_wb = 'SRV'.
    
        TRY.
            CALL METHOD ('CL_WB_OBJECT_OPERATOR')=>('CREATE_INSTANCE')
              EXPORTING
                object_type = ls_object_type
                object_key  = mv_service_definition_key
              RECEIVING
                result      = mo_object_operator.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        ro_object_operator = mo_object_operator.
    
      ENDMETHOD.
    
      METHOD merge_object_data.
    
        DATA:
          lo_object_data        TYPE REF TO object,
          lo_object_data_old    TYPE REF TO if_wb_object_data_model,
          lr_new                TYPE REF TO data,
          lr_old                TYPE REF TO data,
          lo_wb_object_operator TYPE REF TO object.
    
        FIELD-SYMBOLS:
                 TYPE any,
                 TYPE any,
           TYPE any,
           TYPE any.
    
        CREATE OBJECT lo_object_data TYPE ('CL_SRVD_WB_OBJECT_DATA').
        lo_object_data = io_object_data.
    
        CREATE DATA lr_new TYPE ('CL_SRVD_WB_OBJECT_DATA=>TY_SRVD_OBJECT_DATA').
        ASSIGN lr_new->* TO .
        ASSERT sy-subrc = 0.
    
        CREATE DATA lr_old TYPE ('CL_SRVD_WB_OBJECT_DATA=>TY_SRVD_OBJECT_DATA').
        ASSIGN lr_old->* TO .
        ASSERT sy-subrc = 0.
    
        CALL METHOD lo_object_data->('IF_WB_OBJECT_DATA_MODEL~GET_DATA')
          EXPORTING
            p_metadata_only  = abap_false
            p_data_selection = 'AL'
          IMPORTING
            p_data           = .
    
        lo_wb_object_operator = get_wb_object_operator( ).
    
        CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
          EXPORTING
            data_selection = 'AL' " if_wb_object_data_selection_co=>c_all_data
          IMPORTING
            eo_object_data = lo_object_data_old.
    
        CALL METHOD lo_object_data_old->('GET_DATA')
          EXPORTING
            p_metadata_only  = abap_false
            p_data_selection = 'AL' " if_wb_object_data_selection_co=>c_all_data
          IMPORTING
            p_data           = .
    
        ASSIGN COMPONENT 'METADATA-DESCRIPTION' OF STRUCTURE  TO .
        ASSIGN COMPONENT 'METADATA-DESCRIPTION' OF STRUCTURE  TO .
         = .
    
        ASSIGN COMPONENT 'CONTENT-SOURCE' OF STRUCTURE  TO .
        ASSIGN COMPONENT 'CONTENT-SOURCE' OF STRUCTURE  TO .
         = .
    
        CREATE OBJECT ro_object_data_merged TYPE ('CL_SRVD_WB_OBJECT_DATA').
    
        CALL METHOD ro_object_data_merged->('SET_DATA')
          EXPORTING
            p_data = .
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA:
          li_object_data_model  TYPE REF TO if_wb_object_data_model,
          li_wb_object_operator TYPE REF TO object,
          lx_error              TYPE REF TO cx_root.
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        TRY.
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
              IMPORTING
                eo_object_data = li_object_data_model.
    
            rv_user = li_object_data_model->get_changed_by( ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
        DATA:
          lx_error              TYPE REF TO cx_root,
          li_wb_object_operator TYPE REF TO object.
    
        li_wb_object_operator = get_wb_object_operator( ).
    
        TRY.
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~DELETE')
              EXPORTING
                transport_request = iv_transport.
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA:
          lo_object_data        TYPE REF TO if_wb_object_data_model,
          lx_error              TYPE REF TO cx_root,
          lo_wb_object_operator TYPE REF TO object,
          lo_merged_data_all    TYPE REF TO if_wb_object_data_model,
          lo_merged_data_prop   TYPE REF TO if_wb_object_data_model,
          lo_merged_data_cont   TYPE REF TO if_wb_object_data_model,
          lr_wbobjtype          TYPE REF TO data,
          lr_category           TYPE REF TO data.
    
        FIELD-SYMBOLS:
           TYPE any,
            TYPE any,
               TYPE any.
    
        TRY.
            lo_object_data = get_object_data( io_xml ).
            lo_wb_object_operator = get_wb_object_operator( ).
    
            CREATE DATA lr_wbobjtype TYPE ('WBOBJTYPE').
            ASSIGN lr_wbobjtype->* TO .
            ASSIGN COMPONENT 'OBJTYPE_TR' OF STRUCTURE  TO .
             = 'SRVD'.
            ASSIGN COMPONENT 'SUBTYPE_WB' OF STRUCTURE  TO .
             = 'SRV'.
    
            CREATE DATA lr_category TYPE ('WBADT_RESOURCE_CATEGORY').
            ASSIGN lr_category->* TO .
    
            CALL METHOD ('CL_BLUE_WB_UTILITY')=>('GET_RESOURCE_CATEGORY')
              EXPORTING
                is_object_type = 
              RECEIVING
                result         = .
    
            lo_wb_object_operator = get_wb_object_operator( ).
    
            tadir_insert( iv_package ).
    
            " exists() can return false even when a ghost WB entry remains
            " (e.g. after incomplete delete/activation). In that case CREATE
            " throws CX_WB_OBJECT_ALREADY_EXISTS and we fall through to UPDATE.
            TRY.
                IF zif_abapgit_object~exists( ) = abap_false.
                  CASE .
                    WHEN '1'. "if_wb_adt_plugin_resource_co=>co_sfs_res_category_atomic.
                      CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~CREATE')
                        EXPORTING
                          io_object_data    = lo_object_data
                          data_selection    = 'AL' "if_wb_object_data_selection_co=>c_all_data
                          version           = 'I' "swbm_version_inactive
                          package           = iv_package
                          transport_request = iv_transport.
                    WHEN '2'. "if_wb_adt_plugin_resource_co=>co_sfs_res_category_compound_s.
                      CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~CREATE')
                        EXPORTING
                          io_object_data    = lo_object_data
                          data_selection    = 'P' "if_wb_object_data_selection_co=>c_properties
                          version           = 'I' "swbm_version_inactive
                          package           = iv_package
                          transport_request = iv_transport.
                      CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
                        EXPORTING
                          io_object_data    = lo_object_data
                          data_selection    = 'D' "if_wb_object_data_selection_co=>c_data_content
                          version           = 'I' "swbm_version_inactive
                          transport_request = iv_transport.
                    WHEN OTHERS.
                      zcx_abapgit_exception=>raise( |Category '{  }' not supported| ).
                  ENDCASE.
                ELSE.
                  RAISE EXCEPTION TYPE cx_wb_object_already_exists.
                ENDIF.
              CATCH cx_wb_object_already_exists.
                CASE .
                  WHEN '1'. "if_wb_adt_plugin_resource_co=>co_sfs_res_category_atomic.
                    lo_merged_data_all = merge_object_data( lo_object_data ).
                    CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
                      EXPORTING
                        io_object_data    = lo_merged_data_all
                        data_selection    = 'AL' "if_wb_object_data_selection_co=>c_all_data
                        version           = 'I' "swbm_version_inactive
                        transport_request = iv_transport.
                  WHEN '2'. "if_wb_adt_plugin_resource_co=>co_sfs_res_category_compound_s.
                    lo_merged_data_prop = merge_object_data( lo_object_data ).
                    lo_merged_data_cont = merge_object_data( lo_object_data ).
                    CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
                      EXPORTING
                        io_object_data    = lo_merged_data_prop
                        data_selection    = 'P' "if_wb_object_data_selection_co=>c_properties
                        version           = 'I' "swbm_version_inactive
                        transport_request = iv_transport.
                    CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
                      EXPORTING
                        io_object_data    = lo_merged_data_cont
                        data_selection    = 'D' "if_wb_object_data_selection_co=>c_data_content
                        version           = 'I' "swbm_version_inactive
                        transport_request = iv_transport.
                  WHEN OTHERS.
                    zcx_abapgit_exception=>raise( |Category '{  }' not supported| ).
                ENDCASE.
            ENDTRY.
    
            corr_insert( iv_package ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA lo_object_data TYPE REF TO if_wb_object_data_model.
        DATA lo_wb_object_operator TYPE REF TO object.
    
        TRY.
            lo_wb_object_operator = get_wb_object_operator( ).
            CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
              EXPORTING
                data_selection = 'P'
                version        = 'A'
              IMPORTING
                eo_object_data = lo_object_data.
            rv_bool = boolc( lo_object_data IS NOT INITIAL AND lo_object_data->get_object_key( ) IS NOT INITIAL ).
    
            IF rv_bool = abap_false.
              CALL METHOD lo_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
                EXPORTING
                  data_selection = 'P'
                  version        = 'I'
                IMPORTING
                  eo_object_data = lo_object_data.
              rv_bool = boolc( lo_object_data IS NOT INITIAL AND lo_object_data->get_object_key( ) IS NOT INITIAL ).
            ENDIF.
          CATCH cx_root.
            rv_bool = abap_false.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESWB_EO'
                                                iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          li_object_data_model  TYPE REF TO if_wb_object_data_model,
          li_wb_object_operator TYPE REF TO object,
          lx_error              TYPE REF TO cx_root,
          lv_source             TYPE string.
    
        FIELD-SYMBOLS:
           TYPE uccheck,
              TYPE any,
                        TYPE any,
                          TYPE string.
    
        ASSIGN mr_service_definition->* TO .
        ASSERT sy-subrc = 0.
    
        TRY.
            li_wb_object_operator = get_wb_object_operator( ).
    
            CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
              EXPORTING
                version        = 'A'
                data_selection = 'AL'
              IMPORTING
                "data           = 
                eo_object_data = li_object_data_model.
    
            CALL METHOD li_object_data_model->('GET_DATA')
              IMPORTING
                p_data = .
    
            ASSIGN COMPONENT 'METADATA' OF STRUCTURE  TO .
            ASSERT sy-subrc = 0.
    
            clear_fields( CHANGING cs_metadata =  ).
    
            ASSIGN COMPONENT 'ABAP_LANGU_VERSION' OF STRUCTURE  TO .
            IF sy-subrc = 0.
              clear_abap_language_version( CHANGING cv_abap_language_version =  ).
            ENDIF.
    
            ASSIGN COMPONENT 'CONTENT-SOURCE' OF STRUCTURE  TO .
            ASSERT sy-subrc = 0.
            lv_source = .
    
            io_xml->add(
              iv_name = c_xml_parent_name
              ig_data =  ).
    
            mo_files->add_string(
              iv_ext    = c_source_file
              iv_string = lv_source ).
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ssfo IMPLEMENTATION.
    
      METHOD deserialize_sources.
    
        DATA:
          lv_extra   TYPE string,
          ls_abap    TYPE abaptxt255,
          lt_abap    TYPE abaptxt255_tab,
          li_node    TYPE REF TO if_ixml_node,
          li_ixml    TYPE REF TO if_ixml,
          li_xml_doc TYPE REF TO if_ixml_document.
    
        li_ixml = cl_ixml=>create( ).
        li_xml_doc = li_ixml->create_document( ).
    
        " Old format
        lv_extra = ii_node->get_value( ).
        IF lv_extra NS c_prefix.
          RETURN.
        ENDIF.
    
        " New format
        lv_extra = lv_extra+5(*).
    
        lt_abap = mo_files->read_abap( iv_extra = lv_extra ).
    
        ii_node->set_value( '' ).
        LOOP AT lt_abap INTO ls_abap.
          li_node = li_xml_doc->create_element( 'item' ).
          li_node->set_value( |{ ls_abap-line }| ).
          ii_node->append_child( li_node ).
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD fix_ids.
    
        " makes sure ID and IDREF values are the same values for each serialization run
        " the standard code has a counter that keeps increasing values.
        "
        " It is important that IDs and IDREFs which are the same before the fix
        " are also the same after the fix.
    
        TYPES:
          BEGIN OF ty_id_mapping,
            old TYPE string,
            new TYPE string,
          END OF ty_id_mapping,
          ty_id_mappings TYPE HASHED TABLE OF ty_id_mapping
                              WITH UNIQUE KEY old.
    
        DATA: lv_name       TYPE string,
              li_idref      TYPE REF TO if_ixml_node,
              li_node       TYPE REF TO if_ixml_node,
              li_attr       TYPE REF TO if_ixml_named_node_map,
              li_iterator   TYPE REF TO if_ixml_node_iterator,
              lt_id_mapping TYPE ty_id_mappings,
              ls_id_mapping LIKE LINE OF lt_id_mapping.
    
        li_iterator = ii_xml_doc->create_iterator( ).
        li_node = li_iterator->get_next( ).
        WHILE NOT li_node IS INITIAL.
          lv_name = li_node->get_name( ).
          IF lv_name = 'NODE' OR lv_name = 'WINDOW'.
            li_idref = li_node->get_attributes( )->get_named_item( 'IDREF' ).
            IF li_idref IS BOUND.
    
              ls_id_mapping-old = li_idref->get_value( ).
              READ TABLE lt_id_mapping WITH KEY old = ls_id_mapping-old
                                       INTO ls_id_mapping.
              IF sy-subrc <> 0.
                lv_name = lines( lt_id_mapping ) + 1.
                ls_id_mapping-new = condense( lv_name ).
                INSERT ls_id_mapping INTO TABLE lt_id_mapping.
              ENDIF.
    
              li_idref->set_value( |{ ls_id_mapping-new }| ).
            ENDIF.
          ENDIF.
          li_node = li_iterator->get_next( ).
        ENDWHILE.
    
        li_iterator = ii_xml_doc->create_iterator( ).
        li_node = li_iterator->get_next( ).
        WHILE NOT li_node IS INITIAL.
          lv_name = li_node->get_name( ).
          IF lv_name = 'NODE' OR lv_name = 'WINDOW'.
            li_idref = li_node->get_attributes( )->get_named_item( 'ID' ).
            IF li_idref IS BOUND.
    
              ls_id_mapping-old = li_idref->get_value( ).
              READ TABLE lt_id_mapping WITH KEY old = ls_id_mapping-old
                                       INTO ls_id_mapping.
              IF sy-subrc = 0.
                li_idref->set_value( |{ ls_id_mapping-new }| ).
              ELSE.
                li_attr = li_node->get_attributes( ).
                li_attr->remove_named_item( 'ID' ).
              ENDIF.
    
            ENDIF.
          ENDIF.
          li_node = li_iterator->get_next( ).
        ENDWHILE.
    
      ENDMETHOD.
    
      METHOD get_hash_for_path.
    
        DATA:
          lv_name  TYPE string,
          lv_path  TYPE string,
          li_node  TYPE REF TO if_ixml_node,
          li_name  TYPE REF TO if_ixml_node,
          li_iname TYPE REF TO if_ixml_node.
    
        li_node = ii_node->get_parent( ).
        WHILE NOT li_node IS INITIAL.
          lv_name = li_node->get_name( ).
          IF ( lv_name = 'CODE' OR lv_name = 'WINDOW' OR lv_name = 'PAGE' )
            AND li_node->get_namespace_prefix( ) IS NOT INITIAL.
            li_name  = li_node->get_first_child( ).
            li_iname = li_node->get_first_child( ).
            lv_name  = lv_name && ':' && li_iname->get_value( ).
          ENDIF.
          lv_path = lv_name && '/' && lv_path.
          li_node = li_node->get_parent( ).
        ENDWHILE.
    
        rv_hash = substring(
          val = zcl_abapgit_hash=>sha1_string( lv_path )
          len = 8 ).
    
      ENDMETHOD.
    
      METHOD get_range_node_codes.
    
        DATA: ls_range_node_code TYPE LINE OF ty_string_range.
    
        IF gt_range_node_codes IS INITIAL.
          ls_range_node_code-sign   = 'I'.
          ls_range_node_code-option = 'EQ'.
          ls_range_node_code-low    = 'CODE'.
          INSERT ls_range_node_code INTO TABLE gt_range_node_codes.
          ls_range_node_code-low    = 'GTYPES'.
          INSERT ls_range_node_code INTO TABLE gt_range_node_codes.
          ls_range_node_code-low    = 'GCODING'.
          INSERT ls_range_node_code INTO TABLE gt_range_node_codes.
          ls_range_node_code-low    = 'FCODING'.
          INSERT ls_range_node_code INTO TABLE gt_range_node_codes.
        ENDIF.
    
        rt_range_node_codes = gt_range_node_codes.
    
      ENDMETHOD.
    
      METHOD serialize_sources.
    
        DATA:
          lv_extra    TYPE string,
          ls_abap     TYPE abaptxt255,
          lt_abap     TYPE abaptxt255_tab,
          li_node     TYPE REF TO if_ixml_node,
          li_iterator TYPE REF TO if_ixml_node_iterator.
    
        " Store code as separate ABAP files instead of XML
        lv_extra    = to_lower( ii_node->get_name( ) ).
        li_iterator = ii_node->get_children( )->create_iterator( ).
        li_node     = li_iterator->get_next( ).
        WHILE NOT li_node IS INITIAL.
          ls_abap-line = li_node->get_value( ).
          INSERT ls_abap INTO TABLE lt_abap.
    
          li_node = li_iterator->get_next( ).
        ENDWHILE.
    
        " For CODE sections, get full path and hash it
        IF lv_extra = 'code'.
          lv_extra = get_hash_for_path( ii_node ).
        ENDIF.
    
        mo_files->add_abap(
          iv_extra = lv_extra
          it_abap  = lt_abap ).
    
        ii_node->set_value( c_prefix && lv_extra ).
    
      ENDMETHOD.
    
      METHOD sort_texts.
    
        DATA: li_node      TYPE REF TO if_ixml_node,
              li_item      TYPE REF TO if_ixml_node,
              li_field     TYPE REF TO if_ixml_node,
              li_item_list TYPE REF TO if_ixml_node_list,
              li_iterator  TYPE REF TO if_ixml_node_iterator,
              li_items     TYPE REF TO if_ixml_node_iterator,
              lv_index     TYPE i,
              lv_field     TYPE fieldname,
              ls_item      TYPE stxfobjt,
              lt_items     TYPE STANDARD TABLE OF stxfobjt.
    
        FIELD-SYMBOLS  TYPE any.
    
        li_iterator = ii_xml_doc->create_iterator( ).
        li_node = li_iterator->get_next( ).
        WHILE NOT li_node IS INITIAL.
          IF li_node->get_name( ) = 'T_CAPTION'.
    
            " Read all records for T_CAPTION
            CLEAR lt_items.
            li_item_list = li_node->get_children( ).
            li_items = li_item_list->create_iterator( ).
            DO.
              li_item = li_items->get_next( ).
              IF li_item IS INITIAL.
                EXIT.
              ENDIF.
              CLEAR ls_item.
              li_field = li_item->get_first_child( ).
              WHILE NOT li_field IS INITIAL.
                lv_field = li_field->get_name( ).
                ASSIGN COMPONENT lv_field OF STRUCTURE ls_item TO .
                ASSERT sy-subrc = 0.
                 = li_field->get_value( ).
                li_field = li_field->get_next( ).
              ENDWHILE.
              INSERT ls_item INTO TABLE lt_items.
            ENDDO.
    
            SORT lt_items.
    
            " Write all records back after sorting
            lv_index = 1.
            li_items = li_item_list->create_iterator( ).
            DO.
              li_item = li_items->get_next( ).
              IF li_item IS INITIAL.
                EXIT.
              ENDIF.
              READ TABLE lt_items INTO ls_item INDEX lv_index.
              li_field = li_item->get_first_child( ).
              WHILE NOT li_field IS INITIAL.
                lv_field = li_field->get_name( ).
                ASSIGN COMPONENT lv_field OF STRUCTURE ls_item TO .
                ASSERT sy-subrc = 0.
                li_field->set_value( |{  }| ).
                li_field = li_field->get_next( ).
              ENDWHILE.
    
    * guess this can only happen for CAPTION field, as other are key fields
    * always add the empty values or they will cause diffs
              IF lv_field <> 'CAPTION'.
                ii_xml_doc->create_simple_element(
                  name   = 'CAPTION'
                  value  = |{ ls_item-caption }|
                  parent = li_item ).
              ENDIF.
    
              lv_index = lv_index + 1.
            ENDDO.
    
          ENDIF.
          li_node = li_iterator->get_next( ).
        ENDWHILE.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE lastuser FROM stxfadm INTO rv_user
          WHERE formname = ms_item-obj_name.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_formname TYPE tdsfname.
    
        lv_formname = ms_item-obj_name.
    
        CALL FUNCTION 'FB_DELETE_FORM'
          EXPORTING
            i_formname            = lv_formname
            i_with_dialog         = abap_false
            i_with_confirm_dialog = abap_false
          EXCEPTIONS
            no_form               = 1
            OTHERS                = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    * see function module FB_UPLOAD_FORM
    
        DATA:
          li_node     TYPE REF TO if_ixml_node,
          lv_formname TYPE tdsfname,
          lv_name     TYPE string,
          li_iterator TYPE REF TO if_ixml_node_iterator,
          lo_sf       TYPE REF TO cl_ssf_fb_smart_form,
          lo_res      TYPE REF TO cl_ssf_fb_smart_form,
          lx_error    TYPE REF TO cx_ssf_fb,
          lv_text     TYPE string.
    
        CREATE OBJECT lo_sf.
    
    * set "created by" and "changed by" to current user
        li_iterator = io_xml->get_raw( )->get_root_element( )->create_iterator( ).
        li_node = li_iterator->get_next( ).
        WHILE NOT li_node IS INITIAL.
          lv_name = li_node->get_name( ).
          CASE lv_name.
            WHEN 'LASTDATE'.
              li_node->set_value( sy-datum(4) && '-' && sy-datum+4(2) && '-' && sy-datum+6(2) ).
            WHEN 'LASTTIME'.
              li_node->set_value( sy-uzeit(2) && ':' && sy-uzeit+2(2) && ':' && sy-uzeit+4(2) ).
            WHEN 'FIRSTUSER' OR 'LASTUSER'.
              li_node->set_value( sy-uname && '' ).
    
          ENDCASE.
    
          IF lv_name IN get_range_node_codes( ) AND li_node->get_namespace_prefix( ) IS INITIAL.
            deserialize_sources( li_node ).
          ENDIF.
    
          li_node = li_iterator->get_next( ).
        ENDWHILE.
    
        tadir_insert( iv_package ).
    
        lv_formname = ms_item-obj_name.
    
        TRY.
            lo_sf->enqueue( suppress_corr_check = space
                            master_language     = mv_language
                            mode                = 'INSERT'
                            formname            = lv_formname ).
    
            lo_sf->xml_upload( EXPORTING dom      = io_xml->get_raw( )->get_root_element( )
                                         formname = lv_formname
                                         language = mv_language
                               CHANGING  sform    = lo_res ).
    
            lo_res->store( im_formname = lo_res->header-formname
                           im_language = mv_language
                           im_active   = abap_true ).
    
            lo_sf->dequeue( lv_formname ).
    
          CATCH cx_ssf_fb INTO lx_error.
            lv_text = lx_error->get_text( ).
            zcx_abapgit_exception=>raise( |{ ms_item-obj_type } { ms_item-obj_name }: { lv_text } | ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_formname TYPE stxfadm-formname.
    
        SELECT SINGLE formname FROM stxfadm INTO lv_formname
          WHERE formname = ms_item-obj_name.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
    
        DATA: lv_ssfo_formname TYPE tdsfname.
        DATA lv_inactive TYPE abap_bool.
    
        lv_ssfo_formname = ms_item-obj_name.
    
        CALL FUNCTION 'SSF_STATUS_INFO'
          EXPORTING
            i_formname = lv_ssfo_formname
          IMPORTING
            o_inactive = lv_inactive.
    
        rv_active = boolc( lv_inactive = abap_false ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'E_SMFORM'
                                                iv_argument    = |{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: lt_bdcdata  TYPE TABLE OF bdcdata,
              lv_formtype TYPE stxfadm-formtype.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_bdcdata.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -program  = 'SAPMSSFO'.
        -dynpro   = '0100'.
        -dynbegin = abap_true.
    
        SELECT SINGLE formtype FROM stxfadm INTO lv_formtype
               WHERE formname = ms_item-obj_name.
    
        IF lv_formtype = cssf_formtype_text.
    
          APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
          -fnam = 'RB_TX'.
          -fval = abap_true.
    
          APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
          -fnam = 'BDC_OKCODE'.
          -fval = '=RB'.
    
          APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
          -program  = 'SAPMSSFO'.
          -dynpro   = '0100'.
          -dynbegin = abap_true.
    
          APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
          -fnam = 'SSFSCREEN-TNAME'.
          -fval = ms_item-obj_name.
    
        ELSE.
    
          APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
          -fnam = 'SSFSCREEN-FNAME'.
          -fval = ms_item-obj_name.
    
        ENDIF.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'BDC_OKCODE'.
        -fval = '=DISPLAY'.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'SMARTFORMS'
          it_bdcdata = lt_bdcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    * see function module FB_DOWNLOAD_FORM
    
        DATA: lo_sf       TYPE REF TO cl_ssf_fb_smart_form,
              lv_name     TYPE string,
              li_node     TYPE REF TO if_ixml_node,
              li_element  TYPE REF TO if_ixml_element,
              li_iterator TYPE REF TO if_ixml_node_iterator,
              lv_formname TYPE tdsfname,
              li_ixml     TYPE REF TO if_ixml,
              li_xml_doc  TYPE REF TO if_ixml_document.
    
        li_ixml = cl_ixml=>create( ).
        li_xml_doc = li_ixml->create_document( ).
    
        CREATE OBJECT lo_sf.
        lv_formname = ms_item-obj_name. " convert type
        TRY.
            lo_sf->load( im_formname = lv_formname
                         im_language = '' ).
          CATCH cx_ssf_fb.
    * the smartform is not present in system, or other error occurred
            RETURN.
        ENDTRY.
    
        lo_sf->xml_download( EXPORTING parent   = li_xml_doc
                             CHANGING  document = li_xml_doc ).
    
        li_iterator = li_xml_doc->create_iterator( ).
        li_node = li_iterator->get_next( ).
        WHILE NOT li_node IS INITIAL.
    
          lv_name = li_node->get_name( ).
          IF lv_name = 'DEVCLASS'
              OR lv_name = 'LASTDATE'
              OR lv_name = 'LASTTIME'.
            li_node->set_value( '' ).
          ENDIF.
          IF lv_name = 'FIRSTUSER'
              OR lv_name = 'LASTUSER'.
            li_node->set_value( 'DUMMY' ).
          ENDIF.
    
          IF lv_name IN get_range_node_codes( ) AND li_node->get_namespace_prefix( ) IS INITIAL.
            serialize_sources( li_node ).
          ENDIF.
    
          li_node = li_iterator->get_next( ).
        ENDWHILE.
    
        fix_ids( li_xml_doc ).
    
        sort_texts( li_xml_doc ).
    
        li_element = li_xml_doc->get_root_element( ).
        li_element->set_attribute(
          name      = 'sf'
          namespace = 'xmlns'
          value     = 'urn:sap-com:SmartForms:2000:internal-structure' ).
        li_element->set_attribute(
          name  = 'xmlns'
          value = 'urn:sap-com:sdixml-ifr:2000' ).
    
        io_xml->set_raw( li_xml_doc->get_root_element( ) ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ssst IMPLEMENTATION.
    
      METHOD validate_font.
    
        DATA: lv_tdfamily TYPE tfo01-tdfamily.
    
        SELECT SINGLE tdfamily FROM tfo01 INTO lv_tdfamily
          WHERE tdfamily = iv_tdfamily.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'Font family not found' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE lastuser FROM stxsadm INTO rv_user
          WHERE stylename = ms_item-obj_name.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_stylename TYPE tdssname.
    
        lv_stylename = ms_item-obj_name.
    
        CALL FUNCTION 'SSF_DELETE_STYLE'
          EXPORTING
            i_stylename           = lv_stylename
            i_with_dialog         = abap_false
            i_with_confirm_dialog = abap_false
          EXCEPTIONS
            no_name               = 1
            no_style              = 2
            style_locked          = 3
            cancelled             = 4
            no_access_permission  = 5
            illegal_language      = 6
            OTHERS                = 7.
        IF sy-subrc <> 0 AND sy-subrc <> 2.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    * see fm SSF_UPLOAD_STYLE
    
        DATA: ls_header     TYPE ssfcats,
              ls_new_header TYPE ssfcats,
              lt_paragraphs TYPE TABLE OF ssfparas,
              lt_strings    TYPE TABLE OF ssfstrings,
              lt_tabstops   TYPE TABLE OF stxstab.
    
        FIELD-SYMBOLS:  TYPE spras.
    
        io_xml->read( EXPORTING iv_name = 'HEADER'
                      CHANGING cg_data = ls_header ).
        io_xml->read( EXPORTING iv_name = 'SSFPARAS'
                      CHANGING cg_data = lt_paragraphs ).
        io_xml->read( EXPORTING iv_name = 'SSFSTRINGS'
                      CHANGING cg_data = lt_strings ).
        io_xml->read( EXPORTING iv_name = 'STXSTAB'
                      CHANGING cg_data = lt_tabstops ).
    
        validate_font( ls_header-tdfamily ).
    
        CALL FUNCTION 'SSF_READ_STYLE' "Just load FG
          EXPORTING
            i_style_name        = ls_header-stylename
            i_style_active_flag = 'A'
          EXCEPTIONS
            OTHERS              = 0.
    
        set_default_package( iv_package ).
        ASSIGN ('(SAPLSTXBS)MASTER_LANGUAGE') TO .
        IF sy-subrc = 0.
           = ls_header-masterlang.
        ENDIF.
    
        tadir_insert( iv_package ).
    
        CALL FUNCTION 'SSF_SAVE_STYLE'
          EXPORTING
            i_header     = ls_header
          IMPORTING
            e_header     = ls_new_header
          TABLES
            i_paragraphs = lt_paragraphs
            i_strings    = lt_strings
            i_tabstops   = lt_tabstops.
    
        IF ls_new_header IS NOT INITIAL.
    
          CALL FUNCTION 'SSF_ACTIVATE_STYLE'
            EXPORTING
              i_stylename          = ls_header-stylename
              redirect_error_msg   = abap_true " otherwise warnings write list output
            EXCEPTIONS
              no_name              = 1
              no_style             = 2
              cancelled            = 3
              no_access_permission = 4
              illegal_language     = 5
              OTHERS               = 6.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_stylename TYPE stxsadm-stylename.
    
        SELECT SINGLE stylename
          FROM stxshead INTO lv_stylename
          WHERE active    = c_style_active
            AND stylename = ms_item-obj_name
            AND vari      = ''.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'E_SMSTYLE'
                                                iv_argument    = |{ ms_item-obj_name }| ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: ls_bcdata TYPE bdcdata,
              lt_bcdata TYPE STANDARD TABLE OF bdcdata.
    
        ls_bcdata-program  = 'SAPMSSFS'.
        ls_bcdata-dynpro   = '0100'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam     = 'SSFSCREENS-SNAME'.
        ls_bcdata-fval     = ms_item-obj_name.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam = 'BDC_OKCODE'.
        ls_bcdata-fval = '=DISPLAY'.
        APPEND ls_bcdata TO lt_bcdata.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'SMARTSTYLES'
          it_bdcdata = lt_bcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    * see fm SSF_DOWNLOAD_STYLE
    
        DATA: lv_style_name TYPE tdssname,
              ls_header     TYPE ssfcats,
              lt_paragraphs TYPE TABLE OF ssfparas,
              lt_strings    TYPE TABLE OF ssfstrings,
              lt_tabstops   TYPE TABLE OF stxstab.
    
        lv_style_name = ms_item-obj_name.
    
        CALL FUNCTION 'SSF_READ_STYLE'
          EXPORTING
            i_style_name             = lv_style_name
            i_style_active_flag      = c_style_active
            i_style_variant          = '%MAIN'
            i_style_language         = mv_language
          IMPORTING
            e_header                 = ls_header
          TABLES
            e_paragraphs             = lt_paragraphs
            e_strings                = lt_strings
            e_tabstops               = lt_tabstops
          EXCEPTIONS
            no_name                  = 1
            no_style                 = 2
            active_style_not_found   = 3
            inactive_style_not_found = 4
            no_variant               = 5
            no_main_variant          = 6
            cancelled                = 7
            no_access_permission     = 8
            OTHERS                   = 9.
        IF sy-subrc = 2.
          RETURN.
        ELSEIF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CLEAR ls_header-version.
        CLEAR ls_header-firstuser.
        CLEAR ls_header-firstdate.
        CLEAR ls_header-firsttime.
        CLEAR ls_header-lastuser.
        CLEAR ls_header-lastdate.
        CLEAR ls_header-lasttime.
    
        io_xml->add( iv_name = 'HEADER'
                     ig_data = ls_header ).
        io_xml->add( ig_data = lt_paragraphs
                     iv_name = 'SSFPARAS' ).
        io_xml->add( ig_data = lt_strings
                     iv_name = 'SSFSTRINGS' ).
        io_xml->add( ig_data = lt_tabstops
                     iv_name = 'STXSTAB' ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_stvi IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lv_transaction_variant TYPE utcvariant.
    
        lv_transaction_variant = ms_item-obj_name.
    
        SELECT SINGLE chuser
        FROM shdtvciu
        INTO rv_user
        WHERE tcvariant = lv_transaction_variant.
        IF sy-subrc <> 0
        OR rv_user IS INITIAL.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_transaction_variant TYPE tcvariant.
    
        lv_transaction_variant = ms_item-obj_name.
    
        CALL FUNCTION 'RS_HDSYS_DELETE_VARIANT'
          EXPORTING
            tcvariant                 = lv_transaction_variant
            i_flag_client_independent = abap_true
          EXCEPTIONS
            variant_enqueued          = 1
            no_correction             = 2
            OTHERS                    = 3.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_transaction_variant TYPE ty_transaction_variant.
    
        io_xml->read(
          EXPORTING
            iv_name = 'STVI'
          CHANGING
            cg_data = ls_transaction_variant ).
    
        CALL FUNCTION 'ENQUEUE_ESTCVARCIU'
          EXPORTING
            tcvariant = ls_transaction_variant-shdtvciu-tcvariant
          EXCEPTIONS
            OTHERS    = 1.
        IF sy-subrc <> 0.
          MESSAGE e413(ms) WITH ls_transaction_variant-shdtvciu-tcvariant INTO zcx_abapgit_exception=>null.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        corr_insert( iv_package ).
    
    *   Populate user details
        ls_transaction_variant-shdtvciu-crdate = sy-datum.
        ls_transaction_variant-shdtvciu-cruser = sy-uname.
        ls_transaction_variant-shdtvciu-chdate = sy-datum.
        ls_transaction_variant-shdtvciu-chuser = sy-uname.
    
        MODIFY shdtvciu   FROM ls_transaction_variant-shdtvciu.
        MODIFY shdttciu   FROM TABLE ls_transaction_variant-shdttciu[].
        INSERT shdfvguicu FROM TABLE ls_transaction_variant-shdfvguicu[] ACCEPTING DUPLICATE KEYS.
        INSERT shdtvsvciu FROM TABLE ls_transaction_variant-shdtvsvciu[] ACCEPTING DUPLICATE KEYS.
    
        CALL FUNCTION 'DEQUEUE_ESTCVARCIU'
          EXPORTING
            tcvariant = ls_transaction_variant-shdtvciu-tcvariant.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_transaction_variant TYPE tcvariant.
    
        lv_transaction_variant = ms_item-obj_name.
    
        CALL FUNCTION 'RS_HDSYS_EXIST_CHECK_VARIANT'
          EXPORTING
            tcvariant                 = lv_transaction_variant
            i_flag_client_independent = abap_true
          EXCEPTIONS
            no_variant                = 1
            OTHERS                    = 2.
        IF sy-subrc = 0.
          rv_bool = abap_true.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
    
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
    
        rs_metadata = get_metadata( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
    
        rv_active = is_active( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_transaction_variant TYPE ty_transaction_variant.
    
        ls_transaction_variant-shdtvciu-tcvariant = ms_item-obj_name.
    
        CALL FUNCTION 'RS_HDSYS_READ_TC_VARIANT_DB'
          EXPORTING
            tcvariant               = ls_transaction_variant-shdtvciu-tcvariant
            flag_client_independent = abap_true
          IMPORTING
            header_tcvariant        = ls_transaction_variant-shdtvciu
          TABLES
            screen_variants         = ls_transaction_variant-shdtvsvciu[]
            inactive_functions      = ls_transaction_variant-shdfvguicu[]
          EXCEPTIONS
            no_variant              = 1
            OTHERS                  = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        SORT ls_transaction_variant-shdtvsvciu ASCENDING.
        SORT ls_transaction_variant-shdfvguicu ASCENDING.
    
    *   Clear all user details
        CLEAR: ls_transaction_variant-shdtvciu-crdate,
               ls_transaction_variant-shdtvciu-cruser,
               ls_transaction_variant-shdtvciu-chdate,
               ls_transaction_variant-shdtvciu-chuser.
    
        SELECT *
          FROM shdttciu
          INTO TABLE ls_transaction_variant-shdttciu[]
          WHERE tcvariant = ls_transaction_variant-shdtvciu-tcvariant
          ORDER BY PRIMARY KEY.
    
        io_xml->add( iv_name = 'STVI'
                     ig_data = ls_transaction_variant ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_styl IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: ls_style TYPE ty_style,
              lv_name  TYPE itcda-tdstyle.
    
        lv_name = ms_item-obj_name.
    
        CALL FUNCTION 'READ_STYLE'
          EXPORTING
            style        = lv_name
          IMPORTING
            style_header = ls_style-header
          TABLES
            paragraphs   = ls_style-paragraphs
            strings      = ls_style-strings
            tabs         = ls_style-tabs.
    
        rv_user = ls_style-header-tdluser.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_style TYPE itcda-tdstyle.
    
        lv_style = ms_item-obj_name.
    
        CALL FUNCTION 'DELETE_STYLE'
          EXPORTING
            style    = lv_style
            language = '*'.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_style TYPE ty_style.
    
        io_xml->read( EXPORTING iv_name = 'STYLE'
                      CHANGING cg_data = ls_style ).
    
        CALL FUNCTION 'SAVE_STYLE'
          EXPORTING
            style_header = ls_style-header
          TABLES
            paragraphs   = ls_style-paragraphs
            strings      = ls_style-strings
            tabs         = ls_style-tabs.
    
        tadir_insert( iv_package ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: ls_style TYPE ty_style,
              lv_name  TYPE itcda-tdstyle,
              lv_found TYPE abap_bool.
    
        lv_name = ms_item-obj_name.
    
        CALL FUNCTION 'READ_STYLE'
          EXPORTING
            style      = lv_name
          IMPORTING
            found      = lv_found
          TABLES
            paragraphs = ls_style-paragraphs
            strings    = ls_style-strings
            tabs       = ls_style-tabs.
    
        rv_bool = boolc( lv_found = abap_true ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: ls_bcdata TYPE bdcdata,
              lt_bcdata TYPE STANDARD TABLE OF bdcdata.
    
        ls_bcdata-program  = 'SAPMSSCS'.
        ls_bcdata-dynpro   = '1100'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam     = 'RSSCS-TDSTYLE'.
        ls_bcdata-fval     = ms_item-obj_name.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam     = 'RSSCS-TDSPRAS'.
        ls_bcdata-fval     = mv_language.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam     = 'RSSCS-TDHEADEROB'.
        ls_bcdata-fval     = 'X'.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam = 'BDC_OKCODE'.
        ls_bcdata-fval = '=SHOW'.
        APPEND ls_bcdata TO lt_bcdata.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'SE72'
          it_bdcdata = lt_bcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_style TYPE ty_style,
              lv_name  TYPE itcda-tdstyle.
    
        lv_name = ms_item-obj_name.
    
        CALL FUNCTION 'READ_STYLE'
          EXPORTING
            style        = lv_name
          IMPORTING
            style_header = ls_style-header
          TABLES
            paragraphs   = ls_style-paragraphs
            strings      = ls_style-strings
            tabs         = ls_style-tabs.
    
        CLEAR: ls_style-header-tdfuser,
               ls_style-header-tdfdate,
               ls_style-header-tdftime,
               ls_style-header-tdfreles,
               ls_style-header-tdluser,
               ls_style-header-tdldate,
               ls_style-header-tdltime,
               ls_style-header-tdlreles.
    
        io_xml->add( iv_name = 'STYLE'
                     ig_data = ls_style ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sucu IMPLEMENTATION.
    
      METHOD get_generic.
    
        CREATE OBJECT ro_generic
          EXPORTING
            is_item     = ms_item
            iv_language = mv_language.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        rv_user = c_user_unknown. " not stored by SAP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        get_generic( )->delete( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        get_generic( )->serialize( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_susc IMPLEMENTATION.
    
      METHOD delete_class.
    
        DELETE FROM tobc  WHERE oclss = iv_auth_object_class.
        DELETE FROM tobct WHERE oclss = iv_auth_object_class.
    
      ENDMETHOD.
    
      METHOD has_authorization.
    
        AUTHORITY-CHECK OBJECT 'S_DEVELOP'
               ID 'DEVCLASS' DUMMY
               ID 'OBJTYPE' FIELD 'SUSC'
               ID 'OBJNAME' FIELD iv_class
               ID 'P_GROUP' DUMMY
               ID 'ACTVT'   FIELD iv_activity.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( iv_msgid = '01'
                                             iv_msgno = '467' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD is_used.
    
        DATA: lv_used_auth_object_class TYPE tobc-oclss.
    
        SELECT SINGLE oclss
          FROM tobj
          INTO lv_used_auth_object_class
          WHERE oclss = iv_auth_object_class ##WARN_OK.
        IF sy-subrc = 0.
          zcx_abapgit_exception=>raise_t100( iv_msgid = '01'
                                             iv_msgno = '212'
                                             iv_msgv1 = |{ iv_auth_object_class }| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD put_delete_to_transport.
    
        DATA: lv_tr_object_name TYPE e071-obj_name,
              lv_tr_return      TYPE char1,
              ls_package_info   TYPE tdevc.
    
        lv_tr_object_name = ms_item-obj_name.
    
        CALL FUNCTION 'SUSR_COMMEDITCHECK'
          EXPORTING
            objectname       = lv_tr_object_name
            transobjecttype  = c_transobjecttype_class
          IMPORTING
            return_from_korr = lv_tr_return.
    
        IF lv_tr_return <> 'M'.
          zcx_abapgit_exception=>raise( |error in SUSC delete at SUSR_COMMEDITCHECK| ).
        ENDIF.
    
        CALL FUNCTION 'TR_DEVCLASS_GET'
          EXPORTING
            iv_devclass = ms_item-devclass
          IMPORTING
            es_tdevc    = ls_package_info
          EXCEPTIONS
            OTHERS      = 1.
        IF sy-subrc = 0 AND ls_package_info-korrflag IS INITIAL.
          tadir_delete( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        rv_user = c_user_unknown. " not stored by SAP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        CONSTANTS lc_activity_delete_06 TYPE activ_auth VALUE '06'.
    
        DATA: lv_auth_object_class TYPE tobc-oclss.
    
        lv_auth_object_class = ms_item-obj_name.
    
        TRY.
            IF zif_abapgit_object~exists( ) = abap_false.
              RETURN.
            ENDIF.
          CATCH zcx_abapgit_exception.
            RETURN.
        ENDTRY.
    
        has_authorization( iv_class    = lv_auth_object_class
                           iv_activity = lc_activity_delete_06 ).
    
        is_used( lv_auth_object_class ).
    
        delete_class( lv_auth_object_class ).
    
        put_delete_to_transport( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    * see function group SUSA
    
        DATA: ls_tobc       TYPE tobc,
              lv_objectname TYPE e071-obj_name,
              ls_tobct      TYPE tobct.
    
        io_xml->read( EXPORTING iv_name = 'TOBC'
                      CHANGING cg_data = ls_tobc ).
        io_xml->read( EXPORTING iv_name = 'TOBCT'
                      CHANGING cg_data = ls_tobct ).
    
        tadir_insert( iv_package ).
    
        lv_objectname = ms_item-obj_name.
        CALL FUNCTION 'SUSR_COMMEDITCHECK'
          EXPORTING
            objectname      = lv_objectname
            transobjecttype = c_transobjecttype_class.
    
        INSERT tobc FROM ls_tobc.                             "#EC CI_SUBRC
    * ignore sy-subrc as all fields are key fields
    
        MODIFY tobct FROM ls_tobct.                           "#EC CI_SUBRC
        ASSERT sy-subrc = 0.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_oclss TYPE tobc-oclss.
    
        SELECT SINGLE oclss FROM tobc INTO lv_oclss
          WHERE oclss = ms_item-obj_name.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: lv_objclass TYPE tobc-oclss.
    
        lv_objclass = ms_item-obj_name.
        CALL FUNCTION 'SUSR_SHOW_OBJECT_CLASS'
          EXPORTING
            objclass = lv_objclass.
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_tobc  TYPE tobc,
              ls_tobct TYPE tobct.
    
        SELECT SINGLE * FROM tobc INTO ls_tobc
          WHERE oclss = ms_item-obj_name.
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        SELECT SINGLE * FROM tobct INTO ls_tobct
          WHERE oclss = ms_item-obj_name
          AND langu = mv_language.
    
        io_xml->add( iv_name = 'TOBC'
                     ig_data = ls_tobc ).
        io_xml->add( iv_name = 'TOBCT'
                     ig_data = ls_tobct ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sush IMPLEMENTATION.
    
      METHOD clear_metadata.
    
        DATA:
          BEGIN OF ls_empty_metadata,
            modifier  TYPE c LENGTH 12, " usob_sm-modifier
            moddate   TYPE d, " usob_sm-moddate,
            modtime   TYPE t, " usob_sm-modtime,
            srcsystem TYPE tadir-srcsystem,
            author    TYPE tadir-author,
            devclass  TYPE tadir-devclass,
          END OF ls_empty_metadata.
    
        FIELD-SYMBOLS:
           TYPE any,
           TYPE any.
    
        MOVE-CORRESPONDING ls_empty_metadata TO cs_data_head.
    
        LOOP AT ct_usobx ASSIGNING .
          MOVE-CORRESPONDING ls_empty_metadata TO .
        ENDLOOP.
    
        LOOP AT ct_usobt ASSIGNING .
          MOVE-CORRESPONDING ls_empty_metadata TO .
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        DATA: lr_data_head TYPE REF TO data.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        TRY.
            CREATE DATA lr_data_head TYPE ('IF_SU22_ADT_OBJECT=>TS_SU2X_HEAD').
    
          CATCH cx_sy_create_data_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
        ms_key = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE modifier FROM usob_sm INTO rv_user
          WHERE name = ms_key-name AND type = ms_key-type.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA:
          lo_su22 TYPE REF TO object,
          lx_err  TYPE REF TO cx_static_check.
    
        TRY.
            CREATE OBJECT lo_su22 TYPE ('CL_SU22_ADT_OBJECT').
    
            CALL METHOD lo_su22->('IF_SU22_ADT_OBJECT~DELETE')
              EXPORTING
                iv_key     = ms_key
                iv_cleanup = abap_true.
          CATCH cx_static_check INTO lx_err.
            zcx_abapgit_exception=>raise_with_text( lx_err ).
        ENDTRY.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA:
          ls_key       TYPE usobkey,
          lo_su22      TYPE REF TO object,
          lo_appl      TYPE REF TO object,
          lt_usobx     TYPE usobx_t,
          lt_usobt     TYPE usobt_t,
          ls_usobhash  TYPE usobhash,
          lr_appl_head TYPE REF TO data,
          lr_data_head TYPE REF TO data,
          lx_err       TYPE REF TO cx_static_check,
          lv_text      TYPE string.
    
        FIELD-SYMBOLS:
                       TYPE any,
                       TYPE any,
           TYPE uccheck,
                    TYPE any,
                        TYPE any.
    
        TRY.
            CREATE DATA lr_data_head TYPE ('IF_SU22_ADT_OBJECT=>TS_SU2X_HEAD').
            ASSIGN lr_data_head->* TO .
    
            io_xml->read( EXPORTING iv_name = 'HEAD'
                          CHANGING  cg_data =  ).
    
            io_xml->read( EXPORTING iv_name = 'USOBX'
                          CHANGING  cg_data = lt_usobx ).
    
            io_xml->read( EXPORTING iv_name = 'USOBT'
                          CHANGING  cg_data = lt_usobt ).
    
            io_xml->read( EXPORTING iv_name = 'USOBHASH'
                          CHANGING  cg_data = ls_usobhash ).
    
            CREATE OBJECT lo_su22 TYPE ('CL_SU22_ADT_OBJECT').
    
            ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE  TO .
            IF sy-subrc = 0.
              set_abap_language_version( CHANGING cv_abap_language_version =  ).
            ENDIF.
    
            IF zif_abapgit_object~exists( ) = abap_false.
              " Older repos will not have USOBHASH so we try to reconstruct it
              IF ls_usobhash IS INITIAL.
                ASSIGN COMPONENT 'DISPLAY_NAME' OF STRUCTURE  TO .
                CASE ms_key-type.
                  WHEN 'TR'.
                    ls_usobhash-pgmid    = 'R3TR'.
                    ls_usobhash-object   = 'TRAN'.
                    ls_usobhash-obj_name = .
                  WHEN 'RF'.
                    ls_usobhash-pgmid    = 'R3TR'.
                    ls_usobhash-object   = 'FUGR'.
                    ls_usobhash-obj_name = .
                  WHEN 'HT'.
                    IF  CP 'R3TR*'.
                      SPLIT  AT space INTO ls_usobhash-pgmid ls_usobhash-object ls_usobhash-obj_name.
                    ENDIF.
                  WHEN 'HS'.
                    " TODO: Can we derive them from display name?
                    ls_usobhash-service_type = ''.
                    ls_usobhash-service      = ''.
                  WHEN OTHERS.
                    ASSERT 0 = 1.
                ENDCASE.
              ENDIF.
    
              MOVE-CORRESPONDING ms_key TO ls_usobhash.
    
              " Not for transactions
              IF ms_key-type <> 'TR'.
                CALL METHOD lo_su22->('IF_SU22_ADT_OBJECT~CREATE')
                  EXPORTING
                    iv_new_key = ls_usobhash
                  RECEIVING
                    rs_key     = ls_key.
              ENDIF.
            ELSE.
              " check if lead application exists
              CALL METHOD lo_su22->('IF_SU22_ADT_OBJECT~CHECK')
                EXPORTING
                  id_mode = '02'
                CHANGING
                  cs_head = .
    
              CREATE DATA lr_appl_head TYPE ('CL_SU2X=>TS_HEAD').
              ASSIGN lr_appl_head->* TO .
    
              CREATE OBJECT lo_appl TYPE ('CL_SU22_APPL').
    
              CALL METHOD lo_appl->('GET_DATA')
                EXPORTING
                  is_key  = ms_key
                IMPORTING
                  es_head = .
    
              ASSIGN COMPONENT 'DEVCLASS' OF STRUCTURE  TO .
              IF  <> iv_package.
                lv_text =
                |Lead application of object { ms_item-obj_name } does not exist in package {  }|.
                zcx_abapgit_exception=>raise( lv_text ).
              ENDIF.
            ENDIF.
    
            " Not for transactions
            IF ms_key-type <> 'TR'.
              corr_insert( iv_package ).
            ENDIF.
    
            CALL METHOD lo_su22->('IF_SU22_ADT_OBJECT~UPDATE')
              EXPORTING
                is_head  = 
                it_usobx = lt_usobx
                it_usobt = lt_usobt.
    
          CATCH cx_static_check INTO lx_err.
            zcx_abapgit_exception=>raise_with_text( lx_err ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA ls_usobhash TYPE usobhash.
    
        SELECT SINGLE * FROM usobhash INTO ls_usobhash WHERE name = ms_key-name AND type = ms_key-type.
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'E_USOBX'
                                                iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA:
          lo_su22      TYPE REF TO object,
          ls_usobhash  TYPE usobhash,
          lt_usobx     TYPE usobx_t,
          lt_usobt     TYPE usobt_t,
          lr_head      TYPE REF TO data,
          lr_usobx_ext TYPE REF TO data,
          lr_usobt_ext TYPE REF TO data,
          lx_err       TYPE REF TO cx_static_check.
    
        FIELD-SYMBOLS:
                            TYPE any,
           TYPE uccheck,
                       TYPE ANY TABLE,
                       TYPE ANY TABLE.
    
        TRY.
            CREATE DATA lr_head TYPE ('IF_SU22_ADT_OBJECT=>TS_SU2X_HEAD').
            ASSIGN lr_head->* TO .
    
            CREATE DATA lr_usobx_ext TYPE ('IF_SU22_ADT_OBJECT=>TT_SU2X_X').
            ASSIGN lr_usobx_ext->* TO .
    
            CREATE DATA lr_usobt_ext TYPE ('IF_SU22_ADT_OBJECT=>TT_SU2X_T').
            ASSIGN lr_usobt_ext->* TO .
    
            CREATE OBJECT lo_su22 TYPE ('CL_SU22_ADT_OBJECT').
    
            CALL METHOD lo_su22->('IF_SU22_ADT_OBJECT~SELECT')
              EXPORTING
                iv_key       = ms_key
              IMPORTING
                es_head      = 
                et_usobx     = lt_usobx
                et_usobt     = lt_usobt
                et_usobx_ext = 
                et_usobt_ext = .
    
            clear_metadata(
              CHANGING
                cs_data_head = 
                ct_usobx     = lt_usobx
                ct_usobt     = lt_usobt ).
    
            ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE  TO .
            IF sy-subrc = 0.
              clear_abap_language_version( CHANGING cv_abap_language_version =  ).
            ENDIF.
    
            io_xml->add( iv_name = 'HEAD'
                         ig_data =  ).
    
            io_xml->add( iv_name = 'USOBX'
                         ig_data = lt_usobx ).
    
            io_xml->add( iv_name = 'USOBT'
                         ig_data = lt_usobt ).
    
            " Serialize hash data because it contains the leading application name needed for recreating the object
            IF ms_key-type = 'HS' OR ms_key-type = 'HT'.
              SELECT SINGLE * FROM usobhash INTO ls_usobhash WHERE name = ms_key-name AND type = ms_key-type.
              IF sy-subrc = 0.
                CLEAR: ls_usobhash-name, ls_usobhash-type.
    
                io_xml->add( iv_name = 'USOBHASH'
                             ig_data = ls_usobhash ).
              ENDIF.
            ENDIF.
    
          CATCH cx_static_check INTO lx_err.
            zcx_abapgit_exception=>raise_with_text( lx_err ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_OBJECT_SUSO IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_objectname = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD delete_documentation.
    
        DATA:
          lv_docu_obj TYPE dokhl-object,
          lv_dummy    TYPE sy-langu.
    
        lv_docu_obj  = mv_objectname.
    
        SELECT SINGLE langu
               FROM dokil INTO lv_dummy
               WHERE id   = 'UO'                            "#EC CI_GENBUFF
               AND object = lv_docu_obj.                    "#EC CI_NOORDER
    
        IF sy-subrc = 0.
    
          CALL FUNCTION 'DOKU_DELETE_ALL'
            EXPORTING
              doku_id                        = 'UO'
              doku_object                    = lv_docu_obj
              suppress_transport             = space
            EXCEPTIONS
              header_without_text            = 1
              index_without_header           = 2
              no_authority_for_devclass_xxxx = 3
              no_docu_found                  = 4
              object_is_already_enqueued     = 5
              object_is_enqueued_by_corr     = 6
              techn_enqueue_problem          = 7
              user_break                     = 8
              OTHERS                         = 9.
    
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD pre_check.
    
        CONSTANTS:
          lc_act_delete TYPE activ_auth VALUE '06'.
    
        DATA:
          lv_act_head            TYPE activ_auth,
          lo_suso                TYPE REF TO object,
          lv_failed              TYPE abap_bool,
          lv_suso_collect_in_cts TYPE i,
          ls_clskey              TYPE seoclskey.
    
        " Downport: CL_SUSO_GEN doesn't exist in 702
        ls_clskey-clsname = |CL_SUSO_GEN|.
    
        CALL FUNCTION 'SEO_CLASS_EXISTENCE_CHECK'
          EXPORTING
            clskey        = ls_clskey
          EXCEPTIONS
            not_specified = 1
            not_existing  = 2
            is_interface  = 3
            no_text       = 4
            inconsistent  = 5
            OTHERS        = 6.
    
        IF sy-subrc = 0.
    
          " so these checks are not executed in 702
    
          CREATE OBJECT lo_suso
            TYPE
              ('CL_SUSO_GEN').
    
          CALL METHOD lo_suso->('SUSO_LOAD_FROM_DB')
            EXPORTING
              id_object = mv_objectname
            RECEIVING
              ed_failed = lv_failed.
    
          IF lv_failed = abap_true.
            " Object & does not exist; choose an existing object
            MESSAGE s111(01) WITH mv_objectname INTO zcx_abapgit_exception=>null.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          CALL METHOD lo_suso->('GET_SUSO_EDIT_MODE')
            EXPORTING
              id_object     = mv_objectname
              id_planed_act = lc_act_delete
            IMPORTING
              ed_mode_head  = lv_act_head.
    
          IF lv_act_head <> lc_act_delete.
            zcx_abapgit_exception=>raise( |SUSO { mv_objectname }: Delete not allowed. Check where-used in SU21| ).
          ENDIF.
    
          CALL METHOD lo_suso->('SUSO_COLLECT_IN_CTS')
            EXPORTING
              id_object = mv_objectname
            RECEIVING
              ed_result = lv_suso_collect_in_cts.
    
          IF lv_suso_collect_in_cts IS NOT INITIAL.
            zcx_abapgit_exception=>raise( |SUSO { mv_objectname }: Cannot delete| ).
          ENDIF.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD regenerate_sap_all.
    
        DATA lv_mem_file TYPE protfile VALUE 'zabapgit.log'.
        DATA: ls_e071  TYPE e071,
              lt_e071  TYPE STANDARD TABLE OF e071,
              lt_e071k TYPE STANDARD TABLE OF e071k.
    
        ls_e071-pgmid = 'R3TR'.
        ls_e071-object = ms_item-obj_type.
        ls_e071-obj_name = ms_item-obj_name.
        INSERT ls_e071 INTO TABLE lt_e071.
    
        " Avoid output of log (see TR_FLUSH_LOG)
        EXPORT gv_mem_file = lv_mem_file TO MEMORY ID 'prot_file'.
    
        CALL FUNCTION 'PRGN_AFTER_IMP_SUSO_SAP_ALL'
          EXPORTING
            iv_tarclient  = '000'
            iv_is_upgrade = space
          TABLES
            tt_e071       = lt_e071
            tt_e071k      = lt_e071k.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        SELECT SINGLE modifier FROM tobjvor INTO rv_user
          WHERE objct = mv_objectname.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        " FM SUSR_DELETE_OBJECT calls the UI. Therefore we reimplement it here.
        " As the class CL_SUSO_GEN isn't present in 702, we call dynamically and
        " skip the pre checks on 702 system. That seems ok.
    
        pre_check( ).
    
        delete_documentation( ).
    
        DELETE FROM tobj  WHERE objct  = mv_objectname.
        DELETE FROM tobjt WHERE object = mv_objectname.
        DELETE FROM tactz WHERE brobj  = mv_objectname.
    
        CALL FUNCTION 'SUPV_DELETE_OBJECT_ASSIGNMENTS'
          EXPORTING
            object_name  = mv_objectname
            all_releases = abap_true.
    
        CALL FUNCTION 'RS_TREE_OBJECT_PLACEMENT'
          EXPORTING
            object    = mv_objectname
            type      = 'SUSO'
            operation = 'DELETE'.
    
        regenerate_sap_all( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    * see function group SUSA
    
        DATA: lv_objectname TYPE trobj_name,
              ls_tobj       TYPE tobj,
              ls_tobjt      TYPE tobjt,
              ls_tobjvorflg TYPE tobjvorflg,
              lt_tactz      TYPE TABLE OF tactz,
              lt_tobjvordat TYPE TABLE OF tobjvordat,
              lt_tobjvor    TYPE TABLE OF tobjvor.
        DATA lv_abap_language_version TYPE uccheck.
        DATA lr_structdescr TYPE REF TO cl_abap_structdescr.
        DATA lr_tobj_attr TYPE REF TO data.
        FIELD-SYMBOLS  TYPE any.
        FIELD-SYMBOLS  TYPE any.
    
        ASSERT NOT ms_item-obj_name IS INITIAL.
    
        io_xml->read( EXPORTING iv_name = 'TOBJ'
                      CHANGING  cg_data = ls_tobj ).
        ls_tobj-bname = sy-uname.
        io_xml->read( EXPORTING iv_name = 'TOBJT'
                      CHANGING  cg_data = ls_tobjt ).
        io_xml->read( EXPORTING iv_name = 'TOBJVORFLG'
                      CHANGING  cg_data = ls_tobjvorflg ).
        io_xml->read( EXPORTING iv_name = 'TACTZ'
                      CHANGING  cg_data = lt_tactz ).
        io_xml->read( EXPORTING iv_name = 'TOBJVORDAT'
                      CHANGING  cg_data = lt_tobjvordat ).
        io_xml->read( EXPORTING iv_name = 'TOBJVOR'
                      CHANGING  cg_data = lt_tobjvor ).
    
        tadir_insert( iv_package ).
    
        lv_objectname = mv_objectname.
    
        CALL FUNCTION 'SUSR_COMMEDITCHECK'
          EXPORTING
            objectname      = lv_objectname
            transobjecttype = 'O'.
    
        MODIFY tobj FROM ls_tobj.                             "#EC CI_SUBRC
        MODIFY tobjt FROM ls_tobjt.                           "#EC CI_SUBRC
        MODIFY tobjvorflg FROM ls_tobjvorflg.                 "#EC CI_SUBRC
        DELETE FROM tactz WHERE brobj = ms_item-obj_name.     "#EC CI_SUBRC
        INSERT tactz FROM TABLE lt_tactz.                     "#EC CI_SUBRC
        DELETE FROM tobjvordat WHERE objct = ms_item-obj_name. "#EC CI_SUBRC
        INSERT tobjvordat FROM TABLE lt_tobjvordat.           "#EC CI_SUBRC
        DELETE FROM tobjvor WHERE objct = ms_item-obj_name.   "#EC CI_SUBRC
        INSERT tobjvor FROM TABLE lt_tobjvor.                 "#EC CI_SUBRC
    
        TRY.
            io_xml->read( EXPORTING iv_name = 'ABAP_LANGUAGE_VERSION'
                          CHANGING  cg_data = lv_abap_language_version ).
    
            set_abap_language_version( CHANGING cv_abap_language_version = lv_abap_language_version ).
    
            lr_structdescr ?= cl_abap_typedescr=>describe_by_name( p_name = 'TOBJ_ATTR' ).
            CREATE DATA lr_tobj_attr TYPE HANDLE lr_structdescr.
            ASSIGN lr_tobj_attr->* TO .
    
            ASSIGN COMPONENT 'OBJCT' OF STRUCTURE  TO .
            IF sy-subrc = 0.
               = ms_item-obj_name.
            ENDIF.
            ASSIGN COMPONENT 'MODIFIER' OF STRUCTURE  TO .
            IF sy-subrc = 0.
               = sy-uname.
            ENDIF.
            ASSIGN COMPONENT 'MODDATE' OF STRUCTURE  TO .
            IF sy-subrc = 0.
               = sy-datum.
            ENDIF.
            ASSIGN COMPONENT 'MODTIME' OF STRUCTURE  TO .
            IF sy-subrc = 0.
               = sy-uzeit.
            ENDIF.
            ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE  TO .
            IF sy-subrc = 0.
               = lv_abap_language_version.
            ENDIF.
    
            MODIFY ('TOBJ_ATTR') FROM .
          CATCH cx_root ##NO_HANDLER.
        ENDTRY.
    
        deserialize_longtexts( ii_xml         = io_xml
                               iv_longtext_id = c_longtext_id_suso ).
    
        regenerate_sap_all( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_objct TYPE tobj-objct.
    
        SELECT SINGLE objct FROM tobj INTO lv_objct
          WHERE objct = ms_item-obj_name.
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        CALL FUNCTION 'SUSR_SHOW_OBJECT'
          EXPORTING
            object = mv_objectname.
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_tobj       TYPE tobj,
              ls_tobjt      TYPE tobjt,
              ls_tobjvorflg TYPE tobjvorflg,
              lt_tactz      TYPE TABLE OF tactz,
              lt_tobjvordat TYPE TABLE OF tobjvordat,
              lt_tobjvor    TYPE TABLE OF tobjvor.
        DATA lv_abap_language_version TYPE uccheck.
    
        SELECT SINGLE * FROM tobj INTO ls_tobj
          WHERE objct = ms_item-obj_name.
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
        CLEAR ls_tobj-bname.
    
        SELECT SINGLE * FROM tobjt INTO ls_tobjt
          WHERE object = ms_item-obj_name
          AND langu = mv_language.                          "#EC CI_GENBUFF
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'TOBJT no English description'
            && ' for object (' && ms_item-obj_name && ')' ).
        ENDIF.
    
        SELECT SINGLE * FROM tobjvorflg INTO ls_tobjvorflg
          WHERE objct = ms_item-obj_name.                     "#EC CI_SUBRC
    
        SELECT * FROM tactz INTO TABLE lt_tactz
          WHERE brobj = ms_item-obj_name
          ORDER BY PRIMARY KEY.               "#EC CI_SUBRC "#EC CI_GENBUFF
    
        SELECT * FROM tobjvordat INTO TABLE lt_tobjvordat
          WHERE objct = ms_item-obj_name
          ORDER BY PRIMARY KEY.               "#EC CI_SUBRC "#EC CI_GENBUFF
    
        SELECT * FROM tobjvor INTO TABLE lt_tobjvor
          WHERE objct = ms_item-obj_name
          ORDER BY PRIMARY KEY.                               "#EC CI_SUBRC
    
        io_xml->add( iv_name = 'TOBJ'
                     ig_data = ls_tobj ).
        io_xml->add( iv_name = 'TOBJT'
                     ig_data = ls_tobjt ).
        io_xml->add( iv_name = 'TOBJVORFLG'
                     ig_data = ls_tobjvorflg ).
        io_xml->add( ig_data = lt_tactz
                     iv_name = 'TACTZ' ).
        io_xml->add( ig_data = lt_tobjvordat
                     iv_name = 'TOBJVORDAT' ).
        io_xml->add( ig_data = lt_tobjvor
                     iv_name = 'TOBJVOR' ).
    
        TRY.
            SELECT SINGLE ('ABAP_LANGUAGE_VERSION') FROM ('TOBJ_ATTR') INTO lv_abap_language_version
              WHERE objct = ms_item-obj_name.
    
            IF sy-subrc = 0.
              clear_abap_language_version( CHANGING cv_abap_language_version = lv_abap_language_version ).
    
              io_xml->add( iv_name = 'ABAP_LANGUAGE_VERSION'
                           ig_data = lv_abap_language_version ).
            ENDIF.
          CATCH cx_root ##NO_HANDLER.
        ENDTRY.
    
        serialize_longtexts( ii_xml         = io_xml
                             iv_longtext_id = c_longtext_id_suso ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_swcr IMPLEMENTATION.
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lr_data        TYPE REF TO data,
              lo_swcr_db_api TYPE REF TO object,
              lv_name        TYPE c LENGTH 30,
              lx_error       TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:  TYPE any,
                          TYPE any.
    
        TRY.
            CALL METHOD ('CL_SCR_DB_ACCESS')=>('CREATE_INSTANCE')
              RECEIVING
                result = lo_swcr_db_api.
            CREATE DATA lr_data TYPE ('IF_SCR_TYPES=>TY_S_HEADER').
            ASSIGN lr_data->* TO .
    
            lv_name = ms_item-obj_name.
    
            TRY.
                CALL METHOD lo_swcr_db_api->('IF_SCR_DB_ACCESS~READ_HEADER')
                  EXPORTING
                    software_component = lv_name
                    version            = 'A'
                  RECEIVING
                    result             = .
              CATCH cx_root. " no active version found, try to find an inactive version
                CALL METHOD lo_swcr_db_api->('IF_SCR_DB_ACCESS~READ_HEADER')
                  EXPORTING
                    software_component = lv_name
                    version            = 'I'
                  RECEIVING
                    result             = .
            ENDTRY.
    
            ASSIGN COMPONENT 'LAST_CHANGED_BY' OF STRUCTURE  TO .
            rv_user = .
    
          CATCH cx_root INTO lx_error.
            zcx_abapgit_exception=>raise( iv_text     = lx_error->get_text( )
                                         ix_previous = lx_error ).
        ENDTRY.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sxci IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE uname FROM sxc_attr INTO rv_user WHERE imp_name = ms_item-obj_name.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_implementation_name TYPE rsexscrn-imp_name.
    
        lv_implementation_name = ms_item-obj_name.
    
        CALL FUNCTION 'SXO_IMPL_DELETE'
          EXPORTING
            imp_name           = lv_implementation_name
            no_dialog          = abap_true
          EXCEPTIONS
            imp_not_existing   = 1
            action_canceled    = 2
            access_failure     = 3
            data_inconsistency = 4
            OTHERS             = 5.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_badi_definition             TYPE badi_data,
              lo_filter_object               TYPE REF TO cl_badi_flt_struct,
              lo_filter_values_object        TYPE REF TO cl_badi_flt_values_alv,
              lv_korrnum                     TYPE trkorr,
              lv_filter_type_enhanceability  TYPE rsexscrn-flt_ext,
              lv_package                     TYPE devclass,
              ls_classic_badi_implementation TYPE ty_classic_badi_implementation.
    
        io_xml->read(
          EXPORTING
            iv_name = 'SXCI'
          CHANGING
            cg_data = ls_classic_badi_implementation ).
    
        CALL FUNCTION 'SXO_BADI_READ'
          EXPORTING
            exit_name    = ls_classic_badi_implementation-implementation_data-exit_name
          IMPORTING
            badi         = ls_badi_definition
            filter_obj   = lo_filter_object
          EXCEPTIONS
            read_failure = 1
            OTHERS       = 2.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        lv_package = iv_package.
    
        CREATE OBJECT lo_filter_values_object
          EXPORTING
            filter_object = lo_filter_object
            filter_values = ls_classic_badi_implementation-filters.
    
        CALL FUNCTION 'SXO_IMPL_SAVE'
          EXPORTING
            impl             = ls_classic_badi_implementation-implementation_data
            flt_ext          = lv_filter_type_enhanceability
            filter_val_obj   = lo_filter_values_object
            genflag          = abap_true
            no_dialog        = abap_true
          TABLES
            fcodes_to_insert = ls_classic_badi_implementation-function_codes
            cocos_to_insert  = ls_classic_badi_implementation-control_composites
            intas_to_insert  = ls_classic_badi_implementation-customer_includes
            sscrs_to_insert  = ls_classic_badi_implementation-screens
          CHANGING
            korrnum          = lv_korrnum
            devclass         = lv_package
          EXCEPTIONS
            save_failure     = 1
            action_canceled  = 2
            OTHERS           = 3.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CALL FUNCTION 'SXO_IMPL_ACTIVE'
          EXPORTING
            imp_name                  = ls_classic_badi_implementation-implementation_data-imp_name
            no_dialog                 = abap_true
          EXCEPTIONS
            badi_not_existing         = 1
            imp_not_existing          = 2
            already_active            = 3
            data_inconsistency        = 4
            activation_not_admissable = 5
            action_canceled           = 6
            access_failure            = 7
            OTHERS                    = 8.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_implementation_name TYPE rsexscrn-imp_name.
    
        lv_implementation_name = ms_item-obj_name.
    
        CALL FUNCTION 'SXV_IMP_EXISTS'
          EXPORTING
            imp_name           = lv_implementation_name
          EXCEPTIONS
            not_existing       = 1
            data_inconsistency = 2
            OTHERS             = 3.
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
    
        rs_metadata = get_metadata( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
    
        "Note: SAP does not show inactive classic BAdIs as "Inactive objects" in SE80
        "Therefore, rv_active will always be true. The implementation state (runtime
        "behaviour of the BAdI) will be serialized as part of the XML
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_implementation_name         TYPE rsexscrn-imp_name,
              lv_exit_name                   TYPE rsexscrn-exit_name,
              lo_filter_object               TYPE REF TO cl_badi_flt_struct,
              ls_badi_definition             TYPE badi_data,
              lo_filter_values_object        TYPE REF TO cl_badi_flt_values_alv,
              lt_methods                     TYPE seex_mtd_table,
              ls_classic_badi_implementation TYPE ty_classic_badi_implementation.
    
        lv_implementation_name = ms_item-obj_name.
    
        CALL FUNCTION 'SXV_EXIT_FOR_IMP'
          EXPORTING
            imp_name           = lv_implementation_name
          IMPORTING
            exit_name          = lv_exit_name
          TABLES
            filters            = ls_classic_badi_implementation-filters
          EXCEPTIONS
            data_inconsistency = 1
            OTHERS             = 2.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CALL FUNCTION 'SXO_BADI_READ'
          EXPORTING
            exit_name    = lv_exit_name
          IMPORTING
            badi         = ls_badi_definition
            filter_obj   = lo_filter_object
          TABLES
            fcodes       = ls_classic_badi_implementation-function_codes
            cocos        = ls_classic_badi_implementation-control_composites
            intas        = ls_classic_badi_implementation-customer_includes
            scrns        = ls_classic_badi_implementation-screens
            methods      = lt_methods
          EXCEPTIONS
            read_failure = 1
            OTHERS       = 2.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CALL FUNCTION 'SXO_IMPL_FOR_BADI_READ'
          EXPORTING
            imp_name                    = lv_implementation_name
            exit_name                   = lv_exit_name
            inter_name                  = ls_badi_definition-inter_name
            filter_obj                  = lo_filter_object
            no_create_filter_values_obj = abap_true
          IMPORTING
            impl                        = ls_classic_badi_implementation-implementation_data
            filter_values_obj           = lo_filter_values_object
          TABLES
            fcodes                      = ls_classic_badi_implementation-function_codes
            cocos                       = ls_classic_badi_implementation-control_composites
            intas                       = ls_classic_badi_implementation-customer_includes
            scrns                       = ls_classic_badi_implementation-screens
          CHANGING
            methods                     = lt_methods
          EXCEPTIONS
            read_failure                = 1
            OTHERS                      = 2.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CLEAR: ls_classic_badi_implementation-implementation_data-aname,
               ls_classic_badi_implementation-implementation_data-adate,
               ls_classic_badi_implementation-implementation_data-atime,
               ls_classic_badi_implementation-implementation_data-uname,
               ls_classic_badi_implementation-implementation_data-udate,
               ls_classic_badi_implementation-implementation_data-utime.
    
        io_xml->add( iv_name = 'SXCI'
                     ig_data = ls_classic_badi_implementation ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_sxsd IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        SELECT SINGLE * FROM sxc_attr INTO ms_badi_attr WHERE imp_name = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        IF ms_badi_attr-uname IS NOT INITIAL.
          rv_user = ms_badi_attr-uname.
        ELSE.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
        "Not allowed, SAP Object
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
        " Not allowed, SAP Object
        " Can't create Z-BADIs since at least 7.0
        zcx_abapgit_exception=>raise_t100(
          iv_msgid = 'ENHANCEMENT'
          iv_msgno = '269' ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
        rv_bool = boolc( ms_badi_attr IS NOT INITIAL ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        "Not allowed, SAP Object
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        "Serialize only, irrelevant
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA lv_exit_name TYPE rsexscrn-exit_name.
    
        lv_exit_name = ms_item-obj_name.
    
        CALL FUNCTION 'SXO_BADI_SHOW'
          EXPORTING
            exit_name         = lv_exit_name
          EXCEPTIONS
            action_canceled   = 1
            access_failure    = 2
            badi_not_exixting = 3
            OTHERS            = 4.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100(
            iv_msgid = sy-msgid
            iv_msgno = sy-msgno
            iv_msgv1 = sy-msgv1
            iv_msgv2 = sy-msgv2
            iv_msgv3 = sy-msgv3
            iv_msgv4 = sy-msgv4 ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        TYPES: BEGIN OF ty_badi_definition,
                 badi               TYPE badi_data,
                 mast_langu         TYPE sy-langu,
                 ext_clname         TYPE seoclsname,
                 fcodes             TYPE seex_fcode_table,
                 cocos              TYPE seex_coco_table,
                 intas              TYPE seex_table_table,
                 scrns              TYPE seex_screen_table,
                 methods            TYPE seex_mtd_table,
                 inactive_tabstrips TYPE seex_inactive_tabstrips,
               END OF ty_badi_definition.
    
        DATA ls_badi_definition TYPE ty_badi_definition.
        DATA lv_exit_name TYPE rsexscrn-exit_name.
    
        lv_exit_name = ms_item-obj_name.
    
        CALL FUNCTION 'SXO_BADI_READ'
          EXPORTING
            exit_name          = lv_exit_name
          IMPORTING
            badi               = ls_badi_definition-badi
            mast_langu         = ls_badi_definition-mast_langu
            ext_clname         = ls_badi_definition-ext_clname
          TABLES
            fcodes             = ls_badi_definition-fcodes
            cocos              = ls_badi_definition-cocos
            intas              = ls_badi_definition-intas
            scrns              = ls_badi_definition-scrns
            methods            = ls_badi_definition-methods
            inactive_tabstrips = ls_badi_definition-inactive_tabstrips
          EXCEPTIONS
            read_failure       = 1
            OTHERS             = 2.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Could not read definition for BAdI { lv_exit_name }| ).
        ENDIF.
    
        CLEAR: ls_badi_definition-badi-uname,
               ls_badi_definition-badi-udate,
               ls_badi_definition-badi-utime.
    
        io_xml->add(
          iv_name = 'SXSD'
          ig_data = ls_badi_definition ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS lcl_tabl_xml DEFINITION FINAL.
      PUBLIC SECTION.
        CLASS-METHODS add
          IMPORTING
            io_xml      TYPE REF TO zif_abapgit_xml_output
            is_internal TYPE zif_abapgit_object_tabl=>ty_internal
          RAISING
            zcx_abapgit_exception.
    
        CLASS-METHODS read
          IMPORTING
            io_xml             TYPE REF TO zif_abapgit_xml_input
          RETURNING
            VALUE(rs_internal) TYPE zif_abapgit_object_tabl=>ty_internal
          RAISING
            zcx_abapgit_exception.
    ENDCLASS.
    
    CLASS lcl_tabl_xml IMPLEMENTATION.
      METHOD add.
    
    * adding to xml must be done in the right sequence to avoid changes
        io_xml->add( iv_name = 'DD02V'
                     ig_data = is_internal-dd02v ).
        IF NOT is_internal-dd09l IS INITIAL.
          io_xml->add( iv_name = 'DD09L'
                       ig_data = is_internal-dd09l ).
        ENDIF.
        io_xml->add( iv_name = 'DD03P_TABLE'
                     ig_data = is_internal-dd03p ).
        io_xml->add( iv_name = 'DD05M_TABLE'
                     ig_data = is_internal-dd05m ).
        io_xml->add( iv_name = 'DD08V_TABLE'
                     ig_data = is_internal-dd08v ).
        io_xml->add( iv_name = 'DD12V'
                     ig_data = is_internal-dd12v ).
        io_xml->add( iv_name = 'DD17V'
                     ig_data = is_internal-dd17v ).
        io_xml->add( iv_name = 'DD35V_TALE'
                     ig_data = is_internal-dd35v ).
        io_xml->add( iv_name = 'DD36M'
                     ig_data = is_internal-dd36m ).
    
        IF lines( is_internal-i18n_langs ) > 0.
          io_xml->add( iv_name = 'I18N_LANGS'
                       ig_data = is_internal-i18n_langs ).
    
          io_xml->add( iv_name = 'DD02_TEXTS'
                       ig_data = is_internal-dd02_texts ).
        ENDIF.
    
        io_xml->add( iv_name = 'LONGTEXTS'
                     ig_data = is_internal-longtexts ).
    
        io_xml->add( iv_name = zif_abapgit_object_tabl=>c_s_dataname-segment_definition
                     ig_data = is_internal-segment_definitions ).
    
        io_xml->add( iv_name = zif_abapgit_object_tabl=>c_s_dataname-tabl_extras
                     ig_data = is_internal-extras ).
    
      ENDMETHOD.
    
      METHOD read.
    
        io_xml->read(
          EXPORTING iv_name = zif_abapgit_object_tabl=>c_s_dataname-segment_definition
          CHANGING  cg_data = rs_internal-segment_definitions ).
        io_xml->read(
          EXPORTING iv_name = 'DD02V'
          CHANGING cg_data = rs_internal-dd02v ).
        io_xml->read(
          EXPORTING iv_name = 'DD09L'
          CHANGING cg_data = rs_internal-dd09l ).
        io_xml->read(
          EXPORTING iv_name  = 'DD03P_TABLE'
          CHANGING cg_data = rs_internal-dd03p ).
        io_xml->read(
          EXPORTING iv_name = 'DD05M_TABLE'
          CHANGING cg_data = rs_internal-dd05m ).
        io_xml->read(
          EXPORTING iv_name = 'DD08V_TABLE'
          CHANGING cg_data = rs_internal-dd08v ).
        io_xml->read(
          EXPORTING iv_name = 'DD35V_TALE'
          CHANGING cg_data = rs_internal-dd35v ).
        io_xml->read(
          EXPORTING iv_name = 'DD36M'
          CHANGING cg_data = rs_internal-dd36m ).
        io_xml->read(
          EXPORTING iv_name = zif_abapgit_object_tabl=>c_s_dataname-tabl_extras
          CHANGING cg_data = rs_internal-extras ).
        io_xml->read(
          EXPORTING iv_name = 'DD12V'
          CHANGING cg_data = rs_internal-dd12v ).
        io_xml->read(
          EXPORTING iv_name = 'DD17V'
          CHANGING cg_data = rs_internal-dd17v ).
        io_xml->read(
          EXPORTING iv_name = 'I18N_LANGS'
          CHANGING  cg_data = rs_internal-i18n_langs ).
        io_xml->read(
          EXPORTING iv_name = 'DD02_TEXTS'
          CHANGING  cg_data = rs_internal-dd02_texts ).
        io_xml->read(
          EXPORTING iv_name = 'LONGTEXTS'
          CHANGING  cg_data = rs_internal-longtexts ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_tabl IMPLEMENTATION.
    
      METHOD clear_dd03p_fields.
    
        CONSTANTS lc_comptype_dataelement TYPE comptype VALUE 'E'.
    
        DATA: lv_masklen TYPE c LENGTH 4.
    
        FIELD-SYMBOLS:  TYPE dd03p.
    
    * remove nested structures
        DELETE ct_dd03p WHERE depth <> '00'.
    * remove fields from .INCLUDEs
        DELETE ct_dd03p WHERE adminfield <> '0'.
    
        LOOP AT ct_dd03p ASSIGNING  WHERE NOT rollname IS INITIAL.
    
          clear_dd03p_fields_common( CHANGING cs_dd03p =  ).
    
          lv_masklen = -masklen.
          IF lv_masklen = '' OR NOT lv_masklen CO '0123456789'.
    * make sure the field contains valid data, or the XML will dump
            CLEAR -masklen.
          ENDIF.
    
          IF -comptype = lc_comptype_dataelement.
            clear_dd03p_fields_dataelement( CHANGING cs_dd03p =  ).
          ENDIF.
    
          IF -shlporigin = 'D'.
    * search help from domain
            CLEAR: -shlpfield,
                   -shlpname.
          ENDIF.
    
    * XML output assumes correct field content
          IF -routputlen = '      '.
            CLEAR -routputlen.
          ENDIF.
    
        ENDLOOP.
    
        " Clear position to avoid issues with include structures that contain different number of fields
        LOOP AT ct_dd03p ASSIGNING .
          CLEAR: -position, -tabname, -ddlanguage.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD clear_dd03p_fields_common.
    
        CLEAR: cs_dd03p-ddlanguage,
               cs_dd03p-dtelmaster,
               cs_dd03p-logflag,
               cs_dd03p-ddtext,
               cs_dd03p-reservedte,
               cs_dd03p-reptext,
               cs_dd03p-scrtext_s,
               cs_dd03p-scrtext_m,
               cs_dd03p-scrtext_l.
    
      ENDMETHOD.
    
      METHOD clear_dd03p_fields_dataelement.
    
    * type specified via data element
        CLEAR: cs_dd03p-domname,
               cs_dd03p-inttype,
               cs_dd03p-intlen,
               cs_dd03p-mask,
               cs_dd03p-memoryid,
               cs_dd03p-headlen,
               cs_dd03p-scrlen1,
               cs_dd03p-scrlen2,
               cs_dd03p-scrlen3,
               cs_dd03p-datatype,
               cs_dd03p-leng,
               cs_dd03p-outputlen,
               cs_dd03p-deffdname,
               cs_dd03p-convexit,
               cs_dd03p-entitytab,
               cs_dd03p-dommaster,
               cs_dd03p-domname3l,
               cs_dd03p-decimals,
               cs_dd03p-lowercase,
               cs_dd03p-signflag.
    
      ENDMETHOD.
    
      METHOD delete_extras.
    
        DELETE FROM tddat WHERE tabname = iv_tabname.
    
      ENDMETHOD.
    
      METHOD delete_idoc_segment.
    
        DATA lv_segment_type        TYPE edilsegtyp.
        DATA lv_result              LIKE sy-subrc.
    
        IF is_idoc_segment( ) = abap_false.
          rv_deleted = abap_false.
          RETURN. "previous XML version or no IDoc segment
        ENDIF.
    
        rv_deleted = abap_true.
        lv_segment_type = ms_item-obj_name.
    
        CALL FUNCTION 'SEGMENT_DELETE'
          EXPORTING
            segmenttyp = lv_segment_type
          IMPORTING
            result     = lv_result
          EXCEPTIONS
            OTHERS     = 1.
        IF sy-subrc <> 0 OR lv_result <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
      ENDMETHOD.
    
      METHOD deserialize_idoc_segment.
    
        DATA lv_result              LIKE sy-subrc.
        DATA lv_package             TYPE devclass.
        DATA lv_transport           TYPE trkorr.
        DATA ls_edisdef             TYPE edisdef.
        DATA ls_segment_definition  TYPE zif_abapgit_object_tabl=>ty_segment_definition.
        FIELD-SYMBOLS  TYPE zif_abapgit_object_tabl=>ty_segment_definition.
    
        rv_deserialized = abap_false.
    
        IF lines( is_internal-segment_definitions ) = 0.
          RETURN. "no IDoc segment
        ENDIF.
    
        rv_deserialized = abap_true.
    
        lv_package = iv_package.
        lv_transport = iv_transport.
    
        LOOP AT is_internal-segment_definitions ASSIGNING .
          ls_segment_definition = .
          ls_segment_definition-segmentheader-presp = sy-uname.
          ls_segment_definition-segmentheader-pwork = sy-uname.
    
          CALL FUNCTION 'SEGMENT_READ'
            EXPORTING
              segmenttyp = ls_segment_definition-segmentdefinition-segtyp
            IMPORTING
              result     = lv_result
            EXCEPTIONS
              OTHERS     = 1.
          IF sy-subrc <> 0 OR lv_result <> 0.
            CALL FUNCTION 'SEGMENT_CREATE'
              IMPORTING
                segmentdefinition = ls_segment_definition-segmentdefinition
              TABLES
                segmentstructure  = ls_segment_definition-segmentstructures
              CHANGING
                segmentheader     = ls_segment_definition-segmentheader
                devclass          = lv_package
              EXCEPTIONS
                OTHERS            = 1.
          ELSE.
    
            CALL FUNCTION 'SEGMENT_MODIFY'
              CHANGING
                segmentheader = ls_segment_definition-segmentheader
                devclass      = lv_package
              EXCEPTIONS
                OTHERS        = 1.
            IF sy-subrc = 0.
              CALL FUNCTION 'SEGMENTDEFINITION_MODIFY'
                TABLES
                  segmentstructure  = ls_segment_definition-segmentstructures
                CHANGING
                  segmentdefinition = ls_segment_definition-segmentdefinition
                EXCEPTIONS
                  OTHERS            = 1.
            ENDIF.
          ENDIF.
    
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          " Check status of segment as stored in repo (field-symbol)
          IF -segmentdefinition-closed = abap_true.
            IF lv_transport IS NOT INITIAL.
              CALL FUNCTION 'SEGMENTDEFINITION_CLOSE'
                EXPORTING
                  segmenttyp = ls_segment_definition-segmentdefinition-segtyp
                CHANGING
                  order      = lv_transport
                EXCEPTIONS
                  OTHERS     = 1.
              IF sy-subrc <> 0.
                zcx_abapgit_exception=>raise_t100( ).
              ENDIF.
            ENDIF.
    
            " SEGMENTDEFINITION_CLOSE saves current release but it should be same as in repo
            SELECT SINGLE * FROM edisdef INTO ls_edisdef
              WHERE segtyp  = ls_segment_definition-segmentdefinition-segtyp
                AND version = ls_segment_definition-segmentdefinition-version.
            ls_edisdef-released = -segmentdefinition-released.
            ls_edisdef-applrel  = -segmentdefinition-applrel.
            ls_edisdef-closed   = -segmentdefinition-closed.
            UPDATE edisdef FROM ls_edisdef.
            IF sy-subrc <> 0.
              zcx_abapgit_exception=>raise( |Error updating IDOC segment {
                ls_segment_definition-segmentdefinition-segtyp }| ).
            ENDIF.
          ENDIF.
        ENDLOOP.
    
        zcl_abapgit_factory=>get_tadir( )->insert_single(
          iv_object      = ms_item-obj_type
          iv_obj_name    = ms_item-obj_name
          iv_package     = iv_package
          iv_language    = mv_language
          iv_set_edtflag = abap_true ).
      ENDMETHOD.
    
      METHOD deserialize_indexes.
    
        DATA:
          lv_name      TYPE ddobjname,
          lv_subrc     TYPE sy-subrc,
          lt_dd12v_db  LIKE is_internal-dd12v,
          ls_dd12v     LIKE LINE OF is_internal-dd12v,
          ls_dd17v     LIKE LINE OF is_internal-dd17v,
          lt_secondary LIKE is_internal-dd17v.
    
        lv_name = ms_item-obj_name.
    
        " Get existing indexes and drop the ones that are not included in remote
        CALL FUNCTION 'DDIF_TABL_GET'
          EXPORTING
            name          = lv_name
            langu         = mv_language
          TABLES
            dd12v_tab     = lt_dd12v_db
          EXCEPTIONS
            illegal_input = 1
            OTHERS        = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error from DDIF_TABL_GET' ).
        ENDIF.
    
        LOOP AT lt_dd12v_db INTO ls_dd12v.
          READ TABLE is_internal-dd12v TRANSPORTING NO FIELDS WITH KEY
            sqltab    = ls_dd12v-sqltab
            indexname = ls_dd12v-indexname.
          IF sy-subrc <> 0.
            CALL FUNCTION 'DD_INDX_DEL'
              EXPORTING
                sqltab    = ls_dd12v-sqltab
                indexname = ls_dd12v-indexname
                del_state = 'M'     "all states
              IMPORTING
                rc        = lv_subrc.
            IF lv_subrc <> 0.
              zcx_abapgit_exception=>raise( |Error deleting index { ls_dd12v-sqltab }~{ ls_dd12v-indexname }| ).
            ENDIF.
          ENDIF.
        ENDLOOP.
    
        " Create new or update existing indexes
        LOOP AT is_internal-dd12v INTO ls_dd12v.
    
          CLEAR lt_secondary.
          LOOP AT is_internal-dd17v INTO ls_dd17v
              WHERE sqltab = ls_dd12v-sqltab AND indexname = ls_dd12v-indexname.
            APPEND ls_dd17v TO lt_secondary.
          ENDLOOP.
    
          CALL FUNCTION 'DDIF_INDX_PUT'
            EXPORTING
              name              = ls_dd12v-sqltab
              id                = ls_dd12v-indexname
              dd12v_wa          = ls_dd12v
            TABLES
              dd17v_tab         = lt_secondary
            EXCEPTIONS
              indx_not_found    = 1
              name_inconsistent = 2
              indx_inconsistent = 3
              put_failure       = 4
              put_refused       = 5
              OTHERS            = 6.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          " Secondary indexes are automatically activated as part of R3TR TABL
          " So there's no need to add them to activation queue
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD deserialize_texts.
    
        DATA: lv_name      TYPE ddobjname,
              ls_dd02v_tmp TYPE dd02v.
    
        FIELD-SYMBOLS:       LIKE LINE OF cs_internal-i18n_langs,
                        LIKE LINE OF cs_internal-dd02_texts.
    
        lv_name = ms_item-obj_name.
    
        mo_i18n_params->trim_saplang_list( CHANGING ct_sap_langs = cs_internal-i18n_langs ).
    
        SORT cs_internal-i18n_langs.
        SORT cs_internal-dd02_texts BY ddlanguage. " Optimization
    
        LOOP AT cs_internal-i18n_langs ASSIGNING .
    
          " Table description
          ls_dd02v_tmp = cs_internal-dd02v.
          READ TABLE cs_internal-dd02_texts ASSIGNING  WITH KEY ddlanguage = .
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( |DD02_TEXTS cannot find lang {  } in XML| ).
          ENDIF.
          MOVE-CORRESPONDING  TO ls_dd02v_tmp.
          CALL FUNCTION 'DDIF_TABL_PUT'
            EXPORTING
              name              = lv_name
              dd02v_wa          = ls_dd02v_tmp
            EXCEPTIONS
              tabl_not_found    = 1
              name_inconsistent = 2
              tabl_inconsistent = 3
              put_failure       = 4
              put_refused       = 5
              OTHERS            = 6.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD is_idoc_segment.
    
        DATA lv_segment_type TYPE edilsegtyp.
    
        lv_segment_type = ms_item-obj_name.
    
        SELECT SINGLE segtyp
               FROM edisegment
               INTO lv_segment_type
               WHERE segtyp = lv_segment_type.
        rv_is_idoc_segment = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD read_extras.
    
        SELECT SINGLE * FROM tddat INTO rs_tabl_extras-tddat WHERE tabname = iv_tabname.
    
        rs_tabl_extras-abap_language_version = get_abap_language_version( ).
    
      ENDMETHOD.
    
      METHOD serialize_idoc_segment.
    
        DATA lv_segment_type        TYPE edilsegtyp.
        DATA lv_result              LIKE sy-subrc.
        DATA lv_devclass            TYPE devclass.
        DATA lt_segmentdefinitions  TYPE STANDARD TABLE OF edisegmdef.
        DATA ls_segment_definition  TYPE zif_abapgit_object_tabl=>ty_segment_definition.
    
        FIELD-SYMBOLS:  TYPE edisegmdef.
    
        IF is_idoc_segment( ) = abap_false.
          RETURN.
        ENDIF.
    
        lv_segment_type = ms_item-obj_name.
        CALL FUNCTION 'SEGMENT_READ'
          EXPORTING
            segmenttyp        = lv_segment_type
          IMPORTING
            result            = lv_result
          TABLES
            segmentdefinition = lt_segmentdefinitions
          EXCEPTIONS
            OTHERS            = 1.
        IF sy-subrc <> 0 OR lv_result <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        LOOP AT lt_segmentdefinitions ASSIGNING .
          CLEAR ls_segment_definition.
          CALL FUNCTION 'SEGMENTDEFINITION_READ'
            EXPORTING
              segmenttyp           = -segtyp
            IMPORTING
              result               = lv_result
              devclass             = lv_devclass
              segmentheader        = ls_segment_definition-segmentheader
              segmentdefinition    = ls_segment_definition-segmentdefinition
            TABLES
              segmentstructure     = ls_segment_definition-segmentstructures
            CHANGING
              version              = -version
            EXCEPTIONS
              no_authority         = 1
              segment_not_existing = 2
              OTHERS               = 3.
          IF sy-subrc <> 0 OR lv_result <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          zcl_abapgit_object_idoc=>clear_idoc_segement_fields(
                                     CHANGING cg_structure = ls_segment_definition-segmentdefinition ).
          zcl_abapgit_object_idoc=>clear_idoc_segement_fields(
                                     CHANGING cg_structure = ls_segment_definition-segmentheader ).
    
          APPEND ls_segment_definition TO cs_internal-segment_definitions.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD serialize_texts.
    
        DATA: lv_name            TYPE ddobjname,
              lv_index           TYPE i,
              ls_dd02v           TYPE dd02v,
              lt_language_filter TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        FIELD-SYMBOLS:       LIKE LINE OF cs_internal-i18n_langs,
                        LIKE LINE OF cs_internal-dd02_texts.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          RETURN.
        ENDIF.
    
        lv_name = ms_item-obj_name.
    
        " Collect additional languages, skip main lang - it was serialized already
        lt_language_filter = mo_i18n_params->build_language_filter( ).
    
        SELECT DISTINCT ddlanguage AS langu INTO TABLE cs_internal-i18n_langs
          FROM dd02v
          WHERE tabname = lv_name
          AND ddlanguage IN lt_language_filter
          AND ddlanguage <> mv_language
          ORDER BY langu.                                     "#EC CI_SUBRC
    
        LOOP AT cs_internal-i18n_langs ASSIGNING .
          lv_index = sy-tabix.
          CALL FUNCTION 'DDIF_TABL_GET'
            EXPORTING
              name          = lv_name
              langu         = 
            IMPORTING
              dd02v_wa      = ls_dd02v
            EXCEPTIONS
              illegal_input = 1
              OTHERS        = 2.
          IF sy-subrc <> 0 OR ls_dd02v-ddlanguage IS INITIAL.
            DELETE cs_internal-i18n_langs INDEX lv_index. " Don't save this lang
            CONTINUE.
          ENDIF.
    
          APPEND INITIAL LINE TO cs_internal-dd02_texts ASSIGNING .
          MOVE-CORRESPONDING ls_dd02v TO .
        ENDLOOP.
    
        SORT cs_internal-i18n_langs ASCENDING.
        SORT cs_internal-dd02_texts BY ddlanguage ASCENDING.
    
      ENDMETHOD.
    
      METHOD update_extras.
    
        DATA lv_abap_language_version TYPE uccheck.
    
        IF is_tabl_extras-tddat IS INITIAL.
          delete_extras( iv_tabname ).
        ELSE.
          MODIFY tddat FROM is_tabl_extras-tddat.
        ENDIF.
    
        " Fields that are not part of dd02v
        TRY.
            lv_abap_language_version = is_tabl_extras-abap_language_version.
    
            set_abap_language_version( CHANGING cv_abap_language_version = lv_abap_language_version ).
    
            UPDATE ('DD02L') SET abap_language_version = lv_abap_language_version WHERE tabname = iv_tabname.
          CATCH cx_sy_dynamic_osql_semantics ##NO_HANDLER.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        TYPES: BEGIN OF ty_data,
                 as4user TYPE dd02l-as4user,
                 as4date TYPE dd02l-as4date,
                 as4time TYPE dd02l-as4time,
               END OF ty_data.
    
        DATA: lt_data TYPE STANDARD TABLE OF ty_data WITH DEFAULT KEY,
              ls_data LIKE LINE OF lt_data.
    
        SELECT as4user as4date as4time
          FROM dd02l INTO TABLE lt_data
          WHERE tabname = ms_item-obj_name
          AND as4local = 'A'
          AND as4vers = '0000'
          ORDER BY PRIMARY KEY.
    
        SELECT as4user as4date as4time
          APPENDING TABLE lt_data
          FROM dd09l
          WHERE tabname = ms_item-obj_name
          AND as4local = 'A'
          AND as4vers = '0000'
          ORDER BY PRIMARY KEY.
    
        SELECT as4user as4date as4time
          APPENDING TABLE lt_data
          FROM dd12l
          WHERE sqltab = ms_item-obj_name
          AND as4local = 'A'
          AND as4vers = '0000'
          ORDER BY PRIMARY KEY.
    
        SORT lt_data BY as4date DESCENDING as4time DESCENDING.
    
        READ TABLE lt_data INDEX 1 INTO ls_data.
        IF sy-subrc = 0.
          rv_user = ls_data-as4user.
        ELSE.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_objname TYPE rsedd0-ddobjname.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          " Proxies e.g. delete on its own, nothing todo here then.
          RETURN.
        ENDIF.
    
        lv_objname = ms_item-obj_name.
    
        IF delete_idoc_segment( ) = abap_false.
    
          " User has already confirmed deletion of tables with data in deserialize_checks
          delete_ddic( iv_objtype = 'T'
                       iv_no_ask  = abap_true ).
    
          delete_longtexts( c_longtext_id_tabl ).
    
          delete_extras( lv_objname ).
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_name     TYPE ddobjname,
              ls_internal TYPE zif_abapgit_object_tabl=>ty_internal.
    
        FIELD-SYMBOLS:       TYPE dd03p,
                             TYPE dd05m,
                             TYPE dd08v,
                             TYPE dd35v,
                             TYPE dd36m,
                        TYPE any.
    
        lv_name = ms_item-obj_name. " type conversion
    
        ls_internal = lcl_tabl_xml=>read( io_xml ).
    
        IF deserialize_idoc_segment( is_internal  = ls_internal
                                     iv_transport = iv_transport
                                     iv_package   = iv_package ) = abap_false.
    
          ASSIGN COMPONENT 'ROWORCOLST' OF STRUCTURE ls_internal-dd09l TO .
          IF sy-subrc = 0 AND  IS INITIAL.
             = 'C'. "Reverse fix from serialize
          ENDIF.
    
          " Number fields sequentially and fill table name
          LOOP AT ls_internal-dd03p ASSIGNING .
            -position   = sy-tabix.
            -tabname    = lv_name.
            -ddlanguage = mv_language.
          ENDLOOP.
    
          LOOP AT ls_internal-dd05m ASSIGNING .
            -tabname = lv_name.
          ENDLOOP.
          LOOP AT ls_internal-dd08v ASSIGNING .
            -tabname = lv_name.
            -ddlanguage = mv_language.
          ENDLOOP.
          LOOP AT ls_internal-dd35v ASSIGNING .
            -tabname = lv_name.
          ENDLOOP.
          LOOP AT ls_internal-dd36m ASSIGNING .
            -tabname = lv_name.
          ENDLOOP.
    
          corr_insert( iv_package = iv_package
                       ig_object_class = 'DICT' ).
    
          CALL FUNCTION 'DD_TABL_EXPAND'
            EXPORTING
              dd02v_wa          = ls_internal-dd02v
            TABLES
              dd03p_tab         = ls_internal-dd03p
              dd05m_tab         = ls_internal-dd05m
              dd08v_tab         = ls_internal-dd08v
              dd35v_tab         = ls_internal-dd35v
              dd36m_tab         = ls_internal-dd36m
            EXCEPTIONS
              illegal_parameter = 1
              OTHERS            = 2.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          CALL FUNCTION 'DDIF_TABL_PUT'
            EXPORTING
              name              = lv_name
              dd02v_wa          = ls_internal-dd02v
              dd09l_wa          = ls_internal-dd09l
            TABLES
              dd03p_tab         = ls_internal-dd03p
              dd05m_tab         = ls_internal-dd05m
              dd08v_tab         = ls_internal-dd08v
              dd35v_tab         = ls_internal-dd35v
              dd36m_tab         = ls_internal-dd36m
            EXCEPTIONS
              tabl_not_found    = 1
              name_inconsistent = 2
              tabl_inconsistent = 3
              put_failure       = 4
              put_refused       = 5
              OTHERS            = 6.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          zcl_abapgit_objects_activation=>add_item( ms_item ).
    
          deserialize_indexes( ls_internal ).
    
          IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
            deserialize_texts( CHANGING cs_internal = ls_internal ).
          ENDIF.
    
          deserialize_longtexts( ii_xml         = io_xml
                                 iv_longtext_id = c_longtext_id_tabl ).
    
          update_extras( iv_tabname     = lv_name
                         is_tabl_extras = ls_internal-extras ).
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA lv_tabname TYPE dd02l-tabname.
        DATA ls_x030l   TYPE x030l.
    
        lv_tabname = ms_item-obj_name.
    
        " Check nametab because it's fast
        CALL FUNCTION 'DD_GET_NAMETAB_HEADER'
          EXPORTING
            tabname   = lv_tabname
          IMPORTING
            x030l_wa  = ls_x030l
          EXCEPTIONS
            not_found = 1
            OTHERS    = 2.
        IF sy-subrc <> 0.
          " Check for new, inactive, or modified versions that might not be in nametab
          SELECT SINGLE tabname FROM dd02l INTO lv_tabname
            WHERE tabname = lv_tabname.                     "#EC CI_NOORDER
        ENDIF.
        rv_bool = boolc( sy-subrc = 0 ).
    
        " Skip TABL structures generated by CHDO
        IF rv_bool = abap_true AND ( ls_x030l-tabtype = 'J' OR ls_x030l-tabtype IS INITIAL ).
          SELECT SINGLE tabname FROM tcdrs INTO lv_tabname WHERE tabname = lv_tabname.
          IF sy-subrc = 0.
            rv_bool = abap_false.
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        " Moved to zcl_abapgit_objects_compare
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESDICT'
                                                iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by ZCL_ABAPGIT_OBJECT=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_name     TYPE ddobjname,
              lv_state    TYPE ddgotstate,
              ls_internal TYPE zif_abapgit_object_tabl=>ty_internal,
              lv_index    LIKE sy-index.
    
        FIELD-SYMBOLS:       LIKE LINE OF ls_internal-dd12v,
                             LIKE LINE OF ls_internal-dd05m,
                             LIKE LINE OF ls_internal-dd08v,
                             LIKE LINE OF ls_internal-dd35v,
                             LIKE LINE OF ls_internal-dd36m,
                             LIKE LINE OF ls_internal-dd03p,
                             TYPE any,
                        TYPE any.
    
        lv_name = ms_item-obj_name.
    
        CALL FUNCTION 'DDIF_TABL_GET'
          EXPORTING
            name          = lv_name
            langu         = mv_language
          IMPORTING
            gotstate      = lv_state
            dd02v_wa      = ls_internal-dd02v
            dd09l_wa      = ls_internal-dd09l
          TABLES
            dd03p_tab     = ls_internal-dd03p
            dd05m_tab     = ls_internal-dd05m
            dd08v_tab     = ls_internal-dd08v
            dd12v_tab     = ls_internal-dd12v
            dd17v_tab     = ls_internal-dd17v
            dd35v_tab     = ls_internal-dd35v
            dd36m_tab     = ls_internal-dd36m
          EXCEPTIONS
            illegal_input = 1
            OTHERS        = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error from DDIF_TABL_GET' ).
        ENDIF.
    
        " Check if any active version was returned
        IF lv_state <> 'A'.
          RETURN.
        ENDIF.
    
        CLEAR: ls_internal-dd02v-as4user,
               ls_internal-dd02v-as4date,
               ls_internal-dd02v-as4time.
    
    * reset numeric field, so XML does not crash
        IF ls_internal-dd02v-prozpuff = ''.
          CLEAR ls_internal-dd02v-prozpuff.
        ENDIF.
        IF ls_internal-dd02v-datmin = ''.
          CLEAR ls_internal-dd02v-datmin.
        ENDIF.
        IF ls_internal-dd02v-datmax = ''.
          CLEAR ls_internal-dd02v-datmax.
        ENDIF.
        IF ls_internal-dd02v-datavg = ''.
          CLEAR ls_internal-dd02v-datavg.
        ENDIF.
    
        CLEAR: ls_internal-dd09l-as4user,
               ls_internal-dd09l-as4date,
               ls_internal-dd09l-as4time.
    
        ASSIGN COMPONENT 'ROWORCOLST' OF STRUCTURE ls_internal-dd09l TO .
        IF sy-subrc = 0 AND  = 'C'.
          CLEAR . "To avoid diff errors. This field doesn't exist in all releases
        ENDIF.
    
        LOOP AT ls_internal-dd03p ASSIGNING .
          ASSIGN COMPONENT 'ACTFLAG' OF STRUCTURE  TO .
          IF sy-subrc = 0.
            CLEAR . "To avoid diffs. This field doesn't exist in all releases
          ENDIF.
        ENDLOOP.
    
        LOOP AT ls_internal-dd12v ASSIGNING .
          CLEAR: -as4user,
                 -as4date,
                 -as4time,
                 -dbindex.
          IF -dbstate IS INITIAL OR -dbstate = 'O'.
            " These settings are only relevant if database-specific indexes are defined (dbstate = 'D')
            CLEAR:
              -dbinclexcl,
              -dbsyssel1,
              -dbsyssel2,
              -dbsyssel3,
              -dbsyssel4.
          ENDIF.
        ENDLOOP.
    
        clear_dd03p_fields( CHANGING ct_dd03p = ls_internal-dd03p ).
    
    * remove foreign keys inherited from .INCLUDEs
        DELETE ls_internal-dd08v WHERE noinherit = 'N'.
        LOOP AT ls_internal-dd05m ASSIGNING .
          CLEAR -tabname.
          CLEAR -leng.
          lv_index = sy-tabix.
          READ TABLE ls_internal-dd08v WITH KEY fieldname = -fieldname TRANSPORTING NO FIELDS.
          IF sy-subrc <> 0.
            DELETE ls_internal-dd05m INDEX lv_index.
          ENDIF.
        ENDLOOP.
    
        LOOP AT ls_internal-dd08v ASSIGNING .
          CLEAR: -tabname, -ddlanguage.
        ENDLOOP.
        LOOP AT ls_internal-dd35v ASSIGNING .
          CLEAR -tabname.
        ENDLOOP.
    
    * remove inherited search helps
        DELETE ls_internal-dd35v WHERE shlpinher = abap_true.
        LOOP AT ls_internal-dd36m ASSIGNING .
          CLEAR -tabname.
          CLEAR -rollname.
          CLEAR -domname.
          CLEAR -datatype.
          CLEAR -leng.
          lv_index = sy-tabix.
          READ TABLE ls_internal-dd35v WITH KEY fieldname = -fieldname TRANSPORTING NO FIELDS.
          IF sy-subrc <> 0.
            DELETE ls_internal-dd36m INDEX lv_index.
          ENDIF.
        ENDLOOP.
    
        SORT ls_internal-dd36m BY flposition.
    
        IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
          serialize_texts( CHANGING cs_internal = ls_internal ).
        ENDIF.
    
        ls_internal-longtexts = zcl_abapgit_factory=>get_longtexts( )->serialize(
          iv_object_name = ms_item-obj_name
          iv_longtext_id = c_longtext_id_tabl
          io_i18n_params = mo_i18n_params
          ii_xml         = io_xml ).
    
        serialize_idoc_segment( CHANGING cs_internal = ls_internal ).
    
        ls_internal-extras = read_extras( lv_name ).
    
        lcl_tabl_xml=>add(
          io_xml      = io_xml
          is_internal = ls_internal ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_tabl_compar IMPLEMENTATION.
    
      METHOD constructor.
    
        ms_item = is_item.
    
      ENDMETHOD.
    
      METHOD get_where_used_recursive.
    
        DATA: lt_findstrings TYPE string_table,
              lt_founds      TYPE STANDARD TABLE OF rsfindlst,
              lt_scope       TYPE ty_seu_obj,
              lv_findstring  LIKE LINE OF lt_findstrings.
    
        FIELD-SYMBOLS:  TYPE rsfindlst.
    
        IF iv_object_name IS INITIAL.
          RETURN.
        ENDIF.
    
        lt_scope = it_scope.
    
        lv_findstring = iv_object_name.
        INSERT lv_findstring INTO TABLE lt_findstrings.
    
        DO iv_depth TIMES.
    
          CLEAR: lt_founds.
    
          CALL FUNCTION 'RS_EU_CROSSREF'
            EXPORTING
              i_find_obj_cls           = iv_object_type
              no_dialog                = 'X'
            TABLES
              i_findstrings            = lt_findstrings
              o_founds                 = lt_founds
              i_scope_object_cls       = lt_scope
            EXCEPTIONS
              not_executed             = 1
              not_found                = 2
              illegal_object           = 3
              no_cross_for_this_object = 4
              batch                    = 5
              batchjob_error           = 6
              wrong_type               = 7
              object_not_exist         = 8
              OTHERS                   = 9.
    
          IF sy-subrc = 1 OR sy-subrc = 2 OR lines( lt_founds ) = 0.
            EXIT.
          ELSEIF sy-subrc > 2.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          INSERT LINES OF lt_founds INTO TABLE rt_founds_all.
    
          CLEAR: lt_findstrings.
    
          LOOP AT lt_founds ASSIGNING .
    
            lv_findstring = -object.
            INSERT lv_findstring INTO TABLE lt_findstrings.
    
          ENDLOOP.
    
        ENDDO.
    
      ENDMETHOD.
    
      METHOD is_structure_used_in_db_table.
    
        DATA: lt_scope  TYPE ty_seu_obj,
              lt_founds TYPE ty_founds.
    
        APPEND 'TABL' TO lt_scope.
        APPEND 'STRU' TO lt_scope.
    
        lt_founds = get_where_used_recursive( iv_object_name = iv_object_name
                                              iv_object_type = 'STRU'
                                              it_scope       = lt_scope
                                              iv_depth       = 5 ).
    
        DELETE lt_founds WHERE object_cls <> 'DT'.
    
        rv_is_structure_used_in_db_tab = boolc( lines( lt_founds ) > 0 ).
    
      ENDMETHOD.
    
      METHOD validate.
    
        DATA: lt_old_table_fields TYPE TABLE OF dd03p,
              ls_old_table_field  LIKE LINE OF lt_old_table_fields,
              lt_new_table_fields TYPE TABLE OF dd03p,
              ls_new_table_field  LIKE LINE OF lt_new_table_fields,
              ls_dd02v            TYPE dd02v,
              lv_inconsistent     TYPE abap_bool.
    
        FIELD-SYMBOLS  TYPE abap_bool.
    
        ii_remote_version->read(
          EXPORTING
            iv_name = 'DD02V'
          CHANGING
            cg_data = ls_dd02v ).
    
        " We only want to compare transparent tables, or structures used in transparent tables
        IF ls_dd02v-tabclass <> 'TRANSP' AND is_structure_used_in_db_table( ls_dd02v-tabname ) = abap_false.
          RETURN.
        ENDIF.
    
        " No comparison for global temporary tables
        ASSIGN COMPONENT 'IS_GTT' OF STRUCTURE ls_dd02v TO .
        IF sy-subrc = 0 AND  = abap_true.
          RETURN.
        ENDIF.
    
        ii_local_version->read(
          EXPORTING
            iv_name = 'DD03P_TABLE'
          CHANGING
            cg_data = lt_old_table_fields ).
    
        ii_remote_version->read(
          EXPORTING
            iv_name = 'DD03P_TABLE'
          CHANGING
            cg_data = lt_new_table_fields ).
    
        LOOP AT lt_old_table_fields INTO ls_old_table_field.
          READ TABLE lt_new_table_fields WITH KEY fieldname = ls_old_table_field-fieldname
            INTO ls_new_table_field.
          IF sy-subrc = 0.
            " Field exists in old and new table. Compare data elements, next
            IF ls_new_table_field-rollname <> ls_old_table_field-rollname.
              IF ls_new_table_field-rollname IS NOT INITIAL AND ls_old_table_field-rollname IS NOT INITIAL.
                ii_log->add_info(
                  iv_msg  = |Field { ls_old_table_field-fieldname }: | &
                            |Data element changed from { ls_old_table_field-rollname } | &
                            |to { ls_new_table_field-rollname }|
                  is_item = ms_item ).
              ELSEIF ls_new_table_field-rollname IS NOT INITIAL.
                ii_log->add_info(
                  iv_msg  = |Field { ls_old_table_field-fieldname }: | &
                            |Data type changed from internal type | &
                            |{ ls_old_table_field-inttype }(length { ls_old_table_field-intlen }) | &
                            |to data element { ls_new_table_field-rollname }|
                  is_item = ms_item ).
              ELSEIF ls_old_table_field-rollname IS NOT INITIAL.
                ii_log->add_info(
                  iv_msg  = |Field { ls_old_table_field-fieldname }: | &
                            |Data type changed from data element { ls_old_table_field-rollname } | &
                            |to internal type | &
                            |{ ls_new_table_field-inttype }(length { ls_new_table_field-intlen })|
                  is_item = ms_item ).
              ENDIF.
              "TODO: perform several other checks, e.g. field length truncated, ...
              lv_inconsistent = abap_true.
            ENDIF.
          ELSE.
            " Field does not exist in new table anymore
            ii_log->add_info( iv_msg  = |Field { ls_old_table_field-fieldname } removed|
                              is_item = ms_item ).
            lv_inconsistent = abap_true.
          ENDIF.
        ENDLOOP.
    
        IF lv_inconsistent = abap_true.
          rv_message = 'Fields were changed!'.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_comparator~compare.
    
        IF /apmg/cl_apm_abapgit_objects=>exists( ms_item ) = abap_false.
          RETURN.
        ENDIF.
    
        rs_result-text = validate(
          ii_remote_version = ii_remote
          ii_local_version  = ii_local
          ii_log            = ii_log ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_tabl_ddl IMPLEMENTATION.
    
      METHOD deserialize.
    
    * https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/abenddicddl_define_table.htm
    
    * CL_SBD_STRUCTURE_OBJDATA has serializer in local class
    
        DATA lv_ddl    TYPE string.
        DATA lv_fields TYPE string.
        DATA lv_start  TYPE i.
        DATA lv_length TYPE i.
        DATA lv_end    TYPE i.
        DATA lt_fields TYPE STANDARD TABLE OF string WITH DEFAULT KEY.
        DATA lv_field  TYPE string.
    
        lv_ddl = iv_ddl.
    
        parse_top_annotations( CHANGING
          cs_data = rs_data
          cv_ddl  = lv_ddl ).
    
        FIND FIRST OCCURRENCE OF '{' IN lv_ddl MATCH OFFSET lv_start.
        ASSERT lv_start > 0.
        FIND FIRST OCCURRENCE OF '}' IN lv_ddl MATCH OFFSET lv_end.
        ASSERT lv_end > 0.
    
        lv_start = lv_start + 1.
        lv_length = lv_end - lv_start - 1.
        lv_fields = lv_ddl+lv_start(lv_length).
        SPLIT lv_fields AT |;| INTO TABLE lt_fields.
    
        LOOP AT lt_fields INTO lv_field WHERE table_line IS NOT INITIAL.
          parse_field( EXPORTING iv_field = lv_field CHANGING cs_data = rs_data ).
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD escape_string.
        rv_string = |'{ replace( val  = iv_string
                                 sub  = |'|
                                 with = |''|
                                 occ  = 0 ) }'|.
      ENDMETHOD.
    
      METHOD parse_field.
    
        CONSTANTS: BEGIN OF lc_mode,
                     start     TYPE i VALUE 0,
                     colon     TYPE i VALUE 1,
                     type      TYPE i VALUE 2,
                     aftertype TYPE i VALUE 3,
                     null      TYPE i VALUE 4,
                     afternull TYPE i VALUE 5,
                   END OF lc_mode.
    
        DATA lv_field  TYPE string.
        DATA lv_mode   TYPE i.
        DATA lt_tokens TYPE STANDARD TABLE OF string WITH DEFAULT KEY.
        DATA lv_token  TYPE string.
        DATA ls_dd08v  TYPE dd08v.
    
        FIELD-SYMBOLS  LIKE LINE OF cs_data-dd03p.
    
        lv_field = iv_field.
        parse_field_annotations(
          IMPORTING es_dd08v = ls_dd08v
          CHANGING cv_ddl = lv_field ).
    
        SPLIT lv_field AT space INTO TABLE lt_tokens.
    
        APPEND INITIAL LINE TO cs_data-dd03p ASSIGNING .
    
        LOOP AT lt_tokens INTO lv_token WHERE table_line IS NOT INITIAL.
          CASE lv_mode.
            WHEN lc_mode-start.
    * todo, is it possible to have a key field named "key" ?
              IF lv_token = 'key'.
                -keyflag = abap_true.
              ELSE.
                -fieldname = to_upper( lv_token ).
                lv_mode = lc_mode-colon.
              ENDIF.
            WHEN lc_mode-colon.
              ASSERT lv_token = ':'.
              lv_mode = lc_mode-type.
            WHEN lc_mode-type.
              parse_type(
                EXPORTING iv_token = lv_token
                CHANGING cs_dd03p =  ).
              RETURN.
            WHEN lc_mode-aftertype.
              IF lv_token = 'not'.
                -notnull = abap_true.
                lv_mode = lc_mode-null.
              ENDIF.
            WHEN lc_mode-null.
              ASSERT lv_token = 'null'.
              lv_mode = lc_mode-afternull.
            WHEN lc_mode-afternull.
              ASSERT lv_token = 'with'.
              RETURN. " todo
            WHEN OTHERS.
              ASSERT 1 = 'todo'.
          ENDCASE.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD parse_field_annotations.
    
        DATA lv_annotation TYPE string.
        DATA lv_name       TYPE string.
        DATA lv_value      TYPE string.
    
        REPLACE FIRST OCCURRENCE OF REGEX '^[\n ]*' IN cv_ddl WITH || ##REGEX_POSIX.
    
        WHILE cv_ddl CP '@*'.
          SPLIT cv_ddl AT |\n| INTO lv_annotation cv_ddl.
          CONDENSE cv_ddl.
    
          SPLIT lv_annotation AT ':' INTO lv_name lv_value.
          CONDENSE lv_name.
          CONDENSE lv_value.
          ASSERT lv_name IS NOT INITIAL.
          ASSERT lv_value IS NOT INITIAL.
    
          CASE lv_name.
            WHEN '@AbapCatalog.foreignKey.label'.
              es_dd08v-ddtext = unescape_string( lv_value ).
            WHEN '@AbapCatalog.foreignKey.keyType'.
              ASSERT lv_value(1) = '#'.
              es_dd08v-frkart = lv_value+1.
            WHEN '@AbapCatalog.foreignKey.screenCheck'.
              ASSERT lv_value = 'true'.
            WHEN OTHERS.
              WRITE: / 'todo:', lv_name, lv_value.
              ASSERT 1 = 'todo'.
          ENDCASE.
        ENDWHILE.
    
      ENDMETHOD.
    
      METHOD parse_top_annotations.
    
        DATA lv_annotation TYPE string.
        DATA lv_name       TYPE string.
        DATA lv_value      TYPE string.
    
        WHILE cv_ddl CP '@*'.
          SPLIT cv_ddl AT |\n| INTO lv_annotation cv_ddl.
          SPLIT lv_annotation AT ':' INTO lv_name lv_value.
          CONDENSE lv_name.
          CONDENSE lv_value.
          ASSERT lv_name IS NOT INITIAL.
          ASSERT lv_value IS NOT INITIAL.
    
          CASE lv_name.
            WHEN '@EndUserText.label'.
              cs_data-dd02v-ddtext = unescape_string( lv_value ).
            WHEN '@AbapCatalog.enhancementCategory'.
              CASE lv_value.
                WHEN '#NOT_EXTENSIBLE'.
                  cs_data-dd02v-contflag = '1'.
                WHEN OTHERS.
                  ASSERT 1 = 'todo'.
              ENDCASE.
            WHEN '@AbapCatalog.tableCategory'.
              CASE lv_value.
                WHEN '#TRANSPARENT'.
                  cs_data-dd02v-tabclass = 'TRANSP'.
                WHEN OTHERS.
                  ASSERT 1 = 'todo'.
              ENDCASE.
            WHEN '@AbapCatalog.deliveryClass'.
              ASSERT lv_value(1) = '#'.
              cs_data-dd02v-contflag = lv_value+1.
            WHEN '@AbapCatalog.dataMaintenance'.
              CASE lv_value.
                WHEN '#ALLOWED'.
                  cs_data-dd02v-mainflag = abap_true.
                WHEN '#LIMITED'.
                  cs_data-dd02v-mainflag = abap_false.
                WHEN OTHERS.
                  ASSERT 1 = 'todo'.
              ENDCASE.
            WHEN OTHERS.
              WRITE: / 'todo:', lv_name, lv_value.
              ASSERT 1 = 'todo'.
          ENDCASE.
    
        ENDWHILE.
    
      ENDMETHOD.
    
      METHOD parse_type.
    
        DATA lv_token TYPE string.
    
        lv_token = iv_token.
        IF lv_token CP 'abap.*'.
          lv_token = lv_token+5.
          IF lv_token(4) = 'char'.
    * todo, length
            cs_dd03p-datatype = 'CHAR'.
          ELSEIF lv_token(6) = 'string'.
            cs_dd03p-intlen = 8.
            cs_dd03p-inttype = 'g'.
            cs_dd03p-datatype = 'STRG'.
          ELSE.
            ASSERT 1 = 'todo'.
          ENDIF.
        ELSE.
          cs_dd03p-rollname = to_upper( lv_token ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD read_data.
    * temporary method for testing
    
        DATA lv_name TYPE ddobjname.
    
        lv_name = iv_name.
    
        CALL FUNCTION 'DDIF_TABL_GET'
          EXPORTING
            name          = lv_name
            langu         = 'E'
          IMPORTING
            dd02v_wa      = rs_data-dd02v
            dd09l_wa      = rs_data-dd09l
          TABLES
            dd03p_tab     = rs_data-dd03p
            dd05m_tab     = rs_data-dd05m
            dd08v_tab     = rs_data-dd08v
            dd12v_tab     = rs_data-dd12v
            dd17v_tab     = rs_data-dd17v
            dd35v_tab     = rs_data-dd35v
            dd36m_tab     = rs_data-dd36m
          EXCEPTIONS
            illegal_input = 1
            OTHERS        = 2.
        ASSERT sy-subrc = 0.
    
      ENDMETHOD.
    
      METHOD serialize.
    
        DATA ls_dd03p   LIKE LINE OF is_data-dd03p.
        DATA lv_key     TYPE string.
        DATA lv_type    TYPE string.
        DATA lv_pre     TYPE string.
        DATA lv_int     TYPE i.
        DATA lv_suffix  TYPE string.
        DATA lv_notnull TYPE string.
        DATA lv_colon   TYPE i.
    
        rv_ddl = rv_ddl && serialize_top( is_data ).
    
        rv_ddl = rv_ddl && |define table { to_lower( is_data-dd02v-tabname ) } \{\n|.
    
        LOOP AT is_data-dd03p INTO ls_dd03p
            WHERE ( fieldname NP '.INCLU*' OR groupname IS NOT INITIAL )
            AND adminfield = '0'.
          lv_int = 0.
          IF ls_dd03p-keyflag = abap_true.
            lv_int = 4.
          ENDIF.
          IF ls_dd03p-groupname IS INITIAL.
            lv_int = lv_int + strlen( ls_dd03p-fieldname ).
          ELSE.
            lv_int = lv_int + strlen( ls_dd03p-groupname ).
          ENDIF.
          IF lv_int > lv_colon.
            lv_colon = lv_int.
          ENDIF.
        ENDLOOP.
    
    * ADMINFIELD: skip fields inside .INCLUDEs
        LOOP AT is_data-dd03p INTO ls_dd03p WHERE adminfield = '0'.
          CLEAR lv_key.
          CLEAR lv_notnull.
          IF ls_dd03p-keyflag = abap_true.
            lv_key = |key |.
          ENDIF.
    
          lv_pre = |{ lv_key }{ to_lower( ls_dd03p-fieldname ) }|.
          IF ls_dd03p-groupname IS NOT INITIAL.
            lv_pre = |{ lv_key }{ to_lower( ls_dd03p-groupname ) }|.
          ENDIF.
          IF strlen( lv_pre ) < lv_colon.
            lv_pre = lv_pre && repeat(
              val = | |
              occ = lv_colon - strlen( lv_pre ) ).
          ENDIF.
    
          IF ls_dd03p-fieldname = '.INCLU--AP'.
            CONTINUE.
          ELSEIF ls_dd03p-fieldname CP '.INCLU*'.
            IF ls_dd03p-notnull = abap_true.
              lv_notnull = | not null|.
            ENDIF.
            CLEAR lv_suffix.
            IF ls_dd03p-fieldname CA '-'.
              SPLIT ls_dd03p-fieldname AT '-' INTO lv_suffix lv_suffix.
              lv_suffix = | with suffix { to_lower( lv_suffix ) }|.
            ENDIF.
            IF ls_dd03p-groupname IS INITIAL.
              rv_ddl = rv_ddl && |  { lv_key }include { to_lower( ls_dd03p-precfield ) }{ lv_suffix }{ lv_notnull }|.
            ELSE.
              rv_ddl = rv_ddl && |  { lv_pre } : include { to_lower( ls_dd03p-precfield ) }{ lv_suffix }{ lv_notnull }|.
            ENDIF.
            rv_ddl = rv_ddl && serialize_extend(
              is_dd03p = ls_dd03p
              is_data  = is_data ).
            rv_ddl = rv_ddl && |;\n|.
            CONTINUE.
          ENDIF.
    
          rv_ddl = rv_ddl && serialize_field_annotations(
            iv_fieldname = ls_dd03p-fieldname
            is_data      = is_data ).
          rv_ddl = rv_ddl && serialize_fkey_annotations(
            iv_fieldname = ls_dd03p-fieldname
            is_data      = is_data ).
    
          lv_type = serialize_type( ls_dd03p ).
          rv_ddl = rv_ddl && |  { lv_pre } : { lv_type }|.
          rv_ddl = rv_ddl && serialize_field_foreign_key(
            iv_fieldname = ls_dd03p-fieldname
            is_data      = is_data ).
          rv_ddl = rv_ddl && serialize_value_help(
            iv_fieldname = ls_dd03p-fieldname
            is_data      = is_data ).
          rv_ddl = rv_ddl && |;\n|.
        ENDLOOP.
        rv_ddl = rv_ddl && |\n|.
    
        rv_ddl = rv_ddl && |\}|.
    
      ENDMETHOD.
    
      METHOD serialize_adt.
    
        DATA ls_object_type TYPE wbobjtype.
        DATA lv_object_key  TYPE seu_objkey.
        DATA li_object_data TYPE REF TO if_wb_object_data_model.
        DATA lo_operator    TYPE REF TO object.
    
        ls_object_type-objtype_tr = 'TABL'.
        ls_object_type-subtype_wb = 'DT'.
    
        lv_object_key = iv_name.
    
        CALL METHOD ('CL_WB_OBJECT_OPERATOR')=>('CREATE_INSTANCE')
          EXPORTING
            object_type = ls_object_type
            object_key  = lv_object_key
          RECEIVING
            result      = lo_operator.
    
        CALL METHOD lo_operator->('IF_WB_OBJECT_OPERATOR~READ')
          EXPORTING
            version        = 'A'
          IMPORTING
            eo_object_data = li_object_data.
    
        CALL METHOD li_object_data->('GET_CONTENT') IMPORTING p_data = rv_ddl.
    
      ENDMETHOD.
    
      METHOD serialize_extend.
    
        DATA lv_index  TYPE i.
        DATA ls_dd03p  LIKE LINE OF is_data-dd03p.
        DATA lt_fields TYPE STANDARD TABLE OF string WITH DEFAULT KEY.
        DATA lv_field  LIKE LINE OF lt_fields.
        DATA ls_dd08v  LIKE LINE OF is_data-dd08v.
        DATA ls_dd35v  LIKE LINE OF is_data-dd35v.
    
        READ TABLE is_data-dd03p TRANSPORTING NO FIELDS
          WITH KEY fieldname = is_dd03p-fieldname precfield = is_dd03p-precfield.
        ASSERT sy-subrc = 0.
        lv_index = sy-tabix + 1.
    
    * the extended keys are not sorted by the fieldname positions
        LOOP AT is_data-dd03p FROM lv_index INTO ls_dd03p.
          IF ls_dd03p-adminfield = '0'.
            EXIT.
          ENDIF.
          APPEND ls_dd03p-fieldname TO lt_fields.
        ENDLOOP.
        CLEAR ls_dd03p.
    
        LOOP AT is_data-dd08v INTO ls_dd08v
            WHERE ( noinherit = 'Y' OR checktable = '*' ) AND noinherit <> 'N'.
          READ TABLE lt_fields TRANSPORTING NO FIELDS
            WITH KEY table_line = ls_dd08v-fieldname.
          IF sy-subrc <> 0.
            CONTINUE.
          ENDIF.
          DELETE lt_fields WHERE table_line = ls_dd08v-fieldname.
    
          rv_ddl = rv_ddl && |\n|.
    
          IF ls_dd08v-checktable <> '*'.
            rv_ddl = rv_ddl && serialize_fkey_annotations(
              iv_fieldname = ls_dd08v-fieldname
              is_data      = is_data ).
          ENDIF.
    
          rv_ddl = rv_ddl && |  extend { to_lower( ls_dd08v-fieldname ) } :|.
    
          IF ls_dd08v-checktable = '*'.
            rv_ddl = rv_ddl && |\n    remove foreign key|.
          ELSE.
            rv_ddl = rv_ddl && serialize_field_foreign_key(
              iv_fieldname = ls_dd08v-fieldname
              is_data      = is_data ).
          ENDIF.
    
          READ TABLE is_data-dd35v INTO ls_dd35v WITH KEY fieldname = ls_dd08v-fieldname.
          IF sy-subrc = 0.
            IF ls_dd35v-shlpname = '*'.
              rv_ddl = rv_ddl && |\n    remove value help|.
            ELSEIF ls_dd35v-shlpinher <> abap_true.
              rv_ddl = rv_ddl && serialize_value_help(
                iv_fieldname = ls_dd08v-fieldname
                is_data      = is_data ).
            ENDIF.
          ENDIF.
        ENDLOOP.
    
        LOOP AT is_data-dd35v INTO ls_dd35v.
          READ TABLE lt_fields INTO lv_field
            WITH KEY table_line = ls_dd35v-fieldname.
          IF sy-subrc <> 0.
            CONTINUE.
          ENDIF.
    
          IF ls_dd35v-shlpname = '*'.
            rv_ddl = rv_ddl && |\n|.
            rv_ddl = rv_ddl && |  extend { to_lower( lv_field ) } :|.
            rv_ddl = rv_ddl && |\n    remove value help|.
          ELSEIF ls_dd35v-shlpinher <> abap_true.
            rv_ddl = rv_ddl && |\n|.
            rv_ddl = rv_ddl && |  extend { to_lower( lv_field ) } :|.
            rv_ddl = rv_ddl && serialize_value_help(
              iv_fieldname = lv_field
              is_data      = is_data ).
          ENDIF.
        ENDLOOP.
    
        REPLACE ALL OCCURRENCES OF |\n  | IN rv_ddl WITH |\n    |.
    
      ENDMETHOD.
    
      METHOD serialize_field_annotations.
    
        DATA ls_dd03p LIKE LINE OF is_data-dd03p.
    
        READ TABLE is_data-dd03p INTO ls_dd03p WITH KEY fieldname = iv_fieldname.
        IF sy-subrc = 0.
          IF ( ls_dd03p-rollname IS INITIAL AND ls_dd03p-precfield IS INITIAL
            OR ls_dd03p-comptype = 'R' AND ls_dd03p-reftype = 'B' )
            AND ls_dd03p-ddtext IS NOT INITIAL.
            rv_ddl = rv_ddl && |  @EndUserText.label : { escape_string( ls_dd03p-ddtext ) }\n|.
          ENDIF.
    
          IF ls_dd03p-languflag = abap_true.
            rv_ddl = rv_ddl && |  @AbapCatalog.textLanguage\n|.
          ENDIF.
    
          IF ls_dd03p-reftable IS NOT INITIAL AND ls_dd03p-reffield IS NOT INITIAL.
    * this is not completely correct, it must lookup the type of the field in REFTABLE?
            IF ls_dd03p-datatype = 'CURR' OR ls_dd03p-reffield = 'WAERS'.
              rv_ddl = rv_ddl && |  @Semantics.amount.currencyCode : '{ to_lower( ls_dd03p-reftable ) }.{
                to_lower( ls_dd03p-reffield ) }'\n|.
            ELSE.
              rv_ddl = rv_ddl && |  @Semantics.quantity.unitOfMeasure : '{ to_lower( ls_dd03p-reftable ) }.{
                to_lower( ls_dd03p-reffield ) }'\n|.
            ENDIF.
          ENDIF.
    
          IF ls_dd03p-rollname IS INITIAL AND ( ls_dd03p-datatype(3) = 'D16' OR ls_dd03p-datatype(3) = 'D34' ).
    * ls_dd03p-outputstyle
            rv_ddl = rv_ddl && |  @AbapCatalog.decfloat.outputStyle : #NORMAL\n|.
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD serialize_field_foreign_key.
    
        DATA ls_dd08v       LIKE LINE OF is_data-dd08v.
        DATA ls_dd05m       LIKE LINE OF is_data-dd05m.
        DATA lv_pre         TYPE string.
        DATA lv_cardinality TYPE string.
    
        READ TABLE is_data-dd08v INTO ls_dd08v WITH KEY fieldname = iv_fieldname.
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        IF ls_dd08v-cardleft = 'C' AND ls_dd08v-card = '1'.
          lv_cardinality = | [1,0..1] |.
        ELSEIF ls_dd08v-cardleft = '1' AND ls_dd08v-card = 'C'.
          lv_cardinality = | [0..1,1] |.
        ELSEIF ls_dd08v-cardleft = '1' AND ls_dd08v-card = '1'.
          lv_cardinality = | [1,1] |.
        ELSEIF ls_dd08v-cardleft = '1' AND ls_dd08v-card = 'N'.
          lv_cardinality = | [1..*,1] |.
        ELSEIF ls_dd08v-cardleft = '1' AND ls_dd08v-card = 'CN'.
          lv_cardinality = | [0..*,1] |.
        ELSEIF ls_dd08v-cardleft = 'C' AND ls_dd08v-card = 'CN'.
          lv_cardinality = | [0..*,0..1] |.
        ELSEIF ls_dd08v-cardleft = 'C' AND ls_dd08v-card = 'C'.
          lv_cardinality = | [0..1,0..1] |.
        ELSEIF ls_dd08v-cardleft = 'N' AND ls_dd08v-card = 'N'.
          lv_cardinality = | [1..*,] |.
        ELSEIF ls_dd08v-cardleft = 'C' AND ls_dd08v-card = 'N'.
          lv_cardinality = | [1..*,0..1] |.
        ELSEIF ls_dd08v-cardleft IS INITIAL OR ls_dd08v-card IS INITIAL.
          lv_cardinality = | |.
        ELSE.
          ASSERT 1 = 'todo'.
        ENDIF.
    
        rv_ddl = rv_ddl && |\n    with foreign key{ lv_cardinality }{ to_lower( ls_dd08v-checktable ) }|.
    
    * assumption: dd05m table is sorted by PRIMPOS ascending
        LOOP AT is_data-dd05m INTO ls_dd05m WHERE fieldname = iv_fieldname AND fortable <> '*'.
          IF lv_pre IS INITIAL.
            lv_pre = |\n      where |.
          ELSE.
            lv_pre = |\n        and |.
          ENDIF.
          IF ls_dd05m-fortable(1) = |'|.
            rv_ddl = rv_ddl && |{ lv_pre }{ to_lower( ls_dd05m-checkfield ) } = {
              ls_dd05m-fortable }|.
          ELSE.
            rv_ddl = rv_ddl && |{ lv_pre }{ to_lower( ls_dd05m-checkfield ) } = {
              to_lower( ls_dd05m-fortable ) }.{ to_lower( ls_dd05m-forkey ) }|.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD serialize_fkey_annotations.
    
        DATA ls_dd08v LIKE LINE OF is_data-dd08v.
    
        READ TABLE is_data-dd08v INTO ls_dd08v WITH KEY fieldname = iv_fieldname.
        IF sy-subrc = 0.
          IF ls_dd08v-ddtext IS NOT INITIAL.
            rv_ddl = rv_ddl && |  @AbapCatalog.foreignKey.label : { escape_string( ls_dd08v-ddtext ) }\n|.
          ENDIF.
    
          IF ls_dd08v-frkart IS NOT INITIAL.
            CASE ls_dd08v-frkart.
              WHEN 'TEXT'.
                rv_ddl = rv_ddl && |  @AbapCatalog.foreignKey.keyType : #TEXT_KEY\n|.
              WHEN 'REF'.
                rv_ddl = rv_ddl && |  @AbapCatalog.foreignKey.keyType : #NON_KEY\n|.
              WHEN OTHERS.
                rv_ddl = rv_ddl && |  @AbapCatalog.foreignKey.keyType : #{ ls_dd08v-frkart }\n|.
            ENDCASE.
          ENDIF.
    
          IF ls_dd08v-checkflag = abap_false OR ls_dd08v-checkflag = 'N'.
            rv_ddl = rv_ddl && |  @AbapCatalog.foreignKey.screenCheck : true\n|.
          ELSE.
            rv_ddl = rv_ddl && |  @AbapCatalog.foreignKey.screenCheck : false\n|.
          ENDIF.
    
          IF ls_dd08v-arbgb IS NOT INITIAL.
            rv_ddl = rv_ddl && |  @AbapCatalog.foreignKey.messageClass : '{ ls_dd08v-arbgb }'\n|.
          ENDIF.
          IF ls_dd08v-msgnr IS NOT INITIAL.
            rv_ddl = rv_ddl && |  @AbapCatalog.foreignKey.messageNumber : '{ ls_dd08v-msgnr }'\n|.
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD serialize_top.
        FIELD-SYMBOLS:  TYPE c, " ddpk_is_invhash
                               TYPE abap_bool.
    
        rv_ddl = rv_ddl && |@EndUserText.label : { escape_string( is_data-dd02v-ddtext ) }\n|.
    
        CASE is_data-dd02v-exclass.
          WHEN '0'.
            rv_ddl = rv_ddl && |@AbapCatalog.enhancementCategory : #NOT_CLASSIFIED\n|.
          WHEN '1'.
            rv_ddl = rv_ddl && |@AbapCatalog.enhancementCategory : #NOT_EXTENSIBLE\n|.
          WHEN '2'.
            rv_ddl = rv_ddl && |@AbapCatalog.enhancementCategory : #EXTENSIBLE_CHARACTER\n|.
          WHEN '3'.
            rv_ddl = rv_ddl && |@AbapCatalog.enhancementCategory : #EXTENSIBLE_CHARACTER_NUMERIC\n|.
          WHEN '4'.
            rv_ddl = rv_ddl && |@AbapCatalog.enhancementCategory : #EXTENSIBLE_ANY\n|.
          WHEN OTHERS.
            ASSERT 1 = 'todo'.
        ENDCASE.
    
        CASE is_data-dd02v-tabclass.
          WHEN 'TRANSP'.
            " doesn't exist on NW < 750
            ASSIGN COMPONENT 'IS_GTT' OF STRUCTURE is_data-dd02v TO .
            IF sy-subrc = 0 AND  = abap_true.
              rv_ddl = rv_ddl && |@AbapCatalog.tableCategory : #GLOBAL_TEMPORARY\n|.
            ELSE.
              rv_ddl = rv_ddl && |@AbapCatalog.tableCategory : #TRANSPARENT\n|.
            ENDIF.
          WHEN OTHERS.
            ASSERT 1 = 'todo'.
        ENDCASE.
    
        IF is_data-dd02v-authclass = '01'.
          rv_ddl = rv_ddl && |@AbapCatalog.activationType : #NAMETAB_GENERATION_OFFLINE\n|.
        ELSEIF is_data-dd02v-authclass = '02'.
          rv_ddl = rv_ddl && |@AbapCatalog.activationType : #ADAPT_C_STRUCTURES\n|.
        ENDIF.
    
        rv_ddl = rv_ddl && |@AbapCatalog.deliveryClass : #{ is_data-dd02v-contflag }\n|.
    
        IF is_data-dd02v-mainflag = abap_true.
          rv_ddl = rv_ddl && |@AbapCatalog.dataMaintenance : #ALLOWED\n|.
        ELSEIF is_data-dd02v-mainflag = 'N'.
          rv_ddl = rv_ddl && |@AbapCatalog.dataMaintenance : #NOT_ALLOWED\n|.
        ELSEIF is_data-dd02v-mainflag IS INITIAL.
          rv_ddl = rv_ddl && |@AbapCatalog.dataMaintenance : #LIMITED\n|.
        ELSE.
          rv_ddl = rv_ddl && |@AbapCatalog.dataMaintenance : \n|.
        ENDIF.
    
        " doesn't exist on NW <= 750
        ASSIGN
          COMPONENT 'PK_IS_INVHASH'
          OF STRUCTURE is_data-dd02v
          TO .
        IF sy-subrc = 0 AND  = abap_true.
          rv_ddl = rv_ddl && |@AbapCatalog.primaryKey.invertedHashIndex : true\n|.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD serialize_type.
    
        DATA lv_notnull TYPE string.
        DATA lv_leng TYPE i.
        DATA lv_decimals TYPE i.
    
        IF is_dd03p-notnull = abap_true.
          lv_notnull = | not null|.
        ENDIF.
    
        IF is_dd03p-rollname IS NOT INITIAL.
          rv_type = |{ to_lower( is_dd03p-rollname ) }{ lv_notnull }|.
        ELSE.
          lv_leng = is_dd03p-leng.
          lv_decimals = is_dd03p-decimals.
          CASE is_dd03p-datatype.
            WHEN 'STRG'.
              rv_type = |abap.string({ lv_leng }){ lv_notnull }|.
            WHEN 'RSTR'.
              rv_type = |abap.rawstring({ lv_leng }){ lv_notnull }|.
            WHEN 'INT4'.
              rv_type = |abap.int4{ lv_notnull }|.
            WHEN 'ACCP'.
              rv_type = |abap.accp{ lv_notnull }|.
            WHEN 'LANG'.
              rv_type = |abap.lang{ lv_notnull }|.
            WHEN 'DATN'.
              rv_type = |abap.datn{ lv_notnull }|.
            WHEN 'TIMN'.
              rv_type = |abap.timn{ lv_notnull }|.
            WHEN 'UTCL'.
              rv_type = |abap.utcl{ lv_notnull }|.
            WHEN 'INT8'.
              rv_type = |abap.int8{ lv_notnull }|.
            WHEN 'D16D'.
              rv_type = |abap.df16_dec({ lv_leng },{ lv_decimals }){ lv_notnull }|.
            WHEN 'D16R'.
              rv_type = |abap.df16_raw{ lv_notnull }|.
            WHEN 'D16S'.
              rv_type = |abap.df16_scl{ lv_notnull }|.
            WHEN 'D16N'.
              rv_type = |abap.d16n{ lv_notnull }|.
            WHEN 'D34S'.
              rv_type = |abap.df34_scl{ lv_notnull }|.
            WHEN 'D34D'.
              rv_type = |abap.df34_dec({ lv_leng },{ lv_decimals }){ lv_notnull }|.
            WHEN 'D34R'.
              rv_type = |abap.df34_raw{ lv_notnull }|.
            WHEN 'D34N'.
              rv_type = |abap.d34n{ lv_notnull }|.
            WHEN 'INT2'.
              rv_type = |abap.int2{ lv_notnull }|.
            WHEN 'INT1'.
              rv_type = |abap.int1{ lv_notnull }|.
            WHEN 'CUKY'.
              rv_type = |abap.cuky{ lv_notnull }|.
            WHEN 'DATS'.
              rv_type = |abap.dats{ lv_notnull }|.
            WHEN 'TIMS'.
              rv_type = |abap.tims{ lv_notnull }|.
            WHEN 'FLTP'.
              rv_type = |abap.fltp{ lv_notnull }|.
            WHEN 'CLNT'.
              rv_type = |abap.clnt{ lv_notnull }|.
            WHEN 'SSTR'.
              rv_type = |abap.sstring({ lv_leng }){ lv_notnull }|.
            WHEN 'QUAN'.
              rv_type = |abap.quan({ lv_leng },{ lv_decimals }){ lv_notnull }|.
            WHEN 'CURR'.
              rv_type = |abap.curr({ lv_leng },{ lv_decimals }){ lv_notnull }|.
            WHEN 'DEC'.
              rv_type = |abap.dec({ lv_leng },{ lv_decimals }){ lv_notnull }|.
            WHEN OTHERS.
              rv_type = |abap.{ to_lower( is_dd03p-datatype ) }({ lv_leng }){ lv_notnull }|.
          ENDCASE.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD serialize_value_help.
    
        DATA ls_dd35v       LIKE LINE OF is_data-dd35v.
        DATA ls_dd36m       LIKE LINE OF is_data-dd36m.
        DATA lv_pre         TYPE string.
    
        READ TABLE is_data-dd35v INTO ls_dd35v WITH KEY fieldname = iv_fieldname.
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        rv_ddl = rv_ddl && |\n    with value help { to_lower( ls_dd35v-shlpname ) }|.
    
        LOOP AT is_data-dd36m INTO ls_dd36m
            WHERE fieldname = iv_fieldname
            AND shlpname = ls_dd35v-shlpname
            AND shtype <> 'G'.
          IF lv_pre IS INITIAL.
            lv_pre = |\n      where |.
          ELSE.
            lv_pre = |\n        and |.
          ENDIF.
          IF ls_dd36m-shtype = 'C'.
            rv_ddl = rv_ddl && |{ lv_pre }{ to_lower( ls_dd36m-shlpfield ) } = {
              ls_dd36m-shtable }|.
          ELSE.
            rv_ddl = rv_ddl && |{ lv_pre }{ to_lower( ls_dd36m-shlpfield ) } = {
              to_lower( ls_dd36m-shtable ) }.{ to_lower( ls_dd36m-shfield ) }|.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD unescape_string.
        rv_string = iv_string.
        REPLACE FIRST OCCURRENCE OF REGEX |^'| IN rv_string WITH || ##REGEX_POSIX.
        REPLACE FIRST OCCURRENCE OF REGEX |'$| IN rv_string WITH || ##REGEX_POSIX.
        REPLACE ALL OCCURRENCES OF |''| IN rv_string WITH |'|.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_tobj IMPLEMENTATION.
    
      METHOD delete_extra.
    
        DELETE FROM tddat WHERE tabname = iv_tabname.
        DELETE FROM tvdir WHERE tabname = iv_tabname.
        DELETE FROM tvimf WHERE tabname = iv_tabname.
    
      ENDMETHOD.
    
      METHOD read_extra.
    
        SELECT SINGLE * FROM tddat INTO rs_tobj-tddat WHERE tabname = iv_tabname.
    
        SELECT SINGLE * FROM tvdir INTO rs_tobj-tvdir WHERE tabname = iv_tabname.
        CLEAR: rs_tobj-tvdir-gendate, rs_tobj-tvdir-gentime, rs_tobj-tvdir-devclass.
    
        SELECT * FROM tvimf INTO TABLE rs_tobj-tvimf WHERE tabname = iv_tabname
          ORDER BY PRIMARY KEY.
    
      ENDMETHOD.
    
      METHOD update_extra.
        DATA: lt_current_tvimf TYPE STANDARD TABLE OF tvimf.
        FIELD-SYMBOLS:  TYPE tvimf.
    
        MODIFY tddat FROM is_tobj-tddat.
        MODIFY tvdir FROM is_tobj-tvdir.
    
        SELECT * INTO TABLE lt_current_tvimf
          FROM tvimf
          WHERE tabname = is_tobj-tddat-tabname
          ORDER BY PRIMARY KEY.
    
        LOOP AT lt_current_tvimf ASSIGNING .
          READ TABLE is_tobj-tvimf WITH KEY tabname = -tabname
                                            event   = -event
                                   TRANSPORTING NO FIELDS.
          IF sy-subrc <> 0.
            DELETE FROM tvimf
              WHERE tabname = -tabname
              AND event = -event.
          ENDIF.
        ENDLOOP.
    
        MODIFY tvimf FROM TABLE is_tobj-tvimf.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lv_type_pos TYPE i.
    
        lv_type_pos = strlen( ms_item-obj_name ) - 1.
    
        SELECT SINGLE luser FROM objh INTO rv_user
          WHERE objectname = ms_item-obj_name(lv_type_pos)
          AND objecttype = ms_item-obj_name+lv_type_pos.    "#EC CI_GENBUFF
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: ls_objh     TYPE objh,
              lv_type_pos TYPE i.
    
        lv_type_pos = strlen( ms_item-obj_name ) - 1.
    
        ls_objh-objectname = ms_item-obj_name(lv_type_pos).
        ls_objh-objecttype = ms_item-obj_name+lv_type_pos.
    
        IF ls_objh-objecttype = 'L'.
          zcx_abapgit_exception=>raise( |Use transaction SOBJ to delete transport objects { ls_objh-objectname }| ).
        ENDIF.
    
        CALL FUNCTION 'OBJ_GENERATE'
          EXPORTING
            iv_korrnum            = iv_transport
            iv_objectname         = ls_objh-objectname
            iv_objecttype         = ls_objh-objecttype
            iv_maint_mode         = 'D'
          EXCEPTIONS
            illegal_call          = 1
            object_not_found      = 2
            generate_error        = 3
            transport_error       = 4
            object_enqueue_failed = 5
            OTHERS                = 6.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        delete_extra( ls_objh-objectname ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_objh  TYPE objh,
              ls_objt  TYPE objt,
              lt_objs  TYPE tt_objs,
              lt_objsl TYPE tt_objsl,
              lt_objm  TYPE tt_objm,
              ls_tobj  TYPE ty_tobj.
    
        FIELD-SYMBOLS  TYPE uccheck.
    
        io_xml->read( EXPORTING iv_name = 'OBJH'
                      CHANGING  cg_data = ls_objh ).
        io_xml->read( EXPORTING iv_name = 'OBJT'
                      CHANGING  cg_data = ls_objt ).
        io_xml->read( EXPORTING iv_name = 'OBJS'
                      CHANGING  cg_data = lt_objs ).
        io_xml->read( EXPORTING iv_name = 'OBJSL'
                      CHANGING  cg_data = lt_objsl ).
        io_xml->read( EXPORTING iv_name = 'OBJM'
                      CHANGING  cg_data = lt_objm ).
    
        ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE ls_objh TO .
        IF sy-subrc = 0.
          set_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
    
        CALL FUNCTION 'OBJ_GENERATE'
          EXPORTING
            iv_korrnum            = iv_transport
            iv_objectname         = ls_objh-objectname
            iv_objecttype         = ls_objh-objecttype
            iv_maint_mode         = 'I'
            iv_objecttext         = ls_objt-ddtext
            iv_objcateg           = ls_objh-objcateg
            iv_objtransp          = ls_objh-objtransp
            iv_devclass           = iv_package
          TABLES
            tt_v_obj_s            = lt_objs
            tt_objm               = lt_objm
          EXCEPTIONS
            illegal_call          = 1
            object_not_found      = 2
            generate_error        = 3
            transport_error       = 4
            object_enqueue_failed = 5
            OTHERS                = 6.
        IF sy-subrc <> 0.
    * TOBJ has to be saved/generated after the DDIC tables have been
    * activated - fixed with late deserialization
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CALL FUNCTION 'OBJ_SET_IMPORTABLE'
          EXPORTING
            iv_objectname         = ls_objh-objectname
            iv_objecttype         = ls_objh-objecttype
            iv_importable         = ls_objh-importable
          EXCEPTIONS
            object_not_defined    = 1
            invalid               = 2
            transport_error       = 3
            object_enqueue_failed = 4
            OTHERS                = 5.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
    * fm OBJ_GENERATE takes the defaults from the DDIC object
    * set OBJTRANSP directly, should be okay looking at the code in OBJ_SET_IMPORTABLE
    * locking has been done in OBJ_SET_IMPORTABLE plus recording of transport
        UPDATE objh SET objtransp = ls_objh-objtransp
          WHERE objectname = ls_objh-objectname
          AND objecttype = ls_objh-objecttype.
    
    * fm OBJ_GENERATE ignores several fields like primary table flag
    * for Individual Transaction Objects
        IF ls_objh-objecttype = 'T'.
          MODIFY objs FROM TABLE lt_objs.
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'TOBJ'
                      CHANGING  cg_data = ls_tobj ).
        ls_tobj-tvdir-gendate = sy-datum.
        ls_tobj-tvdir-gentime = sy-uzeit.
        ls_tobj-tvdir-devclass = iv_package.
    
        update_extra( ls_tobj ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_objectname TYPE objh-objectname,
              lv_type_pos   TYPE i.
    
        lv_type_pos = strlen( ms_item-obj_name ) - 1.
    
        SELECT SINGLE objectname FROM objh INTO lv_objectname
          WHERE objectname = ms_item-obj_name(lv_type_pos)
          AND objecttype = ms_item-obj_name+lv_type_pos.    "#EC CI_GENBUFF
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: lv_object_name TYPE e071-obj_name.
    
        lv_object_name = ms_item-obj_name.
    
        CALL FUNCTION 'TR_OBJECT_JUMP_TO_TOOL'
          EXPORTING
            iv_pgmid          = 'R3TR'
            iv_object         = ms_item-obj_type
            iv_obj_name       = lv_object_name
          EXCEPTIONS
            jump_not_possible = 1
            OTHERS            = 2.
    
        rv_exit = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_objh     TYPE objh,
              ls_objt     TYPE objt,
              lt_objs     TYPE tt_objs,
              lt_objsl    TYPE tt_objsl,
              lt_objm     TYPE tt_objm,
              ls_tobj     TYPE ty_tobj,
              lv_type_pos TYPE i.
    
        FIELD-SYMBOLS  TYPE uccheck.
    
        lv_type_pos = strlen( ms_item-obj_name ) - 1.
    
        ls_objh-objectname = ms_item-obj_name(lv_type_pos).
        ls_objh-objecttype = ms_item-obj_name+lv_type_pos.
    
        CALL FUNCTION 'CTO_OBJECT_GET'
          EXPORTING
            iv_objectname      = ls_objh-objectname
            iv_objecttype      = ls_objh-objecttype
            iv_language        = mv_language
            iv_sel_objt        = abap_true
            iv_sel_objs        = abap_true
            iv_sel_objsl       = abap_true
            iv_sel_objm        = abap_true
          IMPORTING
            es_objh            = ls_objh
            es_objt            = ls_objt
          TABLES
            tt_objs            = lt_objs
            tt_objsl           = lt_objsl
            tt_objm            = lt_objm
          EXCEPTIONS
            object_not_defined = 1
            OTHERS             = 2.
        IF sy-subrc = 1.
          RETURN.
        ELSEIF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CLEAR: ls_objh-luser,
               ls_objh-ldate.
    
        ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE ls_objh TO .
        IF sy-subrc = 0.
          clear_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
    
        SORT lt_objs BY objectname objecttype tabname.
        SORT lt_objsl BY objectname objecttype trwcount.
        SORT lt_objm BY objectname objecttype method.
    
        io_xml->add( iv_name = 'OBJH'
                     ig_data = ls_objh ).
        io_xml->add( iv_name = 'OBJT'
                     ig_data = ls_objt ).
        io_xml->add( iv_name = 'OBJS'
                     ig_data = lt_objs ).
        io_xml->add( iv_name = 'OBJSL'
                     ig_data = lt_objsl ).
        io_xml->add( iv_name = 'OBJM'
                     ig_data = lt_objm ).
    
        ls_tobj = read_extra( ls_objh-objectname ).
    
        IF ls_tobj-tvdir-detail = ``.
          " to prevent xslt serialization error,
          " force clear if numc field is empty
          CLEAR ls_tobj-tvdir-detail.
        ENDIF.
    
        io_xml->add( iv_name = 'TOBJ'
                     ig_data = ls_tobj ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_tran IMPLEMENTATION.
    
      METHOD add_data.
    
        DATA: ls_bcdata LIKE LINE OF mt_bcdata.
    
        ls_bcdata-fnam = iv_fnam.
        ls_bcdata-fval = iv_fval.
        APPEND ls_bcdata TO mt_bcdata.
    
      ENDMETHOD.
    
      METHOD call_se93.
    
        DATA: lt_message TYPE STANDARD TABLE OF bdcmsgcoll.
        DATA lv_msg TYPE string.
    
        FIELD-SYMBOLS:  TYPE bdcmsgcoll.
    
        CALL FUNCTION 'ABAP4_CALL_TRANSACTION'
          EXPORTING
            tcode     = 'SE93'
            mode_val  = 'N'
          TABLES
            using_tab = mt_bcdata
            mess_tab  = lt_message
          EXCEPTIONS
            OTHERS    = 1.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error deserializing { ms_item-obj_type } { ms_item-obj_name }| ).
        ENDIF.
    
        LOOP AT lt_message ASSIGNING  WHERE msgtyp CA 'EAX'.
          MESSAGE ID -msgid
            TYPE -msgtyp
            NUMBER -msgnr
            WITH -msgv1 -msgv2 -msgv3 -msgv4
            INTO lv_msg.
          zcx_abapgit_exception=>raise_t100( ).
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD clear_functiongroup_globals.
        TYPES ty_param_vari TYPE abap_bool.
    
        DATA lt_error_list TYPE STANDARD TABLE OF rsmp_check WITH DEFAULT KEY.
        FIELD-SYMBOLS  TYPE ty_param_vari.
    
        " only way to clear global fields in function group
        CALL FUNCTION 'RS_TRANSACTION_INCONSISTENCIES'
          EXPORTING
            transaction_code = 'ZTHISTCODENEVEREXIST'
          TABLES
            error_list       = lt_error_list
          EXCEPTIONS
            object_not_found = 1
            OTHERS           = 2.
        IF sy-subrc <> 0.
          "Expected - fine
    
          " but there is no other way to clear this field
          ASSIGN ('(SAPLSEUK)PARAM_VARI') TO .
          IF sy-subrc = 0.
            CLEAR .
          ENDIF.
    
        ENDIF.
    
      ENDMETHOD.
    
      METHOD deserialize_oo_transaction.
    
        " You should remember that we don't use batch input just for fun,
        " but because FM RPY_TRANSACTION_INSERT doesn't support OO transactions.
    
        DATA: ls_bcdata  TYPE bdcdata.
    
        CLEAR mt_bcdata.
    
        ls_bcdata-program  = 'SAPLSEUK'.
        ls_bcdata-dynpro   = '0390'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO mt_bcdata.
    
        add_data( iv_fnam = 'TSTC-TCODE'
                  iv_fval = is_tstc-tcode ).
    
        IF zif_abapgit_object~exists( ) = abap_true.
    
          add_data( iv_fnam = 'BDC_OKCODE'
                    iv_fval = '=CHNG' ).
    
        ELSE.
    
          add_data( iv_fnam = 'BDC_OKCODE'
                    iv_fval = '=ADD' ).
    
        ENDIF.
    
        ls_bcdata-program  = 'SAPLSEUK'.
        ls_bcdata-dynpro   = '0300'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO mt_bcdata.
    
        add_data( iv_fnam = 'TSTCT-TTEXT'
                  iv_fval = is_tstct-ttext ).
    
        add_data( iv_fnam = 'RSSTCD-S_CLASS'
                  iv_fval = 'X' ).
    
        add_data( iv_fnam = 'BDC_OKCODE'
                  iv_fval = '=ENTR' ).
    
        ls_bcdata-program  = 'SAPLSEUK'.
        ls_bcdata-dynpro   = '0360'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO mt_bcdata.
    
        add_data( iv_fnam = 'RSSTCD-S_TRFRAME'
                  iv_fval = is_rsstcd-s_trframe ).
    
        add_data( iv_fnam = 'RSSTCD-S_UPDTASK'
                  iv_fval = is_rsstcd-s_updtask ).
    
        add_data( iv_fnam = 'BDC_OKCODE'
                  iv_fval = '=TR_FRAMEWORK' ).
    
        ls_bcdata-program  = 'SAPLSEUK'.
        ls_bcdata-dynpro   = '0360'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO mt_bcdata.
    
        add_data( iv_fnam = 'RSSTCD-CLASSNAME'
                  iv_fval = is_rsstcd-classname ).
    
        add_data( iv_fnam = 'RSSTCD-METHOD'
                  iv_fval = is_rsstcd-method ).
    
        IF is_rsstcd-s_local IS NOT INITIAL.
          add_data( iv_fnam = 'RSSTCD-S_LOCAL'
                    iv_fval = is_rsstcd-s_local ).
        ENDIF.
    
        IF is_rsstcd-s_updlok IS NOT INITIAL.
          add_data( iv_fnam = 'RSSTCD-S_UPDLOK'
                    iv_fval = is_rsstcd-s_updlok ).
        ENDIF.
    
        add_data( iv_fnam = 'TSTC-PGMNA'
                  iv_fval = is_tstc-pgmna ).
    
        IF is_tstcc-s_webgui = '2'.
    
          add_data( iv_fnam = 'G_IAC_EWT'
                    iv_fval = abap_true ).
    
          add_data( iv_fnam = 'BDC_OKCODE'
                    iv_fval = 'MAKE_PROFI' ).
    
          ls_bcdata-program  = 'SAPLSEUK'.
          ls_bcdata-dynpro   = '0360'.
          ls_bcdata-dynbegin = 'X'.
          APPEND ls_bcdata TO mt_bcdata.
    
        ELSEIF is_tstcc-s_webgui IS NOT INITIAL.
    
          add_data( iv_fnam = 'TSTCC-S_WEBGUI'
                    iv_fval = is_tstcc-s_webgui ).
    
        ENDIF.
    
        IF is_tstcc-s_pervas IS NOT INITIAL.
          add_data( iv_fnam = 'TSTCC-S_PERVAS'
                    iv_fval = is_tstcc-s_pervas ).
        ENDIF.
    
        IF is_tstcc-s_service IS NOT INITIAL.
          add_data( iv_fnam = 'TSTCC-S_SERVICE'
                    iv_fval = is_tstcc-s_service ).
        ENDIF.
    
        IF is_tstcc-s_platin IS NOT INITIAL.
          add_data( iv_fnam = 'TSTCC-S_PLATIN'
                    iv_fval = is_tstcc-s_platin ).
        ENDIF.
    
        IF is_tstcc-s_win32 IS NOT INITIAL.
          add_data( iv_fnam = 'TSTCC-S_WIN32'
                    iv_fval = is_tstcc-s_win32 ).
        ENDIF.
    
        add_data( iv_fnam = 'BDC_OKCODE'
                  iv_fval = '=WB_SAVE' ).
    
        ls_bcdata-program  = 'SAPLSTRD'.
        ls_bcdata-dynpro   = '0100'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO mt_bcdata.
    
        add_data( iv_fnam = 'KO007-L_DEVCLASS'
                  iv_fval = iv_package ).
    
        add_data( iv_fnam = 'BDC_OKCODE'
                  iv_fval = '=ADD' ).
    
        ls_bcdata-program  = 'BDC_OKCODE'.
        ls_bcdata-dynpro   = '0360'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO mt_bcdata.
    
        add_data( iv_fnam = 'BDC_OKCODE'
                  iv_fval = '=WB_BACK' ).
    
        ls_bcdata-program  = 'BDC_OKCODE'.
        ls_bcdata-dynpro   = '0360'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO mt_bcdata.
    
        add_data( iv_fnam = 'BDC_OKCODE'
                  iv_fval = '=WB_BACK' ).
    
        call_se93( ).
    
      ENDMETHOD.
    
      METHOD deserialize_texts.
    
        DATA lt_tpool_i18n TYPE TABLE OF tstct.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_tpool_i18n.
    
        " Read XML-files data
        ii_xml->read( EXPORTING iv_name = 'I18N_TPOOL'
                      CHANGING  cg_data = lt_tpool_i18n ).
    
        mo_i18n_params->trim_saplang_keyed_table(
          EXPORTING
            iv_lang_field_name = 'SPRSL'
          CHANGING
            ct_tab             = lt_tpool_i18n ).
    
        " Force t-code name (security reasons)
        LOOP AT lt_tpool_i18n ASSIGNING .
          -tcode = ms_item-obj_name.
        ENDLOOP.
    
        IF lines( lt_tpool_i18n ) > 0.
          MODIFY tstct FROM TABLE lt_tpool_i18n.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'Update of t-code translations failed' ).
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD is_variant_transaction.
    
        rv_variant_transaction = boolc( is_tstcp-param(1) = '@' ).
    
      ENDMETHOD.
    
      METHOD save_authorizations.
    
        CONSTANTS: lc_hex_chk TYPE x VALUE '04'.
        DATA: ls_transaction TYPE tstc.
    
        transaction_read( EXPORTING iv_transaction = iv_transaction
                          IMPORTING es_transaction = ls_transaction ).
    
        DELETE FROM tstca WHERE tcode = iv_transaction.
    
        IF ls_transaction IS NOT INITIAL.
          INSERT tstca FROM TABLE it_authorizations.
          ls_transaction-cinfo = ls_transaction-cinfo + lc_hex_chk.
          UPDATE tstc SET cinfo = ls_transaction-cinfo WHERE tcode = ls_transaction-tcode.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD serialize_texts.
    
        DATA lt_tpool_i18n TYPE TABLE OF tstct.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          RETURN.
        ENDIF.
    
        " Skip main language - it was already serialized
        " Don't serialize t-code itself
        SELECT sprsl ttext
          INTO CORRESPONDING FIELDS OF TABLE lt_tpool_i18n
          FROM tstct
          WHERE sprsl <> mv_language
          AND   tcode = ms_item-obj_name
          ORDER BY sprsl ##TOO_MANY_ITAB_FIELDS.            "#EC CI_GENBUFF
    
        mo_i18n_params->trim_saplang_keyed_table(
          EXPORTING
            iv_lang_field_name = 'SPRSL'
          CHANGING
            ct_tab             = lt_tpool_i18n ).
    
        IF lines( lt_tpool_i18n ) > 0.
          SORT lt_tpool_i18n BY sprsl ASCENDING.
          ii_xml->add( iv_name = 'I18N_TPOOL'
                       ig_data = lt_tpool_i18n ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD set_oo_parameters.
    
        DATA: ls_param LIKE LINE OF it_rsparam.
    
        IF cs_rsstcd-call_tcode = c_oo_tcode.
          cs_rsstcd-s_trframe = c_true.
          LOOP AT it_rsparam INTO ls_param.
            CASE ls_param-field.
              WHEN c_oo_frclass.
                cs_rsstcd-classname = ls_param-value.
              WHEN c_oo_frmethod.
                cs_rsstcd-method   = ls_param-value.
              WHEN c_oo_frupdtask.
                IF ls_param-value = c_oo_synchron.
                  cs_rsstcd-s_upddir  = c_true.
                  cs_rsstcd-s_updtask = c_false.
                  cs_rsstcd-s_updlok  = c_false.
                ELSEIF ls_param-value = c_oo_asynchron.
                  cs_rsstcd-s_upddir  = c_false.
                  cs_rsstcd-s_updtask = c_true.
                  cs_rsstcd-s_updlok  = c_false.
                ELSE.
                  cs_rsstcd-s_upddir  = c_false.
                  cs_rsstcd-s_updtask = c_false.
                  cs_rsstcd-s_updlok  = c_true.
                ENDIF.
            ENDCASE.
          ENDLOOP.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD shift_param.
    
        DATA: ls_param  LIKE LINE OF ct_rsparam,
              lv_fdpos  TYPE sy-fdpos,
              lv_length TYPE i.
    
        FIELD-SYMBOLS  TYPE any.
    
        DO 254 TIMES.
          IF cs_tstcp-param = space.
            EXIT.
          ENDIF.
          CLEAR ls_param.
          IF cs_tstcp-param CA '='.
            CHECK sy-fdpos <> 0.
            ASSIGN cs_tstcp-param(sy-fdpos) TO .
            ls_param-field = .
            IF ls_param-field(1) = space.
              SHIFT ls_param-field.
            ENDIF.
            lv_fdpos = sy-fdpos + 1.
            SHIFT cs_tstcp-param BY lv_fdpos PLACES.
            IF cs_tstcp-param CA ';'.
              IF sy-fdpos <> 0.
                ASSIGN cs_tstcp-param(sy-fdpos) TO .
                ls_param-value = .
                IF ls_param-value(1) = space.
                  SHIFT ls_param-value.
                ENDIF.
              ENDIF.
              lv_fdpos = sy-fdpos + 1.
              SHIFT cs_tstcp-param BY lv_fdpos PLACES.
              APPEND ls_param TO ct_rsparam.
            ELSE.
              lv_length = strlen( cs_tstcp-param ).
              CHECK lv_length > 0.
              ASSIGN cs_tstcp-param(lv_length) TO .
              ls_param-value = .
              IF ls_param-value(1) = space.
                SHIFT ls_param-value.
              ENDIF.
              lv_length = lv_length + 1.
              SHIFT cs_tstcp-param BY lv_length PLACES.
              APPEND ls_param TO ct_rsparam.
            ENDIF.
          ENDIF.
        ENDDO.
    
      ENDMETHOD.
    
      METHOD split_parameters.
    * see subroutine split_parameters in include LSEUKF01
    
        DATA: lv_off       TYPE i,
              lv_fdpos     TYPE sy-fdpos,
              lv_param_beg TYPE i.
    
        CLEAR cs_rsstcd-s_vari.
    
        IF cs_tstcp-param(1) = '\'.             " OO-Transaction without FR
          split_parameters_comp( EXPORTING ig_type  = c_oo_program
                                           ig_param = cs_tstcp-param
                                 CHANGING  cg_value = cs_tstc-pgmna ).
          split_parameters_comp( EXPORTING ig_type  = c_oo_class
                                           ig_param = cs_tstcp-param
                                 CHANGING  cg_value = cs_rsstcd-classname ).
          split_parameters_comp( EXPORTING ig_type  = c_oo_method
                                           ig_param = cs_tstcp-param
                                 CHANGING  cg_value = cs_rsstcd-method ).
    
          IF NOT cs_tstc-pgmna IS INITIAL.
            cs_rsstcd-s_local = c_true.
          ENDIF.
          RETURN.
        ELSEIF cs_tstcp-param(1) = '@'.         " Transaction variant
          cs_rsstcd-s_vari = c_true.
          IF cs_tstcp-param(2) = '@@'.
            cs_rsstcd-s_ind_vari = c_true.
            lv_off = 2.
          ELSE.
            CLEAR cs_rsstcd-s_ind_vari.
            lv_off = 1.
          ENDIF.
          IF cs_tstcp-param CA ' '.
          ENDIF.
          lv_fdpos = sy-fdpos - lv_off.
          IF lv_fdpos > 0.
            cs_rsstcd-call_tcode = cs_tstcp-param+lv_off(sy-fdpos).
            lv_fdpos = lv_fdpos + 1 + lv_off.
            cs_rsstcd-variant = cs_tstcp-param+lv_fdpos.
          ENDIF.
        ELSEIF cs_tstcp-param(1) = '/'.
          cs_rsstcd-st_tcode = c_true.
          cs_rsstcd-st_prog  = space.
          IF cs_tstcp-param+1(1) = '*'.
            cs_rsstcd-st_skip_1 = c_true.
          ELSE.
            CLEAR cs_rsstcd-st_skip_1.
          ENDIF.
          IF cs_tstcp-param CA ' '.
          ENDIF.
          lv_param_beg = sy-fdpos + 1.
          lv_fdpos = sy-fdpos - 2.
          IF lv_fdpos > 0.
            cs_rsstcd-call_tcode = cs_tstcp-param+2(lv_fdpos).
          ENDIF.
          SHIFT cs_tstcp-param BY lv_param_beg PLACES.
        ELSE.
          cs_rsstcd-st_tcode = space.
          cs_rsstcd-st_prog  = c_true.
          cs_rsstcd-variant  = cs_tstcp-param.
        ENDIF.
    
        shift_param(
          CHANGING
            ct_rsparam = ct_rsparam
            cs_tstcp   = cs_tstcp ).
    
        set_oo_parameters(
          EXPORTING
            it_rsparam = ct_rsparam
          CHANGING
            cs_rsstcd  = cs_rsstcd ).
    
      ENDMETHOD.
    
      METHOD split_parameters_comp.
        DATA: lv_off TYPE i.
    
        IF ig_param CS ig_type.
          lv_off = sy-fdpos + strlen( ig_type ).
          cg_value = ig_param+lv_off.
          IF cg_value CA '\'.
            CLEAR cg_value+sy-fdpos.
          ENDIF.
        ENDIF.
      ENDMETHOD.
    
      METHOD transaction_read.
    
        DATA: lt_tcodes   TYPE TABLE OF tstc,
              lt_gui_attr TYPE TABLE OF tstcc.
    
        CLEAR: es_transaction, es_gui_attr.
    
        CALL FUNCTION 'RPY_TRANSACTION_READ'
          EXPORTING
            transaction      = iv_transaction
          TABLES
            tcodes           = lt_tcodes
            gui_attributes   = lt_gui_attr
          EXCEPTIONS
            permission_error = 1
            cancelled        = 2
            not_found        = 3
            object_not_found = 4
            OTHERS           = 5.
        IF sy-subrc = 4 OR sy-subrc = 3.
          RETURN.
        ELSEIF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        READ TABLE lt_tcodes INDEX 1 INTO es_transaction.
        ASSERT sy-subrc = 0.
        READ TABLE lt_gui_attr INDEX 1 INTO es_gui_attr.
        ASSERT sy-subrc = 0.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        " Changed-by-user is not stored in transaction metadata
        " Instead, use owner of last transport or object directory
    
        DATA lv_transport TYPE trkorr.
    
        lv_transport = zcl_abapgit_factory=>get_cts_api( )->get_transport_for_object( ms_item ).
    
        IF lv_transport IS NOT INITIAL.
          SELECT SINGLE as4user FROM e070 INTO rv_user WHERE trkorr = lv_transport.
        ELSE.
          SELECT SINGLE author FROM tadir INTO rv_user
            WHERE pgmid = 'R3TR' AND object = ms_item-obj_type AND obj_name = ms_item-obj_name.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_transaction TYPE tstc-tcode.
    
        lv_transaction = ms_item-obj_name.
    
        CALL FUNCTION 'RPY_TRANSACTION_DELETE'
          EXPORTING
            transaction      = lv_transaction
          EXCEPTIONS
            not_excecuted    = 1
            object_not_found = 0
            OTHERS           = 3.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        CONSTANTS: lc_hex_tra TYPE x VALUE '00',
    *               lc_hex_men TYPE x VALUE '01',
                   lc_hex_par TYPE x VALUE '02',
                   lc_hex_rep TYPE x VALUE '80',
                   lc_hex_var TYPE x VALUE '90',
                   lc_hex_rpv TYPE x VALUE '10',
                   lc_hex_obj TYPE x VALUE '08'.
    
        DATA: lv_dynpro       TYPE d020s-dnum,
              ls_tstc         TYPE tstc,
              lv_type         TYPE rglif-docutype,
              ls_tstct        TYPE tstct,
              ls_tstcc        TYPE tstcc,
              ls_tstcp        TYPE tstcp,
              lt_tstca        TYPE ty_tstca,
              lt_param_values TYPE ty_param_values,
              ls_rsstcd       TYPE rsstcd.
        DATA: ls_item TYPE zif_abapgit_definitions=>ty_item,
              lr_head TYPE REF TO data,
              lx_err  TYPE REF TO cx_root,
              lo_sush TYPE REF TO zcl_abapgit_object_sush.
    
        FIELD-SYMBOLS  TYPE any.
    
        IF zif_abapgit_object~exists( ) = abap_true.
          zif_abapgit_object~delete( iv_package   = iv_package
                                     iv_transport = iv_transport
                                     ii_log       = ii_log ).
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'TSTC'
                      CHANGING  cg_data = ls_tstc ).
        io_xml->read( EXPORTING iv_name = 'TSTCC'
                      CHANGING  cg_data = ls_tstcc ).
        io_xml->read( EXPORTING iv_name = 'TSTCT'
                      CHANGING  cg_data = ls_tstct ).
        io_xml->read( EXPORTING iv_name = 'TSTCP'
                      CHANGING  cg_data = ls_tstcp ).
        io_xml->read( EXPORTING iv_name = 'AUTHORIZATIONS'
                      CHANGING  cg_data = lt_tstca ).
    
        lv_dynpro = ls_tstc-dypno.
    
        IF ls_tstc-cinfo O lc_hex_rep.
          lv_type = c_variant_type-report.
        ELSEIF ls_tstc-cinfo O lc_hex_obj.
          lv_type = c_variant_type-object.
        ELSEIF ls_tstc-cinfo O lc_hex_par.
          IF is_variant_transaction( ls_tstcp ) = abap_true.
            lv_type = c_variant_type-variant.
          ELSE.
            lv_type = c_variant_type-parameters.
          ENDIF.
        ELSEIF ls_tstc-cinfo O lc_hex_tra.
          lv_type = c_variant_type-dialog.
        ELSE.
          zcx_abapgit_exception=>raise( 'Transaction, unknown CINFO' ).
        ENDIF.
    
        IF ls_tstcp IS NOT INITIAL.
          split_parameters( CHANGING ct_rsparam = lt_param_values
                                     cs_rsstcd  = ls_rsstcd
                                     cs_tstcp   = ls_tstcp
                                     cs_tstc    = ls_tstc ).
        ENDIF.
    
        CASE lv_type.
          WHEN c_variant_type-object.
    
            deserialize_oo_transaction( iv_package = iv_package
                                        is_tstc    = ls_tstc
                                        is_tstcc   = ls_tstcc
                                        is_tstct   = ls_tstct
                                        is_rsstcd  = ls_rsstcd ).
    
          WHEN OTHERS.
    
            clear_functiongroup_globals( ).
    
            corr_insert( iv_package ).
    
            CALL FUNCTION 'RPY_TRANSACTION_INSERT'
              EXPORTING
                transaction             = ls_tstc-tcode
                program                 = ls_tstc-pgmna
                dynpro                  = lv_dynpro
                language                = mv_language
                development_class       = iv_package
                transaction_type        = lv_type
                shorttext               = ls_tstct-ttext
                called_transaction      = ls_rsstcd-call_tcode
                called_transaction_skip = ls_rsstcd-st_skip_1
                variant                 = ls_rsstcd-variant
                cl_independend          = ls_rsstcd-s_ind_vari
                html_enabled            = ls_tstcc-s_webgui
                java_enabled            = ls_tstcc-s_platin
                wingui_enabled          = ls_tstcc-s_win32
                suppress_corr_insert    = abap_true
              TABLES
                param_values            = lt_param_values
              EXCEPTIONS
                cancelled               = 1
                already_exist           = 2
                permission_error        = 3
                name_not_allowed        = 4
                name_conflict           = 5
                illegal_type            = 6
                object_inconsistent     = 7
                db_access_error         = 8
                OTHERS                  = 9.
            IF sy-subrc <> 0.
              zcx_abapgit_exception=>raise_t100( ).
            ENDIF.
    
            " RPY_TRANSACTION_INSERT does not set the variant flag (x'10') in cinfo for report transactions with variants,
            " so we update it manually to include both report (x'80') and variant (x'10') flags (i.e., '90' in hexadecimal).
            IF ls_tstc-cinfo O lc_hex_rpv.
              UPDATE tstc SET cinfo = lc_hex_var WHERE tcode = ls_tstc-tcode.
              IF sy-subrc <> 0.
                zcx_abapgit_exception=>raise( 'Update of TSTC cinfo failed' ).
              ENDIF.
            ENDIF.
    
        ENDCASE.
    
        IF lt_tstca IS NOT INITIAL.
          save_authorizations( iv_transaction    = ls_tstc-tcode
                               it_authorizations = lt_tstca ).
        ENDIF.
    
        IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
          deserialize_texts( io_xml ).
        ENDIF.
    
        " Import SU22 data by reusing SUSH deserializer
        TRY.
            CREATE DATA lr_head TYPE ('IF_SU22_ADT_OBJECT=>TS_SU2X_HEAD').
            ASSIGN lr_head->* TO .
          CATCH cx_root.
            RETURN. ">>> no SU22 in this release
        ENDTRY.
    
        TRY.
            io_xml->read( EXPORTING iv_name = 'HEAD'
                          CHANGING  cg_data =  ).
    
            IF  IS NOT INITIAL.
              ls_item-obj_type    = 'SUSH'.
              ls_item-obj_name    = ms_item-obj_name.
              ls_item-obj_name+30 = 'TR'.
    
              CREATE OBJECT lo_sush TYPE zcl_abapgit_object_sush
                EXPORTING
                  is_item     = ls_item
                  iv_language = mv_language.
    
              lo_sush->zif_abapgit_object~deserialize(
                iv_package   = iv_package
                io_xml       = io_xml
                iv_step      = iv_step
                ii_log       = ii_log
                iv_transport = iv_transport ).
            ENDIF.
    
          CATCH cx_root INTO lx_err.
            zcx_abapgit_exception=>raise_with_text( lx_err ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_tcode TYPE tstc-tcode.
    
        SELECT SINGLE tcode FROM tstc INTO lv_tcode
          WHERE tcode = ms_item-obj_name.                   "#EC CI_GENBUFF
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'EEUDB'
                                                iv_argument    = ms_item-obj_name
                                                iv_prefix      = 'TN' ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: lt_bdcdata TYPE TABLE OF bdcdata.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_bdcdata.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -program  = 'SAPLSEUK'.
        -dynpro   = '0390'.
        -dynbegin = abap_true.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'BDC_OKCODE'.
        -fval = '=SHOW'.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'TSTC-TCODE'.
        -fval = ms_item-obj_name.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode      = 'SE93'
          it_bdcdata    = lt_bdcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_transaction TYPE tstc-tcode,
              ls_tcode       TYPE tstc,
              ls_tstct       TYPE tstct,
              ls_tstcp       TYPE tstcp,
              lt_tstca       TYPE ty_tstca,
              ls_gui_attr    TYPE tstcc.
        DATA: ls_item TYPE zif_abapgit_definitions=>ty_item,
              ls_sush TYPE usob_sm,
              lo_sush TYPE REF TO zcl_abapgit_object_sush.
    
        lv_transaction = ms_item-obj_name.
    
        transaction_read( EXPORTING iv_transaction = lv_transaction
                          IMPORTING es_transaction = ls_tcode
                                    es_gui_attr    = ls_gui_attr ).
        IF ls_tcode IS INITIAL.
          RETURN.
        ENDIF.
    
        SELECT SINGLE * FROM tstct INTO ls_tstct
          WHERE sprsl = mv_language
          AND tcode = lv_transaction.         "#EC CI_SUBRC "#EC CI_GENBUFF
    
        SELECT SINGLE * FROM tstcp INTO ls_tstcp
          WHERE tcode = lv_transaction.       "#EC CI_SUBRC "#EC CI_GENBUFF
    
        SELECT * FROM tstca INTO TABLE lt_tstca
          WHERE tcode = lv_transaction
          ORDER BY PRIMARY KEY.
        IF sy-subrc <> 0.
          CLEAR: lt_tstca.
        ENDIF.
    
        io_xml->add( iv_name = 'TSTC'
                     ig_data = ls_tcode ).
        io_xml->add( iv_name = 'TSTCC'
                     ig_data = ls_gui_attr ).
        io_xml->add( iv_name = 'TSTCT'
                     ig_data = ls_tstct ).
        IF ls_tstcp IS NOT INITIAL.
          io_xml->add( iv_name = 'TSTCP'
                       ig_data = ls_tstcp ).
        ENDIF.
        io_xml->add( iv_name = 'AUTHORIZATIONS'
                     ig_data = lt_tstca ).
    
        IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
          serialize_texts( io_xml ).
        ENDIF.
    
        " Add SU22 data by reusing SUSH serializer
        SELECT SINGLE * FROM usob_sm INTO ls_sush WHERE name = ms_item-obj_name AND type = 'TR'.
        IF sy-subrc = 0.
          ls_item-obj_type    = 'SUSH'.
          ls_item-obj_name    = ms_item-obj_name.
          ls_item-obj_name+30 = 'TR'.
    
          TRY.
              CREATE OBJECT lo_sush TYPE zcl_abapgit_object_sush
                EXPORTING
                  is_item     = ls_item
                  iv_language = mv_language.
    
              lo_sush->zif_abapgit_object~serialize( io_xml ).
            CATCH zcx_abapgit_type_not_supported.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ttyp IMPLEMENTATION.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE as4user FROM dd40l INTO rv_user
          WHERE typename = ms_item-obj_name
          AND as4local = 'A'.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        delete_ddic( 'A' ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_name  TYPE ddobjname,
              lt_dd42v TYPE dd42v_tab,
              lt_dd43v TYPE dd43v_tab,
              ls_dd40v TYPE dd40v,
              ls_extra TYPE ty_extra,
              lv_msg   TYPE string.
    
        io_xml->read( EXPORTING iv_name = 'DD40V'
                      CHANGING cg_data = ls_dd40v ).
    
        io_xml->read( EXPORTING iv_name = 'DD42V'
                      CHANGING cg_data = lt_dd42v ).
        io_xml->read( EXPORTING iv_name = 'DD43V'
                      CHANGING cg_data = lt_dd43v ).
    
        corr_insert( iv_package = iv_package
                     ig_object_class = 'DICT' ).
    
        lv_name = ms_item-obj_name. " type conversion
    
        CALL FUNCTION 'DDIF_TTYP_PUT'
          EXPORTING
            name              = lv_name
            dd40v_wa          = ls_dd40v
          TABLES
            dd42v_tab         = lt_dd42v
            dd43v_tab         = lt_dd43v
          EXCEPTIONS
            ttyp_not_found    = 1
            name_inconsistent = 2
            ttyp_inconsistent = 3
            put_failure       = 4
            put_refused       = 5
            OTHERS            = 6.
    
        IF sy-subrc <> 0.
          lv_msg = |Error in DDIF_TTYP_PUT on object { lv_name }|.
    
          CASE sy-subrc.
            WHEN 1.
              lv_msg = lv_msg && | (TTYP_NOT_FOUND)|.
            WHEN 2.
              lv_msg = lv_msg && | (NAME_INCONSISTENT)|.
            WHEN 3.
              lv_msg = lv_msg && | (TTYP_INCONSISTENT)|.
            WHEN 4.
              lv_msg = lv_msg && | (PUT_FAILURE)|.
            WHEN 5.
              lv_msg = lv_msg && | (PUT_REFUSED)|.
            WHEN OTHERS.
          ENDCASE.
    
          zcx_abapgit_exception=>raise( lv_msg ).
        ENDIF.
    
        " Fields that are not part of dd40v
        io_xml->read( EXPORTING iv_name = 'DD40L_EXTRA'
                      CHANGING  cg_data = ls_extra ).
    
        TRY.
            set_abap_language_version( CHANGING cv_abap_language_version = ls_extra-abap_language_version ).
    
            UPDATE ('DD40L') SET abap_language_version = ls_extra-abap_language_version WHERE typename = lv_name.
          CATCH cx_sy_dynamic_osql_semantics ##NO_HANDLER.
        ENDTRY.
    
        deserialize_longtexts( ii_xml         = io_xml
                               iv_longtext_id = c_longtext_id_ttyp ).
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_typename TYPE dd40l-typename.
    
        SELECT SINGLE typename FROM dd40l INTO lv_typename
          WHERE typename = ms_item-obj_name.
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESDICT'
                                                iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by ZCL_ABAPGIT_OBJECT=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_name  TYPE ddobjname,
              lv_state TYPE ddgotstate,
              lt_dd42v TYPE dd42v_tab,
              lt_dd43v TYPE dd43v_tab,
              ls_extra TYPE ty_extra,
              ls_dd40v TYPE dd40v.
    
        FIELD-SYMBOLS  TYPE any.
    
        lv_name = ms_item-obj_name.
    
        CALL FUNCTION 'DDIF_TTYP_GET'
          EXPORTING
            name          = lv_name
            state         = 'A'
            langu         = mv_language
          IMPORTING
            gotstate      = lv_state
            dd40v_wa      = ls_dd40v
          TABLES
            dd42v_tab     = lt_dd42v
            dd43v_tab     = lt_dd43v
          EXCEPTIONS
            illegal_input = 1
            OTHERS        = 2.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        IF ls_dd40v IS INITIAL OR lv_state <> 'A'.
          RETURN.
        ENDIF.
    
        CLEAR: ls_dd40v-as4user,
               ls_dd40v-as4date,
               ls_dd40v-as4time.
    
        IF NOT ls_dd40v-rowkind IS INITIAL.
          CLEAR ls_dd40v-typelen.
        ENDIF.
    
        ASSIGN COMPONENT 'ACTFLAG' OF STRUCTURE ls_dd40v TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
    
        io_xml->add( iv_name = 'DD40V'
                     ig_data = ls_dd40v ).
        io_xml->add( iv_name = 'DD42V'
                     ig_data = lt_dd42v ).
        io_xml->add( iv_name = 'DD43V'
                     ig_data = lt_dd43v ).
    
        ls_extra-abap_language_version = get_abap_language_version( ).
    
        io_xml->add( iv_name = 'DD40L_EXTRA'
                     ig_data = ls_extra ).
    
        serialize_longtexts( ii_xml         = io_xml
                             iv_longtext_id = c_longtext_id_ttyp ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_type IMPLEMENTATION.
    
      METHOD create.
    
        DATA: lv_progname  TYPE reposrc-progname,
              lv_typegroup TYPE rsedd0-typegroup.
    
        lv_typegroup = ms_item-obj_name.
    
        CALL FUNCTION 'RS_DD_TYGR_INSERT_SOURCES'
          EXPORTING
            typegroupname        = lv_typegroup
            ddtext               = iv_ddtext
            corrnum              = ''
            devclass             = iv_devclass
          TABLES
            source               = it_source
          EXCEPTIONS
            already_exists       = 1
            not_executed         = 2
            permission_failure   = 3
            object_not_specified = 4
            illegal_name         = 5
            OTHERS               = 6.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CONCATENATE c_prefix lv_typegroup INTO lv_progname.
        UPDATE progdir SET uccheck = zif_abapgit_aff_types_v1=>co_abap_language_version_src-standard
          WHERE name = lv_progname.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error setting uccheck' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD read.
    
        DATA: lv_typdname  TYPE rsedd0-typegroup,
              lt_psmodisrc TYPE TABLE OF smodisrc,
              lt_psmodilog TYPE TABLE OF smodilog,
              lt_ptrdir    TYPE TABLE OF trdir.
    
        SELECT SINGLE ddtext FROM ddtypet
          INTO ev_ddtext
          WHERE typegroup  = ms_item-obj_name
            AND ddlanguage = mv_language.
    
        lv_typdname = ms_item-obj_name.
    
        " Get active version, ignore errors if not found
        CALL FUNCTION 'TYPD_GET_OBJECT'
          EXPORTING
            typdname          = lv_typdname
          TABLES
            psmodisrc         = lt_psmodisrc
            psmodilog         = lt_psmodilog
            psource           = et_source
            ptrdir            = lt_ptrdir
          EXCEPTIONS
            version_not_found = 1
            reps_not_exist    = 2
            OTHERS            = 3 ##FM_SUBRC_OK.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        DATA lv_prog TYPE progname.
    
        CONCATENATE '%_C' ms_item-obj_name INTO lv_prog.
    
        SELECT SINGLE unam FROM reposrc INTO rv_user
          WHERE progname = lv_prog AND r3state = 'A'.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        delete_ddic( 'G' ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_ddtext    TYPE ddtypet-ddtext,
              lt_source    TYPE abaptxt255_tab,
              lv_progname  TYPE reposrc-progname,
              lv_typegroup TYPE rsedd0-typegroup.
    
        lv_typegroup = ms_item-obj_name.
    
        io_xml->read( EXPORTING iv_name = 'DDTEXT'
                      CHANGING cg_data = lv_ddtext ).
    
        lt_source = mo_files->read_abap( ).
    
        IF zif_abapgit_object~exists( ) = abap_false.
          create( iv_ddtext   = lv_ddtext
                  it_source   = lt_source
                  iv_devclass = iv_package ).
        ELSE.
          CONCATENATE c_prefix lv_typegroup INTO lv_progname.
    
          zcl_abapgit_factory=>get_sap_report( )->insert_report(
            iv_name    = lv_progname
            iv_package = iv_package
            iv_version = zif_abapgit_aff_types_v1=>co_abap_language_version_src-standard
            it_source  = lt_source ).
        ENDIF.
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_progname TYPE progname,
              lv_state    TYPE r3state.
    
        lv_progname = |%_C{ ms_item-obj_name }|.
        SELECT SINGLE state
          FROM progdir
          INTO lv_state
          WHERE name = lv_progname.                         "#EC CI_NOORDER
        IF lv_state IS NOT INITIAL.
          rv_bool = abap_true.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by ZCL_ABAPGIT_OBJECT=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_ddtext TYPE ddtypet-ddtext,
              lt_source TYPE abaptxt255_tab.
    
        read( IMPORTING ev_ddtext = lv_ddtext
                        et_source = lt_source ).
    
        IF lt_source IS INITIAL.
          RETURN.
        ENDIF.
    
        io_xml->add( iv_name = 'DDTEXT'
                     ig_data = lv_ddtext ).
    
        mo_files->add_abap( lt_source ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ucsa IMPLEMENTATION.
    
      METHOD clear_dynamic_fields.
    
        FIELD-SYMBOLS:  TYPE any.
    
        ASSIGN COMPONENT 'HEADER' OF STRUCTURE cg_complete_comm_assembly
               TO .
        ASSERT sy-subrc = 0.
    
        clear_field(
          EXPORTING iv_fieldname = 'CREATEDBY'
          CHANGING  cg_header    =  ).
    
        clear_field(
          EXPORTING iv_fieldname = 'CREATEDON'
          CHANGING  cg_header    =  ).
    
        clear_field(
          EXPORTING iv_fieldname = 'CREATEDAT'
          CHANGING  cg_header    =  ).
    
        clear_field(
          EXPORTING iv_fieldname = 'CHANGEDBY'
          CHANGING  cg_header    =  ).
    
        clear_field(
          EXPORTING iv_fieldname = 'CHANGEDON'
          CHANGING  cg_header    =  ).
    
        clear_field(
          EXPORTING iv_fieldname = 'CHANGEDAT'
          CHANGING  cg_header    =  ).
    
      ENDMETHOD.
    
      METHOD clear_field.
    
        FIELD-SYMBOLS:  TYPE any.
    
        ASSIGN COMPONENT iv_fieldname OF STRUCTURE cg_header
               TO .
        ASSERT sy-subrc = 0.
        CLEAR .
    
      ENDMETHOD.
    
      METHOD get_persistence.
    
        CALL METHOD ('CL_UCON_SA_DB_PERSIST')=>('IF_UCON_SA_PERSIST~GET_INSTANCE')
          EXPORTING
            id       = iv_id
          RECEIVING
            instance = ro_persistence.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lv_id                     TYPE ty_id,
              lx_root                   TYPE REF TO cx_root,
              lo_persistence            TYPE REF TO object,
              lr_complete_comm_assembly TYPE REF TO data.
    
        FIELD-SYMBOLS:  TYPE any,
                                          TYPE any.
    
        lv_id = ms_item-obj_name.
    
        TRY.
            CREATE DATA lr_complete_comm_assembly TYPE ('UCONSERVASCOMPLETE').
            ASSIGN lr_complete_comm_assembly->* TO .
            ASSERT sy-subrc = 0.
    
            lo_persistence = get_persistence( lv_id ).
    
            CALL METHOD lo_persistence->('IF_UCON_SA_PERSIST~LOAD')
              EXPORTING
                version  = c_version-active
                language = mv_language
              IMPORTING
                sa       = .
    
            ASSIGN COMPONENT 'CHANGEDBY' OF STRUCTURE  TO .
            IF sy-subrc = 0.
              rv_user = .
            ENDIF.
    
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise_with_text( lx_root ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_id          TYPE ty_id,
              lx_root        TYPE REF TO cx_root,
              lv_text        TYPE string,
              lo_persistence TYPE REF TO object.
    
        TRY.
            lv_id = ms_item-obj_name.
    
            lo_persistence = get_persistence( lv_id ).
    
            CALL METHOD lo_persistence->('IF_UCON_SA_PERSIST~DELETE')
              EXPORTING
                version = c_version-active.
    
          CATCH cx_root INTO lx_root.
            lv_text = lx_root->get_text( ).
            zcx_abapgit_exception=>raise( lv_text ).
        ENDTRY.
    
        tadir_delete( ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_id                     TYPE ty_id,
              lx_root                   TYPE REF TO cx_root,
              lv_text                   TYPE string,
              lo_persistence            TYPE REF TO object,
              lr_complete_comm_assembly TYPE REF TO data.
    
        FIELD-SYMBOLS:  TYPE any.
    
        TRY.
            CREATE DATA lr_complete_comm_assembly TYPE ('UCONSERVASCOMPLETE').
            ASSIGN lr_complete_comm_assembly->* TO .
            ASSERT sy-subrc = 0.
    
            io_xml->read(
              EXPORTING
                iv_name = 'UCSA'
              CHANGING
                cg_data =  ).
    
            lv_id = ms_item-obj_name.
    
            lo_persistence = get_persistence( lv_id ).
    
            CALL METHOD lo_persistence->('IF_UCON_SA_PERSIST~CREATE').
    
            CALL METHOD lo_persistence->('IF_UCON_SA_PERSIST~SAVE')
              EXPORTING
                sa      = 
                version = c_version-active.
    
            tadir_insert( iv_package ).
    
            corr_insert( iv_package ).
    
          CATCH cx_root INTO lx_root.
            lv_text = lx_root->get_text( ).
            zcx_abapgit_exception=>raise( lv_text ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_id          TYPE ty_id,
              lo_persistence TYPE REF TO object.
    
        lv_id = ms_item-obj_name.
    
        TRY.
            lo_persistence = get_persistence( lv_id ).
    
            " Interface IF_UCON_SA_PERSIST and other objects are not present
            " in lower NetWeaver releases. Therefore we have to call them
            " dynamically to be downward compatible.
    
            CALL METHOD lo_persistence->('IF_UCON_SA_PERSIST~LOAD')
              EXPORTING
                version  = c_version-active
                language = mv_language.
    
          CATCH cx_root.
            rv_bool = abap_false.
            RETURN.
        ENDTRY.
    
        rv_bool = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_id                     TYPE ty_id,
              lx_root                   TYPE REF TO cx_root,
              lo_persistence            TYPE REF TO object,
              lr_complete_comm_assembly TYPE REF TO data.
    
        FIELD-SYMBOLS:  TYPE any.
    
        lv_id = ms_item-obj_name.
    
        TRY.
            CREATE DATA lr_complete_comm_assembly TYPE ('UCONSERVASCOMPLETE').
            ASSIGN lr_complete_comm_assembly->* TO .
            ASSERT sy-subrc = 0.
    
            lo_persistence = get_persistence( lv_id ).
    
            CALL METHOD lo_persistence->('IF_UCON_SA_PERSIST~LOAD')
              EXPORTING
                version  = c_version-active
                language = mv_language
              IMPORTING
                sa       = .
    
            clear_dynamic_fields( CHANGING cg_complete_comm_assembly =  ).
    
            io_xml->add( iv_name = 'UCSA'
                         ig_data =  ).
    
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise_with_text( lx_root ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_udmo IMPLEMENTATION.
    
      METHOD access_free.
    
        " Release the lock on the object.
    
        CALL FUNCTION 'RS_ACCESS_PERMISSION'
          EXPORTING
            mode                     = 'FREE'
            object                   = ms_object_type
            object_class             = c_transport_object_class
          EXCEPTIONS
            canceled_in_corr         = 1
            enqueued_by_user         = 2
            enqueue_system_failure   = 3
            illegal_parameter_values = 4
            locked_by_author         = 5
            no_modify_permission     = 6
            no_show_permission       = 7
            permission_failure       = 8
            request_language_denied  = 9
            OTHERS                   = 10.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ELSE.
          rv_result = abap_true.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD access_modify.
    
    * You are reminded that mode modify is the same as insert, with one important difference:
    
    * Mode INSERT is intended for newly created objects, for which a TADIR entry does not yet
    * exist. In that case, the system shows a pop-up for the entry of the package, which isn't
    * desirable when the SAPGUI is not available.
    
    * In the context of abapGit, the package is known.
    
        CALL FUNCTION 'RS_ACCESS_PERMISSION'
          EXPORTING
            authority_check          = abap_true
            global_lock              = abap_true
            mode                     = 'MODIFY'
            object                   = ms_object_type
            object_class             = c_transport_object_class
          EXCEPTIONS
            canceled_in_corr         = 1
            enqueued_by_user         = 2
            enqueue_system_failure   = 3
            illegal_parameter_values = 4
            locked_by_author         = 5
            no_modify_permission     = 6
            no_show_permission       = 7
            permission_failure       = 8
            request_language_denied  = 9
            OTHERS                   = 10.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ELSE.
          rv_result = abap_true.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        " Conversion to Data model
        mv_data_model = is_item-obj_name.
        " Default activation state is active
        mv_activation_state = c_active_state.
        " Derive the data model's text object
        mv_text_object = 'UDMD' && is_item-obj_name.
        " And set the text object to active
        mv_text_object+30(1) = mv_activation_state.
        mv_lxe_text_name = mv_text_object.
    
        " Correction and Transport System object
        ms_object_type-objtype = c_correction_object_type.
        ms_object_type-objname = is_item-obj_name.
    
      ENDMETHOD.
    
      METHOD corr_insert.
    
        DATA lv_obj_name TYPE tadir-obj_name.
    
        " You are reminded that SUDM - Data Model has no part objects e.g. no LIMU
        " Therefore global lock is always appropriate
    
        " You are reminded that the main language (in TADIR) is taken from MV_LANGUAGE.
        lv_obj_name = ms_object_type.
    
        zcl_abapgit_factory=>get_cts_api( )->insert_transport_object(
          iv_object   = c_transport_object_class
          iv_obj_name = lv_obj_name
          iv_package  = iv_package
          iv_language = mv_language ).
    
      ENDMETHOD.
    
      METHOD deserialize_entities.
    
        DATA lt_udmo_entities TYPE STANDARD TABLE OF dm41s WITH DEFAULT KEY.
        DATA ls_udmo_entity LIKE LINE OF lt_udmo_entities.
    
        io_xml->read( EXPORTING iv_name = 'UDMO_ENTITIES'
                      CHANGING  cg_data = lt_udmo_entities ).
    
        LOOP AT lt_udmo_entities INTO ls_udmo_entity.
    
          CALL FUNCTION 'SDU_DMO_ENT_PUT'
            EXPORTING
              object = ls_udmo_entity
            EXCEPTIONS
              OTHERS = 0.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD deserialize_long_texts.
    
        DATA BEGIN OF ls_udmo_long_text.
        DATA language TYPE dm40t-sprache.
        DATA header   TYPE thead.
        DATA content TYPE xstring.
        DATA END OF ls_udmo_long_text.
    
        DATA lt_udmo_long_texts LIKE STANDARD TABLE OF ls_udmo_long_text.
        DATA ls_header TYPE thead.
    
        io_xml->read( EXPORTING iv_name = 'UDMO_LONG_TEXTS'
                      CHANGING  cg_data = lt_udmo_long_texts ).
    
        LOOP AT lt_udmo_long_texts INTO ls_udmo_long_text.
    
          ls_udmo_long_text-header-tdfuser = sy-uname.
          ls_udmo_long_text-header-tdfdate = sy-datum.
          ls_udmo_long_text-header-tdftime = sy-uzeit.
    
          " You are reminded that the target system may already have some texts in
          " existence. So we determine the highest existent version.
    
          CLEAR ls_header-tdversion.
    
          SELECT MAX( dokversion )
          INTO ls_header-tdversion
          FROM dokhl
          WHERE id = c_lxe_text_type
          AND object = mv_text_object
          AND langu  = ls_udmo_long_text-language.
    
          " Increment the version
          ls_header-tdversion = ls_header-tdversion + 1.
          ls_udmo_long_text-header-tdversion = ls_header-tdversion.
    
          " This function module takes care of the variation in text processing between various objects.
          CALL FUNCTION 'LXE_OBJ_DOKU_PUT_XSTRING'
            EXPORTING
              slang   = mv_language
              tlang   = ls_udmo_long_text-language
              objtype = c_lxe_text_type
              objname = mv_lxe_text_name
              header  = ls_udmo_long_text-header
              content = ls_udmo_long_text-content.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD deserialize_model.
    
        DATA ls_dm40l TYPE dm40l.
    
        io_xml->read( EXPORTING iv_name = 'DM40L'
                      CHANGING cg_data = ls_dm40l ).
    
        " See SDU_MODEL_PUT
        GET TIME.
    
        ls_dm40l-flg_frame = abap_true.
        ls_dm40l-fstdate   = sy-datum.
        ls_dm40l-fsttime   = sy-uzeit.
        ls_dm40l-fstuser   = sy-uname.
        ls_dm40l-lstdate   = sy-datum.
        ls_dm40l-lsttime   = sy-uzeit.
        ls_dm40l-lstuser   = sy-uname.
    
        MODIFY dm40l FROM ls_dm40l.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error from SDU_MODEL_PUT' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD deserialize_short_texts.
    
        DATA lt_udmo_texts TYPE STANDARD TABLE OF ty_udmo_text_type WITH DEFAULT KEY.
        DATA ls_udmo_text  TYPE ty_udmo_text_type.
        DATA ls_dm40t TYPE dm40t.
    
        " Deserialize the XML
        io_xml->read( EXPORTING iv_name = 'UDMO_TEXTS'
                      CHANGING  cg_data = lt_udmo_texts ).
    
        " For every text provided
        LOOP AT lt_udmo_texts INTO ls_udmo_text.
    
          " Does the text already exist? This is the same logic as used
          " in the FM SDU_MODEL_PUT
          SELECT SINGLE *
            FROM dm40t
            INTO ls_dm40t
            WHERE sprache = ls_udmo_text-sprache
            AND dmoid     = ls_udmo_text-dmoid
            AND as4local  = mv_activation_state.
    
          IF sy-subrc = 0.
            " There is already an active description for this language
            " but the provided description differs
            IF ls_dm40t-langbez <> ls_udmo_text-langbez.
    
              ls_dm40t-langbez = ls_udmo_text-langbez.
              ls_dm40t-lstdate = sy-datum.
              ls_dm40t-lsttime = sy-uzeit.
              ls_dm40t-lstuser = sy-uname.
    
              MODIFY dm40t FROM ls_dm40t.
    
            ENDIF.
          ELSE.
    
            " There is no EXISTING active description in this language
    
            ls_dm40t-as4local = ls_udmo_text-as4local.
            ls_dm40t-dmoid    = ls_udmo_text-dmoid.
            ls_dm40t-langbez  = ls_udmo_text-langbez.
            ls_dm40t-lstdate  = sy-datum.
            ls_dm40t-lsttime  = sy-uzeit.
            ls_dm40t-lstuser  = sy-uname.
            ls_dm40t-sprache  = ls_udmo_text-sprache.
    
            INSERT dm40t FROM ls_dm40t.
    
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD is_name_permitted.
    
        " It is unlikely that a serialized data model will have a name that is not permitted. However
        " there may be reservations in TRESE which could prohibit the data model name.
        " So to be safe, we check. Tx SD11 does this check.
    
        CALL FUNCTION 'SDU_SAA_CHECK'
          EXPORTING
            obj_name   = ms_object_type-objname
            obj_type   = ms_object_type-objtype
          EXCEPTIONS
            wrong_type = 1.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD serialize_entities.
    
        DATA lt_udmo_entities TYPE STANDARD TABLE OF dm41s WITH DEFAULT KEY.
        FIELD-SYMBOLS  TYPE dm41s.
    
        SELECT * FROM dm41s
          INTO TABLE lt_udmo_entities
          WHERE dmoid = mv_data_model
          AND as4local = mv_activation_state
          ORDER BY PRIMARY KEY.
    
        LOOP AT lt_udmo_entities ASSIGNING .
          " You are reminded that administrative information, such as last changed by user, date, time is not serialized.
          CLEAR -lstuser.
          CLEAR -lstdate.
          CLEAR -lsttime.
          CLEAR -fstuser.
          CLEAR -fstdate.
          CLEAR -fsttime.
        ENDLOOP.
    
        " You are reminded that descriptions in other languages do not have to be in existence, although they may.
        IF lines( lt_udmo_entities ) > 0.
          io_xml->add( iv_name = 'UDMO_ENTITIES'
                       ig_data = lt_udmo_entities ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD serialize_long_texts.
    
        " The model has short texts in multiple languages. These are held in DM40T.
    
        " The model has a long description also in a main language, with other long descriptions
        " maintained as translations using SE63 Translation Editor. All of these long texts are held in DOK*
    
        TYPES BEGIN OF ty_language_type.
        TYPES language TYPE dm40t-sprache.
        TYPES END OF ty_language_type.
    
        DATA BEGIN OF ls_udmo_long_text.
        DATA language TYPE dm40t-sprache.
        DATA header   TYPE thead.
        DATA content TYPE xstring.
        DATA END OF ls_udmo_long_text.
    
        DATA lt_udmo_long_texts LIKE STANDARD TABLE OF ls_udmo_long_text.
        DATA lt_udmo_languages TYPE STANDARD TABLE OF ty_language_type.
        DATA ls_udmo_language  LIKE LINE OF lt_udmo_languages.
        DATA: lv_error_status  TYPE lxestatprc.
    
        " In which languages are the short texts are maintained.
        SELECT sprache AS language
          FROM dm40t
          INTO TABLE lt_udmo_languages
          WHERE dmoid    = mv_data_model
          AND as4local = mv_activation_state
          ORDER BY sprache ASCENDING.                       "#EC CI_NOFIRST
    
        " For every language for which a short text is maintained,
        LOOP AT lt_udmo_languages INTO ls_udmo_language.
    
          CLEAR ls_udmo_long_text.
          CLEAR lv_error_status.
    
          ls_udmo_long_text-language = ls_udmo_language-language.
    
          " You are reminded that this function gets the most recent version of the texts.
          CALL FUNCTION 'LXE_OBJ_DOKU_GET_XSTRING'
            EXPORTING
              lang    = ls_udmo_language-language
              objtype = c_lxe_text_type
              objname = mv_lxe_text_name
            IMPORTING
              header  = ls_udmo_long_text-header
              content = ls_udmo_long_text-content
              pstatus = lv_error_status.
    
          CHECK lv_error_status = 'S'. "Success
    
          " Administrative information is not serialized
          CLEAR ls_udmo_long_text-header-tdfuser.
          CLEAR ls_udmo_long_text-header-tdfdate.
          CLEAR ls_udmo_long_text-header-tdftime.
    
          CLEAR ls_udmo_long_text-header-tdluser.
          CLEAR ls_udmo_long_text-header-tdldate.
          CLEAR ls_udmo_long_text-header-tdltime.
    
          APPEND ls_udmo_long_text TO lt_udmo_long_texts.
    
        ENDLOOP.
    
        " You are reminded that long texts do not have to be in existence
        IF lines( lt_udmo_long_texts ) > 0.
          io_xml->add( iv_name = 'UDMO_LONG_TEXTS'
                       ig_data = lt_udmo_long_texts ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD serialize_model.
    
        DATA ls_dm40l TYPE dm40l.
    
        " See SDU_MODEL_GET.
        SELECT SINGLE *
        FROM dm40l
        INTO ls_dm40l
        WHERE dmoid    = mv_data_model
        AND as4local = mv_activation_state.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error from UDMO - model serialisation' ).
        ENDIF.
    
        " You are reminded that administrative data is not serialized.
        CLEAR ls_dm40l-lstdate.
        CLEAR ls_dm40l-lsttime.
        CLEAR ls_dm40l-lstuser.
        CLEAR ls_dm40l-fstdate.
        CLEAR ls_dm40l-fsttime.
        CLEAR ls_dm40l-fstuser.
    
        io_xml->add( iv_name = 'DM40L'
                     ig_data = ls_dm40l ).
    
      ENDMETHOD.
    
      METHOD serialize_short_texts.
    
        DATA lt_udmo_texts TYPE STANDARD TABLE OF ty_udmo_text_type WITH DEFAULT KEY.
        " You are reminded that administrative information, such as last changed by user, date, time is not serialized.
    
        " You are reminded that active short texts of all (existent) languages are serialized.
    
        SELECT sprache dmoid as4local langbez
          FROM dm40t
          INTO CORRESPONDING FIELDS OF TABLE lt_udmo_texts
          WHERE dmoid    = mv_data_model
          AND as4local = mv_activation_state
          ORDER BY sprache ASCENDING.                       "#EC CI_NOFIRST
    
        " You are reminded that descriptions in other languages do not have to be in existence.
        IF lines( lt_udmo_texts ) > 0.
          io_xml->add( iv_name = 'UDMO_TEXTS'
                       ig_data = lt_udmo_texts ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD update_tree.
    
        CALL FUNCTION 'RS_TREE_OBJECT_PLACEMENT'
          EXPORTING
            object    = mv_data_model
            operation = 'INSERT'
            type      = c_correction_object_type.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE lstuser INTO rv_user
          FROM dm40l
          WHERE dmoid = mv_data_model
          AND as4local = mv_activation_state.
    
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
    * You are reminded that this function model checks for
    *  - permissions
    *  - locks
    *  - connection to transport and correction system
    *  - deletion of data model, model relations and all documentation
    *  - update of object tree
    *  - releasing of lock
    
        CALL FUNCTION 'RPY_DATAMODEL_DELETE'
          EXPORTING
            model_name       = mv_data_model
          EXCEPTIONS
            cancelled        = 1
            permission_error = 2
            not_found        = 3
            is_used          = 4
            OTHERS           = 5.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
    * You are reminded that this method checks for
    *  - validity of data model name with regard to naming conventions
    *  - permissions and locks
    *  - connection to transport and correction system
    *  - insert of data model, model relations and all documentation
    *  - update of object tree
    *  - releasing of lock
    
    * Is the data model name compliant with naming conventions?
        is_name_permitted( ).
    
    * Access Permission granted?
        access_modify( ).
    
    * Connection to transport and correction system
        corr_insert( iv_package ).
    
    * Insert the data model, relations and documentation
        TRY.
            deserialize_model( io_xml ).
            deserialize_entities( io_xml ).
            deserialize_short_texts( io_xml ).
            deserialize_long_texts( io_xml ).
            update_tree( ).
            access_free( ).
    
          CATCH zcx_abapgit_exception.
    
            access_free( ).
    
            zcx_abapgit_exception=>raise( 'Error in deserialization of UDMO' ).
    
        ENDTRY.
    
        " You are reminded that data models are not relevant for activation.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        "  See Function Module SDU_MODEL_EXISTS
    
        SELECT COUNT( * ) FROM dm40l
          WHERE dmoid = mv_data_model AND as4local = mv_activation_state.
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for(
          iv_lock_object = 'ESDUM'
          iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        " The function module listed below do not open a new window - so we revert to BDC.
        "    CALL FUNCTION 'SDU_MODEL_SHOW'
        "    CALL FUNCTION 'RS_TOOL_ACCESS'
    
        DATA lt_bdcdata TYPE TABLE OF bdcdata.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_bdcdata.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -program  = 'SAPMUD00'.
        -dynpro   = '0100'.
        -dynbegin = abap_true.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'BDC_OKCODE'.
        -fval = '=SHOW'.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'RSUD3-DATM'.
        -fval = abap_true.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'RSUD3-OBJ_KEY'.
        -fval = ms_item-obj_name.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'SD11'
          it_bdcdata = lt_bdcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        serialize_model( io_xml ).
        serialize_entities( io_xml ).
        serialize_short_texts( io_xml ).
        serialize_long_texts( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_ueno IMPLEMENTATION.
    
      METHOD build_text_name.
    
        TYPES BEGIN OF ty_text_name.
        TYPES id       TYPE c LENGTH 4.
        TYPES entity   TYPE c LENGTH 26.
        TYPES modifier TYPE c LENGTH 2.
        TYPES END OF ty_text_name.
    
        DATA ls_text_name TYPE ty_text_name.
    
        ls_text_name-id = iv_id.
        ls_text_name-entity = mv_entity_id.
        ls_text_name-modifier = 'A%'.
    
        rv_result = ls_text_name.
    
      ENDMETHOD.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        mv_entity_id = is_item-obj_name.
    
      ENDMETHOD.
    
      METHOD delete_docu_uen.
    
        DATA lt_dm02l TYPE STANDARD TABLE OF dm02l WITH DEFAULT KEY.
        DATA ls_dm02l TYPE dm02l.
    
        SELECT *
          FROM dm02l
          INTO TABLE lt_dm02l
          WHERE entid = mv_entity_id
          ORDER BY PRIMARY KEY.
    
        LOOP AT lt_dm02l INTO ls_dm02l.
    
          CALL FUNCTION 'SDU_DOCU_DELETE'
            EXPORTING
              key1     = ls_dm02l-entid
              key2     = ls_dm02l-as4local
              key3     = '00'
              langu    = mv_language
              obj_id   = 'UENC' "Entity Comments
            EXCEPTIONS
              ret_code = 0.
    
          CALL FUNCTION 'SDU_DOCU_DELETE'
            EXPORTING
              key1     = ls_dm02l-entid
              key2     = ls_dm02l-as4local
              key3     = '00'
              langu    = mv_language
              obj_id   = 'UEND' "Entity Definition
            EXCEPTIONS
              ret_code = 0.
    
          CALL FUNCTION 'SDU_DOCU_DELETE'
            EXPORTING
              key1     = ls_dm02l-entid
              key2     = ls_dm02l-as4local
              key3     = '00'
              langu    = mv_language
              obj_id   = 'UENE' "Entity Example
            EXCEPTIONS
              ret_code = 0.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD delete_docu_url.
    
        DATA lt_dm42s TYPE STANDARD TABLE OF dm42s WITH DEFAULT KEY.
        DATA ls_dm42s LIKE LINE OF lt_dm42s.
    
        SELECT *
          FROM dm42s
          INTO TABLE lt_dm42s
          WHERE entidto = mv_entity_id
          ORDER BY PRIMARY KEY.
    
        LOOP AT lt_dm42s INTO ls_dm42s.
    
          CALL FUNCTION 'SDU_DOCU_DELETE'
            EXPORTING
              langu    = mv_language
              obj_id   = 'URL1'
              key1     = ls_dm42s-entidto
              key2     = ls_dm42s-as4local
              key3     = ls_dm42s-entidfrom
              key4     = ls_dm42s-ebrolnr
            EXCEPTIONS
              ret_code = 0.
    
          CALL FUNCTION 'SDU_DOCU_DELETE'
            EXPORTING
              langu    = mv_language
              obj_id   = 'URL2'
              key1     = ls_dm42s-entidto
              key2     = ls_dm42s-as4local
              key3     = ls_dm42s-entidfrom
              key4     = ls_dm42s-ebrolnr
            EXCEPTIONS
              ret_code = 0.
    
          CALL FUNCTION 'SDU_DOCU_DELETE'
            EXPORTING
              langu    = mv_language
              obj_id   = 'URLC'
              key1     = ls_dm42s-entidto
              key2     = ls_dm42s-as4local
              key3     = ls_dm42s-entidfrom
              key4     = ls_dm42s-ebrolnr
            EXCEPTIONS
              ret_code = 0.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD delete_docu_usp.
    
        DATA lt_dm45l TYPE STANDARD TABLE OF dm45l WITH DEFAULT KEY.
        DATA ls_dm45l LIKE LINE OF lt_dm45l.
    
        SELECT *
          FROM dm45l
          INTO TABLE lt_dm45l
          WHERE entid = ms_item-obj_name
          ORDER BY PRIMARY KEY.
    
        LOOP AT lt_dm45l INTO ls_dm45l.
    
          CALL FUNCTION 'SDU_DOCU_DELETE'
            EXPORTING
              langu    = mv_language
              obj_id   = 'USPD'
              key1     = ls_dm45l-entid
              key2     = ls_dm45l-as4local
              key3     = ls_dm45l-spezid
            EXCEPTIONS
              ret_code = 0.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD deserialize_docu_uen.
    
        DATA lt_docu TYPE ty_docu_lines.
    
        io_xml->read( EXPORTING iv_name = 'DOCU_UENC'
                     CHANGING cg_data = lt_docu ).
        deserialize_docu_xxxx( lt_docu ).
    
        CLEAR lt_docu.
        io_xml->read( EXPORTING iv_name = 'DOCU_UEND'
                     CHANGING cg_data = lt_docu ).
        deserialize_docu_xxxx( lt_docu ).
    
        CLEAR lt_docu.
        io_xml->read( EXPORTING iv_name = 'DOCU_UENE'
                     CHANGING cg_data = lt_docu ).
        deserialize_docu_xxxx( lt_docu ).
    
      ENDMETHOD.
    
      METHOD deserialize_docu_url.
    
        DATA lt_docu TYPE ty_docu_lines.
    
        io_xml->read( EXPORTING iv_name = 'DOCU_URL1'
                     CHANGING cg_data = lt_docu ).
    
        deserialize_docu_xxxx( lt_docu ).
    
        CLEAR lt_docu.
        io_xml->read( EXPORTING iv_name = 'DOCU_URL2'
                     CHANGING cg_data = lt_docu ).
    
        deserialize_docu_xxxx( lt_docu ).
    
        CLEAR lt_docu.
        io_xml->read( EXPORTING iv_name = 'DOCU_URLC'
                     CHANGING cg_data = lt_docu ).
    
        deserialize_docu_xxxx( lt_docu ).
    
      ENDMETHOD.
    
      METHOD deserialize_docu_usp.
    
        DATA lt_docu TYPE ty_docu_lines.
    
        io_xml->read( EXPORTING iv_name = 'DOCU_USPD'
                     CHANGING cg_data = lt_docu ).
    
        deserialize_docu_xxxx( lt_docu ).
    
      ENDMETHOD.
    
      METHOD deserialize_docu_xxxx.
    
        DATA ls_docu LIKE LINE OF it_docu.
        DATA lv_objname TYPE lxeobjname.
        DATA lv_change_flag TYPE char1.
        DATA lv_error_status  TYPE lxestatprc.
    
        LOOP AT it_docu INTO ls_docu.
    
          ls_docu-header-tdfuser = sy-uname.
          ls_docu-header-tdfdate = sy-datum.
          ls_docu-header-tdftime = sy-uzeit.
          ls_docu-header-tdfreles = sy-saprl.
    
          ls_docu-header-tdluser = sy-uname.
          ls_docu-header-tdldate = sy-datum.
          ls_docu-header-tdltime = sy-uzeit.
          ls_docu-header-tdlreles = sy-saprl.
    
          lv_objname = ls_docu-header-tdname.
    
          CALL FUNCTION 'LXE_OBJ_DOKU_PUT_XSTRING'
            EXPORTING
              slang       = mv_language
              tlang       = ls_docu-language
              objtype     = ls_docu-header-tdid
              objname     = lv_objname
              header      = ls_docu-header
              content     = ls_docu-content
            IMPORTING
              change_flag = lv_change_flag
              pstatus     = lv_error_status.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD get_field_rules.
    
        DATA:
          lt_fields    TYPE TABLE OF string,
          lv_fields    TYPE string,
          lv_table     TYPE tabname,
          lv_field     TYPE string,
          lv_rule      TYPE string,
          lv_rule_iter TYPE string,
          lv_fill_rule TYPE zif_abapgit_field_rules=>ty_fill_rule,
          lv_prefix    TYPE fieldname,
          lv_suffix    TYPE fieldname.
    
        ro_result = zcl_abapgit_field_rules=>create( ).
    
        " Many tables and fields with date,time,user so we encode them
        APPEND 'DM02L,FL,DTU' TO lt_fields.
        APPEND 'DM02T,L,DTU' TO lt_fields.
        APPEND 'DM03S,FL,DTU' TO lt_fields.
        APPEND 'DM25L,FL,DTU' TO lt_fields.
        APPEND 'DM26L,FL,DTU' TO lt_fields.
        APPEND 'DM42S,FL,DTU' TO lt_fields.
        APPEND 'DM42T,L,DTU' TO lt_fields.
        APPEND 'DM43T,L,DU' TO lt_fields.
        APPEND 'DM45L,FL,DTU' TO lt_fields.
        APPEND 'DM45T,L,DTU' TO lt_fields.
        APPEND 'DM46S,FL,DTU' TO lt_fields.
    
        LOOP AT lt_fields INTO lv_fields.
          SPLIT lv_fields AT ',' INTO lv_table lv_field lv_rule_iter.
    
          DO strlen( lv_field ) TIMES.
            CASE lv_field(1).
              WHEN 'F'.
                lv_prefix = 'FST'.
              WHEN 'L'.
                lv_prefix = 'LST'.
            ENDCASE.
    
            lv_rule = lv_rule_iter.
            DO strlen( lv_rule ) TIMES.
              CASE lv_rule(1).
                WHEN 'D'.
                  lv_suffix    = 'DATE'.
                  lv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-date.
                WHEN 'T'.
                  lv_suffix    = 'TIME'.
                  lv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-time.
                WHEN 'U'.
                  lv_suffix    = 'USER'.
                  lv_fill_rule = zif_abapgit_field_rules=>c_fill_rule-user.
              ENDCASE.
    
              ro_result->add(
                iv_table     = lv_table
                iv_field     = lv_prefix && lv_suffix
                iv_fill_rule = lv_fill_rule ).
    
              SHIFT lv_rule LEFT.
            ENDDO.
    
            SHIFT lv_field LEFT.
          ENDDO.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD get_generic.
    
        CREATE OBJECT ro_generic
          EXPORTING
            io_field_rules = get_field_rules( )
            is_item        = ms_item
            iv_language    = mv_language.
    
      ENDMETHOD.
    
      METHOD is_name_permitted.
    
        " It is unlikely that a serialized entity will have a name that is not permitted. However
        " there may be reservations in TRESE which could prohibit the entity name.
        " So to be safe, we check. Tx SD11 does this check.
    
        CALL FUNCTION 'SDU_SAA_CHECK'
          EXPORTING
            obj_name   = ms_item-obj_name
            obj_type   = ms_item-obj_type
          EXCEPTIONS
            wrong_type = 1.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD serialize_docu_uen.
    
        DATA lt_docu            TYPE ty_docu_lines.
    
        lt_docu = serialize_docu_xxxx( 'UENC' ).
    
        io_xml->add( iv_name = 'DOCU_UENC'
                     ig_data = lt_docu ).
    
        lt_docu = serialize_docu_xxxx( 'UEND' ).
    
        io_xml->add( iv_name = 'DOCU_UEND'
                     ig_data = lt_docu ).
    
        lt_docu = serialize_docu_xxxx( 'UENE' ).
    
        io_xml->add( iv_name = 'DOCU_UENE'
                     ig_data = lt_docu ).
      ENDMETHOD.
    
      METHOD serialize_docu_url.
    
        DATA lt_docu            TYPE ty_docu_lines.
    
        lt_docu = serialize_docu_xxxx( 'URL1' ).
        io_xml->add( iv_name = 'DOCU_URL1'
                     ig_data = lt_docu ).
    
        lt_docu = serialize_docu_xxxx( 'URL2' ).
        io_xml->add( iv_name = 'DOCU_URL2'
                     ig_data = lt_docu ).
    
        lt_docu = serialize_docu_xxxx( 'URLC' ).
        io_xml->add( iv_name = 'DOCU_URLC'
                     ig_data = lt_docu ).
    
      ENDMETHOD.
    
      METHOD serialize_docu_usp.
    
        DATA lt_docu            TYPE ty_docu_lines.
    
        lt_docu = serialize_docu_xxxx( 'USPD' ).
    
        io_xml->add( iv_name = 'DOCU_USPD'
                     ig_data = lt_docu ).
    
      ENDMETHOD.
    
      METHOD serialize_docu_xxxx.
    
        DATA ls_docu            TYPE ty_docu.
        DATA ls_dokvl           TYPE dokvl.
        DATA lt_dokvl           TYPE STANDARD TABLE OF dokvl.
        DATA lv_error_status    TYPE lxestatprc.
        DATA lv_objname         TYPE lxeobjname.
    
        ls_dokvl-object = build_text_name( iv_id ).
    
        SELECT id object langu
          FROM dokvl
          INTO CORRESPONDING FIELDS OF TABLE lt_dokvl
          WHERE id = c_text_object_type
          AND   object LIKE ls_dokvl-object
          ORDER BY id object langu ##TOO_MANY_ITAB_FIELDS.
    
        LOOP AT lt_dokvl INTO ls_dokvl.
    
          ls_docu-language = ls_dokvl-langu.
          lv_objname = ls_dokvl-object.
    
          " You are reminded that this function gets the most recent version of the texts.
          CALL FUNCTION 'LXE_OBJ_DOKU_GET_XSTRING'
            EXPORTING
              lang    = ls_docu-language
              objtype = c_text_object_type
              objname = lv_objname
            IMPORTING
              header  = ls_docu-header
              content = ls_docu-content
              itf     = ls_docu-itf
              pstatus = lv_error_status ##ARG_OK.
    
          CHECK lv_error_status = 'S'. "Success
    
          " Administrative information is not
          CLEAR ls_docu-header-tdfuser.
          CLEAR ls_docu-header-tdfdate.
          CLEAR ls_docu-header-tdftime.
          CLEAR ls_docu-header-tdfreles.
    
          CLEAR ls_docu-header-tdluser.
          CLEAR ls_docu-header-tdldate.
          CLEAR ls_docu-header-tdltime.
          CLEAR ls_docu-header-tdlreles.
    
          APPEND ls_docu TO rt_result.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE lstuser INTO rv_user
          FROM dm02l
          WHERE entid = mv_entity_id
          AND as4local = c_active_state.
    
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        " The deletion of the documentation occurs before the deletion of
        " the associated tables - otherwise we don't know what
        " documentation needs deletion
        delete_docu_uen( ).
        delete_docu_url( ).
        delete_docu_usp( ).
    
        " the deletion of the tables of the entity
        get_generic( )->delete( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        " Is the entity type name compliant with naming conventions?
        " Entity Type have their own conventions.
        is_name_permitted( ).
    
        get_generic( )->deserialize(
          iv_package = iv_package
          io_xml     = io_xml ).
    
        deserialize_docu_uen( io_xml ).
        deserialize_docu_url( io_xml ).
        deserialize_docu_usp( io_xml ).
    
        " You are reminded that entity types are not relevant for activation.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        rv_bool = get_generic( )->exists( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = exists_a_lock_entry_for(
          iv_lock_object = 'ESDUM'
          iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        " The function module listed below do not open a new window - so we revert to BDC.
        "    CALL FUNCTION 'SDU_MODEL_SHOW'
        "    CALL FUNCTION 'RS_TOOL_ACCESS'
    
        DATA lt_bdcdata TYPE TABLE OF bdcdata.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_bdcdata.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -program  = 'SAPMUD00'.
        -dynpro   = '0100'.
        -dynbegin = abap_true.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'BDC_OKCODE'.
        -fval = '=SHOW'.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'RSUD3-ENTI'.
        -fval = abap_true.
    
        APPEND INITIAL LINE TO lt_bdcdata ASSIGNING .
        -fnam = 'RSUD3-OBJ_KEY'.
        -fval = ms_item-obj_name.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'SD11'
          it_bdcdata = lt_bdcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        get_generic( )->serialize( io_xml ).
    
        serialize_docu_uen( io_xml ).
        serialize_docu_url( io_xml ).
        serialize_docu_usp( io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_uiad IMPLEMENTATION.
    
      METHOD constructor.
    
        DATA: lo_db_api TYPE REF TO object,
              lr_data   TYPE REF TO data.
    
        super->constructor(
            is_item        = is_item
            iv_language    = iv_language
            io_files       = io_files
            io_i18n_params = io_i18n_params ).
    
        TRY.
            CALL METHOD ('CL_SUI_UIAD_DB_ACCESS')=>('GET_INSTANCE')
              RECEIVING
                ro_instance = lo_db_api.
            CREATE DATA lr_data TYPE ('CL_BLUE_AFF_WB_ACCESS=>TY_METADATA').
          CATCH cx_sy_dyn_call_error
                cx_sy_create_data_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_db_api     TYPE REF TO object,
              lr_data       TYPE REF TO data,
              lv_object_key TYPE c LENGTH 32,
              lx_root       TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:    TYPE any,
                        TYPE any.
    
        CALL METHOD ('CL_SUI_UIAD_DB_ACCESS')=>('GET_INSTANCE')
          RECEIVING
            ro_instance = lo_db_api.
        CREATE DATA lr_data TYPE ('CL_BLUE_AFF_WB_ACCESS=>TY_METADATA').
        ASSIGN lr_data->* TO .
    
        TRY.
            lv_object_key = ms_item-obj_name.
            CALL METHOD lo_db_api->('IF_SUI_UIAD_DB_ACCESS~READ_WB_METADATA')
              EXPORTING
                iv_id       = lv_object_key
                iv_version  = 'A'
                iv_language = mv_language
              RECEIVING
                rs_metadata = .
    
            ASSIGN COMPONENT 'CHANGED_BY' OF STRUCTURE  TO .
            rv_user = .
    
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise_with_text( lx_root ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_uipg IMPLEMENTATION.
    
      METHOD constructor.
    
        DATA: lo_db_api TYPE REF TO object,
              lr_data   TYPE REF TO data.
    
        super->constructor(
            is_item        = is_item
            iv_language    = iv_language
            io_files       = io_files
            io_i18n_params = io_i18n_params ).
    
        TRY.
            CALL METHOD ('/UI2/CL_UIPG_DB_ACCESS')=>('GET_INSTANCE')
              RECEIVING
                ro_instance = lo_db_api.
            CREATE DATA lr_data TYPE ('CL_BLUE_AFF_WB_ACCESS=>TY_METADATA').
          CATCH cx_sy_dyn_call_error
                cx_sy_create_data_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_db_api     TYPE REF TO object,
              lr_data       TYPE REF TO data,
              lv_object_key TYPE c LENGTH 35,
              lx_root       TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:    TYPE any,
                        TYPE any.
    
        TRY.
            CALL METHOD ('/UI2/CL_UIPG_DB_ACCESS')=>('GET_INSTANCE')
              RECEIVING
                ro_instance = lo_db_api.
            CREATE DATA lr_data TYPE ('CL_BLUE_AFF_WB_ACCESS=>TY_METADATA').
            ASSIGN lr_data->* TO .
          CATCH cx_sy_create_object_error
                  cx_sy_create_data_error.
            zcx_abapgit_exception=>raise( 'Object UIPG not supported' ).
        ENDTRY.
    
        TRY.
            lv_object_key = ms_item-obj_name.
            CALL METHOD lo_db_api->('/UI2/IF_UIPG_DB_ACCESS~READ_WB_METADATA')
              EXPORTING
                iv_page_id  = lv_object_key
                iv_version  = 'A'
                iv_language = mv_language
              RECEIVING
                rs_metadata = .
    
            ASSIGN COMPONENT 'CHANGED_BY' OF STRUCTURE  TO .
            rv_user = .
    
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise_with_text( lx_root ).
        ENDTRY.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_uist IMPLEMENTATION.
    
      METHOD constructor.
    
        DATA: lo_db_api TYPE REF TO object,
              lr_data   TYPE REF TO data.
    
        super->constructor(
            is_item        = is_item
            iv_language    = iv_language
            io_files       = io_files
            io_i18n_params = io_i18n_params ).
    
        TRY.
            CREATE OBJECT lo_db_api TYPE ('/UI2/CL_UIST_SVAL_SQL').
            CREATE DATA lr_data TYPE ('CL_BLUE_AFF_WB_ACCESS=>TY_METADATA').
          CATCH cx_sy_create_object_error
                cx_sy_create_data_error.
            RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_db_api     TYPE REF TO object,
              lr_data       TYPE REF TO data,
              lv_object_key TYPE seu_objkey,
              lx_root       TYPE REF TO cx_root.
    
        FIELD-SYMBOLS:    TYPE any,
                        TYPE any.
    
        TRY.
            CREATE OBJECT lo_db_api TYPE ('/UI2/CL_UIST_SVAL_SQL').
            CREATE DATA lr_data TYPE ('CL_BLUE_AFF_WB_ACCESS=>TY_METADATA').
            ASSIGN lr_data->* TO .
          CATCH cx_sy_create_object_error
                  cx_sy_create_data_error.
            zcx_abapgit_exception=>raise( 'Object UIST not supported' ).
        ENDTRY.
    
        TRY.
            lv_object_key = ms_item-obj_name.
            CALL METHOD lo_db_api->('/UI2/IF_UIST_SVAL~GET_METADATA')
              EXPORTING
                object_name = lv_object_key
                version     = 'A'
                language    = mv_language
              RECEIVING
                result      = .
    
            ASSIGN COMPONENT 'CHANGED_BY' OF STRUCTURE  TO .
            rv_user = .
    
          CATCH cx_root INTO lx_root.
            zcx_abapgit_exception=>raise_with_text( lx_root ).
        ENDTRY.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_vcls IMPLEMENTATION.
    
      METHOD is_locked.
    
        DATA:
          ls_rstable_key TYPE rstable, " Lock argument for table RSTABLE
          lv_argument    TYPE eqegraarg.
    
        " Set Values for generic table lock
        ls_rstable_key-tabname = iv_tabname.
        ls_rstable_key-varkey  = iv_argument.
    
        " include all sub keys
        lv_argument = ls_rstable_key.
        lv_argument = lv_argument && '*'.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object         = 'E_TABLEE'
                                                iv_argument            = lv_argument ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        SELECT SINGLE author FROM vcldir INTO rv_user
          WHERE vclname = ms_item-obj_name.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    * Do the same as in VIEWCLUSTER_SAVE_DEFINITION
        DATA: lv_vclname TYPE vcl_name.
    
        lv_vclname = ms_item-obj_name.
    
        DELETE FROM vcldir WHERE vclname = lv_vclname.        "#EC CI_SUBRC
        DELETE FROM vcldirt WHERE vclname = lv_vclname. "#EC CI_NOFIRST "#EC CI_SUBRC
        DELETE FROM vclstruc WHERE vclname = lv_vclname.      "#EC CI_SUBRC
        DELETE FROM vclstruct WHERE vclname = lv_vclname. "#EC CI_NOFIRST "#EC CI_SUBRC
        DELETE FROM vclstrudep WHERE vclname = lv_vclname.    "#EC CI_SUBRC
        DELETE FROM vclmf WHERE vclname = lv_vclname.         "#EC CI_SUBRC
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_vcldir_entry TYPE v_vcldir,
              lt_vclstruc     TYPE TABLE OF v_vclstruc,
              lt_vclstrudep   TYPE TABLE OF v_vclstdep,
              lt_vclmf        TYPE TABLE OF v_vclmf,
              lv_objectname   TYPE ob_object.
    
        io_xml->read( EXPORTING iv_name = 'VCLDIR'
                      CHANGING cg_data = ls_vcldir_entry ).
        io_xml->read( EXPORTING iv_name = 'VLCSTRUC_TAB'
                      CHANGING cg_data = lt_vclstruc ).
        io_xml->read( EXPORTING iv_name = 'VCLSTRUDEP_TAB'
                      CHANGING cg_data = lt_vclstrudep ).
        io_xml->read( EXPORTING iv_name = 'lt_vclstrudep'
                      CHANGING cg_data = lt_vclmf ).
    
        ls_vcldir_entry-author = sy-uname.
        ls_vcldir_entry-changedate = sy-datum.
    
        CALL FUNCTION 'VIEWCLUSTER_SAVE_DEFINITION'
          EXPORTING
            vcldir_entry   = ls_vcldir_entry
          TABLES
            vclstruc_tab   = lt_vclstruc
            vclstrudep_tab = lt_vclstrudep
            vclmf_tab      = lt_vclmf.
    
        corr_insert( iv_package ).
    
        lv_objectname = ls_vcldir_entry-vclname.
    
        CALL FUNCTION 'OBJ_GENERATE'
          EXPORTING
            iv_objectname         = lv_objectname
            iv_objecttype         = c_cluster_type
            iv_maint_mode         = c_mode_insert
            iv_devclass           = iv_package
          EXCEPTIONS
            illegal_call          = 1
            object_not_found      = 2
            generate_error        = 3
            transport_error       = 4
            object_enqueue_failed = 5
            OTHERS                = 6.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA lv_changedate TYPE vcldir-changedate.
    
        SELECT SINGLE changedate INTO lv_changedate FROM vcldir
          WHERE vclname = ms_item-obj_name.
    
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
    
        DATA lv_changedate TYPE vcldir-changedate.
    
        SELECT SINGLE changedate INTO lv_changedate FROM vcldir
          WHERE vclname = ms_item-obj_name.
    
    * see logic in function module VIEWCLUSTER_GET_DEFINITION
        rv_active = boolc( lv_changedate IS NOT INITIAL ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA:
          lv_argument       TYPE seqg3-garg,
          lv_argument_langu TYPE seqg3-garg.
    
        lv_argument       = ms_item-obj_name.
        lv_argument_langu = |@{ ms_item-obj_name }|.
    
        "Check all relevant maintain tables for view clusters
        IF is_locked( iv_tabname = 'VCLDIR'
                      iv_argument = lv_argument ) = abap_true
            OR is_locked( iv_tabname = 'VCLDIRT'
                          iv_argument = lv_argument_langu ) = abap_true
            OR is_locked( iv_tabname = 'VCLSTRUC'
                          iv_argument = lv_argument )       = abap_true
            OR is_locked( iv_tabname = 'VCLSTRUCT'
                          iv_argument = lv_argument_langu ) = abap_true
            OR is_locked( iv_tabname = 'VCLMF'
                          iv_argument = lv_argument )       = abap_true.
    
          rv_is_locked = abap_true.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: ls_bcdata TYPE bdcdata,
              lt_bcdata TYPE STANDARD TABLE OF bdcdata.
    
        ls_bcdata-program  = 'SAPMSVIM'.
        ls_bcdata-dynpro   = '0050'.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam     = 'VIMDYNFLDS-VIEWNAME'.
        ls_bcdata-fval     = ms_item-obj_name.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam     = 'VIMDYNFLDS-STRUCT_MNT'.
        ls_bcdata-fval     = 'X'.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam = 'BDC_OKCODE'.
        ls_bcdata-fval = '=CLUS'.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-program  = 'SAPMSVIM'.
        ls_bcdata-dynpro   = '0052 '.
        ls_bcdata-dynbegin = 'X'.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam     = 'VIMDYNFLDS-VCLNAME'.
        ls_bcdata-fval     = ms_item-obj_name.
        APPEND ls_bcdata TO lt_bcdata.
    
        CLEAR ls_bcdata.
        ls_bcdata-fnam = 'BDC_OKCODE'.
        ls_bcdata-fval = '=CLSH'.
        APPEND ls_bcdata TO lt_bcdata.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'SE54'
          it_bdcdata = lt_bcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_vclname      TYPE vcl_name,
              ls_vcldir_entry TYPE v_vcldir,
              lt_vclstruc     TYPE TABLE OF v_vclstruc,
              lt_vclstrudep   TYPE TABLE OF v_vclstdep,
              lt_vclmf        TYPE TABLE OF v_vclmf.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        lv_vclname = ms_item-obj_name.
    
        CALL FUNCTION 'VIEWCLUSTER_GET_DEFINITION'
          EXPORTING
            vclname                = lv_vclname
          IMPORTING
            vcldir_entry           = ls_vcldir_entry
          TABLES
            vclstruc_tab           = lt_vclstruc
            vclstrudep_tab         = lt_vclstrudep
            vclmf_tab              = lt_vclmf
          EXCEPTIONS
            viewcluster_not_found  = 1
            incomplete_viewcluster = 2
            OTHERS                 = 3.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        SORT lt_vclstrudep BY vclname object objfield.
    
        CLEAR ls_vcldir_entry-author.
        CLEAR ls_vcldir_entry-changedate.
    
        io_xml->add( iv_name = 'VCLDIR'
                     ig_data = ls_vcldir_entry ).
        io_xml->add( iv_name = 'VLCSTRUC_TAB'
                     ig_data = lt_vclstruc ).
        io_xml->add( iv_name = 'VCLSTRUDEP_TAB'
                     ig_data = lt_vclstrudep ).
        io_xml->add( iv_name = 'VCLMF_TAB'
                     ig_data = lt_vclmf ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_view IMPLEMENTATION.
    
      METHOD delete_extras.
    
        DELETE FROM tddat WHERE tabname = iv_name.
    
        insert_transport(
          iv_name      = iv_name
          iv_transport = iv_transport ).
    
      ENDMETHOD.
    
      METHOD deserialize_texts.
    
        DATA:
          lv_name       TYPE ddobjname,
          lt_i18n_langs TYPE TABLE OF langu,
          lt_dd25_texts TYPE ty_dd25_texts,
          ls_dd25v_tmp  TYPE dd25v.
    
        FIELD-SYMBOLS:
                TYPE langu,
           LIKE LINE OF lt_dd25_texts.
    
        lv_name = ms_item-obj_name.
    
        ii_xml->read( EXPORTING iv_name = 'I18N_LANGS'
                      CHANGING  cg_data = lt_i18n_langs ).
    
        ii_xml->read( EXPORTING iv_name = 'DD25_TEXTS'
                      CHANGING  cg_data = lt_dd25_texts ).
    
        mo_i18n_params->trim_saplang_list( CHANGING ct_sap_langs = lt_i18n_langs ).
    
        SORT lt_i18n_langs.
        SORT lt_dd25_texts BY ddlanguage.
    
        LOOP AT lt_i18n_langs ASSIGNING .
    
          " View description
          ls_dd25v_tmp = is_dd25v.
          READ TABLE lt_dd25_texts ASSIGNING  WITH KEY ddlanguage = .
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( |DD25_TEXTS cannot find lang {  } in XML| ).
          ENDIF.
          MOVE-CORRESPONDING  TO ls_dd25v_tmp.
          CALL FUNCTION 'DDIF_VIEW_PUT'
            EXPORTING
              name              = lv_name
              dd25v_wa          = ls_dd25v_tmp
            EXCEPTIONS
              view_not_found    = 1
              name_inconsistent = 2
              view_inconsistent = 3
              put_failure       = 4
              put_refused       = 5
              OTHERS            = 6.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD insert_transport.
    
        DATA:
          ls_key  TYPE tddat,
          lt_keys TYPE TABLE OF tddat.
    
        IF iv_transport IS INITIAL.
          RETURN.
        ENDIF.
    
        ls_key-tabname = iv_name.
        INSERT ls_key INTO TABLE lt_keys.
    
        zcl_abapgit_factory=>get_cts_api( )->create_transport_entries(
          iv_transport = iv_transport
          it_table_ins = lt_keys
          iv_tabname   = 'TDDAT' ).
    
      ENDMETHOD.
    
      METHOD read_extras.
    
        SELECT SINGLE * FROM tddat INTO rs_tabl_extras-tddat WHERE tabname = iv_name.
    
        " Fields that are not part of dd25v
        TRY.
            SELECT SINGLE abap_language_version FROM ('DD25L') INTO CORRESPONDING FIELDS OF rs_tabl_extras
              WHERE viewname = iv_name AND as4local = 'A' AND as4vers = '0000'.
            IF sy-subrc = 0.
              clear_abap_language_version( CHANGING cv_abap_language_version = rs_tabl_extras-abap_language_version ).
            ENDIF.
          CATCH cx_sy_dynamic_osql_semantics ##NO_HANDLER.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD read_view.
    
        DATA: lv_name TYPE ddobjname.
    
        lv_name = ms_item-obj_name.
    
        CALL FUNCTION 'DDIF_VIEW_GET'
          EXPORTING
            name          = lv_name
            state         = 'A'
            langu         = iv_language
          IMPORTING
            gotstate      = ev_state
            dd25v_wa      = es_dd25v
            dd09l_wa      = es_dd09l
          TABLES
            dd26v_tab     = et_dd26v
            dd27p_tab     = et_dd27p
            dd28j_tab     = et_dd28j
            dd28v_tab     = et_dd28v
          EXCEPTIONS
            illegal_input = 1
            OTHERS        = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        es_extras = read_extras( lv_name ).
    
      ENDMETHOD.
    
      METHOD serialize_texts.
    
        DATA:
          lv_index           TYPE i,
          ls_dd25v           TYPE dd25v,
          lt_dd25_texts      TYPE ty_dd25_texts,
          lt_i18n_langs      TYPE TABLE OF langu,
          lt_language_filter TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        FIELD-SYMBOLS:
                LIKE LINE OF lt_i18n_langs,
           LIKE LINE OF lt_dd25_texts.
    
        IF mo_i18n_params->ms_params-main_language_only = abap_true.
          RETURN.
        ENDIF.
    
        " Collect additional languages, skip main lang - it was serialized already
        lt_language_filter = mo_i18n_params->build_language_filter( ).
    
        SELECT DISTINCT ddlanguage AS langu INTO TABLE lt_i18n_langs
          FROM dd25v
          WHERE viewname = ms_item-obj_name
          AND ddlanguage IN lt_language_filter
          AND ddlanguage <> mv_language
          ORDER BY langu.                                     "#EC CI_SUBRC
    
        LOOP AT lt_i18n_langs ASSIGNING .
          lv_index = sy-tabix.
          CLEAR: ls_dd25v.
    
          TRY.
              read_view(
                EXPORTING
                  iv_language = 
                IMPORTING
                  es_dd25v    = ls_dd25v ).
    
            CATCH zcx_abapgit_exception.
              CONTINUE.
          ENDTRY.
    
          IF ls_dd25v-ddlanguage IS INITIAL.
            DELETE lt_i18n_langs INDEX lv_index. " Don't save this lang
            CONTINUE.
          ENDIF.
    
          APPEND INITIAL LINE TO lt_dd25_texts ASSIGNING .
          MOVE-CORRESPONDING ls_dd25v TO .
    
        ENDLOOP.
    
        SORT lt_i18n_langs ASCENDING.
        SORT lt_dd25_texts BY ddlanguage ASCENDING.
    
        IF lines( lt_i18n_langs ) > 0.
          ii_xml->add( iv_name = 'I18N_LANGS'
                       ig_data = lt_i18n_langs ).
    
          ii_xml->add( iv_name = 'DD25_TEXTS'
                       ig_data = lt_dd25_texts ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD update_extras.
    
        DATA lv_abap_language_version TYPE uccheck.
    
        IF is_tabl_extras-tddat IS INITIAL.
          delete_extras(
            iv_name      = iv_name
            iv_transport = iv_transport ).
        ELSE.
          MODIFY tddat FROM is_tabl_extras-tddat.
    
          insert_transport(
            iv_name      = iv_name
            iv_transport = iv_transport ).
        ENDIF.
    
        " Fields that are not part of dd25v
        TRY.
            lv_abap_language_version = is_tabl_extras-abap_language_version.
    
            set_abap_language_version( CHANGING cv_abap_language_version = lv_abap_language_version ).
    
            UPDATE ('DD25L') SET abap_language_version = lv_abap_language_version WHERE viewname = iv_name.
          CATCH cx_sy_dynamic_osql_semantics ##NO_HANDLER.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE as4user FROM dd25l INTO rv_user
          WHERE viewname = ms_item-obj_name
          AND as4local = 'A'
          AND as4vers = '0000'.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA lv_objname TYPE rsedd0-ddobjname.
    
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
    
        lv_objname = ms_item-obj_name.
        delete_ddic( 'V' ).
    
        delete_extras(
          iv_name      = lv_objname
          iv_transport = iv_transport ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_name   TYPE ddobjname,
              ls_dd25v  TYPE dd25v,
              ls_dd09l  TYPE dd09l,
              lt_dd26v  TYPE TABLE OF dd26v,
              lt_dd27p  TYPE TABLE OF dd27p,
              lt_dd28j  TYPE TABLE OF dd28j,
              lt_dd28v  TYPE TABLE OF dd28v,
              ls_extras TYPE zif_abapgit_object_tabl=>ty_internal-extras.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_dd27p.
    
        io_xml->read( EXPORTING iv_name = 'DD25V'
                      CHANGING cg_data = ls_dd25v ).
        io_xml->read( EXPORTING iv_name = 'DD09L'
                      CHANGING cg_data = ls_dd09l ).
        io_xml->read( EXPORTING iv_name = 'DD26V_TABLE'
                      CHANGING cg_data = lt_dd26v ).
        io_xml->read( EXPORTING iv_name = 'DD27P_TABLE'
                      CHANGING cg_data = lt_dd27p ).
        io_xml->read( EXPORTING iv_name = 'DD28J_TABLE'
                      CHANGING cg_data = lt_dd28j ).
        io_xml->read( EXPORTING iv_name = 'DD28V_TABLE'
                      CHANGING cg_data = lt_dd28v ).
        io_xml->read( EXPORTING iv_name = zif_abapgit_object_tabl=>c_s_dataname-tabl_extras
                      CHANGING cg_data = ls_extras ).
    
        lv_name = ms_item-obj_name. " type conversion
    
        IF iv_step = zif_abapgit_object=>gc_step_id-ddic.
    
          LOOP AT lt_dd27p ASSIGNING .
            -objpos = sy-tabix.
            -viewname = lv_name.
            " rollname seems to be mandatory in the API, but is typically not defined in the VIEW
            SELECT SINGLE rollname FROM dd03l INTO -rollname
              WHERE tabname = -tabname
              AND fieldname = -fieldname.
            IF -rollnamevi IS INITIAL.
              -rollnamevi = -rollname.
            ENDIF.
          ENDLOOP.
    
          corr_insert( iv_package = iv_package
                       ig_object_class = 'DICT' ).
    
          CALL FUNCTION 'DDIF_VIEW_PUT'
            EXPORTING
              name              = lv_name
              dd25v_wa          = ls_dd25v
              dd09l_wa          = ls_dd09l
            TABLES
              dd26v_tab         = lt_dd26v
              dd27p_tab         = lt_dd27p
              dd28j_tab         = lt_dd28j
              dd28v_tab         = lt_dd28v
            EXCEPTIONS
              view_not_found    = 1
              name_inconsistent = 2
              view_inconsistent = 3
              put_failure       = 4
              put_refused       = 5
              OTHERS            = 6.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
            deserialize_texts(
              ii_xml   = io_xml
              is_dd25v = ls_dd25v ).
          ENDIF.
    
          deserialize_longtexts( ii_xml         = io_xml
                                 iv_longtext_id = c_longtext_id_view ).
    
          zcl_abapgit_objects_activation=>add_item( ms_item ).
    
        ELSE.
          " Late update after activation because activation removes ABAP Language Version (in lower releases?)
          update_extras( iv_name        = lv_name
                         iv_transport   = iv_transport
                         is_tabl_extras = ls_extras ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_viewname TYPE dd25l-viewname,
              lv_ddl_view TYPE abap_bool.
    
        SELECT SINGLE viewname FROM dd25l INTO lv_viewname
          WHERE viewname = ms_item-obj_name.
        rv_bool = boolc( sy-subrc = 0 ).
    
        IF rv_bool = abap_true.
          TRY.
              CALL METHOD ('CL_DD_DDL_UTILITIES')=>('CHECK_FOR_DDL_VIEW')
                EXPORTING
                  objname     = lv_viewname
                RECEIVING
                  is_ddl_view = lv_ddl_view.
    
              IF lv_ddl_view = abap_true.
                rv_bool = abap_false.
              ENDIF.
            CATCH cx_root ##NO_HANDLER.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-ddic TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by ZCL_ABAPGIT_OBJECT=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_dd25v  TYPE dd25v,
              lv_state  TYPE ddgotstate,
              ls_dd09l  TYPE dd09l,
              lt_dd26v  TYPE ty_dd26v,
              lt_dd27p  TYPE ty_dd27p,
              lt_dd28j  TYPE ty_dd28j,
              lt_dd28v  TYPE ty_dd28v,
              ls_extras TYPE zif_abapgit_object_tabl=>ty_tabl_extras.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_dd27p.
        FIELD-SYMBOLS  TYPE any.
    
        read_view(
          EXPORTING
            iv_language = mv_language
          IMPORTING
            ev_state    = lv_state
            es_dd25v    = ls_dd25v
            es_dd09l    = ls_dd09l
            et_dd26v    = lt_dd26v
            et_dd27p    = lt_dd27p
            et_dd28j    = lt_dd28j
            et_dd28v    = lt_dd28v
            es_extras   = ls_extras ).
    
        IF ls_dd25v IS INITIAL OR lv_state <> 'A'.
          RETURN.
        ENDIF.
    
        CLEAR: ls_dd25v-as4user,
               ls_dd25v-as4date,
               ls_dd25v-as4time.
    
        ASSIGN COMPONENT 'ACTFLAG' OF STRUCTURE ls_dd25v TO .
        IF sy-subrc = 0.
          CLEAR .
        ENDIF.
    
        CLEAR: ls_dd09l-as4user,
               ls_dd09l-as4date,
               ls_dd09l-as4time.
    
        LOOP AT lt_dd27p ASSIGNING .
          CLEAR: -ddtext,
                 -reptext,
                 -scrtext_s,
                 -scrtext_m,
                 -scrtext_l,
                 -outputlen,
                 -decimals,
                 -lowercase,
                 -convexit,
                 -signflag,
                 -flength,
                 -domname,
                 -datatype,
                 -entitytab,
                 -inttype,
                 -intlen,
                 -headlen,
                 -scrlen1,
                 -scrlen2,
                 -scrlen3,
                 -memoryid.
          IF -rollchange = abap_false.
            CLEAR -rollnamevi.
          ENDIF.
          CLEAR -ddlanguage.
          CLEAR -rollname.
          CLEAR -viewname.
          CLEAR -objpos.
        ENDLOOP.
    
        io_xml->add( iv_name = 'DD25V'
                     ig_data = ls_dd25v ).
        io_xml->add( iv_name = 'DD09L'
                     ig_data = ls_dd09l ).
        io_xml->add( ig_data = lt_dd26v
                     iv_name = 'DD26V_TABLE' ).
        io_xml->add( ig_data = lt_dd27p
                     iv_name = 'DD27P_TABLE' ).
        io_xml->add( ig_data = lt_dd28j
                     iv_name = 'DD28J_TABLE' ).
        io_xml->add( ig_data = lt_dd28v
                     iv_name = 'DD28V_TABLE' ).
        io_xml->add( iv_name = zif_abapgit_object_tabl=>c_s_dataname-tabl_extras
                     ig_data = ls_extras ).
    
        IF mo_i18n_params->is_lxe_applicable( ) = abap_false.
          serialize_texts( io_xml ).
        ENDIF.
    
        serialize_longtexts( ii_xml         = io_xml
                             iv_longtext_id = c_longtext_id_view ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_w3xx_super IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        ms_key-relid = ms_item-obj_type+2(2).
        ms_key-objid = ms_item-obj_name.
    
      ENDMETHOD.
    
      METHOD find_param.
    
        FIELD-SYMBOLS  LIKE LINE OF it_params.
    
        READ TABLE it_params ASSIGNING  WITH KEY name = iv_name.
        IF sy-subrc > 0.
          zcx_abapgit_exception=>raise( |W3xx: Cannot find { iv_name } for { ms_key-objid }| ).
        ENDIF.
    
        rv_value = -value.
    
      ENDMETHOD.
    
      METHOD get_ext.
    
        rv_ext = find_param( it_params = it_params
                             iv_name = c_param_names-fileext ).
        SHIFT rv_ext LEFT DELETING LEADING '.'.
    
      ENDMETHOD.
    
      METHOD normalize_params.
    
        FIELD-SYMBOLS  LIKE LINE OF ct_params.
    
        " Ensure filesize param exists
        READ TABLE ct_params ASSIGNING  WITH KEY name = c_param_names-filesize.
        IF sy-subrc <> 0.
          APPEND INITIAL LINE TO ct_params ASSIGNING .
          -name  = c_param_names-filesize.
        ENDIF.
    
        LOOP AT ct_params ASSIGNING .
          -relid = ms_key-relid. " Ensure param key = object key
          -objid = ms_key-objid.
          IF -name = c_param_names-filesize. " Patch filesize = real file size
            -value = iv_size.
            CONDENSE -value.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD strip_params.
    
        FIELD-SYMBOLS  LIKE LINE OF ct_params.
    
        " Remove path from filename
        find_param( it_params = ct_params
                    iv_name = c_param_names-filename ). " Check exists
        READ TABLE ct_params ASSIGNING  WITH KEY name = c_param_names-filename.
        -value = zcl_abapgit_path=>get_filename_from_syspath( |{ -value }| ).
    
        " Clear id and object name
        LOOP AT ct_params ASSIGNING .
          CLEAR: -relid, -objid.
        ENDLOOP.
    
        " Clear version & filesize
        DELETE ct_params WHERE name = c_param_names-version.
        DELETE ct_params WHERE name = c_param_names-filesize.
    
        " Avoid diffs due to different order
        SORT ct_params.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE chname INTO rv_user
          FROM wwwdata
          WHERE relid = ms_key-relid
          AND objid = ms_key-objid
          AND srtf2 = 0.
    
        IF sy-subrc IS NOT INITIAL OR rv_user IS INITIAL.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        CALL FUNCTION 'WWWDATA_DELETE'
          EXPORTING
            key               = ms_key
          EXCEPTIONS
            wrong_object_type = 1
            delete_error      = 2.
    
        IF sy-subrc IS NOT INITIAL.
          zcx_abapgit_exception=>raise( 'Cannot delete W3xx data' ).
        ENDIF.
    
        CALL FUNCTION 'WWWPARAMS_DELETE_ALL'
          EXPORTING
            key          = ms_key
          EXCEPTIONS
            delete_error = 1.
    
        IF sy-subrc IS NOT INITIAL.
          zcx_abapgit_exception=>raise( 'Cannot delete W3xx params' ).
        ENDIF.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA lv_base64str TYPE string.
        DATA lt_w3params  TYPE STANDARD TABLE OF wwwparams.
        DATA lv_xstring   TYPE xstring.
        DATA lt_w3mime    TYPE STANDARD TABLE OF w3mime.
        DATA lt_w3html    TYPE STANDARD TABLE OF w3html.
        DATA lv_size      TYPE i.
    
        io_xml->read( EXPORTING iv_name = 'TEXT'
                      CHANGING  cg_data = ms_key-text ).
    
        io_xml->read( EXPORTING iv_name = 'PARAMS'
                      CHANGING  cg_data = lt_w3params ).
    
        CASE io_xml->get_metadata( )-version.
          WHEN 'v1.0.0'.
            io_xml->read( EXPORTING iv_name = 'DATA'
                          CHANGING  cg_data = lv_base64str ).
            lv_xstring = cl_http_utility=>decode_x_base64( lv_base64str ).
          WHEN 'v2.0.0'.
            lv_xstring = mo_files->read_raw( iv_extra = 'data'
                                             iv_ext   = get_ext( lt_w3params ) ).
          WHEN OTHERS.
            zcx_abapgit_exception=>raise( 'W3xx: Unknown serializer version' ).
        ENDCASE.
    
        CASE ms_key-relid.
          WHEN 'MI'.
            CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
              EXPORTING
                buffer        = lv_xstring
              IMPORTING
                output_length = lv_size
              TABLES
                binary_tab    = lt_w3mime.
          WHEN 'HT'.
            CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
              EXPORTING
                buffer        = lv_xstring
              IMPORTING
                output_length = lv_size
              TABLES
                binary_tab    = lt_w3mime.
    
            CALL FUNCTION 'SCMS_BINARY_TO_TEXT'
              EXPORTING
                input_length  = lv_size
              IMPORTING
                output_length = lv_size
              TABLES
                binary_tab    = lt_w3mime
                text_tab      = lt_w3html
              EXCEPTIONS
                failed        = 1.
            IF sy-subrc IS NOT INITIAL.
              zcx_abapgit_exception=>raise( 'Cannot update W3xx params' ).
            ENDIF.
    
            CLEAR lt_w3mime.
          WHEN OTHERS.
            zcx_abapgit_exception=>raise( 'Wrong W3xx type' ).
        ENDCASE.
    
        " Update size of file based on actual data file size, prove param object name
        normalize_params( EXPORTING iv_size   = lv_size
                          CHANGING  ct_params = lt_w3params ).
    
        CALL FUNCTION 'WWWPARAMS_UPDATE'
          TABLES
            params       = lt_w3params
          EXCEPTIONS
            update_error = 1.
    
        IF sy-subrc IS NOT INITIAL.
          zcx_abapgit_exception=>raise( 'Cannot update W3xx params' ).
        ENDIF.
    
        ms_key-tdate    = sy-datum.
        ms_key-ttime    = sy-uzeit.
        ms_key-chname   = sy-uname.
        ms_key-devclass = iv_package.
    
        CALL FUNCTION 'WWWDATA_EXPORT'
          EXPORTING
            key               = ms_key
          TABLES
            mime              = lt_w3mime
            html              = lt_w3html
          EXCEPTIONS
            wrong_object_type = 1
            export_error      = 2.
    
        IF sy-subrc IS NOT INITIAL.
          zcx_abapgit_exception=>raise( 'Cannot upload W3xx data' ).
        ENDIF.
    
        tadir_insert( iv_package ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        SELECT SINGLE objid INTO ms_key-objid
          FROM wwwdata
          WHERE relid = ms_key-relid
          AND objid = ms_key-objid
          AND srtf2 = 0.
    
        IF sy-subrc IS NOT INITIAL.
          RETURN.
        ENDIF.
    
        rv_bool = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata         = get_metadata( ).
        rs_metadata-version = 'v2.0.0'. " Serialization v2, separate data file
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lv_object TYPE eqegraarg.
    
        lv_object = |{ ms_item-obj_type+2(2) }{ ms_item-obj_name }|.
        OVERLAY lv_object WITH '                                          '.
        lv_object = lv_object && '*'.
    
        rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'E_WWW_HTML'
                                                iv_argument    = lv_object ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
    
        DATA: ls_bdcdata TYPE bdcdata,
              lt_bdcdata TYPE ty_bdcdata.
    
        ls_bdcdata-program  = 'SAPMWWW0'.
        ls_bdcdata-dynpro   = '0100'.
        ls_bdcdata-dynbegin = 'X'.
        APPEND ls_bdcdata TO lt_bdcdata.
    
        change_bdc_jump_data( CHANGING ct_bdcdata = lt_bdcdata ).
    
        CLEAR ls_bdcdata.
        ls_bdcdata-fnam = 'BDC_OKCODE'.
        ls_bdcdata-fval = '=CRO1'.
        APPEND ls_bdcdata TO lt_bdcdata.
    
        ls_bdcdata-program  = 'RSWWWSHW'.
        ls_bdcdata-dynpro   = '1000'.
        ls_bdcdata-dynbegin = 'X'.
        APPEND ls_bdcdata TO lt_bdcdata.
    
        CLEAR ls_bdcdata.
        ls_bdcdata-fnam     = 'SO_OBJID-LOW'.
        ls_bdcdata-fval     = ms_item-obj_name.
        APPEND ls_bdcdata TO lt_bdcdata.
    
        CLEAR ls_bdcdata.
        ls_bdcdata-fnam = 'BDC_OKCODE'.
        ls_bdcdata-fval = '=ONLI'.
        APPEND ls_bdcdata TO lt_bdcdata.
    
        zcl_abapgit_objects_factory=>get_gui_jumper( )->jump_batch_input(
          iv_tcode   = 'SMW0'
          it_bdcdata = lt_bdcdata ).
    
        rv_exit = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA lt_w3mime    TYPE STANDARD TABLE OF w3mime.
        DATA lt_w3html    TYPE STANDARD TABLE OF w3html.
        DATA lt_w3params  TYPE STANDARD TABLE OF wwwparams.
        DATA lv_xstring   TYPE xstring.
        DATA lv_size      TYPE i.
    
        SELECT SINGLE * INTO CORRESPONDING FIELDS OF ms_key
          FROM wwwdata
          WHERE relid = ms_key-relid
          AND objid = ms_key-objid
          AND srtf2 = 0.
    
        IF sy-subrc IS NOT INITIAL.
          RETURN.
        ENDIF.
    
        CALL FUNCTION 'WWWDATA_IMPORT'
          EXPORTING
            key               = ms_key
          TABLES
            mime              = lt_w3mime
            html              = lt_w3html
          EXCEPTIONS
            wrong_object_type = 1
            import_error      = 2.
    
        IF sy-subrc IS NOT INITIAL.
          zcx_abapgit_exception=>raise( 'Cannot read W3xx data' ).
        ENDIF.
    
        CALL FUNCTION 'WWWPARAMS_READ_ALL'
          EXPORTING
            type             = ms_key-relid
            objid            = ms_key-objid
          TABLES
            params           = lt_w3params
          EXCEPTIONS
            entry_not_exists = 1.
    
        IF sy-subrc IS NOT INITIAL.
          zcx_abapgit_exception=>raise( 'Cannot read W3xx data' ).
        ENDIF.
    
        lv_size = find_param( it_params = lt_w3params
                              iv_name = c_param_names-filesize ).
        " Clean params (remove version, filesize & clear filename from path)
        strip_params( CHANGING  ct_params = lt_w3params ).
    
        CASE ms_key-relid.
          WHEN 'MI'.
            CALL FUNCTION 'SCMS_BINARY_TO_XSTRING'
              EXPORTING
                input_length = lv_size
              IMPORTING
                buffer       = lv_xstring
              TABLES
                binary_tab   = lt_w3mime
              EXCEPTIONS
                failed       = 1.
          WHEN 'HT'.
            CALL FUNCTION 'SCMS_TEXT_TO_XSTRING'
              IMPORTING
                buffer   = lv_xstring
              TABLES
                text_tab = lt_w3html
              EXCEPTIONS
                failed   = 1.
          WHEN OTHERS.
            zcx_abapgit_exception=>raise( 'Wrong W3xx type' ).
        ENDCASE.
    
        IF sy-subrc IS NOT INITIAL.
          zcx_abapgit_exception=>raise( 'Cannot convert W3xx to xstring' ).
        ENDIF.
    
        io_xml->add( iv_name = 'NAME'
                     ig_data = ms_key-objid ).
    
        io_xml->add( iv_name = 'TEXT'
                     ig_data = ms_key-text ).
    
        SORT lt_w3params.
    
        io_xml->add( iv_name = 'PARAMS'
                     ig_data = lt_w3params ).
    
        " Serialization v2, separate data file. 'extra' added to prevent conflict with .xml
        mo_files->add_raw( iv_data  = lv_xstring
                           iv_extra = 'data'
                           iv_ext   = get_ext( lt_w3params ) ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_w3ht IMPLEMENTATION.
    
      METHOD change_bdc_jump_data.
    
        DATA: ls_bdcdata LIKE LINE OF ct_bdcdata.
    
        ls_bdcdata-fnam = 'RADIO_HT'.
        ls_bdcdata-fval = 'X'.
        APPEND ls_bdcdata TO ct_bdcdata.
    
        CLEAR ls_bdcdata.
        ls_bdcdata-fnam = 'RADIO_MI'.
        ls_bdcdata-fval = ' '.
        APPEND ls_bdcdata TO ct_bdcdata.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_w3mi IMPLEMENTATION.
    
      METHOD change_bdc_jump_data.
    
        DATA: ls_bdcdata LIKE LINE OF ct_bdcdata.
    
        ls_bdcdata-fnam = 'RADIO_HT'.
        ls_bdcdata-fval = ' '.
        APPEND ls_bdcdata TO ct_bdcdata.
    
        CLEAR ls_bdcdata.
        ls_bdcdata-fnam = 'RADIO_MI'.
        ls_bdcdata-fval = 'X'.
        APPEND ls_bdcdata TO ct_bdcdata.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_wapa IMPLEMENTATION.
    
      METHOD create_new_application.
    
        DATA: ls_item   LIKE ms_item,
              lv_objkey TYPE seu_objkey.
    
        cl_o2_api_application=>create_new(
          EXPORTING
            p_application_data      = is_attributes
            p_nodes                 = it_nodes
            p_navgraph              = it_navgraph
          IMPORTING
            p_application           = ro_bsp
          EXCEPTIONS
            object_already_existing = 1
            object_just_created     = 2
            not_authorized          = 3
            undefined_name          = 4
            author_not_existing     = 5
            action_cancelled        = 6
            error_occured           = 7
            invalid_parameter       = 8 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |WAPA - error from create_new: { sy-subrc }| ).
        ENDIF.
    
        ro_bsp->save( ).
    
        ro_bsp->set_changeable(
          p_changeable           = abap_false
          p_complete_application = abap_true ).
    
        ls_item-obj_type = 'WAPD'.
        ls_item-obj_name = ms_item-obj_name.
        zcl_abapgit_objects_activation=>add_item( ls_item ).
    
        lv_objkey = ls_item-obj_name.
    * todo, hmm, the WAPD is not added to the worklist during activation
        cl_o2_api_application=>activate( lv_objkey ).
    
      ENDMETHOD.
    
      METHOD create_new_page.
    
        cl_o2_api_pages=>create_new_page(
          EXPORTING
            p_pageattrs           = is_page_attributes
          IMPORTING
            p_page                = ro_page
          EXCEPTIONS
            object_already_exists = 1
            invalid_name          = 2
            error_occured         = 3
            o2appl_not_existing   = 4
            OTHERS                = 5 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error { sy-subrc } from CL_O2_API_PAGES=>CREATE_NEW_PAGE| ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD delete_superfluous_pages.
    
        DATA: ls_pagekey TYPE o2pagkey.
        FIELD-SYMBOLS:  LIKE LINE OF it_local_pages.
    
        " delete local pages which doesn't exist remotely
        LOOP AT it_local_pages ASSIGNING .
    
          READ TABLE it_remote_pages WITH KEY attributes-pagekey = -pagekey
                                   TRANSPORTING NO FIELDS.
          IF sy-subrc <> 0.
            " page exists locally but not remotely -> delete
    
            ls_pagekey-applname = -applname.
            ls_pagekey-pagekey = -pagekey.
    
            cl_o2_page=>delete_page_for_application(
              EXPORTING
                p_pagekey           = ls_pagekey
              EXCEPTIONS
                object_not_existing = 1
                error_occured       = 2 ).
    
            IF sy-subrc <> 0.
              zcx_abapgit_exception=>raise( |Error { sy-subrc } from CL_O2_PAGE=>DELETE_PAGE_FOR_APPLICATION| ).
            ENDIF.
    
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD get_page_content.
    
        DATA: lt_content TYPE o2pageline_table,
              lv_string  TYPE string.
    
        io_page->get_page(
          IMPORTING
            p_content    = lt_content
          EXCEPTIONS
            invalid_call = 1
            page_deleted = 2
            OTHERS       = 3 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |WAPA - error from get_page_content| ).
        ENDIF.
    
        CONCATENATE LINES OF lt_content INTO lv_string SEPARATED BY cl_abap_char_utilities=>newline RESPECTING BLANKS.
    
        rv_content = zcl_abapgit_convert=>string_to_xstring_utf8( lv_string ).
    
      ENDMETHOD.
    
      METHOD read_page.
    
        DATA: lv_name    TYPE o2applname,
              ls_pagekey TYPE o2pagkey,
              lv_content TYPE xstring,
              lv_extra   TYPE string,
              lv_ext     TYPE string,
              lo_page    TYPE REF TO cl_o2_api_pages.
    
        lv_name = ms_item-obj_name.
    
        ls_pagekey-applname = lv_name.
        ls_pagekey-pagekey = is_page-pagekey.
    
        cl_o2_api_pages=>load(
          EXPORTING
            p_pagekey = ls_pagekey
          IMPORTING
            p_page    = lo_page ).
    
        lo_page->get_attrs( IMPORTING p_attrs = rs_page-attributes ).
    
        IF rs_page-attributes-pagetype <> so2_controller.
    
          lo_page->get_event_handlers(
            IMPORTING
              p_ev_handler = rs_page-event_handlers
            EXCEPTIONS
              page_deleted = 1
              invalid_call = 2 ).
          ASSERT sy-subrc = 0.
    
          lo_page->get_parameters(
            IMPORTING
              p_parameters = rs_page-parameters
            EXCEPTIONS
              page_deleted = 1
              invalid_call = 2
              OTHERS       = 3 ).
          ASSERT sy-subrc = 0.
    
          lo_page->get_type_source(
            IMPORTING
              p_source     = rs_page-types
            EXCEPTIONS
              page_deleted = 1
              invalid_call = 2
              OTHERS       = 3 ).
          ASSERT sy-subrc = 0.
    
          lv_content = get_page_content( lo_page ).
          SPLIT is_page-pagename AT '.' INTO lv_extra lv_ext.
          REPLACE ALL OCCURRENCES OF '/' IN lv_ext WITH '_-'.
          REPLACE ALL OCCURRENCES OF '/' IN lv_extra WITH '_-'.
          IF iv_no_files_add = abap_false.
            mo_files->add_raw(
              iv_extra = lv_extra
              iv_ext   = lv_ext
              iv_data  = lv_content ).
          ENDIF.
    
          CLEAR: rs_page-attributes-implclass.
    
        ENDIF.
    
        CLEAR: rs_page-attributes-author,
               rs_page-attributes-createdon,
               rs_page-attributes-changedby,
               rs_page-attributes-changedon,
               rs_page-attributes-changetime,
               rs_page-attributes-gendate,
               rs_page-attributes-gentime,
               rs_page-attributes-devclass.
    
      ENDMETHOD.
    
      METHOD to_page_content.
    
        DATA: lv_string TYPE string.
    
        lv_string = zcl_abapgit_convert=>xstring_to_string_utf8( iv_content ).
    
        SPLIT lv_string AT cl_abap_char_utilities=>newline INTO TABLE rt_content.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lv_name   TYPE o2applname,
              lt_pages  TYPE STANDARD TABLE OF o2pagdir WITH DEFAULT KEY,
              ls_latest LIKE LINE OF lt_pages.
    
        lv_name = ms_item-obj_name.
    
        SELECT * FROM o2pagdir INTO TABLE lt_pages WHERE applname = lv_name
          ORDER BY changedon DESCENDING changetime DESCENDING.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
          RETURN.
        ENDIF.
    
        READ TABLE lt_pages INDEX 1 INTO ls_latest.
        ASSERT sy-subrc = 0.
    
        rv_user = ls_latest-changedby.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_name        TYPE o2applname,
              lo_bsp         TYPE REF TO cl_o2_api_application,
              ls_pagekey     TYPE o2pagkey,
              lv_object      TYPE seu_objkey,
              lt_pages       TYPE o2pagelist,
              lt_local_mimes TYPE o2pagename_table.
    
        FIELD-SYMBOLS:        LIKE LINE OF lt_pages,
                        TYPE o2pagename.
    
        lv_name = ms_item-obj_name.
    
        cl_o2_api_application=>load(
          EXPORTING
            p_application_name  = lv_name
          IMPORTING
            p_application       = lo_bsp
          EXCEPTIONS
            object_not_existing = 1
            permission_failure  = 2
            error_occured       = 3 ).
        ASSERT sy-subrc = 0.
    
        lo_bsp->set_changeable(
          p_changeable           = abap_true
          p_complete_application = abap_true ).
    
        cl_o2_api_pages=>get_all_pages(
          EXPORTING
            p_applname = lv_name
            p_version  = c_active
          IMPORTING
            p_pages    = lt_pages ).
    
        LOOP AT lt_pages ASSIGNING .
          CLEAR ls_pagekey.
          ls_pagekey-applname = lv_name.
          ls_pagekey-pagekey  = -pagekey.
    
          cl_o2_page=>delete_page_for_application(
            EXPORTING
              p_pagekey           = ls_pagekey
            EXCEPTIONS
              object_not_existing = 1
              error_occured       = 2 ).
          ASSERT sy-subrc = 0.
        ENDLOOP.
    
        lo_bsp->get_local_mimes(
          IMPORTING
            p_local_mimes  = lt_local_mimes
          EXCEPTIONS
            object_invalid = 1
            object_deleted = 2
            error_occured  = 3
            OTHERS         = 4 ).
    
        LOOP AT lt_local_mimes ASSIGNING .
          CLEAR ls_pagekey.
          ls_pagekey-applname = -applname.
          ls_pagekey-pagekey  = -pagekey.
    
          cl_o2_page=>delete_page_for_application(
            EXPORTING
              p_pagekey           = ls_pagekey
            EXCEPTIONS
              object_not_existing = 1
              error_occured       = 2 ).
          ASSERT sy-subrc = 0.
        ENDLOOP.
    
        lo_bsp->delete(
          EXCEPTIONS
            object_not_empty      = 1
            object_not_changeable = 2
            object_invalid        = 3
            action_cancelled      = 4
            permission_failure    = 5
            error_occured         = 6
            OTHERS                = 7 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |WAPA - error from delete: { sy-subrc }| ).
        ENDIF.
    
    * release lock
        lv_object = lv_name.
        cl_o2_api_application=>call_access_permission(
          p_mode                 = 'FREE'
          p_object               = lv_object
          p_complete_application = abap_true ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lo_bsp            TYPE REF TO cl_o2_api_application,
              ls_attributes     TYPE o2applattr,
              lt_nodes          TYPE o2applnode_table,
              lt_navgraph       TYPE o2applgrap_table,
              lv_obj_name       TYPE string,
              lv_extra          TYPE string,
              lv_ext            TYPE string,
              lo_page           TYPE REF TO cl_o2_api_pages,
              lt_pages_info     TYPE ty_pages_tt,
              ls_pagekey        TYPE o2pagkey,
              ls_local_page     TYPE ty_page,
              lt_remote_content TYPE o2pageline_table,
              lt_local_content  TYPE o2pageline_table,
              lt_local_pages    TYPE o2pagelist.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_pages_info.
        FIELD-SYMBOLS  TYPE uccheck.
    
        io_xml->read( EXPORTING iv_name = 'ATTRIBUTES'
                      CHANGING  cg_data = ls_attributes ).
        io_xml->read( EXPORTING iv_name = 'NAVGRAPH'
                      CHANGING  cg_data = lt_navgraph ).
        io_xml->read( EXPORTING iv_name = 'PAGES'
                      CHANGING  cg_data = lt_pages_info ).
    
        ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE ls_attributes TO .
        IF sy-subrc = 0.
          set_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
    
        ls_attributes-devclass = iv_package.
    
        cl_o2_api_application=>load(
          EXPORTING
            p_application_name  = ls_attributes-applname    " Application Name
          IMPORTING
            p_application       = lo_bsp    " Instance Created
          EXCEPTIONS
            object_not_existing = 1
            permission_failure  = 2
            error_occured       = 3
            OTHERS              = 4 ).
    
        CASE sy-subrc.
          WHEN 0.
    
            cl_o2_api_pages=>get_all_pages(
              EXPORTING
                p_applname = ls_attributes-applname
                p_version  = c_active
              IMPORTING
                p_pages    = lt_local_pages ).
    
          WHEN 1.
    
            lo_bsp = create_new_application( is_attributes = ls_attributes
                                             it_nodes      = lt_nodes
                                             it_navgraph   = lt_navgraph ).
    
          WHEN OTHERS.
    
            zcx_abapgit_exception=>raise( |Error { sy-subrc } from CL_O2_API_APPLICATION=>LOAD| ).
    
        ENDCASE.
    
        LOOP AT lt_pages_info ASSIGNING .
    
          ls_pagekey-applname = -attributes-applname.
          ls_pagekey-pagekey = -attributes-pagekey.
    
          cl_o2_api_pages=>load(
            EXPORTING
              p_pagekey            = ls_pagekey
            IMPORTING
              p_page               = lo_page
            EXCEPTIONS
              object_not_existing  = 1
              version_not_existing = 2
              OTHERS               = 3 ).
    
          CASE sy-subrc.
            WHEN 0.
    
              ls_local_page = read_page( is_page         = -attributes
                                         iv_no_files_add = abap_true ).
    
            WHEN 1.
    
              lo_page = create_new_page( -attributes ).
    
            WHEN 2.
    
              " Do nothing...
    
            WHEN OTHERS.
    
              zcx_abapgit_exception=>raise( |Error { sy-subrc } from CL_O2_API_PAGES=>LOAD| ).
    
          ENDCASE.
    
          SPLIT -attributes-pagename AT '.' INTO lv_extra lv_ext.
          REPLACE ALL OCCURRENCES OF '/' IN lv_extra WITH '_-'.
          REPLACE ALL OCCURRENCES OF '/' IN lv_ext WITH '_-'.
    
          lt_remote_content = to_page_content( mo_files->read_raw( iv_extra = lv_extra
                                                                   iv_ext   = lv_ext ) ).
          lt_local_content = to_page_content( get_page_content( lo_page ) ).
    
          IF ls_local_page =  AND lt_local_content = lt_remote_content.
            " no changes -> nothing to do
            CONTINUE.
          ENDIF.
    
          IF -attributes-pagetype <> so2_controller.
    
            lo_page->set_page( lt_remote_content ).
    
            lo_page->set_event_handlers( -event_handlers ).
            lo_page->set_parameters( -parameters ).
            lo_page->set_type_source( -types ).
    
          ENDIF.
    
          lo_page->save( p_with_all_texts = abap_true ).
    
          lv_obj_name = cl_wb_object_type=>get_concatenated_key_from_id(
            p_key_component1 = -attributes-applname
            p_key_component2 = -attributes-pagekey
            p_external_id    = 'WG ' ).
    
          zcl_abapgit_objects_activation=>add( iv_type = 'WAPP'
                                               iv_name = lv_obj_name ).
    
        ENDLOOP.
    
        delete_superfluous_pages( it_local_pages  = lt_local_pages
                                  it_remote_pages = lt_pages_info ).
    
        zcl_abapgit_sotr_handler=>create_sotr(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_name TYPE o2applname.
    
        lv_name = ms_item-obj_name.
    
        cl_o2_api_application=>load(
          EXPORTING
            p_application_name  = lv_name
          EXCEPTIONS
            object_not_existing = 1
            permission_failure  = 2
            error_occured       = 3 ).
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        rv_is_locked = abap_false.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_name       TYPE o2applname,
              ls_attributes TYPE o2applattr,
              lt_navgraph   TYPE o2applgrap_table,
              lt_pages      TYPE o2pagelist,
              lt_pages_info TYPE ty_pages_tt,
              lo_bsp        TYPE REF TO cl_o2_api_application.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_pages.
        FIELD-SYMBOLS  TYPE uccheck.
    
        lv_name = ms_item-obj_name.
    
        cl_o2_api_application=>load(
          EXPORTING
            p_application_name  = lv_name
          IMPORTING
            p_application       = lo_bsp
          EXCEPTIONS
            object_not_existing = 1
            permission_failure  = 2
            error_occured       = 3 ).
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        lo_bsp->get_attributes(
          EXPORTING
            p_version    = c_active
          IMPORTING
            p_attributes = ls_attributes ).
    
        CLEAR: ls_attributes-author,
               ls_attributes-createdon,
               ls_attributes-changedby,
               ls_attributes-changedon,
               ls_attributes-devclass.
    
        ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE ls_attributes TO .
        IF sy-subrc = 0.
          clear_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
    
        io_xml->add( iv_name = 'ATTRIBUTES'
                     ig_data = ls_attributes ).
    
        lo_bsp->get_navgraph(
          EXPORTING
            p_version  = c_active
          IMPORTING
            p_navgraph = lt_navgraph ).
    
        io_xml->add( iv_name = 'NAVGRAPH'
                     ig_data = lt_navgraph ).
    
        cl_o2_api_pages=>get_all_pages(
          EXPORTING
            p_applname = lv_name
            p_version  = c_active
          IMPORTING
            p_pages    = lt_pages ).
    
        LOOP AT lt_pages ASSIGNING .
          APPEND read_page(  ) TO lt_pages_info.
        ENDLOOP.
    
        io_xml->add( iv_name = 'PAGES'
                     ig_data = lt_pages_info ).
    
        zcl_abapgit_sotr_handler=>read_sotr(
          iv_pgmid       = 'LIMU'
          iv_object      = 'WAPP'
          iv_obj_name    = ms_item-obj_name
          io_i18n_params = mo_i18n_params
          io_xml         = io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_wdca IMPLEMENTATION.
    
      METHOD check.
    
        FIELD-SYMBOLS:  TYPE LINE OF cts_messages.
    
        LOOP AT it_messages ASSIGNING  WHERE severity = 'E'.
          zcx_abapgit_exception=>raise( -text ).
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD delete.
    
        DATA:
          lo_cfg       TYPE REF TO cl_wdr_cfg_persistence_appl,
          lx_err       TYPE REF TO cx_wd_configuration,
          lt_messages  TYPE cts_messages,
          ls_key       TYPE wdy_config_key,
          ls_outline   TYPE wdy_cfg_outline_data,
          lv_operation TYPE i,
          lv_name      TYPE wdy_md_object_name,
          lv_exists    TYPE wdy_boolean.
    
        ls_key = ms_item-obj_name.
    
        TRY.
            CREATE OBJECT lo_cfg
              EXPORTING
                config_key  = ls_key
                object_name = lv_name.
    
            MOVE-CORRESPONDING ls_key TO ls_outline.
    
            lo_cfg->check_config_existent(
              EXPORTING
                i_outline_data       = ls_outline
                i_only_current_layer = abap_false
                i_is_original        = abap_true
              IMPORTING
                e_is_existent        = lv_exists ).
    
            IF lv_exists = abap_false.
              RETURN.
            ENDIF.
    
            lo_cfg->set_transport( trkorr   = iv_transport
                                   devclass = iv_package ).
    
            lv_operation = if_wdr_cfg_constants=>c_cts_operation-e_delete.
            " First call, check, second call, delete
            DO 2 TIMES.
              lo_cfg->do_next_step(
                IMPORTING
                  e_messages  = lt_messages
                CHANGING
                  c_operation = lv_operation ).
              check( lt_messages ).
            ENDDO.
    
          CATCH cx_wd_configuration INTO lx_err.
            IF lx_err->textid = cx_wd_configuration=>conf_config_not_exist.
              RETURN.
            ELSE.
              zcx_abapgit_exception=>raise( 'WDCA, delete error:' && lx_err->get_text( ) ).
            ENDIF.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD read.
    
        DATA:
          lo_cfg    TYPE REF TO cl_wdr_cfg_persistence_appl,
          ls_key    TYPE wdy_config_key,
          lv_exists TYPE abap_bool,
          lx_err    TYPE REF TO cx_wd_configuration,
          lv_name   TYPE wdy_md_object_name.
    
        FIELD-SYMBOLS:
                  LIKE LINE OF et_data,
           LIKE LINE OF -appl_params.
    
        CLEAR: es_outline, et_data.
    
        ls_key = ms_item-obj_name.
    
        TRY.
            CREATE OBJECT lo_cfg
              EXPORTING
                config_key  = ls_key
                object_name = lv_name.
    
            MOVE-CORRESPONDING ls_key TO es_outline.
    
            lo_cfg->check_config_existent(
              EXPORTING
                i_outline_data       = es_outline
                i_only_current_layer = abap_false
                i_is_original        = abap_true
              IMPORTING
                e_is_existent        = lv_exists ).
    
            IF lv_exists = abap_false.
              RETURN.
            ENDIF.
    
            es_outline = lo_cfg->read_outline_data( ).
    
            CLEAR: es_outline-devclass,
                   es_outline-author,
                   es_outline-createdon,
                   es_outline-changedby,
                   es_outline-changedon.
    
            et_data = lo_cfg->read_data( ).
    
            " Clear descriptions since they are release and language-specific
            LOOP AT et_data ASSIGNING .
              LOOP AT -appl_params ASSIGNING .
                CLEAR -description.
              ENDLOOP.
            ENDLOOP.
    
          CATCH cx_wd_configuration INTO lx_err.
            zcx_abapgit_exception=>raise( 'WDCA, read error:' && lx_err->get_text( ) ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD save.
    
        DATA:
          lo_cfg       TYPE REF TO cl_wdr_cfg_persistence_appl,
          lx_err       TYPE REF TO cx_wd_configuration,
          lt_messages  TYPE cts_messages,
          ls_key       TYPE wdy_config_key,
          ls_data      LIKE LINE OF it_data,
          lv_operation TYPE i,
          lv_name      TYPE wdy_md_object_name,
          lv_exists    TYPE wdy_boolean.
    
        MOVE-CORRESPONDING is_outline TO ls_key.
    
        TRY.
            CREATE OBJECT lo_cfg
              EXPORTING
                config_key  = ls_key
                object_name = lv_name.
    
            READ TABLE it_data INDEX 1 INTO ls_data.
            ASSERT sy-subrc = 0.
    
            lo_cfg->check_config_existent(
              EXPORTING
                i_outline_data       = is_outline
                i_only_current_layer = abap_false
                i_is_original        = abap_true
              IMPORTING
                e_is_existent        = lv_exists ).
    
          CATCH cx_wd_configuration ##NO_HANDLER.
            " Ignore
        ENDTRY.
    
        TRY.
            lo_cfg->set_transport( trkorr   = iv_transport
                                   devclass = iv_package ).
            lo_cfg->set_save_data( ls_data ).
            lo_cfg->set_config_description( is_outline ).
    
            IF lv_exists = abap_false.
              lv_operation = if_wdr_cfg_constants=>c_cts_operation-e_create.
            ELSE.
              lv_operation = if_wdr_cfg_constants=>c_cts_operation-e_save.
            ENDIF.
    
            " First call, check, second call, create/save
            DO 2 TIMES.
              lo_cfg->do_next_step(
                IMPORTING
                  e_messages  = lt_messages
                CHANGING
                  c_operation = lv_operation ).
              check( lt_messages ).
            ENDDO.
    
          CATCH cx_wd_configuration INTO lx_err.
            zcx_abapgit_exception=>raise( 'WDCA, save error:' && lx_err->get_text( ) ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA ls_key TYPE wdy_config_key.
    
        ls_key = ms_item-obj_name.
    
        SELECT SINGLE changedby FROM wdy_config_appl INTO rv_user
          WHERE config_id = ls_key-config_id AND config_type = ls_key-config_type AND config_var = ls_key-config_var.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        delete( iv_package   = iv_package
                iv_transport = iv_transport ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_outline     TYPE wdy_cfg_outline_data,
              lt_data        TYPE wdy_cfg_persist_data_appl_tab,
              lt_config_appt TYPE TABLE OF wdy_config_appt,
              lv_xml_string  TYPE string,
              lv_xml_xstring TYPE xstring.
    
        io_xml->read( EXPORTING iv_name = 'OUTLINE'
                      CHANGING  cg_data = ls_outline ).
        io_xml->read( EXPORTING iv_name = 'DATA'
                      CHANGING  cg_data = lt_data ).
    
        save( is_outline   = ls_outline
              it_data      = lt_data
              iv_package   = iv_package
              iv_transport = iv_transport ).
    
        TRY.
            lv_xml_string = mo_files->read_string(
              iv_extra = 'appl_config'
              iv_ext   = 'xml' ).
    
            TRY.
                lv_xml_string = zcl_abapgit_xml_pretty=>print( iv_xml           = lv_xml_string
                                                               iv_ignore_errors = abap_false
                                                               iv_unpretty      = abap_true ).
              CATCH zcx_abapgit_exception.
                zcx_abapgit_exception=>raise( 'Error Un-Pretty Printing WDCA XML Content: ' && ms_item-obj_name ).
            ENDTRY.
    
            REPLACE FIRST OCCURRENCE
              OF REGEX '<\?xml version="1\.0" encoding="[\w-]+"\?>'
              IN lv_xml_string
              WITH '' ##REGEX_POSIX.
            ASSERT sy-subrc = 0.
    
            lv_xml_xstring = zcl_abapgit_convert=>string_to_xstring( lv_xml_string ).
            UPDATE wdy_config_appl
              SET xcontent = lv_xml_xstring
              WHERE config_id   = ls_outline-config_id
                AND config_type = ls_outline-config_type
                AND config_var  = ls_outline-config_var.
          CATCH zcx_abapgit_exception ##NO_HANDLER.
            " File not found
        ENDTRY.
    
        io_xml->read( EXPORTING iv_name = 'DESCR_LANG'
                      CHANGING  cg_data = lt_config_appt ).
    
        IF lt_config_appt IS NOT INITIAL.
          DELETE FROM wdy_config_appt
            WHERE config_id   = ls_outline-config_id
              AND config_type = ls_outline-config_type
              AND config_var  = ls_outline-config_var.
          MODIFY wdy_config_appt FROM TABLE lt_config_appt.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'Error Updating WDY_CONFIG_APPT for Component Config ' && ms_item-obj_name ).
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
        DATA: ls_wdy_config_appl TYPE wdy_config_appl.
        DATA: ls_wdy_config_key TYPE wdy_config_key.
    
        ls_wdy_config_key = ms_item-obj_name.
        SELECT SINGLE * FROM wdy_config_appl
          INTO ls_wdy_config_appl
          WHERE config_id = ls_wdy_config_key-config_id
            AND config_type = ls_wdy_config_key-config_type
            AND config_var = ls_wdy_config_key-config_var.  "#EC CI_GENBUFF
        rv_bool = boolc( sy-subrc = 0 ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_outline     TYPE wdy_cfg_outline_data,
              lt_data        TYPE wdy_cfg_persist_data_appl_tab,
              lt_cc_text     TYPE TABLE OF wdy_config_appt,
              lv_xml_xstring TYPE xstring,
              lv_xml_string  TYPE string.
    
        read( IMPORTING es_outline = ls_outline
                        et_data    = lt_data ).
    
        IF ls_outline IS INITIAL.
          RETURN.
        ENDIF.
    
        io_xml->add( iv_name = 'OUTLINE'
                     ig_data = ls_outline ).
        io_xml->add( iv_name = 'DATA'
                     ig_data = lt_data ).
    
        SELECT SINGLE xcontent
          INTO lv_xml_xstring
          FROM wdy_config_appl
          WHERE config_id = ls_outline-config_id
            AND config_type = ls_outline-config_type
            AND config_var = ls_outline-config_var.
        lv_xml_string = zcl_abapgit_convert=>xstring_to_string_utf8( lv_xml_xstring ).
        IF lv_xml_string IS NOT INITIAL.
          TRY.
              lv_xml_string = zcl_abapgit_xml_pretty=>print(
                iv_xml           = lv_xml_string
                iv_ignore_errors = abap_false ).
            CATCH zcx_abapgit_exception.
              zcx_abapgit_exception=>raise( 'Error Pretty Printing WDCA XML Content: ' && ms_item-obj_name ).
          ENDTRY.
    
          REPLACE FIRST OCCURRENCE
            OF REGEX '<\?xml version="1\.0" encoding="[\w-]+"\?>'
            IN lv_xml_string
            WITH '' ##REGEX_POSIX.
          ASSERT sy-subrc = 0.
        ENDIF.
    
        mo_files->add_string(
          iv_extra  = 'appl_config'
          iv_ext    = 'xml'
          iv_string = lv_xml_string ).
    
        SELECT * FROM wdy_config_appt INTO TABLE lt_cc_text
          WHERE config_id   = ls_outline-config_id
          AND config_type = ls_outline-config_type
          AND config_var  = ls_outline-config_var
          ORDER BY PRIMARY KEY.
        IF lt_cc_text IS NOT INITIAL.
          io_xml->add( iv_name = 'DESCR_LANG'
                       ig_data = lt_cc_text ).
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_wdcc IMPLEMENTATION.
    
      METHOD constructor.
    
        DATA:
          ls_orig_config      TYPE wdy_config_data.
    
        FIELD-SYMBOLS:
           TYPE data.
    
        super->constructor(
          is_item = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        ASSIGN COMPONENT 'CONFIG_IDPAR' OF STRUCTURE ls_orig_config TO .
        IF sy-subrc <> 0.
          RAISE EXCEPTION TYPE zcx_abapgit_type_not_supported EXPORTING obj_type = is_item-obj_type.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: ls_outline    TYPE wdy_cfg_outline_data,
              ls_config_key TYPE wdy_config_key.
    
        ls_config_key-config_id = ms_item-obj_name+0(32).
        ls_config_key-config_type = ms_item-obj_name+32(2).
        ls_config_key-config_var = ms_item-obj_name+34(6).
    
        TRY.
            cl_wdr_cfg_persistence_utils=>read_comp_config_from_db(
              EXPORTING
                config_key   = ls_config_key
              IMPORTING
                outline_data = ls_outline ).
          CATCH cx_static_check.
            zcx_abapgit_exception=>raise( 'Error Reading Component Config from DB: ' && ms_item-obj_name ).
        ENDTRY.
    
        rv_user = ls_outline-changedby.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
        DATA: ls_config_key TYPE wdy_config_key,
              lv_subrc      TYPE sysubrc.
    
        ls_config_key-config_id = ms_item-obj_name+0(32).
        ls_config_key-config_type = ms_item-obj_name+32(2).
        ls_config_key-config_var = ms_item-obj_name+34(6).
    
        " does not exist in 702
        CALL METHOD cl_wdr_cfg_persistence_utils=>('DELETE_CONFIGURATION')
          EXPORTING
            config_key = ls_config_key
          RECEIVING
            subrc      = lv_subrc.
        IF lv_subrc <> 0.
          zcx_abapgit_exception=>raise( 'Error deleting WDCC: ' && ms_item-obj_name ).
        ENDIF.
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_config_id   TYPE c LENGTH 32,
              lv_config_type TYPE n LENGTH 2,
              lv_config_var  TYPE c LENGTH 6,
              lt_otr_texts   TYPE TABLE OF wdy_config_compt,
              ls_orig_config TYPE wdy_config_data,
              lt_config_datt TYPE TABLE OF wdy_config_datt,
              lv_xml_string  TYPE string,
              lv_xml_xstring TYPE xstring.
    
        FIELD-SYMBOLS:  TYPE any.
    
        io_xml->read( EXPORTING iv_name = 'CONFIG_ID'
                      CHANGING  cg_data = ls_orig_config-config_id ).
    
        io_xml->read( EXPORTING iv_name = 'CONFIG_TYPE'
                      CHANGING  cg_data = ls_orig_config-config_type ).
    
        io_xml->read( EXPORTING iv_name = 'CONFIG_VAR'
                      CHANGING  cg_data = ls_orig_config-config_var ).
    
        lv_config_id = ls_orig_config-config_id.
        lv_config_type = ls_orig_config-config_type.
        lv_config_var = ls_orig_config-config_var.
    
        ASSIGN COMPONENT 'CONFIG_IDPAR' OF STRUCTURE ls_orig_config TO .
        IF sy-subrc = 0.
          io_xml->read( EXPORTING iv_name = 'CONFIG_IDPAR'
                         CHANGING cg_data =  ).
        ENDIF.
    
        ASSIGN COMPONENT 'CONFIG_TYPEPAR' OF STRUCTURE ls_orig_config TO .
        IF sy-subrc = 0.
          io_xml->read( EXPORTING iv_name = 'CONFIG_TYPEPAR'
                         CHANGING cg_data =  ).
        ENDIF.
    
        ASSIGN COMPONENT 'CONFIG_VARPAR' OF STRUCTURE ls_orig_config TO .
        IF sy-subrc = 0.
          io_xml->read( EXPORTING iv_name = 'CONFIG_VARPAR'
                         CHANGING cg_data =  ).
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'WDA_COMPONENT'
                      CHANGING  cg_data = ls_orig_config-component ).
    
        lv_xml_string = mo_files->read_string(
          iv_extra = 'comp_config'
          iv_ext   = 'xml' ).
    
        TRY.
            lv_xml_string = zcl_abapgit_xml_pretty=>print( iv_xml           = lv_xml_string
                                                           iv_ignore_errors = abap_false
                                                           iv_unpretty      = abap_true ).
          CATCH zcx_abapgit_exception.
            zcx_abapgit_exception=>raise( 'Error Un-Pretty Printing WDCC XML Content: ' && ms_item-obj_name ).
        ENDTRY.
    
        REPLACE FIRST OCCURRENCE
          OF REGEX '<\?xml version="1\.0" encoding="[\w-]+"\?>'
          IN lv_xml_string
          WITH '' ##REGEX_POSIX.
        ASSERT sy-subrc = 0.
    
        lv_xml_xstring = zcl_abapgit_convert=>string_to_xstring( lv_xml_string ).
        ls_orig_config-xcontent = lv_xml_xstring.
    
        ASSIGN COMPONENT 'PARENT' OF STRUCTURE ls_orig_config TO .
        IF sy-subrc = 0.
          io_xml->read( EXPORTING iv_name = 'PARENT'
                         CHANGING cg_data =  ).
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'RELID'
                      CHANGING  cg_data = ls_orig_config-relid ).
    
        SELECT SINGLE author createdon FROM wdy_config_data INTO (ls_orig_config-author, ls_orig_config-createdon)
          WHERE config_id = lv_config_id AND
        config_type = lv_config_type AND
        config_var = lv_config_var.
    
        IF ls_orig_config-author IS INITIAL.
          ls_orig_config-author = sy-uname.
        ENDIF.
        ls_orig_config-changedby = sy-uname.
        ls_orig_config-changedon = sy-datum.
    
        IF ls_orig_config-createdon IS INITIAL.
          ls_orig_config-createdon = sy-datum.
        ENDIF.
    
        CALL FUNCTION 'ENQUEUE_E_WDY_CONFCOMP'
          EXPORTING
            mode_wdy_config_data = 'E' "if_wdr_cfg_constants=>c_lock_mode_exclusive
            config_id            = lv_config_id
            config_type          = lv_config_type
            config_var           = lv_config_var
            x_config_id          = 'X'
            x_config_type        = 'X'
            x_config_var         = 'X'
          EXCEPTIONS
            foreign_lock         = 1
            system_failure       = 2
            OTHERS               = 3.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'Error Enqueueing Component Config: ' && ms_item-obj_name ).
        ENDIF.
    
        " CL_WDR_CFG_PERSISTENCE_UTILS=>SAVE_COMP_CONFIG_TO_DB does not exist in 702 so we save directly to DB
        DELETE FROM wdy_config_data
          WHERE config_id   = ls_orig_config-config_id
            AND config_type = ls_orig_config-config_type
            AND config_var  = ls_orig_config-config_var.
        MODIFY wdy_config_data FROM ls_orig_config.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'Error Updating WDY_CONFIG_DATA for Component Config ' && ms_item-obj_name ).
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'OTR_TEXT'
                      CHANGING  cg_data = lt_otr_texts ).
    
        IF lt_otr_texts IS NOT INITIAL.
          DELETE FROM wdy_config_compt
            WHERE config_id   = ls_orig_config-config_id
              AND config_type = ls_orig_config-config_type
              AND config_var  = ls_orig_config-config_var.
          MODIFY wdy_config_compt FROM TABLE lt_otr_texts.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'Error Updating WDY_CONFIG_COMPT for Component Config ' && ms_item-obj_name ).
          ENDIF.
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'DESCR_LANG'
                      CHANGING  cg_data = lt_config_datt ).
    
        IF lt_config_datt IS NOT INITIAL.
          DELETE FROM wdy_config_datt
            WHERE config_id   = ls_orig_config-config_id
              AND config_type = ls_orig_config-config_type
              AND config_var  = ls_orig_config-config_var.
          MODIFY wdy_config_datt FROM TABLE lt_config_datt.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'Error Updating WDY_CONFIG_DATT for Component Config ' && ms_item-obj_name ).
          ENDIF.
        ENDIF.
    
        CALL FUNCTION 'DEQUEUE_E_WDY_CONFCOMP'
          EXPORTING
            mode_wdy_config_data = 'E' "if_wdr_cfg_constants=>c_lock_mode_exclusive
            config_id            = lv_config_id
            config_type          = lv_config_type
            config_var           = lv_config_var
            x_config_id          = 'X'
            x_config_type        = 'X'
            x_config_var         = 'X'.
    
        tadir_insert( iv_package ).
    
        after_import( ).
    
        corr_insert( iv_package ).
    
      ENDMETHOD.
    
      METHOD after_import.
    
        DATA: lt_cts_object_entry TYPE STANDARD TABLE OF e071 WITH DEFAULT KEY,
              ls_cts_object_entry LIKE LINE OF lt_cts_object_entry,
              lt_cts_key          TYPE STANDARD TABLE OF e071k WITH DEFAULT KEY.
    
        ls_cts_object_entry-pgmid    = 'R3TR'.
        ls_cts_object_entry-object   = ms_item-obj_type.
        ls_cts_object_entry-obj_name = ms_item-obj_name.
        INSERT ls_cts_object_entry INTO TABLE lt_cts_object_entry.
    
        CALL FUNCTION 'WDR_CFG_AFTER_IMPORT'
          EXPORTING
            iv_tarclient  = sy-mandt
            iv_is_upgrade = abap_false
          TABLES
            tt_e071       = lt_cts_object_entry
            tt_e071k      = lt_cts_key.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: ls_outline    TYPE wdy_cfg_outline_data,
              ls_config_key TYPE wdy_config_key.
    
        ls_config_key-config_id = ms_item-obj_name+0(32).
        ls_config_key-config_type = ms_item-obj_name+32(2).
        ls_config_key-config_var = ms_item-obj_name+34(6).
    
        TRY.
            cl_wdr_cfg_persistence_utils=>read_comp_config_from_db(
              EXPORTING
                config_key   = ls_config_key
              IMPORTING
                outline_data = ls_outline ).
          CATCH cx_static_check.
            rv_bool = abap_false.
            RETURN.
        ENDTRY.
    
        rv_bool = abap_true.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = abap_true.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
    
        DATA: lt_enq   TYPE STANDARD TABLE OF seqg3,
              lv_subrc TYPE sysubrc,
              lv_garg  TYPE eqegraarg.
    
        lv_garg = ms_item-obj_name.
    
        CALL FUNCTION 'ENQUEUE_READ'
          EXPORTING
            gclient               = sy-mandt
            gname                 = 'WDY_CONFIG_DATA'
            garg                  = lv_garg
          IMPORTING
            subrc                 = lv_subrc
          TABLES
            enq                   = lt_enq
          EXCEPTIONS
            communication_failure = 2
            OTHERS                = 1.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'Error check object lock WDCC: ' && ms_item-obj_name ).
        ENDIF.
    
        rv_is_locked = boolc( lines( lt_enq ) > 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lv_xml_xstring TYPE xstring,
              lt_otr_texts   TYPE TABLE OF wdy_config_compt,
              lt_cc_text     TYPE TABLE OF wdy_config_datt,
              ls_orig_config TYPE wdy_config_data,
              ls_outline     TYPE wdy_cfg_outline_data,
              ls_config_key  TYPE wdy_config_key,
              lv_xml_string  TYPE string.
    
        FIELD-SYMBOLS:  TYPE any.
    
        io_xml->add( iv_name = 'OBJECT_NAME'
                     ig_data = ms_item-obj_name ).
    
        ls_config_key-config_id = ms_item-obj_name+0(32).
        ls_config_key-config_type = ms_item-obj_name+32(2).
        ls_config_key-config_var = ms_item-obj_name+34(6).
    
        TRY.
            " original_config_data does not exist in 702
            CALL METHOD cl_wdr_cfg_persistence_utils=>('READ_COMP_CONFIG_FROM_DB')
              EXPORTING
                config_key           = ls_config_key
              IMPORTING
                xml_xcontent         = lv_xml_xstring
                original_config_data = ls_orig_config
                outline_data         = ls_outline.
    
          CATCH cx_static_check.
            zcx_abapgit_exception=>raise( 'Error Reading Component Config from DB: ' && ms_item-obj_name ).
        ENDTRY.
    
        io_xml->add( iv_name = 'CONFIG_ID'
                     ig_data = ls_orig_config-config_id ).
    
        io_xml->add( iv_name = 'CONFIG_TYPE'
                     ig_data = ls_orig_config-config_type ).
    
        io_xml->add( iv_name = 'CONFIG_VAR'
                     ig_data = ls_orig_config-config_var ).
    
        io_xml->add( iv_name = 'WDA_COMPONENT'
                     ig_data = ls_orig_config-component ).
    
        ASSIGN COMPONENT 'CONFIG_IDPAR' OF STRUCTURE ls_orig_config TO .
        IF sy-subrc = 0.
          io_xml->add( iv_name = 'CONFIG_IDPAR'
                       ig_data =  ).
        ENDIF.
    
        ASSIGN COMPONENT 'CONFIG_TYPEPAR' OF STRUCTURE ls_orig_config TO .
        IF sy-subrc = 0.
          io_xml->add( iv_name = 'CONFIG_TYPEPAR'
                       ig_data =  ).
        ENDIF.
    
        ASSIGN COMPONENT 'CONFIG_VARPAR' OF STRUCTURE ls_orig_config TO .
        IF sy-subrc = 0.
          io_xml->add( iv_name = 'CONFIG_VARPAR'
                       ig_data =  ).
        ENDIF.
    
        ASSIGN COMPONENT 'PARENT' OF STRUCTURE ls_orig_config TO .
        IF sy-subrc = 0.
          io_xml->add( iv_name = 'PARENT'
                       ig_data =  ).
        ENDIF.
    
        io_xml->add( iv_name = 'RELID'
                     ig_data = ls_orig_config-relid ).
    
        lv_xml_string = zcl_abapgit_convert=>xstring_to_string_utf8( lv_xml_xstring ).
        IF lv_xml_string IS NOT INITIAL.
          TRY.
              lv_xml_string = zcl_abapgit_xml_pretty=>print(
                iv_xml           = lv_xml_string
                iv_ignore_errors = abap_false ).
            CATCH zcx_abapgit_exception.
              zcx_abapgit_exception=>raise( 'Error Pretty Printing WDCC XML Content: ' && ms_item-obj_name ).
          ENDTRY.
    
          REPLACE FIRST OCCURRENCE
            OF REGEX '<\?xml version="1\.0" encoding="[\w-]+"\?>'
            IN lv_xml_string
            WITH '' ##REGEX_POSIX.
          ASSERT sy-subrc = 0.
        ENDIF.
    
        mo_files->add_string(
          iv_extra  = 'comp_config'
          iv_ext    = 'xml'
          iv_string = lv_xml_string ).
    
        SELECT * FROM wdy_config_compt INTO TABLE lt_otr_texts
          WHERE config_id   = ls_orig_config-config_id
          AND config_type = ls_orig_config-config_type
          AND config_var  = ls_orig_config-config_var
          ORDER BY PRIMARY KEY.
        IF lt_otr_texts IS NOT INITIAL.
          io_xml->add( iv_name = 'OTR_TEXT'
                       ig_data = lt_otr_texts ).
        ENDIF.
    
        SELECT * FROM wdy_config_datt INTO TABLE lt_cc_text
          WHERE config_id   = ls_orig_config-config_id
          AND config_type = ls_orig_config-config_type
          AND config_var  = ls_orig_config-config_var
          ORDER BY PRIMARY KEY.
        IF lt_cc_text IS NOT INITIAL.
          io_xml->add( iv_name = 'DESCR_LANG'
                       ig_data = lt_cc_text ).
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_wdya IMPLEMENTATION.
    
      METHOD read.
    
        DATA: li_app  TYPE REF TO if_wdy_md_application,
              li_map  TYPE REF TO if_object_map,
              lo_prop TYPE REF TO cl_wdy_md_application_property,
              ls_prop LIKE LINE OF et_properties,
              lv_name TYPE wdy_application_name.
    
        CLEAR es_app.
        CLEAR et_properties.
    
        lv_name = ms_item-obj_name.
        TRY.
            li_app = cl_wdy_md_application=>get_object_by_key(
                       name    = lv_name
                       version = 'A' ).
          CATCH cx_wdy_md_not_existing.
            RETURN.
          CATCH cx_wdy_md_permission_failure.
            zcx_abapgit_exception=>raise( 'WDYA, permission failure' ).
        ENDTRY.
    
        li_app->if_wdy_md_object~get_definition( IMPORTING definition = es_app ).
        CLEAR: es_app-author,
               es_app-createdon,
               es_app-changedby,
               es_app-changedon.
    
        li_map = li_app->get_properties( ).
        DO li_map->size( ) TIMES.
          lo_prop ?= li_map->get_by_position( sy-index ).
          lo_prop->get_definition( IMPORTING definition = ls_prop ).
          APPEND ls_prop TO et_properties.
        ENDDO.
    
      ENDMETHOD.
    
      METHOD save.
    
        DATA: li_prop TYPE REF TO if_wdy_md_application_property,
              lo_app  TYPE REF TO cl_wdy_md_application.
    
        FIELD-SYMBOLS:  LIKE LINE OF it_properties.
    
        TRY.
            CREATE OBJECT lo_app
              EXPORTING
                name       = is_app-application_name
                definition = is_app
                devclass   = iv_package.
    
            LOOP AT it_properties ASSIGNING .
              li_prop = lo_app->if_wdy_md_application~create_property( -name ).
              li_prop->set_value( -value ).
            ENDLOOP.
    
            tadir_insert( iv_package ).
    
            lo_app->if_wdy_md_lockable_object~save_to_database( ).
    
            lo_app->if_wdy_md_lockable_object~unlock( ).
          CATCH cx_wdy_md_exception.
            IF lo_app IS NOT INITIAL.
              lo_app->if_wdy_md_lockable_object~unlock( ).
            ENDIF.
            zcx_abapgit_exception=>raise( 'error saving WDYA' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: li_app  TYPE REF TO if_wdy_md_application,
              ls_app  TYPE wdy_application,
              lv_name TYPE wdy_application_name.
    
        lv_name = ms_item-obj_name.
        TRY.
            li_app = cl_wdy_md_application=>get_object_by_key(
                       name    = lv_name
                       version = 'A' ).
    
            li_app->if_wdy_md_object~get_definition( IMPORTING definition = ls_app ).
    
            IF ls_app-changedby IS INITIAL.
              rv_user = ls_app-author.
            ELSE.
              rv_user = ls_app-changedby.
            ENDIF.
          CATCH cx_root.
            rv_user = c_user_unknown.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: li_app    TYPE REF TO if_wdy_md_application,
              lv_objkey TYPE wdy_wb_appl_name,
              lv_type   TYPE seu_type,
              lv_name   TYPE wdy_application_name.
    
        lv_name = ms_item-obj_name.
        TRY.
            li_app = cl_wdy_md_application=>get_object_by_key(
                       name    = lv_name
                       version = 'A' ).
            li_app->if_wdy_md_object~delete( ).
            li_app->if_wdy_md_lockable_object~save_to_database( ).
    
    * method save_to_database calls function module TR_TADIR_INTERFACE
    * with test mode = X, so it does not delete the TADIR entry.
    * Instead the standard code uses RS_TREE_OBJECT_PLACEMENT to delete
    * the TADIR entry
            lv_objkey = ms_item-obj_name.
            CONCATENATE 'O' swbm_c_type_wdy_application INTO lv_type.
            CALL FUNCTION 'RS_TREE_OBJECT_PLACEMENT'
              EXPORTING
                object    = lv_objkey
                type      = lv_type
                operation = 'DELETE'.
    
          CATCH cx_wdy_md_not_existing.
            RETURN.
          CATCH cx_wdy_md_exception.
            zcx_abapgit_exception=>raise( 'WDYA, error deleting' ).
        ENDTRY.
    
        delete_longtexts( c_longtext_id_wdya ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_app        TYPE wdy_application,
              lt_properties TYPE wdy_app_property_table.
    
        io_xml->read( EXPORTING iv_name = 'APP'
                      CHANGING cg_data = ls_app ).
        io_xml->read( EXPORTING iv_name = 'PROPERTIES'
                      CHANGING cg_data = lt_properties ).
    
        save( is_app        = ls_app
              it_properties = lt_properties
              iv_package    = iv_package ).
    
        zcl_abapgit_sotr_handler=>create_sotr(
          iv_package = iv_package
          io_xml     = io_xml ).
    
        deserialize_longtexts( ii_xml         = io_xml
                               iv_longtext_id = c_longtext_id_wdya ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_name TYPE wdy_application_name.
    
        lv_name = ms_item-obj_name.
    
        TRY.
            cl_wdy_md_application=>get_object_by_key(
              name    = lv_name
              version = 'A' ).
            rv_bool = abap_true.
          CATCH cx_wdy_md_not_existing.
            rv_bool = abap_false.
          CATCH cx_wdy_md_permission_failure.
            zcx_abapgit_exception=>raise( 'WDYA, permission failure' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_app        TYPE wdy_application,
              lt_properties TYPE wdy_app_property_table.
    
        read( IMPORTING es_app        = ls_app
                        et_properties = lt_properties ).
    
        io_xml->add( iv_name = 'APP'
                     ig_data = ls_app ).
        io_xml->add( iv_name = 'PROPERTIES'
                     ig_data = lt_properties ).
    
        zcl_abapgit_sotr_handler=>read_sotr(
          iv_pgmid    = 'R3TR'
          iv_object   = ms_item-obj_type
          iv_obj_name = ms_item-obj_name
          io_i18n_params = mo_i18n_params
          io_xml      = io_xml ).
    
        serialize_longtexts( ii_xml         = io_xml
                             iv_longtext_id = c_longtext_id_wdya ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_wdyn IMPLEMENTATION.
    
      METHOD add_fm_exception.
    
        DATA: ls_exception LIKE LINE OF ct_exception.
    
        ls_exception-name = iv_name.
        ls_exception-value = iv_value.
    
        INSERT ls_exception INTO TABLE ct_exception.
    
      ENDMETHOD.
    
      METHOD add_fm_param_exporting.
    
        DATA: ls_param LIKE LINE OF ct_param.
    
        ls_param-kind = abap_func_exporting.
        ls_param-name = iv_name.
        GET REFERENCE OF ig_value INTO ls_param-value.
    
        INSERT ls_param INTO TABLE ct_param.
    
      ENDMETHOD.
    
      METHOD add_fm_param_tables.
    
        DATA: ls_param LIKE LINE OF ct_param.
    
        ls_param-kind = abap_func_tables.
        ls_param-name = iv_name.
        GET REFERENCE OF ct_value INTO ls_param-value.
    
        INSERT ls_param INTO TABLE ct_param.
    
      ENDMETHOD.
    
      METHOD add_with_inactive_parts.
    
        DATA:
          lv_obj_name TYPE trobj_name,
          lv_object   TYPE trobjtype,
          lt_objects  TYPE dwinactiv_tab.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_objects.
    
        lv_obj_name = ms_item-obj_name.
        lv_object = ms_item-obj_type.
    
        CALL FUNCTION 'RS_INACTIVE_OBJECTS_IN_OBJECT'
          EXPORTING
            obj_name         = lv_obj_name
            object           = lv_object
          TABLES
            inactive_objects = lt_objects
          EXCEPTIONS
            object_not_found = 1
            OTHERS           = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        LOOP AT lt_objects ASSIGNING .
          zcl_abapgit_objects_activation=>add( iv_type = -object
                                               iv_name = -obj_name ).
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD delta_controller.
    
        DATA: li_controller TYPE REF TO if_wdy_md_controller,
              lx_error      TYPE REF TO cx_wdy_md_exception,
              lv_found      TYPE abap_bool,
              ls_key        TYPE wdy_md_controller_key,
              ls_obj_new    TYPE svrs2_versionable_object,
              ls_obj_old    TYPE svrs2_versionable_object.
    
        FIELD-SYMBOLS:             LIKE LINE OF mt_components,
                                      LIKE LINE OF mt_sources,
                             TYPE ANY TABLE,
                        TYPE ANY TABLE,
                                        TYPE ANY TABLE,
                                       TYPE ANY TABLE.
    
        ls_key-component_name = is_controller-definition-component_name.
        ls_key-controller_name = is_controller-definition-controller_name.
    
        lv_found = cl_wdy_md_controller=>check_existency(
              component_name  = ls_key-component_name
              controller_name = ls_key-controller_name ).
        IF lv_found = abap_false.
          TRY.
              li_controller ?= cl_wdy_md_controller=>create_complete(
                    component_name  = ls_key-component_name
                    controller_name = ls_key-controller_name
                    controller_type = is_controller-definition-controller_type ).
              li_controller->save_to_database( ).
              li_controller->unlock( ).
            CATCH cx_wdy_md_exception INTO lx_error.
              zcx_abapgit_exception=>raise( |Error creating dummy controller: { lx_error->get_text( ) }| ).
          ENDTRY.
        ENDIF.
    
        ls_obj_new-objtype = wdyn_limu_component_controller.
        ls_obj_new-objname = ls_key.
    
        ls_obj_old-objtype = wdyn_limu_component_controller.
        ls_obj_old-objname = ls_key.
    
        APPEND is_controller-definition TO ls_obj_old-wdyc-defin.
    
        LOOP AT mt_components ASSIGNING 
            WHERE component_name = ls_key-component_name
            AND controller_name = ls_key-controller_name.
          APPEND  TO ls_obj_old-wdyc-ccomp.
        ENDLOOP.
        LOOP AT mt_sources ASSIGNING 
            WHERE component_name = ls_key-component_name
            AND controller_name = ls_key-controller_name.
          APPEND  TO ls_obj_old-wdyc-ccoms.
        ENDLOOP.
    
        ls_obj_old-wdyc-descr = is_controller-descriptions.
        ls_obj_old-wdyc-cusag = is_controller-controller_usages.
        ls_obj_old-wdyc-ccomt = is_controller-controller_component_texts.
        ls_obj_old-wdyc-cpara = is_controller-controller_parameters.
        ls_obj_old-wdyc-cpart = is_controller-controller_parameter_texts.
        ls_obj_old-wdyc-cnode = is_controller-context_nodes.
        ls_obj_old-wdyc-cattr = is_controller-context_attributes.
        ls_obj_old-wdyc-cmapp = is_controller-context_mappings.
    *   Version 702 doesn't have these two attributes so we
    *   use them dynamically for downward compatibility
        ASSIGN COMPONENT 'CONTROLLER_EXCEPTIONS' OF STRUCTURE is_controller
          TO .
        IF sy-subrc = 0.
          ASSIGN COMPONENT 'EXCP' OF STRUCTURE ls_obj_old-wdyc TO .
          IF sy-subrc = 0.
             = .
          ENDIF.
        ENDIF.
        ASSIGN COMPONENT 'CONTROLLER_EXCEPTIONS_TEXTS' OF STRUCTURE is_controller
          TO .
        IF sy-subrc = 0.
          ASSIGN COMPONENT 'EXCPT' OF STRUCTURE ls_obj_old-wdyc TO .
          IF sy-subrc = 0.
             = .
          ENDIF.
        ENDIF.
        ls_obj_old-wdyc-fgrps = is_controller-fieldgroups.
    
        CALL FUNCTION 'SVRS_MAKE_OBJECT_DELTA'
          EXPORTING
            obj_old              = ls_obj_new
            obj_new              = ls_obj_old
          CHANGING
            delta                = rs_delta
          EXCEPTIONS
            inconsistent_objects = 1.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error from SVRS_MAKE_OBJECT_DELTA' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD delta_definition.
    
        DATA: ls_key       TYPE wdy_md_component_key,
              lv_found     TYPE abap_bool,
              ls_obj_new   TYPE svrs2_versionable_object,
              li_component TYPE REF TO if_wdy_md_component,
              lx_error     TYPE REF TO cx_wdy_md_exception,
              ls_obj_old   TYPE svrs2_versionable_object.
    
        ls_key-component_name = is_definition-definition-component_name.
    
        lv_found = cl_wdy_md_component=>check_existency( ls_key-component_name ).
        IF lv_found = abap_false.
          TRY.
              cl_wdy_md_component=>create_complete(
                EXPORTING
                  name      = ls_key-component_name
                IMPORTING
                  component = li_component
                CHANGING
                  devclass  = iv_package ).
              li_component->save_to_database( ).
              li_component->unlock( ).
            CATCH cx_wdy_md_exception INTO lx_error.
              zcx_abapgit_exception=>raise( |Error creating dummy component: { lx_error->get_text( ) }| ).
          ENDTRY.
        ENDIF.
    
        ls_obj_new-objtype = wdyn_limu_component_definition.
        ls_obj_new-objname = ls_key-component_name.
    
        ls_obj_old-objtype = wdyn_limu_component_definition.
        ls_obj_old-objname = ls_key-component_name.
    
        APPEND is_definition-definition TO ls_obj_old-wdyd-defin.
        ls_obj_old-wdyd-descr = is_definition-descriptions.
        ls_obj_old-wdyd-cusag = is_definition-component_usages.
        ls_obj_old-wdyd-intrf = is_definition-interface_implementings.
        ls_obj_old-wdyd-libra = is_definition-library_usages.
        ls_obj_old-wdyd-ctuse = is_definition-ext_ctlr_usages.
        ls_obj_old-wdyd-ctmap = is_definition-ext_ctx_mappings.
    
        CALL FUNCTION 'SVRS_MAKE_OBJECT_DELTA'
          EXPORTING
            obj_old              = ls_obj_new
            obj_new              = ls_obj_old
          CHANGING
            delta                = rs_delta
          EXCEPTIONS
            inconsistent_objects = 1.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error from SVRS_MAKE_OBJECT_DELTA' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD delta_view.
    
        DATA: ls_key     TYPE wdy_md_view_key,
              ls_obj_new TYPE svrs2_versionable_object,
              ls_obj_old TYPE svrs2_versionable_object,
              lv_found   TYPE abap_bool,
              lx_error   TYPE REF TO cx_wdy_md_exception,
              li_view    TYPE REF TO if_wdy_md_abstract_view.
    
        FIELD-SYMBOLS:  LIKE LINE OF ls_obj_old-wdyv-defin.
    
        ls_key-component_name = is_view-definition-component_name.
        ls_key-view_name      = is_view-definition-view_name.
    
        lv_found = cl_wdy_md_abstract_view=>check_existency(
                     component_name = ls_key-component_name
                     name           = ls_key-view_name ).
        IF lv_found = abap_false.
          TRY.
              li_view = cl_wdy_md_abstract_view=>create(
                          component_name = is_view-definition-component_name
                          view_name      = is_view-definition-view_name
                          type           = is_view-definition-type ).
              li_view->save_to_database( ).
              li_view->unlock( ).
            CATCH cx_wdy_md_exception INTO lx_error.
              zcx_abapgit_exception=>raise( |Error creating dummy view: { lx_error->get_text( ) }| ).
          ENDTRY.
        ENDIF.
    
        ls_obj_new-objtype = wdyn_limu_component_view.
        ls_obj_new-objname = ls_key.
    
        ls_obj_old-objtype = wdyn_limu_component_view.
        ls_obj_old-objname = ls_key.
    
        APPEND INITIAL LINE TO ls_obj_old-wdyv-defin ASSIGNING .
        MOVE-CORRESPONDING is_view-definition TO .
    
        ls_obj_old-wdyv-descr = is_view-descriptions.
        ls_obj_old-wdyv-vcont = is_view-view_containers.
        ls_obj_old-wdyv-vcntt = is_view-view_container_texts.
        ls_obj_old-wdyv-ibplg = is_view-iobound_plugs.
        ls_obj_old-wdyv-ibplt = is_view-iobound_plug_texts.
        ls_obj_old-wdyv-plpar = is_view-plug_parameters.
        ls_obj_old-wdyv-plprt = is_view-plug_parameter_texts.
        ls_obj_old-wdyv-uiele = is_view-ui_elements.
        ls_obj_old-wdyv-uicon = is_view-ui_context_bindings.
        ls_obj_old-wdyv-uievt = is_view-ui_event_bindings.
        ls_obj_old-wdyv-uiddc = is_view-ui_ddic_bindings.
        ls_obj_old-wdyv-uiprp = is_view-ui_properties.
        ls_obj_old-wdyv-navil = is_view-navigation_links.
        ls_obj_old-wdyv-navit = is_view-navigation_target_refs.
        ls_obj_old-wdyv-vshno = is_view-vsh_nodes.
        ls_obj_old-wdyv-vshpl = is_view-vsh_placeholders.
        ls_obj_old-wdyv-views = is_view-viewset_properties.
    
        CALL FUNCTION 'SVRS_MAKE_OBJECT_DELTA'
          EXPORTING
            obj_old              = ls_obj_new
            obj_new              = ls_obj_old
          CHANGING
            delta                = rs_delta
          EXCEPTIONS
            inconsistent_objects = 1.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error from SVRS_MAKE_OBJECT_DELTA' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD deserialize_sources.
    
        DATA:
          lv_extra   TYPE string,
          lt_extra   TYPE string_table,
          ls_abap    TYPE abaptxt255,
          lt_abap    TYPE abaptxt255_tab,
          lv_line    TYPE wdy_ctlr_compo_source_vrs-line_number,
          lv_cmpname TYPE wdy_ctlr_compo_source_vrs-cmpname,
          ls_sources LIKE LINE OF mt_sources.
    
        " Old format
        ii_xml->read( EXPORTING iv_name  = 'SOURCES'
                      CHANGING cg_data = mt_sources ).
    
        IF mt_sources IS NOT INITIAL.
          RETURN.
        ENDIF.
    
        " New format
        ii_xml->read( EXPORTING iv_name  = 'FILES'
                      CHANGING cg_data = lt_extra ).
    
        LOOP AT lt_extra INTO lv_extra.
          lv_line = 0.
          lt_abap = mo_files->read_abap( iv_extra = lv_extra ).
          LOOP AT lt_abap INTO ls_abap.
            " Start of method
            FIND REGEX '\s*method\s+(.*)\s*\.' IN ls_abap-line IGNORING CASE SUBMATCHES lv_cmpname ##REGEX_POSIX.
            IF sy-subrc = 0.
              lv_line = 1.
            ENDIF.
    
            IF lv_cmpname IS NOT INITIAL AND lv_line > 0.
              CLEAR ls_sources.
              ls_sources-component_name  = ms_item-obj_name.
              ls_sources-controller_name = to_upper( lv_extra ).
              ls_sources-cmpname         = to_upper( lv_cmpname ).
              ls_sources-line_number     = lv_line.
              ls_sources-source_line     = ls_abap-line.
              INSERT ls_sources INTO TABLE mt_sources.
              lv_line = lv_line + 1.
            ENDIF.
    
            " End of method
            FIND REGEX '\s*endmethod\s*\.' IN ls_abap-line IGNORING CASE ##REGEX_POSIX.
            IF sy-subrc = 0.
              lv_line = 0.
            ENDIF.
          ENDLOOP.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD get_limu_objects.
    
        DATA: lv_name TYPE wdy_component_name.
    
        lv_name = ms_item-obj_name.
        CALL FUNCTION 'WDYN_GET_LIMU_OBJECTS'
          EXPORTING
            component_name = lv_name
          IMPORTING
            limu_objects   = rt_objects.
    
      ENDMETHOD.
    
      METHOD read.
    
        DATA: lt_objects        TYPE wdy_md_transport_keys,
              ls_controller_key TYPE wdy_md_controller_key,
              ls_component_key  TYPE wdy_md_component_key,
              ls_view_key       TYPE wdy_md_view_key.
    
        FIELD-SYMBOLS:                LIKE LINE OF lt_objects,
                                        LIKE LINE OF rs_component-ctlr_metadata,
                                        LIKE LINE OF rs_component-view_metadata,
                             TYPE ANY TABLE,
                        TYPE ANY TABLE.
    
        CLEAR mt_components.
        CLEAR mt_sources.
    
        lt_objects = get_limu_objects( ).
    
        LOOP AT lt_objects ASSIGNING .
          CASE -sub_type.
            WHEN wdyn_limu_component_controller.
              ls_controller_key = -sub_name.
              APPEND read_controller( ls_controller_key ) TO rs_component-ctlr_metadata.
            WHEN wdyn_limu_component_definition.
              ls_component_key = -sub_name.
              rs_component-comp_metadata = read_definition( ls_component_key ).
            WHEN wdyn_limu_component_view.
              ls_view_key = -sub_name.
              APPEND read_view( ls_view_key ) TO rs_component-view_metadata.
            WHEN OTHERS.
              ASSERT 0 = 1.
          ENDCASE.
        ENDLOOP.
    
        SORT rs_component-ctlr_metadata BY
          definition-component_name ASCENDING
          definition-controller_name ASCENDING.
    
        LOOP AT rs_component-ctlr_metadata ASSIGNING .
          SORT -descriptions.
          SORT -controller_usages.
          SORT -controller_components.
          SORT -controller_component_texts.
          SORT -controller_parameters.
          SORT -controller_parameter_texts.
          SORT -context_nodes.
          SORT -context_attributes.
          SORT -context_mappings.
          SORT -fieldgroups.
    *     Version 702 doesn't have these two attributes so we
    *     use them dynamically for downward compatibility
          ASSIGN COMPONENT 'CONTROLLER_EXCEPTIONS' OF STRUCTURE  TO .
          IF sy-subrc = 0.
            SORT .
          ENDIF.
          ASSIGN COMPONENT 'CONTROLLER_EXCEPTION_TEXTS' OF STRUCTURE  TO .
          IF sy-subrc = 0.
            SORT .
          ENDIF.
        ENDLOOP.
    
        SORT rs_component-view_metadata BY
          definition-component_name ASCENDING
          definition-view_name ASCENDING.
    
        LOOP AT rs_component-view_metadata ASSIGNING .
          SORT -descriptions.
          SORT -view_containers.
          SORT -view_container_texts.
          SORT -iobound_plugs.
          SORT -iobound_plug_texts.
          SORT -plug_parameters.
          SORT -plug_parameter_texts.
          SORT -ui_elements.
          SORT -ui_context_bindings.
          SORT -ui_event_bindings.
          SORT -ui_ddic_bindings.
          SORT -ui_properties.
          SORT -navigation_links.
          SORT -navigation_target_refs.
          SORT -vsh_nodes.
          SORT -vsh_placeholders.
          SORT -viewset_properties.
        ENDLOOP.
    
        SORT mt_components BY
          component_name ASCENDING
          controller_name ASCENDING
          cmpname ASCENDING.
    
        SORT mt_sources BY
          component_name ASCENDING
          controller_name ASCENDING
          cmpname ASCENDING
          line_number ASCENDING.
    
      ENDMETHOD.
    
      METHOD read_controller.
    
        DATA: lt_components   TYPE TABLE OF wdy_ctlr_compo_vrs,
              lt_sources      TYPE TABLE OF wdy_ctlr_compo_source_vrs,
              lt_definition   TYPE TABLE OF wdy_controller,
              lt_psmodilog    TYPE TABLE OF smodilog,
              lt_psmodisrc    TYPE TABLE OF smodisrc,
              lt_fm_param     TYPE abap_func_parmbind_tab,
              lt_fm_exception TYPE abap_func_excpbind_tab.
    
        FIELD-SYMBOLS:       TYPE ANY TABLE,
                        TYPE ANY TABLE.
    
    *   Calling FM dynamically because version 702 has less parameters
    
    *   FM parameters
        add_fm_param_exporting( EXPORTING iv_name     = 'CONTROLLER_KEY'
                                          ig_value    = is_key
                                CHANGING  ct_param = lt_fm_param ).
        add_fm_param_exporting( EXPORTING iv_name     = 'GET_ALL_TRANSLATIONS'
                                          ig_value    = abap_false
                                CHANGING  ct_param = lt_fm_param ).
        add_fm_param_tables( EXPORTING iv_name = 'DEFINITION'
                             CHANGING  ct_value = lt_definition
                                       ct_param = lt_fm_param ).
        add_fm_param_tables( EXPORTING iv_name = 'DESCRIPTIONS'
                             CHANGING ct_value = rs_controller-descriptions
                                      ct_param = lt_fm_param ).
        add_fm_param_tables( EXPORTING iv_name = 'CONTROLLER_USAGES'
                             CHANGING ct_value = rs_controller-controller_usages
                                      ct_param = lt_fm_param ).
        add_fm_param_tables( EXPORTING iv_name = 'CONTROLLER_COMPONENTS'
                             CHANGING ct_value = lt_components
                                      ct_param = lt_fm_param ).
        add_fm_param_tables( EXPORTING iv_name = 'CONTROLLER_COMPONENT_SOURCES'
                             CHANGING ct_value = lt_sources
                                      ct_param = lt_fm_param ).
        add_fm_param_tables( EXPORTING iv_name = 'CONTROLLER_COMPONENT_TEXTS'
                             CHANGING ct_value = rs_controller-controller_component_texts
                                      ct_param = lt_fm_param ).
        add_fm_param_tables( EXPORTING iv_name = 'CONTROLLER_PARAMETERS'
                             CHANGING ct_value = rs_controller-controller_parameters
                                      ct_param = lt_fm_param ).
        add_fm_param_tables( EXPORTING iv_name = 'CONTROLLER_PARAMETER_TEXTS'
                             CHANGING ct_value = rs_controller-controller_parameter_texts
                                      ct_param = lt_fm_param ).
        add_fm_param_tables( EXPORTING iv_name = 'CONTEXT_NODES'
                             CHANGING ct_value = rs_controller-context_nodes
                                      ct_param = lt_fm_param ).
        add_fm_param_tables( EXPORTING iv_name = 'CONTEXT_ATTRIBUTES'
                             CHANGING ct_value = rs_controller-context_attributes
                                      ct_param = lt_fm_param ).
        add_fm_param_tables( EXPORTING iv_name = 'CONTEXT_MAPPINGS'
                             CHANGING ct_value = rs_controller-context_mappings
                                      ct_param = lt_fm_param ).
        add_fm_param_tables( EXPORTING iv_name = 'FIELDGROUPS'
                             CHANGING ct_value = rs_controller-fieldgroups
                                      ct_param = lt_fm_param ).
    *   Version 702 doesn't have these two attributes so we
    *   use them dynamically for downward compatibility
        ASSIGN COMPONENT 'CONTROLLER_EXCEPTIONS' OF STRUCTURE rs_controller TO .
        IF sy-subrc = 0.
          add_fm_param_tables( EXPORTING iv_name = 'CONTROLLER_EXCEPTIONS'
                               CHANGING ct_value = 
                                        ct_param = lt_fm_param ).
        ENDIF.
        ASSIGN COMPONENT 'CONTROLLER_EXCEPTION_TEXTS' OF STRUCTURE rs_controller TO .
        IF sy-subrc = 0.
          add_fm_param_tables( EXPORTING iv_name = 'CONTROLLER_EXCEPTION_TEXTS'
                               CHANGING ct_value = 
                                        ct_param = lt_fm_param ).
        ENDIF.
        add_fm_param_tables( EXPORTING iv_name = 'PSMODILOG'
                             CHANGING ct_value = lt_psmodilog
                                      ct_param = lt_fm_param ).
        add_fm_param_tables( EXPORTING iv_name = 'PSMODISRC'
                             CHANGING ct_value = lt_psmodisrc
                                      ct_param = lt_fm_param ).
    
    *   FM exceptions
        add_fm_exception( EXPORTING iv_name = 'NOT_EXISTING'
                                    iv_value = 1
                          CHANGING ct_exception = lt_fm_exception ).
        add_fm_exception( EXPORTING iv_name = 'OTHERS'
                                    iv_value = 2
                          CHANGING ct_exception = lt_fm_exception ).
    
        CALL FUNCTION 'WDYC_GET_OBJECT'
          PARAMETER-TABLE
          lt_fm_param
          EXCEPTION-TABLE
          lt_fm_exception.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error from WDYC_GET_OBJECT' ).
        ENDIF.
    
        APPEND LINES OF lt_components TO mt_components.
        APPEND LINES OF lt_sources TO mt_sources.
    
        READ TABLE lt_definition INDEX 1 INTO rs_controller-definition.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'WDYC, definition not found' ).
        ENDIF.
    
        CLEAR: rs_controller-definition-author,
               rs_controller-definition-createdon,
               rs_controller-definition-changedby,
               rs_controller-definition-changedon.
    
      ENDMETHOD.
    
      METHOD read_definition.
    
        DATA: lt_definition TYPE TABLE OF wdy_component,
              lt_psmodilog  TYPE TABLE OF smodilog,
              lt_psmodisrc  TYPE TABLE OF smodisrc.
    
        CALL FUNCTION 'WDYD_GET_OBJECT'
          EXPORTING
            component_key           = is_key
            get_all_translations    = abap_false
          TABLES
            definition              = lt_definition
            descriptions            = rs_definition-descriptions
            component_usages        = rs_definition-component_usages
            interface_implementings = rs_definition-interface_implementings
            library_usages          = rs_definition-library_usages
            ext_ctlr_usages         = rs_definition-ext_ctlr_usages
            ext_ctx_mappings        = rs_definition-ext_ctx_mappings
            psmodilog               = lt_psmodilog " not optional in all versions
            psmodisrc               = lt_psmodisrc " not optional in all versions
          EXCEPTIONS
            not_existing            = 1
            OTHERS                  = 2.
        IF sy-subrc = 1.
          RETURN.
        ELSEIF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error from WDYD_GET_OBJECT' ).
        ENDIF.
    
        READ TABLE lt_definition INDEX 1 INTO rs_definition-definition.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'WDYD, definition not found' ).
        ENDIF.
    
        CLEAR: rs_definition-definition-author,
               rs_definition-definition-createdon,
               rs_definition-definition-changedby,
               rs_definition-definition-changedon,
               rs_definition-definition-gendate,
               rs_definition-definition-gentime.
    
      ENDMETHOD.
    
      METHOD read_view.
    
        DATA: lt_definition TYPE TABLE OF wdy_view_vrs,
              lt_psmodilog  TYPE TABLE OF smodilog,
              lt_psmodisrc  TYPE TABLE OF smodisrc.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_definition.
    
        CALL FUNCTION 'WDYV_GET_OBJECT'
          EXPORTING
            view_key               = is_key
            get_all_translations   = abap_false
          TABLES
            definition             = lt_definition
            descriptions           = rs_view-descriptions
            view_containers        = rs_view-view_containers
            view_container_texts   = rs_view-view_container_texts
            iobound_plugs          = rs_view-iobound_plugs
            iobound_plug_texts     = rs_view-iobound_plug_texts
            plug_parameters        = rs_view-plug_parameters
            plug_parameter_texts   = rs_view-plug_parameter_texts
            ui_elements            = rs_view-ui_elements
            ui_context_bindings    = rs_view-ui_context_bindings
            ui_event_bindings      = rs_view-ui_event_bindings
            ui_ddic_bindings       = rs_view-ui_ddic_bindings
            ui_properties          = rs_view-ui_properties
            navigation_links       = rs_view-navigation_links
            navigation_target_refs = rs_view-navigation_target_refs
            vsh_nodes              = rs_view-vsh_nodes
            vsh_placeholders       = rs_view-vsh_placeholders
            viewset_properties     = rs_view-viewset_properties
            psmodilog              = lt_psmodilog
            psmodisrc              = lt_psmodisrc
          EXCEPTIONS
            not_existing           = 1
            OTHERS                 = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error from WDYV_GET_OBJECT' ).
        ENDIF.
    
        READ TABLE lt_definition INDEX 1 ASSIGNING .
        ASSERT sy-subrc = 0.
        MOVE-CORRESPONDING  TO rs_view-definition.
    
        CLEAR: rs_view-definition-author,
               rs_view-definition-createdon,
               rs_view-definition-changedby,
               rs_view-definition-changedon.
    
      ENDMETHOD.
    
      METHOD recover_controller.
    
        DATA: ls_key    TYPE wdy_controller_key,
              lv_corrnr TYPE trkorr,
              lx_error  TYPE REF TO cx_wdy_md_exception,
              ls_delta  TYPE svrs2_xversionable_object.
    
        ls_delta = delta_controller( is_controller ).
        ls_key-component_name  = is_controller-definition-component_name.
        ls_key-controller_name = is_controller-definition-controller_name.
    
        TRY.
            cl_wdy_md_controller=>recover_version(
              EXPORTING
                controller_key = ls_key
                delta          = ls_delta-wdyc
              CHANGING
                corrnr         = lv_corrnr ).
          CATCH cx_wdy_md_exception INTO lx_error.
            zcx_abapgit_exception=>raise( |Error recovering version of controller: { lx_error->get_text( ) }| ).
        ENDTRY.
    
        unlock_controller( ls_key ).
    
      ENDMETHOD.
    
      METHOD recover_definition.
    
        DATA: ls_key    TYPE wdy_md_component_key,
              lv_corrnr TYPE trkorr,
              lx_error  TYPE REF TO cx_wdy_md_exception,
              ls_delta  TYPE svrs2_xversionable_object.
    
        ls_delta = delta_definition(
          is_definition = is_definition
          iv_package    = iv_package ).
    
        ls_key-component_name = is_definition-definition-component_name.
    
        TRY.
            cl_wdy_md_component=>recover_version(
              EXPORTING
                component_key = ls_key
                delta         = ls_delta-wdyd
              CHANGING
                corrnr        = lv_corrnr ).
          CATCH cx_wdy_md_exception INTO lx_error.
            zcx_abapgit_exception=>raise( |Error recovering version of component: { lx_error->get_text( ) }| ).
        ENDTRY.
    
        unlock_definition( ls_key ).
    
      ENDMETHOD.
    
      METHOD recover_view.
    
        DATA: ls_key    TYPE wdy_md_view_key,
              lv_corrnr TYPE trkorr,
              lx_error  TYPE REF TO cx_wdy_md_exception,
              ls_delta  TYPE svrs2_xversionable_object.
    
        ls_delta = delta_view( is_view ).
        ls_key-component_name = is_view-definition-component_name.
        ls_key-view_name      = is_view-definition-view_name.
    
        TRY.
            cl_wdy_md_abstract_view=>recover_version(
              EXPORTING
                view_key = ls_key
                delta    = ls_delta-wdyv
              CHANGING
                corrnr   = lv_corrnr ).
          CATCH cx_wdy_md_exception INTO lx_error.
            zcx_abapgit_exception=>raise( |Error recovering version of abstract view: { lx_error->get_text( ) }| ).
        ENDTRY.
    
        unlock_view( ls_key ).
    
      ENDMETHOD.
    
      METHOD unlock_controller.
    
        DATA lo_controller TYPE REF TO cl_wdy_md_controller.
    
        TRY.
            lo_controller ?= cl_wdy_md_controller=>get_object_by_key(
              component_name  = is_controller_key-component_name
              controller_name = is_controller_key-controller_name ).
            lo_controller->if_wdy_md_lockable_object~unlock( ).
          CATCH cx_wdy_md_permission_failure cx_wdy_md_not_existing ##NO_HANDLER.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD unlock_definition.
    
        DATA: lo_component     TYPE REF TO cl_wdy_md_component,
              lo_comp_intf_def TYPE REF TO cl_wdy_md_component_intf_def.
    
        TRY.
            lo_component ?= cl_wdy_md_component=>get_object_by_key( name = is_component_key-component_name ).
            lo_component->if_wdy_md_component~unlock( ).
          CATCH cx_wdy_md_not_existing.
            TRY.
                lo_comp_intf_def ?= cl_wdy_md_component_intf_def=>get_object_by_key( name = is_component_key-component_name ).
                lo_comp_intf_def->if_wdy_md_component_intf_def~unlock( ).
              CATCH cx_wdy_md_permission_failure cx_wdy_md_not_existing ##NO_HANDLER.
            ENDTRY.
          CATCH cx_wdy_md_permission_failure ##NO_HANDLER.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD unlock_view.
    
        DATA lo_view TYPE REF TO cl_wdy_md_abstract_view.
    
        TRY.
            lo_view ?= cl_wdy_md_abstract_view=>get_object_by_key(
              component_name = is_view_key-component_name
              view_name      = is_view_key-view_name ).
            lo_view->if_wdy_md_lockable_object~unlock( ).
          CATCH cx_wdy_md_permission_failure cx_wdy_md_not_existing ##NO_HANDLER.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD serialize_sources.
    
        DATA:
          lv_extra TYPE string,
          lt_extra TYPE string_table,
          ls_abap  TYPE abaptxt255,
          lt_abap  TYPE abaptxt255_tab.
    
        FIELD-SYMBOLS  LIKE LINE OF mt_sources.
    
        " Store code as separate ABAP files instead of XML (assumes sorted data, see "read")
        LOOP AT mt_sources ASSIGNING .
          AT NEW controller_name.
            CLEAR lt_abap.
            lv_extra = to_lower( -controller_name ).
          ENDAT.
    
          ls_abap-line = -source_line.
          INSERT ls_abap INTO TABLE lt_abap.
    
          AT END OF cmpname.
            CLEAR ls_abap.
            INSERT ls_abap INTO TABLE lt_abap.
          ENDAT.
          AT END OF controller_name.
            IF lt_abap IS NOT INITIAL.
              mo_files->add_abap(
                iv_extra = lv_extra
                it_abap  = lt_abap ).
              INSERT lv_extra INTO TABLE lt_extra.
            ENDIF.
          ENDAT.
        ENDLOOP.
    
        ii_xml->add(
          iv_name = 'FILES'
          ig_data = lt_extra ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        SELECT SINGLE changedby FROM wdy_component INTO rv_user
          WHERE component_name = ms_item-obj_name AND version = 'A'.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lo_component   TYPE REF TO cl_wdy_wb_component,
              lo_request     TYPE REF TO cl_wb_request,
              li_state       TYPE REF TO if_wb_program_state,
              lv_object_name TYPE seu_objkey.
    
        CREATE OBJECT lo_component.
    
        lv_object_name = ms_item-obj_name.
        CREATE OBJECT lo_request
          EXPORTING
            p_object_type = 'YC'
            p_object_name = lv_object_name
            p_operation   = swbm_c_op_delete_no_dialog.
    
        lo_component->if_wb_program~process_wb_request(
          p_wb_request       = lo_request
          p_wb_program_state = li_state ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_component   TYPE wdy_component_metadata,
              ls_description TYPE wdy_ext_ctx_map.
    
        FIELD-SYMBOLS:        LIKE LINE OF ls_component-view_metadata,
                        LIKE LINE OF ls_component-ctlr_metadata.
    
        io_xml->read( EXPORTING iv_name = 'COMPONENT'
                      CHANGING cg_data = ls_component ).
        io_xml->read( EXPORTING iv_name  = 'COMPONENTS'
                      CHANGING cg_data = mt_components ).
    
        deserialize_sources( io_xml ).
    
        ls_component-comp_metadata-definition-author = sy-uname.
        ls_component-comp_metadata-definition-createdon = sy-datum.
        recover_definition( is_definition = ls_component-comp_metadata
                            iv_package    = iv_package ).
    
        LOOP AT ls_component-ctlr_metadata ASSIGNING .
          -definition-author = sy-uname.
          -definition-createdon = sy-datum.
          recover_controller(  ).
        ENDLOOP.
        LOOP AT ls_component-view_metadata ASSIGNING .
          -definition-author = sy-uname.
          -definition-createdon = sy-datum.
          recover_view(  ).
        ENDLOOP.
    
        READ TABLE ls_component-comp_metadata-descriptions INTO ls_description INDEX 1.
        IF sy-subrc = 0.
          zcl_abapgit_sotr_handler=>create_sotr(
            iv_package = iv_package
            io_xml     = io_xml ).
        ENDIF.
    
        add_with_inactive_parts( ).
    
        deserialize_longtexts(
          ii_xml         = io_xml
          iv_longtext_id = c_longtext_id_wd ).
    
        deserialize_longtexts(
          ii_xml           = io_xml
          iv_longtext_id   = c_longtext_id_wc
          iv_longtext_name = c_longtext_name_wc ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_component_name TYPE wdy_component-component_name.
    
        SELECT SINGLE component_name FROM wdy_component
          INTO lv_component_name
          WHERE component_name = ms_item-obj_name.          "#EC CI_GENBUFF
        rv_bool = boolc( sy-subrc = 0 ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_component   TYPE wdy_component_metadata,
              ls_comp        TYPE wdy_ctlr_compo_vrs,
              lv_object      TYPE dokil-object,
              lt_object      TYPE STANDARD TABLE OF dokil-object WITH DEFAULT KEY,
              lt_dokil       TYPE STANDARD TABLE OF dokil WITH DEFAULT KEY,
              ls_description TYPE wdy_ext_ctx_map.
    
        ls_component = read( ).
    
        io_xml->add( iv_name = 'COMPONENT'
                     ig_data = ls_component ).
        io_xml->add( ig_data = mt_components
                     iv_name = 'COMPONENTS' ).
    
        serialize_sources( io_xml ).
    
        READ TABLE ls_component-comp_metadata-descriptions INTO ls_description INDEX 1.
        IF sy-subrc = 0.
          zcl_abapgit_sotr_handler=>read_sotr(
            iv_pgmid    = 'LIMU'
            iv_object   = 'WDYV'
            iv_obj_name = ms_item-obj_name
            io_i18n_params = mo_i18n_params
            io_xml      = io_xml ).
        ENDIF.
    
        serialize_longtexts(
          ii_xml         = io_xml
          iv_longtext_id = c_longtext_id_wd ).
    
        LOOP AT mt_components INTO ls_comp.
          lv_object    = ls_comp-component_name.
          lv_object+30 = ls_comp-controller_name.
          COLLECT lv_object INTO lt_object.
        ENDLOOP.
    
        IF lt_object IS NOT INITIAL.
          IF mo_i18n_params->ms_params-main_language_only = abap_true.
            SELECT * FROM dokil INTO TABLE lt_dokil
              FOR ALL ENTRIES IN lt_object
              WHERE id = c_longtext_id_wc AND object = lt_object-table_line AND masterlang = abap_true
              ORDER BY PRIMARY KEY.
          ELSE.
            SELECT * FROM dokil INTO TABLE lt_dokil
              FOR ALL ENTRIES IN lt_object
              WHERE id = c_longtext_id_wc AND object = lt_object-table_line
              ORDER BY PRIMARY KEY.
          ENDIF.
    
          serialize_longtexts(
            ii_xml           = io_xml
            it_dokil         = lt_dokil
            iv_longtext_id   = c_longtext_id_wc
            iv_longtext_name = c_longtext_name_wc ).
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_OBJECT_WEBI IMPLEMENTATION.
    
      METHOD handle_endpoint.
    
        DATA: ls_endpoint LIKE LINE OF is_webi-pvependpoint,
              li_endpoint TYPE REF TO if_ws_md_vif_endpoint_ref.
    
        FIELD-SYMBOLS:  LIKE LINE OF is_webi-pvepfunction.
    
        READ TABLE is_webi-pvependpoint INDEX 1 INTO ls_endpoint.
        ASSERT sy-subrc = 0.
    
        IF mi_vi->has_endpoint_reference( sews_c_vif_version-all ) = abap_true.
          RETURN.
        ENDIF.
    
        li_endpoint = mi_vi->create_endpoint_reference(
          endpoint_type          = ls_endpoint-endpointtype
          service_def_startpoint = ls_endpoint-def_start_pt
          auto_generated         = ls_endpoint-auto_generated
          i_is_srvv              = ls_endpoint-is_srvv ).
    
        IF ls_endpoint-endpointtype = 'BAPI'.
    * it looks like some special handling is needed when calling
    * set_data, and looking at the cluster data LS_ENDPOINT-CLUSTD
          zcx_abapgit_exception=>raise( 'todo, WEBI BAPI' ).
        ENDIF.
    
    * field ls_endpoint-endpointname does not exist in 702
        READ TABLE is_webi-pvepfunction INDEX 1 ASSIGNING .
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |WEBI { ms_item-obj_name }: couldn't detect endpoint name| ).
        ENDIF.
    
        li_endpoint->set_data(
          data_version = '1'
          data         = -function ).
    
      ENDMETHOD.
    
      METHOD handle_function.
    
        DATA: li_parameter TYPE REF TO if_ws_md_vif_param,
              li_soap      TYPE REF TO if_ws_md_soap_ext_func,
              li_fault     TYPE REF TO if_ws_md_vif_fault,
              li_function  TYPE REF TO if_ws_md_vif_func.
    
        FIELD-SYMBOLS:   LIKE LINE OF is_webi-pvepfunction,
                             LIKE LINE OF is_webi-pvepfuncsoapext,
                            LIKE LINE OF is_webi-pvepfault,
                        LIKE LINE OF is_webi-pvepparameter.
    
        LOOP AT is_webi-pvepfunction ASSIGNING .
    
          IF mi_vi->has_function( funcname = -function
                                  version  = sews_c_vif_version-active ) = abap_true.
            CONTINUE.
          ENDIF.
    
          IF mi_vi->has_function( funcname = -function
                                  version  = sews_c_vif_version-inactive ) = abap_true.
    
            li_function = mi_vi->get_function( funcname = -function
                                               version  = sews_c_vif_version-inactive ).
    
          ELSE.
    
            li_function = mi_vi->create_function( funcname    = -function
                                                  mapped_name = -mappedname ).
    
          ENDIF.
    
          li_function->set_is_exposed( -is_exposed ).
    
          LOOP AT is_webi-pvepparameter ASSIGNING 
              WHERE function = -function.
    
            li_parameter = handle_single_parameter( iv_name           = -vepparam
                                                    ii_function       = li_function
                                                    iv_parameter_type = -vepparamtype ).
    
            li_parameter->set_name_mapped_to( -mappedname ).
            li_parameter->set_is_exposed( -is_exposed ).
            li_parameter->set_is_optional( -is_optional ).
            li_parameter->set_default_value( -default_value ).
            li_parameter->set_initial( -is_initial ).
            li_parameter->set_type( -typename ).
          ENDLOOP.
    
          LOOP AT is_webi-pvepfuncsoapext ASSIGNING 
              WHERE function = -function.
            IF li_function->has_soap_extension_function( 'I' ) = abap_true.
              li_function->delete_soap_extension_function( ).
            ENDIF.
            li_soap = li_function->create_soap_extension_function( ).
            li_soap->set_soap_request_name( -requestname ).
            li_soap->set_soap_response_name( -responsename ).
            li_soap->set_namespace( -namespace ).
          ENDLOOP.
    
          LOOP AT is_webi-pvepfault ASSIGNING 
              WHERE function = -function.
            li_fault = li_function->create_fault( -fault ).
            li_fault->set_name_mapped_to( -mappedname ).
            li_fault->set_detail( -detail ).
          ENDLOOP.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD handle_single_parameter.
        CONSTANTS:
          BEGIN OF lc_parameter_type,
            import TYPE vepparamtype VALUE 'I',
            export TYPE vepparamtype VALUE 'O',
          END OF lc_parameter_type.
    
        CASE iv_parameter_type.
          WHEN lc_parameter_type-import.
            ri_parameter = ii_function->get_incoming_parameter( parameter_name  = iv_name
                                                                version         = 'I' ).
            IF ri_parameter IS BOUND.
              ii_function->delete_incoming_parameter( ri_parameter ).
            ENDIF.
            ri_parameter = ii_function->create_incoming_parameter( iv_name ).
    
          WHEN lc_parameter_type-export.
    
            ri_parameter = ii_function->get_outgoing_parameter( parameter_name  = iv_name
                                                                version         = 'I' ).
            IF ri_parameter IS BOUND.
              ii_function->delete_outgoing_parameter( parameter = ri_parameter ).
            ENDIF.
    
            ri_parameter = ii_function->create_outgoing_parameter( iv_name ).
    
          WHEN OTHERS.
            ASSERT 0 = 1.
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD handle_soap.
    
        DATA: li_soap TYPE REF TO if_ws_md_soap_ext_virtinfc,
              ls_soap LIKE LINE OF is_webi-pvepvisoapext.
    
        READ TABLE is_webi-pvepvisoapext INDEX 1 INTO ls_soap.
        ASSERT sy-subrc = 0.
    
        IF mi_vi->has_soap_extension_virtinfc( sews_c_vif_version-active ) = abap_true.
          RETURN.
        ENDIF.
    
        IF mi_vi->has_soap_extension_virtinfc( sews_c_vif_version-inactive ) = abap_true.
          li_soap = mi_vi->get_soap_extension_virtinfc( sews_c_vif_version-inactive ).
        ELSE.
          li_soap = mi_vi->create_soap_extension_virtinfc( ls_soap-soap_appl_uri ).
        ENDIF.
    
        li_soap->set_namespace( ls_soap-namespace ).
    
      ENDMETHOD.
    
      METHOD handle_types.
    
        DATA: lv_index TYPE i,
              li_soap  TYPE REF TO if_ws_md_soap_extension_type,
              li_struc TYPE REF TO if_ws_md_vif_struc_type,
              li_field TYPE REF TO if_ws_md_vif_field,
              li_table TYPE REF TO if_ws_md_vif_table_type,
              li_elem  TYPE REF TO if_ws_md_vif_elem_type.
    
        FIELD-SYMBOLS:   LIKE LINE OF is_webi-pvepelemtype,
                        LIKE LINE OF is_webi-pveptabletype,
                         LIKE LINE OF is_webi-pveptypesoapext,
                        LIKE LINE OF is_webi-pvepstrutype.
    
        LOOP AT is_webi-pvepelemtype ASSIGNING .
          li_elem = mi_vi->create_type_as_elementary( -typename ).
          li_elem->set_built_in_type( -build_in_type ).
          li_elem->set_decimals( -decimals ).
          li_elem->set_kind( -kind ).
          li_elem->set_length( -length ).
          li_elem->set_signed( -signed ).
          li_elem->set_abaptype( -abaptype ).
    
          IF li_elem->if_ws_md_vif_type~has_soap_extension_type( sews_c_vif_version-all ) = abap_false.
            READ TABLE is_webi-pveptypesoapext ASSIGNING 
              WITH KEY typename = -typename.
            IF sy-subrc = 0.
              li_soap = li_elem->if_ws_md_vif_type~create_soap_extension_type( ).
              li_soap->set_namespace( -namespace ).
            ENDIF.
          ENDIF.
        ENDLOOP.
    
        LOOP AT is_webi-pvepstrutype ASSIGNING .
          lv_index = sy-tabix.
    
          li_struc = mi_vi->create_type_as_structure( -typename ).
    
          IF li_struc->has_field( field_pos = -fieldpos
              version = sews_c_vif_version-active ) = abap_true.
            CONTINUE.
          ENDIF.
    
          li_field = li_struc->create_field(
            field_name = -fieldname
            fieldpos = -fieldpos ).
          li_field->set_type( mi_vi->get_type( typename = -typeref
                                               version  = sews_c_vif_version-inactive ) ).
    
          IF lv_index = 1
              AND li_struc->if_ws_md_vif_type~has_soap_extension_type(
              sews_c_vif_version-all ) = abap_false.
            READ TABLE is_webi-pveptypesoapext ASSIGNING 
              WITH KEY typename = -typename.
            IF sy-subrc = 0.
              li_soap = li_struc->if_ws_md_vif_type~create_soap_extension_type( ).
              li_soap->set_namespace( -namespace ).
            ENDIF.
          ENDIF.
        ENDLOOP.
    
        LOOP AT is_webi-pveptabletype ASSIGNING .
          li_table = mi_vi->create_type_as_table( -typename ).
          li_table->set_line_type( mi_vi->get_type( typename = -typeref
                                                    version  = sews_c_vif_version-inactive ) ).
    
          IF li_table->if_ws_md_vif_type~has_soap_extension_type( sews_c_vif_version-all ) = abap_false.
            READ TABLE is_webi-pveptypesoapext ASSIGNING 
              WITH KEY typename = -typename.
            IF sy-subrc = 0.
              li_soap = li_table->if_ws_md_vif_type~create_soap_extension_type( ).
              li_soap->set_namespace( -namespace ).
            ENDIF.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD sort.
        SORT cs_webi-pvepheader BY vepname version.
        SORT cs_webi-pvepfunction BY vepname version function.
        SORT cs_webi-pvepfault BY vepname version function fault.
        SORT cs_webi-pvepparameter BY vepname version function vepparam vepparamtype.
        SORT cs_webi-pveptype BY vepname version typename.
        SORT cs_webi-pvepelemtype BY vepname version typename.
        SORT cs_webi-pveptabletype BY vepname version typename.
        SORT cs_webi-pvepstrutype BY vepname version typename fieldpos.
        SORT cs_webi-pveptypesoapext BY vepname version typename.
        SORT cs_webi-pvepeletypsoap BY vepname version typename assign_type assign_data1 assign_data2.
        SORT cs_webi-pveptabtypsoap BY vepname version typename.
        SORT cs_webi-pvepfuncsoapext BY vepname version function.
        SORT cs_webi-pvepfieldref BY vepname version function vepparam vepparamtype strucid fieldname.
        SORT cs_webi-pvependpoint BY relid vepname version sortfield.
        SORT cs_webi-pvepvisoapext BY vepname version.
        SORT cs_webi-pvepparasoapext BY vepname version function vepparam vepparamtype.
        SORT cs_webi-pwsheader BY wsname version.
        SORT cs_webi-pwssoapprop BY wsname version feature soapapp funcref propnum.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        SELECT SINGLE changedby FROM vepheader INTO rv_user
          WHERE vepname = ms_item-obj_name AND version = 'A'.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lv_name TYPE vepname,
              lo_vif  TYPE REF TO cl_ws_md_vif_root.
    
        lv_name = ms_item-obj_name.
    
        CREATE OBJECT lo_vif.
        TRY.
            lo_vif->if_ws_md_vif_root~delete_virtual_interface( lv_name ).
          CATCH cx_ws_md_exception.
            zcx_abapgit_exception=>raise( 'error deleting WEBI' ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_webi     TYPE ty_webi,
              lv_name     TYPE vepname,
              ls_header   LIKE LINE OF ls_webi-pvepheader,
              lx_root     TYPE REF TO cx_root,
              lv_exists   TYPE abap_bool,
              li_root     TYPE REF TO if_ws_md_vif_root,
              ls_endpoint LIKE LINE OF ls_webi-pvependpoint.
    
        io_xml->read( EXPORTING iv_name = 'WEBI'
                      CHANGING  cg_data = ls_webi ).
    
        lv_name = ms_item-obj_name.
    
        READ TABLE ls_webi-pvependpoint INDEX 1 INTO ls_endpoint.
        ASSERT sy-subrc = 0.
        IF ls_endpoint-auto_generated = abap_true.
          " handled by SPRX.
          RETURN.
        ENDIF.
    
        READ TABLE ls_webi-pvepheader INDEX 1 INTO ls_header.
        ASSERT sy-subrc = 0.
    
        lv_exists = cl_ws_md_vif_root=>check_existence_by_vif_name(
          name      = lv_name
          i_version = sews_c_vif_version-all ).
    
        li_root = cl_ws_md_factory=>get_vif_root( ).
        TRY.
            IF lv_exists = abap_false.
              mi_vi = li_root->create_virtual_interface(
                name    = lv_name
                nameext = ls_header-vepnameext ).
            ELSE.
              mi_vi = li_root->get_virtual_interface( lv_name ).
              mi_vi->if_ws_md_lockable_object~lock( ).
            ENDIF.
    
            mi_vi->set_short_text( ls_webi-veptext ).
    
            handle_endpoint( ls_webi ).
            handle_types( ls_webi ).
            handle_function( ls_webi ).
            handle_soap( ls_webi ).
    
            tadir_insert( iv_package ).
    
            mi_vi->if_ws_md_lockable_object~save( ).
            mi_vi->if_ws_md_lockable_object~unlock( ).
          CATCH cx_ws_md_exception INTO lx_root.
            TRY.
                mi_vi->if_ws_md_lockable_object~unlock( ).
              CATCH cx_ws_md_exception ##NO_HANDLER.
            ENDTRY.
            zcx_abapgit_exception=>raise_with_text( lx_root ).
        ENDTRY.
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
        zcl_abapgit_sotr_handler=>create_sotr(
          iv_package = iv_package
          io_xml     = io_xml ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_name TYPE vepname.
        DATA lv_generated TYPE abap_bool.
    
        lv_name = ms_item-obj_name.
    
        " Check if service is generated by proxy
        SELECT SINGLE auto_generated FROM vependpoint INTO lv_generated
          WHERE vepname = lv_name AND version = sews_c_vif_version-active.
        IF sy-subrc = 0 AND lv_generated = abap_true.
          RETURN.
        ENDIF.
    
        rv_bool = cl_ws_md_vif_root=>check_existence_by_vif_name(
          name      = lv_name
          i_version = sews_c_vif_version-all ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_webi    TYPE ty_webi,
              lx_error   TYPE REF TO cx_ws_md_exception,
              lt_modilog TYPE STANDARD TABLE OF smodilog WITH DEFAULT KEY,
              li_vi      TYPE REF TO if_ws_md_vif,
              lv_name    TYPE vepname.
    
        FIELD-SYMBOLS:    LIKE LINE OF ls_webi-pvepheader,
                        LIKE LINE OF ls_webi-pvependpoint,
                           TYPE wsheader.
    
        CALL FUNCTION 'WEBI_GET_OBJECT'
          EXPORTING
            webiname          = ms_item-obj_name
          TABLES
            psmodilog         = lt_modilog
            pvepheader        = ls_webi-pvepheader
            pvepfunction      = ls_webi-pvepfunction
            pvepfault         = ls_webi-pvepfault
            pvepparameter     = ls_webi-pvepparameter
            pveptype          = ls_webi-pveptype
            pvepelemtype      = ls_webi-pvepelemtype
            pveptabletype     = ls_webi-pveptabletype
            pvepstrutype      = ls_webi-pvepstrutype
            pveptypesoapext   = ls_webi-pveptypesoapext
            pvepeletypsoap    = ls_webi-pvepeletypsoap
            pveptabtypsoap    = ls_webi-pveptabtypsoap
            pvepfuncsoapext   = ls_webi-pvepfuncsoapext
            pvepfieldref      = ls_webi-pvepfieldref
            pvependpoint      = ls_webi-pvependpoint
            pvepvisoapext     = ls_webi-pvepvisoapext
            pvepparasoapext   = ls_webi-pvepparasoapext
            pwsheader         = ls_webi-pwsheader
            pwssoapprop       = ls_webi-pwssoapprop
          EXCEPTIONS
            version_not_found = 1
            webi_not_exist    = 2
            OTHERS            = 3.
        IF sy-subrc = 1.
          " no active version
          RETURN.
        ELSEIF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        sort( CHANGING cs_webi = ls_webi ).
    
        lv_name = ms_item-obj_name.
        TRY.
            li_vi = cl_ws_md_factory=>get_vif_root( )->get_virtual_interface( lv_name ).
            ls_webi-veptext = li_vi->get_short_text( sews_c_vif_version-active ).
          CATCH cx_ws_md_exception INTO lx_error.
            zcx_abapgit_exception=>raise_with_text( lx_error ).
        ENDTRY.
    
        LOOP AT ls_webi-pvepheader ASSIGNING .
          CLEAR -author.
          CLEAR -createdon.
          CLEAR -changedby.
          CLEAR -changedon.
          CLEAR -ctime.
          CLEAR -text_id.
          CLEAR -utime.
          CLEAR -wsint_version.
        ENDLOOP.
    
        LOOP AT ls_webi-pvependpoint ASSIGNING .
          CLEAR -clustd.
        ENDLOOP.
    
        LOOP AT ls_webi-pwsheader ASSIGNING .
    
          CLEAR:
            -author,
            -createdon,
            -changedby,
            -changedon,
            -ctime,
            -utime.
    
        ENDLOOP.
    
        io_xml->add( iv_name = 'WEBI'
                     ig_data = ls_webi ).
    
        zcl_abapgit_sotr_handler=>read_sotr(
          iv_pgmid    = 'R3TR'
          iv_object   = ms_item-obj_type
          iv_obj_name = ms_item-obj_name
          io_i18n_params = mo_i18n_params
          io_xml      = io_xml ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_xinx IMPLEMENTATION.
    
      METHOD constructor.
    
        super->constructor(
          is_item        = is_item
          iv_language    = iv_language
          io_files       = io_files
          io_i18n_params = io_i18n_params ).
    
        cl_wb_object_type=>get_key_components_from_id(
          EXPORTING
            p_key                   = |{ ms_item-obj_name }|
            p_external_id           = swbm_c_type_ddic_db_tabxinx
          IMPORTING
            p_key_component1        = mv_name
            p_key_component2        = mv_id
          EXCEPTIONS
            too_many_key_components = 1
            objecttype_not_existing = 2
            OTHERS                  = 3 ).
    
        ASSERT sy-subrc = 0.
    
      ENDMETHOD.
    
      METHOD xinx_delete_docu.
    
        DATA: lv_docuid  TYPE dokhl-id,
              lv_doctype TYPE dokhl-typ,
              lv_docname TYPE dokhl-object.
    
        lv_docname    = iv_objname.
        lv_docname+30 = iv_id.
        CALL FUNCTION 'INTERN_DD_DOCU_ID_MATCH'
          EXPORTING
            p_trobjtype  = c_objtype_extension_index
          IMPORTING
            p_docu_id    = lv_docuid
            p_doctype    = lv_doctype
          EXCEPTIONS
            illegal_type = 1
            OTHERS       = 2.
    
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        CALL FUNCTION 'DOKU_DELETE_ALL'
          EXPORTING
            doku_id            = lv_docuid
            doku_object        = lv_docname
            doku_typ           = lv_doctype
            suppress_authority = 'X'
            suppress_enqueue   = 'X'
            suppress_transport = 'X'
          EXCEPTIONS
            no_docu_found      = 1
            OTHERS             = 2 ##FM_SUBRC_OK.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
        SELECT SINGLE as4user FROM dd12l INTO rv_user
          WHERE sqltab = mv_name AND indexname = mv_id.
        IF sy-subrc <> 0.
          rv_user = c_user_unknown.
        ENDIF.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        " Reimplement FM RS_DD_INDX_DELETE as it calls the UI
    
        DATA: ls_enqueue      TYPE ddenqs,
              lv_protname     TYPE tstrf01-file,
              lv_del_concname LIKE ls_enqueue-objname,
              lv_concname     TYPE rsdxx-objname,
              ls_transp_key   TYPE trkey,
              ls_e071         TYPE e071,
              lv_clm_corrnum  TYPE e070-trkorr.
    
        CONCATENATE mv_name '-' mv_id INTO lv_concname.
        ls_enqueue-objtype = c_objtype_extension_index.
    
        CALL FUNCTION 'INT_INDX_DEL_LOCK'
          EXPORTING
            i_trobjtype        = ls_enqueue-objtype
            i_tabname          = mv_name
            i_indexname        = mv_id
          EXCEPTIONS
            not_executed       = 1
            error_occured      = 2
            permission_failure = 3
            OTHERS             = 4.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        ls_enqueue-objname = mv_name.
        ls_enqueue-secname = mv_id.
        CALL FUNCTION 'RS_CORR_INSERT'
          EXPORTING
            object        = ls_enqueue
            object_class  = 'DICT'
            mode          = 'DELETE'
          IMPORTING
            transport_key = ls_transp_key
          EXCEPTIONS
            OTHERS        = 1.
    
        IF sy-subrc <> 0.
          " & was not deleted (correction entry not possible or canceled)
          MESSAGE s015(e2) WITH lv_concname INTO zcx_abapgit_exception=>null.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CALL FUNCTION 'DD_LOGNPROT_NAME_GET'
          EXPORTING
            task        = 'DEL'
            obj_type    = ls_enqueue-objtype
            obj_name    = ls_enqueue-objname
            ind_name    = ls_enqueue-secname
          IMPORTING
            protname    = lv_protname
          EXCEPTIONS
            input_error = 0.
    
        PERFORM logdelete IN PROGRAM rddu0001 USING lv_protname.
    
        lv_del_concname = ls_enqueue-objname.
        lv_del_concname+16 = ls_enqueue-secname.
    
        CALL FUNCTION 'DD_OBJ_DEL'
          EXPORTING
            object_name = lv_del_concname
            object_type = ls_enqueue-objtype
            del_state   = 'M'
          EXCEPTIONS
            OTHERS      = 1.
    
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        CALL FUNCTION 'DD_DD_TO_E071'
          EXPORTING
            type          = ls_enqueue-objtype
            name          = ls_enqueue-objname
            id            = ls_enqueue-secname
          IMPORTING
            obj_name      = ls_e071-obj_name
          EXCEPTIONS
            illegal_input = 1
            OTHERS        = 2.
    
        IF sy-subrc <> 0.
          " Internal error & in & (contact person in charge)
          MESSAGE i008(e2) WITH 'DD_DD_TO_E071' 'RS_DD_INDX_DELETE' INTO zcx_abapgit_exception=>null.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        ls_e071-object = ls_enqueue-objtype.
    
        CALL FUNCTION 'RS_DELETE_FROM_WORKING_AREA'
          EXPORTING
            object                 = ls_e071-object
            obj_name               = ls_e071-obj_name
            immediate              = 'X'
            actualize_working_area = 'X'.
    
        xinx_delete_docu(
          iv_objname = mv_name
          iv_id      = mv_id ).
    
        CALL FUNCTION 'RS_TREE_OBJECT_PLACEMENT'
          EXPORTING
            object    = ls_e071-obj_name
            operation = 'DELETE'
            type      = c_objtype_extension_index.
    
        IF mv_id(1) CA 'YZ'.
          CALL FUNCTION 'CLM_INDX_MODIFICATION_DELETE'
            EXPORTING
              idxobj_name   = ls_enqueue-objname
              idx_type      = ls_enqueue-objtype
              idx_name      = mv_id
              transport_key = ls_transp_key
              corrnum       = lv_clm_corrnum.
        ENDIF.
    
        CALL FUNCTION 'RS_DD_DEQUEUE'
          EXPORTING
            objtype = ls_enqueue-objtype
            objname = ls_enqueue-objname
            secname = ls_enqueue-secname.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: ls_extension_index TYPE ty_extension_index,
              lv_rc              TYPE sy-subrc.
    
        FIELD-SYMBOLS  TYPE uccheck.
    
        io_xml->read(
          EXPORTING
            iv_name = 'XINX'
          CHANGING
            cg_data = ls_extension_index ).
    
        ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE ls_extension_index-dd12v TO .
        IF sy-subrc = 0.
          set_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
    
        tadir_insert( iv_package ).
    
        corr_insert( iv_package ).
    
        CALL FUNCTION 'DDIF_INDX_PUT'
          EXPORTING
            name              = mv_name
            id                = mv_id
            dd12v_wa          = ls_extension_index-dd12v
          TABLES
            dd17v_tab         = ls_extension_index-t_dd17v
          EXCEPTIONS
            indx_not_found    = 1
            name_inconsistent = 2
            indx_inconsistent = 3
            put_failure       = 4
            put_refused       = 5
            OTHERS            = 6.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from DDIF_INDX_PUT { sy-subrc }| ).
        ENDIF.
    
        CALL FUNCTION 'DDIF_INDX_ACTIVATE'
          EXPORTING
            name        = mv_name
            id          = mv_id
          IMPORTING
            rc          = lv_rc
          EXCEPTIONS
            not_found   = 1
            put_failure = 2
            OTHERS      = 3.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from DDIF_INDX_ACTIVATE { sy-subrc }| ).
        ENDIF.
    
        IF lv_rc <> 0.
          zcx_abapgit_exception=>raise( |Cannot activate extension index { mv_id } of table { mv_name }| ).
        ENDIF.
    
        deserialize_longtexts( ii_xml         = io_xml
                               iv_longtext_id = c_longtext_id_xinx ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: ls_dd12v TYPE dd12v.
    
        CALL FUNCTION 'DDIF_INDX_GET'
          EXPORTING
            name          = mv_name
            id            = mv_id
          IMPORTING
            dd12v_wa      = ls_dd12v
          EXCEPTIONS
            illegal_input = 1
            OTHERS        = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'Error reading index' ).
        ENDIF.
    
        rv_bool = boolc( ls_dd12v IS NOT INITIAL ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-lxe TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: ls_extension_index TYPE ty_extension_index.
    
        FIELD-SYMBOLS  TYPE uccheck.
    
        CALL FUNCTION 'DDIF_INDX_GET'
          EXPORTING
            name          = mv_name
            id            = mv_id
            langu         = mv_language
          IMPORTING
            dd12v_wa      = ls_extension_index-dd12v
          TABLES
            dd17v_tab     = ls_extension_index-t_dd17v
          EXCEPTIONS
            illegal_input = 1
            OTHERS        = 2.
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from DDIF_INDX_GET { sy-subrc }| ).
        ENDIF.
    
        CLEAR: ls_extension_index-dd12v-as4user,
               ls_extension_index-dd12v-as4date,
               ls_extension_index-dd12v-as4time.
    
        ASSIGN COMPONENT 'ABAP_LANGUAGE_VERSION' OF STRUCTURE ls_extension_index-dd12v TO .
        IF sy-subrc = 0.
          clear_abap_language_version( CHANGING cv_abap_language_version =  ).
        ENDIF.
    
        io_xml->add( iv_name = 'XINX'
                     ig_data = ls_extension_index ).
    
        serialize_longtexts( ii_xml         = io_xml
                             iv_longtext_id = c_longtext_id_xinx ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_object_xslt IMPLEMENTATION.
    
      METHOD get.
    
        DATA: lv_name TYPE cxsltdesc.
    
        lv_name = ms_item-obj_name.
    
        cl_o2_api_xsltdesc=>load(
          EXPORTING
            p_xslt_desc        = lv_name
          IMPORTING
            p_obj              = ro_xslt
          EXCEPTIONS
            not_existing       = 1
            permission_failure = 2
            OTHERS             = 3 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error from cl_o2_api_xsltdesc=>load' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~changed_by.
    
        DATA: lo_xslt       TYPE REF TO cl_o2_api_xsltdesc,
              ls_attributes TYPE o2xsltattr.
    
        lo_xslt = get( ).
        lo_xslt->get_attributes(
          RECEIVING
            p_attributes     = ls_attributes
          EXCEPTIONS
            object_invalid   = 1
            xsltdesc_deleted = 2
            OTHERS           = 3 ).
    
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        rv_user = ls_attributes-changedby.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~delete.
    
        DATA: lo_xslt TYPE REF TO cl_o2_api_xsltdesc,
              lv_name TYPE cxsltdesc.
    
        lv_name = ms_item-obj_name.
    
        cl_o2_api_xsltdesc=>load(
          EXPORTING
            p_xslt_desc        = lv_name
          IMPORTING
            p_obj              = lo_xslt
          EXCEPTIONS
            error_occured      = 1
            not_existing       = 2
            permission_failure = 3
            version_not_found  = 4
            OTHERS             = 5 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( 'error from cl_o2_api_xsltdesc=>load' ).
        ENDIF.
    
        lo_xslt->set_changeable( abap_true ).
        lo_xslt->delete( ).
        lo_xslt->save( ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~deserialize.
    
        DATA: lv_source     TYPE string,
              lo_xslt       TYPE REF TO cl_o2_api_xsltdesc,
              ls_extra      TYPE ty_extra,
              lv_len        TYPE i,
              ls_attributes TYPE o2xsltattr.
    
        " Transformation might depend on other objects like a class
        " We attempt to activate it in late step
        IF iv_step = zif_abapgit_object=>gc_step_id-late.
          IF zif_abapgit_object~is_active( ) = abap_false.
            zcl_abapgit_objects_activation=>add_item( ms_item ).
          ENDIF.
          RETURN.
        ENDIF.
    
        IF zif_abapgit_object~exists( ) = abap_true.
          zif_abapgit_object~delete( iv_package   = iv_package
                                     iv_transport = iv_transport
                                     ii_log       = ii_log ).
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'ATTRIBUTES'
                      CHANGING cg_data = ls_attributes ).
    
        ls_attributes-devclass = iv_package.
    
        lv_source = mo_files->read_string(
          iv_extra = 'source'
          iv_ext   = 'xml' ).
    
        zcl_abapgit_utils=>check_eol( lv_source ).
    
    * workaround: somewhere additional linefeeds are added
        lv_len = strlen( lv_source ) - 2.
        IF lv_source+lv_len(2) = cl_abap_char_utilities=>cr_lf.
          lv_source = lv_source(lv_len).
        ENDIF.
    
        cl_o2_api_xsltdesc=>create_new_from_string(
          EXPORTING
            p_source                = lv_source
            p_attr                  = ls_attributes
          IMPORTING
            p_obj                   = lo_xslt
          EXCEPTIONS
            action_cancelled        = 1
            error_occured           = 2
            not_authorized          = 3
            object_already_existing = 4
            undefined_name          = 5
            OTHERS                  = 6 ).
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise( |Error from XSLT new, { sy-subrc }| ).
        ENDIF.
    
        lo_xslt->save(
          EXCEPTIONS
            action_cancelled      = 1
            error_occured         = 2
            object_invalid        = 3
            object_not_changeable = 4
            permission_failure    = 5
            OTHERS                = 6 ).
        IF sy-subrc <> 0.
          lo_xslt->set_changeable( abap_false ). " unlock
          zcx_abapgit_exception=>raise( |Error from XSLT save, { sy-subrc }| ).
        ENDIF.
    
        io_xml->read( EXPORTING iv_name = 'EXTRA'
                      CHANGING  cg_data = ls_extra ).
    
        TRY.
            set_abap_language_version( CHANGING cv_abap_language_version = ls_extra-abap_language_version ).
    
            UPDATE ('O2XSLTDESC') SET abap_language_version = ls_extra-abap_language_version
              WHERE relid = 'TR' AND xsltdesc = ms_item-obj_name.
          CATCH cx_sy_dynamic_osql_semantics ##NO_HANDLER.
        ENDTRY.
    
        lo_xslt->set_changeable( abap_false ).
    
        zcl_abapgit_objects_activation=>add_item( ms_item ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~exists.
    
        DATA: lv_name TYPE cxsltdesc.
    
        lv_name = ms_item-obj_name.
    
        rv_bool = cl_o2_api_xsltdesc=>exists( lv_name ).
        rv_bool = boolc( rv_bool = '1' ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_comparator.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_order.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_deserialize_steps.
        APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
        APPEND zif_abapgit_object=>gc_step_id-late TO rt_steps.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~get_metadata.
        rs_metadata = get_metadata( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_active.
        rv_active = is_active( ).
      ENDMETHOD.
    
      METHOD zif_abapgit_object~is_locked.
        rv_is_locked = abap_false.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~jump.
        " Covered by /apmg/cl_apm_abapgit_objects=>JUMP
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_filename_to_object.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~map_object_to_filename.
        RETURN.
      ENDMETHOD.
    
      METHOD zif_abapgit_object~serialize.
    
        DATA: lo_xslt       TYPE REF TO cl_o2_api_xsltdesc,
              ls_extra      TYPE ty_extra,
              lv_source     TYPE string,
              ls_attributes TYPE o2xsltattr.
    
        lo_xslt = get( ).
    
        ls_attributes = lo_xslt->get_attributes( ).
    
        CLEAR: ls_attributes-author,
               ls_attributes-createdon,
               ls_attributes-changedby,
               ls_attributes-changedon,
               ls_attributes-devclass.
    
        io_xml->add( iv_name = 'ATTRIBUTES'
                     ig_data = ls_attributes ).
    
        lv_source = lo_xslt->get_source_string( ).
    
        mo_files->add_string(
          iv_extra  = 'source'
          iv_ext    = 'xml'
          iv_string = lv_source ).
    
        TRY.
            SELECT SINGLE abap_language_version FROM ('O2XSLTDESC') INTO CORRESPONDING FIELDS OF ls_extra
              WHERE relid = 'TR' AND xsltdesc = ms_item-obj_name AND state = 'A'.
            IF sy-subrc = 0.
              clear_abap_language_version( CHANGING cv_abap_language_version = ls_extra-abap_language_version ).
            ENDIF.
          CATCH cx_sy_dynamic_osql_semantics ##NO_HANDLER.
        ENDTRY.
    
        io_xml->add( iv_name = 'EXTRA'
                     ig_data = ls_extra ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_OO_FACTORY IMPLEMENTATION.
    
      METHOD get_by_type.
        IF iv_object_type = 'CLAS'.
          CREATE OBJECT ri_object_oriented_object TYPE zcl_abapgit_oo_class.
        ELSEIF iv_object_type = 'INTF'.
          CREATE OBJECT ri_object_oriented_object TYPE zcl_abapgit_oo_interface.
        ENDIF.
      ENDMETHOD.
    
      METHOD get_by_name.
    
        DATA:
          li_interface   TYPE REF TO zif_abapgit_oo_object_fnc,
          li_class       TYPE REF TO zif_abapgit_oo_object_fnc,
          ls_object_name TYPE seoclskey.
    
        ls_object_name-clsname = to_upper( iv_object_name ).
    
        CREATE OBJECT li_class TYPE zcl_abapgit_oo_class.
        IF li_class->exists( ls_object_name-clsname ) = abap_true.
          ri_object_oriented_object = li_class.
          RETURN.
        ENDIF.
    
        CREATE OBJECT li_interface TYPE zcl_abapgit_oo_interface.
        IF li_interface->exists( ls_object_name-clsname ) = abap_true.
          ri_object_oriented_object = li_interface.
          RETURN.
        ENDIF.
    
        zcx_abapgit_exception=>raise( |{ iv_object_name } is neither a class nor an interface| ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_oo_serializer IMPLEMENTATION.
    
      METHOD are_test_classes_skipped.
        rv_return = mv_skip_testclass.
      ENDMETHOD.
    
      METHOD calculate_skip_testclass.
    
        DATA: lv_line1 LIKE LINE OF it_source,
              lv_line2 LIKE LINE OF it_source.
    
    * when creating classes in Eclipse it automatically generates the
    * testclass include, but it is not needed, so skip to avoid
    * creating an extra file in the repository.
    * Also remove it if the content is manually removed, but
    * the class still thinks it contains tests
    
        rv_skip_testclass = abap_false.
        IF lines( it_source ) = 2.
          READ TABLE it_source INDEX 1 INTO lv_line1.
          ASSERT sy-subrc = 0.
          READ TABLE it_source INDEX 2 INTO lv_line2.
          ASSERT sy-subrc = 0.
          IF strlen( lv_line1 ) >= 3 AND lv_line1(3) = '*"*' AND lv_line2 IS INITIAL.
            rv_skip_testclass = abap_true.
          ENDIF.
        ELSEIF lines( it_source ) = 1.
          READ TABLE it_source INDEX 1 INTO lv_line1.
          ASSERT sy-subrc = 0.
          IF lv_line1 IS INITIAL
              OR ( strlen( lv_line1 ) >= 3 AND lv_line1(3) = '*"*' )
              OR ( strlen( lv_line1 ) = 1 AND lv_line1(1) = '*' ).
            rv_skip_testclass = abap_true.
          ENDIF.
        ELSEIF lines( it_source ) = 0.
          rv_skip_testclass = abap_true.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD read_include.
    
        DATA ls_include TYPE progstruc.
        DATA lv_program TYPE syrepid.
        DATA lt_source  TYPE abaptxt255_tab.
    
        ASSERT iv_type = seop_ext_class_locals_def
          OR iv_type = seop_ext_class_locals_imp
          OR iv_type = seop_ext_class_macros
          OR iv_type = seop_ext_class_testclasses.
    
        ls_include-rootname = is_clskey-clsname.
        TRANSLATE ls_include-rootname USING ' ='.
        ls_include-categorya = iv_type(1).
        ls_include-codea = iv_type+1(4).
    
    * it looks like there is an issue in function module SEO_CLASS_GET_INCLUDE_SOURCE
    * on 750 kernels, where the READ REPORT without STATE addition does not
    * return the active version, this method is a workaround for this issue
        lv_program = ls_include.
        TRY.
            lt_source = zcl_abapgit_factory=>get_sap_report( )->read_report( lv_program ).
          CATCH zcx_abapgit_exception ##NO_HANDLER.
    * ignore if the report is not found, sometimes the CCDEF include does not exist
        ENDTRY.
        rt_source = lt_source.
    
      ENDMETHOD.
    
      METHOD reduce.
    
        DATA: lv_source LIKE LINE OF ct_source,
              lv_found  TYPE abap_bool.
    
    * skip files that only contain the standard comments
        lv_found = abap_false.
        LOOP AT ct_source INTO lv_source.
          IF strlen( lv_source ) >= 3 AND lv_source(3) <> '*"*'.
            lv_found = abap_true.
          ENDIF.
        ENDLOOP.
        IF lv_found = abap_false.
          CLEAR ct_source.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD remove_signatures.
    
    * signatures messes up in CL_OO_SOURCE when deserializing and serializing
    * within same session
    
        DATA: lv_begin  TYPE string,
              lv_end    TYPE string,
              lv_remove TYPE abap_bool,
              lv_source LIKE LINE OF ct_source.
    
        "@TODO: Put under test
        CONCATENATE '* ------------------------------------'
          '---------------------------------------------------+'
          INTO lv_begin.
    
        CONCATENATE '* +------------------------------------------------'
          '--------------------------------------'
          INTO lv_end.
    
        lv_remove = abap_false.
        LOOP AT ct_source INTO lv_source.
          IF lv_source = lv_begin.
            lv_remove = abap_true.
          ENDIF.
          IF lv_remove = abap_true.
            DELETE ct_source INDEX sy-tabix.
          ENDIF.
          IF lv_source = lv_end.
            lv_remove = abap_false.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD serialize_abap_clif_source.
        rt_source = zcl_abapgit_exit=>get_instance( )->custom_serialize_abap_clif( is_class_key ).
        IF rt_source IS NOT INITIAL.
          RETURN.
        ENDIF.
    
        TRY.
            rt_source = serialize_abap_new( is_class_key ).
          CATCH cx_sy_dyn_call_error.
            rt_source = serialize_abap_old( is_class_key ).
        ENDTRY.
    
        " Call exit again for optional post-processing
        rt_source = zcl_abapgit_exit=>get_instance( )->custom_serialize_abap_clif(
          is_class_key = is_class_key
          it_source    = rt_source ).
      ENDMETHOD.
    
      METHOD serialize_abap_new.
    
        DATA: lo_source   TYPE REF TO object,
              lo_instance TYPE REF TO object.
    
    * do not call the class/methods statically, as it will
    * give syntax errors on old versions
        CALL METHOD ('CL_OO_FACTORY')=>('CREATE_INSTANCE')
          RECEIVING
            result = lo_instance.
    
        CALL METHOD lo_instance->('CREATE_CLIF_SOURCE')
          EXPORTING
            clif_name = is_clskey-clsname
            version   = 'A'
          RECEIVING
            result    = lo_source.
    
        CALL METHOD lo_source->('GET_SOURCE')
          IMPORTING
            source = rt_source.
    
      ENDMETHOD.
    
      METHOD serialize_abap_old.
    * for old ABAP AS versions
        DATA: lo_source TYPE REF TO object.
    
        CREATE OBJECT lo_source TYPE ('CL_OO_SOURCE')
          EXPORTING
            clskey             = is_clskey
          EXCEPTIONS
            class_not_existing = 1
            OTHERS             = 2.
        IF sy-subrc <> 0.
          zcx_abapgit_exception=>raise_t100( ).
        ENDIF.
    
        CALL METHOD lo_source->('READ')
          EXPORTING
            version = 'A'.
        CALL METHOD lo_source->('GET_OLD_SOURCE')
          RECEIVING
            old_source = rt_source.
        remove_signatures( CHANGING ct_source = rt_source ).
    
      ENDMETHOD.
    
      METHOD serialize_locals_def.
    
        rt_source = read_include( is_clskey = is_clskey
                                  iv_type = seop_ext_class_locals_def ).
    
        reduce( CHANGING ct_source = rt_source ).
    
      ENDMETHOD.
    
      METHOD serialize_locals_imp.
    
        rt_source = read_include( is_clskey = is_clskey
                                  iv_type = seop_ext_class_locals_imp ).
    
        reduce( CHANGING ct_source = rt_source ).
    
      ENDMETHOD.
    
      METHOD serialize_macros.
    
        rt_source = read_include( is_clskey = is_clskey
                                  iv_type = seop_ext_class_macros ).
    
        reduce( CHANGING ct_source = rt_source ).
    
      ENDMETHOD.
    
      METHOD serialize_testclasses.
    
        DATA ls_vseoclass TYPE vseoclass.
    
        CALL FUNCTION 'SEO_CLIF_GET'
          EXPORTING
            cifkey       = is_clskey
            version      = seoc_version_active
          IMPORTING
            class        = ls_vseoclass
          EXCEPTIONS
            not_existing = 1
            deleted      = 2
            model_only   = 3
            OTHERS       = 4.
        IF sy-subrc <> 0 OR ls_vseoclass-with_unit_tests = abap_false.
          mv_skip_testclass = abap_true.
          RETURN.
        ENDIF.
    
        rt_source = read_include( is_clskey = is_clskey
                                  iv_type = seop_ext_class_testclasses ).
    
        mv_skip_testclass = calculate_skip_testclass( rt_source ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_PATH IMPLEMENTATION.
    
      METHOD change_dir.
    
        DATA: lv_last TYPE i,
              lv_temp TYPE string.
    
        lv_last = strlen( iv_cur_dir ) - 1.
    
        IF iv_cd = '' OR iv_cd = '.'. " No change
          rv_path = iv_cur_dir.
        ELSEIF iv_cd+0(1) = '/'.      " Absolute path
          rv_path = iv_cd.
        ELSEIF iv_cd = '..'.          " CD back
          IF iv_cur_dir = '/' OR iv_cur_dir = ''. " Back from root = root
            rv_path = iv_cur_dir.
          ELSE.
            lv_temp = reverse( iv_cur_dir ).
            IF lv_temp+0(1) = '/'.
              SHIFT lv_temp BY 1 PLACES LEFT.
            ENDIF.
            SHIFT lv_temp UP TO '/' LEFT.
            rv_path = reverse( lv_temp ).
          ENDIF.
        ELSEIF iv_cur_dir+lv_last(1) = '/'.  " Append cd to cur_dir separated by /
          rv_path = iv_cur_dir && iv_cd.
        ELSE.
          rv_path = iv_cur_dir && '/' && iv_cd.
        ENDIF.
    
        " TODO: improve logic and cases
    
      ENDMETHOD.
    
      METHOD get_filename_from_syspath.
    
        DATA: lv_split TYPE c LENGTH 1,
              lv_index TYPE i,
              lt_split TYPE TABLE OF string.
    
        " filename | c:\filename | /dir/filename | \\server\filename
        IF iv_path CA '/'.
          lv_split = '/'.
        ELSE.
          lv_split = '\'.
        ENDIF.
    
        SPLIT iv_path AT lv_split INTO TABLE lt_split.
    
        lv_index = lines( lt_split ).
    
        READ TABLE lt_split INDEX lv_index INTO rv_filename.
    
      ENDMETHOD.
    
      METHOD is_root.
        rv_yes = boolc( iv_path = '/' ).
      ENDMETHOD.
    
      METHOD is_subdir.
    
        DATA lv_len  TYPE i.
        DATA lv_last TYPE i.
    
        lv_len  = strlen( iv_parent ).
        lv_last = lv_len - 1.
        rv_yes  = boolc( strlen( iv_path ) > lv_len
                     AND iv_path+0(lv_len) = iv_parent
                     AND ( iv_parent+lv_last(1) = '/' OR iv_path+lv_len(1) = '/' ) ).
    
      ENDMETHOD.
    
      METHOD split_file_location.
    
        DATA: lv_cnt TYPE i,
              lv_len TYPE i.
    
        FIND FIRST OCCURRENCE OF REGEX '^/(.*/)?' IN iv_fullpath
          MATCH COUNT lv_cnt
          MATCH LENGTH lv_len ##REGEX_POSIX.
    
        IF lv_cnt > 0.
          ev_path     = iv_fullpath+0(lv_len).
          ev_filename = iv_fullpath+lv_len.
        ELSE.
          CLEAR ev_path.
          ev_filename = iv_fullpath.
        ENDIF.
    
        ev_filename = cl_http_utility=>unescape_url( escaped = ev_filename ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_PO_FILE IMPLEMENTATION.
    
      METHOD build_po_body.
    
        FIELD-SYMBOLS  LIKE LINE OF mt_pairs.
        FIELD-SYMBOLS  LIKE LINE OF -comments.
    
        CREATE OBJECT ro_buf.
    
        LOOP AT mt_pairs ASSIGNING .
          IF sy-tabix <> 1.
            ro_buf->add( '' ).
          ENDIF.
    
          " TODO integrate translator comments ?
    
          SORT -comments BY kind.
          LOOP AT -comments ASSIGNING .
            ro_buf->add( |#{ get_comment_marker( -kind ) } { -text }| ).
          ENDLOOP.
    
          ro_buf->add( |msgid { quote( -source ) }| ).
          ro_buf->add( |msgstr { quote( -target ) }| ).
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD build_po_head.
    
        CREATE OBJECT ro_buf.
    
        " TODO, more headers ? sample: https://www.gnu.org/software/trans-coord/manual/gnun/html_node/PO-Header.html
        " TODO, does \n really necessary ? check editors support for non-\n
        " TODO, should be unfuzzy for final version, and potentially should have more fields
    
        ro_buf->add( '#, fuzzy' ).
        ro_buf->add( 'msgid ""' ).
        ro_buf->add( 'msgstr ""' ).
        ro_buf->add( '"MIME-Version: 1.0\n"' ).
        ro_buf->add( '"Content-Type: text/plain; charset=UTF-8\n"' ).
        ro_buf->add( '"Content-Transfer-Encoding: 8bit\n"' ).
        ro_buf->add( '' ).
    
      ENDMETHOD.
    
      METHOD constructor.
        mv_lang = to_lower( iv_lang ).
        mv_suppress_comments = iv_suppress_comments.
      ENDMETHOD.
    
      METHOD get_comment_marker.
        CASE iv_comment_kind.
          WHEN c_comment-translator.
            rv_marker = ''.
          WHEN c_comment-extracted.
            rv_marker = '.'.
          WHEN c_comment-reference.
            rv_marker = ':'.
          WHEN c_comment-flag.
            rv_marker = ','.
          WHEN c_comment-previous.
            rv_marker = '|'.
        ENDCASE.
      ENDMETHOD.
    
      METHOD parse.
    
        DATA lv_xdata TYPE xstring.
        DATA lv_data TYPE string.
    
        IF xstrlen( iv_xdata ) > 3 AND iv_xdata(3) = cl_abap_char_utilities=>byte_order_mark_utf8.
          lv_xdata = iv_xdata+3.
        ELSE.
          lv_xdata = iv_xdata.
        ENDIF.
    
        lv_data = zcl_abapgit_convert=>xstring_to_string_utf8( lv_xdata ).
    
        parse_po( lv_data ).
    
      ENDMETHOD.
    
      METHOD parse_po.
    
        CONSTANTS:
          BEGIN OF c_state,
            wait_id  TYPE i VALUE 0,
            wait_str TYPE i VALUE 1,
            wait_eos TYPE i VALUE 2,
            " TODO msgctx
          END OF c_state.
    
        DATA lv_state TYPE i VALUE c_state-wait_id.
        DATA lt_lines TYPE string_table.
        DATA ls_pair LIKE LINE OF mt_pairs.
        DATA lv_whitespace TYPE c LENGTH 2.
        FIELD-SYMBOLS  TYPE string.
    
        lv_whitespace = ` ` && cl_abap_char_utilities=>horizontal_tab.
    
        SPLIT iv_data AT cl_abap_char_utilities=>newline INTO TABLE lt_lines.
        APPEND '' TO lt_lines. " terminator
    
        LOOP AT lt_lines ASSIGNING .
          IF lv_state = c_state-wait_eos.
            IF strlen(  ) >= 1 AND +0(1) = '"'.
              ls_pair-target = ls_pair-target && unquote(  ).
              CONTINUE.
            ELSE.
              lv_state = c_state-wait_id.
              IF ls_pair-source IS NOT INITIAL. " skip header entry for now
                INSERT ls_pair INTO TABLE mt_pairs. " Sorted, duplicates will not be inserted
              ENDIF.
              CLEAR ls_pair.
            ENDIF.
          ENDIF.
    
          CASE lv_state.
            WHEN c_state-wait_id.
              IF  IS INITIAL
                OR +0(1) = '#' " TODO, potentially parse comments in future, to re-integrate
                OR  CO lv_whitespace.
                CONTINUE.
              ENDIF.
              IF strlen(  ) >= 6 AND +0(6) = `msgid `. " w/trailing space
                ls_pair-source = unquote( substring(
                  val = 
                  off = 6 ) ).
                lv_state = c_state-wait_str.
              ELSE.
                zcx_abapgit_exception=>raise( 'PO file format error: expected msgid' ).
              ENDIF.
    
            WHEN c_state-wait_str.
              IF strlen(  ) >= 7 AND +0(7) = `msgstr `. " w/trailing space
                ls_pair-target = unquote( substring(
                  val = 
                  off = 7 ) ).
                lv_state = c_state-wait_eos.
              ELSE.
                zcx_abapgit_exception=>raise( 'PO file format error: expected msgstr' ).
              ENDIF.
    
          ENDCASE.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD push_text_pairs.
    
        DATA ls_out LIKE LINE OF mt_pairs.
        FIELD-SYMBOLS  LIKE LINE OF it_text_pairs.
        FIELD-SYMBOLS  LIKE LINE OF mt_pairs.
        DATA ls_comment LIKE LINE OF -comments.
    
        LOOP AT it_text_pairs ASSIGNING .
          CHECK -s_text IS NOT INITIAL.
    
          READ TABLE mt_pairs ASSIGNING  WITH KEY source = -s_text.
          IF sy-subrc <> 0.
            ls_out-source = -s_text.
            INSERT ls_out INTO TABLE mt_pairs ASSIGNING .
            ASSERT sy-subrc = 0.
          ENDIF.
    
          IF -target IS INITIAL. " For a case of orig text duplication
            -target = -t_text.
          ENDIF.
    
          IF mv_suppress_comments = abap_false.
            ls_comment-kind = c_comment-reference.
            ls_comment-text = condense( |{ iv_objtype }/{ iv_objname }/{ -textkey }| )
              && |, maxlen={ -unitmlt }|.
            APPEND ls_comment TO -comments.
            ASSERT sy-subrc = 0.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD quote.
        rv_text = '"' && replace(
          val  = iv_text
          sub  = '"'
          with = '\"'
          occ  = 0 ) && '"'.
      ENDMETHOD.
    
      METHOD unquote.
    
        DATA lv_len TYPE i.
        DATA lv_prev_char TYPE i.
    
        rv_text = iv_text.
        SHIFT rv_text RIGHT DELETING TRAILING space. " Measure perf ? Could be slowish, maybe use find
        SHIFT rv_text LEFT DELETING LEADING space.
        lv_len = strlen( rv_text ).
    
        IF lv_len < 2.
          zcx_abapgit_exception=>raise( 'PO file format error: bad quoting' ).
        ENDIF.
    
        lv_prev_char = lv_len - 1.
        IF rv_text+0(1) <> '"' OR rv_text+lv_prev_char(1) <> '"'.
          zcx_abapgit_exception=>raise( 'PO file format error: bad quoting' ).
        ENDIF.
    
        lv_prev_char = lv_prev_char - 1.
        IF lv_len >= 3 AND rv_text+lv_prev_char(1) = '\'. " escaped quote
          zcx_abapgit_exception=>raise( 'PO file format error: bad quoting' ).
        ENDIF.
    
        rv_text = substring(
          val = rv_text
          off = 1
          len = lv_len - 2 ).
    
        rv_text = replace(
          val  = rv_text
          sub  = '\"'
          with = '"'
          occ  = 0 ).
    
        rv_text = replace(
          val  = rv_text
          sub  = '\n'
          with = cl_abap_char_utilities=>newline
          occ  = 0 ).
    
        " TODO: theoretically there can be unescaped " - is it a problem ? check standard
    
      ENDMETHOD.
    
      METHOD zif_abapgit_i18n_file~ext.
        rv_ext = 'po'.
      ENDMETHOD.
    
      METHOD zif_abapgit_i18n_file~lang.
        rv_lang = mv_lang.
      ENDMETHOD.
    
      METHOD zif_abapgit_i18n_file~lang_suffix.
        rv_lang_suffix = mv_lang.
      ENDMETHOD.
    
      METHOD zif_abapgit_i18n_file~render.
    
        DATA lv_str TYPE string.
    
        lv_str = build_po_body( )->join_w_newline_and_flush( ).
    
        IF lv_str IS NOT INITIAL.
          lv_str = build_po_head( )->join_w_newline_and_flush( )
            && cl_abap_char_utilities=>newline
            && lv_str
            && cl_abap_char_utilities=>newline. " Trailing LF
          rv_data = zcl_abapgit_convert=>string_to_xstring_utf8( lv_str ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_i18n_file~translate.
    
        FIELD-SYMBOLS  LIKE LINE OF ct_text_pairs.
        FIELD-SYMBOLS  LIKE LINE OF mt_pairs.
        DATA lv_idx TYPE i.
    
        LOOP AT ct_text_pairs ASSIGNING .
          CHECK -s_text IS NOT INITIAL.
          lv_idx = sy-tabix.
    
          READ TABLE mt_pairs ASSIGNING  WITH KEY source = -s_text.
          IF sy-subrc = 0 AND -target IS NOT INITIAL.
            IF -t_text <> -target.
              cv_changed = abap_true.
              -t_text = -target.
            ENDIF.
          ELSE.
            DELETE ct_text_pairs INDEX lv_idx. " Otherwise error in LXE FMs for empty translation
            cv_changed = abap_true.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_progress IMPLEMENTATION.
    
      METHOD calc_pct.
    
        DATA: lv_f TYPE f.
    
        TRY.
            lv_f = ( iv_current / mv_total ) * 100.
            rv_pct = lv_f.
    
            IF rv_pct = 100.
              rv_pct = 99.
            ELSEIF rv_pct = 0.
              rv_pct = 1.
            ENDIF.
          CATCH cx_sy_zerodivide.
            rv_pct = 0.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD get_instance.
    
    * max one progress indicator at a time is supported
    
        IF gi_progress IS INITIAL.
          CREATE OBJECT gi_progress TYPE zcl_abapgit_progress.
        ENDIF.
    
        gi_progress->set_total( iv_total ).
    
        ri_progress = gi_progress.
    
      ENDMETHOD.
    
      METHOD set_instance.
    
        gi_progress = ii_progress.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_progress~off.
    
        " Clear the status bar
        CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_progress~set_total.
    
        mv_total = iv_total.
    
        CLEAR mv_cv_time_next.
        CLEAR mv_cv_datum_next.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_progress~show.
    
        DATA: lv_pct  TYPE i,
              lv_time TYPE t.
    
        CONSTANTS: lc_wait_secs TYPE i VALUE 2.
    
        GET TIME.
        lv_time = sy-uzeit.
        IF mv_cv_time_next IS INITIAL AND mv_cv_datum_next IS INITIAL.
          mv_cv_time_next  = lv_time.
          mv_cv_datum_next = sy-datum.
        ENDIF.
    
        "We only do a progress indication if enough time has passed
        IF lv_time >= mv_cv_time_next
            AND sy-datum = mv_cv_datum_next
            OR sy-datum > mv_cv_datum_next.
    
          lv_pct = calc_pct( iv_current ).
    
          CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
            EXPORTING
              percentage = lv_pct
              text       = iv_text.
          mv_cv_time_next = lv_time + lc_wait_secs.
    
        ENDIF.
        IF sy-datum > mv_cv_datum_next.
          mv_cv_datum_next = sy-datum.
        ENDIF.
        IF mv_cv_time_next < lv_time.
          mv_cv_datum_next = sy-datum + 1.
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_PROPERTIES_FILE IMPLEMENTATION.
    
      METHOD constructor.
        mv_lang = to_upper( iv_lang ).
      ENDMETHOD.
    
      METHOD get_translations.
    
        DATA:
          lv_translation TYPE string,
          lo_ajson       TYPE REF TO /apmg/if_apm_ajson,
          lo_json_path   TYPE REF TO zcl_abapgit_json_path,
          lx_exception   TYPE REF TO cx_static_check.
    
        CREATE OBJECT lo_json_path.
    
        TRY.
            lv_translation = lo_json_path->deserialize( mt_translation ).
    
            lo_ajson = /apmg/cl_apm_ajson=>parse( lv_translation
              )->map( /apmg/cl_apm_ajson_mapping=>create_to_snake_case( ) ).
    
            lo_ajson->to_abap( IMPORTING ev_container = ev_data ).
    
          CATCH cx_static_check INTO lx_exception.
            zcx_abapgit_exception=>raise_with_text( lx_exception ).
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD parse.
    
        DATA lv_data TYPE string.
    
        lv_data = zcl_abapgit_convert=>xstring_to_string_utf8( iv_xdata ).
    
        SPLIT lv_data AT cl_abap_char_utilities=>newline INTO TABLE mt_translation.
    
      ENDMETHOD.
    
      METHOD push_text_pairs.
        mt_translation = it_translation.
      ENDMETHOD.
    
      METHOD zif_abapgit_i18n_file~ext.
        rv_ext = 'properties'.
      ENDMETHOD.
    
      METHOD zif_abapgit_i18n_file~lang.
        rv_lang = mv_lang.
      ENDMETHOD.
    
      METHOD zif_abapgit_i18n_file~lang_suffix.
    
        DATA: lv_langu_sap1 TYPE sy-langu,
              lv_langu_bcp47 TYPE string.
    
        lv_langu_sap1 = zcl_abapgit_convert=>language_sap2_to_sap1( to_upper( mv_lang ) ).
        lv_langu_bcp47 = zcl_abapgit_convert=>language_sap1_to_bcp47( lv_langu_sap1 ).
    
        rv_lang_suffix = lv_langu_bcp47.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_i18n_file~render.
    
        DATA lv_translation TYPE string.
    
        lv_translation = concat_lines_of( table = mt_translation
                                          sep   = cl_abap_char_utilities=>newline ) && cl_abap_char_utilities=>newline.
        rv_data = zcl_abapgit_convert=>string_to_xstring_utf8( lv_translation ).
    
      ENDMETHOD.
    
      METHOD zif_abapgit_i18n_file~translate.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_sotr_handler IMPLEMENTATION.
    
      METHOD change_sotr_package.
    
        DATA lt_concepts TYPE btfr_conc_tt.
    
        " Short texts
        SELECT concept FROM sotr_head INTO TABLE lt_concepts
          WHERE paket = iv_old_package
          ORDER BY PRIMARY KEY.
    
        IF lt_concepts IS NOT INITIAL.
          CALL FUNCTION 'BTFR_CHANGE_PACKAGE'
            EXPORTING
              concept_tab         = lt_concepts
              new_package         = iv_new_package
              flag_string         = abap_false
              flag_ignore_system  = abap_true
            EXCEPTIONS
              invalid_package     = 1
              invalid_tadir_entry = 2
              update_error        = 3
              OTHERS              = 4.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
        ENDIF.
    
        CLEAR lt_concepts.
    
        " Long texts
        SELECT concept FROM sotr_headu INTO TABLE lt_concepts
          WHERE paket = iv_old_package
          ORDER BY PRIMARY KEY.
    
        IF lt_concepts IS NOT INITIAL.
          CALL FUNCTION 'BTFR_CHANGE_PACKAGE'
            EXPORTING
              concept_tab         = lt_concepts
              new_package         = iv_new_package
              flag_string         = abap_true
              flag_ignore_system  = abap_true
            EXCEPTIONS
              invalid_package     = 1
              invalid_tadir_entry = 2
              update_error        = 3
              OTHERS              = 4.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD create_sotr.
    
        DATA:
          lt_sotr     TYPE ty_sotr_tt,
          lt_sotr_use TYPE ty_sotr_use_tt.
    
        io_xml->read( EXPORTING iv_name = 'SOTR'
                      CHANGING cg_data = lt_sotr ).
        io_xml->read( EXPORTING iv_name = 'SOTR_USE'
                      CHANGING cg_data = lt_sotr_use ).
    
        create_sotr_from_data(
          iv_package  = iv_package
          it_sotr     = lt_sotr
          it_sotr_use = lt_sotr_use ).
    
      ENDMETHOD.
    
      METHOD create_sotr_from_data.
    
        DATA:
          lt_objects TYPE sotr_objects,
          ls_paket   TYPE sotr_pack,
          lv_alias   TYPE sotr_head-alias_name,
          lv_object  LIKE LINE OF lt_objects.
    
        FIELD-SYMBOLS:  LIKE LINE OF it_sotr.
    
        LOOP AT it_sotr ASSIGNING .
          CALL FUNCTION 'SOTR_OBJECT_GET_OBJECTS'
            EXPORTING
              object_vector    = -header-objid_vec
            IMPORTING
              objects          = lt_objects
            EXCEPTIONS
              object_not_found = 1
              OTHERS           = 2.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
          READ TABLE lt_objects INDEX 1 INTO lv_object.
          ASSERT sy-subrc = 0.
    
          ls_paket-paket = iv_package.
    
          " Replace package in alias with new package
          lv_alias = -header-alias_name.
          IF lv_alias CS '/'.
            lv_alias = iv_package && lv_alias+sy-fdpos(*).
          ENDIF.
    
          CALL FUNCTION 'SOTR_CREATE_CONCEPT'
            EXPORTING
              paket                         = ls_paket
              crea_lan                      = -header-crea_lan
              alias_name                    = lv_alias
              object                        = lv_object
              entries                       = -entries
              concept_default               = -header-concept
            EXCEPTIONS
              package_missing               = 1
              crea_lan_missing              = 2
              object_missing                = 3
              paket_does_not_exist          = 4
              alias_already_exist           = 5
              object_type_not_found         = 6
              langu_missing                 = 7
              identical_context_not_allowed = 8
              text_too_long                 = 9
              error_in_update               = 10
              no_master_langu               = 11
              error_in_concept_id           = 12
              alias_not_allowed             = 13
              tadir_entry_creation_failed   = 14
              internal_error                = 15
              error_in_correction           = 16
              user_cancelled                = 17
              no_entry_found                = 18
              OTHERS                        = 19.
          IF sy-subrc <> 0 AND sy-subrc <> 5.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
        ENDLOOP.
    
        CALL FUNCTION 'SOTR_USAGE_MODIFY'
          EXPORTING
            sotr_usage = it_sotr_use.
    
      ENDMETHOD.
    
      METHOD delete_sotr.
    
        DATA lt_sotr_use TYPE ty_sotr_use_tt.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_sotr_use.
    
        lt_sotr_use = get_sotr_usage( iv_pgmid    = iv_pgmid
                                      iv_object   = iv_object
                                      iv_obj_name = iv_obj_name ).
    
        " Remove any usage to ensure deletion, see function module BTFR_CHECK
        DELETE sotr_use FROM TABLE lt_sotr_use ##SUBRC_OK.
    
        LOOP AT lt_sotr_use ASSIGNING  WHERE concept IS NOT INITIAL.
    
          CALL FUNCTION 'SOTR_DELETE_CONCEPT'
            EXPORTING
              concept             = -concept
            EXCEPTIONS
              no_entry_found      = 1
              text_not_found      = 2
              invalid_package     = 3
              text_not_changeable = 4
              text_enqueued       = 5
              no_correction       = 6
              parameter_error     = 7
              OTHERS              = 8.
          IF sy-subrc > 2.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD delete_sotr_package.
    
        DATA lt_sotr_head TYPE STANDARD TABLE OF sotr_head WITH DEFAULT KEY.
        DATA lv_obj_name TYPE tadir-obj_name.
    
        FIELD-SYMBOLS  LIKE LINE OF lt_sotr_head.
    
        SELECT * FROM sotr_head INTO TABLE lt_sotr_head WHERE paket = iv_package ORDER BY PRIMARY KEY.
    
        LOOP AT lt_sotr_head ASSIGNING  WHERE concept IS NOT INITIAL.
    
          CALL FUNCTION 'SOTR_DELETE_CONCEPT'
            EXPORTING
              concept             = -concept
            EXCEPTIONS
              no_entry_found      = 1
              text_not_found      = 2
              invalid_package     = 3
              text_not_changeable = 4
              text_enqueued       = 5
              no_correction       = 6
              parameter_error     = 7
              OTHERS              = 8.
          IF sy-subrc > 2.
            zcx_abapgit_exception=>raise_t100( ).
          ENDIF.
    
        ENDLOOP.
    
        " Nothing left, then delete SOTR from TADIR
        SELECT * FROM sotr_head INTO TABLE lt_sotr_head WHERE paket = iv_package ORDER BY PRIMARY KEY.
        IF sy-subrc <> 0.
          SELECT SINGLE obj_name FROM tadir INTO lv_obj_name
            WHERE pgmid = 'R3TR' AND object = 'SOTR' AND obj_name = iv_package.
          IF sy-subrc = 0.
            zcl_abapgit_factory=>get_tadir( )->delete_single(
              iv_object   = 'SOTR'
              iv_obj_name = lv_obj_name ).
    
            IF zcl_abapgit_factory=>get_sap_package( iv_package )->are_changes_recorded_in_tr_req( ) = abap_true.
    
              zcl_abapgit_factory=>get_cts_api( )->insert_transport_object(
                iv_object   = 'SOTR'
                iv_obj_name = lv_obj_name
                iv_package  = iv_package
                iv_mode     = zif_abapgit_cts_api=>c_transport_mode-delete ).
    
            ENDIF.
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_sotr_4_concept.
    
        DATA: ls_header  TYPE ty_sotr-header,
              lv_paket   LIKE ls_header-alias_name,
              lt_entries TYPE ty_sotr-entries.
    
        FIELD-SYMBOLS:  LIKE LINE OF lt_entries.
    
        CALL FUNCTION 'SOTR_GET_CONCEPT'
          EXPORTING
            concept        = iv_concept
          IMPORTING
            header         = ls_header
          TABLES
            entries        = lt_entries
          EXCEPTIONS
            no_entry_found = 1
            OTHERS         = 2.
        IF sy-subrc <> 0.
          RETURN.
        ENDIF.
    
        " If alias contains package, remove it
        lv_paket = ls_header-paket && '/'.
        IF ls_header-alias_name CS lv_paket.
          ls_header-alias_name = replace(
            val  = ls_header-alias_name
            sub  = lv_paket
            with = '/'
            occ  = 1 ).
        ENDIF.
    
        CLEAR: ls_header-paket,
               ls_header-crea_name,
               ls_header-crea_tstut,
               ls_header-chan_name,
               ls_header-chan_tstut,
               ls_header-system_id.
    
        LOOP AT lt_entries ASSIGNING .
          CLEAR: -version,
                 -crea_name,
                 -crea_tstut,
                 -chan_name,
                 -chan_tstut.
        ENDLOOP.
    
        rs_sotr-header  = ls_header.
        rs_sotr-entries = lt_entries.
    
      ENDMETHOD.
    
      METHOD get_sotr_usage.
    
        DATA: lv_obj_name TYPE trobj_name.
    
        lv_obj_name = iv_obj_name.
    
        " Objects with multiple components
        IF iv_pgmid = 'LIMU' AND ( iv_object CP 'WDY*' OR iv_object = 'WAPP' ).
          lv_obj_name+30 = '%'.
        ENDIF.
    
        CALL FUNCTION 'SOTR_USAGE_READ'
          EXPORTING
            pgmid          = iv_pgmid
            object         = iv_object
            obj_name       = lv_obj_name
          IMPORTING
            sotr_usage     = rt_sotr_use
          EXCEPTIONS
            no_entry_found = 1
            error_in_pgmid = 2
            OTHERS         = 3.
        IF sy-subrc = 0.
          SORT rt_sotr_use.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD read_sotr.
    
        FIELD-SYMBOLS  LIKE LINE OF et_sotr_use.
    
        DATA: lv_sotr            TYPE ty_sotr,
              lt_language_filter TYPE zif_abapgit_environment=>ty_system_language_filter.
    
        " OTR short text usage: see TABLE BTFR_OBJ_IDS
        " LIMU: CPUB, WAPP, WDYC, WDYD, WDYV
        " R3TR: ENHC, ENHO, ENHS, ENSC, SCGR, SMIF, WDCA, WDCC, WEBI, WEBS
    
        et_sotr_use = get_sotr_usage( iv_pgmid    = iv_pgmid
                                      iv_object   = iv_object
                                      iv_obj_name = iv_obj_name ).
    
        LOOP AT et_sotr_use ASSIGNING  WHERE concept IS NOT INITIAL.
          lv_sotr = get_sotr_4_concept( -concept ).
    
          IF io_xml IS BOUND AND io_i18n_params->ms_params-main_language_only = abap_true.
            DELETE lv_sotr-entries WHERE langu <> io_i18n_params->ms_params-main_language.
            CHECK lv_sotr-entries IS NOT INITIAL.
          ENDIF.
          lt_language_filter = io_i18n_params->build_language_filter( ).
          DELETE lv_sotr-entries WHERE NOT langu IN lt_language_filter
            AND langu <> io_i18n_params->ms_params-main_language.
          CHECK lv_sotr-entries IS NOT INITIAL.
    
          INSERT lv_sotr INTO TABLE et_sotr.
        ENDLOOP.
    
        IF io_xml IS BOUND.
          io_xml->add( iv_name = 'SOTR'
                       ig_data = et_sotr ).
          io_xml->add( iv_name = 'SOTR_USE'
                       ig_data = et_sotr_use ).
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_status_calc IMPLEMENTATION.
    
      METHOD build_existing.
    
        DATA ls_file_sig LIKE LINE OF it_state.
    
        " Item
        rs_result-obj_type  = is_local-item-obj_type.
        rs_result-obj_name  = is_local-item-obj_name.
        rs_result-package   = is_local-item-devclass.
        rs_result-srcsystem = is_local-item-srcsystem.
        rs_result-origlang  = is_local-item-origlang.
        rs_result-inactive  = is_local-item-inactive.
    
        " File
        rs_result-path     = is_local-file-path.
        rs_result-filename = is_local-file-filename.
    
        rs_result-match    = boolc( is_local-file-sha1 = is_remote-sha1 ).
        IF rs_result-match = abap_true.
          RETURN.
        ENDIF.
    
        " Match against current state
        READ TABLE it_state INTO ls_file_sig
          WITH KEY
            path     = is_local-file-path
            filename = is_local-file-filename
          BINARY SEARCH.
    
        IF sy-subrc = 0.
          IF ls_file_sig-sha1 <> is_local-file-sha1.
            rs_result-lstate = zif_abapgit_definitions=>c_state-modified.
          ENDIF.
          IF ls_file_sig-sha1 <> is_remote-sha1.
            rs_result-rstate = zif_abapgit_definitions=>c_state-modified.
          ENDIF.
        ELSE.
          " This is a strange situation. As both local and remote exist
          " the state should also be present. Maybe this is a first run of the code.
          " In this case just compare hashes directly and mark both changed
          " the user will presumably decide what to do after checking the actual diff
          rs_result-lstate = zif_abapgit_definitions=>c_state-modified.
          rs_result-rstate = zif_abapgit_definitions=>c_state-modified.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD build_new_local.
    
        " Item
        rs_result-obj_type  = is_local-item-obj_type.
        rs_result-obj_name  = is_local-item-obj_name.
        rs_result-package   = is_local-item-devclass.
        rs_result-srcsystem = is_local-item-srcsystem.
        rs_result-origlang  = is_local-item-origlang.
        rs_result-inactive  = is_local-item-inactive.
    
        " File
        rs_result-path     = is_local-file-path.
        rs_result-filename = is_local-file-filename.
    
        " Match
        rs_result-match    = abap_false.
        rs_result-lstate   = zif_abapgit_definitions=>c_state-added.
    
      ENDMETHOD.
    
      METHOD build_new_remote.
    
        DATA ls_item     LIKE LINE OF it_items_idx.
        DATA ls_file_sig LIKE LINE OF it_state_idx.
    
        " Common and default part
        rs_result-path     = is_remote-path.
        rs_result-filename = is_remote-filename.
        rs_result-match    = abap_false.
        rs_result-rstate   = zif_abapgit_definitions=>c_state-added.
    
        zcl_abapgit_filename_logic=>file_to_object(
          EXPORTING
            iv_filename = is_remote-filename
            iv_path     = is_remote-path
            iv_devclass = mv_root_package
            io_dot      = mo_dot
          IMPORTING
            es_item     = ls_item ).
    
        " Check if in item index + get package
        READ TABLE it_items_idx INTO ls_item
          WITH KEY
            obj_type = ls_item-obj_type
            obj_name = ls_item-obj_name.
    
        IF sy-subrc = 0.
    
          " Completely new (xml, abap) and new file in an existing object
          rs_result-obj_type  = ls_item-obj_type.
          rs_result-obj_name  = ls_item-obj_name.
          rs_result-package   = ls_item-devclass.
          rs_result-srcsystem = ''.
          rs_result-origlang  = ''.
    
          READ TABLE it_state_idx INTO ls_file_sig
            WITH KEY
              path     = is_remote-path
              filename = is_remote-filename.
    
          " Existing file but from another package
          " was not added during local file proc as was not in tadir for repo package
          IF sy-subrc = 0.
            IF ls_file_sig-sha1 = is_remote-sha1.
              rs_result-match = abap_true.
              CLEAR rs_result-rstate.
            ELSE.
              rs_result-rstate = zif_abapgit_definitions=>c_state-modified.
            ENDIF.
    
            " Item is in state and in cache but with no package - it was deleted
            " OR devclass is the same as repo package (see #532)
            IF ls_item-devclass IS INITIAL OR ls_item-devclass = mv_root_package.
              rs_result-match  = abap_false.
              rs_result-lstate = zif_abapgit_definitions=>c_state-deleted.
            ENDIF.
          ENDIF.
    
        ELSE. " Completely unknown file, probably non-abapgit
          ASSERT 1 = 1. " No action, just follow defaults
        ENDIF.
    
      ENDMETHOD.
    
      METHOD check_local_remote_consistency.
        IF is_remote-sha1 IS INITIAL.
          IF is_local-file-filename = zcl_abapgit_filename_logic=>c_package_file.
            zcx_abapgit_exception=>raise(
              |Package name conflict { is_local-item-obj_type } { is_local-item-obj_name }. | &&
              |Rename package or use FULL folder logic| ).
          ELSE.
            zcx_abapgit_exception=>raise(
              |Checksum conflict { is_local-item-obj_type } { is_local-item-obj_name }. | &&
              |Please create an issue on Github| ).
          ENDIF.
        ENDIF.
      ENDMETHOD.
    
      METHOD constructor.
        mv_root_package = iv_root_package.
        mo_dot          = io_dot.
      ENDMETHOD.
    
      METHOD ensure_state.
    
        FIELD-SYMBOLS  LIKE LINE OF rt_state.
        FIELD-SYMBOLS  LIKE LINE OF it_local.
    
        IF lines( it_cur_state ) = 0.
          " Empty state is usually not expected. Maybe for new repos.
          " In this case suppose the local state is unchanged
          LOOP AT it_local ASSIGNING .
            APPEND INITIAL LINE TO rt_state ASSIGNING .
            MOVE-CORRESPONDING -file TO .
          ENDLOOP.
        ELSE.
          rt_state = it_cur_state.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_instance.
    
        CREATE OBJECT ri_instance TYPE zcl_abapgit_status_calc
          EXPORTING
            iv_root_package = iv_root_package
            io_dot          = io_dot.
    
      ENDMETHOD.
    
      METHOD get_object_package.
        DATA: lv_name    TYPE devclass,
              li_package TYPE REF TO zif_abapgit_sap_package.
    
        rv_devclass = zcl_abapgit_factory=>get_tadir( )->get_object_package(
          iv_object   = iv_object
          iv_obj_name = iv_obj_name ).
        IF rv_devclass IS INITIAL AND iv_object = 'DEVC' AND iv_obj_name(1) = '$'.
          " local packages usually have no tadir entry
          lv_name = iv_obj_name.
          li_package = zcl_abapgit_factory=>get_sap_package( lv_name ).
          IF li_package->exists( ) = abap_true.
            rv_devclass = lv_name.
          ENDIF.
        ENDIF.
      ENDMETHOD.
    
      METHOD process_items.
    
        DATA:
          ls_item         LIKE LINE OF ct_items,
          lv_is_xml       TYPE abap_bool,
          lv_is_json      TYPE abap_bool,
          lv_sub_fetched  TYPE abap_bool,
          lt_sub_packages TYPE SORTED TABLE OF devclass WITH UNIQUE KEY table_line.
    
        FIELD-SYMBOLS  LIKE LINE OF it_unprocessed_remote.
    
        LOOP AT it_unprocessed_remote ASSIGNING .
    
          zcl_abapgit_filename_logic=>file_to_object(
            EXPORTING
              iv_filename = -filename
              iv_path     = -path
              io_dot      = mo_dot
              iv_devclass = mv_root_package
            IMPORTING
              es_item     = ls_item
              ev_is_xml   = lv_is_xml
              ev_is_json  = lv_is_json ).
    
          CHECK lv_is_xml = abap_true OR lv_is_json = abap_true. " only object definitions
    
          ls_item-devclass = get_object_package(
            iv_object   = ls_item-obj_type
            iv_obj_name = ls_item-obj_name ).
    
          IF ls_item-devclass IS NOT INITIAL AND mv_root_package <> ls_item-devclass.
            IF lv_sub_fetched = abap_false.
              lt_sub_packages = zcl_abapgit_factory=>get_sap_package( mv_root_package )->list_subpackages( ).
              lv_sub_fetched  = abap_true.
            ENDIF.
    
            " Make sure the package is under the repo main package
            READ TABLE lt_sub_packages TRANSPORTING NO FIELDS
              WITH KEY table_line = ls_item-devclass.
            IF sy-subrc <> 0 AND ls_item-obj_type = 'DEVC'.
              CLEAR ls_item-devclass.
            ENDIF.
          ENDIF.
    
          APPEND ls_item TO ct_items.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD process_local.
    
        FIELD-SYMBOLS:
           LIKE LINE OF ct_remote,
           LIKE LINE OF ct_results,
            LIKE LINE OF it_state_idx,
            LIKE LINE OF it_local.
    
        LOOP AT it_local ASSIGNING .
          " Skip ignored files
          CHECK mo_dot->is_ignored(
            iv_path     = -file-path
            iv_filename = -file-filename ) = abap_false.
    
          IF -item IS NOT INITIAL
            AND zcl_abapgit_filename_logic=>is_obj_definition_file( -file-filename ) = abap_true.
            " Collect for item index
            APPEND -item TO ct_items.
          ENDIF.
    
          APPEND INITIAL LINE TO ct_results ASSIGNING .
    
          " Find a match in remote
          READ TABLE ct_remote ASSIGNING 
            WITH KEY file_path
            COMPONENTS
              path     = -file-path
              filename = -file-filename.
          IF sy-subrc = 0.  " Both local and remote exist
            check_local_remote_consistency(
              is_local  = 
              is_remote =  ).
             = build_existing(
              is_local  = 
              is_remote = 
              it_state  = it_state_idx ).
            CLEAR -sha1. " Mark as processed
          ELSE. " Only local exists
             = build_new_local(  ).
            " Check if same file exists in different location
            READ TABLE ct_remote ASSIGNING 
              WITH KEY file
              COMPONENTS filename = -file-filename.
            IF sy-subrc = 0 AND -file-sha1 = -sha1.
              " If yes, then it was probably moved
              -packmove = abap_true.
            ELSEIF sy-subrc = 4.
              " Check if file existed before and was deleted remotely
              READ TABLE it_state_idx ASSIGNING 
                WITH KEY
                  path     = -file-path
                  filename = -file-filename.
              IF sy-subrc = 0.
                IF -file-sha1 = -sha1.
                  -lstate = zif_abapgit_definitions=>c_state-unchanged.
                ELSE.
                  -lstate = zif_abapgit_definitions=>c_state-modified.
                ENDIF.
                -rstate = zif_abapgit_definitions=>c_state-deleted. " ??
              ENDIF.
            ENDIF.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD process_remote.
    
        FIELD-SYMBOLS:
           LIKE LINE OF it_unprocessed_remote,
           LIKE LINE OF ct_results,
            LIKE LINE OF it_local.
    
        LOOP AT it_unprocessed_remote ASSIGNING .
    
          APPEND INITIAL LINE TO ct_results ASSIGNING .
    
           = build_new_remote(
            is_remote   = 
            it_items_idx = it_items_idx
            it_state_idx = it_state_idx ).
    
          " Check if same file exists in different location (not for generic package files)
          READ TABLE it_local ASSIGNING 
            WITH KEY file-filename = -filename.
          IF sy-subrc = 0 AND -filename <> zcl_abapgit_filename_logic=>c_package_file.
            -match = abap_false.
            -lstate = zif_abapgit_definitions=>c_state-deleted.
            -rstate = zif_abapgit_definitions=>c_state-unchanged.
            IF -file-sha1 = -sha1.
              -packmove = abap_true.
            ENDIF.
          ELSE.
            " Check if file existed before and was deleted locally
            READ TABLE it_state_idx TRANSPORTING NO FIELDS
              WITH KEY
                path     = -path
                filename = -filename.
            IF sy-subrc = 0.
              -match  = abap_false.
              -lstate = zif_abapgit_definitions=>c_state-deleted.
            ENDIF.
          ENDIF.
        ENDLOOP.
    
      ENDMETHOD.
    
      METHOD zif_abapgit_status_calc~calculate_status.
    
        DATA:
          lt_remote        LIKE it_remote,
          lt_items         TYPE zif_abapgit_definitions=>ty_items_tt,
          lt_items_by_obj  TYPE zif_abapgit_definitions=>ty_items_ts, " Sorted by obj_type+obj_name
          lt_state_by_file TYPE zif_abapgit_git_definitions=>ty_file_signatures_ts. " Sorted by path+filename
    
        lt_state_by_file = ensure_state( " Index by file
          it_cur_state = it_cur_state
          it_local     = it_local ).
        lt_remote        = it_remote.
    
        " Process local files and new local files
        process_local(
          EXPORTING
            it_local     = it_local
            it_state_idx = lt_state_by_file
          CHANGING
            ct_remote    = lt_remote
            ct_items     = lt_items
            ct_results   = rt_results ).
    
        " Remove processed remotes (with cleared SHA1)
        DELETE lt_remote WHERE sha1 IS INITIAL.
    
        " Complete item index for unmarked remote files
        process_items( " TODO: rename ?
          EXPORTING
            it_unprocessed_remote = lt_remote
          CHANGING
            ct_items              = lt_items ).
    
        " The item list was not unique by now, just collected as "mention" list
        SORT lt_items DESCENDING. " Default key - type, name, pkg, ...
        DELETE ADJACENT DUPLICATES FROM lt_items COMPARING obj_type obj_name.
        lt_items_by_obj = lt_items.
    
        " Process new remote files (marked above with empty SHA1)
        process_remote(
          EXPORTING
            it_local              = it_local
            it_unprocessed_remote = lt_remote
            it_state_idx          = lt_state_by_file
            it_items_idx          = lt_items_by_obj
          CHANGING
            ct_results            = rt_results ).
    
        SORT rt_results BY
          obj_type ASCENDING
          obj_name ASCENDING
          filename ASCENDING
          path ASCENDING.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS ZCL_ABAPGIT_STRING_BUFFER IMPLEMENTATION.
    
      METHOD add.
        APPEND iv_str TO mt_buffer.
        ro_me = me.
      ENDMETHOD.
    
      METHOD join_and_flush.
        rv_str = concat_lines_of( mt_buffer ).
        CLEAR mt_buffer.
      ENDMETHOD.
    
      METHOD join_w_newline_and_flush.
        rv_str = concat_lines_of(
          table = mt_buffer
          sep   = cl_abap_char_utilities=>newline ).
        CLEAR mt_buffer.
      ENDMETHOD.
    
      METHOD join_w_space_and_flush.
        rv_str = concat_lines_of(
          table = mt_buffer
          sep   = ` ` ).
        CLEAR mt_buffer.
      ENDMETHOD.
    
      METHOD new.
        CREATE OBJECT ro_me.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_timer IMPLEMENTATION.
    
      METHOD constructor.
        mv_text  = iv_text.
        mv_count = iv_count.
      ENDMETHOD.
    
      METHOD create.
        CREATE OBJECT ro_timer
          EXPORTING
            iv_text  = iv_text
            iv_count = iv_count.
      ENDMETHOD.
    
      METHOD end.
    
        DATA:
          lv_timestamp TYPE timestampl,
          lv_runtime   TYPE timestampl,
          lv_sec       TYPE p LENGTH 11 DECIMALS 2.
    
        IF mv_timer IS INITIAL.
          rv_result = 'Runtime measurement has not been started'.
        ELSE.
          GET TIME STAMP FIELD lv_timestamp.
    
          TRY.
              lv_runtime = cl_abap_tstmp=>subtract(
                tstmp1 = lv_timestamp
                tstmp2 = mv_timer ).
    
              lv_sec = lv_runtime. " round to 2 decimal places
    
              IF mv_count = 1.
                rv_result = |1 object, |.
              ELSEIF mv_count > 1.
                rv_result = |{ mv_count } objects, |.
              ENDIF.
    
              rv_result = rv_result && |{ lv_sec } seconds|.
    
            CATCH cx_parameter_invalid.
              rv_result = 'Error getting runtime measurement'.
          ENDTRY.
        ENDIF.
    
        IF iv_output_as_status_message = abap_true.
          MESSAGE s000(oo) WITH mv_text rv_result.
        ENDIF.
    
        IF mv_text IS NOT INITIAL.
          rv_result = |{ mv_text } { rv_result }|.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD start.
        GET TIME STAMP FIELD mv_timer.
        ro_timer = me.
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_url IMPLEMENTATION.
    
      METHOD host.
    
        regex( EXPORTING iv_url = iv_url
               IMPORTING ev_host = rv_host ).
    
      ENDMETHOD.
    
      METHOD is_abapgit_repo.
    
        IF iv_url CS 'github.com' AND ( iv_url CP '*/abapGit' OR iv_url CP '*/abapGit.git' ).
          rv_abapgit = abap_true.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD name.
    
        DATA: lv_path TYPE string.
    
        TRY.
            regex( EXPORTING iv_url = iv_url
                   IMPORTING ev_name = rv_name
                             ev_path = lv_path ).
    
            IF rv_name IS INITIAL.
              FIND REGEX '([\w-]+)/$' IN lv_path SUBMATCHES rv_name ##REGEX_POSIX.
              IF sy-subrc <> 0.
                zcx_abapgit_exception=>raise( 'Malformed URL' ).
              ENDIF.
            ENDIF.
    
          CATCH zcx_abapgit_exception.
            IF iv_validate = abap_true.
              zcx_abapgit_exception=>raise( 'Malformed URL' ).
            ELSE.
              rv_name = 'URL error (fix repo with "Advanced > Change Remote")'.
            ENDIF.
        ENDTRY.
    
      ENDMETHOD.
    
      METHOD path_name.
    
        DATA: lv_host TYPE string ##NEEDED.
    
        FIND REGEX '(.*://[^/]*)(.*)' IN iv_url
          SUBMATCHES lv_host rv_path_name ##REGEX_POSIX.
    
      ENDMETHOD.
    
      METHOD regex.
    
        FIND REGEX '^(https?://[^/]*)(.*/)(.*)\.git$' IN iv_url
          SUBMATCHES ev_host ev_path ev_name ##REGEX_POSIX.
        IF sy-subrc <> 0.
          FIND REGEX '^(https?://[^/]*)(.*/)(.*)$' IN iv_url
            SUBMATCHES ev_host ev_path ev_name ##REGEX_POSIX.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'Malformed URL' ).
          ENDIF.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD url_address.
    
        DATA:
          lv_host TYPE string,
          lv_path TYPE string,
          lv_name TYPE string,
          lv_len  TYPE i.
    
        regex( EXPORTING iv_url  = iv_url
               IMPORTING ev_host = lv_host
                         ev_path = lv_path
                         ev_name = lv_name ).
    
        IF lv_path IS INITIAL AND lv_name IS INITIAL.
          zcx_abapgit_exception=>raise( 'Malformed URL' ).
        ELSEIF lv_name IS INITIAL.
          lv_len = strlen( lv_path ) - 1.
          IF lv_path+lv_len(1) = '/'.
            lv_path = lv_path(lv_len).
          ENDIF.
        ENDIF.
    
        rv_adress = |{ lv_host }{ lv_path }{ lv_name }|.
    
      ENDMETHOD.
    
      METHOD validate.
    
        name( iv_url      = iv_url
              iv_validate = abap_true ).
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_utils IMPLEMENTATION.
    
      METHOD check_eol.
    
        " Check if data is using CRLF as EOL separator. If only LF is used, data was likely
        " edited by external tools
        IF iv_data IS NOT INITIAL AND
           iv_data CS cl_abap_char_utilities=>newline AND
           iv_data NS cl_abap_char_utilities=>cr_lf.
          zcx_abapgit_exception=>raise( 'Incorrect source format: Requires CRLF instead of LF' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD is_binary.
    
        " Previously we did a simple char range test described here
        " stackoverflow.com/questions/277521/how-to-identify-the-file-content-as-ascii-or-binary
        " but this is insufficient if the data contains German umlauts and other special characters.
        " Therefore we adopted another algorithm, which is similarly used by AL11
        " RSWATCH0 / GUESS_FILE_TYPE
        " We count non-printable characters if there are more than 10% it's binary.
    
        CONSTANTS:
          lc_binary_threshold TYPE i VALUE 10,
          lc_bytes_to_check   TYPE i VALUE 1000.
    
        DATA: lv_string_data           TYPE string,
              lv_non_printable_chars   TYPE i,
              lv_percentage            TYPE i,
              lv_data                  TYPE xstring,
              lv_xlen                  TYPE i.
    
        lv_xlen = xstrlen( iv_data ).
        IF lv_xlen = 0.
          RETURN.
        ENDIF.
    
        lv_xlen = nmin(
                    val1 = lv_xlen
                    val2 = lc_bytes_to_check ).
    
        lv_data = iv_data(lv_xlen).
    
        TRY.
            lv_string_data = zcl_abapgit_convert=>xstring_to_string_utf8( lv_data ).
          CATCH zcx_abapgit_exception.
            " Contains data that does not convert to UTF-8 so consider it binary
            rv_is_binary = abap_true.
            RETURN.
        ENDTRY.
    
        REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf IN lv_string_data WITH space.
        REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>newline IN lv_string_data WITH space.
    
        FIND ALL OCCURRENCES OF REGEX '[^[:print:]]' IN lv_string_data MATCH COUNT lv_non_printable_chars  ##REGEX_POSIX.
        lv_percentage = lv_non_printable_chars  * 100 / strlen( lv_string_data ).
        rv_is_binary = boolc( lv_percentage > lc_binary_threshold ).
    
      ENDMETHOD.
    
      METHOD is_valid_email.
    
        " Email address validation (RFC 5322)
        " https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch04s01.html
        CONSTANTS lc_email_regex TYPE string VALUE
          '[\w!#$%&*+/=?`{|}~^-]+(?:\.[\w!#$%&*+/=?`{|}~^-]+)*@(?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,6}'.
    
        IF iv_email IS INITIAL.
          rv_valid = abap_true.
        ELSE.
          FIND REGEX lc_email_regex IN iv_email ##REGEX_POSIX.
          rv_valid = boolc( sy-subrc = 0 ).
        ENDIF.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_version IMPLEMENTATION.
    
      METHOD check_dependant_version.
    
        CONSTANTS: lc_message TYPE string VALUE 'Current version is older than required'.
    
        IF is_dependant-major > is_current-major.
          zcx_abapgit_exception=>raise( lc_message ).
        ELSEIF is_dependant-major < is_current-major.
          RETURN.
        ENDIF.
    
        IF is_dependant-minor > is_current-minor.
          zcx_abapgit_exception=>raise( lc_message ).
        ELSEIF is_dependant-minor < is_current-minor.
          RETURN.
        ENDIF.
    
        IF is_dependant-patch > is_current-patch.
          zcx_abapgit_exception=>raise( lc_message ).
        ELSEIF is_dependant-patch < is_current-patch.
          RETURN.
        ENDIF.
    
        IF is_current-prerelase IS INITIAL.
          RETURN.
        ENDIF.
    
        CASE is_current-prerelase.
          WHEN 'rc'.
            IF is_dependant-prerelase = ''.
              zcx_abapgit_exception=>raise( lc_message ).
            ENDIF.
    
          WHEN 'beta'.
            IF is_dependant-prerelase = '' OR is_dependant-prerelase = 'rc'.
              zcx_abapgit_exception=>raise( lc_message ).
            ENDIF.
    
          WHEN 'alpha'.
            IF is_dependant-prerelase = '' OR is_dependant-prerelase = 'rc' OR is_dependant-prerelase = 'beta'.
              zcx_abapgit_exception=>raise( lc_message ).
            ENDIF.
    
        ENDCASE.
    
        IF is_dependant-prerelase = is_current-prerelase AND is_dependant-prerelase_patch > is_current-prerelase_patch.
          zcx_abapgit_exception=>raise( lc_message ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD compare.
    
        DATA: ls_version_a TYPE zif_abapgit_definitions=>ty_version,
              ls_version_b TYPE zif_abapgit_definitions=>ty_version.
    
        TRY.
            IF is_a IS NOT INITIAL.
              ls_version_a = is_a.
            ELSE.
              ls_version_a = conv_str_to_version( iv_a ).
            ENDIF.
    
            IF is_b IS NOT INITIAL.
              ls_version_b = is_b.
            ELSE.
              ls_version_b = conv_str_to_version( iv_b ).
            ENDIF.
          CATCH zcx_abapgit_exception.
            rv_result = 0.
            RETURN.
        ENDTRY.
    
        IF ls_version_a = ls_version_b.
          rv_result = 0.
        ELSE.
          TRY.
              check_dependant_version( is_current   = ls_version_a
                                       is_dependant = ls_version_b ).
              rv_result = 1.
            CATCH zcx_abapgit_exception.
              rv_result = -1.
              RETURN.
          ENDTRY.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD conv_str_to_version.
    
        DATA: lt_segments TYPE STANDARD TABLE OF string,
              lt_parts    TYPE STANDARD TABLE OF string,
              lv_segment  TYPE string.
    
        SPLIT iv_version AT '-' INTO TABLE lt_segments.
    
        READ TABLE lt_segments INTO lv_segment INDEX 1. " Version
        IF sy-subrc <> 0.   " No version
          RETURN.
        ENDIF.
    
        SPLIT lv_segment AT '.' INTO TABLE lt_parts.
    
        LOOP AT lt_parts INTO lv_segment.
    
          TRY.
              CASE sy-tabix.
                WHEN 1.
                  rs_version-major = lv_segment.
                WHEN 2.
                  rs_version-minor = lv_segment.
                WHEN 3.
                  rs_version-patch = lv_segment.
              ENDCASE.
            CATCH cx_sy_conversion_no_number.
              zcx_abapgit_exception=>raise( 'Incorrect format for Semantic Version' ).
          ENDTRY.
    
        ENDLOOP.
    
        READ TABLE lt_segments INTO lv_segment INDEX 2. " Pre-release Version
        IF sy-subrc <> 0.   " No version
          RETURN.
        ENDIF.
    
        SPLIT lv_segment AT '.' INTO TABLE lt_parts.
    
        LOOP AT lt_parts INTO lv_segment.
    
          CASE sy-tabix.
            WHEN 1.
              rs_version-prerelase = lv_segment.
              TRANSLATE rs_version-prerelase TO LOWER CASE.
            WHEN 2.
              rs_version-prerelase_patch = lv_segment.
          ENDCASE.
    
        ENDLOOP.
    
        IF rs_version-prerelase <> 'rc' AND rs_version-prerelase <> 'beta' AND rs_version-prerelase <> 'alpha'.
          zcx_abapgit_exception=>raise( 'Incorrect format for Semantic Version' ).
        ENDIF.
    
      ENDMETHOD.
    
      METHOD get_version_constant_value.
        DATA: lv_version_class     TYPE seoclsname,
              lv_version_component TYPE string.
        FIELD-SYMBOLS:  TYPE simple.
    
        IF iv_version_constant NP '*=>*'.
          zcx_abapgit_exception=>raise( 'Version constant needs to use the format CLASS/INTERFACE=>CONSTANT' ).
        ENDIF.
    
        SPLIT iv_version_constant AT '=>' INTO lv_version_class lv_version_component.
        IF sy-subrc <> 0 OR lv_version_class IS INITIAL OR lv_version_component IS INITIAL.
          zcx_abapgit_exception=>raise( 'Version constant cannot be parsed' ).
        ENDIF.
    
        " You should remember that accessing a class or an interface with syntax errors
        " gives us a shortdump. Therefore we do a syntax check here.
        zcl_abapgit_oo_factory=>get_by_name( lv_version_class )->syntax_check( lv_version_class ).
    
        ASSIGN (lv_version_class)=>(lv_version_component) TO .
        IF sy-subrc = 0.
          rv_version = .
        ELSE.
          zcx_abapgit_exception=>raise( |Could not access version at class { lv_version_class } component | &&
                                        |{ lv_version_component }| ).
        ENDIF.
      ENDMETHOD.
    
      METHOD normalize.
    
        " Internal program version should be in format "XXX.XXX.XXX" or "vXXX.XXX.XXX"
        CONSTANTS:
          lc_version_pattern    TYPE string VALUE '^v?(\d{1,3}\.\d{1,3}\.\d{1,3})\s*$',
          lc_prerelease_pattern TYPE string VALUE '^((rc|beta|alpha)\.\d{1,3})\s*$'.
    
        DATA: lv_version      TYPE string,
              lv_prerelease   TYPE string,
              lv_version_n    TYPE string,
              lv_prerelease_n TYPE string.
    
        SPLIT iv_version AT '-' INTO lv_version lv_prerelease.
    
        FIND FIRST OCCURRENCE OF REGEX lc_version_pattern
          IN lv_version SUBMATCHES lv_version_n ##REGEX_POSIX.
    
        IF lv_prerelease IS NOT INITIAL.
    
          FIND FIRST OCCURRENCE OF REGEX lc_prerelease_pattern
            IN lv_prerelease SUBMATCHES lv_prerelease_n ##REGEX_POSIX.
    
        ENDIF.
    
        IF lv_version_n IS INITIAL.
          RETURN.
        ENDIF.
    
        rv_version = lv_version_n.
    
        IF lv_prerelease_n IS NOT INITIAL.
          CONCATENATE rv_version '-' lv_prerelease_n INTO rv_version.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD version_to_numeric.
    
        DATA: lv_major   TYPE n LENGTH 4,
              lv_minor   TYPE n LENGTH 4,
              lv_release TYPE n LENGTH 4.
    
        SPLIT iv_version AT '.' INTO lv_major lv_minor lv_release.
    
        " Calculated value of version number, empty version will become 0 which is OK
        rv_version = lv_major * 1000000 + lv_minor * 1000 + lv_release.
    
      ENDMETHOD.
    ENDCLASS.
    
    CLASS zcl_abapgit_xml_pretty IMPLEMENTATION.
    
      METHOD print.
    
        DATA: li_ixml           TYPE REF TO if_ixml,
              li_xml_doc        TYPE REF TO if_ixml_document,
              li_stream_factory TYPE REF TO if_ixml_stream_factory,
              li_istream        TYPE REF TO if_ixml_istream,
              li_parser         TYPE REF TO if_ixml_parser,
              lv_xstring        TYPE xstring,
              li_encoding       TYPE REF TO if_ixml_encoding,
              li_ostream        TYPE REF TO if_ixml_ostream,
              li_renderer       TYPE REF TO if_ixml_renderer.
    
        ASSERT NOT iv_xml IS INITIAL.
    
        li_ixml    = cl_ixml=>create( ).
        li_xml_doc = li_ixml->create_document( ).
    
        li_stream_factory = li_ixml->create_stream_factory( ).
        li_istream        = li_stream_factory->create_istream_xstring(
          zcl_abapgit_convert=>string_to_xstring_utf8( iv_xml ) ).
        li_parser         = li_ixml->create_parser( stream_factory = li_stream_factory
                                                    istream        = li_istream
                                                    document       = li_xml_doc ).
        li_parser->set_normalizing( abap_true ).
        IF li_parser->parse( ) <> 0.
          IF iv_ignore_errors = abap_true.
            rv_xml = iv_xml.
            RETURN.
          ELSE.
            raise_error( li_parser ).
          ENDIF.
        ENDIF.
    
        li_ostream  = li_stream_factory->create_ostream_xstring( lv_xstring ).
    
        li_encoding = li_ixml->create_encoding(
          character_set = 'utf-8'
          byte_order    = if_ixml_encoding=>co_big_endian ).
    
        li_xml_doc->set_encoding( li_encoding ).
        li_renderer = li_ixml->create_renderer( ostream  = li_ostream
                                                document = li_xml_doc ).
    
        li_renderer->set_normalizing( boolc( iv_unpretty = abap_false ) ).
    
        li_renderer->render( ).
    
        rv_xml = zcl_abapgit_convert=>xstring_to_string_utf8_bom( lv_xstring ).
        REPLACE FIRST OCCURRENCE OF 'utf-8' IN rv_xml WITH 'utf-16'.
    
      ENDMETHOD.
    
      METHOD raise_error.
    
        DATA:
          lv_num_errors TYPE i,
          li_error      TYPE REF TO if_ixml_parse_error,
          lv_reason     TYPE string.
    
        lv_num_errors = ii_parser->num_errors( ).
    
        DO lv_num_errors TIMES.
    
          li_error = ii_parser->get_error(
                         index        = sy-index
                         min_severity = if_ixml_parse_error=>co_info ).
    
          IF li_error IS BOUND.
            lv_reason = li_error->get_reason( ).
          ENDIF.
    
          IF lv_reason IS NOT INITIAL.
            EXIT.
          ENDIF.
    
        ENDDO.
    
        IF lv_reason IS NOT INITIAL.
          zcx_abapgit_exception=>raise( lv_reason ).
        ELSE.
          zcx_abapgit_exception=>raise( 'error parsing xml' ).
        ENDIF.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    * From abapGit Objects
    * The following are excluded or replaced during build process
    * - zcl_abapgit_objects
    * - zcl_abapgit_objects_bridge
    * - zcl_abapgit_objects_check
    
    SELECTION-SCREEN BEGIN OF SCREEN 1001.
    * dummy for triggering screen on Java SAP GUI
    SELECTION-SCREEN END OF SCREEN 1001.
    
    TABLES sscrfields.
    
    ************************************************************************
    * apm Password Dialog
    *
    * Copyright (c) 2014 abapGit Contributors
    * SPDX-License-Identifier: MIT
    ************************************************************************
    
    SELECTION-SCREEN BEGIN OF SCREEN 1002 TITLE sc_title.
      SELECTION-SCREEN SKIP.
      SELECTION-SCREEN BEGIN OF LINE.
        SELECTION-SCREEN COMMENT 1(18) sc_url FOR FIELD p_url.
        PARAMETERS p_url TYPE string LOWER CASE VISIBLE LENGTH 60 ##SEL_WRONG.
      SELECTION-SCREEN END OF LINE.
      SELECTION-SCREEN SKIP.
      SELECTION-SCREEN BEGIN OF LINE.
        SELECTION-SCREEN COMMENT 1(18) sc_user FOR FIELD p_user.
        PARAMETERS p_user TYPE string LOWER CASE VISIBLE LENGTH 60 ##SEL_WRONG.
      SELECTION-SCREEN END OF LINE.
      SELECTION-SCREEN BEGIN OF LINE.
        SELECTION-SCREEN COMMENT 1(18) sc_pass FOR FIELD p_pass.
        PARAMETERS p_pass TYPE c LENGTH 255 LOWER CASE VISIBLE LENGTH 60 ##SEL_WRONG.
      SELECTION-SCREEN END OF LINE.
      SELECTION-SCREEN SKIP.
      SELECTION-SCREEN BEGIN OF LINE.
        SELECTION-SCREEN COMMENT 1(18) sc_cmnt FOR FIELD p_cmnt.
        PARAMETERS p_cmnt TYPE c LENGTH 255 LOWER CASE VISIBLE LENGTH 60 ##SEL_WRONG.
      SELECTION-SCREEN END OF LINE.
    SELECTION-SCREEN END OF SCREEN 1002.
    
    *-----------------------------------------------------------------------
    * LCL_PASSWORD_DIALOG
    *-----------------------------------------------------------------------
    CLASS lcl_password_dialog DEFINITION FINAL.
    
    **************
    * This class will remain local in the report
    **************
    
      PUBLIC SECTION.
    
        CONSTANTS c_dynnr TYPE c LENGTH 4 VALUE '1002'.
    
        CLASS-METHODS popup
          IMPORTING
            url      TYPE string
          CHANGING
            username TYPE string
            password TYPE string.
    
        CLASS-METHODS on_screen_init.
        CLASS-METHODS on_screen_output.
        CLASS-METHODS on_screen_event
          IMPORTING
            command TYPE sy-ucomm.
    
      PRIVATE SECTION.
    
        CLASS-DATA is_confirmed TYPE abap_bool.
    
        CLASS-METHODS enrich_title_by_hostname
          IMPORTING
            url TYPE string.
    
    ENDCLASS.
    
    CLASS lcl_password_dialog IMPLEMENTATION.
    
      METHOD popup.
    
        CLEAR p_pass.
        p_url      = url.
        p_user     = username.
        is_confirmed = abap_false.
    
        p_cmnt = 'Press F1 for Help'.
    
        enrich_title_by_hostname( url ).
    
        DATA(position) = /apmg/cl_apm_popups=>center(
          iv_width  = 65
          iv_height = 7 ) ##NEEDED.
    
        CALL SELECTION-SCREEN c_dynnr
          STARTING AT position-start_column position-start_row
          ENDING AT position-end_column position-end_row.
    
        IF is_confirmed = abap_true.
          username = p_user.
          password = p_pass.
        ELSE.
          CLEAR: username, password.
        ENDIF.
    
        CLEAR: p_url, p_user, p_pass.
    
      ENDMETHOD.
    
      METHOD on_screen_init.
        sc_title = 'Login'.
        sc_url   = 'Registry URL'.
        sc_user  = 'User'.
        sc_pass  = 'Password or Token'.
        sc_cmnt  = 'Note'.
      ENDMETHOD.
    
      METHOD on_screen_output.
    
        DATA excluded_commands TYPE STANDARD TABLE OF sy-ucomm WITH KEY table_line.
    
        ASSERT sy-dynnr = c_dynnr.
    
        LOOP AT SCREEN.
          IF screen-name = 'P_URL' OR screen-name = 'P_CMNT'.
            screen-input       = '0'.
            screen-intensified = '1'.
            screen-display_3d  = '0'.
            MODIFY SCREEN.
          ENDIF.
          IF screen-name = 'P_CMNT' OR screen-name = 'SC_CMNT'.
            screen-active    = '1'.
            screen-invisible = '0'.
            MODIFY SCREEN.
          ENDIF.
          IF screen-name = 'P_PASS'.
            screen-invisible = '1'.
            MODIFY SCREEN.
          ENDIF.
        ENDLOOP.
    
        APPEND 'PICK' TO excluded_commands.
    
        CALL FUNCTION 'RS_SET_SELSCREEN_STATUS'
          EXPORTING
            p_status  = 'DETL'
            p_program = 'RSPFPAR'
          TABLES
            p_exclude = excluded_commands.
    
        IF p_user IS NOT INITIAL.
          SET CURSOR FIELD 'P_PASS'.
        ENDIF.
    
      ENDMETHOD.
    
      METHOD on_screen_event.
    
        ASSERT sy-dynnr = c_dynnr.
    
        CASE command.
          WHEN 'OK'. " Enter
            is_confirmed = abap_true.
            LEAVE TO SCREEN 0.
          WHEN 'HELP'. " F1
            " TODO: open help page
          WHEN OTHERS. " Escape
            is_confirmed = abap_false.
            LEAVE TO SCREEN 0.
        ENDCASE.
    
      ENDMETHOD.
    
      METHOD enrich_title_by_hostname.
    
        FIND REGEX 'https?://([^/^:]*)' IN url SUBMATCHES DATA(host) ##REGEX_POSIX.
        IF sy-subrc = 0 AND host IS NOT INITIAL.
          CLEAR sc_title.
          CONCATENATE 'Login:' host INTO sc_title IN CHARACTER MODE SEPARATED BY space.
        ENDIF.
    
      ENDMETHOD.
    
    ENDCLASS.
    
    FORM password_popup
      USING
        url      TYPE string
      CHANGING
        username TYPE string
        password TYPE string.
    
      lcl_password_dialog=>popup(
        EXPORTING
          url      = url
        CHANGING
          username = username
          password = password ).
    
    ENDFORM.
    
    ************************************************************************
    * apm Forms
    *
    * Copyright (c) 2014 abapGit Contributors
    * SPDX-License-Identifier: MIT
    ************************************************************************
    
    FORM run.
    
      TRY.
          " TODO: Authorization check
    
          " Initialize persistence and global settings
          /apmg/cl_apm_persist_apm_setup=>install( ).
          /apmg/cl_apm_settings=>initialize_global_settings( ).
    
          PERFORM open_gui.
    
        CATCH cx_root INTO DATA(error).
          MESSAGE error TYPE 'S' DISPLAY LIKE 'E'.
      ENDTRY.
    
    ENDFORM.
    
    FORM open_gui RAISING /apmg/cx_apm_error.
    
      DATA:
        action TYPE string,
        mode   TYPE tabname.
    
      IF sy-batch = abap_true.
        " FUTURE: One day we will add this
        " /apmg/cl_apm_background=>run( )
        MESSAGE s000(oo) WITH 'apm does not support background processing'.
      ELSE.
    
        " TODO: Add startup option to jump to a specific package
        GET PARAMETER ID 'DBT' FIELD mode.
        CASE mode.
          WHEN 'ZAPM'.
            " Emergency mode: jump to database utility
            action = /apmg/if_apm_gui_router=>c_action-go_db.
          WHEN OTHERS.
            action = /apmg/if_apm_gui_router=>c_action-go_home.
        ENDCASE.
    
        /apmg/cl_apm_gui_factory=>get_gui( )->go_home( action ).
    
        CALL SELECTION-SCREEN 1001. " trigger screen
    
      ENDIF.
    
    ENDFORM.
    
    FORM output.
    
      DATA excluded_commands TYPE STANDARD TABLE OF sy-ucomm WITH KEY table_line.
    
      PERFORM set_pf_status IN PROGRAM rsdbrunt IF FOUND.
    
      APPEND 'CRET' TO excluded_commands.  "Button Execute
      APPEND 'SPOS' TO excluded_commands.  "Button Save
    
      CALL FUNCTION 'RS_SET_SELSCREEN_STATUS'
        EXPORTING
          p_status  = sy-pfkey
        TABLES
          p_exclude = excluded_commands.
    
      TRY.
          /apmg/cl_apm_gui_factory=>get_gui( )->set_focus( ).
        CATCH cx_root INTO DATA(error).
          MESSAGE error TYPE 'S' DISPLAY LIKE 'E'.
      ENDTRY.
    
    ENDFORM.
    
    FORM exit.
    
      " The exit logic should only be applied for our 'main' selection screen 1001.
      " All other selection-screens are called as popups and shouldn't influence
      " the gui navigation as it would lead to inpredictable behaviour like dumps.
      IF sy-dynnr <> 1001.
        RETURN.
      ENDIF.
    
      TRY.
          CASE sy-ucomm.
            WHEN 'CBAC' OR 'CCAN'.  "Back & Escape
              IF /apmg/cl_apm_gui_factory=>get_gui( )->back( iv_graceful = abap_true ) = abap_true. " end of stack
                /apmg/cl_apm_gui_factory=>get_gui( )->free( ). " Graceful shutdown
              ELSE.
                LEAVE TO SCREEN 1001.
              ENDIF.
          ENDCASE.
        CATCH cx_root INTO DATA(error).
          MESSAGE error TYPE 'S' DISPLAY LIKE 'E'.
      ENDTRY.
    
    ENDFORM.
    
    FORM adjust_toolbar USING pv_dynnr TYPE sy-dynnr.
    
      DATA:
        header               TYPE rpy_dyhead,
        containers           TYPE dycatt_tab,
        fields_to_containers TYPE dyfatc_tab,
        flow_logic           TYPE swydyflow,
        no_toolbar           LIKE header-no_toolbar.
    
      CALL FUNCTION 'RPY_DYNPRO_READ'
        EXPORTING
          progname             = sy-cprog
          dynnr                = pv_dynnr
        IMPORTING
          header               = header
        TABLES
          containers           = containers
          fields_to_containers = fields_to_containers
          flow_logic           = flow_logic
        EXCEPTIONS
          cancelled            = 1
          not_found            = 2
          permission_error     = 3
          OTHERS               = 4.
      IF sy-subrc IS NOT INITIAL.
        RETURN. " Ignore errors, just exit
      ENDIF.
    
      " Remove toolbar on html screen
      no_toolbar = abap_true.
    
      IF header-no_toolbar = no_toolbar.
        RETURN. " No change required
      ENDIF.
    
      header-no_toolbar = no_toolbar.
    
      CALL FUNCTION 'RPY_DYNPRO_INSERT'
        EXPORTING
          header                 = header
          suppress_exist_checks  = abap_true
        TABLES
          containers             = containers
          fields_to_containers   = fields_to_containers
          flow_logic             = flow_logic
        EXCEPTIONS
          cancelled              = 1
          already_exists         = 2
          program_not_exists     = 3
          not_executed           = 4
          missing_required_field = 5
          illegal_field_value    = 6
          field_not_allowed      = 7
          not_generated          = 8
          illegal_field_position = 9
          OTHERS                 = 10.
      IF sy-subrc <> 2 AND sy-subrc <> 0.
        RETURN. " Ignore errors, just exit
      ENDIF.
    
    ENDFORM.
    
    **********************************************************************
    
    INITIALIZATION.
      PERFORM adjust_toolbar USING '1001'.
      lcl_password_dialog=>on_screen_init( ).
    
    * Hide Execute button from screen
    AT SELECTION-SCREEN OUTPUT.
      IF sy-dynnr = lcl_password_dialog=>c_dynnr.
        lcl_password_dialog=>on_screen_output( ).
      ELSE.
        PERFORM output.
      ENDIF.
    
    * SAP back command re-direction
    AT SELECTION-SCREEN ON EXIT-COMMAND.
      PERFORM exit.
    
    AT SELECTION-SCREEN.
      IF sy-dynnr = lcl_password_dialog=>c_dynnr.
        lcl_password_dialog=>on_screen_event( sscrfields-ucomm ).
      ENDIF.
    
    START-OF-SELECTION.
      PERFORM run.
    
    **********************************************************************
    INTERFACE lif_abapmerge_marker.
      CONSTANTS c_merge_timestamp TYPE string VALUE `2026-02-12T13:15:41Z`.
      CONSTANTS c_abapinst_version TYPE string VALUE `1.2.0`.
    ENDINTERFACE.
    **********************************************************************