CLASS zcl_abapgit_gui_html_processor DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    CONSTANTS c_css_build_name TYPE string VALUE 'css/bundle.css'.
    CONSTANTS c_preprocess_marker TYPE string VALUE `<!-- abapgit HTML preprocessor -->`.
    CONSTANTS c_comment_start TYPE string VALUE `<!--`.
    CONSTANTS c_comment_end TYPE string VALUE `-->`.

    INTERFACES zif_abapgit_gui_html_processor .

    METHODS constructor
      IMPORTING
        ii_asset_man TYPE REF TO zif_abapgit_gui_asset_manager.

    METHODS preserve_css
      IMPORTING
        !iv_css_url TYPE string .

  PROTECTED SECTION.
  PRIVATE SECTION.
    DATA mt_preserve_css TYPE string_table.
    DATA mi_asset_man TYPE REF TO zif_abapgit_gui_asset_manager.

    METHODS patch_html
      IMPORTING
        iv_html TYPE string
      EXPORTING
        ev_html TYPE string
        et_css_urls TYPE string_table
      RAISING
        zcx_abapgit_exception.

    METHODS is_preserved
      IMPORTING
        !iv_css_url TYPE string
      RETURNING
        VALUE(rv_yes) TYPE abap_bool.

    METHODS find_head_offset
      IMPORTING
        iv_html            TYPE string
      RETURNING
        VALUE(rv_head_end) TYPE i
      RAISING
        zcx_abapgit_exception.

ENDCLASS.

CLASS ZCL_ABAPGIT_GUI_HTML_PROCESSOR IMPLEMENTATION.

  METHOD constructor.
    mi_asset_man = ii_asset_man.
  ENDMETHOD.

  METHOD is_preserved.
    READ TABLE mt_preserve_css TRANSPORTING NO FIELDS WITH KEY table_line = iv_css_url.
    rv_yes = boolc( sy-subrc = 0 ).
  ENDMETHOD.

  METHOD patch_html.

    CONSTANTS lc_css_re TYPE string VALUE `<link\s+rel="stylesheet"\s+type="text/css"\s+href="(\S+)">`.

    DATA lv_head_end TYPE i.
    DATA lo_css_re   TYPE REF TO cl_abap_regex.
    DATA lo_matcher  TYPE REF TO cl_abap_matcher.
    DATA lv_css_path TYPE string.
    DATA lv_marker   TYPE string.

    DATA lv_off TYPE i.
    DATA lv_len TYPE i.
    DATA lv_cur TYPE i.

    DATA lv_css_build TYPE string VALUE '<link rel="stylesheet" type="text/css" href="$BUILD_NAME">'.
    REPLACE FIRST OCCURRENCE OF '$BUILD_NAME' IN lv_css_build WITH c_css_build_name. " Mmmm

    CLEAR: ev_html, et_css_urls.

    lv_head_end = find_head_offset( iv_html ).

    CREATE OBJECT lo_css_re
      EXPORTING
        ignore_case = abap_true
        pattern     = lc_css_re.

    lo_matcher = lo_css_re->create_matcher( text = substring( val = iv_html len = lv_head_end ) ).
    WHILE lo_matcher->find_next( ) = abap_true.
      lv_css_path = lo_matcher->get_submatch( 1 ).
      IF abap_false = is_preserved( lv_css_path ).
        lv_off = lo_matcher->get_offset( ).
        lv_len = lo_matcher->get_length( ).
        ev_html = ev_html && substring( val = iv_html
                                        off = lv_cur
                                        len = lv_off - lv_cur ).
        ev_html = ev_html && c_comment_start && substring( val = iv_html
                                                           off = lv_off
                                                           len = lv_len ) && c_comment_end.
        lv_cur  = lv_off + lv_len.
        APPEND lv_css_path TO et_css_urls.
      ENDIF.
    ENDWHILE.

    ev_html = ev_html && substring( val = iv_html
                                    off = lv_cur
                                    len = lv_head_end - lv_cur ).
    IF lines( et_css_urls ) > 0.
      lv_marker = cl_abap_char_utilities=>newline
        && `    ` " Assume 4 space indent, maybe improve and detect ?
        && c_preprocess_marker
        && cl_abap_char_utilities=>newline
        && `    `.
      ev_html = ev_html && lv_marker && lv_css_build.
    ENDIF.
    ev_html = ev_html && substring( val = iv_html
                                    off = lv_head_end ).

  ENDMETHOD.

  METHOD preserve_css.
    APPEND iv_css_url TO mt_preserve_css.
  ENDMETHOD.

  METHOD zif_abapgit_gui_html_processor~process.

    DATA lo_css_processor TYPE REF TO zcl_abapgit_gui_css_processor.
    DATA lt_css_urls TYPE string_table.
    DATA lv_css_build TYPE string.

    FIELD-SYMBOLS <lv_url> LIKE LINE OF lt_css_urls.

    patch_html(
      EXPORTING
        iv_html = iv_html
      IMPORTING
        ev_html = rv_html
        et_css_urls = lt_css_urls ).

    IF lines( lt_css_urls ) > 0.
      CREATE OBJECT lo_css_processor
        EXPORTING
          ii_asset_manager = mi_asset_man.

      LOOP AT lt_css_urls ASSIGNING <lv_url>.
        lo_css_processor->add_file( <lv_url> ).
      ENDLOOP.

      lv_css_build = lo_css_processor->process( ).

      ii_gui_services->cache_asset(
        iv_url     = |{ c_css_build_name }|
        iv_type    = 'text'
        iv_subtype = 'css'
        iv_text    = lv_css_build ).
    ENDIF.

  ENDMETHOD.

  METHOD find_head_offset.

    rv_head_end = find( val = iv_html
                        regex = |{ cl_abap_char_utilities=>newline }?\\s*|
                        case = abap_false ).
    IF rv_head_end <= 0.
      rv_head_end = find( val = iv_html
                          regex = ||
                          case = abap_false ).
      IF rv_head_end <= 0.
        zcx_abapgit_exception=>raise( 'HTML preprocessor:  not found' ).
      ENDIF.
    ENDIF.

  ENDMETHOD.

ENDCLASS.


*----------------------------------------------------------------------*
*       CLASS lcl_Test DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS ltcl_test DEFINITION FOR TESTING
  DURATION SHORT
  RISK LEVEL HARMLESS
  FINAL.

  PRIVATE SECTION.
* ================

    DATA: mt_code   TYPE string_table,
          ms_result TYPE scirest_ad,
          mo_check  TYPE REF TO zcl_aoc_check_101.

    METHODS:
      setup,
      export_import FOR TESTING,
      test001_01 FOR TESTING,
      test001_02 FOR TESTING,
      test001_03 FOR TESTING RAISING cx_static_check,
      test001_04 FOR TESTING RAISING cx_static_check,
      test001_05 FOR TESTING RAISING cx_static_check,
      test001_06 FOR TESTING RAISING cx_static_check,
      test002_01 FOR TESTING RAISING cx_static_check,
      test002_02 FOR TESTING RAISING cx_static_check,
      test002_03 FOR TESTING RAISING cx_static_check,
      test002_04 FOR TESTING RAISING cx_static_check,
      test002_05 FOR TESTING RAISING cx_static_check.

ENDCLASS.       "lcl_Test

*----------------------------------------------------------------------*
*       CLASS lcl_Test IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS ltcl_test IMPLEMENTATION.
* ==============================

  DEFINE _code.
    APPEND &1 TO mt_code.
  END-OF-DEFINITION.

  METHOD setup.
    CREATE OBJECT mo_check.
    zcl_aoc_unit_test=>set_check( mo_check ).
  ENDMETHOD.                                               "setup

  METHOD export_import.
    zcl_aoc_unit_test=>export_import( mo_check ).
  ENDMETHOD.

  METHOD test001_01.
* ===========

    _code 'IF foo IS NOT INITIAL.'.
    _code '  foo = foo + 1.'.
    _code 'ENDIF.'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_initial( ms_result ).

  ENDMETHOD.                                               "test1

  METHOD test001_02.
* ===========

    cl_abap_unit_assert=>assert_initial( ms_result ).
    _code 'IF NOT foo IS INITIAL.'.
    _code '  foo = foo + 1.'.
    _code 'ENDIF.'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_equals( exp = '001'
                                        act = ms_result-code ).

  ENDMETHOD.                                               "test2

  METHOD test001_03.
* ===========

    cl_abap_unit_assert=>assert_initial( ms_result ).
    _code 'IF foo IS NOT INITIAL AND NOT bar IS INITIAL.'.
    _code '  foo = foo + 1.'.
    _code 'ENDIF.'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_equals( exp = '001'
                                        act = ms_result-code ).

  ENDMETHOD.                                               "test2

  METHOD test001_04.
* ===========

    _code 'IF NOT ( foo IS INITIAL ).'.                    "Not handled by check
    _code '  foo = foo + 1.'.
    _code 'ENDIF.'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_initial( ms_result ).

  ENDMETHOD.

  METHOD test001_05.
* ===========

    _code 'IF ( NOT foo IS INITIAL ).'.
    _code '  foo = foo + 1.'.
    _code 'ENDIF.'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_equals( exp = '001'
                                        act = ms_result-code ).
  ENDMETHOD.

  METHOD test001_06.
* ===========

    _code 'IF lines( foo ) > 1.'.                          "Not handled by check
    _code '  bar = bar + 1.'.
    _code 'ENDIF.'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_initial( ms_result ).
  ENDMETHOD.

  METHOD test002_01.
* ===========

    _code 'IF NOT line_exists( foo[ 1 ] ).'.
    _code '  WRITE 1.'.
    _code 'ENDIF.'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_initial( ms_result ).
  ENDMETHOD.

  METHOD test002_02.
* ===========

    _code 'IF NOT line_exists( foo[ bar = 1 ] ).'.
    _code '  WRITE 1.'.
    _code 'ENDIF.'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_initial( ms_result ).
  ENDMETHOD.

  METHOD test002_03.
* ===========

    _code 'IF NOT foo[ bar = 1 ]-var = 1.'.
    _code '  WRITE 1.'.
    _code 'ENDIF.'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_equals( exp = '001'
                                        act = ms_result-code ).
  ENDMETHOD.

  METHOD test002_04.
* ===========

    _code 'IF matches( val = <ls_token>-str regex = `^\w*\[$` ).'.
    _code '  WRITE 1.'.
    _code 'ENDIF.'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_initial( ms_result ).
  ENDMETHOD.

  METHOD test002_05.
* ===========

    _code 'if not line_exists( foo['.
    _code '    var1 = 1'.
    _code '    var2 = 2 ] ).'.
    _code '  select * from mara into table lt_mara'.
    _code '    where matnr = ls_matnr.'.
    _code ' endif.'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_initial( ms_result ).
  ENDMETHOD.

ENDCLASS.       "lcl_Test


REPORT zaoc_count_classes_with_tests.

TABLES: tadir.

SELECT-OPTIONS: s_objn FOR tadir-obj_name,
                s_devc FOR tadir-devclass.

START-OF-SELECTION.
  PERFORM run.

FORM run RAISING cx_salv_msg.

  TYPES: BEGIN OF ty_alv,
           obj_name TYPE tadir-obj_name,
           with     TYPE i,
           without  TYPE i,
         END OF ty_alv.

  TYPES: BEGIN OF ty_tadir,
           object   TYPE tadir-object,
           obj_name TYPE tadir-obj_name,
         END OF ty_tadir.

  DATA: lt_tadir      TYPE STANDARD TABLE OF ty_tadir WITH DEFAULT KEY,
        ls_tadir      LIKE LINE OF lt_tadir,
        lo_alv        TYPE REF TO cl_salv_table,
        lt_alv        TYPE STANDARD TABLE OF ty_alv WITH DEFAULT KEY,
        ls_alv        LIKE LINE OF lt_alv,
        ls_aunit_info TYPE if_aunit_prog_info_types=>ty_s_program.

  SELECT object obj_name FROM tadir INTO TABLE lt_tadir
    WHERE pgmid = 'R3TR'
    AND object = 'CLAS'
    AND obj_name IN s_objn
    AND devclass IN s_devc
    AND delflag = abap_false.             "#EC CI_GENBUFF "#EC CI_SUBRC

  LOOP AT lt_tadir INTO ls_tadir.
    CLEAR ls_alv.
    ls_aunit_info = cl_aunit_prog_info=>get_program_info(
      allow_commit = abap_true
      obj_type     = ls_tadir-object
      obj_name     = ls_tadir-obj_name ).
    ls_alv-obj_name = ls_tadir-obj_name.
    IF ls_aunit_info-has_tests = abap_true.
      ls_alv-with = 1.
    ELSE.
      ls_alv-without = 1.
    ENDIF.
    APPEND ls_alv TO lt_alv.
  ENDLOOP.

  cl_salv_table=>factory(
    IMPORTING
      r_salv_table = lo_alv
    CHANGING
      t_table      = lt_alv ).

  lo_alv->get_functions( )->set_all( ).
  lo_alv->get_columns( )->set_optimize( ).
  lo_alv->display( ).

ENDFORM.


