CLASS zcl_abapgit_json_handler DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    TYPES:
      BEGIN OF ty_json_abap_mapping,
        json TYPE string,
        abap TYPE string,
      END OF ty_json_abap_mapping,
      ty_json_abap_mappings TYPE STANDARD TABLE OF ty_json_abap_mapping WITH DEFAULT KEY,
      BEGIN OF ty_enum_mapping,
        path     TYPE string,
        mappings TYPE ty_json_abap_mappings,
      END OF ty_enum_mapping,
      ty_enum_mappings TYPE TABLE OF ty_enum_mapping WITH DEFAULT KEY.

    TYPES:
      BEGIN OF ty_path_value_pair,
        path  TYPE string,
        value TYPE string,
      END OF ty_path_value_pair,
      ty_skip_paths TYPE STANDARD TABLE OF ty_path_value_pair WITH KEY path.

    "! Serializes data to xstring. Type of data is specified in the
    "! implementing class.
    "!
    "! @parameter iv_data | data to be serialized
    "! @parameter iv_enum_mappings | ABAP/JSON value mappings
    "! @parameter iv_skip_paths | path/value pairs to be skipped during serialization
    "! @parameter rv_result | serialized data
    METHODS serialize
      IMPORTING iv_data          TYPE data
                iv_enum_mappings TYPE ty_enum_mappings
                iv_skip_paths    TYPE ty_skip_paths
      RETURNING VALUE(rv_result) TYPE xstring
      RAISING   cx_static_check.

    "! Deserializes xstring into data. The type of data is specified in
    "! the implementing class
    "!
    "! @parameter iv_content | xstring to be deserialized
    "! @parameter iv_defaults | path-value pairs that apply if value is initial
    "! @parameter ev_data | data of the xstring
    METHODS deserialize
      IMPORTING iv_content       TYPE xstring
                iv_defaults      TYPE ty_skip_paths
                iv_enum_mappings TYPE ty_enum_mappings
      EXPORTING ev_data          TYPE data
      RAISING   cx_static_check.

  PROTECTED SECTION.

  PRIVATE SECTION.

    METHODS:
      map2json_original_language
        CHANGING co_ajson TYPE REF TO zcl_abapgit_ajson
        RAISING  zcx_abapgit_ajson_error,
      map2json_custom_enum
        IMPORTING it_enum_mappings TYPE ty_enum_mappings
        CHANGING  co_ajson         TYPE REF TO zcl_abapgit_ajson
        RAISING   zcx_abapgit_ajson_error,
      map2json_abap_language_version
        CHANGING co_ajson TYPE REF TO zcl_abapgit_ajson
        RAISING  zcx_abapgit_ajson_error,
      "! Get the enum mapping from object handler, as other enums as well
      map2abap_abap_language_version
        CHANGING co_ajson TYPE REF TO zcl_abapgit_ajson
        RAISING  zcx_abapgit_ajson_error,
      "! For deserialization
      map2abap_original_language
        CHANGING co_ajson TYPE REF TO zcl_abapgit_ajson
        RAISING  zcx_abapgit_ajson_error,
      "! For deserialization
      set_defaults
        IMPORTING it_defaults TYPE ty_skip_paths
        CHANGING  co_ajson    TYPE REF TO zcl_abapgit_ajson
        RAISING   zcx_abapgit_ajson_error,
      map2abap_custom_enum
        IMPORTING it_enum_mappings TYPE ty_enum_mappings
        CHANGING  co_ajson         TYPE REF TO zcl_abapgit_ajson
        RAISING   zcx_abapgit_ajson_error.

ENDCLASS.

CLASS zcl_abapgit_json_handler IMPLEMENTATION.

  METHOD deserialize.
    DATA lv_json    TYPE string.
    DATA lo_ajson   TYPE REF TO zcl_abapgit_ajson.
    DATA lo_mapping TYPE REF TO zif_abapgit_ajson_mapping.

    CLEAR ev_data.

    lv_json = zcl_abapgit_convert=>xstring_to_string_utf8( iv_content ).
    lo_mapping = zcl_abapgit_ajson_mapping=>create_camel_case( ).

    lo_ajson = zcl_abapgit_ajson=>parse( iv_json           = lv_json
                                         ii_custom_mapping = lo_mapping ).

    map2abap_original_language( CHANGING co_ajson = lo_ajson ).
    set_defaults( EXPORTING it_defaults = iv_defaults
                  CHANGING  co_ajson    = lo_ajson ).
    map2abap_abap_language_version( CHANGING co_ajson = lo_ajson ).
    map2abap_custom_enum( EXPORTING it_enum_mappings = iv_enum_mappings
                          CHANGING co_ajson          = lo_ajson  ).

    lo_ajson->zif_abapgit_ajson~to_abap( IMPORTING ev_container = ev_data ).

  ENDMETHOD.

  METHOD serialize.
    DATA: lt_st_source      TYPE abap_trans_srcbind_tab,
          lo_mapping        TYPE REF TO zif_abapgit_ajson_mapping,
          lv_json           TYPE string,
          lo_ajson          TYPE REF TO zcl_abapgit_ajson,
          lo_ajson_filtered TYPE REF TO zif_abapgit_ajson,
          lv_enum_abap      TYPE string,
          lo_filter         TYPE REF TO lcl_aff_filter.

    FIELD-SYMBOLS: <lg_source> LIKE LINE OF lt_st_source.

    APPEND INITIAL LINE TO lt_st_source ASSIGNING <lg_source>.
    GET REFERENCE OF iv_data INTO <lg_source>-value.

    lo_mapping = zcl_abapgit_ajson_mapping=>create_camel_case( iv_first_json_upper = abap_false ).

    lo_ajson = zcl_abapgit_ajson=>create_empty( lo_mapping ).

    lo_ajson->keep_item_order( ).
    lo_ajson->set(
      iv_path = '/'
      iv_val  = iv_data ).

    map2json_original_language( CHANGING co_ajson = lo_ajson ).
    map2json_abap_language_version( CHANGING co_ajson = lo_ajson ).
    map2json_custom_enum( EXPORTING it_enum_mappings = iv_enum_mappings
                          CHANGING co_ajson          = lo_ajson ).

    CREATE OBJECT lo_filter EXPORTING iv_skip_paths = iv_skip_paths.
    lo_ajson_filtered = zcl_abapgit_ajson=>create_from(
                          ii_source_json = lo_ajson
                          ii_filter      = lo_filter ).

    lo_ajson_filtered->keep_item_order( ).

    lv_json = lo_ajson_filtered->stringify( 2 ).

    " files end with an empty line (EOF)
    lv_json = lv_json && cl_abap_char_utilities=>newline.

    rv_result = zcl_abapgit_convert=>string_to_xstring_utf8( lv_json ).
  ENDMETHOD.

  METHOD map2json_original_language.
    DATA:
      lv_iso_language      TYPE i18_a_langiso2,
      lv_original_language TYPE sy-langu.

    lv_original_language = co_ajson->get_string( '/header/originalLanguage' ).

    cl_i18n_languages=>sap1_to_iso639_1(
      EXPORTING
        im_lang_sap1   = lv_original_language
      IMPORTING
        ex_lang_iso639 = lv_iso_language
      EXCEPTIONS
        OTHERS         = 1 ).

    IF sy-subrc = 0.
      co_ajson->set_string( iv_path = '/header/originalLanguage'
                            iv_val  = lv_iso_language ).
    ENDIF.
  ENDMETHOD.

  METHOD map2json_abap_language_version.
    DATA:
      lv_enum_abap TYPE string,
      lv_enum_json TYPE string.

    lv_enum_abap = co_ajson->get_string( '/header/abapLanguageVersion' ).
    IF lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version_src-standard
      OR lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version-standard.
      lv_enum_json = 'standard'.
    ELSEIF lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version-cloud_development.
      lv_enum_json = 'cloudDevelopment'.
    ELSEIF lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version-key_user.
      lv_enum_json = 'keyUser'.
    ENDIF.

    co_ajson->set_string( iv_path = '/header/abapLanguageVersion'
                          iv_val  = lv_enum_json ).
  ENDMETHOD.

  METHOD map2abap_abap_language_version.
    DATA:
      lv_enum_abap TYPE string,
      lv_enum_json TYPE string.

    lv_enum_json = co_ajson->get_string( '/header/abapLanguageVersion' ).
    IF lv_enum_json = 'standard'.
      lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version_src-standard.
    ELSEIF lv_enum_json = 'cloudDevelopment'.
      lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version-cloud_development.
    ELSEIF lv_enum_json = 'keyUser'.
      lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version-key_user.
    ENDIF.

    co_ajson->set_string( iv_path = '/header/abapLanguageVersion'
                          iv_val  = lv_enum_abap ).
  ENDMETHOD.

  METHOD map2json_custom_enum.
    DATA:
      lv_enum_abap    TYPE string,
      ls_enum_mapping TYPE ty_enum_mapping,
      ls_mapping      TYPE ty_json_abap_mapping.

    LOOP AT it_enum_mappings INTO ls_enum_mapping.
      lv_enum_abap = co_ajson->get_string( ls_enum_mapping-path ).
      READ TABLE ls_enum_mapping-mappings WITH KEY abap = lv_enum_abap INTO ls_mapping.
      IF sy-subrc = 0.
        co_ajson->set_string( iv_path = ls_enum_mapping-path
                              iv_val  = ls_mapping-json ).
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

  METHOD map2abap_custom_enum.
    DATA:
      lv_enum_json    TYPE string,
      ls_enum_mapping TYPE ty_enum_mapping,
      ls_mapping      TYPE ty_json_abap_mapping.

    LOOP AT it_enum_mappings INTO ls_enum_mapping.
      lv_enum_json = co_ajson->get_string( ls_enum_mapping-path ).
      READ TABLE ls_enum_mapping-mappings WITH KEY json = lv_enum_json INTO ls_mapping.
      IF sy-subrc = 0.
        co_ajson->set_string( iv_path = ls_enum_mapping-path
                              iv_val  = ls_mapping-abap ).
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

  METHOD set_defaults.
    DATA:
      lv_enum_json TYPE string,
      ls_default   TYPE ty_path_value_pair.

    LOOP AT it_defaults INTO ls_default.
      lv_enum_json = co_ajson->get_string( ls_default-path ).
      IF lv_enum_json = ``.
        co_ajson->set_string( iv_path = ls_default-path
                              iv_val  = ls_default-value ).
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

  METHOD map2abap_original_language.
    DATA:
      lv_iso_language      TYPE i18_a_langiso2,
      lv_original_language TYPE sy-langu.

    lv_iso_language = co_ajson->get_string( '/header/originalLanguage' ).

    cl_i18n_languages=>locale_to_sap1(
      EXPORTING
        iv_localename        = lv_iso_language
        iv_consider_inactive = abap_true
      IMPORTING
        ev_lang_sap1         = lv_original_language
      EXCEPTIONS
        no_assignment        = 1
        OTHERS               = 2 ).
    IF sy-subrc <> 0.
      FIND REGEX `[A-Z]{2}` IN lv_iso_language.
      IF sy-subrc = 0.
        "Fallback try to convert from SAP language
        cl_i18n_languages=>sap2_to_sap1(
          EXPORTING
            im_lang_sap2      = lv_iso_language
          RECEIVING
            re_lang_sap1      = lv_original_language
          EXCEPTIONS
            no_assignment     = 1
            no_representation = 2
            OTHERS            = 3 ).
        IF sy-subrc <> 0.
          RAISE EXCEPTION TYPE zcx_abapgit_ajson_error.
        ENDIF.
      ELSE.
        RAISE EXCEPTION TYPE zcx_abapgit_ajson_error.
      ENDIF.
    ENDIF.

    co_ajson->set_string( iv_path = '/header/originalLanguage'
                          iv_val  = lv_original_language ).
  ENDMETHOD.

ENDCLASS.

