CLASS zcl_ags_service_git DEFINITION
  PUBLIC
  CREATE PUBLIC.

  PUBLIC SECTION.

    INTERFACES zif_ags_service.

    METHODS constructor
      IMPORTING
        !ii_server TYPE REF TO if_http_server.
  PROTECTED SECTION.
  PRIVATE SECTION.

    TYPES:
      BEGIN OF ty_push,
        old    TYPE zags_sha1,
        new    TYPE zags_sha1,
        name   TYPE zags_branch_name,
        length TYPE i,
      END OF ty_push.
    TYPES:
      BEGIN OF ty_request,
        want   TYPE STANDARD TABLE OF zags_sha1 WITH DEFAULT KEY,
        deepen TYPE i,
      END OF ty_request.

    DATA mi_server TYPE REF TO if_http_server.

    METHODS branch_list
      RAISING
        zcx_ags_error.
    METHODS decode_push
      IMPORTING
        !iv_data       TYPE xstring
      RETURNING
        VALUE(rs_push) TYPE ty_push
      RAISING
        zcx_ags_error.
    METHODS decode_request
      IMPORTING
        !iv_string        TYPE string
      RETURNING
        VALUE(rs_request) TYPE ty_request.
    METHODS get_null
      RETURNING
        VALUE(rv_char) TYPE char1.
    METHODS pack
      RAISING
        zcx_ags_error.
    METHODS repo_name
      RETURNING
        VALUE(rv_name) TYPE zags_repos-name.
    METHODS unpack
      RAISING
        zcx_ags_error.
    METHODS unpack_ok.
ENDCLASS.

CLASS ZCL_AGS_SERVICE_GIT IMPLEMENTATION.

  METHOD branch_list.

    DEFINE _capability.
      APPEND &1 TO lt_capabilities ##no_text.
    END-OF-DEFINITION.

    DATA: lv_reply        TYPE string,
          lt_reply        TYPE TABLE OF string,
          lv_length       TYPE xstring,
          lv_tmp          TYPE string,
          lt_capabilities TYPE TABLE OF string,
          lv_content      TYPE string,
          lv_utf          TYPE string,
          lv_name         TYPE zags_repos-name,
          lv_head         TYPE zags_sha1,
          lo_repo         TYPE REF TO zcl_ags_repo,
          lt_branches     TYPE zcl_ags_repo=>ty_branches_tt.

    FIELD-SYMBOLS: <lo_branch> LIKE LINE OF lt_branches.

    _capability 'multi_ack'.
    _capability 'thin-pack'.
    _capability 'side-band'.
    _capability 'side-band-64k'.
    _capability 'ofs-delta'.
    _capability 'shallow'.
    _capability 'no-progress'.
    _capability 'include-tag'.
    _capability 'multi_ack_detailed'.
    _capability 'no-done'.
    _capability 'symref=HEAD:refs/heads/master'.
    _capability 'agent=git/abapGitServer'.

    CONCATENATE LINES OF lt_capabilities INTO lv_reply SEPARATED BY space.

    lv_name = repo_name( ).
    CREATE OBJECT lo_repo
      EXPORTING
        iv_name = lv_name.
    lv_head = lo_repo->get_branch( lo_repo->get_data( )-head )->get_data( )-sha1.

    APPEND |001e# service=git-upload-pack| TO lt_reply ##no_text.

    lv_content = |{ lv_head } HEAD{ get_null( ) }{ lv_reply }|.
    lv_length = lcl_length=>encode( strlen( lv_content ) + 5 ).
    lv_utf = to_lower( zcl_ags_util=>xstring_to_string_utf8( lv_length ) ).

    lv_tmp = '0000' && lv_utf && lv_content.
    APPEND lv_tmp TO lt_reply.

    lt_branches = lo_repo->list_branches( ).
    LOOP AT lt_branches ASSIGNING <lo_branch>.
      lv_content = <lo_branch>->get_data( )-sha1
        && ' refs/heads/'
        && <lo_branch>->get_data( )-name ##no_text.

      lv_length = lcl_length=>encode( strlen( lv_content ) + 5 ).
      lv_utf = to_lower( zcl_ags_util=>xstring_to_string_utf8( lv_length ) ).

      lv_tmp = lv_utf && lv_content.
      APPEND lv_tmp TO lt_reply.
    ENDLOOP.

    APPEND '0000' TO lt_reply.

    CONCATENATE LINES OF lt_reply INTO lv_reply
      SEPARATED BY cl_abap_char_utilities=>newline.

    mi_server->response->set_header_field(
      name  = 'Server'
      value = 'abapGitServer' ) ##NO_TEXT.
    mi_server->response->set_header_field(
      name  = 'Cache-Control'
      value = 'no-cache' ) ##NO_TEXT.
    mi_server->response->set_content_type(
      'application/x-git-upload-pack-advertisement' ) ##NO_TEXT.

* must be sent as raw, using data will change the content-type of the response
    DATA(lv_raw) = zcl_ags_util=>string_to_xstring_utf8( lv_reply ).
    mi_server->response->set_data( lv_raw ).

  ENDMETHOD.

  METHOD constructor.

    mi_server = ii_server.

  ENDMETHOD.

  METHOD decode_push.

    DATA: lv_first TYPE xstring,
          lv_utf   TYPE string,
          lv_data  TYPE string,
          lt_data  TYPE TABLE OF string.

    lv_first = iv_data(4).
    lv_utf = zcl_ags_util=>xstring_to_string_utf8( lv_first ).
    rs_push-length = lcl_length=>decode( lv_utf ).

    lv_first = iv_data(rs_push-length).
    lv_data = zcl_ags_util=>xstring_to_string_utf8( lv_first ).
    lv_data = lv_data+4. " skip length, already decoded

    SPLIT lv_data AT get_null( ) INTO TABLE lt_data.
    ASSERT lines( lt_data ) > 0.

    READ TABLE lt_data INDEX 1 INTO lv_data.              "#EC CI_SUBRC

    rs_push-old  = lv_data.
    rs_push-new  = lv_data+41.
    rs_push-name = lv_data+93. " also skip 'refs/heads/'

  ENDMETHOD.

  METHOD decode_request.

    DATA: lv_line    TYPE string,
          lv_command TYPE string,
          lv_rest    TYPE string,
          lt_lines   TYPE TABLE OF string.

    SPLIT iv_string AT cl_abap_char_utilities=>newline INTO TABLE lt_lines.

    LOOP AT lt_lines INTO lv_line.

      SPLIT lv_line AT space INTO lv_command lv_rest.
      IF lv_command(4) = '0000'.
        RETURN.
      ENDIF.

      lv_command = lv_command+4.

      CASE lv_command.
        WHEN 'want'.
          APPEND lv_rest TO rs_request-want.
        WHEN 'deepen'.
          rs_request-deepen = lv_rest.
        WHEN OTHERS.
* todo, unknown command
          ASSERT 0 = 1.
      ENDCASE.

    ENDLOOP.

  ENDMETHOD.

  METHOD get_null.

    DATA: lv_x(4) TYPE x VALUE '00000000',
          lv_z(2) TYPE c.

    FIELD-SYMBOLS <lv_y> TYPE c.

    ASSIGN lv_x TO <lv_y> CASTING.
    lv_z = <lv_y>.
    rv_char = lv_z(1).

  ENDMETHOD.

  METHOD pack.

    CONSTANTS: lc_band1  TYPE x VALUE '01',
               lc_length TYPE i VALUE 8196.

    DATA: lv_response TYPE xstring,
          lv_branch   TYPE zags_sha1,
          lo_commit   TYPE REF TO zcl_ags_obj_commit,
          lv_encoded  TYPE zags_hex4,
          lv_pack     TYPE xstring,
          ls_request  TYPE ty_request,
          lv_length   TYPE i.

    ls_request = decode_request( mi_server->request->get_cdata( ) ).
    lv_branch = ls_request-want[ 1 ]. " todo

    lv_pack = zcl_ags_util=>string_to_xstring_utf8( |NAK\n| ).
    lv_encoded = lcl_length=>encode( xstrlen( lv_pack ) + 4 ).
    CONCATENATE lv_response lv_encoded lv_pack INTO lv_response IN BYTE MODE.

    CREATE OBJECT lo_commit
      EXPORTING
        iv_sha1 = lv_branch.

    lv_pack = zcl_ags_pack=>encode( zcl_ags_pack=>explode(
      ii_object = lo_commit
      iv_deepen = ls_request-deepen ) ).

    WHILE xstrlen( lv_pack ) > 0.
      IF xstrlen( lv_pack ) >= lc_length.
        lv_length = lc_length.
      ELSE.
        lv_length = xstrlen( lv_pack ).
      ENDIF.

* make sure to include the length encoding itself and band identifier in the length
      lv_encoded = lcl_length=>encode( lv_length + 5 ).

      CONCATENATE lv_response lv_encoded lc_band1 lv_pack(lv_length)
        INTO lv_response IN BYTE MODE.

      lv_pack = lv_pack+lv_length.
    ENDWHILE.

    lv_pack = zcl_ags_util=>string_to_xstring_utf8( '0000' ).
    CONCATENATE lv_response lv_pack INTO lv_response IN BYTE MODE.

    mi_server->response->set_data( lv_response ).

  ENDMETHOD.

  METHOD repo_name.

    DATA: lv_path TYPE string.

    lv_path = mi_server->request->get_header_field( '~path' ).
    FIND REGEX 'sap/zgit/git/(.*)\.git*'
      IN lv_path
      SUBMATCHES rv_name ##no_text.

  ENDMETHOD.

  METHOD unpack.

    CONSTANTS: lc_utf_0000 TYPE x LENGTH 4 VALUE '30303030'.

    DATA: lv_xstring TYPE xstring,
          lt_objects TYPE zcl_ags_pack=>ty_objects_tt,
          lo_repo    TYPE REF TO zcl_ags_repo,
          lo_branch  TYPE REF TO zcl_ags_branch,
          ls_push    TYPE ty_push.

    ls_push = decode_push( mi_server->request->get_data( ) ).

    lv_xstring = mi_server->request->get_data( ).
    lv_xstring = lv_xstring+ls_push-length.
    ASSERT lv_xstring(4) = lc_utf_0000.
    lv_xstring = lv_xstring+4.

    lt_objects = zcl_ags_pack=>decode( lv_xstring ).

    CREATE OBJECT lo_repo
      EXPORTING
        iv_name = repo_name( ).

    IF ls_push-old CO '0'.
* create branch
      zcl_ags_branch=>create(
        io_repo   = lo_repo
        iv_name   = ls_push-name
        iv_commit = ls_push-new ).
    ELSEIF ls_push-new CO '0'.
* delete branch
* todo
      ASSERT 0 = 1.
    ELSE.
* update branch

      READ TABLE lt_objects WITH KEY sha1 = ls_push-new TRANSPORTING NO FIELDS.
* new commit should exist in objects
      ASSERT sy-subrc = 0.

      lo_branch = lo_repo->get_branch( ls_push-name ).

      ASSERT lo_branch->get_data( )-sha1 = ls_push-old.

      zcl_ags_pack=>save( lt_objects ).

      lo_branch->update_sha1( ls_push-new ).
    ENDIF.

    unpack_ok( ).

  ENDMETHOD.

  METHOD unpack_ok.

* todo, this is all wrong(but will work in most cases):
    mi_server->response->set_cdata( '000eunpack ok#0019ok refs/heads/master#00000000' ).

  ENDMETHOD.

  METHOD zif_ags_service~run.

    DATA: lv_path  TYPE string,
          lv_xdata TYPE string.

    lv_path = mi_server->request->get_header_field( '~path_info' ).
    lv_xdata = mi_server->request->get_data( ).

    IF lv_xdata IS INITIAL.
      branch_list( ).
    ELSEIF lv_path CP '*git-upload-pack*'.
      pack( ).
    ELSEIF lv_path CP '*git-receive-pack*'.
      unpack( ).
    ELSE.
      RAISE EXCEPTION TYPE zcx_ags_error
        EXPORTING
          textid = zcx_ags_error=>m008.
    ENDIF.

  ENDMETHOD.