CLASS zcl_abapgit_gui_component DEFINITION
  PUBLIC
  ABSTRACT
  CREATE PUBLIC .

  PUBLIC SECTION.

    CONSTANTS:
      BEGIN OF c_html_parts,
        scripts      TYPE string VALUE 'scripts',
        hidden_forms TYPE string VALUE 'hidden_forms',
      END OF c_html_parts.

  PROTECTED SECTION.

    METHODS register_deferred_script
      IMPORTING
        ii_part TYPE REF TO zif_abapgit_html
      RAISING
        zcx_abapgit_exception.
    METHODS gui_services
      RETURNING
        VALUE(ri_gui_services) TYPE REF TO zif_abapgit_gui_services
      RAISING
        zcx_abapgit_exception.

  PRIVATE SECTION.
    DATA mi_gui_services TYPE REF TO zif_abapgit_gui_services.
ENDCLASS.

CLASS ZCL_ABAPGIT_GUI_COMPONENT IMPLEMENTATION.

  METHOD gui_services.
    IF mi_gui_services IS NOT BOUND.
      mi_gui_services = zcl_abapgit_ui_factory=>get_gui_services( ).
    ENDIF.
    ri_gui_services = mi_gui_services.
  ENDMETHOD.

  METHOD register_deferred_script.
    gui_services( )->get_html_parts( )->add_part(
      iv_collection = c_html_parts-scripts
      ii_part       = ii_part ).
  ENDMETHOD.
ENDCLASS.


CLASS lcl_attribute_setter DEFINITION
  INHERITING FROM cl_workflow_general_task_def
  CREATE PUBLIC
  FINAL.
  PUBLIC SECTION.

    CLASS-METHODS set_objid IMPORTING iv_objid TYPE hrobject-objid
                                      io_task  TYPE REF TO cl_workflow_general_task_def.

    CLASS-METHODS set_container_id IMPORTING iv_id   TYPE guid_32
                                             io_task TYPE REF TO cl_workflow_general_task_def. "#EC NEEDED
ENDCLASS.

CLASS lcl_attribute_setter IMPLEMENTATION.

  METHOD set_container_id.

    FIELD-SYMBOLS <lv_object> TYPE REF TO if_swf_cnt_container.

    ASSIGN ('IO_TASK->CONTAINER') TO <lv_object>.
    ASSERT sy-subrc = 0.

    CALL METHOD <lv_object>->('SET_GUID')
      EXPORTING
        guid_32 = iv_id.

  ENDMETHOD.

  METHOD set_objid.
    io_task->objid = iv_objid.
  ENDMETHOD.

ENDCLASS.

CLASS lcl_task_definition DEFINITION
  CREATE PUBLIC
  FINAL.

  PUBLIC SECTION.

    INTERFACES lif_task_definition.

    CLASS-METHODS load IMPORTING iv_objid         TYPE hrobject-objid
                       RETURNING VALUE(ri_result) TYPE REF TO lif_task_definition
                       RAISING   zcx_abapgit_exception.

    CLASS-METHODS create IMPORTING iv_objid         TYPE hrobject-objid
                                   is_task_data     TYPE lif_task_definition=>ty_task_data
                         RETURNING VALUE(ri_result) TYPE REF TO lif_task_definition
                         RAISING   zcx_abapgit_exception.

  PRIVATE SECTION.
    CONSTANTS c_subty_task_description TYPE hr_s_subty VALUE '0120'.

    DATA mo_taskdef TYPE REF TO cl_workflow_task_ts.
    DATA ms_task TYPE lif_task_definition=>ty_task_data.

    DATA: mv_objid                      TYPE hrobjid.

    METHODS supply_instance RAISING zcx_abapgit_exception.
    METHODS check_subrc_for IMPORTING iv_call TYPE clike OPTIONAL
                            RAISING   zcx_abapgit_exception.

ENDCLASS.

CLASS lcl_task_definition IMPLEMENTATION.

  METHOD load.

    DATA lo_taskdef TYPE REF TO lcl_task_definition.

    CREATE OBJECT lo_taskdef.
    lo_taskdef->mv_objid = iv_objid.
    lo_taskdef->supply_instance( ).

    ri_result = lo_taskdef.

  ENDMETHOD.

  METHOD check_subrc_for.
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( iv_call && ' returned ' && sy-subrc ).
    ENDIF.
  ENDMETHOD.

  METHOD supply_instance.

    cl_workflow_factory=>create_ts(
      EXPORTING
        objid                        = mv_objid
      RECEIVING
        ts_inst                      = mo_taskdef
      EXCEPTIONS
        standard_task_does_not_exist = 1
        object_could_not_be_locked   = 2
        objid_not_given              = 3
        OTHERS                       = 4 )  ##SUBRC_OK.

    check_subrc_for( 'CREATE_TS' ).

    ms_task-wi_text                    = mo_taskdef->wi_text.
    ms_task-short_text                 = mo_taskdef->short_text.
    ms_task-plvar                      = mo_taskdef->plvar.
    ms_task-method                     = mo_taskdef->method.
    ms_task-method_binding             = mo_taskdef->method_binding.
    ms_task-starting_events            = mo_taskdef->starting_events.
    ms_task-starting_events_binding    = mo_taskdef->starting_events_binding.
    ms_task-terminating_events         = mo_taskdef->terminating_events.
    ms_task-terminating_events_binding = mo_taskdef->terminating_events_binding.
    ms_task-descriptions               = mo_taskdef->descriptions.

  ENDMETHOD.

  METHOD lif_task_definition~clear_origin_data.

    FIELD-SYMBOLS: <ls_description>             TYPE hrs1002,
                   <ls_method_binding>          TYPE hrs1214,
                   <ls_starting_events_binding> TYPE hrs1212,
                   <ls_term_events_binding>     TYPE hrs1212.

    CLEAR: ms_task-method-aedtm,
           ms_task-method-uname.

    LOOP AT ms_task-method_binding ASSIGNING <ls_method_binding>.
      CLEAR: <ls_method_binding>-aedtm,
             <ls_method_binding>-uname.
    ENDLOOP.

    LOOP AT ms_task-starting_events_binding ASSIGNING <ls_starting_events_binding>.
      CLEAR: <ls_starting_events_binding>-aedtm,
             <ls_starting_events_binding>-uname.
    ENDLOOP.

    LOOP AT ms_task-descriptions ASSIGNING <ls_description>.
      CLEAR: <ls_description>-aedtm,
             <ls_description>-uname.
    ENDLOOP.

    LOOP AT ms_task-terminating_events_binding ASSIGNING <ls_term_events_binding>.
      CLEAR: <ls_term_events_binding>-aedtm,
             <ls_term_events_binding>-uname.
    ENDLOOP.

  ENDMETHOD.

  METHOD lif_task_definition~get_definition.
    rs_result = me->ms_task.
  ENDMETHOD.

  METHOD lif_task_definition~get_container.
    ri_result = mo_taskdef->container.
  ENDMETHOD.

  METHOD lif_task_definition~get_user_container.

    DATA: li_container       TYPE REF TO if_swf_cnt_element_access_1,
          lt_user_elements   TYPE swfdnamtab,
          lt_system_elements TYPE swfdnamtab,
          lv_element         TYPE swfdname.

    li_container = mo_taskdef->container.
    lt_user_elements = li_container->all_elements_list( ).
    lt_system_elements = li_container->all_elements_list( list_system = abap_true ).

    LOOP AT lt_system_elements INTO lv_element.
      READ TABLE lt_user_elements WITH KEY table_line = lv_element TRANSPORTING NO FIELDS.
      IF sy-subrc <> 0.
        TRY.
            li_container->element_remove( name = lv_element ).
          CATCH cx_swf_cnt_container.
            "Shouldn't happen, doesn't matter if it does
        ENDTRY.
      ENDIF.
    ENDLOOP.

    ri_result ?= li_container.

  ENDMETHOD.

  METHOD create.
    DATA lo_task TYPE REF TO lcl_task_definition.

    CREATE OBJECT lo_task TYPE lcl_task_definition.
    lo_task->mv_objid = iv_objid.
    lo_task->ms_task = is_task_data.
    ri_result = lo_task.

  ENDMETHOD.

  METHOD lif_task_definition~import_container.

    DATA lt_exception_list TYPE swf_cx_tab.
    DATA lx_exception TYPE REF TO cx_swf_ifs_exception.

    mo_taskdef->container->import_from_xml(
            EXPORTING xml_stream     = iv_xml_string
            IMPORTING exception_list = lt_exception_list ).

    IF lt_exception_list IS NOT INITIAL.
      READ TABLE lt_exception_list INDEX 1 INTO lx_exception.
      zcx_abapgit_exception=>raise_with_text( lx_exception ).
    ENDIF.

  ENDMETHOD.

  METHOD lif_task_definition~create_task.

    cl_workflow_factory=>create_new_ts(
      EXPORTING
        short_text          = |{ ms_task-short_text }|
        text                = |{ ms_task-wi_text }|
      RECEIVING
        task_object         = mo_taskdef
      EXCEPTIONS
        text_exists_already = 1
        OTHERS              = 2 ).                        "#EC SUBRC_OK

    check_subrc_for( `CREATE_NEW_TS` ).

    lcl_attribute_setter=>set_objid( iv_objid = mv_objid
                                           io_task  = mo_taskdef ).

    lcl_attribute_setter=>set_container_id( iv_id    = |TS{ mv_objid }|
                                            io_task  = mo_taskdef ).

  ENDMETHOD.

  METHOD lif_task_definition~change_start_events.

    mo_taskdef->change_start_events_complete(
      EXPORTING
        starting_events    = ms_task-starting_events
      EXCEPTIONS
        no_changes_allowed = 1
        OTHERS             = 2 ).                         "#EC SUBRC_OK

    check_subrc_for( `CHANGE_START_EVENTS_COMPLETE` ).

    mo_taskdef->change_start_evt_bind_complete(
      EXPORTING
        new_bindings       = ms_task-starting_events_binding
      EXCEPTIONS
        no_changes_allowed = 1
        OTHERS             = 2 ).                         "#EC SUBRC_OK

    check_subrc_for( `CHANGE_START_EVT_BIND_COMPLETE` ).

  ENDMETHOD.

  METHOD lif_task_definition~save.

    DATA ls_hrsobject TYPE hrsobject.
    ls_hrsobject-otype = 'TS'. "swfco_org_standard_task - todo: linter can't resolve this
    ls_hrsobject-objid = mv_objid.
    INSERT hrsobject FROM ls_hrsobject.

    mo_taskdef->save_standard_task(
      EXPORTING
        development_class          = iv_package
        iv_force_gen               = abap_true
      EXCEPTIONS
        no_changes_allowed         = 1
        no_client_indep_maint      = 2
        update_error               = 3
        insert_error_new_ts        = 4
        new_ts_could_not_be_locked = 5
        save_abort_by_user         = 6
        OTHERS                     = 7 ).                 "#EC SUBRC_OK

    check_subrc_for( `SAVE_STANDARD_TASK` ).

  ENDMETHOD.

  METHOD lif_task_definition~change_wi_text.

    mo_taskdef->change_wi_text(
      EXPORTING
        new_wi_text        = ms_task-wi_text
      EXCEPTIONS
        no_changes_allowed = 1
        OTHERS             = 2 ).                         "#EC SUBRC_OK

    check_subrc_for( `CHANGE_WI_TEXT` ).

  ENDMETHOD.

  METHOD lif_task_definition~change_method.

    FIELD-SYMBOLS <ls_method_binding> TYPE hrs1214.

    mo_taskdef->change_method(
      EXPORTING
        new_method                   = ms_task-method    " New Method or Settings
      EXCEPTIONS
        no_changes_allowed           = 1
        problem_method_web_enabling  = 2
        problem_method_phon_enabling = 3
        OTHERS                       = 4 ).               "#EC SUBRC_OK

    check_subrc_for( `CHANGE_METHOD` ).

    LOOP AT ms_task-method_binding ASSIGNING <ls_method_binding>.

      mo_taskdef->change_method_binding(
        EXPORTING
          binding                       = <ls_method_binding>
          delete                        = abap_false
          insert                        = abap_true
        EXCEPTIONS
          no_changes_allowed            = 1
          desired_action_not_clear      = 2
          ts_cnt_element_does_not_exist = 3
          binding_could_not_be_deleted  = 4
          OTHERS                        = 5 ).            "#EC SUBRC_OK

      check_subrc_for( `CHANGE_METHOD_BINDING` ).

    ENDLOOP.

  ENDMETHOD.

  METHOD lif_task_definition~change_terminating_events.

    mo_taskdef->change_term_events_complete(
      EXPORTING
        terminating_events = ms_task-terminating_events
      EXCEPTIONS
        no_changes_allowed = 1
        OTHERS             = 2 ).                         "#EC SUBRC_OK

    check_subrc_for( `CHANGE_TERM_EVENTS_COMPLETE` ).

    mo_taskdef->change_term_evt_bind_complete(
      EXPORTING
        new_bindings       = ms_task-terminating_events_binding
      EXCEPTIONS
        no_changes_allowed = 1
        OTHERS             = 2 ).                         "#EC SUBRC_OK

    check_subrc_for( `CHANGE_TERM_EVT_BIND_COMPLETE` ).

  ENDMETHOD.

  METHOD lif_task_definition~change_text.

    mo_taskdef->change_text(
      EXPORTING
        subty              = c_subty_task_description
        new_text           = ms_task-descriptions
      EXCEPTIONS
        no_changes_allowed = 1
        OTHERS             = 2 ).                         "#EC SUBRC_OK

    check_subrc_for( `CHANGE_TEXT` ).

  ENDMETHOD.

