CLASS zcl_abapgit_persistence_db DEFINITION
  PUBLIC
  CREATE PRIVATE .

  PUBLIC SECTION.
    CONSTANTS c_tabname TYPE c LENGTH 30 VALUE 'ZABAPGIT' ##NO_TEXT.
    CONSTANTS c_lock TYPE c LENGTH 30 VALUE 'EZABAPGIT' ##NO_TEXT.

    CONSTANTS:
      c_type_settings   TYPE zif_abapgit_persistence=>ty_type VALUE 'SETTINGS' ##NO_TEXT,
      c_type_repo       TYPE zif_abapgit_persistence=>ty_type VALUE 'REPO' ##NO_TEXT,
      c_type_repo_csum  TYPE zif_abapgit_persistence=>ty_type VALUE 'REPO_CS' ##NO_TEXT,
      c_type_background TYPE zif_abapgit_persistence=>ty_type VALUE 'BACKGROUND' ##NO_TEXT,
      c_type_packages   TYPE zif_abapgit_persistence=>ty_type VALUE 'PACKAGES' ##NO_TEXT,
      c_type_user       TYPE zif_abapgit_persistence=>ty_type VALUE 'USER' ##NO_TEXT.

    CLASS-METHODS get_instance
      RETURNING
        VALUE(ro_db) TYPE REF TO zcl_abapgit_persistence_db .
    METHODS add
      IMPORTING
        !iv_type  TYPE zif_abapgit_persistence=>ty_type
        !iv_value TYPE zif_abapgit_persistence=>ty_content-value
        !iv_data  TYPE zif_abapgit_persistence=>ty_content-data_str
      RAISING
        zcx_abapgit_exception .
    METHODS delete
      IMPORTING
        !iv_type  TYPE zif_abapgit_persistence=>ty_type
        !iv_value TYPE zif_abapgit_persistence=>ty_content-value
      RAISING
        zcx_abapgit_exception .
    METHODS list
      RETURNING
        VALUE(rt_content) TYPE zif_abapgit_persistence=>ty_contents .
    METHODS list_by_type
      IMPORTING
        !iv_type          TYPE zif_abapgit_persistence=>ty_type
      RETURNING
        VALUE(rt_content) TYPE zif_abapgit_persistence=>ty_contents .
    METHODS list_by_keys
      IMPORTING it_keys            TYPE zif_abapgit_persistence=>ty_repo_keys
                iv_type            TYPE zif_abapgit_persistence=>ty_type
      RETURNING VALUE(rt_contents) TYPE zif_abapgit_persistence=>ty_contents.
    METHODS lock
      IMPORTING
        !iv_mode  TYPE enqmode DEFAULT 'E'
        !iv_type  TYPE zif_abapgit_persistence=>ty_type
        !iv_value TYPE zif_abapgit_persistence=>ty_content-value
      RAISING
        zcx_abapgit_exception .
    METHODS modify
      IMPORTING
        !iv_type  TYPE zif_abapgit_persistence=>ty_type
        !iv_value TYPE zif_abapgit_persistence=>ty_content-value
        !iv_data  TYPE zif_abapgit_persistence=>ty_content-data_str
      RAISING
        zcx_abapgit_exception .
    METHODS read
      IMPORTING
        !iv_type       TYPE zif_abapgit_persistence=>ty_type
        !iv_value      TYPE zif_abapgit_persistence=>ty_content-value
      RETURNING
        VALUE(rv_data) TYPE zif_abapgit_persistence=>ty_content-data_str
      RAISING
        zcx_abapgit_not_found .
    METHODS update
      IMPORTING
        !iv_type  TYPE zif_abapgit_persistence=>ty_type
        !iv_value TYPE zif_abapgit_persistence=>ty_content-value
        !iv_data  TYPE zif_abapgit_persistence=>ty_content-data_str
      RAISING
        zcx_abapgit_exception .
  PROTECTED SECTION.
  PRIVATE SECTION.

    CLASS-DATA go_db TYPE REF TO zcl_abapgit_persistence_db .
    DATA mv_update_function TYPE funcname .

    METHODS get_update_function
      RETURNING
        VALUE(rv_funcname) TYPE funcname .
    METHODS validate_and_unprettify_xml
      IMPORTING
        !iv_xml       TYPE string
      RETURNING
        VALUE(rv_xml) TYPE string
      RAISING
        zcx_abapgit_exception .
ENDCLASS.

CLASS ZCL_ABAPGIT_PERSISTENCE_DB IMPLEMENTATION.

  METHOD add.

    DATA ls_table TYPE zif_abapgit_persistence=>ty_content.

    ls_table-type  = iv_type.
    ls_table-value = iv_value.
    ls_table-data_str = iv_data.

    INSERT (c_tabname) FROM ls_table.                     "#EC CI_SUBRC
    ASSERT sy-subrc = 0.

  ENDMETHOD.

  METHOD delete.

    lock( iv_type  = iv_type
          iv_value = iv_value ).

    DELETE FROM (c_tabname)
      WHERE type = iv_type
      AND value = iv_value.
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( 'DB Delete failed' ).
    ENDIF.

  ENDMETHOD.

  METHOD get_instance.

    IF go_db IS NOT BOUND.
      CREATE OBJECT go_db.
    ENDIF.
    ro_db = go_db.

  ENDMETHOD.

  METHOD get_update_function.
    IF mv_update_function IS INITIAL.
      mv_update_function = 'CALL_V1_PING'.
      CALL FUNCTION 'FUNCTION_EXISTS'
        EXPORTING
          funcname = mv_update_function
        EXCEPTIONS
          OTHERS   = 2.

      IF sy-subrc <> 0.
        mv_update_function = 'BANK_OBJ_WORKL_RELEASE_LOCKS'.
      ENDIF.
    ENDIF.
    rv_funcname = mv_update_function.

  ENDMETHOD.

  METHOD list.
    SELECT * FROM (c_tabname)
      INTO TABLE rt_content.                              "#EC CI_SUBRC
  ENDMETHOD.

  METHOD list_by_keys.
    FIELD-SYMBOLS: <ls_key> LIKE LINE OF it_keys.
    LOOP AT it_keys ASSIGNING <ls_key>.
      SELECT * FROM (c_tabname)
      APPENDING TABLE rt_contents
      WHERE value = <ls_key> AND
            type  = iv_type.
    ENDLOOP.
  ENDMETHOD.

  METHOD list_by_type.
    SELECT * FROM (c_tabname)
      INTO TABLE rt_content
      WHERE type = iv_type
      ORDER BY PRIMARY KEY.                               "#EC CI_SUBRC
  ENDMETHOD.

  METHOD lock.
    DATA: lv_dummy_update_function TYPE funcname.

    CALL FUNCTION 'ENQUEUE_EZABAPGIT'
      EXPORTING
        mode_zabapgit  = iv_mode
        type           = iv_type
        value          = iv_value
      EXCEPTIONS
        foreign_lock   = 1
        system_failure = 2
        OTHERS         = 3.
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise_t100( ).
    ENDIF.

    lv_dummy_update_function = get_update_function( ).

* trigger dummy update task to automatically release locks at commit
    CALL FUNCTION lv_dummy_update_function
      IN UPDATE TASK.

  ENDMETHOD.

  METHOD modify.

    DATA: ls_content TYPE zif_abapgit_persistence=>ty_content.

    lock( iv_type  = iv_type
          iv_value = iv_value ).

    ls_content-type  = iv_type.
    ls_content-value = iv_value.
    ls_content-data_str = iv_data.

    MODIFY (c_tabname) FROM ls_content.
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( 'DB modify failed' ).
    ENDIF.

  ENDMETHOD.

  METHOD read.

    SELECT SINGLE data_str FROM (c_tabname) INTO rv_data
      WHERE type = iv_type
      AND value = iv_value.
    IF sy-subrc <> 0.
      RAISE EXCEPTION TYPE zcx_abapgit_not_found.
    ENDIF.

  ENDMETHOD.

  METHOD update.

    DATA lv_data LIKE iv_data.

    lv_data = validate_and_unprettify_xml( iv_data ).

    lock( iv_type  = iv_type
          iv_value = iv_value ).

    UPDATE (c_tabname) SET data_str = lv_data
      WHERE type  = iv_type
      AND value = iv_value.
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( 'DB update failed' ).
    ENDIF.

  ENDMETHOD.

  METHOD validate_and_unprettify_xml.

    rv_xml = zcl_abapgit_xml_pretty=>print(
      iv_xml           = iv_xml
      iv_unpretty      = abap_true
      iv_ignore_errors = abap_false ).

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_persistence_db DEFINITION
  PUBLIC
  CREATE PRIVATE .

  PUBLIC SECTION.
    CONSTANTS c_tabname TYPE c LENGTH 30 VALUE 'ZABAPGIT' ##NO_TEXT.
    CONSTANTS c_lock TYPE c LENGTH 30 VALUE 'EZABAPGIT' ##NO_TEXT.

    CONSTANTS:
      c_type_settings   TYPE zif_abapgit_persistence=>ty_type VALUE 'SETTINGS' ##NO_TEXT,
      c_type_repo       TYPE zif_abapgit_persistence=>ty_type VALUE 'REPO' ##NO_TEXT,
      c_type_repo_csum  TYPE zif_abapgit_persistence=>ty_type VALUE 'REPO_CS' ##NO_TEXT,
      c_type_background TYPE zif_abapgit_persistence=>ty_type VALUE 'BACKGROUND' ##NO_TEXT,
      c_type_packages   TYPE zif_abapgit_persistence=>ty_type VALUE 'PACKAGES' ##NO_TEXT,
      c_type_user       TYPE zif_abapgit_persistence=>ty_type VALUE 'USER' ##NO_TEXT.

    CLASS-METHODS get_instance
      RETURNING
        VALUE(ro_db) TYPE REF TO zcl_abapgit_persistence_db .
    METHODS add
      IMPORTING
        !iv_type  TYPE zif_abapgit_persistence=>ty_type
        !iv_value TYPE zif_abapgit_persistence=>ty_content-value
        !iv_data  TYPE zif_abapgit_persistence=>ty_content-data_str
      RAISING
        zcx_abapgit_exception .
    METHODS delete
      IMPORTING
        !iv_type  TYPE zif_abapgit_persistence=>ty_type
        !iv_value TYPE zif_abapgit_persistence=>ty_content-value
      RAISING
        zcx_abapgit_exception .
    METHODS list
      RETURNING
        VALUE(rt_content) TYPE zif_abapgit_persistence=>ty_contents .
    METHODS list_by_type
      IMPORTING
        !iv_type          TYPE zif_abapgit_persistence=>ty_type
      RETURNING
        VALUE(rt_content) TYPE zif_abapgit_persistence=>ty_contents .
    METHODS list_by_keys
      IMPORTING it_keys            TYPE zif_abapgit_persistence=>ty_repo_keys
                iv_type            TYPE zif_abapgit_persistence=>ty_type
      RETURNING VALUE(rt_contents) TYPE zif_abapgit_persistence=>ty_contents.
    METHODS lock
      IMPORTING
        !iv_mode  TYPE enqmode DEFAULT 'E'
        !iv_type  TYPE zif_abapgit_persistence=>ty_type
        !iv_value TYPE zif_abapgit_persistence=>ty_content-value
      RAISING
        zcx_abapgit_exception .
    METHODS modify
      IMPORTING
        !iv_type  TYPE zif_abapgit_persistence=>ty_type
        !iv_value TYPE zif_abapgit_persistence=>ty_content-value
        !iv_data  TYPE zif_abapgit_persistence=>ty_content-data_str
      RAISING
        zcx_abapgit_exception .
    METHODS read
      IMPORTING
        !iv_type       TYPE zif_abapgit_persistence=>ty_type
        !iv_value      TYPE zif_abapgit_persistence=>ty_content-value
      RETURNING
        VALUE(rv_data) TYPE zif_abapgit_persistence=>ty_content-data_str
      RAISING
        zcx_abapgit_not_found .
    METHODS update
      IMPORTING
        !iv_type  TYPE zif_abapgit_persistence=>ty_type
        !iv_value TYPE zif_abapgit_persistence=>ty_content-value
        !iv_data  TYPE zif_abapgit_persistence=>ty_content-data_str
      RAISING
        zcx_abapgit_exception .
  PROTECTED SECTION.
  PRIVATE SECTION.

    CLASS-DATA go_db TYPE REF TO zcl_abapgit_persistence_db .
    DATA mv_update_function TYPE funcname .

    METHODS get_update_function
      RETURNING
        VALUE(rv_funcname) TYPE funcname .
    METHODS validate_and_unprettify_xml
      IMPORTING
        !iv_xml       TYPE string
      RETURNING
        VALUE(rv_xml) TYPE string
      RAISING
        zcx_abapgit_exception .