ENDCLASS.

CLASS zcl_ags_service_git DEFINITION
  PUBLIC
  CREATE PUBLIC.

  PUBLIC SECTION.

    INTERFACES zif_ags_service.

    METHODS constructor
      IMPORTING
        !ii_server TYPE REF TO if_http_server.
  PROTECTED SECTION.
  PRIVATE SECTION.

    TYPES:
      BEGIN OF ty_push,
        old    TYPE zags_sha1,
        new    TYPE zags_sha1,
        name   TYPE zags_branch_name,
        length TYPE i,
      END OF ty_push.
    TYPES:
      BEGIN OF ty_request,
        want   TYPE STANDARD TABLE OF zags_sha1 WITH DEFAULT KEY,
        deepen TYPE i,
      END OF ty_request.

    DATA mi_server TYPE REF TO if_http_server.

    METHODS branch_list
      RAISING
        zcx_ags_error.
    METHODS decode_push
      IMPORTING
        !iv_data       TYPE xstring
      RETURNING
        VALUE(rs_push) TYPE ty_push
      RAISING
        zcx_ags_error.
    METHODS decode_request
      IMPORTING
        !iv_string        TYPE string
      RETURNING
        VALUE(rs_request) TYPE ty_request.
    METHODS get_null
      RETURNING
        VALUE(rv_char) TYPE char1.
    METHODS pack
      RAISING
        zcx_ags_error.
    METHODS repo_name
      RETURNING
        VALUE(rv_name) TYPE zags_repos-name.
    METHODS unpack
      RAISING
        zcx_ags_error.
    METHODS unpack_ok.
ENDCLASS.

CLASS ZCL_AGS_SERVICE_GIT IMPLEMENTATION.

  METHOD branch_list.

    DEFINE _capability.
      APPEND &1 TO lt_capabilities ##no_text.
    END-OF-DEFINITION.

    DATA: lv_reply        TYPE string,
          lt_reply        TYPE TABLE OF string,
          lv_length       TYPE xstring,
          lv_tmp          TYPE string,
          lt_capabilities TYPE TABLE OF string,
          lv_content      TYPE string,
          lv_utf          TYPE string,
          lv_name         TYPE zags_repos-name,
          lv_head         TYPE zags_sha1,
          lo_repo         TYPE REF TO zcl_ags_repo,
          lt_branches     TYPE zcl_ags_repo=>ty_branches_tt.

    FIELD-SYMBOLS: <lo_branch> LIKE LINE OF lt_branches.

    _capability 'multi_ack'.
    _capability 'thin-pack'.
    _capability 'side-band'.
    _capability 'side-band-64k'.
    _capability 'ofs-delta'.
    _capability 'shallow'.
    _capability 'no-progress'.
    _capability 'include-tag'.
    _capability 'multi_ack_detailed'.
    _capability 'no-done'.
    _capability 'symref=HEAD:refs/heads/master'.
    _capability 'agent=git/abapGitServer'.

    CONCATENATE LINES OF lt_capabilities INTO lv_reply SEPARATED BY space.

    lv_name = repo_name( ).
    CREATE OBJECT lo_repo
      EXPORTING
        iv_name = lv_name.
    lv_head = lo_repo->get_branch( lo_repo->get_data( )-head )->get_data( )-sha1.

    APPEND |001e# service=git-upload-pack| TO lt_reply ##no_text.

    lv_content = |{ lv_head } HEAD{ get_null( ) }{ lv_reply }|.
    lv_length = lcl_length=>encode( strlen( lv_content ) + 5 ).
    lv_utf = to_lower( zcl_ags_util=>xstring_to_string_utf8( lv_length ) ).

    lv_tmp = '0000' && lv_utf && lv_content.
    APPEND lv_tmp TO lt_reply.

    lt_branches = lo_repo->list_branches( ).
    LOOP AT lt_branches ASSIGNING <lo_branch>.
      lv_content = <lo_branch>->get_data( )-sha1
        && ' refs/heads/'
        && <lo_branch>->get_data( )-name ##no_text.

      lv_length = lcl_length=>encode( strlen( lv_content ) + 5 ).
      lv_utf = to_lower( zcl_ags_util=>xstring_to_string_utf8( lv_length ) ).

      lv_tmp = lv_utf && lv_content.
      APPEND lv_tmp TO lt_reply.
    ENDLOOP.

    APPEND '0000' TO lt_reply.

    CONCATENATE LINES OF lt_reply INTO lv_reply
      SEPARATED BY cl_abap_char_utilities=>newline.

    mi_server->response->set_header_field(
      name  = 'Server'
      value = 'abapGitServer' ) ##NO_TEXT.
    mi_server->response->set_header_field(
      name  = 'Cache-Control'
      value = 'no-cache' ) ##NO_TEXT.
    mi_server->response->set_content_type(
      'application/x-git-upload-pack-advertisement' ) ##NO_TEXT.

* must be sent as raw, using data will change the content-type of the response
    DATA(lv_raw) = zcl_ags_util=>string_to_xstring_utf8( lv_reply ).
    mi_server->response->set_data( lv_raw ).

  ENDMETHOD.

  METHOD constructor.

    mi_server = ii_server.

  ENDMETHOD.

  METHOD decode_push.

    DATA: lv_first TYPE xstring,
          lv_utf   TYPE string,
          lv_data  TYPE string,
          lt_data  TYPE TABLE OF string.

    lv_first = iv_data(4).
    lv_utf = zcl_ags_util=>xstring_to_string_utf8( lv_first ).
    rs_push-length = lcl_length=>decode( lv_utf ).

    lv_first = iv_data(rs_push-length).
    lv_data = zcl_ags_util=>xstring_to_string_utf8( lv_first ).
    lv_data = lv_data+4. " skip length, already decoded

    SPLIT lv_data AT get_null( ) INTO TABLE lt_data.
    ASSERT lines( lt_data ) > 0.

    READ TABLE lt_data INDEX 1 INTO lv_data.              "#EC CI_SUBRC

    rs_push-old  = lv_data.
    rs_push-new  = lv_data+41.
    rs_push-name = lv_data+93. " also skip 'refs/heads/'

  ENDMETHOD.

  METHOD decode_request.

    DATA: lv_line    TYPE string,
          lv_command TYPE string,
          lv_rest    TYPE string,
          lt_lines   TYPE TABLE OF string.

    SPLIT iv_string AT cl_abap_char_utilities=>newline INTO TABLE lt_lines.

    LOOP AT lt_lines INTO lv_line.

      SPLIT lv_line AT space INTO lv_command lv_rest.
      IF lv_command(4) = '0000'.
        RETURN.
      ENDIF.

      lv_command = lv_command+4.

      CASE lv_command.
        WHEN 'want'.
          APPEND lv_rest TO rs_request-want.
        WHEN 'deepen'.
          rs_request-deepen = lv_rest.
        WHEN OTHERS.
* todo, unknown command
          ASSERT 0 = 1.
      ENDCASE.

    ENDLOOP.

    SORT rs_request-want ASCENDING.
    DELETE ADJACENT DUPLICATES FROM rs_request-want.

  ENDMETHOD.

  METHOD get_null.

    DATA: lv_x(4) TYPE x VALUE '00000000',
          lv_z(2) TYPE c.

    FIELD-SYMBOLS <lv_y> TYPE c.

    ASSIGN lv_x TO <lv_y> CASTING.
    lv_z = <lv_y>.
    rv_char = lv_z(1).

  ENDMETHOD.

  METHOD pack.

    CONSTANTS: lc_band1  TYPE x VALUE '01',
               lc_length TYPE i VALUE 8196.

    DATA: lv_response TYPE xstring,
          lo_commit   TYPE REF TO zcl_ags_obj_commit,
          lv_encoded  TYPE zags_hex4,
          lt_objects  TYPE zcl_ags_pack=>ty_objects_tt,
          lv_pack     TYPE xstring,
          ls_request  TYPE ty_request,
          lv_branch   LIKE LINE OF ls_request-want,
          lv_length   TYPE i.

    ls_request = decode_request( mi_server->request->get_cdata( ) ).

    lv_pack = zcl_ags_util=>string_to_xstring_utf8( |NAK\n| ).
    lv_encoded = lcl_length=>encode( xstrlen( lv_pack ) + 4 ).
    CONCATENATE lv_response lv_encoded lv_pack INTO lv_response IN BYTE MODE.

    LOOP AT ls_request-want INTO lv_branch.
      CREATE OBJECT lo_commit
        EXPORTING
          iv_sha1 = lv_branch.

      APPEND LINES OF zcl_ags_pack=>explode(
        ii_object = lo_commit
        iv_deepen = ls_request-deepen ) TO lt_objects.
    ENDLOOP.

    SORT lt_objects BY type ASCENDING sha1 ASCENDING.
    DELETE ADJACENT DUPLICATES FROM lt_objects COMPARING type sha1.

    lv_pack = zcl_ags_pack=>encode( lt_objects ).

    WHILE xstrlen( lv_pack ) > 0.
      IF xstrlen( lv_pack ) >= lc_length.
        lv_length = lc_length.
      ELSE.
        lv_length = xstrlen( lv_pack ).
      ENDIF.

* make sure to include the length encoding itself and band identifier in the length
      lv_encoded = lcl_length=>encode( lv_length + 5 ).

      CONCATENATE lv_response lv_encoded lc_band1 lv_pack(lv_length)
        INTO lv_response IN BYTE MODE.

      lv_pack = lv_pack+lv_length.
    ENDWHILE.

    lv_pack = zcl_ags_util=>string_to_xstring_utf8( '0000' ).
    CONCATENATE lv_response lv_pack INTO lv_response IN BYTE MODE.

    mi_server->response->set_data( lv_response ).

  ENDMETHOD.

  METHOD repo_name.

    DATA: lv_path TYPE string.

    lv_path = mi_server->request->get_header_field( '~path' ).
    FIND REGEX 'sap/zgit/git/(.*)\.git*'
      IN lv_path
      SUBMATCHES rv_name ##no_text.

  ENDMETHOD.

  METHOD unpack.

    CONSTANTS: lc_utf_0000 TYPE x LENGTH 4 VALUE '30303030'.

    DATA: lv_xstring TYPE xstring,
          lt_objects TYPE zcl_ags_pack=>ty_objects_tt,
          lo_repo    TYPE REF TO zcl_ags_repo,
          lo_branch  TYPE REF TO zcl_ags_branch,
          ls_push    TYPE ty_push.

    ls_push = decode_push( mi_server->request->get_data( ) ).

    lv_xstring = mi_server->request->get_data( ).
    lv_xstring = lv_xstring+ls_push-length.
    ASSERT lv_xstring(4) = lc_utf_0000.
    lv_xstring = lv_xstring+4.

    lt_objects = zcl_ags_pack=>decode( lv_xstring ).

    CREATE OBJECT lo_repo
      EXPORTING
        iv_name = repo_name( ).

    IF ls_push-old CO '0'.
* create branch
      zcl_ags_branch=>create(
        io_repo   = lo_repo
        iv_name   = ls_push-name
        iv_commit = ls_push-new ).
    ELSEIF ls_push-new CO '0'.
* delete branch
* todo
      ASSERT 0 = 1.
    ELSE.
* update branch

      READ TABLE lt_objects WITH KEY sha1 = ls_push-new TRANSPORTING NO FIELDS.
* new commit should exist in objects
      ASSERT sy-subrc = 0.

      lo_branch = lo_repo->get_branch( ls_push-name ).

      ASSERT lo_branch->get_data( )-sha1 = ls_push-old.

      zcl_ags_pack=>save( lt_objects ).

      lo_branch->update_sha1( ls_push-new ).
    ENDIF.

    unpack_ok( ).

  ENDMETHOD.

  METHOD unpack_ok.