ENDCLASS.


CLASS zcl_abapgit_object_amsd 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
        RAISING
          zcx_abapgit_exception.

  PROTECTED SECTION.
  PRIVATE SECTION.
    METHODS:
      clear_fields
        CHANGING
          cs_logical_db_schema TYPE any,

      clear_field
        IMPORTING
          iv_fieldname         TYPE csequence
        CHANGING
          cs_logical_db_schema TYPE any,

      fill_metadata_from_db
        CHANGING
          cs_logical_db_schema TYPE any
        RAISING
          zcx_abapgit_exception,

      get_wb_object_operator
        RETURNING
          VALUE(ri_wb_object_operator) TYPE REF TO object
        RAISING
          zcx_abapgit_exception.

    DATA:
      mr_logical_db_schema     TYPE REF TO data,
      mv_logical_db_schema_key TYPE seu_objkey,
      mi_persistence           TYPE REF TO if_wb_object_persist,
      mi_wb_object_operator    TYPE REF TO object.
ENDCLASS.

CLASS zcl_abapgit_object_amsd IMPLEMENTATION.

  METHOD clear_field.

    FIELD-SYMBOLS: <lv_value> TYPE data.

    ASSIGN COMPONENT iv_fieldname OF STRUCTURE cs_logical_db_schema
           TO <lv_value>.
    ASSERT sy-subrc = 0.

    CLEAR: <lv_value>.

  ENDMETHOD.

  METHOD clear_fields.

    clear_field(
      EXPORTING
        iv_fieldname          = 'METADATA-CREATED_AT'
      CHANGING
        cs_logical_db_schema = cs_logical_db_schema ).

    clear_field(
      EXPORTING
        iv_fieldname          = 'METADATA-CREATED_BY'
      CHANGING
        cs_logical_db_schema = cs_logical_db_schema ).

    clear_field(
      EXPORTING
        iv_fieldname          = 'METADATA-CHANGED_AT'
      CHANGING
        cs_logical_db_schema = cs_logical_db_schema ).

    clear_field(
      EXPORTING
        iv_fieldname          = 'METADATA-CHANGED_BY'
      CHANGING
        cs_logical_db_schema = cs_logical_db_schema ).

  ENDMETHOD.

  METHOD constructor.

    super->constructor(
        is_item     = is_item
        iv_language = iv_language ).

    mv_logical_db_schema_key = ms_item-obj_name.

    TRY.
        CREATE DATA mr_logical_db_schema TYPE ('CL_AMDP_SCHEMA_OBJECT_DATA=>TY_OBJECT_DATA').
        CREATE OBJECT mi_persistence TYPE ('CL_AMDP_SCHEMA_OBJECT_PERSIST').

      CATCH cx_sy_create_error.
        zcx_abapgit_exception=>raise( |AMSD not supported by your NW release| ).
    ENDTRY.

  ENDMETHOD.

  METHOD fill_metadata_from_db.

    DATA:
      li_wb_object_operator    TYPE REF TO object,
      lr_logical_db_schema_old TYPE REF TO data.

    FIELD-SYMBOLS:
      <ls_logical_db_schema_old> TYPE any,
      <lv_created_at>            TYPE xsddatetime_z,
      <lv_created_by>            TYPE syuname,
      <lv_created_at_old>        TYPE xsddatetime_z,
      <lv_created_by_old>        TYPE syuname.

    li_wb_object_operator = get_wb_object_operator( ).

    CREATE DATA lr_logical_db_schema_old TYPE ('CL_AMDP_SCHEMA_OBJECT_DATA=>TY_OBJECT_DATA').
    ASSIGN lr_logical_db_schema_old->* TO <ls_logical_db_schema_old>.
    ASSERT sy-subrc = 0.

    CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
      IMPORTING
        data = <ls_logical_db_schema_old>.

    ASSIGN COMPONENT 'METADATA-CREATED_BY' OF STRUCTURE cs_logical_db_schema
           TO <lv_created_by>.
    ASSERT sy-subrc = 0.

    ASSIGN COMPONENT 'METADATA-CREATED_AT' OF STRUCTURE cs_logical_db_schema
           TO <lv_created_at>.
    ASSERT sy-subrc = 0.

    ASSIGN COMPONENT 'METADATA-CREATED_BY' OF STRUCTURE <ls_logical_db_schema_old>
           TO <lv_created_by_old>.
    ASSERT sy-subrc = 0.

    ASSIGN COMPONENT 'METADATA-CREATED_AT' OF STRUCTURE <ls_logical_db_schema_old>
           TO <lv_created_at_old>.
    ASSERT sy-subrc = 0.

    <lv_created_at> = <lv_created_at_old>.
    <lv_created_by> = <lv_created_by_old>.

  ENDMETHOD.

  METHOD get_wb_object_operator.

    DATA:
      ls_object_type TYPE wbobjtype,
      lx_error       TYPE REF TO cx_root.

    IF mi_wb_object_operator IS BOUND.
      ri_wb_object_operator = mi_wb_object_operator.
    ENDIF.

    ls_object_type-objtype_tr = 'AMSD'.
    ls_object_type-subtype_wb = 'TYP'.

    TRY.
        CALL METHOD ('CL_WB_OBJECT_OPERATOR')=>('CREATE_INSTANCE')
          EXPORTING
            object_type = ls_object_type
            object_key  = mv_logical_db_schema_key
          RECEIVING
            result      = mi_wb_object_operator.

      CATCH cx_root INTO lx_error.
        zcx_abapgit_exception=>raise_with_text( lx_error ).
    ENDTRY.

    ri_wb_object_operator = mi_wb_object_operator.

  ENDMETHOD.

  METHOD zif_abapgit_object~changed_by.

    DATA:
      li_wb_object_operator TYPE REF TO object,
      li_object_data_model  TYPE REF TO if_wb_object_data_model,
      lx_error              TYPE REF TO cx_root.

    TRY.
        li_wb_object_operator = get_wb_object_operator( ).

        CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
          IMPORTING
            eo_object_data = li_object_data_model.

        rv_user = li_object_data_model->get_changed_by( ).

      CATCH cx_root INTO lx_error.
        zcx_abapgit_exception=>raise_with_text( lx_error ).
    ENDTRY.

  ENDMETHOD.

  METHOD zif_abapgit_object~delete.

    DATA:
      li_wb_object_operator TYPE REF TO object,
      lx_error              TYPE REF TO cx_root.

    li_wb_object_operator = get_wb_object_operator( ).

    TRY.
        CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~DELETE')
          EXPORTING
            transport_request = iv_transport.

      CATCH cx_root INTO lx_error.
        zcx_abapgit_exception=>raise_with_text( lx_error ).
    ENDTRY.

  ENDMETHOD.

  METHOD zif_abapgit_object~deserialize.

    DATA:
      li_object_data_model  TYPE REF TO if_wb_object_data_model,
      li_wb_object_operator TYPE REF TO object,
      lx_error              TYPE REF TO cx_root.

    FIELD-SYMBOLS:
      <ls_logical_db_schema> TYPE any.

    ASSIGN mr_logical_db_schema->* TO <ls_logical_db_schema>.
    ASSERT sy-subrc = 0.

    io_xml->read(
      EXPORTING
        iv_name = 'AMSD'
      CHANGING
        cg_data = <ls_logical_db_schema> ).

    li_wb_object_operator = get_wb_object_operator( ).

    TRY.
        CREATE OBJECT li_object_data_model TYPE ('CL_AMDP_SCHEMA_OBJECT_DATA').

        tadir_insert( iv_package ).

        IF zif_abapgit_object~exists( ) = abap_true.

          " We need to populate created_at, created_by, because otherwise update  is not possible
          fill_metadata_from_db( CHANGING cs_logical_db_schema = <ls_logical_db_schema> ).
          li_object_data_model->set_data( <ls_logical_db_schema> ).

          CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
            EXPORTING
              io_object_data    = li_object_data_model
              transport_request = iv_transport.

        ELSE.

          li_object_data_model->set_data( <ls_logical_db_schema> ).

          CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~CREATE')
            EXPORTING
              io_object_data    = li_object_data_model
              data_selection    = 'P' " if_wb_object_data_selection_co=>c_properties
              package           = iv_package
              transport_request = iv_transport.

          CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~UPDATE')
            EXPORTING
              io_object_data    = li_object_data_model
              data_selection    = 'D' " if_wb_object_data_selection_co=>c_data_content
              transport_request = iv_transport.

        ENDIF.

        CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~ACTIVATE').

        corr_insert( iv_package ).

      CATCH cx_root INTO lx_error.
        zcx_abapgit_exception=>raise_with_text( lx_error ).
    ENDTRY.

  ENDMETHOD.

  METHOD zif_abapgit_object~exists.

    TRY.
        mi_persistence->get(
            p_object_key           = mv_logical_db_schema_key
            p_version              = 'A'
            p_existence_check_only = abap_true ).
        rv_bool = abap_true.

      CATCH cx_swb_exception.
        rv_bool = abap_false.
    ENDTRY.

  ENDMETHOD.

  METHOD zif_abapgit_object~get_comparator.
    RETURN.
  ENDMETHOD.

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

  METHOD zif_abapgit_object~get_metadata.
    rs_metadata = get_metadata( ).
    rs_metadata-delete_tadir = abap_true.
  ENDMETHOD.

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

  METHOD zif_abapgit_object~is_locked.

    rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'ESWB_EO'
                                            iv_argument    = |{ ms_item-obj_type }{ ms_item-obj_name }| ).

  ENDMETHOD.

  METHOD zif_abapgit_object~jump.
    " Covered by ZCL_ABAPGIT_OBJECTS=>JUMP
  ENDMETHOD.

  METHOD zif_abapgit_object~serialize.

    DATA:
      li_object_data_model  TYPE REF TO if_wb_object_data_model,
      lx_error              TYPE REF TO cx_root,
      li_wb_object_operator TYPE REF TO object.

    FIELD-SYMBOLS:
      <ls_logical_db_schema> TYPE any.

    ASSIGN mr_logical_db_schema->* TO <ls_logical_db_schema>.
    ASSERT sy-subrc = 0.

    li_wb_object_operator = get_wb_object_operator( ).

    TRY.
        CALL METHOD li_wb_object_operator->('IF_WB_OBJECT_OPERATOR~READ')
          EXPORTING
            version        = 'A'
          IMPORTING
            data           = <ls_logical_db_schema>
            eo_object_data = li_object_data_model.

        clear_fields( CHANGING cs_logical_db_schema = <ls_logical_db_schema> ).

      CATCH cx_root INTO lx_error.
        zcx_abapgit_exception=>raise_with_text( lx_error ).
    ENDTRY.

    io_xml->add(
        iv_name = 'AMSD'
        ig_data = <ls_logical_db_schema> ).

  ENDMETHOD.
ENDCLASS.


CLASS lcl_package_interface_facade IMPLEMENTATION.

  METHOD constructor.

    mi_interface = ii_interface.

  ENDMETHOD.

  METHOD lif_package_interface_facade~get_elements.

    mi_interface->get_elements(
      IMPORTING
        e_elements     = rt_elements
      EXCEPTIONS
        object_invalid = 1
        intern_err     = 2
        OTHERS         = 3 ).

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

  ENDMETHOD.

  METHOD lif_package_interface_facade~set_elements_changeable.

    mi_interface->set_elements_changeable(
      EXPORTING
        i_changeable                = iv_changeable
      EXCEPTIONS
        object_already_changeable   = 1
        object_already_unlocked     = 2
        object_locked_by_other_user = 3
        object_modified             = 4
        object_just_created         = 5
        object_deleted              = 6
        permission_failure          = 7
        object_invalid              = 8
        unexpected_error            = 9
        OTHERS                      = 10 ).

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

  ENDMETHOD.

  METHOD lif_package_interface_facade~save_elements.

    mi_interface->save_elements(
      EXCEPTIONS
        object_not_changeable = 1
        object_invalid        = 2
        cancelled_in_corr     = 3
        permission_failure    = 4
        unexpected_error      = 5
        intern_err            = 6
        OTHERS                = 7 ).

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

  ENDMETHOD.

  METHOD lif_package_interface_facade~get_all_attributes.

    mi_interface->get_all_attributes(
      IMPORTING
        e_package_interface_data = rs_package_interface_data
      EXCEPTIONS
        object_invalid           = 1
        OTHERS                   = 2 ).

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

  ENDMETHOD.

  METHOD lif_package_interface_facade~set_changeable.

    mi_interface->set_changeable(
      EXPORTING
        i_changeable                 = iv_changeable
      EXCEPTIONS
        object_locked_by_other_user  = 1
        permission_failure           = 2
        object_already_changeable    = 3
        object_already_unlocked      = 4
        object_just_created          = 5
        object_deleted               = 6
        object_modified              = 7
        object_not_existing          = 8
        object_invalid               = 9
        unexpected_error             = 10
        OTHERS                       = 11 ).

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

  ENDMETHOD.

  METHOD lif_package_interface_facade~delete.

    mi_interface->delete(
      EXCEPTIONS
        object_not_empty      = 1
        object_not_changeable = 2
        object_invalid        = 3
        intern_err            = 4
        OTHERS                = 5 ).

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

  ENDMETHOD.

  METHOD lif_package_interface_facade~save.

    mi_interface->save(
      EXCEPTIONS
        short_text_missing    = 1
        object_not_changeable = 2
        object_invalid        = 3
        cancelled_in_corr     = 4
        permission_failure    = 5
        unexpected_error      = 6
        intern_err            = 7
        OTHERS                = 8 ).

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

  ENDMETHOD.

  METHOD lif_package_interface_facade~remove_elements.

    mi_interface->remove_elements(
      EXPORTING
        i_elements            = it_elements
      EXCEPTIONS
        object_deleted        = 1
        object_invalid        = 2
        object_not_changeable = 3
        element_not_contained = 4
        intern_err            = 5
        OTHERS                = 6 ).

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

  ENDMETHOD.

  METHOD lif_package_interface_facade~add_elements.

    mi_interface->add_elements(
      EXPORTING
        i_elements_data        = it_elements_data
      EXCEPTIONS
        object_invalid         = 1
        intern_err             = 2
        OTHERS                 = 3 ).

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

  ENDMETHOD.

  METHOD lif_package_interface_facade~set_all_attributes.

    mi_interface->set_all_attributes(
      EXPORTING
        i_package_interface_data     = is_package_interface_data
        i_data_sign                  = is_data_sign
      EXCEPTIONS
        object_deleted               = 1
        object_not_changeable        = 2
        interface_not_empty          = 3
        acl_not_empty                = 4
        author_not_existing          = 5
        object_type_mismatch         = 6
        object_invalid               = 7
        OTHERS                       = 8 ).