CLASS zcl_abapgit_json_handler DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    TYPES:
      BEGIN OF ty_json_abap_mapping,
        json TYPE string,
        abap TYPE string,
      END OF ty_json_abap_mapping,
      ty_json_abap_mappings TYPE STANDARD TABLE OF ty_json_abap_mapping WITH DEFAULT KEY,
      BEGIN OF ty_enum_mapping,
        path     TYPE string,
        mappings TYPE ty_json_abap_mappings,
      END OF ty_enum_mapping,
      ty_enum_mappings TYPE TABLE OF ty_enum_mapping WITH DEFAULT KEY.

    TYPES:
      BEGIN OF ty_path_value_pair,
        path  TYPE string,
        value TYPE string,
      END OF ty_path_value_pair,
      ty_skip_paths TYPE STANDARD TABLE OF ty_path_value_pair WITH KEY path.

    "! Serializes data to xstring. Type of data is specified in the
    "! implementing class.
    "!
    "! @parameter iv_data | data to be serialized
    "! @parameter iv_enum_mappings | ABAP/JSON value mappings
    "! @parameter iv_skip_paths | path/value pairs to be skipped during serialization
    "! @parameter rv_result | serialized data
    METHODS serialize
      IMPORTING iv_data          TYPE data
                iv_enum_mappings TYPE ty_enum_mappings
                iv_skip_paths    TYPE ty_skip_paths
      RETURNING VALUE(rv_result) TYPE xstring
      RAISING   cx_static_check.

    "! Deserializes xstring into data. The type of data is specified in
    "! the implementing class
    "!
    "! @parameter iv_content | xstring to be deserialized
    "! @parameter iv_defaults | path-value pairs that apply if value is initial
    "! @parameter ev_data | data of the xstring
    METHODS deserialize
      IMPORTING iv_content       TYPE xstring
                iv_defaults      TYPE ty_skip_paths
                iv_enum_mappings TYPE ty_enum_mappings
      EXPORTING ev_data          TYPE data
      RAISING   cx_static_check.

  PROTECTED SECTION.

  PRIVATE SECTION.

    METHODS:
      map2json_original_language
        CHANGING co_ajson TYPE REF TO zcl_abapgit_ajson
        RAISING  zcx_abapgit_ajson_error,
      map2json_custom_enum
        IMPORTING it_enum_mappings TYPE ty_enum_mappings
        CHANGING  co_ajson         TYPE REF TO zcl_abapgit_ajson
        RAISING   zcx_abapgit_ajson_error,
      map2json_abap_language_version
        CHANGING co_ajson TYPE REF TO zcl_abapgit_ajson
        RAISING  zcx_abapgit_ajson_error,
      "! Get the enum mapping from object handler, as other enums as well
      map2abap_abap_language_version
        CHANGING co_ajson TYPE REF TO zcl_abapgit_ajson
        RAISING  zcx_abapgit_ajson_error,
      "! For deserialization
      map2abap_original_language
        CHANGING co_ajson TYPE REF TO zcl_abapgit_ajson
        RAISING  zcx_abapgit_ajson_error,
      "! For deserialization
      set_defaults
        IMPORTING it_defaults TYPE ty_skip_paths
        CHANGING  co_ajson    TYPE REF TO zcl_abapgit_ajson
        RAISING   zcx_abapgit_ajson_error,
      map2abap_custom_enum
        IMPORTING it_enum_mappings TYPE ty_enum_mappings
        CHANGING  co_ajson         TYPE REF TO zcl_abapgit_ajson
        RAISING   zcx_abapgit_ajson_error.

ENDCLASS.

CLASS zcl_abapgit_json_handler IMPLEMENTATION.

  METHOD deserialize.
    DATA lv_json    TYPE string.
    DATA lo_ajson   TYPE REF TO zcl_abapgit_ajson.
    DATA lo_mapping TYPE REF TO zif_abapgit_ajson_mapping.

    CLEAR ev_data.

    lv_json = zcl_abapgit_convert=>xstring_to_string_utf8( iv_content ).
    lo_mapping = zcl_abapgit_ajson_mapping=>create_camel_case( ).

    lo_ajson = zcl_abapgit_ajson=>parse( iv_json           = lv_json
                                         ii_custom_mapping = lo_mapping ).

    map2abap_original_language( CHANGING co_ajson = lo_ajson ).
    set_defaults( EXPORTING it_defaults = iv_defaults
                  CHANGING  co_ajson    = lo_ajson ).
    map2abap_abap_language_version( CHANGING co_ajson = lo_ajson ).
    map2abap_custom_enum( EXPORTING it_enum_mappings = iv_enum_mappings
                          CHANGING co_ajson          = lo_ajson  ).

    lo_ajson->zif_abapgit_ajson~to_abap( IMPORTING ev_container = ev_data ).

  ENDMETHOD.

  METHOD serialize.
    DATA: lt_st_source      TYPE abap_trans_srcbind_tab,
          lo_mapping        TYPE REF TO zif_abapgit_ajson_mapping,
          lv_json           TYPE string,
          lo_ajson          TYPE REF TO zcl_abapgit_ajson,
          lo_ajson_filtered TYPE REF TO zif_abapgit_ajson,
          lv_enum_abap      TYPE string,
          lo_filter         TYPE REF TO lcl_aff_filter.

    FIELD-SYMBOLS: <lg_source> LIKE LINE OF lt_st_source.

    APPEND INITIAL LINE TO lt_st_source ASSIGNING <lg_source>.
    GET REFERENCE OF iv_data INTO <lg_source>-value.

    lo_mapping = zcl_abapgit_ajson_mapping=>create_camel_case( iv_first_json_upper = abap_false ).

    lo_ajson = zcl_abapgit_ajson=>create_empty( lo_mapping ).

    lo_ajson->keep_item_order( ).
    lo_ajson->set(
      iv_path = '/'
      iv_val  = iv_data ).

    map2json_original_language( CHANGING co_ajson = lo_ajson ).
    map2json_abap_language_version( CHANGING co_ajson = lo_ajson ).
    map2json_custom_enum( EXPORTING it_enum_mappings = iv_enum_mappings
                          CHANGING co_ajson          = lo_ajson ).

    CREATE OBJECT lo_filter EXPORTING iv_skip_paths = iv_skip_paths.
    lo_ajson_filtered = zcl_abapgit_ajson=>create_from(
                          ii_source_json = lo_ajson
                          ii_filter      = lo_filter ).

    lo_ajson_filtered->keep_item_order( ).

    lv_json = lo_ajson_filtered->stringify( 2 ).

    " files end with an empty line (EOF)
    lv_json = lv_json && cl_abap_char_utilities=>newline.

    rv_result = zcl_abapgit_convert=>string_to_xstring_utf8( lv_json ).
  ENDMETHOD.

  METHOD map2json_original_language.
    DATA:
      lv_iso_language      TYPE i18_a_langiso2,
      lv_original_language TYPE sy-langu.

    lv_original_language = co_ajson->get_string( '/header/originalLanguage' ).

    CALL FUNCTION 'CONVERSION_EXIT_ISOLA_OUTPUT'
      EXPORTING
        input  = lv_original_language
      IMPORTING
        output = lv_iso_language.

    TRANSLATE lv_iso_language TO LOWER CASE.
    co_ajson->set_string( iv_path = '/header/originalLanguage'
                          iv_val  = lv_iso_language ).
  ENDMETHOD.

  METHOD map2json_abap_language_version.
    DATA:
      lv_enum_abap TYPE string,
      lv_enum_json TYPE string.

    lv_enum_abap = co_ajson->get_string( '/header/abapLanguageVersion' ).
    IF lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version_src-standard
      OR lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version-standard.
      lv_enum_json = 'standard'.
    ELSEIF lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version-cloud_development.
      lv_enum_json = 'cloudDevelopment'.
    ELSEIF lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version-key_user.
      lv_enum_json = 'keyUser'.
    ENDIF.

    co_ajson->set_string( iv_path = '/header/abapLanguageVersion'
                          iv_val  = lv_enum_json ).
  ENDMETHOD.

  METHOD map2abap_abap_language_version.
    DATA:
      lv_enum_abap TYPE string,
      lv_enum_json TYPE string.

    lv_enum_json = co_ajson->get_string( '/header/abapLanguageVersion' ).
    IF lv_enum_json = 'standard'.
      lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version_src-standard.
    ELSEIF lv_enum_json = 'cloudDevelopment'.
      lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version-cloud_development.
    ELSEIF lv_enum_json = 'keyUser'.
      lv_enum_abap = zif_abapgit_aff_types_v1=>co_abap_language_version-key_user.
    ENDIF.

    co_ajson->set_string( iv_path = '/header/abapLanguageVersion'
                          iv_val  = lv_enum_abap ).
  ENDMETHOD.

  METHOD map2json_custom_enum.
    DATA:
      lv_enum_abap    TYPE string,
      ls_enum_mapping TYPE ty_enum_mapping,
      ls_mapping      TYPE ty_json_abap_mapping.

    LOOP AT it_enum_mappings INTO ls_enum_mapping.
      lv_enum_abap = co_ajson->get_string( ls_enum_mapping-path ).
      READ TABLE ls_enum_mapping-mappings WITH KEY abap = lv_enum_abap INTO ls_mapping.
      IF sy-subrc = 0.
        co_ajson->set_string( iv_path = ls_enum_mapping-path
                              iv_val  = ls_mapping-json ).
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

  METHOD map2abap_custom_enum.
    DATA:
      lv_enum_json    TYPE string,
      ls_enum_mapping TYPE ty_enum_mapping,
      ls_mapping      TYPE ty_json_abap_mapping.

    LOOP AT it_enum_mappings INTO ls_enum_mapping.
      lv_enum_json = co_ajson->get_string( ls_enum_mapping-path ).
      READ TABLE ls_enum_mapping-mappings WITH KEY json = lv_enum_json INTO ls_mapping.
      IF sy-subrc = 0.
        co_ajson->set_string( iv_path = ls_enum_mapping-path
                              iv_val  = ls_mapping-abap ).
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

  METHOD set_defaults.
    DATA:
      lv_enum_json TYPE string,
      ls_default   TYPE ty_path_value_pair.

    LOOP AT it_defaults INTO ls_default.
      lv_enum_json = co_ajson->get_string( ls_default-path ).
      IF lv_enum_json = ``.
        co_ajson->set_string( iv_path = ls_default-path
                              iv_val  = ls_default-value ).
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

  METHOD map2abap_original_language.
    DATA:
      lv_iso_language      TYPE i18_a_langiso2,
      lv_original_language TYPE sy-langu.

    lv_iso_language = co_ajson->get_string( '/header/originalLanguage' ).

    CALL FUNCTION 'CONVERSION_EXIT_ISOLA_INPUT'
      EXPORTING
        input  = lv_iso_language
      IMPORTING
        output = lv_original_language.

    co_ajson->set_string( iv_path = '/header/originalLanguage'
                          iv_val  = lv_original_language ).
  ENDMETHOD.

ENDCLASS.