* todo, this is all wrong(but will work in most cases):
    mi_server->response->set_cdata( '000eunpack ok#0019ok refs/heads/master#00000000' ).

  ENDMETHOD.

  METHOD zif_ags_service~run.

    DATA: lv_path  TYPE string,
          lv_xdata TYPE string.

    lv_path = mi_server->request->get_header_field( '~path_info' ).
    lv_xdata = mi_server->request->get_data( ).

    IF lv_xdata IS INITIAL.
      branch_list( ).
    ELSEIF lv_path CP '*git-upload-pack*'.
      pack( ).
    ELSEIF lv_path CP '*git-receive-pack*'.
      unpack( ).
    ELSE.
      RAISE EXCEPTION TYPE zcx_ags_error
        EXPORTING
          textid = zcx_ags_error=>m008.
    ENDIF.

  ENDMETHOD.
ENDCLASS.

implement multiple want

implement multiple want


CLASS zcl_abapgit_lxe_texts DEFINITION
  PUBLIC
  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.

    CLASS-DATA gt_installed_languages_cache TYPE zif_abapgit_definitions=>ty_languages.

    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 .
    CLASS-METHODS
      langu_to_laiso_safe
        IMPORTING
          iv_langu TYPE sy-langu
        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_lxe_texts IMPLEMENTATION.

  METHOD check_langs_versus_installed.

    DATA lt_installed_hash TYPE HASHED TABLE OF laiso WITH UNIQUE KEY table_line.
    FIELD-SYMBOLS <lv_lang> LIKE LINE OF it_languages.

    CLEAR: et_intersection, et_missfits.
    lt_installed_hash = it_installed.

    LOOP AT it_languages ASSIGNING <lv_lang>.
      READ TABLE lt_installed_hash WITH KEY table_line = <lv_lang> TRANSPORTING NO FIELDS.
      IF sy-subrc = 0.
        APPEND <lv_lang> TO et_intersection.
      ELSE.
        APPEND <lv_lang> 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:
      <lv_str>  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_str>.
      lv_laiso = condense( to_upper( <lv_str> ) ).
      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:
      <lv_lang> LIKE LINE OF it_languages,
      <lv_str>  TYPE string.

    " Convert table of sy-langu codes into string of 2-letter ISO languages
    LOOP AT it_languages ASSIGNING <lv_lang>.
      " Keep * as indicator for 'all installed languages'
      IF <lv_lang> = '*'.
        CLEAR lt_langs_str.
        APPEND '*' TO lt_langs_str.
        EXIT.
      ENDIF.

      APPEND INITIAL LINE TO lt_langs_str ASSIGNING <lv_str>.
      <lv_str> = <lv_lang>.
    ENDLOOP.

    CONCATENATE LINES OF lt_langs_str INTO rv_langs SEPARATED BY ','.

  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.

    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.

      DO strlen( lv_installed_languages ) TIMES.
        lv_index = sy-index - 1.
        lv_langu = lv_installed_languages+lv_index(1).
        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 lv_lang_iso639 TYPE i18_a_langiso2.
    DATA lv_country TYPE land1.

    cl_i18n_languages=>sap2_to_iso639_1(
      EXPORTING
        im_lang_sap2   = iv_src
      IMPORTING
        ex_lang_iso639 = lv_lang_iso639
        ex_country     = lv_country
      EXCEPTIONS
        no_assignment  = 1
        OTHERS         = 2 ).
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Failed to convert [{ iv_src }] lang to iso639| ).
    ENDIF.

    CONCATENATE lv_lang_iso639 lv_country INTO rv_iso4.

  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 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 langu_to_laiso_safe.

    cl_i18n_languages=>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 zif_abapgit_lxe_texts~deserialize.

    DATA:
      lt_lxe_texts      TYPE zif_abapgit_lxe_texts=>ty_tlxe_i18n,
      ls_lxe_item       TYPE zif_abapgit_lxe_texts=>ty_lxe_i18n,
      lv_error          TYPE lxestring,
      lt_text_pairs_tmp LIKE ls_lxe_item-text_pairs.

    ii_xml->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
      CLEAR: lt_text_pairs_tmp.
      CALL FUNCTION 'LXE_OBJ_TEXT_PAIR_READ'
        EXPORTING
          s_lang    = ls_lxe_item-source_lang
          t_lang    = ls_lxe_item-target_lang
          custmnr   = ls_lxe_item-custmnr
          objtype   = ls_lxe_item-objtype
          objname   = ls_lxe_item-objname
          read_only = abap_false
        IMPORTING
          err_msg   = lv_error
        TABLES
          lt_pcx_s1 = lt_text_pairs_tmp.
      IF lv_error IS NOT INITIAL.
        zcx_abapgit_exception=>raise( lv_error ).
      ENDIF.

      "Call actual Write FM
      CALL FUNCTION 'LXE_OBJ_TEXT_PAIR_WRITE'
        EXPORTING
          s_lang    = ls_lxe_item-source_lang
          t_lang    = ls_lxe_item-target_lang
          custmnr   = ls_lxe_item-custmnr
          objtype   = ls_lxe_item-objtype
          objname   = ls_lxe_item-objname
        IMPORTING
          err_msg   = lv_error
        TABLES
          lt_pcx_s1 = ls_lxe_item-text_pairs.
      IF lv_error IS NOT INITIAL.
        zcx_abapgit_exception=>raise( lv_error ).
      ENDIF.
    ENDLOOP.

  ENDMETHOD.

  METHOD zif_abapgit_lxe_texts~serialize.

    DATA:
      lt_obj_list      TYPE lxe_tt_colob,
      lv_main_lang     TYPE lxeisolang,
      lv_error         TYPE lxestring,
      lt_languages     TYPE zif_abapgit_definitions=>ty_languages,
      lt_lxe_texts     TYPE zif_abapgit_lxe_texts=>ty_tlxe_i18n,
      ls_lxe_text_item TYPE zif_abapgit_lxe_texts=>ty_lxe_i18n.

    FIELD-SYMBOLS:
      <lv_language>   LIKE LINE OF lt_languages,
      <lv_lxe_object> 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( ii_xml->i18n_params( )-main_language ) ).
    lt_languages = ii_xml->i18n_params( )-translation_languages.

    LOOP AT lt_obj_list ASSIGNING <lv_lxe_object>.
      CLEAR ls_lxe_text_item.
      ls_lxe_text_item-custmnr = <lv_lxe_object>-custmnr.
      ls_lxe_text_item-objtype = <lv_lxe_object>-objtype.
      ls_lxe_text_item-objname = <lv_lxe_object>-objname.

      LOOP AT lt_languages ASSIGNING <lv_language>.
        ls_lxe_text_item-source_lang = lv_main_lang.
        ls_lxe_text_item-target_lang = get_lang_iso4( <lv_language> ).
        IF ls_lxe_text_item-source_lang = ls_lxe_text_item-target_lang.
          CONTINUE. " if source = target -> skip
        ENDIF.

        CLEAR ls_lxe_text_item-text_pairs.
        CALL FUNCTION 'LXE_OBJ_TEXT_PAIR_READ'
          EXPORTING
            s_lang    = ls_lxe_text_item-source_lang
            t_lang    = ls_lxe_text_item-target_lang
            custmnr   = ls_lxe_text_item-custmnr
            objtype   = ls_lxe_text_item-objtype
            objname   = ls_lxe_text_item-objname
          IMPORTING
            err_msg   = lv_error
          TABLES
            lt_pcx_s1 = ls_lxe_text_item-text_pairs.
        IF lv_error IS NOT INITIAL.
          zcx_abapgit_exception=>raise( lv_error ).
        ENDIF.

        IF ls_lxe_text_item-text_pairs IS NOT INITIAL.
          APPEND ls_lxe_text_item TO lt_lxe_texts.
        ENDIF.
      ENDLOOP.
    ENDLOOP.

    ii_xml->add( iv_name = iv_lxe_text_name
                 ig_data = lt_lxe_texts ).

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_lxe_texts DEFINITION
  PUBLIC
  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.

    CLASS-DATA gt_installed_languages_cache TYPE zif_abapgit_definitions=>ty_languages.

    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 .
    CLASS-METHODS
      langu_to_laiso_safe
        IMPORTING
          iv_langu TYPE sy-langu
        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_lxe_texts IMPLEMENTATION.

  METHOD check_langs_versus_installed.

    DATA lt_installed_hash TYPE HASHED TABLE OF laiso WITH UNIQUE KEY table_line.
    FIELD-SYMBOLS <lv_lang> LIKE LINE OF it_languages.

    CLEAR: et_intersection, et_missfits.
    lt_installed_hash = it_installed.

    LOOP AT it_languages ASSIGNING <lv_lang>.
      READ TABLE lt_installed_hash WITH KEY table_line = <lv_lang> TRANSPORTING NO FIELDS.
      IF sy-subrc = 0.
        APPEND <lv_lang> TO et_intersection.
      ELSE.
        APPEND <lv_lang> 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:
      <lv_str>  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_str>.
      lv_laiso = condense( to_upper( <lv_str> ) ).
      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:
      <lv_lang> LIKE LINE OF it_languages,
      <lv_str>  TYPE string.

    " Convert table of sy-langu codes into string of 2-letter ISO languages
    LOOP AT it_languages ASSIGNING <lv_lang>.
      " Keep * as indicator for 'all installed languages'
      IF <lv_lang> = '*'.
        CLEAR lt_langs_str.
        APPEND '*' TO lt_langs_str.
        EXIT.
      ENDIF.

      APPEND INITIAL LINE TO lt_langs_str ASSIGNING <lv_str>.
      <lv_str> = <lv_lang>.
    ENDLOOP.

    CONCATENATE LINES OF lt_langs_str INTO rv_langs SEPARATED BY ','.

  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 lv_lang_iso639 TYPE i18_a_langiso2.
    DATA lv_country TYPE land1.

    cl_i18n_languages=>sap2_to_iso639_1(
      EXPORTING
        im_lang_sap2   = iv_src
      IMPORTING
        ex_lang_iso639 = lv_lang_iso639
        ex_country     = lv_country
      EXCEPTIONS
        no_assignment  = 1
        OTHERS         = 2 ).
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Failed to convert [{ iv_src }] lang to iso639| ).
    ENDIF.

    CONCATENATE lv_lang_iso639 lv_country INTO rv_iso4.

  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 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 langu_to_laiso_safe.

    cl_i18n_languages=>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 zif_abapgit_lxe_texts~deserialize.

    DATA:
      lt_lxe_texts      TYPE zif_abapgit_lxe_texts=>ty_tlxe_i18n,
      ls_lxe_item       TYPE zif_abapgit_lxe_texts=>ty_lxe_i18n,
      lv_error          TYPE lxestring,
      lt_text_pairs_tmp LIKE ls_lxe_item-text_pairs.

    ii_xml->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
      CLEAR: lt_text_pairs_tmp.
      CALL FUNCTION 'LXE_OBJ_TEXT_PAIR_READ'
        EXPORTING
          s_lang    = ls_lxe_item-source_lang
          t_lang    = ls_lxe_item-target_lang
          custmnr   = ls_lxe_item-custmnr
          objtype   = ls_lxe_item-objtype
          objname   = ls_lxe_item-objname
          read_only = abap_false
        IMPORTING
          err_msg   = lv_error
        TABLES
          lt_pcx_s1 = lt_text_pairs_tmp.
      IF lv_error IS NOT INITIAL.
        zcx_abapgit_exception=>raise( lv_error ).
      ENDIF.

      "Call actual Write FM
      CALL FUNCTION 'LXE_OBJ_TEXT_PAIR_WRITE'
        EXPORTING
          s_lang    = ls_lxe_item-source_lang
          t_lang    = ls_lxe_item-target_lang
          custmnr   = ls_lxe_item-custmnr
          objtype   = ls_lxe_item-objtype
          objname   = ls_lxe_item-objname
        IMPORTING
          err_msg   = lv_error
        TABLES
          lt_pcx_s1 = ls_lxe_item-text_pairs.
      IF lv_error IS NOT INITIAL.
        zcx_abapgit_exception=>raise( lv_error ).
      ENDIF.
    ENDLOOP.

  ENDMETHOD.

  METHOD zif_abapgit_lxe_texts~serialize.

    DATA:
      lt_obj_list      TYPE lxe_tt_colob,
      lv_main_lang     TYPE lxeisolang,
      lv_error         TYPE lxestring,
      lt_languages     TYPE zif_abapgit_definitions=>ty_languages,
      lt_lxe_texts     TYPE zif_abapgit_lxe_texts=>ty_tlxe_i18n,
      ls_lxe_text_item TYPE zif_abapgit_lxe_texts=>ty_lxe_i18n.

    FIELD-SYMBOLS:
      <lv_language>   LIKE LINE OF lt_languages,
      <lv_lxe_object> 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( ii_xml->i18n_params( )-main_language ) ).
    lt_languages = ii_xml->i18n_params( )-translation_languages.

    LOOP AT lt_obj_list ASSIGNING <lv_lxe_object>.
      CLEAR ls_lxe_text_item.
      ls_lxe_text_item-custmnr = <lv_lxe_object>-custmnr.
      ls_lxe_text_item-objtype = <lv_lxe_object>-objtype.
      ls_lxe_text_item-objname = <lv_lxe_object>-objname.

      LOOP AT lt_languages ASSIGNING <lv_language>.
        ls_lxe_text_item-source_lang = lv_main_lang.
        ls_lxe_text_item-target_lang = get_lang_iso4( <lv_language> ).
        IF ls_lxe_text_item-source_lang = ls_lxe_text_item-target_lang.
          CONTINUE. " if source = target -> skip
        ENDIF.

        CLEAR ls_lxe_text_item-text_pairs.
        CALL FUNCTION 'LXE_OBJ_TEXT_PAIR_READ'
          EXPORTING
            s_lang    = ls_lxe_text_item-source_lang
            t_lang    = ls_lxe_text_item-target_lang
            custmnr   = ls_lxe_text_item-custmnr
            objtype   = ls_lxe_text_item-objtype
            objname   = ls_lxe_text_item-objname
          IMPORTING
            err_msg   = lv_error
          TABLES
            lt_pcx_s1 = ls_lxe_text_item-text_pairs.
        IF lv_error IS NOT INITIAL.
          zcx_abapgit_exception=>raise( lv_error ).
        ENDIF.

        IF ls_lxe_text_item-text_pairs IS NOT INITIAL.
          APPEND ls_lxe_text_item TO lt_lxe_texts.
        ENDIF.
      ENDLOOP.
    ENDLOOP.

    ii_xml->add( iv_name = iv_lxe_text_name
                 ig_data = lt_lxe_texts ).

  ENDMETHOD.