* Downport: exception "logical_package_types_differ"
* does not exist in lower versions

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

  ENDMETHOD.

  METHOD lif_package_interface_facade~get_changeable.

    mi_interface->get_changeable(
      IMPORTING
        e_changeable   = rv_changeable
      EXCEPTIONS
        object_invalid = 1
        OTHERS         = 2 ).

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

  ENDMETHOD.

ENDCLASS.


INTERFACE zif_abapgit_persistence PUBLIC.

  TYPES:
    ty_type  TYPE c LENGTH 12 .
  TYPES:
    ty_value TYPE c LENGTH 12 .
  TYPES:
    BEGIN OF ty_content,
      type     TYPE ty_type,
      value    TYPE ty_value,
      data_str TYPE string,
    END OF ty_content .
  TYPES:
    ty_contents TYPE SORTED TABLE OF ty_content WITH UNIQUE KEY type value .

  TYPES: BEGIN OF ty_local_checksum,
           item  TYPE zif_abapgit_definitions=>ty_item,
           files TYPE zif_abapgit_definitions=>ty_file_signatures_tt,
         END OF ty_local_checksum.

  TYPES:
    BEGIN OF ty_local_settings,
      display_name                 TYPE string,
      ignore_subpackages           TYPE abap_bool,
      write_protected              TYPE abap_bool,
      only_local_objects           TYPE abap_bool,
      code_inspector_check_variant TYPE sci_chkv,
      block_commit                 TYPE abap_bool,
      main_language_only           TYPE abap_bool,
    END OF ty_local_settings.

  TYPES: ty_local_checksum_tt TYPE STANDARD TABLE OF ty_local_checksum WITH DEFAULT KEY.

  TYPES: BEGIN OF ty_repo_xml,
           url             TYPE string,
           branch_name     TYPE string,
           selected_commit TYPE zif_abapgit_definitions=>ty_sha1,
           package         TYPE devclass,
           created_by      TYPE syuname,
           created_at      TYPE timestampl,
           deserialized_by TYPE syuname,
           deserialized_at TYPE timestampl,
           offline         TYPE abap_bool,
           switched_origin TYPE string,
           local_checksums TYPE ty_local_checksum_tt,
           dot_abapgit     TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit,
           head_branch     TYPE string,   " HEAD symref of the repo, master branch
           local_settings  TYPE ty_local_settings,
         END OF ty_repo_xml.

  TYPES:
    BEGIN OF ty_repo_meta_mask,
      url             TYPE abap_bool,
      branch_name     TYPE abap_bool,
      selected_commit TYPE abap_bool,
      package         TYPE abap_bool,
      created_by      TYPE abap_bool,
      created_at      TYPE abap_bool,
      deserialized_by TYPE abap_bool,
      deserialized_at TYPE abap_bool,
      offline         TYPE abap_bool,
      switched_origin TYPE abap_bool,
      local_checksums TYPE abap_bool,
      dot_abapgit     TYPE abap_bool,
      head_branch     TYPE abap_bool,
      local_settings  TYPE abap_bool,
    END OF ty_repo_meta_mask.

  TYPES: BEGIN OF ty_repo,
           key TYPE ty_value.
      INCLUDE TYPE ty_repo_xml.
  TYPES: END OF ty_repo.
  TYPES: ty_repos TYPE STANDARD TABLE OF ty_repo WITH DEFAULT KEY.
  TYPES: ty_repo_keys TYPE STANDARD TABLE OF ty_repo-key WITH DEFAULT KEY.

ENDINTERFACE.


CLASS zcl_abapgit_object_filter_tran DEFINITION
  PUBLIC
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES zif_abapgit_object_filter.

    TYPES ty_trrngtrkor_tt TYPE RANGE OF trkorr.
    TYPES: BEGIN OF ty_e071_filter,
             pgmid    TYPE tadir-pgmid,
             object   TYPE tadir-object,
             obj_name TYPE trobj_name,
           END OF ty_e071_filter,
           ty_e071_filter_tt TYPE STANDARD TABLE OF ty_e071_filter.

    METHODS set_filter_values
      IMPORTING
        iv_package  TYPE tadir-devclass
        it_r_trkorr TYPE ty_trrngtrkor_tt
      RAISING
        zcx_abapgit_exception .

    METHODS get_filter_values
      EXPORTING
        ev_package  TYPE tadir-devclass
        et_r_trkorr TYPE ty_trrngtrkor_tt.

  PROTECTED SECTION.
    METHODS adjust_local_filter
      IMPORTING
                it_e071_filter   TYPE ty_e071_filter_tt
                iv_package       TYPE tadir-devclass
      RETURNING VALUE(rt_filter) TYPE zif_abapgit_definitions=>ty_tadir_tt
      RAISING
                zcx_abapgit_exception.

  PRIVATE SECTION.

    DATA mt_filter TYPE zif_abapgit_definitions=>ty_tadir_tt .
    DATA mt_r_trkorr TYPE ty_trrngtrkor_tt .
    DATA mv_package TYPE tadir-devclass.

    METHODS generate_local_filter
      IMPORTING
        iv_package       TYPE tadir-devclass
        it_r_trkorr      TYPE ty_trrngtrkor_tt
      RETURNING
        VALUE(rt_filter) TYPE zif_abapgit_definitions=>ty_tadir_tt
      RAISING
        zcx_abapgit_exception .

    METHODS init .

    METHODS get_all_sub_packages
      IMPORTING
        iv_package       TYPE tadir-devclass
      RETURNING
        VALUE(rt_filter) TYPE zif_abapgit_definitions=>ty_tadir_tt.
ENDCLASS.

CLASS zcl_abapgit_object_filter_tran IMPLEMENTATION.

  METHOD generate_local_filter.
    DATA lt_e071_filter TYPE ty_e071_filter_tt.

    SELECT DISTINCT pgmid
                object
                obj_name
           INTO CORRESPONDING FIELDS OF TABLE lt_e071_filter
           FROM e071
      WHERE trkorr IN it_r_trkorr.
    IF sy-subrc <> 0.
      CLEAR lt_e071_filter.
    ENDIF.
    rt_filter = adjust_local_filter(
      iv_package     = iv_package
      it_e071_filter = lt_e071_filter ).
  ENDMETHOD.

  METHOD get_filter_values.
    et_r_trkorr = mt_r_trkorr.
    ev_package = mv_package.
  ENDMETHOD.

  METHOD zif_abapgit_object_filter~get_filter.
    rt_filter = mt_filter.
  ENDMETHOD.

  METHOD init.
    CLEAR mt_filter.
    CLEAR mt_r_trkorr.
    CLEAR mv_package.
  ENDMETHOD.

  METHOD set_filter_values.
    init( ).
    mt_r_trkorr = it_r_trkorr.
    mv_package = iv_package.
    IF it_r_trkorr IS NOT INITIAL.
      mt_filter = generate_local_filter(
        iv_package  = mv_package
        it_r_trkorr = mt_r_trkorr ).

    ENDIF.
  ENDMETHOD.

  METHOD adjust_local_filter.

    DATA lt_e071_filter TYPE ty_e071_filter_tt.
    DATA lr_e071_filter TYPE REF TO ty_e071_filter.
    DATA ls_filter TYPE zif_abapgit_definitions=>ty_tadir.
    DATA lv_trobj_name_new TYPE trobj_name.
    DATA lv_trobj_type_new TYPE tadir-object.
    DATA lt_filter TYPE zif_abapgit_definitions=>ty_tadir_tt.
    DATA lr_cts_api TYPE REF TO zif_abapgit_cts_api.

    lt_e071_filter = it_e071_filter.

    LOOP AT lt_e071_filter REFERENCE INTO lr_e071_filter.

      IF lr_e071_filter->pgmid = 'LIMU'.
        "Get Main Object from LIMU Object (Example the Class (R3TR) of a Method (LIMU))

        lr_cts_api = zcl_abapgit_factory=>get_cts_api( ).

        TRY.
            lr_cts_api->get_r3tr_obj_for_limu_obj(
              EXPORTING
                iv_object   = lr_e071_filter->object
                iv_obj_name = lr_e071_filter->obj_name
              IMPORTING
                ev_object   = lv_trobj_type_new
                ev_obj_name = lv_trobj_name_new ).
          CATCH zcx_abapgit_exception.
            CONTINUE.
        ENDTRY.

        CLEAR ls_filter.
        ls_filter-pgmid = 'R3TR'.
        ls_filter-object = lv_trobj_type_new.
        ls_filter-obj_name = lv_trobj_name_new.
      ELSE.
        ls_filter-pgmid = lr_e071_filter->pgmid.
        ls_filter-object = lr_e071_filter->object.
        ls_filter-obj_name = lr_e071_filter->obj_name.
      ENDIF.
      INSERT ls_filter INTO TABLE rt_filter.
    ENDLOOP.

    IF iv_package IS NOT INITIAL.
      ls_filter-pgmid = 'R3TR'.
      ls_filter-object = 'DEVC'.
      ls_filter-obj_name = iv_package.
      INSERT ls_filter INTO TABLE rt_filter.

      lt_filter = get_all_sub_packages( iv_package ).
      INSERT LINES OF lt_filter INTO TABLE rt_filter.

    ENDIF.

    SORT rt_filter.
    DELETE ADJACENT DUPLICATES FROM rt_filter.

    IF rt_filter IS INITIAL.

      zcx_abapgit_exception=>raise( 'No objects found for transport filter' ).

    ENDIF.

  ENDMETHOD.

  METHOD get_all_sub_packages.

    DATA li_package TYPE REF TO zif_abapgit_sap_package.
    DATA lt_list TYPE  zif_abapgit_sap_package=>ty_devclass_tt.
    DATA lr_list TYPE REF TO devclass.
    DATA ls_filter TYPE zif_abapgit_definitions=>ty_tadir.

    li_package = zcl_abapgit_factory=>get_sap_package( iv_package = iv_package ).
    lt_list = li_package->list_subpackages( ).
    LOOP AT lt_list REFERENCE INTO lr_list.
      ls_filter-pgmid = 'R3TR'.
      ls_filter-object = 'DEVC'.
      ls_filter-obj_name = lr_list->*.
      INSERT ls_filter INTO TABLE rt_filter.
    ENDLOOP.

  ENDMETHOD.

ENDCLASS.


CLASS y_exemption_general DEFINITION PUBLIC CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES y_if_exemption.
    ALIASES is_object_exempted FOR y_if_exemption~is_object_exempted.

  PRIVATE SECTION.
    CLASS-METHODS is_tadir_generated IMPORTING object_type   TYPE trobjtype
                                               object_name   TYPE sobj_name
                                     RETURNING VALUE(result) TYPE abap_bool.

    CLASS-METHODS is_object_existing IMPORTING object_type   TYPE trobjtype
                                               object_name   TYPE sobj_name
                                     RETURNING VALUE(result) TYPE abap_bool.
ENDCLASS.

CLASS y_exemption_general IMPLEMENTATION.

  METHOD y_if_exemption~is_object_exempted.
    DATA(object_exists) = is_object_existing( object_type = object_type
                                              object_name = object_name ).

    DATA(tadir_generated) = is_tadir_generated( object_type = object_type
                                                object_name = object_name ).

    result = xsdbool( object_exists = abap_true OR tadir_generated = abap_true ).
  ENDMETHOD.

  METHOD is_object_existing.
    CONSTANTS object_exists TYPE char1 VALUE 'X'.

    DATA existence_flag TYPE strl_pari-flag.
    DATA l_object_type  TYPE e071-object.
    DATA l_object_name  TYPE e071-obj_name.

    l_object_type = object_type.
    l_object_name = object_name.

    CALL FUNCTION 'TR_CHECK_EXIST'
      EXPORTING
        iv_pgmid             = 'R3TR'
        iv_object            = l_object_type
        iv_obj_name          = l_object_name
      IMPORTING
        e_exist              = existence_flag
      EXCEPTIONS
        tr_no_check_function = 1.

    IF sy-subrc = 0 AND existence_flag <> object_exists.
      result = abap_true.
    ENDIF.
  ENDMETHOD.

  METHOD is_tadir_generated.
    SELECT SINGLE genflag
    FROM tadir
    INTO @result
    WHERE pgmid = 'R3TR'
    AND object = @object_type
    AND obj_name = @object_name.
  ENDMETHOD.