ENDCLASS.

CLASS zcl_abapgit_persistence_db IMPLEMENTATION.

  METHOD add.

    DATA ls_table TYPE zif_abapgit_persistence=>ty_content.

    ls_table-type  = iv_type.
    ls_table-value = iv_value.
    ls_table-data_str = iv_data.

    INSERT (c_tabname) FROM ls_table.                     "#EC CI_SUBRC
    ASSERT sy-subrc = 0.

  ENDMETHOD.

  METHOD delete.

    DATA lv_data TYPE string.

    lock( iv_type  = iv_type
          iv_value = iv_value ).

    DELETE FROM (c_tabname)
      WHERE type = iv_type
      AND value = iv_value.
    IF sy-subrc <> 0.
      " If entry is still there? That's an error, otherwise ignore
      SELECT SINGLE data_str FROM (c_tabname) INTO lv_data
        WHERE type = iv_type
        AND value = iv_value.
      IF sy-subrc = 0.
        zcx_abapgit_exception=>raise( 'DB Delete failed' ).
      ENDIF.
    ENDIF.

  ENDMETHOD.

  METHOD get_instance.

    IF go_db IS NOT BOUND.
      CREATE OBJECT go_db.
    ENDIF.
    ro_db = go_db.

  ENDMETHOD.

  METHOD get_update_function.
    IF mv_update_function IS INITIAL.
      mv_update_function = 'CALL_V1_PING'.
      CALL FUNCTION 'FUNCTION_EXISTS'
        EXPORTING
          funcname = mv_update_function
        EXCEPTIONS
          OTHERS   = 2.

      IF sy-subrc <> 0.
        mv_update_function = 'BANK_OBJ_WORKL_RELEASE_LOCKS'.
      ENDIF.
    ENDIF.
    rv_funcname = mv_update_function.

  ENDMETHOD.

  METHOD list.
    SELECT * FROM (c_tabname)
      INTO TABLE rt_content.                              "#EC CI_SUBRC
  ENDMETHOD.

  METHOD list_by_keys.
    FIELD-SYMBOLS: <ls_key> LIKE LINE OF it_keys.
    LOOP AT it_keys ASSIGNING <ls_key>.
      SELECT * FROM (c_tabname)
      APPENDING TABLE rt_contents
      WHERE value = <ls_key> AND
            type  = iv_type.
    ENDLOOP.
  ENDMETHOD.

  METHOD list_by_type.
    SELECT * FROM (c_tabname)
      INTO TABLE rt_content
      WHERE type = iv_type
      ORDER BY PRIMARY KEY.                               "#EC CI_SUBRC
  ENDMETHOD.

  METHOD lock.
    DATA: lv_dummy_update_function TYPE funcname.

    CALL FUNCTION 'ENQUEUE_EZABAPGIT'
      EXPORTING
        mode_zabapgit  = iv_mode
        type           = iv_type
        value          = iv_value
      EXCEPTIONS
        foreign_lock   = 1
        system_failure = 2
        OTHERS         = 3.
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise_t100( ).
    ENDIF.

    lv_dummy_update_function = get_update_function( ).

* trigger dummy update task to automatically release locks at commit
    CALL FUNCTION lv_dummy_update_function
      IN UPDATE TASK.

  ENDMETHOD.

  METHOD modify.

    DATA: ls_content TYPE zif_abapgit_persistence=>ty_content.

    lock( iv_type  = iv_type
          iv_value = iv_value ).

    ls_content-type  = iv_type.
    ls_content-value = iv_value.
    ls_content-data_str = iv_data.

    MODIFY (c_tabname) FROM ls_content.
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( 'DB modify failed' ).
    ENDIF.

  ENDMETHOD.

  METHOD read.

    SELECT SINGLE data_str FROM (c_tabname) INTO rv_data
      WHERE type = iv_type
      AND value = iv_value.
    IF sy-subrc <> 0.
      RAISE EXCEPTION TYPE zcx_abapgit_not_found.
    ENDIF.

  ENDMETHOD.

  METHOD update.

    DATA lv_data LIKE iv_data.

    lv_data = validate_and_unprettify_xml( iv_data ).

    lock( iv_type  = iv_type
          iv_value = iv_value ).

    UPDATE (c_tabname) SET data_str = lv_data
      WHERE type  = iv_type
      AND value = iv_value.
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( 'DB update failed' ).
    ENDIF.

  ENDMETHOD.

  METHOD validate_and_unprettify_xml.

    rv_xml = zcl_abapgit_xml_pretty=>print(
      iv_xml           = iv_xml
      iv_unpretty      = abap_true
      iv_ignore_errors = abap_false ).

  ENDMETHOD.
ENDCLASS.

Fix "DB delete failed" (#5517)

Fix "DB delete failed" (#5517)

Closes #5515

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


CLASS lcl_helper DEFINITION FINAL.

  PUBLIC SECTION.
    CLASS-METHODS get_sap_basis_component
      RETURNING
        VALUE(rs_result) TYPE cvers_sdu
      RAISING
        zcx_abapgit_exception.

ENDCLASS.

CLASS lcl_helper IMPLEMENTATION.

  METHOD get_sap_basis_component.

    DATA:
      lt_installed TYPE STANDARD TABLE OF cvers_sdu.

    CALL FUNCTION 'DELIVERY_GET_INSTALLED_COMPS'
      TABLES
        tt_comptab       = lt_installed
      EXCEPTIONS
        no_release_found = 1
        OTHERS           = 2.
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from DELIVERY_GET_INSTALLED_COMPS { sy-subrc }| ).
    ENDIF.

    READ TABLE lt_installed INTO rs_result
      WITH KEY component = `SAP_BASIS`.
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Component SAP_BASIS not found| ).
    ENDIF.

  ENDMETHOD.

ENDCLASS.

CLASS ltcl_lower_release DEFINITION FINAL
  FOR TESTING
  RISK LEVEL HARMLESS
  DURATION SHORT.

  PUBLIC SECTION.

  PRIVATE SECTION.
    METHODS empty_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS lower_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS same_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS higher_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

ENDCLASS.

CLASS ltcl_lower_release IMPLEMENTATION.

  METHOD empty_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release - 1.
    ls_requirement-min_patch   = 0.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

  METHOD lower_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release - 1.
    ls_requirement-min_patch   = ls_component-extrelease - 1.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

  METHOD same_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release - 1.
    ls_requirement-min_patch   = ls_component-extrelease.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

  METHOD higher_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release - 1.
    ls_requirement-min_patch   = ls_component-extrelease + 1.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

ENDCLASS.

CLASS ltcl_same_release DEFINITION FINAL
  FOR TESTING
  RISK LEVEL HARMLESS
  DURATION SHORT.

  PUBLIC SECTION.

  PRIVATE SECTION.
    METHODS empty_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS lower_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS same_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS higher_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

ENDCLASS.

CLASS ltcl_same_release IMPLEMENTATION.

  METHOD empty_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release.
    ls_requirement-min_patch   = 0.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

  METHOD lower_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release.
    ls_requirement-min_patch   = ls_component-extrelease - 1.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

  METHOD same_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release.
    ls_requirement-min_patch   = ls_component-extrelease.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

  METHOD higher_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release.
    ls_requirement-min_patch   = ls_component-extrelease + 1.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_no ).

  ENDMETHOD.

ENDCLASS.

CLASS ltcl_higher_release DEFINITION FINAL
  FOR TESTING
  RISK LEVEL HARMLESS
  DURATION SHORT.

  PUBLIC SECTION.

  PRIVATE SECTION.
    METHODS empty_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS lower_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS same_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS higher_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

ENDCLASS.

CLASS ltcl_higher_release IMPLEMENTATION.

  METHOD empty_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release + 1.
    ls_requirement-min_patch   = 0.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_no ).

  ENDMETHOD.

  METHOD lower_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release + 1.
    ls_requirement-min_patch   = ls_component-extrelease - 1.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_no ).

  ENDMETHOD.

  METHOD same_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release + 1.
    ls_requirement-min_patch   = ls_component-extrelease.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_no ).

  ENDMETHOD.

  METHOD higher_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release + 1.
    ls_requirement-min_patch   = ls_component-extrelease + 1.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_no ).

  ENDMETHOD.

ENDCLASS.

CLASS ltcl_formats DEFINITION FINAL
  FOR TESTING
  RISK LEVEL HARMLESS
  DURATION SHORT.

  PUBLIC SECTION.

  PRIVATE SECTION.
    METHODS shorter_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS higher_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

ENDCLASS.

CLASS ltcl_formats IMPLEMENTATION.

  METHOD shorter_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release.

    CALL FUNCTION 'CONVERSION_EXIT_ALPHA_OUTPUT'
      EXPORTING
        input  = ls_component-extrelease
      IMPORTING
        output = ls_requirement-min_patch.

    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

  METHOD higher_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release.

    CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
      EXPORTING
        input  = ls_component-extrelease
      IMPORTING
        output = ls_requirement-min_patch.

    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

ENDCLASS.

CLASS lcl_helper DEFINITION FINAL.

  PUBLIC SECTION.
    CLASS-METHODS get_sap_basis_component
      RETURNING
        VALUE(rs_result) TYPE cvers_sdu
      RAISING
        zcx_abapgit_exception.

ENDCLASS.

CLASS lcl_helper IMPLEMENTATION.

  METHOD get_sap_basis_component.

    DATA:
      lt_installed TYPE STANDARD TABLE OF cvers_sdu.

    CALL FUNCTION 'DELIVERY_GET_INSTALLED_COMPS'
      TABLES
        tt_comptab       = lt_installed
      EXCEPTIONS
        no_release_found = 1
        OTHERS           = 2.
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from DELIVERY_GET_INSTALLED_COMPS { sy-subrc }| ).
    ENDIF.

    READ TABLE lt_installed INTO rs_result
      WITH KEY component = `SAP_BASIS`.
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Component SAP_BASIS not found| ).
    ENDIF.

  ENDMETHOD.

ENDCLASS.