ENDCLASS.

Exclude technical languages (#5601)

Exclude technical languages (#5601)

from get_installed_languages in lxe_texts


*&---------------------------------------------------------------------*
*&  Include  zabapgit_object_dcls
*&---------------------------------------------------------------------*

CLASS lcl_object_dcls DEFINITION INHERITING FROM lcl_objects_super FINAL.

  PUBLIC SECTION.
    INTERFACES lif_object.
    ALIASES mo_files FOR lif_object~mo_files.

ENDCLASS.

CLASS lcl_object_dcls IMPLEMENTATION.

  METHOD lif_object~has_changed_since.
    rv_changed = abap_true.
  ENDMETHOD.

  METHOD lif_object~changed_by.
    rv_user = c_user_unknown.
  ENDMETHOD.

  METHOD lif_object~get_metadata.
    rs_metadata = get_metadata( ).

    rs_metadata-ddic         = abap_true.
    rs_metadata-delete_tadir = abap_true.
  ENDMETHOD.

  METHOD lif_object~exists.

    DATA: li_dcl TYPE REF TO object.

    TRY.
        CALL METHOD ('CL_ACM_DCL_HANDLER_FACTORY')=>('CREATE')
          RECEIVING
            ro_handler = li_dcl.

        CALL METHOD li_dcl->('CHECK_EXISTENCE')
          EXPORTING
            iv_objectname = ms_item-obj_name
          RECEIVING
            rv_exists     = rv_bool.

      CATCH cx_root.
        rv_bool = abap_false.
    ENDTRY.

  ENDMETHOD.

  METHOD lif_object~jump.

    DATA: adt_link          TYPE string,
          obj_name          TYPE e071-obj_name,
          li_object         TYPE REF TO cl_wb_object,
          li_adt            TYPE REF TO object,
          li_adt_uri_mapper TYPE REF TO object,
          li_adt_objref     TYPE REF TO object.

    FIELD-SYMBOLS: <uri> TYPE string.

    TRY.

        obj_name = ms_item-obj_name.

        li_object = cl_wb_object=>create_from_transport_key( p_object = ms_item-obj_type p_obj_name = obj_name ).

        CALL METHOD ('CL_ADT_TOOLS_CORE_FACTORY')=>('GET_INSTANCE')
          RECEIVING
            result = li_adt.

        CALL METHOD li_adt->('IF_ADT_TOOLS_CORE_FACTORY~GET_URI_MAPPER')
          RECEIVING
            result = li_adt_uri_mapper.

        CALL METHOD li_adt_uri_mapper->('IF_ADT_URI_MAPPER~MAP_WB_OBJECT_TO_OBJREF')
          EXPORTING
            wb_object = li_object
          RECEIVING
            result    = li_adt_objref.

        ASSIGN ('li_adt_objref->ref_data-uri') TO <uri>.

        CONCATENATE 'adt://' sy-sysid <uri> INTO adt_link.

        cl_gui_frontend_services=>execute( EXPORTING  document = adt_link
                                           EXCEPTIONS OTHERS   = 1 ).

      CATCH cx_root.
        lcx_exception=>raise( 'DCLS Jump Error' ).
    ENDTRY.

  ENDMETHOD.

  METHOD lif_object~delete.

    DATA: li_dcl TYPE REF TO object.

    TRY.
        CALL METHOD ('CL_ACM_DCL_HANDLER_FACTORY')=>('CREATE')
          RECEIVING
            ro_handler = li_dcl.

        CALL METHOD li_dcl->('DELETE')
          EXPORTING
            iv_dclname = ms_item-obj_name.

      CATCH cx_root.
        lcx_exception=>raise( 'DCLS error' ).
    ENDTRY.

  ENDMETHOD.

  METHOD lif_object~serialize.

    DATA: lr_data TYPE REF TO data,
          li_dcl  TYPE REF TO object.

    FIELD-SYMBOLS: <ls_data> TYPE any,
                   <field>   TYPE any.

    CREATE DATA lr_data TYPE ('ACM_S_DCLSRC').
    ASSIGN lr_data->* TO <ls_data>.

    TRY.
        CALL METHOD ('CL_ACM_DCL_HANDLER_FACTORY')=>('CREATE')
          RECEIVING
            ro_handler = li_dcl.

        CALL METHOD li_dcl->('READ')
          EXPORTING
            iv_dclname = ms_item-obj_name
          IMPORTING
            es_dclsrc  = <ls_data>.

        ASSIGN COMPONENT 'AS4USER' OF STRUCTURE <ls_data> TO <field>.
        ASSERT sy-subrc = 0.
        CLEAR <field>.

        ASSIGN COMPONENT 'AS4DATE' OF STRUCTURE <ls_data> TO <field>.
        ASSERT sy-subrc = 0.
        CLEAR <field>.

        ASSIGN COMPONENT 'AS4TIME' OF STRUCTURE <ls_data> TO <field>.
        ASSERT sy-subrc = 0.
        CLEAR <field>.

        ASSIGN COMPONENT 'CREATED_BY'
               OF STRUCTURE <ls_data>
               TO <field>.
        ASSERT sy-subrc = 0.
        CLEAR <field>.

        ASSIGN COMPONENT 'CREATED_DATE' OF STRUCTURE <ls_data> TO <field>.
        ASSERT sy-subrc = 0.
        CLEAR <field>.

        ASSIGN COMPONENT 'SOURCE' OF STRUCTURE <ls_data> TO <field>.
        ASSERT sy-subrc = 0.

        mo_files->add_string( iv_ext = 'asdcls'  iv_string = <field> ).

        CLEAR <field>.

        io_xml->add( iv_name = 'DCLS'
                     ig_data = <ls_data> ).

      CATCH cx_root.
        lcx_exception=>raise( 'DCLS error' ).
    ENDTRY.

  ENDMETHOD.

  METHOD lif_object~deserialize.

    DATA: lr_data TYPE REF TO data,
          li_dcl  TYPE REF TO object.

    FIELD-SYMBOLS: <ls_data> TYPE any,
                   <field>   TYPE any.

    CREATE DATA lr_data TYPE ('ACM_S_DCLSRC').
    ASSIGN lr_data->* TO <ls_data>.

    io_xml->read(
      EXPORTING
        iv_name = 'DCLS'
      CHANGING
        cg_data = <ls_data> ).

    ASSIGN COMPONENT 'SOURCE' OF STRUCTURE <ls_data> TO <field>.
    ASSERT sy-subrc = 0.
    <field> = mo_files->read_string( 'asdcls' ).

    TRY.
        CALL METHOD ('CL_ACM_DCL_HANDLER_FACTORY')=>('CREATE')
          RECEIVING
            ro_handler = li_dcl.

        CALL METHOD li_dcl->('SAVE')
          EXPORTING
            iv_dclname     = ms_item-obj_name
            iv_put_state   = 'A'
            is_dclsrc      = <ls_data>
            iv_devclass    = iv_package
            iv_access_mode = 'INSERT'.

        tadir_insert( iv_package ).

      CATCH cx_root.
        lcx_exception=>raise( 'DCLS error' ).
    ENDTRY.

    lcl_objects_activation=>add_item( ms_item ).

  ENDMETHOD.

  METHOD lif_object~compare_to_remote_version.
    CREATE OBJECT ro_comparison_result TYPE lcl_null_comparison_result.
  ENDMETHOD.

ENDCLASS.

Update zabapgit_object_dcls.prog.abap

Update zabapgit_object_dcls.prog.abap


CLASS zcl_abapgit_transport DEFINITION
  PUBLIC
  CREATE PUBLIC .

  PUBLIC SECTION.

    CLASS-METHODS zip
      IMPORTING
        !iv_show_log_popup TYPE abap_bool DEFAULT abap_true
        !iv_logic          TYPE string OPTIONAL
        !is_trkorr         TYPE trwbo_request_header OPTIONAL
      RETURNING
        VALUE(rv_xstr)     TYPE xstring
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS to_tadir
      IMPORTING
        it_transport_headers TYPE trwbo_request_headers
      RETURNING
        VALUE(rt_tadir)      TYPE zif_abapgit_definitions=>ty_tadir_tt
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS add_all_objects_to_trans_req
      IMPORTING
        iv_key TYPE zif_abapgit_persistence=>ty_value
      RAISING
        zcx_abapgit_exception .
  PROTECTED SECTION.

    CLASS-METHODS read_requests
      IMPORTING
        !it_trkorr         TYPE trwbo_request_headers
      RETURNING
        VALUE(rt_requests) TYPE trwbo_requests
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS find_top_package
      IMPORTING
        !it_tadir         TYPE zif_abapgit_definitions=>ty_tadir_tt
      RETURNING
        VALUE(rv_package) TYPE devclass
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS resolve
      IMPORTING
        !it_requests    TYPE trwbo_requests
      RETURNING
        VALUE(rt_tadir) TYPE zif_abapgit_definitions=>ty_tadir_tt
      RAISING
        zcx_abapgit_exception .
  PRIVATE SECTION.
    CLASS-METHODS collect_all_objects
      IMPORTING
        iv_key            TYPE zif_abapgit_persistence=>ty_value
      RETURNING
        VALUE(rt_objects) TYPE tr_objects
      RAISING
        zcx_abapgit_exception.
ENDCLASS.

CLASS zcl_abapgit_transport IMPLEMENTATION.

  METHOD add_all_objects_to_trans_req.

    DATA:
      ls_request      TYPE trwbo_request_header,
      lt_e071         TYPE tr_objects,
      lv_text         TYPE string,
      lv_answer       TYPE c LENGTH 1,
      lv_lock_objects TYPE trparflag.

    lv_answer = zcl_abapgit_ui_factory=>get_popups( )->popup_to_confirm(
                    iv_titlebar              = `Lock objects?`
                    iv_text_question         = `Shall all objects be locked in the transport request?`
                    iv_display_cancel_button = abap_true ).

    CASE lv_answer.
      WHEN '1'.
        lv_lock_objects = abap_true.
      WHEN '2'.
        lv_lock_objects = abap_false.
      WHEN OTHERS.
        RETURN.
    ENDCASE.

    lt_e071 = collect_all_objects( iv_key ).

    CALL FUNCTION 'TR_REQUEST_CHOICE'
      EXPORTING
        it_e071              = lt_e071
        iv_lock_objects      = lv_lock_objects
      IMPORTING
        es_request           = ls_request
      EXCEPTIONS
        invalid_request      = 1
        invalid_request_type = 2
        user_not_owner       = 3
        no_objects_appended  = 4
        enqueue_error        = 5
        cancelled_by_user    = 6
        recursive_call       = 7
        OTHERS               = 8.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise_t100( ).
    ENDIF.

    lv_text = |Objects successfully added to { ls_request-trkorr }|.
    MESSAGE lv_text TYPE 'S'.

  ENDMETHOD.

  METHOD collect_all_objects.

    DATA:
      lt_objects     TYPE scts_tadir,
      lt_objects_all LIKE lt_objects,
      ls_e071        LIKE LINE OF rt_objects,
      lo_repo        TYPE REF TO zcl_abapgit_repo,
      lv_package     TYPE zif_abapgit_persistence=>ty_repo-package,
      lt_packages    TYPE zif_abapgit_sap_package=>ty_devclass_tt.

    FIELD-SYMBOLS:
      <lv_package> TYPE devclass,
      <ls_object>  TYPE tadir.

    lo_repo     = zcl_abapgit_repo_srv=>get_instance( )->get( iv_key ).
    lv_package  = lo_repo->get_package( ).
    lt_packages = zcl_abapgit_factory=>get_sap_package( lv_package )->list_subpackages( ).
    INSERT lv_package INTO TABLE lt_packages.

    LOOP AT lt_packages ASSIGNING <lv_package>.

      CLEAR: lt_objects.

      CALL FUNCTION 'TRINT_SELECT_OBJECTS'
        EXPORTING
          iv_devclass       = <lv_package>
          iv_via_selscreen  = abap_false
        IMPORTING
          et_objects_tadir  = lt_objects
        EXCEPTIONS
          cancelled_by_user = 1
          invalid_input     = 2
          OTHERS            = 3.

      IF sy-subrc <> 0.
        zcx_abapgit_exception=>raise_t100( ).
      ENDIF.

      INSERT LINES OF lt_objects INTO TABLE lt_objects_all.

    ENDLOOP.

    IF lines( lt_objects_all ) = 0.
      zcx_abapgit_exception=>raise( |No objects found| ).
    ENDIF.

    LOOP AT lt_objects_all ASSIGNING <ls_object>.

      CLEAR: ls_e071.

      MOVE-CORRESPONDING <ls_object> TO ls_e071.
      INSERT ls_e071 INTO TABLE rt_objects.

    ENDLOOP.

  ENDMETHOD.

  METHOD find_top_package.
* assumption: all objects in transport share a common super package

    DATA: lt_obj   TYPE zif_abapgit_sap_package=>ty_devclass_tt,
          lt_super TYPE zif_abapgit_sap_package=>ty_devclass_tt,
          lv_super LIKE LINE OF lt_super,
          lv_index TYPE i.

    FIELD-SYMBOLS: <ls_tadir> LIKE LINE OF it_tadir.

    READ TABLE it_tadir INDEX 1 ASSIGNING <ls_tadir>.
    ASSERT sy-subrc = 0.
    lt_super = zcl_abapgit_factory=>get_sap_package( <ls_tadir>-devclass )->list_superpackages( ).

    LOOP AT it_tadir ASSIGNING <ls_tadir>.
      lt_obj = zcl_abapgit_factory=>get_sap_package( <ls_tadir>-devclass )->list_superpackages( ).

* filter out possibilities from lt_super
      LOOP AT lt_super INTO lv_super.
        lv_index = sy-tabix.
        READ TABLE lt_obj FROM lv_super TRANSPORTING NO FIELDS.
        IF sy-subrc <> 0.
          DELETE lt_super INDEX lv_index.
        ENDIF.
      ENDLOOP.
    ENDLOOP.

    READ TABLE lt_super INDEX lines( lt_super ) INTO rv_package.
  ENDMETHOD.

  METHOD read_requests.
    DATA lt_requests LIKE rt_requests.
    FIELD-SYMBOLS <ls_trkorr> LIKE LINE OF it_trkorr.

    LOOP AT it_trkorr ASSIGNING <ls_trkorr>.
      CALL FUNCTION 'TR_READ_REQUEST_WITH_TASKS'
        EXPORTING
          iv_trkorr     = <ls_trkorr>-trkorr
        IMPORTING
          et_requests   = lt_requests
        EXCEPTIONS
          invalid_input = 1
          OTHERS        = 2.
      IF sy-subrc <> 0.
        zcx_abapgit_exception=>raise_t100( ).
      ENDIF.

      APPEND LINES OF lt_requests TO rt_requests.
    ENDLOOP.
  ENDMETHOD.

  METHOD resolve.
    DATA: lv_object     TYPE tadir-object,
          lv_obj_name   TYPE tadir-obj_name,
          lv_trobj_name TYPE trobj_name,
          ls_tadir      TYPE zif_abapgit_definitions=>ty_tadir.

    FIELD-SYMBOLS: <ls_request> LIKE LINE OF it_requests,
                   <ls_object>  LIKE LINE OF <ls_request>-objects.

    LOOP AT it_requests ASSIGNING <ls_request>.
      LOOP AT <ls_request>-objects ASSIGNING <ls_object>.
        " VARX, see https://github.com/abapGit/abapGit/issues/3107
        IF <ls_object>-pgmid = 'LIMU' AND <ls_object>-object <> 'VARX'.
          CALL FUNCTION 'GET_R3TR_OBJECT_FROM_LIMU_OBJ'
            EXPORTING
              p_limu_objtype = <ls_object>-object
              p_limu_objname = <ls_object>-obj_name
            IMPORTING
              p_r3tr_objtype = lv_object
              p_r3tr_objname = lv_trobj_name
            EXCEPTIONS
              no_mapping     = 1
              OTHERS         = 2.
          IF sy-subrc <> 0.
            zcx_abapgit_exception=>raise( 'error from GET_R3TR_OBJECT_FROM_LIMU_OBJ' ).
          ENDIF.
          lv_obj_name = lv_trobj_name.
        ELSE.
          lv_object   = <ls_object>-object.
          lv_obj_name = <ls_object>-obj_name.
        ENDIF.

        ls_tadir = zcl_abapgit_factory=>get_tadir( )->read_single(
          iv_object   = lv_object
          iv_obj_name = lv_obj_name ).

        APPEND ls_tadir TO rt_tadir.
      ENDLOOP.
    ENDLOOP.

    SORT rt_tadir BY object ASCENDING obj_name ASCENDING.
    DELETE ADJACENT DUPLICATES FROM rt_tadir COMPARING object obj_name.
    DELETE rt_tadir WHERE table_line IS INITIAL.
  ENDMETHOD.

  METHOD to_tadir.
    DATA: lt_requests TYPE trwbo_requests.

    IF lines( it_transport_headers ) = 0.
      RETURN.
    ENDIF.

    lt_requests = read_requests( it_transport_headers ).
    rt_tadir = resolve( lt_requests ).
  ENDMETHOD.

  METHOD zip.

    DATA: lt_requests       TYPE trwbo_requests,
          lt_tadir          TYPE zif_abapgit_definitions=>ty_tadir_tt,
          lv_package        TYPE devclass,
          lo_dot_abapgit    TYPE REF TO zcl_abapgit_dot_abapgit,
          ls_local_settings TYPE zif_abapgit_persistence=>ty_repo-local_settings,
          lt_trkorr         TYPE trwbo_request_headers.

    IF is_trkorr IS SUPPLIED.
      APPEND is_trkorr TO lt_trkorr.
    ELSE.
      lt_trkorr = zcl_abapgit_ui_factory=>get_popups( )->popup_to_select_transports( ).
    ENDIF.

    IF lines( lt_trkorr ) = 0.
      RETURN.
    ENDIF.

    lt_requests = read_requests( lt_trkorr ).
    lt_tadir = resolve( lt_requests ).
    IF lines( lt_tadir ) = 0.
      zcx_abapgit_exception=>raise( 'empty transport' ).
    ENDIF.

    lv_package = find_top_package( lt_tadir ).
    IF lv_package IS INITIAL.
      zcx_abapgit_exception=>raise( 'error finding super package' ).
    ENDIF.

    lo_dot_abapgit = zcl_abapgit_dot_abapgit=>build_default( ).
    IF iv_logic IS SUPPLIED AND iv_logic IS NOT INITIAL.
      lo_dot_abapgit->set_folder_logic( iv_logic ).
    ELSE.
      lo_dot_abapgit->set_folder_logic( zcl_abapgit_ui_factory=>get_popups( )->popup_folder_logic( ) ).
    ENDIF.

    rv_xstr = zcl_abapgit_zip=>export(
      iv_package        = lv_package
      io_dot_abapgit    = lo_dot_abapgit
      is_local_settings = ls_local_settings
      it_filter         = lt_tadir
      iv_show_log       = iv_show_log_popup ).

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_transport DEFINITION
  PUBLIC
  CREATE PUBLIC .

  PUBLIC SECTION.

    CLASS-METHODS zip
      IMPORTING
        !iv_show_log_popup TYPE abap_bool DEFAULT abap_true
        !iv_logic          TYPE string OPTIONAL
        !is_trkorr         TYPE trwbo_request_header OPTIONAL
      RETURNING
        VALUE(rv_xstr)     TYPE xstring
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS to_tadir
      IMPORTING
        it_transport_headers TYPE trwbo_request_headers
      RETURNING
        VALUE(rt_tadir)      TYPE zif_abapgit_definitions=>ty_tadir_tt
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS add_all_objects_to_trans_req
      IMPORTING
        iv_key TYPE zif_abapgit_persistence=>ty_value
      RAISING
        zcx_abapgit_exception .
  PROTECTED SECTION.

    CLASS-METHODS read_requests
      IMPORTING
        !it_trkorr         TYPE trwbo_request_headers
      RETURNING
        VALUE(rt_requests) TYPE trwbo_requests
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS find_top_package
      IMPORTING
        !it_tadir         TYPE zif_abapgit_definitions=>ty_tadir_tt
      RETURNING
        VALUE(rv_package) TYPE devclass
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS resolve
      IMPORTING
        !it_requests    TYPE trwbo_requests
      RETURNING
        VALUE(rt_tadir) TYPE zif_abapgit_definitions=>ty_tadir_tt
      RAISING
        zcx_abapgit_exception .
  PRIVATE SECTION.
    CLASS-METHODS collect_all_objects
      IMPORTING
        iv_key            TYPE zif_abapgit_persistence=>ty_value
      RETURNING
        VALUE(rt_objects) TYPE tr_objects
      RAISING
        zcx_abapgit_exception.
ENDCLASS.

CLASS zcl_abapgit_transport IMPLEMENTATION.

  METHOD add_all_objects_to_trans_req.

    DATA:
      ls_request      TYPE trwbo_request_header,
      lt_e071         TYPE tr_objects,
      lv_text         TYPE string,
      lv_answer       TYPE c LENGTH 1,
      lv_lock_objects TYPE trparflag.

    lv_answer = zcl_abapgit_ui_factory=>get_popups( )->popup_to_confirm(
                    iv_titlebar              = `Lock objects?`
                    iv_text_question         = `Shall all objects be locked in the transport request?`
                    iv_display_cancel_button = abap_true ).

    CASE lv_answer.
      WHEN '1'.
        lv_lock_objects = abap_true.
      WHEN '2'.
        lv_lock_objects = abap_false.
      WHEN OTHERS.
        RETURN.
    ENDCASE.

    lt_e071 = collect_all_objects( iv_key ).

    CALL FUNCTION 'TR_REQUEST_CHOICE'
      EXPORTING
        it_e071              = lt_e071
        iv_lock_objects      = lv_lock_objects
      IMPORTING
        es_request           = ls_request
      EXCEPTIONS
        invalid_request      = 1
        invalid_request_type = 2
        user_not_owner       = 3
        no_objects_appended  = 4
        enqueue_error        = 5
        cancelled_by_user    = 6
        recursive_call       = 7
        OTHERS               = 8.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise_t100( ).
    ENDIF.

    lv_text = |Objects successfully added to { ls_request-trkorr }|.
    MESSAGE lv_text TYPE 'S'.

  ENDMETHOD.

  METHOD collect_all_objects.

    DATA:
      lt_objects     TYPE scts_tadir,
      lt_objects_all LIKE lt_objects,
      ls_e071        LIKE LINE OF rt_objects,
      lo_repo        TYPE REF TO zcl_abapgit_repo,
      lv_package     TYPE zif_abapgit_persistence=>ty_repo-package,
      lt_packages    TYPE zif_abapgit_sap_package=>ty_devclass_tt.

    FIELD-SYMBOLS:
      <lv_package> TYPE devclass,
      <ls_object>  TYPE tadir.

    lo_repo     = zcl_abapgit_repo_srv=>get_instance( )->get( iv_key ).
    lv_package  = lo_repo->get_package( ).
    lt_packages = zcl_abapgit_factory=>get_sap_package( lv_package )->list_subpackages( ).
    INSERT lv_package INTO TABLE lt_packages.

    LOOP AT lt_packages ASSIGNING <lv_package>.

      CLEAR: lt_objects.

      CALL FUNCTION 'TRINT_SELECT_OBJECTS'
        EXPORTING
          iv_devclass       = <lv_package>
          iv_via_selscreen  = abap_false
        IMPORTING
          et_objects_tadir  = lt_objects
        EXCEPTIONS
          cancelled_by_user = 1
          invalid_input     = 2
          OTHERS            = 3.

      IF sy-subrc <> 0.
        zcx_abapgit_exception=>raise_t100( ).
      ENDIF.

      INSERT LINES OF lt_objects INTO TABLE lt_objects_all.

    ENDLOOP.

    IF lines( lt_objects_all ) = 0.
      zcx_abapgit_exception=>raise( |No objects found| ).
    ENDIF.

    LOOP AT lt_objects_all ASSIGNING <ls_object>.

      CLEAR: ls_e071.

      MOVE-CORRESPONDING <ls_object> TO ls_e071.
      INSERT ls_e071 INTO TABLE rt_objects.

    ENDLOOP.

  ENDMETHOD.

  METHOD find_top_package.
* assumption: all objects in transport share a common super package

    DATA: lt_obj   TYPE zif_abapgit_sap_package=>ty_devclass_tt,
          lt_super TYPE zif_abapgit_sap_package=>ty_devclass_tt,
          lv_super LIKE LINE OF lt_super,
          lv_index TYPE i.

    FIELD-SYMBOLS: <ls_tadir> LIKE LINE OF it_tadir.

    READ TABLE it_tadir INDEX 1 ASSIGNING <ls_tadir>.
    ASSERT sy-subrc = 0.
    lt_super = zcl_abapgit_factory=>get_sap_package( <ls_tadir>-devclass )->list_superpackages( ).

    LOOP AT it_tadir ASSIGNING <ls_tadir>.
      lt_obj = zcl_abapgit_factory=>get_sap_package( <ls_tadir>-devclass )->list_superpackages( ).

* filter out possibilities from lt_super
      LOOP AT lt_super INTO lv_super.
        lv_index = sy-tabix.
        READ TABLE lt_obj FROM lv_super TRANSPORTING NO FIELDS.
        IF sy-subrc <> 0.
          DELETE lt_super INDEX lv_index.
        ENDIF.
      ENDLOOP.
    ENDLOOP.

    READ TABLE lt_super INDEX lines( lt_super ) INTO rv_package.
  ENDMETHOD.

  METHOD read_requests.
    DATA lt_requests LIKE rt_requests.
    FIELD-SYMBOLS <ls_trkorr> LIKE LINE OF it_trkorr.

    LOOP AT it_trkorr ASSIGNING <ls_trkorr>.
      CALL FUNCTION 'TR_READ_REQUEST_WITH_TASKS'
        EXPORTING
          iv_trkorr     = <ls_trkorr>-trkorr
        IMPORTING
          et_requests   = lt_requests
        EXCEPTIONS
          invalid_input = 1
          OTHERS        = 2.
      IF sy-subrc <> 0.
        zcx_abapgit_exception=>raise_t100( ).
      ENDIF.

      APPEND LINES OF lt_requests TO rt_requests.
    ENDLOOP.
  ENDMETHOD.

  METHOD resolve.
    DATA: lv_object     TYPE tadir-object,
          lv_obj_name   TYPE tadir-obj_name,
          lv_trobj_name TYPE trobj_name,
          ls_tadir      TYPE zif_abapgit_definitions=>ty_tadir,
          lv_result     TYPE trpari-s_checked,
          ls_tadir_sap  TYPE tadir.

    FIELD-SYMBOLS: <ls_request> LIKE LINE OF it_requests,
                   <ls_object>  LIKE LINE OF <ls_request>-objects.

    LOOP AT it_requests ASSIGNING <ls_request>.
      LOOP AT <ls_request>-objects ASSIGNING <ls_object>.
        " VARX, see https://github.com/abapGit/abapGit/issues/3107
        IF <ls_object>-pgmid = 'LIMU' AND <ls_object>-object <> 'VARX'.
          CALL FUNCTION 'TR_CHECK_TYPE'
            EXPORTING
              wi_e071   = <ls_object>
            IMPORTING
              we_tadir  = ls_tadir_sap
              pe_result = lv_result.
          IF lv_result NA 'TL' OR ls_tadir_sap IS INITIAL.
            zcx_abapgit_exception=>raise( 'error from TR_CHECK_TYPE' ).
          ENDIF.
          lv_object   = ls_tadir_sap-object.
          lv_obj_name = ls_tadir_sap-obj_name.
        ELSE.
          lv_object   = <ls_object>-object.
          lv_obj_name = <ls_object>-obj_name.
        ENDIF.

        ls_tadir = zcl_abapgit_factory=>get_tadir( )->read_single(
          iv_object   = lv_object
          iv_obj_name = lv_obj_name ).

        IF ls_tadir-delflag IS INITIAL.
          APPEND ls_tadir TO rt_tadir.
        ENDIF.
      ENDLOOP.
    ENDLOOP.

    SORT rt_tadir BY object ASCENDING obj_name ASCENDING.
    DELETE ADJACENT DUPLICATES FROM rt_tadir COMPARING object obj_name.
    DELETE rt_tadir WHERE table_line IS INITIAL.
  ENDMETHOD.

  METHOD to_tadir.
    DATA: lt_requests TYPE trwbo_requests.

    IF lines( it_transport_headers ) = 0.
      RETURN.
    ENDIF.

    lt_requests = read_requests( it_transport_headers ).
    rt_tadir = resolve( lt_requests ).
  ENDMETHOD.

  METHOD zip.

    DATA: lt_requests       TYPE trwbo_requests,
          lt_tadir          TYPE zif_abapgit_definitions=>ty_tadir_tt,
          lv_package        TYPE devclass,
          lo_dot_abapgit    TYPE REF TO zcl_abapgit_dot_abapgit,
          ls_local_settings TYPE zif_abapgit_persistence=>ty_repo-local_settings,
          lt_trkorr         TYPE trwbo_request_headers.

    IF is_trkorr IS SUPPLIED.
      APPEND is_trkorr TO lt_trkorr.
    ELSE.
      lt_trkorr = zcl_abapgit_ui_factory=>get_popups( )->popup_to_select_transports( ).
    ENDIF.

    IF lines( lt_trkorr ) = 0.
      RETURN.
    ENDIF.

    lt_requests = read_requests( lt_trkorr ).
    lt_tadir = resolve( lt_requests ).
    IF lines( lt_tadir ) = 0.
      zcx_abapgit_exception=>raise( 'empty transport' ).
    ENDIF.

    lv_package = find_top_package( lt_tadir ).
    IF lv_package IS INITIAL.
      zcx_abapgit_exception=>raise( 'error finding super package' ).
    ENDIF.

    lo_dot_abapgit = zcl_abapgit_dot_abapgit=>build_default( ).
    IF iv_logic IS SUPPLIED AND iv_logic IS NOT INITIAL.
      lo_dot_abapgit->set_folder_logic( iv_logic ).
    ELSE.
      lo_dot_abapgit->set_folder_logic( zcl_abapgit_ui_factory=>get_popups( )->popup_folder_logic( ) ).
    ENDIF.

    rv_xstr = zcl_abapgit_zip=>export(
      iv_package        = lv_package
      io_dot_abapgit    = lo_dot_abapgit
      is_local_settings = ls_local_settings
      it_filter         = lt_tadir
      iv_show_log       = iv_show_log_popup ).

  ENDMETHOD.
ENDCLASS.

Replace GET_R3TR_OBJECT_FROM_LIMU_OBJ (#4749)

* Replace GET_R3TR_OBJECT_FROM_LIMU_OBJ, fixes #4715

Replaced with TR_CHECK_TYPE. Only tested locally (with a copy of the method in a separate program), not in full ABAPGit context.

* forgot the additional DATA declarations

* Ignore deleted objects

as requested by Marc Bernard

* remove whitespace at end

Co-authored-by: abaplint[bot] <589dee37d0e82f3cabf9dea701e9ec2fd481eb1e@users.noreply.github.com>

Co-authored-by: abaplint[bot] <589dee37d0e82f3cabf9dea701e9ec2fd481eb1e@users.noreply.github.com>
Co-authored-by: Lars Hvam <721d1b171982cd84b755f6b25130d2b88cd1f696@hotmail.com>


CLASS zcl_abapgit_objects_ci_tests DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC
  FOR TESTING
  DURATION SHORT
  RISK LEVEL CRITICAL .

  PUBLIC SECTION.
    CLASS-METHODS:
      run
        IMPORTING
          !iv_object TYPE tadir-object.

  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.

CLASS ZCL_ABAPGIT_OBJECTS_CI_TESTS IMPLEMENTATION.

  METHOD run.

    DATA:
      ls_repo     TYPE zif_abapgit_exit=>ty_ci_repo,
      lt_repos    TYPE zif_abapgit_exit=>ty_ci_repos,
      lo_ci_repos TYPE REF TO object,
      ld_options  TYPE REF TO data,
      ld_results  TYPE REF TO data,
      lv_msg      TYPE string,
      lv_check    TYPE string,
      lx_error    TYPE REF TO zcx_abapgit_exception.

    FIELD-SYMBOLS:
      <ls_options>     TYPE any,
      <lv_option>      TYPE any,
      <lt_repo_result> TYPE ANY TABLE,
      <ls_repo_result> TYPE any,
      <lv_result>      TYPE any,
      <lv_repo>        TYPE any.

    " Add the default test repo from https://github.com/abapGit-tests
    ls_repo-name = iv_object.
    ls_repo-clone_url = |https://github.com/abapGit-tests/{ iv_object }|.
    APPEND ls_repo TO lt_repos.

    " Get list of repos via exit
    zcl_abapgit_exit=>get_instance( )->get_ci_tests(
      EXPORTING
        iv_object    = iv_object
      CHANGING
        ct_ci_repos  = lt_repos ).

    IF lines( lt_repos ) = 0.
      RETURN.
    ENDIF.

    " Objects will be created and deleted, do not run in customer system!
    " These tests may fail if you are locking the entries (e.g. the ZABAPGIT transaction is open)
    IF zcl_abapgit_persist_settings=>get_instance( )->read( )->get_run_critical_tests( ) = abap_false.
      cl_abap_unit_assert=>fail(
        msg   = 'Cancelled. You can enable these tests in abapGit settings'
        level = if_aunit_constants=>tolerable ).
    ENDIF.

    " Check if abapGit-CI is installed
    SELECT SINGLE clsname FROM seoclass INTO lv_check WHERE clsname = 'ZCL_ABAPGIT_CI_REPOS'.
    IF sy-subrc <> 0.
      cl_abap_unit_assert=>fail(
        msg   = 'Cancelled. abapGit-CI is not installed (https://github.com/abapGit/CI)'
        level = if_aunit_constants=>tolerable ).
    ENDIF.

    TRY.
        " Prepare input for CI repo test
        CREATE DATA ld_options TYPE ('ZIF_ABAPGIT_CI_DEFINITIONS=>TY_REPO_CHECK_OPTIONS').
        ASSIGN ld_options->* TO <ls_options>.

        ASSIGN COMPONENT 'CHECK_LOCAL' OF STRUCTURE <ls_options> TO <lv_option>.
        ASSERT sy-subrc = 0.
        <lv_option> = abap_true.

        CREATE DATA ld_results TYPE ('ZIF_ABAPGIT_CI_DEFINITIONS=>TY_RESULT-REPO_RESULT_LIST').
        ASSIGN ld_results->* TO <lt_repo_result>.

        " Run CI repo tests
        CREATE OBJECT lo_ci_repos TYPE ('ZCL_ABAPGIT_CI_REPOS').

        CALL METHOD lo_ci_repos->('PROCESS_REPOS')
          EXPORTING
            it_repos       = lt_repos
            is_options     = <ls_options>
          RECEIVING
            rt_result_list = <lt_repo_result>.

        " Check results for individual repos
        LOOP AT <lt_repo_result> ASSIGNING <ls_repo_result>.
          ASSIGN COMPONENT 'NAME' OF STRUCTURE <ls_repo_result> TO <lv_repo>.
          ASSERT sy-subrc = 0.

          CLEAR lv_msg.
          DO 7 TIMES.
            CASE sy-index.
              WHEN 1.
                lv_check = 'CREATE_PACKAGE'.
              WHEN 2.
                lv_check = 'CLONE'.
              WHEN 3.
                lv_check = 'PULL'.
              WHEN 4.
                lv_check = 'SYNTAX_CHECK'.
              WHEN 5.
                lv_check = 'OBJECT_CHECK'.
              WHEN 6.
                lv_check = 'PURGE'.
              WHEN 7.
                lv_check = 'CHECK_LEFTOVERS'.
            ENDCASE.
            ASSIGN COMPONENT lv_check OF STRUCTURE <ls_repo_result> TO <lv_result>.
            ASSERT sy-subrc = 0.
            IF <lv_result> <> 'OK'.
              IF lv_msg IS INITIAL.
                lv_msg = |{ lv_check }:{ <lv_result> }|.
              ELSE.
                lv_msg = |{ lv_msg } { lv_check }:{ <lv_result> }|.
              ENDIF.
            ENDIF.
          ENDDO.

          cl_abap_unit_assert=>assert_equals(
            exp = ''
            act = lv_msg
            msg = |{ <lv_repo> } { lv_msg }| ).

        ENDLOOP.

      CATCH zcx_abapgit_exception INTO lx_error.
        cl_abap_unit_assert=>fail( msg = lx_error->get_text( ) ).
    ENDTRY.

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_objects_ci_tests DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC
  FOR TESTING
  DURATION SHORT
  RISK LEVEL CRITICAL .

  PUBLIC SECTION.
    CLASS-METHODS:
      run
        IMPORTING
          iv_object TYPE tadir-object
          iv_url    TYPE string OPTIONAL.

  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.

CLASS zcl_abapgit_objects_ci_tests IMPLEMENTATION.

  METHOD run.

    DATA:
      ls_repo     TYPE zif_abapgit_exit=>ty_ci_repo,
      lt_repos    TYPE zif_abapgit_exit=>ty_ci_repos,
      lo_ci_repos TYPE REF TO object,
      ld_options  TYPE REF TO data,
      ld_results  TYPE REF TO data,
      lv_msg      TYPE string,
      lv_check    TYPE string,
      lx_error    TYPE REF TO zcx_abapgit_exception.

    FIELD-SYMBOLS:
      <ls_options>     TYPE any,
      <lv_option>      TYPE any,
      <lt_repo_result> TYPE ANY TABLE,
      <ls_repo_result> TYPE any,
      <lv_result>      TYPE any,
      <lv_repo>        TYPE any.

    " Add the default test repo from https://github.com/abapGit-tests
    ls_repo-name = iv_object.
    IF iv_url IS NOT INITIAL.
      ls_repo-clone_url = iv_url.
    ELSE.
      ls_repo-clone_url = |https://github.com/abapGit-tests/{ iv_object }|.
    ENDIF.
    APPEND ls_repo TO lt_repos.

    " Get list of repos via exit
    zcl_abapgit_exit=>get_instance( )->get_ci_tests(
      EXPORTING
        iv_object    = iv_object
      CHANGING
        ct_ci_repos  = lt_repos ).

    IF lines( lt_repos ) = 0.
      RETURN.
    ENDIF.

    " Objects will be created and deleted, do not run in customer system!
    " These tests may fail if you are locking the entries (e.g. the ZABAPGIT transaction is open)
    IF zcl_abapgit_persist_settings=>get_instance( )->read( )->get_run_critical_tests( ) = abap_false.
      cl_abap_unit_assert=>fail(
        msg   = 'Cancelled. You can enable these tests in abapGit settings'
        level = if_aunit_constants=>tolerable ).
    ENDIF.

    " Check if abapGit-CI is installed
    SELECT SINGLE clsname FROM seoclass INTO lv_check WHERE clsname = 'ZCL_ABAPGIT_CI_REPOS'.
    IF sy-subrc <> 0.
      cl_abap_unit_assert=>fail(
        msg   = 'Cancelled. abapGit-CI is not installed (https://github.com/abapGit/CI)'
        level = if_aunit_constants=>tolerable ).
    ENDIF.

    TRY.
        " Prepare input for CI repo test
        CREATE DATA ld_options TYPE ('ZIF_ABAPGIT_CI_DEFINITIONS=>TY_REPO_CHECK_OPTIONS').
        ASSIGN ld_options->* TO <ls_options>.

        ASSIGN COMPONENT 'CHECK_LOCAL' OF STRUCTURE <ls_options> TO <lv_option>.
        ASSERT sy-subrc = 0.
        <lv_option> = abap_true.

        CREATE DATA ld_results TYPE ('ZIF_ABAPGIT_CI_DEFINITIONS=>TY_RESULT-REPO_RESULT_LIST').
        ASSIGN ld_results->* TO <lt_repo_result>.

        " Run CI repo tests
        CREATE OBJECT lo_ci_repos TYPE ('ZCL_ABAPGIT_CI_REPOS').

        CALL METHOD lo_ci_repos->('PROCESS_REPOS')
          EXPORTING
            it_repos       = lt_repos
            is_options     = <ls_options>
          RECEIVING
            rt_result_list = <lt_repo_result>.

        " Check results for individual repos
        LOOP AT <lt_repo_result> ASSIGNING <ls_repo_result>.
          ASSIGN COMPONENT 'NAME' OF STRUCTURE <ls_repo_result> TO <lv_repo>.
          ASSERT sy-subrc = 0.

          CLEAR lv_msg.
          DO 7 TIMES.
            CASE sy-index.
              WHEN 1.
                lv_check = 'CREATE_PACKAGE'.
              WHEN 2.
                lv_check = 'CLONE'.
              WHEN 3.
                lv_check = 'PULL'.
              WHEN 4.
                lv_check = 'SYNTAX_CHECK'.
              WHEN 5.
                lv_check = 'OBJECT_CHECK'.
              WHEN 6.
                lv_check = 'PURGE'.
              WHEN 7.
                lv_check = 'CHECK_LEFTOVERS'.
            ENDCASE.
            ASSIGN COMPONENT lv_check OF STRUCTURE <ls_repo_result> TO <lv_result>.
            ASSERT sy-subrc = 0.
            IF <lv_result> <> 'OK'.
              IF lv_msg IS INITIAL.
                lv_msg = |{ lv_check }:{ <lv_result> }|.
              ELSE.
                lv_msg = |{ lv_msg } { lv_check }:{ <lv_result> }|.
              ENDIF.
            ENDIF.
          ENDDO.

          cl_abap_unit_assert=>assert_equals(
            exp = ''
            act = lv_msg
            msg = |{ <lv_repo> } { lv_msg }| ).

        ENDLOOP.

      CATCH zcx_abapgit_exception INTO lx_error.
        cl_abap_unit_assert=>fail( msg = lx_error->get_text( ) ).
    ENDTRY.

  ENDMETHOD.
ENDCLASS.

Add option to specify own repo path (#4107)

Co-authored-by: Lars Hvam <721d1b171982cd84b755f6b25130d2b88cd1f696@hotmail.com>


CLASS zcl_abapgit_gui_page_data DEFINITION
  PUBLIC
  INHERITING FROM zcl_abapgit_gui_page
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    METHODS constructor
      IMPORTING
        !iv_key TYPE zif_abapgit_persistence=>ty_repo-key
      RAISING
        zcx_abapgit_exception .

    METHODS zif_abapgit_gui_event_handler~on_event
        REDEFINITION .
  PROTECTED SECTION.

    CONSTANTS:
      BEGIN OF c_event,
        add    TYPE string VALUE 'add',
        update TYPE string VALUE 'update',
        remove TYPE string VALUE 'remove',
      END OF c_event .

    CONSTANTS:
      BEGIN OF c_id,
        table TYPE string VALUE 'table',
        where TYPE string VALUE 'where',
      END OF c_id .

    DATA mi_config TYPE REF TO zif_abapgit_data_config .

    METHODS render_content
        REDEFINITION .
  PRIVATE SECTION.

    DATA mo_repo TYPE REF TO zcl_abapgit_repo .

    METHODS build_where
      IMPORTING
        !io_map         TYPE REF TO zcl_abapgit_string_map
      RETURNING
        VALUE(rt_where) TYPE string_table .
    METHODS render_add
      RETURNING
        VALUE(ri_html) TYPE REF TO zif_abapgit_html
      RAISING
        zcx_abapgit_exception .
    METHODS render_existing
      RETURNING
        VALUE(ri_html) TYPE REF TO zif_abapgit_html
      RAISING
        zcx_abapgit_exception .
    METHODS event_add
      IMPORTING
        !ii_event TYPE REF TO zif_abapgit_gui_event
      RAISING
        zcx_abapgit_exception .
    METHODS event_remove
      IMPORTING
        !ii_event TYPE REF TO zif_abapgit_gui_event
      RAISING
        zcx_abapgit_exception .
    METHODS event_update
      IMPORTING
        !ii_event TYPE REF TO zif_abapgit_gui_event
      RAISING
        zcx_abapgit_exception .
ENDCLASS.

CLASS zcl_abapgit_gui_page_data IMPLEMENTATION.

  METHOD build_where.

    DATA lv_where LIKE LINE OF rt_where.

    SPLIT io_map->get( c_id-where ) AT |\n| INTO TABLE rt_where.

    DELETE rt_where WHERE table_line IS INITIAL.

    LOOP AT rt_where INTO lv_where.
      IF strlen( lv_where ) <= 2.
        DELETE rt_where INDEX sy-tabix.
      ENDIF.
    ENDLOOP.

  ENDMETHOD.

  METHOD constructor.

    super->constructor( ).

    ms_control-page_title = 'Data'.

    mo_repo = zcl_abapgit_repo_srv=>get_instance( )->get( iv_key ).
    mi_config = mo_repo->get_data_config( ).

  ENDMETHOD.

  METHOD event_add.

    DATA lo_map TYPE REF TO zcl_abapgit_string_map.
    DATA ls_config TYPE zif_abapgit_data_config=>ty_config.

    lo_map = ii_event->form_data( ).

    ls_config-type = zif_abapgit_data_config=>c_data_type-tabu.
    ls_config-name = to_upper( lo_map->get( c_id-table ) ).
    ls_config-where = build_where( lo_map ).

    mi_config->add_config( ls_config ).

  ENDMETHOD.

  METHOD event_remove.

    DATA lo_map TYPE REF TO zcl_abapgit_string_map.
    DATA ls_config TYPE zif_abapgit_data_config=>ty_config.

    lo_map = ii_event->form_data( ).

    ls_config-type = zif_abapgit_data_config=>c_data_type-tabu.
    ls_config-name = to_upper( lo_map->get( c_id-table ) ).

    mi_config->remove_config( ls_config ).

  ENDMETHOD.

  METHOD event_update.

    DATA lo_map TYPE REF TO zcl_abapgit_string_map.
    DATA ls_config TYPE zif_abapgit_data_config=>ty_config.

    lo_map = ii_event->form_data( ).

    ls_config-type = zif_abapgit_data_config=>c_data_type-tabu.
    ls_config-name = to_upper( lo_map->get( c_id-table ) ).
    ls_config-where = build_where( lo_map ).

    mi_config->update_config( ls_config ).

  ENDMETHOD.

  METHOD render_add.

    DATA lo_form TYPE REF TO zcl_abapgit_html_form.
    DATA lo_form_data TYPE REF TO zcl_abapgit_string_map.

    CREATE OBJECT ri_html TYPE zcl_abapgit_html.
    CREATE OBJECT lo_form_data.

    lo_form = zcl_abapgit_html_form=>create( ).
    lo_form->text(
      iv_label    = 'Table'
      iv_name     = c_id-table
      iv_required = abap_true ).
    lo_form->textarea(
      iv_label       = 'Where'
      iv_placeholder = 'Conditions separated by newline'
      iv_name        = c_id-where ).
    lo_form->command(
      iv_label       = 'Add'
      iv_cmd_type    = zif_abapgit_html_form=>c_cmd_type-input_main
      iv_action      = c_event-add ).
    ri_html->add( lo_form->render( lo_form_data ) ).

  ENDMETHOD.

  METHOD render_content.

    CREATE OBJECT ri_html TYPE zcl_abapgit_html.

    ri_html->add( render_add( ) ).
    ri_html->add( render_existing( ) ).

  ENDMETHOD.

  METHOD render_existing.

    DATA lo_form TYPE REF TO zcl_abapgit_html_form.
    DATA lo_form_data TYPE REF TO zcl_abapgit_string_map.
    DATA lt_configs TYPE zif_abapgit_data_config=>ty_config_tt.
    DATA ls_config LIKE LINE OF lt_configs.

    CREATE OBJECT ri_html TYPE zcl_abapgit_html.
    CREATE OBJECT lo_form_data.

    lt_configs = mi_config->get_configs( ).

    LOOP AT lt_configs INTO ls_config.
      lo_form = zcl_abapgit_html_form=>create(  ).
      CREATE OBJECT lo_form_data.

      lo_form_data->set(
        iv_key = c_id-table
        iv_val = |{ ls_config-name }| ).
      lo_form->text(
        iv_label    = 'Table'
        iv_name     = c_id-table
        iv_readonly = abap_true ).

      lo_form_data->set(
        iv_key = c_id-where
        iv_val = concat_lines_of( table = ls_config-where sep = |\n| ) ).
      lo_form->textarea(
        iv_label       = 'Where'
        iv_placeholder = 'Conditions separated by newline'
        iv_name        = c_id-where ).

      lo_form->command(
        iv_label       = 'Update'
        iv_cmd_type    = zif_abapgit_html_form=>c_cmd_type-input_main
        iv_action      = c_event-update ).
      lo_form->command(
        iv_label       = 'Remove'
        iv_cmd_type    = zif_abapgit_html_form=>c_cmd_type-input_main
        iv_action      = c_event-remove ).
      ri_html->add( lo_form->render( lo_form_data ) ).
    ENDLOOP.

  ENDMETHOD.

  METHOD zif_abapgit_gui_event_handler~on_event.

    CASE ii_event->mv_action.
      WHEN c_event-add.
        event_add( ii_event ).
        rs_handled-state = zcl_abapgit_gui=>c_event_state-re_render.
      WHEN c_event-update.
        event_update( ii_event ).
        rs_handled-state = zcl_abapgit_gui=>c_event_state-re_render.
      WHEN c_event-remove.
        event_remove( ii_event ).
        rs_handled-state = zcl_abapgit_gui=>c_event_state-re_render.
    ENDCASE.

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_gui_page_data DEFINITION
  PUBLIC
  INHERITING FROM zcl_abapgit_gui_page
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    METHODS constructor
      IMPORTING
        !iv_key TYPE zif_abapgit_persistence=>ty_repo-key
      RAISING
        zcx_abapgit_exception .

    METHODS zif_abapgit_gui_event_handler~on_event
        REDEFINITION .
  PROTECTED SECTION.

    CONSTANTS:
      BEGIN OF c_event,
        add    TYPE string VALUE 'add',
        update TYPE string VALUE 'update',
        remove TYPE string VALUE 'remove',
      END OF c_event .

    CONSTANTS:
      BEGIN OF c_id,
        table TYPE string VALUE 'table',
        where TYPE string VALUE 'where',
      END OF c_id .

    DATA mi_config TYPE REF TO zif_abapgit_data_config .

    METHODS render_content
        REDEFINITION .
  PRIVATE SECTION.

    DATA mo_repo TYPE REF TO zcl_abapgit_repo .

    METHODS build_where
      IMPORTING
        !io_map         TYPE REF TO zcl_abapgit_string_map
      RETURNING
        VALUE(rt_where) TYPE string_table .
    METHODS render_add
      RETURNING
        VALUE(ri_html) TYPE REF TO zif_abapgit_html
      RAISING
        zcx_abapgit_exception .
    METHODS render_existing
      RETURNING
        VALUE(ri_html) TYPE REF TO zif_abapgit_html
      RAISING
        zcx_abapgit_exception .
    METHODS event_add
      IMPORTING
        !ii_event TYPE REF TO zif_abapgit_gui_event
      RAISING
        zcx_abapgit_exception .
    METHODS event_remove
      IMPORTING
        !ii_event TYPE REF TO zif_abapgit_gui_event
      RAISING
        zcx_abapgit_exception .
    METHODS event_update
      IMPORTING
        !ii_event TYPE REF TO zif_abapgit_gui_event
      RAISING
        zcx_abapgit_exception .
ENDCLASS.

CLASS zcl_abapgit_gui_page_data IMPLEMENTATION.

  METHOD build_where.

    DATA lv_where LIKE LINE OF rt_where.

    SPLIT io_map->get( c_id-where ) AT |\n| INTO TABLE rt_where.

    DELETE rt_where WHERE table_line IS INITIAL.

    LOOP AT rt_where INTO lv_where.
      IF strlen( lv_where ) <= 2.
        DELETE rt_where INDEX sy-tabix.
      ENDIF.
    ENDLOOP.

  ENDMETHOD.

  METHOD constructor.

    super->constructor( ).

    ms_control-page_title = 'Data'.

    mo_repo = zcl_abapgit_repo_srv=>get_instance( )->get( iv_key ).
    mi_config = mo_repo->get_data_config( ).

  ENDMETHOD.

  METHOD event_add.

    DATA lo_map TYPE REF TO zcl_abapgit_string_map.
    DATA ls_config TYPE zif_abapgit_data_config=>ty_config.

    lo_map = ii_event->form_data( ).

    ls_config-type = zif_abapgit_data_config=>c_data_type-tabu.
    ls_config-name = to_upper( lo_map->get( c_id-table ) ).
    ls_config-where = build_where( lo_map ).

    mi_config->add_config( ls_config ).

  ENDMETHOD.

  METHOD event_remove.

    DATA lo_map TYPE REF TO zcl_abapgit_string_map.
    DATA ls_config TYPE zif_abapgit_data_config=>ty_config.

    lo_map = ii_event->form_data( ).

    ls_config-type = zif_abapgit_data_config=>c_data_type-tabu.
    ls_config-name = to_upper( lo_map->get( c_id-table ) ).

    mi_config->remove_config( ls_config ).

  ENDMETHOD.

  METHOD event_update.

    DATA lo_map TYPE REF TO zcl_abapgit_string_map.
    DATA ls_config TYPE zif_abapgit_data_config=>ty_config.

    lo_map = ii_event->form_data( ).

    ls_config-type = zif_abapgit_data_config=>c_data_type-tabu.
    ls_config-name = to_upper( lo_map->get( c_id-table ) ).
    ls_config-where = build_where( lo_map ).

    mi_config->update_config( ls_config ).

  ENDMETHOD.

  METHOD render_add.

    DATA lo_form TYPE REF TO zcl_abapgit_html_form.
    DATA lo_form_data TYPE REF TO zcl_abapgit_string_map.

    CREATE OBJECT ri_html TYPE zcl_abapgit_html.
    CREATE OBJECT lo_form_data.

    lo_form = zcl_abapgit_html_form=>create( ).
    lo_form->text(
      iv_label    = 'Table'
      iv_name     = c_id-table
      iv_required = abap_true ).
    lo_form->textarea(
      iv_label       = 'Where'
      iv_placeholder = 'Conditions separated by newline'
      iv_name        = c_id-where ).
    lo_form->command(
      iv_label       = 'Add'
      iv_cmd_type    = zif_abapgit_html_form=>c_cmd_type-input_main
      iv_action      = c_event-add ).
    ri_html->add( lo_form->render( lo_form_data ) ).

  ENDMETHOD.

  METHOD render_content.

    CREATE OBJECT ri_html TYPE zcl_abapgit_html.

    ri_html->add( render_add( ) ).
    ri_html->add( render_existing( ) ).

  ENDMETHOD.

  METHOD render_existing.

    DATA lo_form TYPE REF TO zcl_abapgit_html_form.
    DATA lo_form_data TYPE REF TO zcl_abapgit_string_map.
    DATA lt_configs TYPE zif_abapgit_data_config=>ty_config_tt.
    DATA ls_config LIKE LINE OF lt_configs.

    CREATE OBJECT ri_html TYPE zcl_abapgit_html.
    CREATE OBJECT lo_form_data.

    lt_configs = mi_config->get_configs( ).

    LOOP AT lt_configs INTO ls_config.
      lo_form = zcl_abapgit_html_form=>create(  ).
      CREATE OBJECT lo_form_data.

      lo_form_data->set(
        iv_key = c_id-table
        iv_val = |{ ls_config-name }| ).
      lo_form->text(
        iv_label    = 'Table'
        iv_name     = c_id-table
        iv_readonly = abap_true ).

      lo_form_data->set(
        iv_key = c_id-where
        iv_val = concat_lines_of( table = ls_config-where sep = |\n| ) ).
      lo_form->textarea(
        iv_label       = 'Where'
        iv_placeholder = 'Conditions separated by newline'
        iv_name        = c_id-where ).

      lo_form->command(
        iv_label       = 'Update'
        iv_cmd_type    = zif_abapgit_html_form=>c_cmd_type-input_main
        iv_action      = c_event-update ).
      lo_form->command(
        iv_label       = 'Remove'
        iv_action      = c_event-remove ).
      ri_html->add( lo_form->render( lo_form_data ) ).
    ENDLOOP.

  ENDMETHOD.

  METHOD zif_abapgit_gui_event_handler~on_event.

    CASE ii_event->mv_action.
      WHEN c_event-add.
        event_add( ii_event ).
        rs_handled-state = zcl_abapgit_gui=>c_event_state-re_render.
      WHEN c_event-update.
        event_update( ii_event ).
        rs_handled-state = zcl_abapgit_gui=>c_event_state-re_render.
      WHEN c_event-remove.
        event_remove( ii_event ).
        rs_handled-state = zcl_abapgit_gui=>c_event_state-re_render.
    ENDCASE.

  ENDMETHOD.
ENDCLASS.

Fix "Remove" button for data tables (#5177)

The remove button was not working because it was defined as a second `main` button.