ENDCLASS.


CLASS zcl_abapgit_file_status DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    CLASS-METHODS status
      IMPORTING
        !io_repo          TYPE REF TO zcl_abapgit_repo
        !ii_log           TYPE REF TO zif_abapgit_log OPTIONAL
      RETURNING
        VALUE(rt_results) TYPE zif_abapgit_definitions=>ty_results_tt
      RAISING
        zcx_abapgit_exception .
  PROTECTED SECTION.
  PRIVATE SECTION.

    CLASS-METHODS calculate_status
      IMPORTING
        !iv_devclass      TYPE devclass
        !io_dot           TYPE REF TO zcl_abapgit_dot_abapgit
        !it_local         TYPE zif_abapgit_definitions=>ty_files_item_tt
        !it_remote        TYPE zif_abapgit_definitions=>ty_files_tt
        !it_cur_state     TYPE zif_abapgit_definitions=>ty_file_signatures_tt
      RETURNING
        VALUE(rt_results) TYPE zif_abapgit_definitions=>ty_results_tt
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS prepare_remote
      IMPORTING
        !io_dot          TYPE REF TO zcl_abapgit_dot_abapgit
        !it_remote       TYPE zif_abapgit_definitions=>ty_files_tt
      RETURNING
        VALUE(rt_remote) TYPE zif_abapgit_definitions=>ty_files_tt
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS process_local
      IMPORTING
        !io_dot       TYPE REF TO zcl_abapgit_dot_abapgit
        !it_local     TYPE zif_abapgit_definitions=>ty_files_item_tt
        !it_state_idx TYPE zif_abapgit_definitions=>ty_file_signatures_ts
      CHANGING
        !ct_remote    TYPE zif_abapgit_definitions=>ty_files_tt
        !ct_items     TYPE zif_abapgit_definitions=>ty_items_tt
        !ct_results   TYPE zif_abapgit_definitions=>ty_results_tt
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS process_items
      IMPORTING
        !iv_devclass TYPE devclass
        !io_dot      TYPE REF TO zcl_abapgit_dot_abapgit
        !it_remote   TYPE zif_abapgit_definitions=>ty_files_tt
      CHANGING
        !ct_items    TYPE zif_abapgit_definitions=>ty_items_tt
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS process_remote
      IMPORTING
        !iv_devclass  TYPE devclass
        !io_dot       TYPE REF TO zcl_abapgit_dot_abapgit
        !it_local     TYPE zif_abapgit_definitions=>ty_files_item_tt
        !it_remote    TYPE zif_abapgit_definitions=>ty_files_tt
        !it_state_idx TYPE zif_abapgit_definitions=>ty_file_signatures_ts
        !it_items_idx TYPE zif_abapgit_definitions=>ty_items_ts
      CHANGING
        !ct_results   TYPE zif_abapgit_definitions=>ty_results_tt
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS run_checks
      IMPORTING
        !ii_log     TYPE REF TO zif_abapgit_log
        !it_results TYPE zif_abapgit_definitions=>ty_results_tt
        !io_dot     TYPE REF TO zcl_abapgit_dot_abapgit
        !iv_top     TYPE devclass
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS build_existing
      IMPORTING
        !is_local        TYPE zif_abapgit_definitions=>ty_file_item
        !is_remote       TYPE zif_abapgit_definitions=>ty_file
        !it_state        TYPE zif_abapgit_definitions=>ty_file_signatures_ts
      RETURNING
        VALUE(rs_result) TYPE zif_abapgit_definitions=>ty_result .
    CLASS-METHODS build_new_local
      IMPORTING
        !is_local        TYPE zif_abapgit_definitions=>ty_file_item
      RETURNING
        VALUE(rs_result) TYPE zif_abapgit_definitions=>ty_result .
    CLASS-METHODS build_new_remote
      IMPORTING
        !iv_devclass     TYPE devclass
        !io_dot          TYPE REF TO zcl_abapgit_dot_abapgit
        !is_remote       TYPE zif_abapgit_definitions=>ty_file
        !it_items        TYPE zif_abapgit_definitions=>ty_items_ts
        !it_state        TYPE zif_abapgit_definitions=>ty_file_signatures_ts
      RETURNING
        VALUE(rs_result) TYPE zif_abapgit_definitions=>ty_result
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS get_object_package
      IMPORTING
        !iv_object         TYPE tadir-object
        !iv_obj_name       TYPE tadir-obj_name
      RETURNING
        VALUE(rv_devclass) TYPE devclass
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS check_package_move
      IMPORTING
        !ii_log     TYPE REF TO zif_abapgit_log
        !it_results TYPE zif_abapgit_definitions=>ty_results_tt
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS check_files_folder
      IMPORTING
        !ii_log     TYPE REF TO zif_abapgit_log
        !it_results TYPE zif_abapgit_definitions=>ty_results_tt
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS check_package_folder
      IMPORTING
        !ii_log     TYPE REF TO zif_abapgit_log
        !it_results TYPE zif_abapgit_definitions=>ty_results_tt
        !io_dot     TYPE REF TO zcl_abapgit_dot_abapgit
        !iv_top     TYPE devclass
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS check_multiple_files
      IMPORTING
        !ii_log     TYPE REF TO zif_abapgit_log
        !it_results TYPE zif_abapgit_definitions=>ty_results_tt
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS check_namespace
      IMPORTING
        !ii_log     TYPE REF TO zif_abapgit_log
        !it_results TYPE zif_abapgit_definitions=>ty_results_tt
      RAISING
        zcx_abapgit_exception .
ENDCLASS.