CLASS ltcl_lower_release DEFINITION FINAL
  FOR TESTING
  RISK LEVEL HARMLESS
  DURATION SHORT.

  PUBLIC SECTION.

  PRIVATE SECTION.
    METHODS empty_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS lower_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS same_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS higher_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

ENDCLASS.

CLASS ltcl_lower_release IMPLEMENTATION.

  METHOD empty_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release - 1.
    ls_requirement-min_patch   = 0.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

  METHOD lower_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release - 1.
    ls_requirement-min_patch   = nmax(
                                   val1 = ls_component-extrelease - 1
                                   val2 = 0 ).
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

  METHOD same_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release - 1.
    ls_requirement-min_patch   = ls_component-extrelease.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

  METHOD higher_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release - 1.
    ls_requirement-min_patch   = ls_component-extrelease + 1.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

ENDCLASS.

CLASS ltcl_same_release DEFINITION FINAL
  FOR TESTING
  RISK LEVEL HARMLESS
  DURATION SHORT.

  PUBLIC SECTION.

  PRIVATE SECTION.
    METHODS empty_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS lower_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS same_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS higher_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

ENDCLASS.

CLASS ltcl_same_release IMPLEMENTATION.

  METHOD empty_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release.
    ls_requirement-min_patch   = 0.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

  METHOD lower_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release.
    ls_requirement-min_patch   = nmax(
                                   val1 = ls_component-extrelease - 1
                                   val2 = 0 ).
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

  METHOD same_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release.
    ls_requirement-min_patch   = ls_component-extrelease.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

  METHOD higher_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release.
    ls_requirement-min_patch   = ls_component-extrelease + 1.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_no ).

  ENDMETHOD.

ENDCLASS.

CLASS ltcl_higher_release DEFINITION FINAL
  FOR TESTING
  RISK LEVEL HARMLESS
  DURATION SHORT.

  PUBLIC SECTION.

  PRIVATE SECTION.
    METHODS empty_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS lower_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS same_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS higher_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

ENDCLASS.

CLASS ltcl_higher_release IMPLEMENTATION.

  METHOD empty_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release + 1.
    ls_requirement-min_patch   = 0.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_no ).

  ENDMETHOD.

  METHOD lower_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release + 1.
    ls_requirement-min_patch   = ls_component-extrelease - 1.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_no ).

  ENDMETHOD.

  METHOD same_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release + 1.
    ls_requirement-min_patch   = ls_component-extrelease.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_no ).

  ENDMETHOD.

  METHOD higher_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release + 1.
    ls_requirement-min_patch   = ls_component-extrelease + 1.
    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_no ).

  ENDMETHOD.

ENDCLASS.

CLASS ltcl_formats DEFINITION FINAL
  FOR TESTING
  RISK LEVEL HARMLESS
  DURATION SHORT.

  PUBLIC SECTION.

  PRIVATE SECTION.
    METHODS shorter_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

    METHODS higher_patch FOR TESTING
      RAISING
        zcx_abapgit_exception.

ENDCLASS.

CLASS ltcl_formats IMPLEMENTATION.

  METHOD shorter_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release.

    CALL FUNCTION 'CONVERSION_EXIT_ALPHA_OUTPUT'
      EXPORTING
        input  = ls_component-extrelease
      IMPORTING
        output = ls_requirement-min_patch.

    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

  METHOD higher_patch.

    DATA:
      ls_component    TYPE cvers_sdu,
      lt_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt,
      ls_requirement  LIKE LINE OF lt_requirements.

    ls_component = lcl_helper=>get_sap_basis_component( ).

    ls_requirement-component   = ls_component-component.
    ls_requirement-min_release = ls_component-release.

    CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
      EXPORTING
        input  = ls_component-extrelease
      IMPORTING
        output = ls_requirement-min_patch.

    APPEND ls_requirement TO lt_requirements.

    cl_abap_unit_assert=>assert_equals(
        act = zcl_abapgit_requirement_helper=>is_requirements_met( lt_requirements )
        exp = zif_abapgit_definitions=>c_yes ).

  ENDMETHOD.

ENDCLASS.