Fix language conversion, now 7.02 compatible (#5727)

solves #5726


*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_PAGE_STAGE
*&---------------------------------------------------------------------*

CLASS lcl_gui_page_stage DEFINITION FINAL INHERITING FROM lcl_gui_page.

  PUBLIC SECTION.
    CONSTANTS: BEGIN OF c_action,
                 stage_all    TYPE string VALUE 'stage_all',
                 stage_commit TYPE string VALUE 'stage_commit',
               END OF c_action.

    METHODS:
      constructor
        IMPORTING io_repo TYPE REF TO lcl_repo_online
        RAISING   lcx_exception,
      lif_gui_page~on_event REDEFINITION.

  PROTECTED SECTION.
    METHODS:
      render_content REDEFINITION,
      scripts        REDEFINITION.

  PRIVATE SECTION.
    DATA: mo_repo  TYPE REF TO lcl_repo_online,
          ms_files TYPE ty_stage_files,
          mo_stage TYPE REF TO lcl_stage,
          mv_ts    TYPE timestamp.

    METHODS:
      render_list
        RETURNING VALUE(ro_html) TYPE REF TO lcl_html,
      render_file
        IMPORTING is_file        TYPE ty_file
                  iv_context     TYPE string
        RETURNING VALUE(ro_html) TYPE REF TO lcl_html,
      render_menu
        RETURNING VALUE(ro_html) TYPE REF TO lcl_html.

    METHODS process_stage_list
      IMPORTING it_postdata TYPE cnht_post_data_tab
      RAISING   lcx_exception.

ENDCLASS.

CLASS lcl_gui_page_stage IMPLEMENTATION.

  METHOD constructor.

    super->constructor( ).
    ms_control-page_title = 'STAGE'.
    mo_repo = io_repo.

    ms_files = lcl_stage_logic=>get( mo_repo ).

    CREATE OBJECT mo_stage
      EXPORTING
        iv_branch_name = io_repo->get_branch_name( )
        iv_branch_sha1 = io_repo->get_sha1_remote( ).

    GET TIME STAMP FIELD mv_ts.

  ENDMETHOD.

  METHOD lif_gui_page~on_event.

    FIELD-SYMBOLS: <ls_file> LIKE LINE OF ms_files-local.

    CASE iv_action.
      WHEN c_action-stage_all.
        mo_stage->reset_all( ).
        LOOP AT ms_files-local ASSIGNING <ls_file>.
          mo_stage->add( iv_path     = <ls_file>-file-path
                         iv_filename = <ls_file>-file-filename
                         iv_data     = <ls_file>-file-data ).
        ENDLOOP.
      WHEN c_action-stage_commit.
        mo_stage->reset_all( ).
        process_stage_list( it_postdata ).
      WHEN OTHERS.
        RETURN.
    ENDCASE.

    CREATE OBJECT ei_page TYPE lcl_gui_page_commit
      EXPORTING
        io_repo  = mo_repo
        io_stage = mo_stage.

    ev_state = gc_event_state-new_page.

  ENDMETHOD.

  METHOD process_stage_list.

    DATA: lv_string TYPE string,
          lt_fields TYPE tihttpnvp,
          ls_file   TYPE ty_file.

    FIELD-SYMBOLS: <ls_file> LIKE LINE OF ms_files-local,
                   <ls_item> LIKE LINE OF lt_fields.

    CONCATENATE LINES OF it_postdata INTO lv_string.
    lt_fields = cl_http_utility=>if_http_utility~string_to_fields( |{ lv_string }| ).

    IF lines( lt_fields ) = 0.
      lcx_exception=>raise( 'process_stage_list: empty list' ).
    ENDIF.

    LOOP AT lt_fields ASSIGNING <ls_item>.

      lcl_path=>split_file_location( EXPORTING iv_fullpath = <ls_item>-name
                                     IMPORTING ev_path     = ls_file-path
                                               ev_filename = ls_file-filename ).

      CASE <ls_item>-value.
        WHEN lcl_stage=>c_method-add.
          READ TABLE ms_files-local ASSIGNING <ls_file>
            WITH KEY file-path     = ls_file-path
                     file-filename = ls_file-filename.
          ASSERT sy-subrc = 0.
          mo_stage->add(    iv_path     = <ls_file>-file-path
                            iv_filename = <ls_file>-file-filename
                            iv_data     = <ls_file>-file-data ).
        WHEN lcl_stage=>c_method-ignore.
          mo_stage->ignore( iv_path     = ls_file-path
                            iv_filename = ls_file-filename ).
        WHEN lcl_stage=>c_method-rm.
          mo_stage->rm(     iv_path     = ls_file-path
                            iv_filename = ls_file-filename ).
        WHEN lcl_stage=>c_method-skip.
          " Do nothing
        WHEN OTHERS.
          lcx_exception=>raise( |process_stage_list: unknown method { <ls_item>-value }| ).
      ENDCASE.
    ENDLOOP.

  ENDMETHOD.        "process_stage_list

  METHOD render_list.

    FIELD-SYMBOLS: <ls_remote> LIKE LINE OF ms_files-remote,
                   <ls_local>  LIKE LINE OF ms_files-local.

    CREATE OBJECT ro_html.

    ro_html->add( '
' ).

    " Local changes
    LOOP AT ms_files-local ASSIGNING <ls_local>.
      AT FIRST.
        ro_html->add('

').
        ro_html->add('	LOCAL	' ).
        ro_html->add('' ).
        IF lines( ms_files-local ) > 1.
          ro_html->add_a( iv_txt = |{ lines( ms_files-local ) } diffs|
                          iv_act = |{ gc_action-go_diff }?key={ mo_repo->get_key( ) }| ).
        ENDIF.
        ro_html->add('	').
        ro_html->add('

').
        ro_html->add('
').
      ENDAT.

      ro_html->add( render_file( is_file = <ls_local>-file iv_context = 'local' ) ).

      AT LAST.
        ro_html->add('
').
      ENDAT.
    ENDLOOP.

    " Remote changes
    LOOP AT ms_files-remote ASSIGNING <ls_remote>.
      AT FIRST.
        ro_html->add('

').
        ro_html->add('	REMOTE	' ).
        ro_html->add('

').
        ro_html->add('
').
      ENDAT.

      ro_html->add( render_file( is_file = <ls_remote> iv_context = 'remote' ) ).

      AT LAST.
        ro_html->add('
').
      ENDAT.
    ENDLOOP.

    ro_html->add( '
' ).

  ENDMETHOD.      "render_lines

  METHOD render_file.

    DATA lv_param TYPE string.

    CREATE OBJECT ro_html.

    ro_html->add( |
| ).
    ro_html->add( |?	| ).
    ro_html->add( |{ is_file-path && is_file-filename }	| ).

    CASE iv_context.
      WHEN 'local'.
        lv_param = lcl_html_action_utils=>file_encode( iv_key  = mo_repo->get_key( )
                                                       ig_file = is_file ).
        ro_html->add( 'add	' ).
        ro_html->add( '' ).
        ro_html->add_a( iv_txt = 'diff' iv_act = |{ gc_action-go_diff }?{ lv_param }| ).
        ro_html->add( '	' ).
      WHEN 'remote'.
        ro_html->add( 'ignoreremove	' ).
        ro_html->add( |-	| ).
    ENDCASE.

    ro_html->add( '
' ).

  ENDMETHOD.  "render_file

  METHOD render_content.

    CREATE OBJECT ro_html.

    ro_html->add( '
' ).
    ro_html->add( lcl_gui_chunk_lib=>render_repo_top( mo_repo ) ).
    ro_html->add( render_menu( ) ).
    ro_html->add( render_list( ) ).
    ro_html->add( '
' ).

  ENDMETHOD.      "render_content

  METHOD render_menu.

    CREATE OBJECT ro_html.

    ro_html->add( '
' ).
    ro_html->add_a( iv_act   = 'gHelper.submit();'
                    iv_typ   = gc_action_type-onclick
                    iv_id    = 'act_commit'
                    iv_style = 'display: none'
                    iv_txt   = 'Commit'
                    iv_opt   = gc_html_opt-strong ) ##NO_TEXT.
    IF lines( ms_files-local ) > 0.
      ro_html->add_a( iv_act = |{ c_action-stage_all }|
                      iv_id  = 'act_commit_all'
                      iv_txt = 'Add all and commit') ##NO_TEXT.
    ENDIF.
    ro_html->add( '
' ).

  ENDMETHOD.      "render_menu

  METHOD scripts.

    CREATE OBJECT ro_html.

    ro_html->add( 'var gStageParams = {' ).
    ro_html->add( |  seed:            "stage{ mv_ts }",| ).
    ro_html->add( '  stageTabId:      "stage_tab",' ).
    ro_html->add( '  formAction:      "stage_commit",' ).
    ro_html->add( '  commitNodeId:    "act_commit",' ).
    ro_html->add( '  commitAllNodeId: "act_commit_all"' ).
    ro_html->add( '}' ).
    ro_html->add( 'var gHelper = new StageHelper(gStageParams);' ).

  ENDMETHOD.  "scripts

ENDCLASS.

*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_PAGE_STAGE
*&---------------------------------------------------------------------*

CLASS lcl_gui_page_stage DEFINITION FINAL INHERITING FROM lcl_gui_page.

  PUBLIC SECTION.
    CONSTANTS: BEGIN OF c_action,
                 stage_all    TYPE string VALUE 'stage_all',
                 stage_commit TYPE string VALUE 'stage_commit',
               END OF c_action.

    METHODS:
      constructor
        IMPORTING io_repo TYPE REF TO lcl_repo_online
        RAISING   lcx_exception,
      lif_gui_page~on_event REDEFINITION.

  PROTECTED SECTION.
    METHODS:
      render_content REDEFINITION,
      scripts        REDEFINITION.

  PRIVATE SECTION.
    DATA: mo_repo  TYPE REF TO lcl_repo_online,
          ms_files TYPE ty_stage_files,
          mo_stage TYPE REF TO lcl_stage,
          mv_ts    TYPE timestamp.

    METHODS:
      render_list
        RETURNING VALUE(ro_html) TYPE REF TO lcl_html,
      render_file
        IMPORTING is_file        TYPE ty_file
                  iv_context     TYPE string
        RETURNING VALUE(ro_html) TYPE REF TO lcl_html,
      render_menu
        RETURNING VALUE(ro_html) TYPE REF TO lcl_html,
      read_last_changed_by
        IMPORTING is_file        TYPE ty_file
        RETURNING VALUE(rv_user) TYPE xubname.

    METHODS process_stage_list
      IMPORTING it_postdata TYPE cnht_post_data_tab
      RAISING   lcx_exception.

ENDCLASS.

CLASS lcl_gui_page_stage IMPLEMENTATION.

  METHOD constructor.

    super->constructor( ).
    ms_control-page_title = 'STAGE'.
    mo_repo = io_repo.

    ms_files = lcl_stage_logic=>get( mo_repo ).

    CREATE OBJECT mo_stage
      EXPORTING
        iv_branch_name = io_repo->get_branch_name( )
        iv_branch_sha1 = io_repo->get_sha1_remote( ).

    GET TIME STAMP FIELD mv_ts.

  ENDMETHOD.

  METHOD lif_gui_page~on_event.

    FIELD-SYMBOLS: <ls_file> LIKE LINE OF ms_files-local.

    CASE iv_action.
      WHEN c_action-stage_all.
        mo_stage->reset_all( ).
        LOOP AT ms_files-local ASSIGNING <ls_file>.
          mo_stage->add( iv_path     = <ls_file>-file-path
                         iv_filename = <ls_file>-file-filename
                         iv_data     = <ls_file>-file-data ).
        ENDLOOP.
      WHEN c_action-stage_commit.
        mo_stage->reset_all( ).
        process_stage_list( it_postdata ).
      WHEN OTHERS.
        RETURN.
    ENDCASE.

    CREATE OBJECT ei_page TYPE lcl_gui_page_commit
      EXPORTING
        io_repo  = mo_repo
        io_stage = mo_stage.

    ev_state = gc_event_state-new_page.

  ENDMETHOD.

  METHOD process_stage_list.

    DATA: lv_string TYPE string,
          lt_fields TYPE tihttpnvp,
          ls_file   TYPE ty_file.

    FIELD-SYMBOLS: <ls_file> LIKE LINE OF ms_files-local,
                   <ls_item> LIKE LINE OF lt_fields.

    CONCATENATE LINES OF it_postdata INTO lv_string.
    lt_fields = cl_http_utility=>if_http_utility~string_to_fields( |{ lv_string }| ).

    IF lines( lt_fields ) = 0.
      lcx_exception=>raise( 'process_stage_list: empty list' ).
    ENDIF.

    LOOP AT lt_fields ASSIGNING <ls_item>.

      lcl_path=>split_file_location( EXPORTING iv_fullpath = <ls_item>-name
                                     IMPORTING ev_path     = ls_file-path
                                               ev_filename = ls_file-filename ).
      CASE <ls_item>-value.
        WHEN lcl_stage=>c_method-add.
          READ TABLE ms_files-local ASSIGNING <ls_file>
            WITH KEY file-path     = ls_file-path
                     file-filename = ls_file-filename.
          ASSERT sy-subrc = 0.
          mo_stage->add(    iv_path     = <ls_file>-file-path
                            iv_filename = <ls_file>-file-filename
                            iv_data     = <ls_file>-file-data ).
        WHEN lcl_stage=>c_method-ignore.
          mo_stage->ignore( iv_path     = ls_file-path
                            iv_filename = ls_file-filename ).
        WHEN lcl_stage=>c_method-rm.
          mo_stage->rm(     iv_path     = ls_file-path
                            iv_filename = ls_file-filename ).
        WHEN lcl_stage=>c_method-skip.
          " Do nothing
        WHEN OTHERS.
          lcx_exception=>raise( |process_stage_list: unknown method { <ls_item>-value }| ).
      ENDCASE.
    ENDLOOP.

  ENDMETHOD.        "process_stage_list

  METHOD render_list.

    FIELD-SYMBOLS: <ls_remote> LIKE LINE OF ms_files-remote,
                   <ls_local>  LIKE LINE OF ms_files-local.

    CREATE OBJECT ro_html.

    ro_html->add( '
' ).

    " Local changes
    LOOP AT ms_files-local ASSIGNING <ls_local>.
      AT FIRST.
        ro_html->add('

').
        ro_html->add('	LOCAL	' ).
        ro_html->add('' ).
        IF lines( ms_files-local ) > 1.
          ro_html->add_a( iv_txt = |{ lines( ms_files-local ) } diffs|
                          iv_act = |{ gc_action-go_diff }?key={ mo_repo->get_key( ) }| ).
        ENDIF.
        ro_html->add('	').
        ro_html->add('Last changed by	').
        ro_html->add('

').
        ro_html->add('
').
      ENDAT.

      ro_html->add( render_file( is_file = <ls_local>-file iv_context = 'local' ) ).

      AT LAST.
        ro_html->add('
').
      ENDAT.
    ENDLOOP.

    " Remote changes
    LOOP AT ms_files-remote ASSIGNING <ls_remote>.
      AT FIRST.
        ro_html->add('

').
        ro_html->add('	REMOTE	' ).
        ro_html->add('

').
        ro_html->add('
').
      ENDAT.

      ro_html->add( render_file( is_file = <ls_remote> iv_context = 'remote' ) ).

      AT LAST.
        ro_html->add('
').
      ENDAT.
    ENDLOOP.

    ro_html->add( '
' ).

  ENDMETHOD.      "render_lines

  METHOD render_file.

    DATA: lv_param TYPE string,
          lv_user  TYPE xubname.

    CREATE OBJECT ro_html.

    ro_html->add( |
| ).
    ro_html->add( |?	| ).
    ro_html->add( |{ is_file-path && is_file-filename }	| ).

    CASE iv_context.
      WHEN 'local'.
        lv_param = lcl_html_action_utils=>file_encode( iv_key  = mo_repo->get_key( )
                                                       ig_file = is_file ).
        ro_html->add( 'add	' ).
        ro_html->add( '' ).
        ro_html->add_a( iv_txt = 'diff' iv_act = |{ gc_action-go_diff }?{ lv_param }| ).
        ro_html->add( '	' ).

        lv_user = read_last_changed_by( is_file ).
        ro_html->add( |{ lv_user }	 | ).
      WHEN 'remote'.
        ro_html->add( 'ignoreremove	' ).
        ro_html->add( |-	| ).
    ENDCASE.

    ro_html->add( '
' ).

  ENDMETHOD.  "render_file

  METHOD render_content.

    CREATE OBJECT ro_html.

    ro_html->add( '
' ).
    ro_html->add( lcl_gui_chunk_lib=>render_repo_top( mo_repo ) ).
    ro_html->add( render_menu( ) ).
    ro_html->add( render_list( ) ).
    ro_html->add( '
' ).

  ENDMETHOD.      "render_content

  METHOD render_menu.

    CREATE OBJECT ro_html.

    ro_html->add( '
' ).
    ro_html->add_a( iv_act   = 'gHelper.submit();'
                    iv_typ   = gc_action_type-onclick
                    iv_id    = 'act_commit'
                    iv_style = 'display: none'
                    iv_txt   = 'Commit'
                    iv_opt   = gc_html_opt-strong ) ##NO_TEXT.
    IF lines( ms_files-local ) > 0.
      ro_html->add_a( iv_act = |{ c_action-stage_all }|
                      iv_id  = 'act_commit_all'
                      iv_txt = 'Add all and commit') ##NO_TEXT.
    ENDIF.
    ro_html->add( '
' ).

  ENDMETHOD.      "render_menu

  METHOD scripts.

    CREATE OBJECT ro_html.

    ro_html->add( 'var gStageParams = {' ).
    ro_html->add( |  seed:            "stage{ mv_ts }",| ).
    ro_html->add( '  stageTabId:      "stage_tab",' ).
    ro_html->add( '  formAction:      "stage_commit",' ).
    ro_html->add( '  commitNodeId:    "act_commit",' ).
    ro_html->add( '  commitAllNodeId: "act_commit_all"' ).
    ro_html->add( '}' ).
    ro_html->add( 'var gHelper = new StageHelper(gStageParams);' ).

  ENDMETHOD.  "scripts

  METHOD read_last_changed_by.
    DATA: ls_local_file TYPE ty_file_item.
    TRY.
        READ TABLE mo_repo->get_files_local( ) INTO ls_local_file WITH KEY file = is_file.
        IF sy-subrc = 0.
          rv_user = lcl_objects=>changed_by( ls_local_file-item ).
        ENDIF.
      CATCH lcx_exception.
        CLEAR rv_user. "Should not raise errors if user last changed by was not found
    ENDTRY.
  ENDMETHOD.

ENDCLASS.

Add "last changed by" user to Stage page


CLASS zcl_wabap_object_devc DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

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

    TYPES: BEGIN OF ty_devc,
             contents TYPE STANDARD TABLE OF ty_tadir WITH DEFAULT KEY,
             tdevc    TYPE tdevc,
             tdevct   TYPE STANDARD TABLE OF tdevct WITH DEFAULT KEY,
             sub      TYPE STANDARD TABLE OF devclass WITH DEFAULT KEY,
           END OF ty_devc.

    METHODS read
      RETURNING
        VALUE(rs_data) TYPE ty_devc.
    METHODS constructor
      IMPORTING
        !iv_name TYPE tadir-obj_name .
protected section.
private section.

  data MV_NAME type DEVCLASS .
ENDCLASS.

CLASS ZCL_WABAP_OBJECT_DEVC IMPLEMENTATION.

  METHOD constructor.

    mv_name = iv_name.

  ENDMETHOD.

  METHOD read.

    SELECT SINGLE * FROM tdevc
      INTO rs_data-tdevc
      WHERE devclass = mv_name.

    SELECT * FROM tdevct
      INTO TABLE rs_data-tdevct
      WHERE devclass = mv_name.

    SELECT * FROM tadir
      INTO CORRESPONDING FIELDS OF TABLE rs_data-contents
      WHERE devclass = mv_name
      AND author <> 'SAP'
      AND author <> 'DDIC'
      ORDER BY PRIMARY KEY.                             "#EC CI_GENBUFF

    SELECT devclass FROM tdevc INTO
      TABLE rs_data-sub
      WHERE parentcl = mv_name.

  ENDMETHOD.
ENDCLASS.

CLASS zcl_wabap_object_devc DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

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

    TYPES: BEGIN OF ty_devc,
             contents TYPE STANDARD TABLE OF ty_tadir WITH DEFAULT KEY,
             tdevc    TYPE tdevc,
             tdevct   TYPE STANDARD TABLE OF tdevct WITH DEFAULT KEY,
             sub      TYPE STANDARD TABLE OF devclass WITH DEFAULT KEY,
           END OF ty_devc.

    METHODS read
      RETURNING
        VALUE(rs_data) TYPE ty_devc.
    METHODS constructor
      IMPORTING
        !iv_name TYPE tadir-obj_name .
  PROTECTED SECTION.
  PRIVATE SECTION.

    DATA mv_name TYPE devclass .
ENDCLASS.

CLASS ZCL_WABAP_OBJECT_DEVC IMPLEMENTATION.

  METHOD constructor.

    mv_name = iv_name.

  ENDMETHOD.

  METHOD read.

    SELECT SINGLE * FROM tdevc
      INTO rs_data-tdevc
      WHERE devclass = mv_name.

    SELECT * FROM tdevct
      INTO TABLE rs_data-tdevct
      WHERE devclass = mv_name
      ORDER BY PRIMARY KEY.

    SELECT * FROM tadir
      INTO CORRESPONDING FIELDS OF TABLE rs_data-contents
      WHERE devclass = mv_name
      AND author <> 'SAP'
      AND author <> 'DDIC'
      ORDER BY PRIMARY KEY.                             "#EC CI_GENBUFF

    SELECT devclass FROM tdevc INTO
      TABLE rs_data-sub
      WHERE parentcl = mv_name
      ORDER BY PRIMARY KEY.

  ENDMETHOD.
ENDCLASS.

order by primary key

order by primary key


"! Illegal argument exception
CLASS zcx_alog_illegal_argument DEFINITION
  PUBLIC
  INHERITING FROM zcx_alog_call_error
  FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    CONSTANTS:
      BEGIN OF gc_illegal_argument,
        msgid TYPE symsgid VALUE 'ZALOG',
        msgno TYPE symsgno VALUE '013',
        attr1 TYPE scx_attrname VALUE '',
        attr2 TYPE scx_attrname VALUE '',
        attr3 TYPE scx_attrname VALUE '',
        attr4 TYPE scx_attrname VALUE '',
      END OF gc_illegal_argument,
      BEGIN OF gc_name,
        msgid TYPE symsgid VALUE 'ZALOG',
        msgno TYPE symsgno VALUE '014',
        attr1 TYPE scx_attrname VALUE 'MV_NAME',
        attr2 TYPE scx_attrname VALUE '',
        attr3 TYPE scx_attrname VALUE '',
        attr4 TYPE scx_attrname VALUE '',
      END OF gc_name,
      BEGIN OF gc_name_and_reason,
        msgid TYPE symsgid VALUE 'ZALOG',
        msgno TYPE symsgno VALUE '015',
        attr1 TYPE scx_attrname VALUE 'MV_NAME',
        attr2 TYPE scx_attrname VALUE 'MV_REASON',
        attr3 TYPE scx_attrname VALUE '',
        attr4 TYPE scx_attrname VALUE '',
      END OF gc_name_and_reason,
      BEGIN OF gc_reason,
        msgid TYPE symsgid VALUE 'ZALOG',
        msgno TYPE symsgno VALUE '016',
        attr1 TYPE scx_attrname VALUE 'MV_REASON',
        attr2 TYPE scx_attrname VALUE '',
        attr3 TYPE scx_attrname VALUE '',
        attr4 TYPE scx_attrname VALUE '',
      END OF gc_reason.
    METHODS:
      "! @parameter ix_previous | Previous exception
      constructor IMPORTING ix_previous LIKE previous OPTIONAL
                            iv_reason   TYPE csequence OPTIONAL
                            iv_value    TYPE csequence OPTIONAL
                            iv_name     TYPE csequence OPTIONAL.
    DATA:
      mv_value TYPE string READ-ONLY,
      mv_name  TYPE string READ-ONLY.
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.

CLASS zcx_alog_illegal_argument IMPLEMENTATION.
  METHOD constructor ##ADT_SUPPRESS_GENERATION.
    super->constructor( ix_previous = ix_previous
                        is_textid   = COND #( WHEN iv_reason IS SUPPLIED
                                                   AND iv_name IS SUPPLIED
                                                   THEN gc_name_and_reason
                                              WHEN iv_reason IS SUPPLIED
                                                   THEN gc_reason
                                              ELSE gc_illegal_argument ) ).
    mv_value = iv_value.
    mv_name = iv_name.
  ENDMETHOD.
ENDCLASS.

"! Illegal argument exception
CLASS zcx_alog_illegal_argument DEFINITION
  PUBLIC
  INHERITING FROM zcx_alog_call_error
  FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    CONSTANTS:
      BEGIN OF gc_illegal_argument,
        msgid TYPE symsgid VALUE 'ZALOG',
        msgno TYPE symsgno VALUE '013',
        attr1 TYPE scx_attrname VALUE '',
        attr2 TYPE scx_attrname VALUE '',
        attr3 TYPE scx_attrname VALUE '',
        attr4 TYPE scx_attrname VALUE '',
      END OF gc_illegal_argument,
      BEGIN OF gc_name,
        msgid TYPE symsgid VALUE 'ZALOG',
        msgno TYPE symsgno VALUE '014',
        attr1 TYPE scx_attrname VALUE 'MV_NAME',
        attr2 TYPE scx_attrname VALUE '',
        attr3 TYPE scx_attrname VALUE '',
        attr4 TYPE scx_attrname VALUE '',
      END OF gc_name,
      BEGIN OF gc_name_and_reason,
        msgid TYPE symsgid VALUE 'ZALOG',
        msgno TYPE symsgno VALUE '015',
        attr1 TYPE scx_attrname VALUE 'MV_NAME',
        attr2 TYPE scx_attrname VALUE 'MV_REASON',
        attr3 TYPE scx_attrname VALUE '',
        attr4 TYPE scx_attrname VALUE '',
      END OF gc_name_and_reason,
      BEGIN OF gc_reason,
        msgid TYPE symsgid VALUE 'ZALOG',
        msgno TYPE symsgno VALUE '016',
        attr1 TYPE scx_attrname VALUE 'MV_REASON',
        attr2 TYPE scx_attrname VALUE '',
        attr3 TYPE scx_attrname VALUE '',
        attr4 TYPE scx_attrname VALUE '',
      END OF gc_reason.
    METHODS:
      "! @parameter ix_previous | Previous exception
      constructor IMPORTING ix_previous LIKE previous OPTIONAL
                            iv_reason   TYPE csequence OPTIONAL
                            iv_value    TYPE csequence OPTIONAL
                            iv_name     TYPE csequence OPTIONAL
                              PREFERRED PARAMETER iv_reason.
    DATA:
      mv_value TYPE string READ-ONLY,
      mv_name  TYPE string READ-ONLY.
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.

CLASS ZCX_ALOG_ILLEGAL_ARGUMENT IMPLEMENTATION.

  METHOD constructor ##ADT_SUPPRESS_GENERATION.
    super->constructor( ix_previous = ix_previous
                        iv_reason   = iv_reason
                        is_textid   = COND #( WHEN iv_reason IS SUPPLIED
                                                   AND iv_name IS SUPPLIED
                                                   THEN gc_name_and_reason
                                              WHEN iv_reason IS SUPPLIED
                                                   THEN gc_reason
                                              ELSE gc_illegal_argument ) ).
    mv_value = iv_value.
    mv_name = iv_name.
  ENDMETHOD.
ENDCLASS.

Fix illegal argument exception reason missing


CLASS zcl_ags_service_git DEFINITION
  PUBLIC
  CREATE PUBLIC.

  PUBLIC SECTION.

    INTERFACES zif_ags_service.

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

    TYPES:
      BEGIN OF ty_push,
        old    TYPE zags_sha1,
        new    TYPE zags_sha1,
        name   TYPE zags_branch_name,
        length TYPE i,
      END OF ty_push.

    DATA mi_server TYPE REF TO if_http_server.

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

CLASS ZCL_AGS_SERVICE_GIT IMPLEMENTATION.

  METHOD branch_list.

    DEFINE _capability.
      append &1 to lt_capabilities ##no_text.
    END-OF-DEFINITION.

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

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

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

    CONCATENATE LINES OF lt_capabilities INTO lv_reply SEPARATED BY space.

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

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

    lv_tmp = |000000e8{ lv_head } HEAD{ get_null( ) }{ lv_reply }|.
    APPEND lv_tmp TO lt_reply.

    lt_branches = lo_repo->list_branches( ).
    LOOP AT lt_branches ASSIGNING <lo_branch>.
      lv_content = <lo_branch>->get_data( )-sha1
        && ' refs/heads/'
        && <lo_branch>->get_data( )-name ##no_text.
      lv_length = lcl_length=>encode( strlen( lv_content ) + 4 ).
      lv_utf = zcl_ags_util=>xstring_to_string_utf8( lv_length ).

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

    APPEND '0000' TO lt_reply.

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

    mi_server->response->set_cdata( lv_reply ).

  ENDMETHOD.

  METHOD constructor.

    mi_server = ii_server.

  ENDMETHOD.

  METHOD decode_push.

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

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

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

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

    READ TABLE lt_data INDEX 1 INTO lv_data.

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

  ENDMETHOD.

  METHOD decode_want.

* todo, proper decoding
    rv_sha1 = iv_string+9.

  ENDMETHOD.

  METHOD get_null.

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

    FIELD-SYMBOLS <lv_y> TYPE c.

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

  ENDMETHOD.

  METHOD pack.

    CONSTANTS: lc_band1 TYPE x VALUE '01'.

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

    lv_branch = decode_want( mi_server->request->get_cdata( ) ).

    CREATE OBJECT lo_commit
      EXPORTING
        iv_sha1 = lv_branch.

    lv_pack = zcl_ags_pack=>encode( zcl_ags_pack=>explode( lo_commit ) ).

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

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

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

      lv_pack = lv_pack+lv_length.
    ENDWHILE.

    mi_server->response->set_data( lv_response ).

  ENDMETHOD.

  METHOD repo_name.

    DATA: lv_path TYPE string.

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

  ENDMETHOD.

  METHOD unpack.

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

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

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

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

    lt_objects = zcl_ags_pack=>decode( lv_xstring ).

    CREATE OBJECT lo_repo
      EXPORTING
        iv_name = repo_name( ).

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

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

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

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

      zcl_ags_pack=>save( lt_objects ).

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

    unpack_ok( ).

  ENDMETHOD.

  METHOD unpack_ok.

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

  ENDMETHOD.

  METHOD zif_ags_service~run.

    DATA: lv_path TYPE string,
          lv_xdata TYPE string.

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

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

  ENDMETHOD.
ENDCLASS.

CLASS zcl_ags_service_git DEFINITION
  PUBLIC
  CREATE PUBLIC.

  PUBLIC SECTION.

    INTERFACES zif_ags_service.

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

    TYPES:
      BEGIN OF ty_push,
        old    TYPE zags_sha1,
        new    TYPE zags_sha1,
        name   TYPE zags_branch_name,
        length TYPE i,
      END OF ty_push.

    DATA mi_server TYPE REF TO if_http_server.

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

CLASS ZCL_AGS_SERVICE_GIT IMPLEMENTATION.

  METHOD branch_list.

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

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

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

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

    CONCATENATE LINES OF lt_capabilities INTO lv_reply SEPARATED BY space.

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

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

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

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

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

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

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

    APPEND '0000' TO lt_reply.

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

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

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

  ENDMETHOD.

  METHOD constructor.

    mi_server = ii_server.

  ENDMETHOD.

  METHOD decode_push.

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

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

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

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

    READ TABLE lt_data INDEX 1 INTO lv_data.

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

  ENDMETHOD.

  METHOD decode_want.

* todo, proper decoding
    rv_sha1 = iv_string+9.

  ENDMETHOD.

  METHOD get_null.

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

    FIELD-SYMBOLS <lv_y> TYPE c.

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

  ENDMETHOD.

  METHOD pack.

    CONSTANTS: lc_band1 TYPE x VALUE '01'.

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

    lv_branch = decode_want( mi_server->request->get_cdata( ) ).

    CREATE OBJECT lo_commit
      EXPORTING
        iv_sha1 = lv_branch.

    lv_pack = zcl_ags_pack=>encode( zcl_ags_pack=>explode( lo_commit ) ).

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

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

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

      lv_pack = lv_pack+lv_length.
    ENDWHILE.

    mi_server->response->set_data( lv_response ).

  ENDMETHOD.

  METHOD repo_name.

    DATA: lv_path TYPE string.

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

  ENDMETHOD.

  METHOD unpack.

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

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

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

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

    lt_objects = zcl_ags_pack=>decode( lv_xstring ).

    CREATE OBJECT lo_repo
      EXPORTING
        iv_name = repo_name( ).

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

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

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

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

      zcl_ags_pack=>save( lt_objects ).

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

    unpack_ok( ).

  ENDMETHOD.

  METHOD unpack_ok.

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

  ENDMETHOD.

  METHOD zif_ags_service~run.

    DATA: lv_path TYPE string,
          lv_xdata TYPE string.

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

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

  ENDMETHOD.
ENDCLASS.

fix refs discovery

fix refs discovery


CLASS zcl_abapgit_object_odso DEFINITION
  PUBLIC
  INHERITING FROM zcl_abapgit_objects_super
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    INTERFACES zif_abapgit_object .

    ALIASES mo_files
      FOR zif_abapgit_object~mo_files .
  PROTECTED SECTION.
  PRIVATE SECTION.

    METHODS:
      clear_field
        IMPORTING
          iv_fieldname TYPE string
        CHANGING
          cg_metadata  TYPE any.
ENDCLASS.

CLASS zcl_abapgit_object_odso IMPLEMENTATION.

  METHOD zif_abapgit_object~changed_by.

    DATA: lv_dsonam  TYPE c LENGTH 30,
          ls_return  TYPE bapiret2,
          lr_details TYPE REF TO data.

    FIELD-SYMBOLS: <lg_details> TYPE any,
                   <lg_tstpnm>  TYPE any.

    TRY.
        CREATE DATA lr_details TYPE ('BAPI6116').
      CATCH cx_sy_create_data_error.
        zcx_abapgit_exception=>raise( |ODSO is not supported on this system| ).
    ENDTRY.

    ASSIGN lr_details->* TO <lg_details>.

    lv_dsonam = ms_item-obj_name.

    CALL FUNCTION 'BAPI_ODSO_GETDETAIL'
      EXPORTING
        odsobject = lv_dsonam
      IMPORTING
        details   = <lg_details>
        return    = ls_return.

    IF ls_return-type = 'E'.
      zcx_abapgit_exception=>raise( |Error when geting changed by of ODSO: { ls_return-message }| ).
    ENDIF.

    ASSIGN COMPONENT 'TSTPNM' OF STRUCTURE <lg_details> TO <lg_tstpnm>.

    rv_user = <lg_tstpnm>.

  ENDMETHOD.

  METHOD zif_abapgit_object~delete.

    DATA: lv_odsonam    TYPE c LENGTH 30,
          lv_objname    TYPE sobj_name,
          lo_collection TYPE REF TO object,
          lt_msg        TYPE STANDARD TABLE OF bal_s_msg,
          ls_msg        TYPE bal_s_msg.

    TRY.
        CREATE OBJECT lo_collection TYPE ('CL_RSD_ODSO_COLLECTION').
      CATCH cx_sy_create_data_error.
        zcx_abapgit_exception=>raise( |ODSO is not supported on this system| ).
    ENDTRY.

    lv_odsonam = ms_item-obj_name.
    lv_objname = ms_item-obj_name.

    TRY.
        CALL METHOD lo_collection->('ADD_TLOGO')
          EXPORTING
            i_objnm  = lv_objname
            i_modify = abap_true
            i_delete = abap_true.

        CALL METHOD lo_collection->('DELETE').

        CALL METHOD ('CL_RSO_APPLICATION_LOG')=>('APPL_LOG_MSG_READ')
          IMPORTING
            e_t_msg = lt_msg.

        READ TABLE lt_msg WITH KEY msgty = 'E' INTO ls_msg.
        IF sy-subrc = 0.
          zcx_abapgit_exception=>raise(
          |Error when deleting ODSO: { ms_item-obj_name } { ls_msg-msgv1 } { ls_msg-msgv2 }| ).
        ENDIF.

      CATCH cx_rs_cancelled.
        zcx_abapgit_exception=>raise( |Canceled deletion of ODSO: { ms_item-obj_name }| ).
      CATCH cx_rs_existing.
        zcx_abapgit_exception=>raise( |ODSO not exist: { ms_item-obj_name }| ).
      CATCH cx_rs_not_found.
        zcx_abapgit_exception=>raise( |ODSO not found: { ms_item-obj_name }| ).
    ENDTRY.

  ENDMETHOD.

  METHOD zif_abapgit_object~deserialize.

    DATA: lv_dsonam      TYPE c LENGTH 30,
          lr_details     TYPE REF TO data,
          lr_infoobjects TYPE REF TO data,
          lr_navigation  TYPE REF TO data,
          lr_indexes     TYPE REF TO data,
          lr_index_iobj  TYPE REF TO data,
          lt_return      TYPE STANDARD TABLE OF bapiret2,
          ls_return      TYPE bapiret2.

    FIELD-SYMBOLS:
      <lg_details>     TYPE any,
      <lt_infoobjects> TYPE STANDARD TABLE,
      <lt_navigation>  TYPE STANDARD TABLE,
      <lt_indexes>     TYPE STANDARD TABLE,
      <lt_index_iobj>  TYPE STANDARD TABLE.

    TRY.
        CREATE DATA lr_details     TYPE ('BAPI6116').
        CREATE DATA lr_infoobjects TYPE STANDARD TABLE OF ('BAPI6116IO').
        CREATE DATA lr_navigation  TYPE STANDARD TABLE OF ('BAPI6116NA').
        CREATE DATA lr_indexes     TYPE STANDARD TABLE OF ('BAPI6116IN').
        CREATE DATA lr_index_iobj  TYPE STANDARD TABLE OF ('BAPI6116II').
      CATCH cx_sy_create_data_error.
        zcx_abapgit_exception=>raise( |ODSO is not supported on this system| ).
    ENDTRY.

    ASSIGN lr_details->*     TO <lg_details>.
    ASSIGN lr_infoobjects->* TO <lt_infoobjects>.
    ASSIGN lr_navigation->*  TO <lt_navigation>.
    ASSIGN lr_indexes->*     TO <lt_indexes>.
    ASSIGN lr_index_iobj->*  TO <lt_index_iobj>.

    io_xml->read( EXPORTING iv_name = 'ODSO'
                  CHANGING  cg_data = <lg_details> ).

    io_xml->read( EXPORTING iv_name = 'INFOOBJECTS'
                  CHANGING  cg_data =  <lt_infoobjects> ).

    io_xml->read( EXPORTING iv_name = 'NAVIGATION'
                  CHANGING  cg_data =  <lt_navigation> ).

    io_xml->read( EXPORTING iv_name = 'INDEXES'
                  CHANGING  cg_data =  <lt_indexes> ).

    io_xml->read( EXPORTING iv_name = 'INDEX_IOBJ'
                  CHANGING  cg_data =  <lt_index_iobj> ).
    TRY.
        CALL FUNCTION 'BAPI_ODSO_CREATE'
          EXPORTING
            details              = <lg_details>
          IMPORTING
            odsobject            = lv_dsonam
          TABLES
            infoobjects          = <lt_infoobjects>
            navigationattributes = <lt_navigation>
            indexes              = <lt_indexes>
            indexesinfoobjects   = <lt_index_iobj>
            return               = lt_return.

      CATCH  cx_sy_dyn_call_illegal_func.
        zcx_abapgit_exception=>raise( |Necessary BW function modules not found or object not supported| ).
    ENDTRY.

    READ TABLE lt_return WITH KEY type = 'E' INTO ls_return.
    IF sy-subrc = 0.
      zcx_abapgit_exception=>raise( |Error when creating ODSO: { ls_return-message }| ).
    ENDIF.

    CALL FUNCTION 'BAPI_ODSO_ACTIVATE'
      EXPORTING
        odsobject = lv_dsonam
      TABLES
        return    = lt_return.

    READ TABLE lt_return WITH KEY type = 'E' INTO ls_return.
    IF sy-subrc = 0.
      zcx_abapgit_exception=>raise( |Error when activating ODSO: { ls_return-message }| ).
    ENDIF.

    tadir_insert( iv_package ).

    corr_insert( iv_package ).

  ENDMETHOD.

  METHOD zif_abapgit_object~exists.

    DATA: lv_iobjnm TYPE c LENGTH 30.

    SELECT SINGLE odsobject
    FROM ('RSDODSO')
    INTO lv_iobjnm
    WHERE odsobject = ms_item-obj_name.

    rv_bool = boolc( sy-subrc = 0 ).

  ENDMETHOD.

  METHOD zif_abapgit_object~get_comparator.
    RETURN.
  ENDMETHOD.

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

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

  METHOD zif_abapgit_object~is_active.

    DATA: lv_dsona TYPE c LENGTH 30,
          lo_odso  TYPE REF TO object,
          lv_isact TYPE abap_bool.

    lv_dsona = ms_item-obj_name.

    CALL METHOD ('CL_RSD_ODSO')=>('FACTORY')
      EXPORTING
        i_odsobject = lv_dsona
      RECEIVING
        r_r_odso    = lo_odso.

    CALL METHOD lo_odso->('IS_ACTIVE')
      RECEIVING
        r_is_active = lv_isact.

    rv_active = lv_isact.

  ENDMETHOD.

  METHOD zif_abapgit_object~is_locked.

    DATA: lv_object TYPE eqegraarg.

    lv_object =  ms_item-obj_name.
    OVERLAY lv_object WITH '                                          '.
    lv_object = lv_object && '*'.

    rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'RSD_S_PROV'
                                            iv_argument    = lv_object ).

  ENDMETHOD.

  METHOD zif_abapgit_object~jump.
    zcx_abapgit_exception=>raise( |Jump to ODSO is not yet supported| ).
  ENDMETHOD.

  METHOD zif_abapgit_object~serialize.

    DATA: lv_dsonam      TYPE c LENGTH 30,
          lr_details     TYPE REF TO data,
          lr_infoobjects TYPE REF TO data,
          lr_navigation  TYPE REF TO data,
          lr_indexes     TYPE REF TO data,
          lr_index_iobj  TYPE REF TO data,
          ls_return      TYPE bapiret2.

    FIELD-SYMBOLS:
      <lg_details>     TYPE any,
      <lt_infoobjects> TYPE STANDARD TABLE,
      <lt_navigation>  TYPE STANDARD TABLE,
      <lt_indexes>     TYPE STANDARD TABLE,
      <lt_index_iobj>  TYPE STANDARD TABLE.

    TRY.
        CREATE DATA lr_details     TYPE ('BAPI6116').
        CREATE DATA lr_infoobjects TYPE STANDARD TABLE OF ('BAPI6116IO').
        CREATE DATA lr_navigation  TYPE STANDARD TABLE OF ('BAPI6116NA').
        CREATE DATA lr_indexes     TYPE STANDARD TABLE OF ('BAPI6116IN').
        CREATE DATA lr_index_iobj  TYPE STANDARD TABLE OF ('BAPI6116II').
      CATCH cx_sy_create_data_error.
        zcx_abapgit_exception=>raise( |ODSO is not supported on this system| ).
    ENDTRY.

    ASSIGN lr_details->*     TO <lg_details>.
    ASSIGN lr_infoobjects->* TO <lt_infoobjects>.
    ASSIGN lr_navigation->*  TO <lt_navigation>.
    ASSIGN lr_indexes->*     TO <lt_indexes>.
    ASSIGN lr_index_iobj->*  TO <lt_index_iobj>.

    lv_dsonam = ms_item-obj_name.

    CALL FUNCTION 'BAPI_ODSO_GETDETAIL'
      EXPORTING
        odsobject            = lv_dsonam
      IMPORTING
        details              = <lg_details>
        return               = ls_return
      TABLES
        infoobjects          = <lt_infoobjects>
        navigationattributes = <lt_navigation>
        indexes              = <lt_indexes>
        indexesinfoobjects   = <lt_index_iobj>.

    IF ls_return-type = 'E'.
      zcx_abapgit_exception=>raise( |Error when geting details of ODSO: { ls_return-message }| ).
    ENDIF.

    clear_field( EXPORTING iv_fieldname = 'TSTPNM'
                 CHANGING  cg_metadata  = <lg_details> ).

    clear_field( EXPORTING iv_fieldname = 'TIMESTMP'
                 CHANGING  cg_metadata  = <lg_details> ).

    clear_field( EXPORTING iv_fieldname = 'CONTTIMESTMP'
                 CHANGING  cg_metadata  = <lg_details> ).

    clear_field( EXPORTING iv_fieldname = 'OWNER'
                 CHANGING  cg_metadata  = <lg_details> ).

    io_xml->add( iv_name = 'ODSO'
                 ig_data = <lg_details> ).

    io_xml->add( iv_name = 'INFOOBJECTS'
                 ig_data = <lt_infoobjects> ).

    io_xml->add( iv_name = 'NAVIGATION'
                 ig_data = <lt_navigation> ).

    io_xml->add( iv_name = 'INDEXES'
                 ig_data = <lt_indexes> ).

    io_xml->add( iv_name = 'INDEX_IOBJ'
                 ig_data = <lt_index_iobj> ).

  ENDMETHOD.

  METHOD clear_field.

    FIELD-SYMBOLS: <lg_field> TYPE data.

    ASSIGN COMPONENT iv_fieldname
           OF STRUCTURE cg_metadata
           TO <lg_field>.
    ASSERT sy-subrc = 0.

    CLEAR: <lg_field>.

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_object_odso DEFINITION
  PUBLIC
  INHERITING FROM zcl_abapgit_objects_super
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    INTERFACES zif_abapgit_object .

    ALIASES mo_files
      FOR zif_abapgit_object~mo_files .
  PROTECTED SECTION.
  PRIVATE SECTION.

    METHODS:
      clear_field
        IMPORTING
          iv_fieldname TYPE string
        CHANGING
          cg_metadata  TYPE any.
ENDCLASS.

CLASS zcl_abapgit_object_odso IMPLEMENTATION.

  METHOD zif_abapgit_object~changed_by.

    DATA: lv_dsonam  TYPE c LENGTH 30,
          ls_return  TYPE bapiret2,
          lr_details TYPE REF TO data.

    FIELD-SYMBOLS: <lg_details> TYPE any,
                   <lg_tstpnm>  TYPE any.

    TRY.
        CREATE DATA lr_details TYPE ('BAPI6116').
      CATCH cx_sy_create_data_error.
        zcx_abapgit_exception=>raise( |ODSO is not supported on this system| ).
    ENDTRY.

    ASSIGN lr_details->* TO <lg_details>.

    lv_dsonam = ms_item-obj_name.

    CALL FUNCTION 'BAPI_ODSO_GETDETAIL'
      EXPORTING
        odsobject = lv_dsonam
      IMPORTING
        details   = <lg_details>
        return    = ls_return.

    IF ls_return-type = 'E'.
      zcx_abapgit_exception=>raise( |Error when geting changed by of ODSO: { ls_return-message }| ).
    ENDIF.

    ASSIGN COMPONENT 'TSTPNM' OF STRUCTURE <lg_details> TO <lg_tstpnm>.

    rv_user = <lg_tstpnm>.

  ENDMETHOD.

  METHOD zif_abapgit_object~delete.

    DATA: lv_odsonam    TYPE c LENGTH 30,
          lv_objname    TYPE sobj_name,
          lo_collection TYPE REF TO object,
          lt_msg        TYPE STANDARD TABLE OF bal_s_msg,
          ls_msg        TYPE bal_s_msg.

    TRY.
        CREATE OBJECT lo_collection TYPE ('CL_RSD_ODSO_COLLECTION').
      CATCH cx_sy_create_data_error.
        zcx_abapgit_exception=>raise( |ODSO is not supported on this system| ).
    ENDTRY.

    lv_odsonam = ms_item-obj_name.
    lv_objname = ms_item-obj_name.

    TRY.
        CALL METHOD lo_collection->('ADD_TLOGO')
          EXPORTING
            i_objnm  = lv_objname
            i_modify = abap_true
            i_delete = abap_true.

        CALL METHOD lo_collection->('DELETE').

        CALL METHOD ('CL_RSO_APPLICATION_LOG')=>('APPL_LOG_MSG_READ')
          IMPORTING
            e_t_msg = lt_msg.

        READ TABLE lt_msg WITH KEY msgty = 'E' INTO ls_msg.
        IF sy-subrc = 0.
          zcx_abapgit_exception=>raise(
          |Error when deleting ODSO: { ms_item-obj_name } { ls_msg-msgv1 } { ls_msg-msgv2 }| ).
        ENDIF.

      CATCH cx_root.
        zcx_abapgit_exception=>raise( |Canceled deletion of ODSO: { ms_item-obj_name }| ).
    ENDTRY.

  ENDMETHOD.

  METHOD zif_abapgit_object~deserialize.

    DATA: lv_dsonam      TYPE c LENGTH 30,
          lr_details     TYPE REF TO data,
          lr_infoobjects TYPE REF TO data,
          lr_navigation  TYPE REF TO data,
          lr_indexes     TYPE REF TO data,
          lr_index_iobj  TYPE REF TO data,
          lt_return      TYPE STANDARD TABLE OF bapiret2,
          ls_return      TYPE bapiret2.

    FIELD-SYMBOLS:
      <lg_details>     TYPE any,
      <lt_infoobjects> TYPE STANDARD TABLE,
      <lt_navigation>  TYPE STANDARD TABLE,
      <lt_indexes>     TYPE STANDARD TABLE,
      <lt_index_iobj>  TYPE STANDARD TABLE.

    TRY.
        CREATE DATA lr_details     TYPE ('BAPI6116').
        CREATE DATA lr_infoobjects TYPE STANDARD TABLE OF ('BAPI6116IO').
        CREATE DATA lr_navigation  TYPE STANDARD TABLE OF ('BAPI6116NA').
        CREATE DATA lr_indexes     TYPE STANDARD TABLE OF ('BAPI6116IN').
        CREATE DATA lr_index_iobj  TYPE STANDARD TABLE OF ('BAPI6116II').
      CATCH cx_sy_create_data_error.
        zcx_abapgit_exception=>raise( |ODSO is not supported on this system| ).
    ENDTRY.

    ASSIGN lr_details->*     TO <lg_details>.
    ASSIGN lr_infoobjects->* TO <lt_infoobjects>.
    ASSIGN lr_navigation->*  TO <lt_navigation>.
    ASSIGN lr_indexes->*     TO <lt_indexes>.
    ASSIGN lr_index_iobj->*  TO <lt_index_iobj>.

    io_xml->read( EXPORTING iv_name = 'ODSO'
                  CHANGING  cg_data = <lg_details> ).

    io_xml->read( EXPORTING iv_name = 'INFOOBJECTS'
                  CHANGING  cg_data =  <lt_infoobjects> ).

    io_xml->read( EXPORTING iv_name = 'NAVIGATION'
                  CHANGING  cg_data =  <lt_navigation> ).

    io_xml->read( EXPORTING iv_name = 'INDEXES'
                  CHANGING  cg_data =  <lt_indexes> ).

    io_xml->read( EXPORTING iv_name = 'INDEX_IOBJ'
                  CHANGING  cg_data =  <lt_index_iobj> ).
    TRY.
        CALL FUNCTION 'BAPI_ODSO_CREATE'
          EXPORTING
            details              = <lg_details>
          IMPORTING
            odsobject            = lv_dsonam
          TABLES
            infoobjects          = <lt_infoobjects>
            navigationattributes = <lt_navigation>
            indexes              = <lt_indexes>
            indexesinfoobjects   = <lt_index_iobj>
            return               = lt_return.

      CATCH  cx_sy_dyn_call_illegal_func.
        zcx_abapgit_exception=>raise( |Necessary BW function modules not found or object not supported| ).
    ENDTRY.

    READ TABLE lt_return WITH KEY type = 'E' INTO ls_return.
    IF sy-subrc = 0.
      zcx_abapgit_exception=>raise( |Error when creating ODSO: { ls_return-message }| ).
    ENDIF.

    CALL FUNCTION 'BAPI_ODSO_ACTIVATE'
      EXPORTING
        odsobject = lv_dsonam
      TABLES
        return    = lt_return.

    READ TABLE lt_return WITH KEY type = 'E' INTO ls_return.
    IF sy-subrc = 0.
      zcx_abapgit_exception=>raise( |Error when activating ODSO: { ls_return-message }| ).
    ENDIF.

    tadir_insert( iv_package ).

    corr_insert( iv_package ).

  ENDMETHOD.

  METHOD zif_abapgit_object~exists.

    DATA: lv_iobjnm TYPE c LENGTH 30.

    SELECT SINGLE odsobject
    FROM ('RSDODSO')
    INTO lv_iobjnm
    WHERE odsobject = ms_item-obj_name.

    rv_bool = boolc( sy-subrc = 0 ).

  ENDMETHOD.

  METHOD zif_abapgit_object~get_comparator.
    RETURN.
  ENDMETHOD.

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

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

  METHOD zif_abapgit_object~is_active.

    DATA: lv_dsona TYPE c LENGTH 30,
          lo_odso  TYPE REF TO object,
          lv_isact TYPE abap_bool.

    lv_dsona = ms_item-obj_name.

    CALL METHOD ('CL_RSD_ODSO')=>('FACTORY')
      EXPORTING
        i_odsobject = lv_dsona
      RECEIVING
        r_r_odso    = lo_odso.

    CALL METHOD lo_odso->('IS_ACTIVE')
      RECEIVING
        r_is_active = lv_isact.

    rv_active = lv_isact.

  ENDMETHOD.

  METHOD zif_abapgit_object~is_locked.

    DATA: lv_object TYPE eqegraarg.

    lv_object =  ms_item-obj_name.
    OVERLAY lv_object WITH '                                          '.
    lv_object = lv_object && '*'.

    rv_is_locked = exists_a_lock_entry_for( iv_lock_object = 'RSD_S_PROV'
                                            iv_argument    = lv_object ).

  ENDMETHOD.

  METHOD zif_abapgit_object~jump.
    zcx_abapgit_exception=>raise( |Jump to ODSO is not yet supported| ).
  ENDMETHOD.

  METHOD zif_abapgit_object~serialize.

    DATA: lv_dsonam      TYPE c LENGTH 30,
          lr_details     TYPE REF TO data,
          lr_infoobjects TYPE REF TO data,
          lr_navigation  TYPE REF TO data,
          lr_indexes     TYPE REF TO data,
          lr_index_iobj  TYPE REF TO data,
          ls_return      TYPE bapiret2.

    FIELD-SYMBOLS:
      <lg_details>     TYPE any,
      <lt_infoobjects> TYPE STANDARD TABLE,
      <lt_navigation>  TYPE STANDARD TABLE,
      <lt_indexes>     TYPE STANDARD TABLE,
      <lt_index_iobj>  TYPE STANDARD TABLE.

    TRY.
        CREATE DATA lr_details     TYPE ('BAPI6116').
        CREATE DATA lr_infoobjects TYPE STANDARD TABLE OF ('BAPI6116IO').
        CREATE DATA lr_navigation  TYPE STANDARD TABLE OF ('BAPI6116NA').
        CREATE DATA lr_indexes     TYPE STANDARD TABLE OF ('BAPI6116IN').
        CREATE DATA lr_index_iobj  TYPE STANDARD TABLE OF ('BAPI6116II').
      CATCH cx_sy_create_data_error.
        zcx_abapgit_exception=>raise( |ODSO is not supported on this system| ).
    ENDTRY.

    ASSIGN lr_details->*     TO <lg_details>.
    ASSIGN lr_infoobjects->* TO <lt_infoobjects>.
    ASSIGN lr_navigation->*  TO <lt_navigation>.
    ASSIGN lr_indexes->*     TO <lt_indexes>.
    ASSIGN lr_index_iobj->*  TO <lt_index_iobj>.

    lv_dsonam = ms_item-obj_name.

    CALL FUNCTION 'BAPI_ODSO_GETDETAIL'
      EXPORTING
        odsobject            = lv_dsonam
      IMPORTING
        details              = <lg_details>
        return               = ls_return
      TABLES
        infoobjects          = <lt_infoobjects>
        navigationattributes = <lt_navigation>
        indexes              = <lt_indexes>
        indexesinfoobjects   = <lt_index_iobj>.

    IF ls_return-type = 'E'.
      zcx_abapgit_exception=>raise( |Error when geting details of ODSO: { ls_return-message }| ).
    ENDIF.

    clear_field( EXPORTING iv_fieldname = 'TSTPNM'
                 CHANGING  cg_metadata  = <lg_details> ).

    clear_field( EXPORTING iv_fieldname = 'TIMESTMP'
                 CHANGING  cg_metadata  = <lg_details> ).

    clear_field( EXPORTING iv_fieldname = 'CONTTIMESTMP'
                 CHANGING  cg_metadata  = <lg_details> ).

    clear_field( EXPORTING iv_fieldname = 'OWNER'
                 CHANGING  cg_metadata  = <lg_details> ).

    io_xml->add( iv_name = 'ODSO'
                 ig_data = <lg_details> ).

    io_xml->add( iv_name = 'INFOOBJECTS'
                 ig_data = <lt_infoobjects> ).

    io_xml->add( iv_name = 'NAVIGATION'
                 ig_data = <lt_navigation> ).

    io_xml->add( iv_name = 'INDEXES'
                 ig_data = <lt_indexes> ).

    io_xml->add( iv_name = 'INDEX_IOBJ'
                 ig_data = <lt_index_iobj> ).

  ENDMETHOD.

  METHOD clear_field.

    FIELD-SYMBOLS: <lg_field> TYPE data.

    ASSIGN COMPONENT iv_fieldname
           OF STRUCTURE cg_metadata
           TO <lg_field>.
    ASSERT sy-subrc = 0.

    CLEAR: <lg_field>.

  ENDMETHOD.
ENDCLASS.

ODSO: Replace exceptions that exist in SAP_BW only (#4694)

Closes #4692


*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_OBJECT_TOBJ
*&---------------------------------------------------------------------*

*----------------------------------------------------------------------*
*       CLASS lcl_object_tobj DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_object_tobj DEFINITION INHERITING FROM lcl_objects_super FINAL.

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

ENDCLASS.                    "lcl_object_tobj DEFINITION

*----------------------------------------------------------------------*
*       CLASS lcl_object_tobj IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_object_tobj IMPLEMENTATION.

  METHOD lif_object~has_changed_since.
    rv_changed = abap_true.
  ENDMETHOD.  "lif_object~has_changed_since

  METHOD lif_object~changed_by.
    rv_user = c_user_unknown. " todo
  ENDMETHOD.

  METHOD lif_object~get_metadata.
    rs_metadata = get_metadata( ).
    rs_metadata-late_deser = abap_true.
  ENDMETHOD.                    "lif_object~get_metadata

  METHOD lif_object~exists.

    DATA: lv_objectname TYPE objh-objectname,
          lv_type_pos   TYPE i.

    lv_type_pos = strlen( ms_item-obj_name ) - 1.

    SELECT SINGLE objectname FROM objh INTO lv_objectname
      WHERE objectname = ms_item-obj_name(lv_type_pos)
      AND objecttype = ms_item-obj_name+lv_type_pos.    "#EC CI_GENBUFF
    rv_bool = boolc( sy-subrc = 0 ).

  ENDMETHOD.                    "lif_object~exists

  METHOD lif_object~serialize.

    DATA: ls_objh     TYPE objh,
          ls_objt     TYPE objt,
          lt_objs     TYPE tt_objs,
          lt_objsl    TYPE tt_objsl,
          lt_objm     TYPE tt_objm,
          lv_type_pos TYPE i.

    lv_type_pos = strlen( ms_item-obj_name ) - 1.

    ls_objh-objectname = ms_item-obj_name(lv_type_pos).
    ls_objh-objecttype = ms_item-obj_name+lv_type_pos.

    CALL FUNCTION 'CTO_OBJECT_GET'
      EXPORTING
        iv_objectname      = ls_objh-objectname
        iv_objecttype      = ls_objh-objecttype
        iv_language        = mv_language
        iv_sel_objt        = abap_true
        iv_sel_objs        = abap_true
        iv_sel_objsl       = abap_true
        iv_sel_objm        = abap_true
      IMPORTING
        es_objh            = ls_objh
        es_objt            = ls_objt
      TABLES
        tt_objs            = lt_objs
        tt_objsl           = lt_objsl
        tt_objm            = lt_objm
      EXCEPTIONS
        object_not_defined = 1
        OTHERS             = 2.
    IF sy-subrc = 1.
      RETURN.
    ELSEIF sy-subrc <> 0.
      lcx_exception=>raise( 'error from CTO_OBJECT_GET' ).
    ENDIF.

    CLEAR: ls_objh-luser,
           ls_objh-ldate.

    io_xml->add( iv_name = 'OBJH'
                 ig_data = ls_objh ).
    io_xml->add( iv_name = 'OBJT'
                 ig_data = ls_objt ).
    io_xml->add( iv_name = 'OBJS'
                 ig_data = lt_objs ).
    io_xml->add( iv_name = 'OBJSL'
                 ig_data = lt_objsl ).
    io_xml->add( iv_name = 'OBJM'
                 ig_data = lt_objm ).

  ENDMETHOD.                    "serialize

  METHOD lif_object~deserialize.

    DATA: ls_objh  TYPE objh,
          ls_objt  TYPE objt,
          lt_objs  TYPE tt_objs,
          lt_objsl TYPE tt_objsl,
          lt_objm  TYPE tt_objm.

    io_xml->read( EXPORTING iv_name = 'OBJH'
                  CHANGING cg_data = ls_objh ).
    io_xml->read( EXPORTING iv_name = 'OBJT'
                  CHANGING cg_data = ls_objt ).
    io_xml->read( EXPORTING iv_name = 'OBJS'
                  CHANGING cg_data = lt_objs ).
    io_xml->read( EXPORTING iv_name = 'OBJSL'
                  CHANGING cg_data = lt_objsl ).
    io_xml->read( EXPORTING iv_name = 'OBJM'
                  CHANGING cg_data = lt_objm ).

    CALL FUNCTION 'OBJ_GENERATE'
      EXPORTING
        iv_objectname         = ls_objh-objectname
        iv_objecttype         = ls_objh-objecttype
        iv_maint_mode         = 'I'
        iv_objecttext         = ls_objt-ddtext
        iv_objcateg           = ls_objh-objcateg
        iv_objtransp          = ls_objh-objtransp
        iv_devclass           = iv_package
      TABLES
        tt_v_obj_s            = lt_objs
        tt_objm               = lt_objm
      EXCEPTIONS
        illegal_call          = 1
        object_not_found      = 2
        generate_error        = 3
        transport_error       = 4
        object_enqueue_failed = 5
        OTHERS                = 6.
    IF sy-subrc <> 0.
* TOBJ has to be saved/generated after the DDIC tables have been
* activated - fixed with late deserialization
      lcx_exception=>raise( 'error from OBJ_GENERATE' ).
    ENDIF.

  ENDMETHOD.                    "deserialize

  METHOD lif_object~delete.

    DATA: ls_objh     TYPE objh,
          lv_type_pos TYPE i.

    lv_type_pos = strlen( ms_item-obj_name ) - 1.

    ls_objh-objectname = ms_item-obj_name(lv_type_pos).
    ls_objh-objecttype = ms_item-obj_name+lv_type_pos.

    CALL FUNCTION 'OBJ_GENERATE'
      EXPORTING
        iv_objectname         = ls_objh-objectname
        iv_objecttype         = ls_objh-objecttype
        iv_maint_mode         = 'D'
      EXCEPTIONS
        illegal_call          = 1
        object_not_found      = 2
        generate_error        = 3
        transport_error       = 4
        object_enqueue_failed = 5
        OTHERS                = 6.
    IF sy-subrc <> 0.
      lcx_exception=>raise( 'error from OBJ_GENERATE' ).
    ENDIF.

  ENDMETHOD.                    "delete

  METHOD lif_object~jump.
    lcx_exception=>raise( 'todo, TOBJ jump' ).
  ENDMETHOD.                    "jump

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

ENDCLASS.                    "lcl_object_tobj IMPLEMENTATION

*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_OBJECT_TOBJ
*&---------------------------------------------------------------------*

*----------------------------------------------------------------------*
*       CLASS lcl_object_tobj DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_object_tobj DEFINITION INHERITING FROM lcl_objects_super FINAL.

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

ENDCLASS.                    "lcl_object_tobj DEFINITION

*----------------------------------------------------------------------*
*       CLASS lcl_object_tobj IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_object_tobj IMPLEMENTATION.

  METHOD lif_object~has_changed_since.
    rv_changed = abap_true.
  ENDMETHOD.  "lif_object~has_changed_since

  METHOD lif_object~changed_by.

    DATA: lv_type_pos TYPE i.

    lv_type_pos = strlen( ms_item-obj_name ) - 1.

    SELECT SINGLE luser FROM objh INTO rv_user
      WHERE objectname = ms_item-obj_name(lv_type_pos)
      AND objecttype = ms_item-obj_name+lv_type_pos.    "#EC CI_GENBUFF
    IF sy-subrc <> 0.
      rv_user = c_user_unknown.
    ENDIF.

  ENDMETHOD.

  METHOD lif_object~get_metadata.
    rs_metadata = get_metadata( ).
    rs_metadata-late_deser = abap_true.
  ENDMETHOD.                    "lif_object~get_metadata

  METHOD lif_object~exists.

    DATA: lv_objectname TYPE objh-objectname,
          lv_type_pos   TYPE i.

    lv_type_pos = strlen( ms_item-obj_name ) - 1.

    SELECT SINGLE objectname FROM objh INTO lv_objectname
      WHERE objectname = ms_item-obj_name(lv_type_pos)
      AND objecttype = ms_item-obj_name+lv_type_pos.    "#EC CI_GENBUFF
    rv_bool = boolc( sy-subrc = 0 ).

  ENDMETHOD.                    "lif_object~exists

  METHOD lif_object~serialize.

    DATA: ls_objh     TYPE objh,
          ls_objt     TYPE objt,
          lt_objs     TYPE tt_objs,
          lt_objsl    TYPE tt_objsl,
          lt_objm     TYPE tt_objm,
          lv_type_pos TYPE i.

    lv_type_pos = strlen( ms_item-obj_name ) - 1.

    ls_objh-objectname = ms_item-obj_name(lv_type_pos).
    ls_objh-objecttype = ms_item-obj_name+lv_type_pos.

    CALL FUNCTION 'CTO_OBJECT_GET'
      EXPORTING
        iv_objectname      = ls_objh-objectname
        iv_objecttype      = ls_objh-objecttype
        iv_language        = mv_language
        iv_sel_objt        = abap_true
        iv_sel_objs        = abap_true
        iv_sel_objsl       = abap_true
        iv_sel_objm        = abap_true
      IMPORTING
        es_objh            = ls_objh
        es_objt            = ls_objt
      TABLES
        tt_objs            = lt_objs
        tt_objsl           = lt_objsl
        tt_objm            = lt_objm
      EXCEPTIONS
        object_not_defined = 1
        OTHERS             = 2.
    IF sy-subrc = 1.
      RETURN.
    ELSEIF sy-subrc <> 0.
      lcx_exception=>raise( 'error from CTO_OBJECT_GET' ).
    ENDIF.

    CLEAR: ls_objh-luser,
           ls_objh-ldate.

    io_xml->add( iv_name = 'OBJH'
                 ig_data = ls_objh ).
    io_xml->add( iv_name = 'OBJT'
                 ig_data = ls_objt ).
    io_xml->add( iv_name = 'OBJS'
                 ig_data = lt_objs ).
    io_xml->add( iv_name = 'OBJSL'
                 ig_data = lt_objsl ).
    io_xml->add( iv_name = 'OBJM'
                 ig_data = lt_objm ).

  ENDMETHOD.                    "serialize

  METHOD lif_object~deserialize.

    DATA: ls_objh  TYPE objh,
          ls_objt  TYPE objt,
          lt_objs  TYPE tt_objs,
          lt_objsl TYPE tt_objsl,
          lt_objm  TYPE tt_objm.

    io_xml->read( EXPORTING iv_name = 'OBJH'
                  CHANGING cg_data = ls_objh ).
    io_xml->read( EXPORTING iv_name = 'OBJT'
                  CHANGING cg_data = ls_objt ).
    io_xml->read( EXPORTING iv_name = 'OBJS'
                  CHANGING cg_data = lt_objs ).
    io_xml->read( EXPORTING iv_name = 'OBJSL'
                  CHANGING cg_data = lt_objsl ).
    io_xml->read( EXPORTING iv_name = 'OBJM'
                  CHANGING cg_data = lt_objm ).

    CALL FUNCTION 'OBJ_GENERATE'
      EXPORTING
        iv_objectname         = ls_objh-objectname
        iv_objecttype         = ls_objh-objecttype
        iv_maint_mode         = 'I'
        iv_objecttext         = ls_objt-ddtext
        iv_objcateg           = ls_objh-objcateg
        iv_objtransp          = ls_objh-objtransp
        iv_devclass           = iv_package
      TABLES
        tt_v_obj_s            = lt_objs
        tt_objm               = lt_objm
      EXCEPTIONS
        illegal_call          = 1
        object_not_found      = 2
        generate_error        = 3
        transport_error       = 4
        object_enqueue_failed = 5
        OTHERS                = 6.
    IF sy-subrc <> 0.
* TOBJ has to be saved/generated after the DDIC tables have been
* activated - fixed with late deserialization
      lcx_exception=>raise( 'error from OBJ_GENERATE' ).
    ENDIF.

  ENDMETHOD.                    "deserialize

  METHOD lif_object~delete.

    DATA: ls_objh     TYPE objh,
          lv_type_pos TYPE i.

    lv_type_pos = strlen( ms_item-obj_name ) - 1.

    ls_objh-objectname = ms_item-obj_name(lv_type_pos).
    ls_objh-objecttype = ms_item-obj_name+lv_type_pos.

    CALL FUNCTION 'OBJ_GENERATE'
      EXPORTING
        iv_objectname         = ls_objh-objectname
        iv_objecttype         = ls_objh-objecttype
        iv_maint_mode         = 'D'
      EXCEPTIONS
        illegal_call          = 1
        object_not_found      = 2
        generate_error        = 3
        transport_error       = 4
        object_enqueue_failed = 5
        OTHERS                = 6.
    IF sy-subrc <> 0.
      lcx_exception=>raise( 'error from OBJ_GENERATE' ).
    ENDIF.

  ENDMETHOD.                    "delete

  METHOD lif_object~jump.
    lcx_exception=>raise( 'todo, TOBJ jump' ).
  ENDMETHOD.                    "jump

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

ENDCLASS.                    "lcl_object_tobj IMPLEMENTATION

fix changed_by

TOBJ: fix changed_by