CLASS ZCL_ABAPGIT_FILE_STATUS IMPLEMENTATION.

  METHOD build_existing.

    DATA: ls_file_sig LIKE LINE OF it_state.

    " Item
    rs_result-obj_type = is_local-item-obj_type.
    rs_result-obj_name = is_local-item-obj_name.
    rs_result-package  = is_local-item-devclass.

    " File
    rs_result-path     = is_local-file-path.
    rs_result-filename = is_local-file-filename.

    IF is_local-file-sha1 = is_remote-sha1.
      rs_result-match = abap_true.
      RETURN.
    ENDIF.

    " Match against current state
    READ TABLE it_state INTO ls_file_sig
      WITH KEY path = is_local-file-path
      filename = is_local-file-filename
      BINARY SEARCH.

    IF sy-subrc = 0.
      IF ls_file_sig-sha1 <> is_local-file-sha1.
        rs_result-lstate = zif_abapgit_definitions=>c_state-modified.
      ENDIF.
      IF ls_file_sig-sha1 <> is_remote-sha1.
        rs_result-rstate = zif_abapgit_definitions=>c_state-modified.
      ENDIF.
      rs_result-match = boolc( rs_result-lstate IS INITIAL
        AND rs_result-rstate IS INITIAL ).
    ELSE.
      " This is a strange situation. As both local and remote exist
      " the state should also be present. Maybe this is a first run of the code.
      " In this case just compare hashes directly and mark both changed
      " the user will presumably decide what to do after checking the actual diff
      rs_result-match = boolc( is_local-file-sha1 = is_remote-sha1 ).
      IF rs_result-match = abap_false.
        rs_result-lstate = zif_abapgit_definitions=>c_state-modified.
        rs_result-rstate = zif_abapgit_definitions=>c_state-modified.
      ENDIF.
    ENDIF.

  ENDMETHOD.

  METHOD build_new_local.

    " Item
    rs_result-obj_type = is_local-item-obj_type.
    rs_result-obj_name = is_local-item-obj_name.
    rs_result-package  = is_local-item-devclass.

    " File
    rs_result-path     = is_local-file-path.
    rs_result-filename = is_local-file-filename.

    " Match
    rs_result-match    = abap_false.
    rs_result-lstate   = zif_abapgit_definitions=>c_state-added.

  ENDMETHOD.

  METHOD build_new_remote.

    DATA: ls_item     LIKE LINE OF it_items,
          ls_file_sig LIKE LINE OF it_state.

    " Common and default part
    rs_result-path     = is_remote-path.
    rs_result-filename = is_remote-filename.
    rs_result-match    = abap_false.
    rs_result-rstate   = zif_abapgit_definitions=>c_state-added.

    zcl_abapgit_filename_logic=>file_to_object(
      EXPORTING
        iv_filename = is_remote-filename
        iv_path     = is_remote-path
        iv_devclass = iv_devclass
        io_dot      = io_dot
      IMPORTING
        es_item     = ls_item ).

    " Check if in item index + get package
    READ TABLE it_items INTO ls_item
      WITH KEY obj_type = ls_item-obj_type obj_name = ls_item-obj_name
      BINARY SEARCH.

    IF sy-subrc = 0.

      " Completely new (xml, abap) and new file in an existing object
      rs_result-obj_type = ls_item-obj_type.
      rs_result-obj_name = ls_item-obj_name.
      rs_result-package  = ls_item-devclass.

      READ TABLE it_state INTO ls_file_sig
        WITH KEY path = is_remote-path filename = is_remote-filename
        BINARY SEARCH.

      " Existing file but from another package
      " was not added during local file proc as was not in tadir for repo package
      IF sy-subrc = 0.
        IF ls_file_sig-sha1 = is_remote-sha1.
          rs_result-match = abap_true.
          CLEAR rs_result-rstate.
        ELSE.
          rs_result-rstate = zif_abapgit_definitions=>c_state-modified.
        ENDIF.

        " Item is in state and in cache but with no package - it was deleted
        " OR devclass is the same as repo package (see #532)
        IF ls_item-devclass IS INITIAL OR ls_item-devclass = iv_devclass.
          rs_result-match  = abap_false.
          rs_result-lstate = zif_abapgit_definitions=>c_state-deleted.
        ENDIF.
      ENDIF.

    ELSE. " Completely unknown file, probably non-abapgit
      ASSERT 1 = 1. " No action, just follow defaults
    ENDIF.

  ENDMETHOD.

  METHOD calculate_status.

    DATA:
      lt_remote    LIKE it_remote,
      lt_items     TYPE zif_abapgit_definitions=>ty_items_tt,
      lt_items_idx TYPE zif_abapgit_definitions=>ty_items_ts, " Sorted by obj_type+obj_name
      lt_state_idx TYPE zif_abapgit_definitions=>ty_file_signatures_ts. " Sorted by path+filename

    lt_state_idx = it_cur_state. " Force sort it

    " Prepare remote files
    lt_remote = prepare_remote(
      io_dot    = io_dot
      it_remote = it_remote ).

    " Process local files and new local files
    process_local(
      EXPORTING
        io_dot       = io_dot
        it_local     = it_local
        it_state_idx = lt_state_idx
      CHANGING
        ct_remote    = lt_remote
        ct_items     = lt_items
        ct_results   = rt_results ).

    " Complete item index for unmarked remote files
    process_items(
      EXPORTING
        iv_devclass = iv_devclass
        io_dot      = io_dot
        it_remote   = lt_remote
      CHANGING
        ct_items    = lt_items ).

    lt_items_idx = lt_items. " Self protection + UNIQUE records assertion

    " Process new remote files (marked above with empty SHA1)
    process_remote(
      EXPORTING
        iv_devclass  = iv_devclass
        io_dot       = io_dot
        it_local     = it_local
        it_remote    = lt_remote
        it_state_idx = lt_state_idx
        it_items_idx = lt_items_idx
      CHANGING
        ct_results   = rt_results ).

    SORT rt_results BY
      obj_type ASCENDING
      obj_name ASCENDING
      filename ASCENDING
      path ASCENDING.

  ENDMETHOD.

  METHOD check_files_folder.

    DATA:
      ls_item     TYPE zif_abapgit_definitions=>ty_item,
      lt_res_sort LIKE it_results,
      lt_item_idx LIKE it_results.

    FIELD-SYMBOLS:
      <ls_result>     LIKE LINE OF it_results,
      <ls_result_idx> LIKE LINE OF it_results.

    " Collect object index
    lt_res_sort = it_results.
    SORT lt_res_sort BY obj_type ASCENDING obj_name ASCENDING.

    LOOP AT it_results ASSIGNING <ls_result> WHERE NOT obj_type IS INITIAL AND packmove = abap_false.

      IF NOT ( <ls_result>-obj_type = ls_item-obj_type
          AND <ls_result>-obj_name = ls_item-obj_name ).
        APPEND INITIAL LINE TO lt_item_idx ASSIGNING <ls_result_idx>.
        <ls_result_idx>-obj_type = <ls_result>-obj_type.
        <ls_result_idx>-obj_name = <ls_result>-obj_name.
        <ls_result_idx>-path     = <ls_result>-path.
        MOVE-CORRESPONDING <ls_result> TO ls_item.
      ENDIF.

    ENDLOOP.

    LOOP AT it_results ASSIGNING <ls_result>
      WHERE NOT obj_type IS INITIAL AND obj_type <> 'DEVC' AND packmove = abap_false.

      READ TABLE lt_item_idx ASSIGNING <ls_result_idx>
        WITH KEY obj_type = <ls_result>-obj_type obj_name = <ls_result>-obj_name
        BINARY SEARCH. " Sorted above

      IF sy-subrc <> 0 OR <ls_result>-path <> <ls_result_idx>-path. " All paths are same
        ii_log->add( iv_msg = |Files for object { <ls_result>-obj_type } {
                              <ls_result>-obj_name } are not placed in the same folder|
                     iv_type = 'W' ).
      ENDIF.

    ENDLOOP.

  ENDMETHOD.

  METHOD check_multiple_files.

    DATA:
      lt_res_sort LIKE it_results,
      ls_file     TYPE zif_abapgit_definitions=>ty_file_signature.

    FIELD-SYMBOLS <ls_result> LIKE LINE OF it_results.

    lt_res_sort = it_results.
    SORT lt_res_sort BY filename ASCENDING.

    LOOP AT lt_res_sort ASSIGNING <ls_result> WHERE obj_type <> 'DEVC' AND packmove = abap_false.
      IF <ls_result>-filename IS NOT INITIAL AND <ls_result>-filename = ls_file-filename.
        ii_log->add( iv_msg  = |Multiple files with same filename, { <ls_result>-filename }|
                     iv_type = 'W' ).
      ENDIF.

      IF <ls_result>-filename IS INITIAL.
        ii_log->add( iv_msg  = |Filename is empty for object { <ls_result>-obj_type } { <ls_result>-obj_name }|
                     iv_type = 'W' ).
      ENDIF.

      MOVE-CORRESPONDING <ls_result> TO ls_file.
    ENDLOOP.

  ENDMETHOD.

  METHOD check_namespace.

    DATA:
      lv_namespace TYPE namespace,
      lt_namespace TYPE TABLE OF namespace,
      ls_trnspace  TYPE trnspace.

    FIELD-SYMBOLS <ls_result> LIKE LINE OF it_results.

    " Collect all namespaces based on name of xml-files
    LOOP AT it_results ASSIGNING <ls_result>.
      FIND REGEX '^#([a-zA-Z0-9]+)#.*\..*\.xml$' IN <ls_result>-filename SUBMATCHES lv_namespace.
      IF sy-subrc = 0.
        lv_namespace = '/' && to_upper( lv_namespace ) && '/'.
        COLLECT lv_namespace INTO lt_namespace.
      ENDIF.
    ENDLOOP.

    LOOP AT lt_namespace INTO lv_namespace.
      CALL FUNCTION 'TR_READ_NAMESPACE'
        EXPORTING
          iv_namespace           = lv_namespace
        IMPORTING
          es_trnspace            = ls_trnspace
        EXCEPTIONS
          namespace_not_existing = 1
          OTHERS                 = 2.
      IF sy-subrc <> 0.
        ii_log->add( iv_msg  = |Namespace { lv_namespace } does not exist. Create it in transaction SE03|
                     iv_type = 'W' ).
      ELSEIF ls_trnspace-editflag <> 'X'.
        ii_log->add( iv_msg  = |Namespace { lv_namespace } is not modifiable. Check it in transaction SE03|
                     iv_type = 'W' ).
      ENDIF.
    ENDLOOP.

  ENDMETHOD.

  METHOD check_package_folder.

    DATA:
      lv_path         TYPE string,
      lo_folder_logic TYPE REF TO zcl_abapgit_folder_logic.

    FIELD-SYMBOLS <ls_result> LIKE LINE OF it_results.

    lo_folder_logic = zcl_abapgit_folder_logic=>get_instance( ).

    LOOP AT it_results ASSIGNING <ls_result>
      WHERE NOT package IS INITIAL AND NOT path IS INITIAL AND packmove = abap_false.

      lv_path = lo_folder_logic->package_to_path(
        iv_top     = iv_top
        io_dot     = io_dot
        iv_package = <ls_result>-package ).

      IF lv_path <> <ls_result>-path.
        ii_log->add( iv_msg = |Package and path does not match for object, {
                       <ls_result>-obj_type } { <ls_result>-obj_name }|
                     iv_type = 'W' ).
      ENDIF.

    ENDLOOP.

  ENDMETHOD.

  METHOD check_package_move.

    DATA:
      lt_move_idx LIKE it_results.

    FIELD-SYMBOLS:
      <ls_result>      LIKE LINE OF it_results,
      <ls_result_move> LIKE LINE OF it_results.

    LOOP AT it_results ASSIGNING <ls_result>
      WHERE lstate = zif_abapgit_definitions=>c_state-added AND packmove = abap_true.

      READ TABLE lt_move_idx TRANSPORTING NO FIELDS
        WITH KEY obj_type = <ls_result>-obj_type obj_name = <ls_result>-obj_name
        BINARY SEARCH. " Sorted since it_result is sorted
      IF sy-subrc <> 0.
        ii_log->add( iv_msg  = |Changed package assignment for object {
                               <ls_result>-obj_type } { <ls_result>-obj_name }|
                     iv_type = 'W' ).
        APPEND INITIAL LINE TO lt_move_idx ASSIGNING <ls_result_move>.
        <ls_result_move>-obj_type = <ls_result>-obj_type.
        <ls_result_move>-obj_name = <ls_result>-obj_name.
        <ls_result_move>-path     = <ls_result>-path.
      ENDIF.

    ENDLOOP.

  ENDMETHOD.

  METHOD get_object_package.
    DATA: lv_name    TYPE devclass,
          li_package TYPE REF TO zif_abapgit_sap_package.

    rv_devclass = zcl_abapgit_factory=>get_tadir( )->get_object_package(
      iv_object   = iv_object
      iv_obj_name = iv_obj_name ).
    IF rv_devclass IS INITIAL AND iv_object = 'DEVC' AND iv_obj_name(1) = '$'.
      " local packages usually have no tadir entry
      lv_name = iv_obj_name.
      li_package = zcl_abapgit_factory=>get_sap_package( lv_name ).
      IF li_package->exists( ) = abap_true.
        rv_devclass = lv_name.
      ENDIF.
    ENDIF.
  ENDMETHOD.

  METHOD prepare_remote.

    DATA lv_index TYPE sy-index.

    FIELD-SYMBOLS <ls_remote> LIKE LINE OF it_remote.

    rt_remote = it_remote.
    SORT rt_remote BY path filename.

    " Skip ignored files
    LOOP AT rt_remote ASSIGNING <ls_remote>.
      lv_index = sy-tabix.
      IF io_dot->is_ignored( iv_path     = <ls_remote>-path
                             iv_filename = <ls_remote>-filename ) = abap_true.
        DELETE rt_remote INDEX lv_index.
      ENDIF.
    ENDLOOP.

  ENDMETHOD.

  METHOD process_items.

    DATA:
      ls_item         LIKE LINE OF ct_items,
      lv_is_xml       TYPE abap_bool,
      lv_sub_fetched  TYPE abap_bool,
      lt_sub_packages TYPE zif_abapgit_sap_package=>ty_devclass_tt,
      lv_msg          TYPE string.

    FIELD-SYMBOLS <ls_remote> LIKE LINE OF it_remote.

    LOOP AT it_remote ASSIGNING <ls_remote> WHERE sha1 IS NOT INITIAL.

      zcl_abapgit_filename_logic=>file_to_object(
        EXPORTING
          iv_filename = <ls_remote>-filename
          iv_path     = <ls_remote>-path
          io_dot      = io_dot
          iv_devclass = iv_devclass
        IMPORTING
          es_item     = ls_item
          ev_is_xml   = lv_is_xml ).

      CHECK lv_is_xml = abap_true. " only object definitions

      ls_item-devclass = get_object_package(
        iv_object   = ls_item-obj_type
        iv_obj_name = ls_item-obj_name ).

      IF NOT ls_item-devclass IS INITIAL AND iv_devclass <> ls_item-devclass.
        IF lv_sub_fetched = abap_false.
          lt_sub_packages = zcl_abapgit_factory=>get_sap_package( iv_devclass )->list_subpackages( ).
          lv_sub_fetched = abap_true.
          SORT lt_sub_packages BY table_line. "Optimize Read Access
        ENDIF.

        " Make sure the package is under the repo main package
        READ TABLE lt_sub_packages TRANSPORTING NO FIELDS
          WITH KEY table_line = ls_item-devclass
          BINARY SEARCH.
        IF sy-subrc <> 0.
          IF ls_item-obj_type = 'DEVC'.
            " If package already exist but is not included in the package hierarchy of
            " the package assigned to the repository, then a manual change of the package
            " is required i.e. setting a parent package to iv_devclass (or one of its
            " subpackages). We don't this automatically since it's not clear where in the
            " hierarchy the new package should be located. (#4108)
            lv_msg = |Package { ls_item-devclass } already exists but is not a subpackage of { iv_devclass
                     }. Check your package and folder logic or assign { ls_item-devclass
                     } to package hierarchy of { iv_devclass } to match the repository.|.
            zcx_abapgit_exception=>raise( lv_msg ).
          ELSE.
            CLEAR ls_item-devclass.
          ENDIF.
        ENDIF.
      ENDIF.

      APPEND ls_item TO ct_items.
    ENDLOOP.

    SORT ct_items DESCENDING. " Default key - type, name, pkg, inactive
    DELETE ADJACENT DUPLICATES FROM ct_items COMPARING obj_type obj_name devclass.

  ENDMETHOD.

  METHOD process_local.

    DATA lv_msg TYPE string.

    FIELD-SYMBOLS:
      <ls_remote> LIKE LINE OF ct_remote,
      <ls_result> LIKE LINE OF ct_results,
      <ls_state>  LIKE LINE OF it_state_idx,
      <ls_local>  LIKE LINE OF it_local.

    LOOP AT it_local ASSIGNING <ls_local>.
      " Skip ignored files
      IF io_dot->is_ignored( iv_path     = <ls_local>-file-path
                             iv_filename = <ls_local>-file-filename ) = abap_true.
        CONTINUE.
      ENDIF.

      APPEND INITIAL LINE TO ct_results ASSIGNING <ls_result>.
      IF <ls_local>-item IS NOT INITIAL.
        APPEND <ls_local>-item TO ct_items. " Collect for item index
      ENDIF.

      READ TABLE ct_remote ASSIGNING <ls_remote>
        WITH KEY path = <ls_local>-file-path filename = <ls_local>-file-filename
        BINARY SEARCH.
      IF sy-subrc = 0.  " Exist local and remote
        <ls_result> = build_existing(
          is_local  = <ls_local>
          is_remote = <ls_remote>
          it_state  = it_state_idx ).
        IF <ls_remote>-sha1 IS INITIAL.
          IF <ls_local>-file-filename = zcl_abapgit_filename_logic=>c_package_file.
            lv_msg = |Package name conflict { <ls_local>-item-obj_type } { <ls_local>-item-obj_name }. | &&
              |Rename package or use FULL folder logic|.
          ELSE.
            lv_msg = |Checksum conflict { <ls_local>-item-obj_type } { <ls_local>-item-obj_name }. | &&
              |Please create an issue on Github|.
          ENDIF.
          zcx_abapgit_exception=>raise( lv_msg ).
        ENDIF.
        CLEAR <ls_remote>-sha1. " Mark as processed
      ELSE.             " Only L exists
        <ls_result> = build_new_local( <ls_local> ).
        " Check if same file exists in different location
        READ TABLE ct_remote ASSIGNING <ls_remote>
          WITH KEY file
          COMPONENTS filename = <ls_local>-file-filename.
        IF sy-subrc = 0 AND <ls_local>-file-sha1 = <ls_remote>-sha1.
          <ls_result>-packmove = abap_true.
        ELSEIF sy-subrc = 4.
          " Check if file existed before and was deleted remotely
          READ TABLE it_state_idx ASSIGNING <ls_state>
            WITH KEY path = <ls_local>-file-path filename = <ls_local>-file-filename
            BINARY SEARCH.
          IF sy-subrc = 0.
            IF <ls_local>-file-sha1 = <ls_state>-sha1.
              <ls_result>-lstate = zif_abapgit_definitions=>c_state-unchanged.
            ELSE.
              <ls_result>-lstate = zif_abapgit_definitions=>c_state-modified.
            ENDIF.
            <ls_result>-rstate = zif_abapgit_definitions=>c_state-deleted.
          ENDIF.
        ENDIF.
      ENDIF.
      <ls_result>-inactive = <ls_local>-item-inactive.
    ENDLOOP.

  ENDMETHOD.

  METHOD process_remote.

    FIELD-SYMBOLS:
      <ls_remote> LIKE LINE OF it_remote,
      <ls_result> LIKE LINE OF ct_results,
      <ls_local>  LIKE LINE OF it_local.

    LOOP AT it_remote ASSIGNING <ls_remote> WHERE sha1 IS NOT INITIAL.
      APPEND INITIAL LINE TO ct_results ASSIGNING <ls_result>.

      <ls_result> = build_new_remote(
        iv_devclass = iv_devclass
        io_dot      = io_dot
        is_remote   = <ls_remote>
        it_items    = it_items_idx
        it_state    = it_state_idx ).

      " Check if same file exists in different location
      READ TABLE it_local ASSIGNING <ls_local>
        WITH KEY file-filename = <ls_remote>-filename.
      IF sy-subrc = 0.
        <ls_result>-match = abap_false.
        <ls_result>-lstate = zif_abapgit_definitions=>c_state-deleted.
        <ls_result>-rstate = zif_abapgit_definitions=>c_state-unchanged.
        IF <ls_local>-file-sha1 = <ls_remote>-sha1.
          <ls_result>-packmove = abap_true.
        ENDIF.
      ELSEIF sy-subrc = 4.
        " Check if file existed before and was deleted locally
        READ TABLE it_state_idx TRANSPORTING NO FIELDS
          WITH KEY path = <ls_remote>-path filename = <ls_remote>-filename
          BINARY SEARCH.
        IF sy-subrc = 0.
          <ls_result>-match = abap_false.
          <ls_result>-lstate = zif_abapgit_definitions=>c_state-deleted.
        ENDIF.
      ENDIF.
    ENDLOOP.

  ENDMETHOD.

  METHOD run_checks.

    " This method just adds messages to the log. No log, nothing to do here
    IF ii_log IS INITIAL.
      RETURN.
    ENDIF.

    " Find all objects which were assigned to a different package
    check_package_move(
      ii_log     = ii_log
      it_results = it_results ).

    " Check files for one object is in the same folder
    check_files_folder(
      ii_log     = ii_log
      it_results = it_results ).

    " Check that objects are created in package corresponding to folder
    check_package_folder(
      ii_log     = ii_log
      it_results = it_results
      io_dot     = io_dot
      iv_top     = iv_top ).

    " Check for multiple files with same filename
    check_multiple_files(
      ii_log     = ii_log
      it_results = it_results ).

    " Check if namespaces exist already
    check_namespace(
      ii_log     = ii_log
      it_results = it_results ).

  ENDMETHOD.

  METHOD status.

    DATA lt_local TYPE zif_abapgit_definitions=>ty_files_item_tt.
    DATA lt_remote TYPE zif_abapgit_definitions=>ty_files_tt.
    DATA li_exit TYPE REF TO zif_abapgit_exit.

    lt_local = io_repo->get_files_local( ii_log = ii_log ).

    IF lines( lt_local ) <= 2.
      " Less equal two means that we have only the .abapgit.xml and the package in
      " our local repository. In this case we have to update our local .abapgit.xml
      " from the remote one. Otherwise we get errors when e.g. the folder starting
      " folder is different.
      io_repo->find_remote_dot_abapgit( ).
    ENDIF.

    lt_remote = io_repo->get_files_remote( ).

    li_exit = zcl_abapgit_exit=>get_instance( ).
    li_exit->pre_calculate_repo_status(
      EXPORTING
        is_repo_meta = io_repo->ms_data
      CHANGING
        ct_local  = lt_local
        ct_remote = lt_remote ).

    rt_results = calculate_status(
      iv_devclass  = io_repo->get_package( )
      io_dot       = io_repo->get_dot_abapgit( )
      it_local     = lt_local
      it_remote    = lt_remote
      it_cur_state = io_repo->get_local_checksums_per_file( ) ).

    run_checks(
      ii_log     = ii_log
      it_results = rt_results
      io_dot     = io_repo->get_dot_abapgit( )
      iv_top     = io_repo->get_package( ) ).

  ENDMETHOD.