Fix failing unit test for SAP_BASIS SP00 (#5801)

* Fix failing unit test for SAP_BASIS SP00

before this fix ls_requirement-min_patch gets negative for SP00 and therefore failing

* fix linter

Co-authored-by: Marc Bernard <b87df29b98274de0b03db8f81c149b8b52bd050e@users.noreply.github.com>


CLASS ltcl_objects_files DEFINITION FOR TESTING
  DURATION SHORT RISK LEVEL HARMLESS.

  PRIVATE SECTION.
    DATA: mo_cut TYPE REF TO zcl_abapgit_objects_files.

    METHODS setup.

    METHODS get_program_data
      IMPORTING
        iv_line_break TYPE clike
      RETURNING VALUE(rv_result) TYPE xstring.

    METHODS get_xml_data
      RETURNING VALUE(rv_result) TYPE xstring.

    METHODS get_expected_abap_source
      RETURNING VALUE(rt_result) TYPE abaptxt255_tab.

    METHODS read_abap FOR TESTING
      RAISING
        cx_static_check.

    METHODS read_xml FOR TESTING
      RAISING
        cx_static_check.

ENDCLASS.

CLASS ltcl_objects_files IMPLEMENTATION.

  METHOD setup.
    DATA: lt_files TYPE zif_abapgit_definitions=>ty_files_tt,
          ls_item TYPE zif_abapgit_definitions=>ty_item.
    FIELD-SYMBOLS: <ls_files> LIKE LINE OF lt_files.

    APPEND INITIAL LINE TO lt_files ASSIGNING <ls_files>.
    <ls_files>-filename = 'zlf.prog.abap'.
    <ls_files>-data = get_program_data( zif_abapgit_definitions=>c_newline ).
    APPEND INITIAL LINE TO lt_files ASSIGNING <ls_files>.
    <ls_files>-filename = 'zlf.prog.xml'.
    <ls_files>-data = get_xml_data( ).

    ls_item-obj_type = 'prog'.
    ls_item-obj_name = 'zlf'.
    CREATE OBJECT mo_cut
      EXPORTING
        is_item = ls_item.
    mo_cut->set_files( lt_files ).

  ENDMETHOD.

  METHOD get_program_data.
    DATA: lv_abap_source TYPE string.

    CONCATENATE 'My first program' iv_line_break 'has syntax errors' INTO lv_abap_source.
    rv_result = zcl_abapgit_convert=>string_to_xstring_utf8( lv_abap_source ).

  ENDMETHOD.

  METHOD get_xml_data.
    DATA: lv_xml_source TYPE string.

    lv_xml_source = '<?xml version="1.0" encoding="utf-8"?>' &&
      '<abapGit version="v1.0.0" serializer="LCL_OBJECT_PROG" serializer_version="v1.0.0">' &&
        '<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">' &&
          '<asx:values>' &&
            '<PROGDIR>' &&
              '<NAME>ZLF</NAME>' &&
              '<SUBC>1</SUBC>' &&
              '<RLOAD>E</RLOAD>' &&
              '<FIXPT>X</FIXPT>' &&
              '<UCCHECK>X</UCCHECK>' &&
            '</PROGDIR>' &&
          '</asx:values>' &&
        '</asx:abap>' &&
      '</abapGit>'.

    rv_result = zcl_abapgit_convert=>string_to_xstring_utf8( lv_xml_source ).

  ENDMETHOD.

  METHOD get_expected_abap_source.

    APPEND 'My first program' TO rt_result.
    APPEND 'has syntax errors' TO rt_result.

  ENDMETHOD.

  METHOD read_abap.

    cl_abap_unit_assert=>assert_equals( exp = get_expected_abap_source( )
      act = mo_cut->read_abap( ) ).

  ENDMETHOD.

  METHOD read_xml.
    DATA: BEGIN OF ls_exp_prog_metadata,
      name TYPE progname VALUE 'ZLF',
      subc(1) TYPE c VALUE '1',
      rload(1) TYPE c VALUE 'E',
      fixpt(1) TYPE c VALUE 'X',
      uccheck(1) TYPE c VALUE 'X',
    END OF ls_exp_prog_metadata,
    ls_act_prog_metadata LIKE ls_exp_prog_metadata,
    ls_exp_metadata TYPE zif_abapgit_definitions=>ty_metadata.

    ls_exp_metadata-class = 'LCL_OBJECT_PROG'.
    ls_exp_metadata-version = 'v1.0.0'.

    mo_cut->read_xml( )->read( EXPORTING iv_name = 'PROGDIR' CHANGING cg_data = ls_act_prog_metadata ).

    cl_abap_unit_assert=>assert_equals( exp = ls_exp_prog_metadata
      act = ls_act_prog_metadata ).
    cl_abap_unit_assert=>assert_equals( exp = ls_exp_metadata
      act = mo_cut->read_xml( )->get_metadata( ) ).

  ENDMETHOD.

ENDCLASS.

CLASS ltcl_objects_files DEFINITION FOR TESTING
  DURATION SHORT RISK LEVEL HARMLESS.

  PRIVATE SECTION.
    DATA: mo_cut TYPE REF TO zcl_abapgit_objects_files.

    METHODS setup.

    METHODS get_program_data
      IMPORTING
        iv_line_break TYPE clike
      RETURNING VALUE(rv_result) TYPE xstring.

    METHODS get_xml_data
      RETURNING VALUE(rv_result) TYPE xstring.

    METHODS get_expected_abap_source
      RETURNING VALUE(rt_result) TYPE abaptxt255_tab.

    METHODS read_abap FOR TESTING
      RAISING
        cx_static_check.

    METHODS read_xml FOR TESTING
      RAISING
        cx_static_check.

ENDCLASS.

CLASS ltcl_objects_files IMPLEMENTATION.

  METHOD setup.
    DATA: lt_files TYPE zif_abapgit_definitions=>ty_files_tt,
          ls_item TYPE zif_abapgit_definitions=>ty_item.
    FIELD-SYMBOLS: <ls_files> LIKE LINE OF lt_files.

    APPEND INITIAL LINE TO lt_files ASSIGNING <ls_files>.
    <ls_files>-filename = 'zlf.prog.abap'.
    <ls_files>-data = get_program_data( zif_abapgit_definitions=>c_newline ).
    APPEND INITIAL LINE TO lt_files ASSIGNING <ls_files>.
    <ls_files>-filename = 'zlf.prog.xml'.
    <ls_files>-data = get_xml_data( ).

    ls_item-obj_type = 'prog'.
    ls_item-obj_name = 'zlf'.
    CREATE OBJECT mo_cut
      EXPORTING
        is_item = ls_item.
    mo_cut->set_files( lt_files ).

  ENDMETHOD.

  METHOD get_program_data.
    DATA: lv_abap_source TYPE string.

    CONCATENATE 'My first program' iv_line_break 'has syntax errors' INTO lv_abap_source.
    rv_result = zcl_abapgit_convert=>string_to_xstring_utf8( lv_abap_source ).

  ENDMETHOD.

  METHOD get_xml_data.
    DATA: lv_xml_source TYPE string.

    lv_xml_source = '<?xml version="1.0" encoding="utf-8"?>' &&
      '<abapGit version="v1.0.0" serializer="LCL_OBJECT_PROG" serializer_version="v1.0.0">' &&
        '<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">' &&
          '<asx:values>' &&
            '<PROGDIR>' &&
              '<NAME>ZLF</NAME>' &&
              '<SUBC>1</SUBC>' &&
              '<RLOAD>E</RLOAD>' &&
              '<FIXPT>X</FIXPT>' &&
              '<UCCHECK>X</UCCHECK>' &&
            '</PROGDIR>' &&
          '</asx:values>' &&
        '</asx:abap>' &&
      '</abapGit>'.

    rv_result = zcl_abapgit_convert=>string_to_xstring_utf8( lv_xml_source ).

  ENDMETHOD.

  METHOD get_expected_abap_source.

    APPEND 'My first program' TO rt_result.
    APPEND 'has syntax errors' TO rt_result.

  ENDMETHOD.

  METHOD read_abap.

    cl_abap_unit_assert=>assert_equals( exp = get_expected_abap_source( )
      act = mo_cut->read_abap( ) ).

  ENDMETHOD.

  METHOD read_xml.
    DATA: BEGIN OF ls_exp_prog_metadata,
      name TYPE progname VALUE 'ZLF',
      subc(1) TYPE c VALUE '1',
      rload(1) TYPE c VALUE 'E',
      fixpt(1) TYPE c VALUE 'X',
      uccheck(1) TYPE c VALUE 'X',
    END OF ls_exp_prog_metadata,
    ls_act_prog_metadata LIKE ls_exp_prog_metadata,
    ls_exp_metadata TYPE zif_abapgit_definitions=>ty_metadata,
    li_xml TYPE REF TO zif_abapgit_xml_input.

    ls_exp_metadata-class = 'LCL_OBJECT_PROG'.
    ls_exp_metadata-version = 'v1.0.0'.

    li_xml = mo_cut->read_xml( ).
    li_xml->read( EXPORTING iv_name = 'PROGDIR' CHANGING cg_data = ls_act_prog_metadata ).

    cl_abap_unit_assert=>assert_equals( exp = ls_exp_prog_metadata
      act = ls_act_prog_metadata ).
    cl_abap_unit_assert=>assert_equals( exp = ls_exp_metadata
      act = mo_cut->read_xml( )->get_metadata( ) ).

  ENDMETHOD.

ENDCLASS.

Fix syntax errors in 702 (#3962)

Fix syntax errors in 702 (#3962)

Only relevant if trying to run dev version in 702


CLASS zcl_abapgit_gui_page_repo_over DEFINITION
  PUBLIC
  INHERITING FROM zcl_abapgit_gui_page
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    METHODS constructor .

    METHODS zif_abapgit_gui_page~on_event
        REDEFINITION .

  PROTECTED SECTION.
    METHODS render_content
        REDEFINITION .

  PRIVATE SECTION.
    TYPES:
      BEGIN OF ty_overview,
        favorite TYPE string,
        type     TYPE string,
        key      TYPE string,
        name     TYPE string,
        url      TYPE string,
        package  TYPE string,
        branch   TYPE string,
      END OF ty_overview,
      tty_overview TYPE STANDARD TABLE OF ty_overview
                   WITH NON-UNIQUE DEFAULT KEY.
    CONSTANTS:
      BEGIN OF gc_action,
        delete          TYPE string VALUE 'delete',
        select          TYPE string VALUE 'select',
        change_order_by TYPE string VALUE 'change_order_by',
        direction       TYPE string VALUE 'direction',
        apply_filter    TYPE string VALUE 'apply_filter',
      END OF gc_action .

    DATA:
      mv_order_by         TYPE string,
      mv_order_descending TYPE char01,
      mv_filter           TYPE string.

    METHODS:
      render_text_input
        IMPORTING iv_name        TYPE string
                  iv_label       TYPE string
                  iv_value       TYPE string OPTIONAL
                  iv_max_length  TYPE string OPTIONAL
        RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html,

      parse_change_order_by
        IMPORTING
          it_postdata TYPE cnht_post_data_tab,

      parse_direction
        IMPORTING
          it_postdata TYPE cnht_post_data_tab,

      parse_filter
        IMPORTING
          it_postdata TYPE cnht_post_data_tab,

      add_order_by_option
        IMPORTING
          iv_option TYPE string
          io_html   TYPE REF TO zcl_abapgit_html,

      add_direction_option
        IMPORTING
          iv_option   TYPE string
          io_html     TYPE REF TO zcl_abapgit_html
          iv_selected TYPE abap_bool,

      apply_order_by
        CHANGING
          ct_overview TYPE zcl_abapgit_gui_page_repo_over=>tty_overview,

      apply_filter
        CHANGING
          ct_overview TYPE zcl_abapgit_gui_page_repo_over=>tty_overview,

      map_repo_list_to_overview
        IMPORTING
          it_repo_list       TYPE zif_abapgit_persistence=>tt_repo
        RETURNING
          VALUE(rt_overview) TYPE zcl_abapgit_gui_page_repo_over=>tty_overview
        RAISING
          zcx_abapgit_exception.

ENDCLASS.

CLASS zcl_abapgit_gui_page_repo_over IMPLEMENTATION.

  METHOD add_direction_option.

    DATA: lv_selected TYPE string.

    IF iv_selected = abap_true.
      lv_selected = 'selected'.
    ENDIF.

    io_html->add( |<option value="{ iv_option }" { lv_selected }>|
               && |{ to_mixed( iv_option ) }</option>| ).

  ENDMETHOD.

  METHOD add_order_by_option.

    DATA: lv_selected TYPE string.

    IF mv_order_by = iv_option.
      lv_selected = 'selected'.
    ENDIF.

    io_html->add( |<option value="{ iv_option }" { lv_selected }>|
               && |{ to_mixed( iv_option ) }</option>| ).

  ENDMETHOD.

  METHOD apply_filter.

    IF mv_filter IS NOT INITIAL.

      DELETE ct_overview WHERE key     NS mv_filter
                           AND name    NS mv_filter
                           AND url     NS mv_filter
                           AND package NS mv_filter
                           AND branch  NS mv_filter.

    ENDIF.

  ENDMETHOD.

  METHOD apply_order_by.

    DATA:
      lt_sort TYPE abap_sortorder_tab,
      ls_sort LIKE LINE OF lt_sort.

    IF mv_order_by IS NOT INITIAL.

      ls_sort-name       = mv_order_by.
      ls_sort-descending = mv_order_descending.
      ls_sort-astext     = abap_true.
      INSERT ls_sort INTO TABLE lt_sort.
      SORT ct_overview BY (lt_sort).

    ENDIF.

  ENDMETHOD.

  METHOD constructor.

    super->constructor( ).
    ms_control-page_title = |Repository Overview|.
    mv_order_by = |NAME|.

  ENDMETHOD.  " constructor.

  METHOD map_repo_list_to_overview.

    DATA: ls_overview LIKE LINE OF rt_overview,
          lo_repo_srv TYPE REF TO zcl_abapgit_repo,
          lo_user     TYPE REF TO zcl_abapgit_persistence_user.

    FIELD-SYMBOLS: <ls_repo> LIKE LINE OF it_repo_list.

    lo_user = zcl_abapgit_persistence_user=>get_instance( ).

    LOOP AT it_repo_list ASSIGNING <ls_repo>.

      CLEAR: ls_overview.
      lo_repo_srv = zcl_abapgit_repo_srv=>get_instance( )->get( <ls_repo>-key ).

      ls_overview-favorite = lo_user->is_favorite_repo( <ls_repo>-key ).
      ls_overview-type     = <ls_repo>-offline.
      ls_overview-key      = <ls_repo>-key.
      ls_overview-name     = lo_repo_srv->get_name( ).
      ls_overview-url      = <ls_repo>-url.
      ls_overview-package  = <ls_repo>-package.
      ls_overview-branch   = zcl_abapgit_git_branch_list=>get_display_name( <ls_repo>-branch_name ).
      INSERT ls_overview INTO TABLE rt_overview.

    ENDLOOP.

  ENDMETHOD.

  METHOD parse_change_order_by.

    FIELD-SYMBOLS: <ls_postdata> TYPE cnht_post_data_line.

    READ TABLE it_postdata ASSIGNING <ls_postdata>
                           INDEX 1.
    IF sy-subrc = 0.
      FIND FIRST OCCURRENCE OF REGEX `orderBy=(.*)`
           IN <ls_postdata>
           SUBMATCHES mv_order_by.
    ENDIF.

    mv_order_by = condense( mv_order_by ).

  ENDMETHOD.

  METHOD parse_direction.

    DATA: lv_direction TYPE string.

    FIELD-SYMBOLS: <ls_postdata> TYPE cnht_post_data_line.

    CLEAR: mv_order_descending.

    READ TABLE it_postdata ASSIGNING <ls_postdata>
                           INDEX 1.
    IF sy-subrc = 0.
      FIND FIRST OCCURRENCE OF REGEX `direction=(.*)`
           IN <ls_postdata>
           SUBMATCHES lv_direction.
    ENDIF.

    IF condense( lv_direction ) = 'DESCENDING'.
      mv_order_descending = abap_true.
    ENDIF.

  ENDMETHOD.

  METHOD parse_filter.

    FIELD-SYMBOLS: <ls_postdata> LIKE LINE OF it_postdata.

    READ TABLE it_postdata ASSIGNING <ls_postdata>
                           INDEX 1.
    IF sy-subrc = 0.
      FIND FIRST OCCURRENCE OF REGEX `filter=(.*)`
           IN <ls_postdata>
           SUBMATCHES mv_filter.
    ENDIF.

    mv_filter = condense( mv_filter ).

  ENDMETHOD.

  METHOD render_content.

    DATA: lv_trclass          TYPE string,
          lo_persistence_repo TYPE REF TO zcl_abapgit_persistence_repo,
          lt_overview         TYPE tty_overview,
          lv_type_icon        TYPE string,
          lv_favorite_icon    TYPE string.

    FIELD-SYMBOLS: <ls_overview> LIKE LINE OF lt_overview.

    CREATE OBJECT lo_persistence_repo.

    lt_overview = map_repo_list_to_overview( lo_persistence_repo->list( ) ).

    apply_order_by( CHANGING ct_overview = lt_overview ).

    apply_filter( CHANGING ct_overview = lt_overview ).

    CREATE OBJECT ro_html.

    ro_html->add( |
| ).

    ro_html->add( |<form id="commit_form" class="grey70"|
               && | method="post" action="sapevent:{ gc_action-apply_filter }">| ).

    ro_html->add( |
| ).

    ro_html->add( |Order by: <select name="order_by" onchange="onOrderByChange(this)">| ).

    add_order_by_option( iv_option = |TYPE|
                         io_html   = ro_html ).

    add_order_by_option( iv_option = |KEY|
                         io_html   = ro_html ).

    add_order_by_option( iv_option = |NAME|
                         io_html   = ro_html ).

    add_order_by_option( iv_option = |URL|
                         io_html   = ro_html ).

    add_order_by_option( iv_option = |PACKAGE|
                         io_html   = ro_html ).

    add_order_by_option( iv_option = |BRANCH|
                         io_html   = ro_html ).

    ro_html->add( |</select>| ).

    ro_html->add( |<select name="direction" onchange="onDirectionChange(this)">| ).

    add_direction_option( iv_option   = |ASCENDING|
                          iv_selected = mv_order_descending
                          io_html     = ro_html ).

    add_direction_option( iv_option   = |DESCENDING|
                          iv_selected = mv_order_descending
                          io_html     = ro_html ).

    ro_html->add( |</select>| ).

    ro_html->add( render_text_input( iv_name  = |filter|
                                     iv_label = |Filter: |
                                     iv_value = mv_filter ) ).

    ro_html->add( |<input type="submit" class="hidden-submit">| ).

    ro_html->add( |
| ).

    ro_html->add( |</form>| ).
    ro_html->add( |
| ).

    ro_html->add( |
| ).
    ro_html->add( |
| ).

    " Header
    ro_html->add( |
| ).
    ro_html->add( |
| ).
    ro_html->add( |Favorite	| ).
    ro_html->add( |Type	| ).
    ro_html->add( |Key	| ).
    ro_html->add( |Name	| ).
    ro_html->add( |Url	| ).
    ro_html->add( |Package	| ).
    ro_html->add( |Branch name	| ).
    ro_html->add( |	| ).
    ro_html->add( '
' ).
    ro_html->add( '
' ).
    ro_html->add( '
' ).

    LOOP AT lt_overview ASSIGNING <ls_overview>.
      CLEAR lv_trclass.
      IF sy-tabix = 1.
        lv_trclass = ' class="firstrow"' ##NO_TEXT.
      ENDIF.

      IF <ls_overview>-type = abap_true.
        lv_type_icon = 'plug/darkgrey'.
      ELSE.
        lv_type_icon = 'cloud-upload/blue'.
      ENDIF.

      IF <ls_overview>-favorite = abap_true.
        lv_favorite_icon = 'star/blue'.
      ELSE.
        lv_favorite_icon = 'star/grey'.
      ENDIF.

      ro_html->add( |
| ).
      ro_html->add( || ).
      ro_html->add_a( iv_act = |{ zif_abapgit_definitions=>gc_action-repo_toggle_fav }?{ <ls_overview>-key }|
                      iv_txt = zcl_abapgit_html=>icon( iv_name  = lv_favorite_icon
                                                       iv_class = 'pad-sides'
                                                       iv_hint  = 'Click to toggle favorite' ) ).
      ro_html->add( |	| ).
      ro_html->add( |{ zcl_abapgit_html=>icon( lv_type_icon )  }	| ).

      ro_html->add( |{ <ls_overview>-key }	| ).
      ro_html->add( |{ zcl_abapgit_html=>a( iv_txt = <ls_overview>-name
                                                iv_act = |{ gc_action-select }?{ <ls_overview>-key }| ) }	| ).
      ro_html->add( |{ <ls_overview>-url }	| ).
      ro_html->add( |{ <ls_overview>-package }	| ).
      ro_html->add( |{ <ls_overview>-branch }	| ).
      ro_html->add( || ).
      ro_html->add( |	| ).
      ro_html->add( |
| ).
    ENDLOOP.

    ro_html->add( |
| ).
    ro_html->add( |
| ).
    ro_html->add( |
| ).

  ENDMETHOD.            "render_content

  METHOD render_text_input.

    DATA lv_attrs TYPE string.

    CREATE OBJECT ro_html.

    IF iv_value IS NOT INITIAL.
      lv_attrs = | value="{ iv_value }"|.
    ENDIF.

    IF iv_max_length IS NOT INITIAL.
      lv_attrs = | maxlength="{ iv_max_length }"|.
    ENDIF.

    ro_html->add( |{ iv_label }| ).
    ro_html->add( |<input id="{ iv_name }" name="{ iv_name }" type="text"{ lv_attrs }>| ).

  ENDMETHOD.  " render_text_input

  METHOD zif_abapgit_gui_page~on_event.

    DATA: lv_key  TYPE zif_abapgit_persistence=>ty_value.

    CASE iv_action.
      WHEN gc_action-select.

        lv_key = iv_getdata.

        zcl_abapgit_persistence_user=>get_instance( )->set_repo_show( lv_key ).

        TRY.
            zcl_abapgit_repo_srv=>get_instance( )->get( lv_key )->refresh( ).
          CATCH zcx_abapgit_exception ##NO_HANDLER.
        ENDTRY.

        ev_state = zif_abapgit_definitions=>gc_event_state-go_back.

      WHEN gc_action-change_order_by.

        parse_change_order_by( it_postdata ).
        ev_state = zif_abapgit_definitions=>gc_event_state-re_render.

      WHEN gc_action-direction.

        parse_direction( it_postdata ).
        ev_state = zif_abapgit_definitions=>gc_event_state-re_render.

      WHEN gc_action-apply_filter.

        parse_filter( it_postdata ).
        ev_state = zif_abapgit_definitions=>gc_event_state-re_render.

    ENDCASE.

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_gui_page_repo_over DEFINITION
  PUBLIC
  INHERITING FROM zcl_abapgit_gui_page
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    METHODS constructor .

    METHODS zif_abapgit_gui_page~on_event
        REDEFINITION .

  PROTECTED SECTION.
    METHODS render_content
        REDEFINITION .

  PRIVATE SECTION.
    TYPES:
      BEGIN OF ty_overview,
        favorite   TYPE string,
        type       TYPE string,
        key        TYPE string,
        name       TYPE string,
        url        TYPE string,
        package    TYPE string,
        branch     TYPE string,
        created_by TYPE string,
        created_at TYPE string,
      END OF ty_overview,
      tty_overview TYPE STANDARD TABLE OF ty_overview
                   WITH NON-UNIQUE DEFAULT KEY.
    CONSTANTS:
      BEGIN OF gc_action,
        delete          TYPE string VALUE 'delete',
        select          TYPE string VALUE 'select',
        change_order_by TYPE string VALUE 'change_order_by',
        direction       TYPE string VALUE 'direction',
        apply_filter    TYPE string VALUE 'apply_filter',
      END OF gc_action .

    DATA:
      mv_order_by         TYPE string,
      mv_order_descending TYPE char01,
      mv_filter           TYPE string.

    METHODS:
      render_text_input
        IMPORTING iv_name        TYPE string
                  iv_label       TYPE string
                  iv_value       TYPE string OPTIONAL
                  iv_max_length  TYPE string OPTIONAL
        RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html,

      parse_change_order_by
        IMPORTING
          it_postdata TYPE cnht_post_data_tab,

      parse_direction
        IMPORTING
          it_postdata TYPE cnht_post_data_tab,

      parse_filter
        IMPORTING
          it_postdata TYPE cnht_post_data_tab,

      add_order_by_option
        IMPORTING
          iv_option TYPE string
          io_html   TYPE REF TO zcl_abapgit_html,

      add_direction_option
        IMPORTING
          iv_option   TYPE string
          io_html     TYPE REF TO zcl_abapgit_html
          iv_selected TYPE abap_bool,

      apply_order_by
        CHANGING
          ct_overview TYPE zcl_abapgit_gui_page_repo_over=>tty_overview,

      apply_filter
        CHANGING
          ct_overview TYPE zcl_abapgit_gui_page_repo_over=>tty_overview,

      map_repo_list_to_overview
        IMPORTING
          it_repo_list       TYPE zif_abapgit_persistence=>tt_repo
        RETURNING
          VALUE(rt_overview) TYPE zcl_abapgit_gui_page_repo_over=>tty_overview
        RAISING
          zcx_abapgit_exception,

      render_table_header
        IMPORTING
          io_html TYPE REF TO zcl_abapgit_html,

      render_table
        IMPORTING
          io_html     TYPE REF TO zcl_abapgit_html
          it_overview TYPE zcl_abapgit_gui_page_repo_over=>tty_overview,

      render_table_body
        IMPORTING
          io_html     TYPE REF TO zcl_abapgit_html
          it_overview TYPE zcl_abapgit_gui_page_repo_over=>tty_overview,

      render_order_by
        IMPORTING
          io_html TYPE REF TO zcl_abapgit_html,

      render_order_by_direction
        IMPORTING
          io_html TYPE REF TO zcl_abapgit_html,

      render_header_bar
        IMPORTING
          io_html TYPE REF TO zcl_abapgit_html.

ENDCLASS.

CLASS zcl_abapgit_gui_page_repo_over IMPLEMENTATION.

  METHOD add_direction_option.

    DATA: lv_selected TYPE string.

    IF iv_selected = abap_true.
      lv_selected = 'selected'.
    ENDIF.

    io_html->add( |<option value="{ iv_option }" { lv_selected }>|
               && |{ to_mixed( iv_option ) }</option>| ).

  ENDMETHOD.

  METHOD add_order_by_option.

    DATA: lv_selected TYPE string.

    IF mv_order_by = iv_option.
      lv_selected = 'selected'.
    ENDIF.

    io_html->add( |<option value="{ iv_option }" { lv_selected }>|
               && |{ to_mixed( iv_option ) }</option>| ).

  ENDMETHOD.

  METHOD apply_filter.

    IF mv_filter IS NOT INITIAL.

      DELETE ct_overview WHERE key        NS mv_filter
                           AND name       NS mv_filter
                           AND url        NS mv_filter
                           AND package    NS mv_filter
                           AND branch     NS mv_filter
                           AND created_by NS mv_filter
                           AND created_at NS mv_filter.

    ENDIF.

  ENDMETHOD.

  METHOD apply_order_by.

    DATA:
      lt_sort TYPE abap_sortorder_tab,
      ls_sort LIKE LINE OF lt_sort.

    IF mv_order_by IS NOT INITIAL.

      ls_sort-name       = mv_order_by.
      ls_sort-descending = mv_order_descending.
      ls_sort-astext     = abap_true.
      INSERT ls_sort INTO TABLE lt_sort.
      SORT ct_overview BY (lt_sort).

    ENDIF.

  ENDMETHOD.

  METHOD constructor.

    super->constructor( ).
    ms_control-page_title = |Repository Overview|.
    mv_order_by = |NAME|.

  ENDMETHOD.  " constructor.

  METHOD map_repo_list_to_overview.

    DATA: ls_overview  LIKE LINE OF rt_overview,
          lo_repo_srv  TYPE REF TO zcl_abapgit_repo,
          lo_user      TYPE REF TO zcl_abapgit_persistence_user,
          lv_time_zone TYPE timezone,
          lv_date      TYPE d,
          lv_time      TYPE t.

    FIELD-SYMBOLS: <ls_repo> LIKE LINE OF it_repo_list.

    lo_user = zcl_abapgit_persistence_user=>get_instance( ).

    CALL FUNCTION 'GET_SYSTEM_TIMEZONE'
      IMPORTING
        timezone            = lv_time_zone
      EXCEPTIONS
        customizing_missing = 1
        OTHERS              = 2.

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

    LOOP AT it_repo_list ASSIGNING <ls_repo>.

      CLEAR: ls_overview.
      lo_repo_srv = zcl_abapgit_repo_srv=>get_instance( )->get( <ls_repo>-key ).

      ls_overview-favorite   = lo_user->is_favorite_repo( <ls_repo>-key ).
      ls_overview-type       = <ls_repo>-offline.
      ls_overview-key        = <ls_repo>-key.
      ls_overview-name       = lo_repo_srv->get_name( ).
      ls_overview-url        = <ls_repo>-url.
      ls_overview-package    = <ls_repo>-package.
      ls_overview-branch     = zcl_abapgit_git_branch_list=>get_display_name( <ls_repo>-branch_name ).
      ls_overview-created_by = <ls_repo>-created_by.

      IF <ls_repo>-created_at IS NOT INITIAL.
        CONVERT TIME STAMP <ls_repo>-created_at
                TIME ZONE lv_time_zone
                INTO DATE lv_date
                     TIME lv_time.

        ls_overview-created_at = |{ lv_date DATE = USER } { lv_time TIME = USER }|.
      ENDIF.

      INSERT ls_overview INTO TABLE rt_overview.

    ENDLOOP.

  ENDMETHOD.

  METHOD parse_change_order_by.

    FIELD-SYMBOLS: <ls_postdata> TYPE cnht_post_data_line.

    READ TABLE it_postdata ASSIGNING <ls_postdata>
                           INDEX 1.
    IF sy-subrc = 0.
      FIND FIRST OCCURRENCE OF REGEX `orderBy=(.*)`
           IN <ls_postdata>
           SUBMATCHES mv_order_by.
    ENDIF.

    mv_order_by = condense( mv_order_by ).

  ENDMETHOD.

  METHOD parse_direction.

    DATA: lv_direction TYPE string.

    FIELD-SYMBOLS: <ls_postdata> TYPE cnht_post_data_line.

    CLEAR: mv_order_descending.

    READ TABLE it_postdata ASSIGNING <ls_postdata>
                           INDEX 1.
    IF sy-subrc = 0.
      FIND FIRST OCCURRENCE OF REGEX `direction=(.*)`
           IN <ls_postdata>
           SUBMATCHES lv_direction.
    ENDIF.

    IF condense( lv_direction ) = 'DESCENDING'.
      mv_order_descending = abap_true.
    ENDIF.

  ENDMETHOD.

  METHOD parse_filter.

    FIELD-SYMBOLS: <ls_postdata> LIKE LINE OF it_postdata.

    READ TABLE it_postdata ASSIGNING <ls_postdata>
                           INDEX 1.
    IF sy-subrc = 0.
      FIND FIRST OCCURRENCE OF REGEX `filter=(.*)`
           IN <ls_postdata>
           SUBMATCHES mv_filter.
    ENDIF.

    mv_filter = condense( mv_filter ).

  ENDMETHOD.

  METHOD render_content.

    DATA: lo_persistence_repo TYPE REF TO zcl_abapgit_persistence_repo,
          lt_overview         TYPE tty_overview.

    CREATE OBJECT lo_persistence_repo.

    lt_overview = map_repo_list_to_overview( lo_persistence_repo->list( ) ).

    apply_order_by( CHANGING ct_overview = lt_overview ).

    apply_filter( CHANGING ct_overview = lt_overview ).

    CREATE OBJECT ro_html.

    ro_html->add( |
| ).

    ro_html->add( |<form id="commit_form" class="grey70"|
               && | method="post" action="sapevent:{ gc_action-apply_filter }">| ).

    render_header_bar( ro_html ).

    render_table( io_html     = ro_html
                  it_overview = lt_overview ).

    ro_html->add( |
| ).

  ENDMETHOD.            "render_content

  METHOD render_text_input.

    DATA lv_attrs TYPE string.

    CREATE OBJECT ro_html.

    IF iv_value IS NOT INITIAL.
      lv_attrs = | value="{ iv_value }"|.
    ENDIF.

    IF iv_max_length IS NOT INITIAL.
      lv_attrs = | maxlength="{ iv_max_length }"|.
    ENDIF.

    ro_html->add( |{ iv_label }| ).
    ro_html->add( |<input id="{ iv_name }" name="{ iv_name }" type="text"{ lv_attrs }>| ).

  ENDMETHOD.  " render_text_input

  METHOD zif_abapgit_gui_page~on_event.

    DATA: lv_key  TYPE zif_abapgit_persistence=>ty_value.

    CASE iv_action.
      WHEN gc_action-select.

        lv_key = iv_getdata.

        zcl_abapgit_persistence_user=>get_instance( )->set_repo_show( lv_key ).

        TRY.
            zcl_abapgit_repo_srv=>get_instance( )->get( lv_key )->refresh( ).
          CATCH zcx_abapgit_exception ##NO_HANDLER.
        ENDTRY.

        ev_state = zif_abapgit_definitions=>gc_event_state-go_back.

      WHEN gc_action-change_order_by.

        parse_change_order_by( it_postdata ).
        ev_state = zif_abapgit_definitions=>gc_event_state-re_render.

      WHEN gc_action-direction.

        parse_direction( it_postdata ).
        ev_state = zif_abapgit_definitions=>gc_event_state-re_render.

      WHEN gc_action-apply_filter.

        parse_filter( it_postdata ).
        ev_state = zif_abapgit_definitions=>gc_event_state-re_render.

    ENDCASE.

  ENDMETHOD.

  METHOD render_table_header.

    io_html->add( |
| ).
    io_html->add( |
| ).
    io_html->add( |Favorite	| ).
    io_html->add( |Type	| ).
    io_html->add( |Key	| ).
    io_html->add( |Name	| ).
    io_html->add( |Url	| ).
    io_html->add( |Package	| ).
    io_html->add( |Branch name	| ).
    io_html->add( |Creator	| ).
    io_html->add( |Created at	| ).
    io_html->add( |	| ).
    io_html->add( '
' ).
    io_html->add( '
' ).
    io_html->add( '
' ).

  ENDMETHOD.

  METHOD render_table.

    io_html->add( |
| ).
    io_html->add( |
| ).

    render_table_header( io_html ).
    render_table_body( io_html     = io_html
                       it_overview = it_overview  ).

    io_html->add( |
| ).
    io_html->add( |
| ).

  ENDMETHOD.

  METHOD render_table_body.

    DATA: lv_trclass       TYPE string,
          lv_type_icon     TYPE string,
          lv_favorite_icon TYPE string.

    FIELD-SYMBOLS: <ls_overview> LIKE LINE OF it_overview.

    LOOP AT it_overview ASSIGNING <ls_overview>.

      CLEAR lv_trclass.
      IF sy-tabix = 1.
        lv_trclass = ' class="firstrow"' ##NO_TEXT.
      ENDIF.

      IF <ls_overview>-type = abap_true.
        lv_type_icon = 'plug/darkgrey'.
      ELSE.
        lv_type_icon = 'cloud-upload/blue'.
      ENDIF.

      IF <ls_overview>-favorite = abap_true.
        lv_favorite_icon = 'star/blue'.
      ELSE.
        lv_favorite_icon = 'star/grey'.
      ENDIF.

      io_html->add( |
| ).
      io_html->add( || ).
      io_html->add_a( iv_act = |{ zif_abapgit_definitions=>gc_action-repo_toggle_fav }?{ <ls_overview>-key }|
                      iv_txt = zcl_abapgit_html=>icon( iv_name  = lv_favorite_icon
                                                       iv_class = 'pad-sides'
                                                       iv_hint  = 'Click to toggle favorite' ) ).
      io_html->add( |	| ).
      io_html->add( |{ zcl_abapgit_html=>icon( lv_type_icon )  }	| ).

      io_html->add( |{ <ls_overview>-key }	| ).
      io_html->add( |{ zcl_abapgit_html=>a( iv_txt = <ls_overview>-name
                                                iv_act = |{ gc_action-select }?{ <ls_overview>-key }| ) }	| ).
      io_html->add( |{ <ls_overview>-url }	| ).
      io_html->add( |{ <ls_overview>-package }	| ).
      io_html->add( |{ <ls_overview>-branch }	| ).
      io_html->add( |{ <ls_overview>-created_by }	| ).
      io_html->add( |{ <ls_overview>-created_at }	| ).
      io_html->add( || ).
      io_html->add( |	| ).
      io_html->add( |
| ).

    ENDLOOP.

  ENDMETHOD.

  METHOD render_order_by.

    io_html->add( |Order by: <select name="order_by" onchange="onOrderByChange(this)">| ).

    add_order_by_option( iv_option = |TYPE|
                         io_html   = io_html ).

    add_order_by_option( iv_option = |KEY|
                         io_html   = io_html ).

    add_order_by_option( iv_option = |NAME|
                         io_html   = io_html ).

    add_order_by_option( iv_option = |URL|
                         io_html   = io_html ).

    add_order_by_option( iv_option = |PACKAGE|
                         io_html   = io_html ).

    add_order_by_option( iv_option = |BRANCH|
                         io_html   = io_html ).

    add_order_by_option( iv_option = |CREATED_BY|
                         io_html   = io_html ).

    add_order_by_option( iv_option = |CREATED_AT|
                         io_html   = io_html ).

    io_html->add( |</select>| ).

  ENDMETHOD.

  METHOD render_order_by_direction.

    io_html->add( |<select name="direction" onchange="onDirectionChange(this)">| ).

    add_direction_option( iv_option   = |ASCENDING|
                          iv_selected = mv_order_descending
                          io_html     = io_html ).

    add_direction_option( iv_option   = |DESCENDING|
                          iv_selected = mv_order_descending
                          io_html     = io_html ).

    io_html->add( |</select>| ).

  ENDMETHOD.

  METHOD render_header_bar.

    io_html->add( |
| ).

    render_order_by( io_html ).
    render_order_by_direction( io_html ).

    io_html->add( render_text_input( iv_name  = |filter|
                                     iv_label = |Filter: |
                                     iv_value = mv_filter ) ).

    io_html->add( |<input type="submit" class="hidden-submit">| ).

    io_html->add( |
| ).

    io_html->add( |</form>| ).
    io_html->add( |
| ).

  ENDMETHOD.

ENDCLASS.

Add creator and created at to overview page


CLASS zcl_abapgit_object_shi3 DEFINITION PUBLIC INHERITING FROM zcl_abapgit_objects_super FINAL.

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

    METHODS constructor
      IMPORTING
        is_item     TYPE zif_abapgit_definitions=>ty_item
        iv_language TYPE spras.

  PROTECTED SECTION.

    METHODS has_authorization
      IMPORTING
        !iv_devclass     TYPE devclass
        !iv_structure_id TYPE hier_guid
        !iv_activity     TYPE activ_auth
      RAISING
        zcx_abapgit_exception .
    METHODS is_used
      IMPORTING
        !iv_structure_id TYPE hier_guid
      RAISING
        zcx_abapgit_exception .
    METHODS delete_tree_structure
      IMPORTING
        !iv_structure_id TYPE hier_guid .
  PRIVATE SECTION.
    DATA: mv_tree_id TYPE ttree-id.

    METHODS jump_se43
      RAISING zcx_abapgit_exception.

    METHODS jump_sbach04
      RAISING zcx_abapgit_exception.

    METHODS clear_fields
      CHANGING cs_head  TYPE ttree
               ct_nodes TYPE hier_iface_t.

ENDCLASS.

CLASS zcl_abapgit_object_shi3 IMPLEMENTATION.

  METHOD clear_fields.

    FIELD-SYMBOLS <ls_node> LIKE LINE OF ct_nodes.

    CLEAR: cs_head-luser, cs_head-ldate, cs_head-ltime.
    CLEAR: cs_head-fuser, cs_head-fdate, cs_head-ftime.
    CLEAR: cs_head-frelease, cs_head-lrelease.
    CLEAR: cs_head-responsibl.

    LOOP AT ct_nodes ASSIGNING <ls_node>.
      CLEAR: <ls_node>-luser, <ls_node>-ldate, <ls_node>-ltime.
      CLEAR: <ls_node>-fuser, <ls_node>-fdate, <ls_node>-ftime.
      CLEAR: <ls_node>-frelease, <ls_node>-lrelease.
    ENDLOOP.

  ENDMETHOD.

  METHOD constructor.
    super->constructor( is_item = is_item
                        iv_language = iv_language ).
    mv_tree_id = ms_item-obj_name.
  ENDMETHOD.

  METHOD delete_tree_structure.
    CALL FUNCTION 'STREE_EXTERNAL_DELETE'
      EXPORTING
        structure_id          = iv_structure_id
        no_confirmation_popup = abap_true.
  ENDMETHOD.

  METHOD has_authorization.

    AUTHORITY-CHECK OBJECT 'S_DEVELOP'
      ID 'DEVCLASS'  FIELD iv_devclass
      ID 'OBJTYPE'   FIELD 'MENU'
      ID 'OBJNAME'   FIELD iv_structure_id
      ID 'P_GROUP'   DUMMY
      ID 'ACTVT'     FIELD iv_activity.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise_t100( iv_msgid = 'S#'
                                         iv_msgno = '203' ).
    ENDIF.
  ENDMETHOD.

  METHOD is_used.

    DATA: lt_used_in_structures TYPE STANDARD TABLE OF ttree WITH DEFAULT KEY.

    CALL FUNCTION 'STREE_GET_STRUCTURE_USAGE'
      EXPORTING
        structure_id       = iv_structure_id
      TABLES
        used_in_structures = lt_used_in_structures.

    IF lt_used_in_structures IS NOT INITIAL.
      zcx_abapgit_exception=>raise( |IMG structure ID { iv_structure_id } is still used| ).
    ENDIF.

  ENDMETHOD.

  METHOD jump_se43.

    DATA: lt_bdcdata TYPE TABLE OF bdcdata.

    FIELD-SYMBOLS: <ls_bdcdata> LIKE LINE OF lt_bdcdata.

    APPEND INITIAL LINE TO lt_bdcdata ASSIGNING <ls_bdcdata>.
    <ls_bdcdata>-program  = 'SAPLBMEN'.
    <ls_bdcdata>-dynpro   = '0200'.
    <ls_bdcdata>-dynbegin = abap_true.

    APPEND INITIAL LINE TO lt_bdcdata ASSIGNING <ls_bdcdata>.
    <ls_bdcdata>-fnam = 'BDC_OKCODE'.
    <ls_bdcdata>-fval = '=SHOW'.

    APPEND INITIAL LINE TO lt_bdcdata ASSIGNING <ls_bdcdata>.
    <ls_bdcdata>-fnam = 'BMENUNAME-ID'.
    <ls_bdcdata>-fval = ms_item-obj_name.

    CALL FUNCTION 'ABAP4_CALL_TRANSACTION'
      STARTING NEW TASK 'GIT'
      EXPORTING
        tcode                 = 'SE43'
        mode_val              = 'E'
      TABLES
        using_tab             = lt_bdcdata
      EXCEPTIONS
        system_failure        = 1
        communication_failure = 2
        resource_failure      = 3
        OTHERS                = 4.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( 'error from ABAP4_CALL_TRANSACTION, SHI3' ).
    ENDIF.

  ENDMETHOD.

  METHOD jump_sbach04.
    DATA: ls_message      TYPE hier_mess,
          lv_structure_id TYPE hier_treeg.

    lv_structure_id = ms_item-obj_name.

    CALL FUNCTION 'STREE_EXTERNAL_EDIT'
      EXPORTING
        structure_id   = lv_structure_id
        language       = mv_language
        edit_structure = abap_false
        no_commit_work = abap_false
        activity       = 'D'
      IMPORTING
        message        = ls_message.
    IF ls_message IS NOT INITIAL.
      zcx_abapgit_exception=>raise_t100(
        iv_msgid = ls_message-msgid
        iv_msgno = ls_message-msgno
        iv_msgv1 = ls_message-msgv1
        iv_msgv2 = ls_message-msgv2
        iv_msgv3 = ls_message-msgv3
        iv_msgv4 = ls_message-msgv4 ).
    ENDIF.
  ENDMETHOD.

  METHOD zif_abapgit_object~changed_by.

    DATA: ls_head TYPE ttree.

    CALL FUNCTION 'STREE_STRUCTURE_READ'
      EXPORTING
        structure_id     = mv_tree_id
      IMPORTING
        structure_header = ls_head.

    rv_user = ls_head-luser.

  ENDMETHOD.

  METHOD zif_abapgit_object~delete.

    CONSTANTS lc_activity_delete_06 TYPE activ_auth VALUE '06'.

    TRY.
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
      CATCH zcx_abapgit_exception.
        RETURN.
    ENDTRY.

    has_authorization( iv_structure_id = mv_tree_id
                       iv_devclass     = ms_item-devclass
                       iv_activity     = lc_activity_delete_06 ).

    is_used( mv_tree_id ).

    delete_tree_structure( mv_tree_id ).

  ENDMETHOD.

  METHOD zif_abapgit_object~deserialize.

    DATA: ls_msg    TYPE hier_mess,
          ls_head   TYPE ttree,
          lt_titles TYPE TABLE OF ttreet,
          lt_nodes  TYPE TABLE OF hier_iface,
          lt_texts  TYPE TABLE OF hier_texts,
          lt_refs   TYPE TABLE OF hier_ref.

    io_xml->read( EXPORTING iv_name = 'TREE_HEAD'
                  CHANGING  cg_data = ls_head ).
    io_xml->read( EXPORTING iv_name = 'TREE_TITLES'
                  CHANGING  cg_data = lt_titles ).
    io_xml->read( EXPORTING iv_name = 'TREE_NODES'
                  CHANGING  cg_data = lt_nodes ).
    io_xml->read( EXPORTING iv_name = 'TREE_REFS'
                  CHANGING  cg_data = lt_refs ).
    io_xml->read( EXPORTING iv_name = 'TREE_TEXTS'
                  CHANGING  cg_data = lt_texts ).

    IF zif_abapgit_object~exists( ) = abap_true.
      delete_tree_structure( mv_tree_id ).
    ENDIF.

    CALL FUNCTION 'STREE_HIERARCHY_SAVE'
      EXPORTING
        structure_id             = mv_tree_id
        structure_type           = ls_head-type
        structure_description    = space
        structure_masterlanguage = mv_language
        structure_responsible    = sy-uname
        structure_buffermode     = ls_head-buffermode
        development_class        = iv_package
      IMPORTING
        message                  = ls_msg
      TABLES
        list_of_nodes            = lt_nodes
        list_of_references       = lt_refs
        list_of_texts            = lt_texts
        structure_descriptions   = lt_titles
      EXCEPTIONS
        no_nodes_given           = 1
        OTHERS                   = 2.
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( 'Error from STREE_HIERARCHY_SAVE, SHI3' ).
    ENDIF.

  ENDMETHOD.

  METHOD zif_abapgit_object~exists.

    DATA: ls_msg    TYPE hier_mess,
          ls_header TYPE ttree,
          ls_tadir  TYPE tadir.

    CALL FUNCTION 'STREE_STRUCTURE_EXIST'
      EXPORTING
        structure_id         = mv_tree_id
        do_not_read_devclass = ''
      IMPORTING
        message              = ls_msg
        structure_header     = ls_header
        structure_tadir      = ls_tadir.

    rv_bool = boolc( ls_header-id IS NOT INITIAL ).

  ENDMETHOD.

  METHOD zif_abapgit_object~get_comparator.
    RETURN.
  ENDMETHOD.

  METHOD zif_abapgit_object~get_deserialize_steps.
    APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
  ENDMETHOD.

  METHOD zif_abapgit_object~get_metadata.
    rs_metadata = get_metadata( ).
  ENDMETHOD.

  METHOD zif_abapgit_object~is_active.
    rv_active = is_active( ).
  ENDMETHOD.

  METHOD zif_abapgit_object~is_locked.
    rv_is_locked = abap_false.
  ENDMETHOD.

  METHOD zif_abapgit_object~jump.

    DATA: ls_head TYPE ttree.

    CALL FUNCTION 'STREE_STRUCTURE_READ'
      EXPORTING
        structure_id     = mv_tree_id
      IMPORTING
        structure_header = ls_head.

    CASE ls_head-type.
      WHEN 'BMENU'.
        jump_se43( ).
      WHEN 'GHIER'.
        jump_sbach04( ).
      WHEN OTHERS.
        zcx_abapgit_exception=>raise( |Jump for type { ls_head-type } not implemented| ).
    ENDCASE.

  ENDMETHOD.

  METHOD zif_abapgit_object~serialize.

    DATA: ls_msg    TYPE hier_mess,
          ls_head   TYPE ttree,
          lt_titles TYPE TABLE OF ttreet,
          lt_nodes  TYPE TABLE OF hier_iface,
          lt_texts  TYPE TABLE OF hier_texts,
          lt_refs   TYPE TABLE OF hier_ref.

    CALL FUNCTION 'STREE_STRUCTURE_READ'
      EXPORTING
        structure_id     = mv_tree_id
      IMPORTING
        message          = ls_msg
        structure_header = ls_head
      TABLES
        description      = lt_titles.

    CALL FUNCTION 'STREE_HIERARCHY_READ'
      EXPORTING
        structure_id       = mv_tree_id
        read_also_texts    = 'X'
        all_languages      = 'X'
      IMPORTING
        message            = ls_msg
      TABLES
        list_of_nodes      = lt_nodes
        list_of_references = lt_refs
        list_of_texts      = lt_texts.

    clear_fields( CHANGING cs_head  = ls_head
                           ct_nodes = lt_nodes ).

    io_xml->add( iv_name = 'TREE_HEAD'
                 ig_data = ls_head ).
    io_xml->add( iv_name = 'TREE_TITLES'
                 ig_data = lt_titles ).
    io_xml->add( iv_name = 'TREE_NODES'
                 ig_data = lt_nodes ).
    io_xml->add( iv_name = 'TREE_REFS'
                 ig_data = lt_refs ).
    io_xml->add( iv_name = 'TREE_TEXTS'
                 ig_data = lt_texts ).

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_object_shi3 DEFINITION PUBLIC INHERITING FROM zcl_abapgit_objects_super FINAL.

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

    METHODS constructor
      IMPORTING
        is_item     TYPE zif_abapgit_definitions=>ty_item
        iv_language TYPE spras.

  PROTECTED SECTION.

    METHODS has_authorization
      IMPORTING
        !iv_devclass     TYPE devclass
        !iv_structure_id TYPE hier_guid
        !iv_activity     TYPE activ_auth
      RAISING
        zcx_abapgit_exception .
    METHODS is_used
      IMPORTING
        !iv_structure_id TYPE hier_guid
      RAISING
        zcx_abapgit_exception .
    METHODS delete_tree_structure
      IMPORTING
        !iv_structure_id TYPE hier_guid .
  PRIVATE SECTION.
    DATA: mv_tree_id TYPE ttree-id.

    METHODS jump_se43
      RAISING zcx_abapgit_exception.

    METHODS jump_sbach04
      RAISING zcx_abapgit_exception.

    METHODS clear_fields
      CHANGING cs_head  TYPE ttree
               ct_nodes TYPE hier_iface_t.

ENDCLASS.

CLASS zcl_abapgit_object_shi3 IMPLEMENTATION.

  METHOD clear_fields.

    FIELD-SYMBOLS <ls_node> LIKE LINE OF ct_nodes.

    CLEAR: cs_head-luser, cs_head-ldate, cs_head-ltime.
    CLEAR: cs_head-fuser, cs_head-fdate, cs_head-ftime.
    CLEAR: cs_head-frelease, cs_head-lrelease.
    CLEAR: cs_head-responsibl.

    LOOP AT ct_nodes ASSIGNING <ls_node>.
      CLEAR: <ls_node>-luser, <ls_node>-ldate, <ls_node>-ltime.
      CLEAR: <ls_node>-fuser, <ls_node>-fdate, <ls_node>-ftime.
      CLEAR: <ls_node>-frelease, <ls_node>-lrelease.
    ENDLOOP.

  ENDMETHOD.

  METHOD constructor.
    super->constructor( is_item = is_item
                        iv_language = iv_language ).
    mv_tree_id = ms_item-obj_name.
  ENDMETHOD.

  METHOD delete_tree_structure.
    CALL FUNCTION 'STREE_EXTERNAL_DELETE'
      EXPORTING
        structure_id          = iv_structure_id
        no_confirmation_popup = abap_true.
  ENDMETHOD.

  METHOD has_authorization.

    AUTHORITY-CHECK OBJECT 'S_DEVELOP'
      ID 'DEVCLASS'  FIELD iv_devclass
      ID 'OBJTYPE'   FIELD 'MENU'
      ID 'OBJNAME'   FIELD iv_structure_id
      ID 'P_GROUP'   DUMMY
      ID 'ACTVT'     FIELD iv_activity.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise_t100( iv_msgid = 'S#'
                                         iv_msgno = '203' ).
    ENDIF.
  ENDMETHOD.

  METHOD is_used.

    DATA: lt_used_in_structures TYPE STANDARD TABLE OF ttree WITH DEFAULT KEY.

    CALL FUNCTION 'STREE_GET_STRUCTURE_USAGE'
      EXPORTING
        structure_id       = iv_structure_id
      TABLES
        used_in_structures = lt_used_in_structures.

    IF lt_used_in_structures IS NOT INITIAL.
      zcx_abapgit_exception=>raise( |IMG structure ID { iv_structure_id } is still used| ).
    ENDIF.

  ENDMETHOD.

  METHOD jump_se43.

    DATA: lt_bdcdata TYPE TABLE OF bdcdata.

    FIELD-SYMBOLS: <ls_bdcdata> LIKE LINE OF lt_bdcdata.

    APPEND INITIAL LINE TO lt_bdcdata ASSIGNING <ls_bdcdata>.
    <ls_bdcdata>-program  = 'SAPLBMEN'.
    <ls_bdcdata>-dynpro   = '0200'.
    <ls_bdcdata>-dynbegin = abap_true.

    APPEND INITIAL LINE TO lt_bdcdata ASSIGNING <ls_bdcdata>.
    <ls_bdcdata>-fnam = 'BDC_OKCODE'.
    <ls_bdcdata>-fval = '=SHOW'.

    APPEND INITIAL LINE TO lt_bdcdata ASSIGNING <ls_bdcdata>.
    <ls_bdcdata>-fnam = 'BMENUNAME-ID'.
    <ls_bdcdata>-fval = ms_item-obj_name.

    CALL FUNCTION 'ABAP4_CALL_TRANSACTION'
      STARTING NEW TASK 'GIT'
      EXPORTING
        tcode                 = 'SE43'
        mode_val              = 'E'
      TABLES
        using_tab             = lt_bdcdata
      EXCEPTIONS
        system_failure        = 1
        communication_failure = 2
        resource_failure      = 3
        OTHERS                = 4.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( 'error from ABAP4_CALL_TRANSACTION, SHI3' ).
    ENDIF.

  ENDMETHOD.

  METHOD jump_sbach04.
    DATA: ls_message      TYPE hier_mess,
          lv_structure_id TYPE hier_treeg.

    lv_structure_id = ms_item-obj_name.

    CALL FUNCTION 'STREE_EXTERNAL_EDIT'
      EXPORTING
        structure_id   = lv_structure_id
        language       = mv_language
        edit_structure = abap_false
        no_commit_work = abap_false
        activity       = 'D'
      IMPORTING
        message        = ls_message.
    IF ls_message IS NOT INITIAL.
      zcx_abapgit_exception=>raise_t100(
        iv_msgid = ls_message-msgid
        iv_msgno = ls_message-msgno
        iv_msgv1 = ls_message-msgv1
        iv_msgv2 = ls_message-msgv2
        iv_msgv3 = ls_message-msgv3
        iv_msgv4 = ls_message-msgv4 ).
    ENDIF.
  ENDMETHOD.

  METHOD zif_abapgit_object~changed_by.

    DATA: ls_head TYPE ttree.

    CALL FUNCTION 'STREE_STRUCTURE_READ'
      EXPORTING
        structure_id     = mv_tree_id
      IMPORTING
        structure_header = ls_head.

    rv_user = ls_head-luser.

  ENDMETHOD.

  METHOD zif_abapgit_object~delete.

    CONSTANTS lc_activity_delete_06 TYPE activ_auth VALUE '06'.

    TRY.
        IF zif_abapgit_object~exists( ) = abap_false.
          RETURN.
        ENDIF.
      CATCH zcx_abapgit_exception.
        RETURN.
    ENDTRY.

    has_authorization( iv_structure_id = mv_tree_id
                       iv_devclass     = ms_item-devclass
                       iv_activity     = lc_activity_delete_06 ).

    is_used( mv_tree_id ).

    delete_tree_structure( mv_tree_id ).

  ENDMETHOD.

  METHOD zif_abapgit_object~deserialize.

    DATA: ls_msg    TYPE hier_mess,
          ls_head   TYPE ttree,
          lt_titles TYPE TABLE OF ttreet,
          lt_nodes  TYPE TABLE OF hier_iface,
          lt_texts  TYPE TABLE OF hier_texts,
          lt_refs   TYPE TABLE OF hier_ref.

    io_xml->read( EXPORTING iv_name = 'TREE_HEAD'
                  CHANGING  cg_data = ls_head ).
    io_xml->read( EXPORTING iv_name = 'TREE_TITLES'
                  CHANGING  cg_data = lt_titles ).
    io_xml->read( EXPORTING iv_name = 'TREE_NODES'
                  CHANGING  cg_data = lt_nodes ).
    io_xml->read( EXPORTING iv_name = 'TREE_REFS'
                  CHANGING  cg_data = lt_refs ).
    io_xml->read( EXPORTING iv_name = 'TREE_TEXTS'
                  CHANGING  cg_data = lt_texts ).

    IF zif_abapgit_object~exists( ) = abap_true.
      delete_tree_structure( mv_tree_id ).
    ENDIF.

    CALL FUNCTION 'STREE_HIERARCHY_SAVE'
      EXPORTING
        structure_id             = mv_tree_id
        structure_type           = ls_head-type
        structure_description    = space
        structure_masterlanguage = mv_language
        structure_responsible    = sy-uname
        structure_buffermode     = ls_head-buffermode
        development_class        = iv_package
      IMPORTING
        message                  = ls_msg
      TABLES
        list_of_nodes            = lt_nodes
        list_of_references       = lt_refs
        list_of_texts            = lt_texts
        structure_descriptions   = lt_titles
      EXCEPTIONS
        no_nodes_given           = 1
        OTHERS                   = 2.
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( 'Error from STREE_HIERARCHY_SAVE, SHI3' ).
    ENDIF.

  ENDMETHOD.

  METHOD zif_abapgit_object~exists.

    DATA: ls_msg    TYPE hier_mess,
          ls_header TYPE ttree,
          ls_tadir  TYPE tadir.

    CALL FUNCTION 'STREE_STRUCTURE_EXIST'
      EXPORTING
        structure_id         = mv_tree_id
        do_not_read_devclass = ''
      IMPORTING
        message              = ls_msg
        structure_header     = ls_header
        structure_tadir      = ls_tadir.

    rv_bool = boolc( ls_header-id IS NOT INITIAL ).

  ENDMETHOD.

  METHOD zif_abapgit_object~get_comparator.
    RETURN.
  ENDMETHOD.

  METHOD zif_abapgit_object~get_deserialize_steps.
    APPEND zif_abapgit_object=>gc_step_id-abap TO rt_steps.
  ENDMETHOD.

  METHOD zif_abapgit_object~get_metadata.
    rs_metadata = get_metadata( ).
  ENDMETHOD.

  METHOD zif_abapgit_object~is_active.
    rv_active = is_active( ).
  ENDMETHOD.

  METHOD zif_abapgit_object~is_locked.
    rv_is_locked = abap_false.
  ENDMETHOD.

  METHOD zif_abapgit_object~jump.

    DATA: ls_head TYPE ttree.

    CALL FUNCTION 'STREE_STRUCTURE_READ'
      EXPORTING
        structure_id     = mv_tree_id
      IMPORTING
        structure_header = ls_head.

    CASE ls_head-type.
      WHEN 'BMENU'.
        jump_se43( ).
      WHEN 'GHIER'.
        jump_sbach04( ).
      WHEN OTHERS.
        zcx_abapgit_exception=>raise( |Jump for type { ls_head-type } not implemented| ).
    ENDCASE.

  ENDMETHOD.

  METHOD zif_abapgit_object~serialize.

    DATA: ls_msg           TYPE hier_mess,
          ls_head          TYPE ttree,
          lt_titles        TYPE TABLE OF ttreet,
          lt_nodes         TYPE TABLE OF hier_iface,
          lt_texts         TYPE TABLE OF hier_texts,
          lt_refs          TYPE TABLE OF hier_ref,
          lv_language      TYPE spras,
          lv_all_languages TYPE abap_bool.

    CALL FUNCTION 'STREE_STRUCTURE_READ'
      EXPORTING
        structure_id     = mv_tree_id
      IMPORTING
        message          = ls_msg
        structure_header = ls_head
      TABLES
        description      = lt_titles.

    lv_all_languages = abap_false.

    IF io_xml->i18n_params( )-serialize_master_lang_only = abap_false.
      lv_all_languages = abap_true.
    ELSE.
      lv_language = mv_language.
      DELETE lt_titles WHERE spras <> lv_language.
    ENDIF.

    CALL FUNCTION 'STREE_HIERARCHY_READ'
      EXPORTING
        structure_id       = mv_tree_id
        read_also_texts    = 'X'
        all_languages      = lv_all_languages
        language           = lv_language
      IMPORTING
        message            = ls_msg
      TABLES
        list_of_nodes      = lt_nodes
        list_of_references = lt_refs
        list_of_texts      = lt_texts.

    clear_fields( CHANGING cs_head  = ls_head
                           ct_nodes = lt_nodes ).

    SORT lt_titles BY id.
    DELETE ADJACENT DUPLICATES FROM lt_titles COMPARING id.

    SORT lt_texts BY spras.
    DELETE ADJACENT DUPLICATES FROM lt_texts COMPARING spras node_id.

    io_xml->add( iv_name = 'TREE_HEAD'
                 ig_data = ls_head ).
    io_xml->add( iv_name = 'TREE_TITLES'
                 ig_data = lt_titles ).
    io_xml->add( iv_name = 'TREE_NODES'
                 ig_data = lt_nodes ).
    io_xml->add( iv_name = 'TREE_REFS'
                 ig_data = lt_refs ).
    io_xml->add( iv_name = 'TREE_TEXTS'
                 ig_data = lt_texts ).

  ENDMETHOD.
ENDCLASS.

Fix master language only, duplicate entries for shi3 objects (#4308)

* Fix master language only for shi3 objects

* Fix abaplint

* Fix duplicates in shi3

* Abaplint

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

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