ENDCLASS.


CLASS ltc_class DEFINITION INHERITING FROM y_unit_test_base FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
  PROTECTED SECTION.
    METHODS get_cut REDEFINITION.
    METHODS get_code_with_issue REDEFINITION.
    METHODS get_code_without_issue REDEFINITION.
    METHODS get_code_with_exemption REDEFINITION.
ENDCLASS.

CLASS ltc_class IMPLEMENTATION.

  METHOD get_cut.
    result ?= NEW y_check_is_interface_in_class( ).
  ENDMETHOD.

  METHOD get_code_with_issue.
    result = VALUE #(
      ( 'REPORT ut_repo.' )

      ( 'CLASS lcl_classname DEFINITION.' )
      ( ' PUBLIC SECTION.' )
      ( '  METHODS publ_method.' )
      ( ' PROTECTED SECTION.' )
      ( ' PRIVATE SECTION.' )
      ( 'ENDCLASS.' )

      ( 'CLASS lcl_classname IMPLEMENTATION.' )
      ( ' METHOD publ_method.' )
      ( ' ENDMETHOD.' )
      ( 'ENDCLASS.' )
    ).
  ENDMETHOD.

  METHOD get_code_without_issue.
    result = VALUE #(
      ( 'REPORT ut_repo.' )

      ( 'CLASS lcl_abstr DEFINITION ABSTRACT.' )
      ( ' PUBLIC SECTION.' )
      ( '  METHODS abstr_method ABSTRACT.' )
      ( '  METHODS constructor.' )
      ( 'ENDCLASS.' )

      ( 'CLASS lcl_abstr IMPLEMENTATION.' )
      ( ' METHOD constructor.' )
      ( ' ENDMETHOD.' )
      ( 'ENDCLASS.' )

      ( 'CLASS lcl_inh_abstr DEFINITION INHERITING FROM lcl_abstr.' )
      ( ' PUBLIC SECTION.' )
      ( '  METHODS abstr_method REDEFINITION.' )
      ( '  METHODS constructor.' )
      ( ' PROTECTED SECTION.' )
      ( '  METHODS prot_method.' )
      ( ' PRIVATE SECTION.' )
      ( '  METHODS priv_method.' )
      ( 'ENDCLASS.' )

      ( 'CLASS lcl_inh_abstr IMPLEMENTATION.' )
      ( ' METHOD constructor.' )
      ( '  super->constructor( ).' )
      ( ' ENDMETHOD.' )

      ( ' METHOD abstr_method.' )
      ( ' ENDMETHOD.' )

      ( ' METHOD prot_method.' )
      ( ' ENDMETHOD.' )

      ( ' METHOD priv_method.' )
      ( ' ENDMETHOD.' )
      ( 'ENDCLASS.' )
    ).
  ENDMETHOD.

  METHOD get_code_with_exemption.
    result = VALUE #(
      ( 'REPORT ut_repo.' )

      ( 'CLASS lcl_classname DEFINITION. ' )
      ( ' PUBLIC SECTION. "#EC INTF_IN_CLASS' )
      ( '  METHODS publ_method.'  )
      ( ' PROTECTED SECTION.' )
      ( ' PRIVATE SECTION.' )
      ( 'ENDCLASS.' )

      ( 'CLASS lcl_classname IMPLEMENTATION.' )
      ( ' METHOD publ_method.' )
      ( ' ENDMETHOD.' )
      ( 'ENDCLASS.' )
    ).
  ENDMETHOD.

ENDCLASS.

CLASS ltc_test_class DEFINITION INHERITING FROM ltc_class FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
  PROTECTED SECTION.
    METHODS get_code_without_issue REDEFINITION.
ENDCLASS.

CLASS ltc_test_class IMPLEMENTATION.

  METHOD get_code_without_issue.
    result = VALUE #(
      ( ' REPORT ut_repo.' )

      ( ' CLASS lcl_classname DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.' )
      ( '   PUBLIC SECTION.' )
      ( '     METHODS publ_method.' )
      ( '     METHODS test_method FOR TESTING.' )
      ( ' ENDCLASS.' )

      ( ' CLASS lcl_classname IMPLEMENTATION.' )
      ( '   METHOD publ_method.' )
      ( '   ENDMETHOD.' )
      ( '   METHOD test_method.' )
      ( '   ENDMETHOD.' )
      ( ' ENDCLASS.' )
    ).
  ENDMETHOD.

ENDCLASS.


*----------------------------------------------------------------------*
*       CLASS lcl_Test DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS ltcl_test DEFINITION FOR TESTING
  DURATION SHORT
  RISK LEVEL HARMLESS
  FINAL.

  PRIVATE SECTION.
* ================

    DATA: mt_code   TYPE string_table,
          ms_result TYPE scirest_ad,
          mo_check  TYPE REF TO zcl_aoc_check_14.

    METHODS:
      setup,
      export_import FOR TESTING,
      test001_01 FOR TESTING,
      test001_02 FOR TESTING,
      test001_03 FOR TESTING,
      test001_04 FOR TESTING,
      test001_05 FOR TESTING,
      test001_06 FOR TESTING.

ENDCLASS.       "lcl_Test

*----------------------------------------------------------------------*
*       CLASS lcl_Test IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS ltcl_test IMPLEMENTATION.
* ==============================

  DEFINE _code.
    APPEND &1 TO mt_code.
  END-OF-DEFINITION.

  METHOD setup.
    CREATE OBJECT mo_check.
    zcl_aoc_unit_test=>set_check( mo_check ).
  ENDMETHOD.                    "setup

  METHOD export_import.
    zcl_aoc_unit_test=>export_import( mo_check ).
  ENDMETHOD.

  METHOD test001_01.
* ===========

    _code '*IF lv_foo = lv_bar.   '.
    _code '*  WRITE ''foo''.      '.
    _code '*ENDIF.                '.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_equals( exp = '001'
                                        act = ms_result-code ).

  ENDMETHOD.                    "test1

  METHOD test001_02.
* ===========

    _code '* IF that is the case, then do this because of something'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_initial( ms_result ).

  ENDMETHOD.                    "test2

  METHOD test001_03.
* ===========

    _code '* MESSAGE s111(zzz) WITH <ls_foo>-progname.'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_equals( exp = '001'
                                        act = ms_result-code ).

  ENDMETHOD.

  METHOD test001_04.
* ===========

    _code '* Aaaaa aaaaaaaaaaa aa all aaaas in aaa aaa aaaaaaa of other aaaa''s result'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_initial( ms_result ).

  ENDMETHOD.

  METHOD test001_05.
* ===========

    _code '** double star'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_initial( ms_result ).

  ENDMETHOD.

  METHOD test001_06.
* ===========

    _code '* ...'.

    ms_result = zcl_aoc_unit_test=>check( mt_code ).

    cl_abap_unit_assert=>assert_initial( ms_result ).

  ENDMETHOD.

ENDCLASS.       "lcl_Test


CLASS zcl_excel_worksheets DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

*"* public components of class ZCL_EXCEL_WORKSHEETS
*"* do not include other source files here!!!
  PUBLIC SECTION.

    DATA active_worksheet TYPE zexcel_active_worksheet VALUE 1. "#EC NOTEXT .  .  .  .  .  .  .  .  . " .
    DATA name TYPE zexcel_worksheets_name VALUE 'Worksheets'. "#EC NOTEXT .  .  .  .  .  .  .  .  . " .

    METHODS add
      IMPORTING
        !ip_worksheet TYPE REF TO zcl_excel_worksheet .
    METHODS clear .
    METHODS constructor .
    METHODS get
      IMPORTING
        !ip_index           TYPE zexcel_active_worksheet
      RETURNING
        VALUE(eo_worksheet) TYPE REF TO zcl_excel_worksheet .
    METHODS get_iterator
      RETURNING
        VALUE(eo_iterator) TYPE REF TO cl_object_collection_iterator .
    METHODS is_empty
      RETURNING
        VALUE(is_empty) TYPE flag .
    METHODS remove
      IMPORTING
        !ip_worksheet TYPE REF TO zcl_excel_worksheet .
    METHODS size
      RETURNING
        VALUE(ep_size) TYPE i .
*"* protected components of class ZCL_EXCEL_WORKSHEETS
*"* do not include other source files here!!!
  PROTECTED SECTION.
*"* private components of class ZCL_EXCEL_WORKSHEETS
*"* do not include other source files here!!!
  PRIVATE SECTION.

    DATA worksheets TYPE REF TO cl_object_collection .
ENDCLASS.

CLASS zcl_excel_worksheets IMPLEMENTATION.

  METHOD add.

    worksheets->add( ip_worksheet ).

  ENDMETHOD.

  METHOD clear.

    worksheets->clear( ).

  ENDMETHOD.

  METHOD constructor.

    CREATE OBJECT worksheets.

  ENDMETHOD.

  METHOD get.

    DATA lv_index TYPE i.
    lv_index = ip_index.
    eo_worksheet ?= worksheets->get( lv_index ).

  ENDMETHOD.

  METHOD get_iterator.

    eo_iterator ?= worksheets->get_iterator( ).

  ENDMETHOD.

  METHOD is_empty.

    is_empty = worksheets->is_empty( ).

  ENDMETHOD.

  METHOD remove.

    worksheets->remove( ip_worksheet ).

  ENDMETHOD.

  METHOD size.

    ep_size = worksheets->size( ).

  ENDMETHOD.
ENDCLASS.


**********************************************************************
* UTIL
**********************************************************************

CLASS lcl_nodes_helper DEFINITION FINAL.
  PUBLIC SECTION.

    DATA mt_nodes TYPE zif_abapgit_ajson=>ty_nodes_tt READ-ONLY.

    METHODS add
      IMPORTING
        iv_str TYPE string.
    METHODS sorted
      RETURNING
        VALUE(rt_nodes) TYPE zif_abapgit_ajson=>ty_nodes_ts.

ENDCLASS.

CLASS lcl_nodes_helper IMPLEMENTATION.
  METHOD add.

    FIELD-SYMBOLS <n> LIKE LINE OF mt_nodes.
    DATA lv_children TYPE string.
    DATA lv_index TYPE string.

    APPEND INITIAL LINE TO mt_nodes ASSIGNING <n>.

    SPLIT iv_str AT '|' INTO
      <n>-path
      <n>-name
      <n>-type
      <n>-value
      lv_index
      lv_children.
    CONDENSE <n>-path.
    CONDENSE <n>-name.
    CONDENSE <n>-type.
    CONDENSE <n>-value.
    <n>-index = lv_index.
    <n>-children = lv_children.

  ENDMETHOD.

  METHOD sorted.
    rt_nodes = mt_nodes.
  ENDMETHOD.
ENDCLASS.

**********************************************************************
* PARSER
**********************************************************************

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

  PUBLIC SECTION.

    CLASS-METHODS sample_json
      IMPORTING
        iv_separator   TYPE string OPTIONAL
      RETURNING
        VALUE(rv_json) TYPE string.

ENDCLASS.

CLASS ltcl_parser_test IMPLEMENTATION.

  METHOD sample_json.

    rv_json =
      '{\n' &&
      '  "string": "abc",\n' &&
      '  "number": 123,\n' &&
      '  "float": 123.45,\n' &&
      '  "boolean": true,\n' &&
      '  "false": false,\n' &&
      '  "null": null,\n' &&
      '  "date": "2020-03-15",\n' &&
      '  "issues": [\n' &&
      '    {\n' &&
      '      "message": "Indentation problem ...",\n' &&
      '      "key": "indentation",\n' &&
      '      "start": {\n' &&
      '        "row": 4,\n' &&
      '        "col": 3\n' &&
      '      },\n' &&
      '      "end": {\n' &&
      '        "row": 4,\n' &&
      '        "col": 26\n' &&
      '      },\n' &&
      '      "filename": "./zxxx.prog.abap"\n' &&
      '    },\n' &&
      '    {\n' &&
      '      "message": "Remove space before XXX",\n' &&
      '      "key": "space_before_dot",\n' &&
      '      "start": {\n' &&
      '        "row": 3,\n' &&
      '        "col": 21\n' &&
      '      },\n' &&
      '      "end": {\n' &&
      '        "row": 3,\n' &&
      '        "col": 22\n' &&
      '      },\n' &&
      '      "filename": "./zxxx.prog.abap"\n' &&
      '    }\n' &&
      '  ]\n' &&
      '}'.

    REPLACE ALL OCCURRENCES OF '\n' IN rv_json WITH iv_separator.

  ENDMETHOD.

ENDCLASS.

**********************************************************************
* JSON UTILITIES
**********************************************************************

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

  PRIVATE SECTION.

    METHODS json_diff FOR TESTING RAISING zcx_abapgit_ajson_error.
    METHODS json_diff_types FOR TESTING RAISING zcx_abapgit_ajson_error.
    METHODS json_sort FOR TESTING RAISING zcx_abapgit_ajson_error.

ENDCLASS.

CLASS ltcl_json_utils IMPLEMENTATION.

  METHOD json_diff.

    DATA:
      lv_json       TYPE string,
      lo_util       TYPE REF TO zcl_abapgit_ajson_utilities,
      lo_insert     TYPE REF TO zif_abapgit_ajson,
      lo_delete     TYPE REF TO zif_abapgit_ajson,
      lo_change     TYPE REF TO zif_abapgit_ajson,
      lo_insert_exp TYPE REF TO lcl_nodes_helper,
      lo_delete_exp TYPE REF TO lcl_nodes_helper,
      lo_change_exp TYPE REF TO lcl_nodes_helper.

    lv_json =
      '{\n' &&
      '  "string": "abc",\n' && " no changes
      '  "number": 789,\n' &&   " changed value
      '  "float": 123.45,\n' &&
      '  "boolean": "true",\n' && " changed type
      '  "true": true,\n' &&    " insert
*      '  "false": false,\n' &&    " delete
      '  "null": null,\n' &&
      '  "date": "2020-03-15",\n' &&
      '  "issues": [\n' &&
      '    {\n' &&
      '      "message": "Indentation problem ...",\n' &&
      '      "key": "indentation",\n' &&
      '      "start": {\n' &&
      '        "row": 5,\n' &&  " array change
      '        "col": 3\n' &&
      '      },\n' &&
      '      "end": {\n' &&
      '        "new": 1,\n' &&  " array insert
*      '        "row": 4,\n' && " array delete
      '        "col": 26\n' &&
      '      },\n' &&
      '      "filename": "./zxxx.prog.abap"\n' &&
      '    },\n' &&
      '    {\n' &&
      '      "message": "Remove space before XXX",\n' &&
      '      "key": "space_before_dot",\n' &&
      '      "start": {\n' &&
      '        "row": 3,\n' &&
      '        "col": 21\n' &&
      '      },\n' &&
      '      "end": {\n' &&
      '        "row": 3,\n' &&
      '        "col": 22\n' &&
      '      },\n' &&
      '      "filename": "./zxxx.prog.abap"\n' &&
      '    }\n' &&
      '  ]\n' &&
      '}'.

    REPLACE ALL OCCURRENCES OF '\n' IN lv_json WITH cl_abap_char_utilities=>newline.

    CREATE OBJECT lo_insert_exp.
    lo_insert_exp->add( '                |        |object |        |0|3' ).
    lo_insert_exp->add( '/               |boolean |str    |true    |0|0' ). " changed type (insert new)
    lo_insert_exp->add( '/               |issues  |array  |        |0|1' ).
    lo_insert_exp->add( '/               |true    |bool   |true    |0|0' ). " insert
    lo_insert_exp->add( '/issues/        |1       |object |        |1|1' ).
    lo_insert_exp->add( '/issues/1/      |end     |object |        |0|1' ).
    lo_insert_exp->add( '/issues/1/end/  |new     |num    |1       |0|0' ). " array insert

    CREATE OBJECT lo_delete_exp.
    lo_delete_exp->add( '                |        |object |        |0|3' ).
    lo_delete_exp->add( '/               |boolean |bool   |true    |0|0' ). " changed type (delete old)
    lo_delete_exp->add( '/               |false   |bool   |false   |0|0' ). " delete
    lo_delete_exp->add( '/               |issues  |array  |        |0|1' ).
    lo_delete_exp->add( '/issues/        |1       |object |        |1|1' ).
    lo_delete_exp->add( '/issues/1/      |end     |object |        |0|1' ).
    lo_delete_exp->add( '/issues/1/end/  |row     |num    |4       |0|0' ). " array delete

    CREATE OBJECT lo_change_exp.
    lo_change_exp->add( '                |        |object |        |0|2' ).
    lo_change_exp->add( '/               |issues  |array  |        |0|1' ).
    lo_change_exp->add( '/               |number  |num    |789     |0|0' ). " changed value
    lo_change_exp->add( '/issues/        |1       |object |        |1|1' ).
    lo_change_exp->add( '/issues/1/      |start   |object |        |0|1' ).
    lo_change_exp->add( '/issues/1/start/|row     |num    |5       |0|0' ). " array change

    CREATE OBJECT lo_util.

    lo_util->diff(
      EXPORTING
        iv_json_a = ltcl_parser_test=>sample_json( )
        iv_json_b = lv_json
      IMPORTING
        eo_insert = lo_insert
        eo_delete = lo_delete
        eo_change = lo_change ).

    cl_abap_unit_assert=>assert_equals(
      act = lo_insert->mt_json_tree
      exp = lo_insert_exp->mt_nodes ).

    cl_abap_unit_assert=>assert_equals(
      act = lo_delete->mt_json_tree
      exp = lo_delete_exp->mt_nodes ).

    cl_abap_unit_assert=>assert_equals(
      act = lo_change->mt_json_tree
      exp = lo_change_exp->mt_nodes ).

  ENDMETHOD.

  METHOD json_diff_types.

    DATA:
      lv_json_a     TYPE string,
      lv_json_b     TYPE string,
      lo_util       TYPE REF TO zcl_abapgit_ajson_utilities,
      lo_insert     TYPE REF TO zif_abapgit_ajson,
      lo_delete     TYPE REF TO zif_abapgit_ajson,
      lo_change     TYPE REF TO zif_abapgit_ajson,
      lo_insert_exp TYPE REF TO lcl_nodes_helper,
      lo_delete_exp TYPE REF TO lcl_nodes_helper.

    " Change single value to array
    lv_json_a =
      '{\n' &&
      '  "string": "abc",\n' &&
      '  "number": 123\n' &&
      '}'.

    lv_json_b =
      '{\n' &&
      '  "string": [\n' &&
      '    "a",\n' &&
      '    "b",\n' &&
      '    "c"\n' &&
      '  ],\n' &&
      '  "number": 123\n' &&
      '}'.

    REPLACE ALL OCCURRENCES OF '\n' IN lv_json_a WITH cl_abap_char_utilities=>newline.
    REPLACE ALL OCCURRENCES OF '\n' IN lv_json_b WITH cl_abap_char_utilities=>newline.

    CREATE OBJECT lo_insert_exp.
    lo_insert_exp->add( '                |        |object |        |0|1' ).
    lo_insert_exp->add( '/               |string  |array  |        |0|3' ).
    lo_insert_exp->add( '/string/        |1       |str    |a       |1|0' ).
    lo_insert_exp->add( '/string/        |2       |str    |b       |2|0' ).
    lo_insert_exp->add( '/string/        |3       |str    |c       |3|0' ).

    CREATE OBJECT lo_delete_exp.
    lo_delete_exp->add( '                |        |object |        |0|1' ).
    lo_delete_exp->add( '/               |string  |str    |abc     |0|0' ).

    CREATE OBJECT lo_util.

    lo_util->diff(
      EXPORTING
        iv_json_a = lv_json_a
        iv_json_b = lv_json_b
      IMPORTING
        eo_insert = lo_insert
        eo_delete = lo_delete
        eo_change = lo_change ).

    cl_abap_unit_assert=>assert_equals(
      act = lo_insert->mt_json_tree
      exp = lo_insert_exp->mt_nodes ).

    cl_abap_unit_assert=>assert_equals(
      act = lo_delete->mt_json_tree
      exp = lo_delete_exp->mt_nodes ).

    cl_abap_unit_assert=>assert_equals(
      act = lines( lo_change->mt_json_tree )
      exp = 0 ).

    " Change array to single value
    lo_util->diff(
      EXPORTING
        iv_json_a = lv_json_b
        iv_json_b = lv_json_a
      IMPORTING
        eo_insert = lo_insert
        eo_delete = lo_delete
        eo_change = lo_change ).

    cl_abap_unit_assert=>assert_equals(
      act = lo_insert->mt_json_tree
      exp = lo_delete_exp->mt_nodes ).

    cl_abap_unit_assert=>assert_equals(
      act = lo_delete->mt_json_tree
      exp = lo_insert_exp->mt_nodes ).

    cl_abap_unit_assert=>assert_equals(
      act = lines( lo_change->mt_json_tree )
      exp = 0 ).

  ENDMETHOD.

  METHOD json_sort.

    DATA:
      lv_json       TYPE string,
      lo_util       TYPE REF TO zcl_abapgit_ajson_utilities,
      lv_sorted     TYPE string,
      lv_sorted_exp TYPE string.

    lv_json =
      '{\n' &&
      '  "string": "abc",\n' &&
      '  "number": 789,\n' &&
      '  "float": 123.45,\n' &&
      '  "boolean": "true",\n' &&
      '  "true": true,\n' &&
      '  "false": false,\n' &&
      '  "null": null,\n' &&
      '  "date": "2020-03-15"\n' &&
      '}'.

    REPLACE ALL OCCURRENCES OF '\n' IN lv_json WITH cl_abap_char_utilities=>newline.

    lv_sorted_exp =
      '{\n' &&
      '  "boolean": "true",\n' &&
      '  "date": "2020-03-15",\n' &&
      '  "false": false,\n' &&
      '  "float": 123.45,\n' &&
      '  "null": null,\n' &&
      '  "number": 789,\n' &&
      '  "string": "abc",\n' &&
      '  "true": true\n' &&
      '}'.

    REPLACE ALL OCCURRENCES OF '\n' IN lv_sorted_exp WITH cl_abap_char_utilities=>newline.

    CREATE OBJECT lo_util.

    lv_sorted = lo_util->sort( iv_json = lv_json ).

    cl_abap_unit_assert=>assert_equals(
      act = lv_sorted
      exp = lv_sorted_exp ).

  ENDMETHOD.

ENDCLASS.


INTERFACE zif_abapgit_repo_checksums
  PUBLIC.

  METHODS get
    RETURNING
      VALUE(rt_checksums) TYPE zif_abapgit_persistence=>ty_local_checksum_tt.

  METHODS get_checksums_per_file
    RETURNING
      VALUE(rt_checksums) TYPE zif_abapgit_definitions=>ty_file_signatures_tt .

  METHODS rebuild
    IMPORTING
      iv_branches_equal TYPE abap_bool DEFAULT abap_false
    RAISING
      zcx_abapgit_exception.

  METHODS update
    IMPORTING
      !it_updated_files TYPE zif_abapgit_definitions=>ty_file_signatures_tt
    RAISING
      zcx_abapgit_exception.

ENDINTERFACE.