CLASS zcl_abapgit_object_sfpf 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 .

    CLASS-METHODS fix_oref
      IMPORTING
        !ii_document TYPE REF TO if_ixml_document
      RAISING
        zcx_abapgit_exception .
  PROTECTED SECTION.
  PRIVATE SECTION.

    CONSTANTS c_layout_file_ext TYPE string VALUE 'xdp'.

    METHODS:
      load
        RETURNING VALUE(ri_wb_form) TYPE REF TO if_fp_wb_form
        RAISING   zcx_abapgit_exception,
      form_to_xstring
        RETURNING VALUE(rv_xstr) TYPE xstring
        RAISING   zcx_abapgit_exception.

ENDCLASS.

CLASS ZCL_ABAPGIT_OBJECT_SFPF IMPLEMENTATION.

  METHOD fix_oref.

* During serialization of a SFPF / SFPI object the interface hierarchy
* is represented by attributes "id" and "href", where the id looks
* like "o<number>" and href like "#o<number>". Every run of
* serialization generates a new <number> in these  attributes, that
* leads to differences even by comparing of untouched forms.
* The purpose of this method is to renumber the id's consequentially
* and therefore to avoid fictive differences.

* NB: As the method iterator->get_next() works quite slowly,
*     it is better to collect all attributes in a cache table
*     instead of implementing of a nested loop using get_next().

    DATA:
      li_iterator TYPE REF TO if_ixml_node_iterator,
      li_elem     TYPE REF TO if_ixml_element,
      lv_new      TYPE string,
      lv_old      TYPE string,
      lv_count    TYPE i,
      BEGIN OF ls_attr_href,
        val  TYPE string,
        attr TYPE REF TO if_ixml_attribute,
      END OF ls_attr_href,
      lt_attr_href LIKE SORTED TABLE OF ls_attr_href WITH NON-UNIQUE KEY val.

    FIELD-SYMBOLS <ls_attr_href> LIKE LINE OF lt_attr_href.

*   Collect all attributes href='#o...' in the cache table
    li_iterator = ii_document->create_iterator_filtered(
      ii_document->create_filter_and(
        filter1 = ii_document->create_filter_node_type( if_ixml_node=>co_node_element )
        filter2 = ii_document->create_filter_attribute( 'href' ) ) ).
    li_elem ?= li_iterator->get_next( ).
    WHILE li_elem IS NOT INITIAL.
      ls_attr_href-attr = li_elem->get_attribute_node( 'href' ).
      ls_attr_href-val = ls_attr_href-attr->get_value( ).
      IF ls_attr_href-val CP '##o*'.
        INSERT ls_attr_href INTO TABLE lt_attr_href.
      ENDIF.
      li_elem ?= li_iterator->get_next( ).
    ENDWHILE.

*   Renumber id='o...' attributes
    li_iterator = ii_document->create_iterator_filtered(
      ii_document->create_filter_and(
        filter1 = ii_document->create_filter_node_type( if_ixml_node=>co_node_element )
        filter2 = ii_document->create_filter_attribute( 'id' ) ) ).
    li_elem ?= li_iterator->get_next( ).
    WHILE li_elem IS NOT INITIAL.
      lv_old = li_elem->get_attribute( 'id' ).
      IF lv_old CP 'o*'.
        lv_count = lv_count + 1.
        lv_new = |o{ lv_count }|.
*       Rewrite id
        IF li_elem->set_attribute( name = 'id'
                                   value = lv_new ) IS NOT INITIAL.
          zcx_abapgit_exception=>raise( 'SFPF error, FIX_OREF' ).
        ENDIF.
*       Update references
        LOOP AT lt_attr_href ASSIGNING <ls_attr_href> WHERE val = '#' && lv_old.
          IF <ls_attr_href>-attr->set_value( '#' && lv_new ) IS NOT INITIAL.
            zcx_abapgit_exception=>raise( 'SFPF error, FIX_OREF' ).
          ENDIF.
        ENDLOOP.
      ENDIF.
      li_elem ?= li_iterator->get_next( ).
    ENDWHILE.

  ENDMETHOD.

  METHOD form_to_xstring.

    CONSTANTS: lc_empty_data TYPE xstring VALUE ''.

    DATA: li_fp_form     TYPE REF TO if_fp_form,
          li_wb_form     TYPE REF TO if_fp_wb_form,
          li_fp_layout   TYPE REF TO if_fp_layout,
          lx_fp_err      TYPE REF TO cx_fp_api,
          lx_fp_conv_err TYPE REF TO cx_fp_api,
          lv_layout_data TYPE xstring.

    li_wb_form = load( ).
    li_fp_form ?= li_wb_form->get_object( ).
    li_fp_layout = li_fp_form->get_layout( ).
    lv_layout_data = li_fp_layout->get_layout_data( ).

    mo_files->add_raw( iv_ext = c_layout_file_ext
                       iv_data = lv_layout_data ).

    TRY.
        li_fp_layout->set_layout_data( i_layout_data   = lc_empty_data
                                       i_set_xliff_ids = abap_false ).
      CATCH cx_fp_api INTO lx_fp_err.
        zcx_abapgit_exception=>raise( |SFPF remove layout: { lx_fp_err->get_text( ) }| ).
    ENDTRY.

    TRY.
        rv_xstr = cl_fp_helper=>convert_form_to_xstring( li_fp_form ).
      CATCH cx_fp_api INTO lx_fp_conv_err.
        " Pass - the exception is handled below!
    ENDTRY.

    TRY.
        li_fp_layout->set_layout_data( i_layout_data   = lv_layout_data
                                       i_set_xliff_ids = abap_false ).
      CATCH cx_fp_api INTO lx_fp_err.
        " Be aware that there might be another exception
        " raised by cl_fp_helper=>convert_form_to_xstring( )
        zcx_abapgit_exception=>raise( |SFPF recover layout: { lx_fp_err->get_text( ) }| ).
    ENDTRY.

    IF lx_fp_conv_err IS BOUND.
      " This statement handles the exception raised from cl_fp_helper=>convert_form_to_xstring( )
      zcx_abapgit_exception=>raise( |SFPF convert_form_to_xstring: { lx_fp_conv_err->get_text( ) }| ).
    ENDIF.
  ENDMETHOD.

  METHOD load.

    DATA: lv_name TYPE fpname.

    lv_name = ms_item-obj_name.

    TRY.
        ri_wb_form = cl_fp_wb_form=>load( lv_name ).
      CATCH cx_fp_api.
        zcx_abapgit_exception=>raise( 'SFPF error, load' ).
    ENDTRY.

  ENDMETHOD.

  METHOD zif_abapgit_object~changed_by.

    SELECT SINGLE lastuser FROM fplayout
      INTO rv_user
      WHERE name = ms_item-obj_name
      AND state = 'A'.
    IF rv_user IS INITIAL.
      SELECT SINGLE firstuser FROM fplayout
        INTO rv_user
        WHERE name = ms_item-obj_name
        AND state = 'A'.
    ENDIF.
    IF rv_user IS INITIAL.
      rv_user = c_user_unknown.
    ENDIF.

  ENDMETHOD.

  METHOD zif_abapgit_object~delete.

    DATA: lv_name    TYPE fpname,
          lo_wb_form TYPE REF TO cl_fp_wb_form.

    lo_wb_form ?= load( ).

    lv_name = ms_item-obj_name.

    TRY.
        lo_wb_form->delete( lv_name ).
      CATCH cx_fp_api.
        zcx_abapgit_exception=>raise( 'SFPI error, delete' ).
    ENDTRY.

  ENDMETHOD.

  METHOD zif_abapgit_object~deserialize.

    DATA: lv_xstr      TYPE xstring,
          lv_layout    TYPE xstring,
          lv_name      TYPE fpname,
          li_wb_object TYPE REF TO if_fp_wb_form,
          li_form      TYPE REF TO if_fp_form,
          lx_fp_err    TYPE REF TO cx_fp_api.

    lv_name = ms_item-obj_name.
    lv_xstr = cl_ixml_80_20=>render_to_xstring( io_xml->get_raw( ) ).

    TRY.
        li_form = cl_fp_helper=>convert_xstring_to_form( lv_xstr ).

        IF mo_files->contains( c_layout_file_ext ) = abap_true.
          lv_layout = mo_files->read_raw( c_layout_file_ext ).
          li_form->get_layout( )->set_layout_data( lv_layout ).
        ENDIF.

        IF zif_abapgit_object~exists( ) = abap_true.
          cl_fp_wb_form=>delete( lv_name ).
        ENDIF.

        tadir_insert( iv_package ).
        li_wb_object = cl_fp_wb_form=>create( i_name = lv_name
                                              i_form = li_form ).
        li_wb_object->save( ).
        li_wb_object->free( ).
      CATCH cx_fp_api INTO lx_fp_err.
        zcx_abapgit_exception=>raise( |SFPF deserialization error: { lx_fp_err->get_text( ) }| ).
    ENDTRY.

    zcl_abapgit_objects_activation=>add_item( ms_item ).

  ENDMETHOD.

  METHOD zif_abapgit_object~exists.

    DATA: lv_name TYPE fpname.

    SELECT SINGLE name FROM fplayout
      INTO lv_name
      WHERE name = ms_item-obj_name
      AND state = 'A'.
    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.
    rv_active = is_active( ).
  ENDMETHOD.

  METHOD zif_abapgit_object~is_locked.

    DATA: lv_object TYPE seqg3-garg.

    lv_object = |{ ms_item-obj_name }|.
    OVERLAY lv_object WITH '                              '.
    lv_object = lv_object && '*'.

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

  ENDMETHOD.

  METHOD zif_abapgit_object~jump.

    CALL FUNCTION 'RS_TOOL_ACCESS'
      EXPORTING
        operation   = 'SHOW'
        object_name = ms_item-obj_name
        object_type = ms_item-obj_type.

  ENDMETHOD.

  METHOD zif_abapgit_object~serialize.

    DATA: lv_xstr     TYPE xstring,
          li_document TYPE REF TO if_ixml_document.

    lv_xstr = form_to_xstring( ).
    li_document = cl_ixml_80_20=>parse_to_document( stream_xstring = lv_xstr ).
    fix_oref( li_document ).
    io_xml->set_raw( li_document->get_root_element( ) ).

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_object_sfpf 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 .

    CLASS-METHODS fix_oref
      IMPORTING
        !ii_document TYPE REF TO if_ixml_document
      RAISING
        zcx_abapgit_exception .
  PROTECTED SECTION.
  PRIVATE SECTION.

    CONSTANTS c_layout_file_ext TYPE string VALUE 'xdp'.

    METHODS:
      load
        RETURNING VALUE(ri_wb_form) TYPE REF TO if_fp_wb_form
        RAISING   zcx_abapgit_exception,
      form_to_xstring
        RETURNING VALUE(rv_xstr) TYPE xstring
        RAISING   zcx_abapgit_exception.

ENDCLASS.

CLASS ZCL_ABAPGIT_OBJECT_SFPF IMPLEMENTATION.

  METHOD fix_oref.

* During serialization of a SFPF / SFPI object the interface hierarchy
* is represented by attributes "id" and "href", where the id looks
* like "o<number>" and href like "#o<number>". Every run of
* serialization generates a new <number> in these  attributes, that
* leads to differences even by comparing of untouched forms.
* The purpose of this method is to renumber the id's consequentially
* and therefore to avoid fictive differences.

* NB: As the method iterator->get_next() works quite slowly,
*     it is better to collect all attributes in a cache table
*     instead of implementing of a nested loop using get_next().

    DATA:
      li_iterator TYPE REF TO if_ixml_node_iterator,
      li_elem     TYPE REF TO if_ixml_element,
      lv_new      TYPE string,
      lv_old      TYPE string,
      lv_count    TYPE i,
      BEGIN OF ls_attr_href,
        val  TYPE string,
        attr TYPE REF TO if_ixml_attribute,
      END OF ls_attr_href,
      lt_attr_href LIKE SORTED TABLE OF ls_attr_href WITH NON-UNIQUE KEY val.

    FIELD-SYMBOLS <ls_attr_href> LIKE LINE OF lt_attr_href.

*   Collect all attributes href='#o...' in the cache table
    li_iterator = ii_document->create_iterator_filtered(
      ii_document->create_filter_and(
        filter1 = ii_document->create_filter_node_type( if_ixml_node=>co_node_element )
        filter2 = ii_document->create_filter_attribute( 'href' ) ) ).
    li_elem ?= li_iterator->get_next( ).
    WHILE li_elem IS NOT INITIAL.
      ls_attr_href-attr = li_elem->get_attribute_node( 'href' ).
      ls_attr_href-val = ls_attr_href-attr->get_value( ).
      IF ls_attr_href-val CP '##o*'.
        INSERT ls_attr_href INTO TABLE lt_attr_href.
      ENDIF.
      li_elem ?= li_iterator->get_next( ).
    ENDWHILE.

*   Renumber id='o...' attributes
    li_iterator = ii_document->create_iterator_filtered(
      ii_document->create_filter_and(
        filter1 = ii_document->create_filter_node_type( if_ixml_node=>co_node_element )
        filter2 = ii_document->create_filter_attribute( 'id' ) ) ).
    li_elem ?= li_iterator->get_next( ).
    WHILE li_elem IS NOT INITIAL.
      lv_old = li_elem->get_attribute( 'id' ).
      IF lv_old CP 'o*'.
        lv_count = lv_count + 1.
        lv_new = |o{ lv_count }|.
*       Rewrite id
        IF li_elem->set_attribute( name = 'id'
                                   value = lv_new ) IS NOT INITIAL.
          zcx_abapgit_exception=>raise( 'SFPF error, FIX_OREF' ).
        ENDIF.
*       Update references
        LOOP AT lt_attr_href ASSIGNING <ls_attr_href> WHERE val = '#' && lv_old.
          IF <ls_attr_href>-attr->set_value( '#' && lv_new ) IS NOT INITIAL.
            zcx_abapgit_exception=>raise( 'SFPF error, FIX_OREF' ).
          ENDIF.
        ENDLOOP.
      ENDIF.
      li_elem ?= li_iterator->get_next( ).
    ENDWHILE.

  ENDMETHOD.

  METHOD form_to_xstring.

    CONSTANTS: lc_empty_data TYPE xstring VALUE ''.

    DATA: li_fp_form     TYPE REF TO if_fp_form,
          li_wb_form     TYPE REF TO if_fp_wb_form,
          li_fp_layout   TYPE REF TO if_fp_layout,
          lx_fp_err      TYPE REF TO cx_fp_api,
          lx_fp_conv_err TYPE REF TO cx_fp_api,
          lv_layout_data TYPE xstring.

    li_wb_form = load( ).
    li_fp_form ?= li_wb_form->get_object( ).
    li_fp_layout = li_fp_form->get_layout( ).
    lv_layout_data = li_fp_layout->get_layout_data( ).

    mo_files->add_raw( iv_ext = c_layout_file_ext
                       iv_data = lv_layout_data ).

    TRY.
        li_fp_layout->set_layout_data( i_layout_data   = lc_empty_data
                                       i_set_xliff_ids = abap_false ).
      CATCH cx_fp_api INTO lx_fp_err.
        zcx_abapgit_exception=>raise( |SFPF remove layout: { lx_fp_err->get_text( ) }| ).
    ENDTRY.

    TRY.
        rv_xstr = cl_fp_helper=>convert_form_to_xstring( li_fp_form ).
      CATCH cx_fp_api INTO lx_fp_conv_err.
        " Pass - the exception is handled below!
    ENDTRY.

    TRY.
        li_fp_layout->set_layout_data( i_layout_data   = lv_layout_data
                                       i_set_xliff_ids = abap_false ).
      CATCH cx_fp_api INTO lx_fp_err.
        " Be aware that there might be another exception
        " raised by cl_fp_helper=>convert_form_to_xstring( )
        zcx_abapgit_exception=>raise( |SFPF recover layout: { lx_fp_err->get_text( ) }| ).
    ENDTRY.

    IF lx_fp_conv_err IS BOUND.
      " This statement handles the exception raised from cl_fp_helper=>convert_form_to_xstring( )
      zcx_abapgit_exception=>raise( |SFPF convert_form_to_xstring: { lx_fp_conv_err->get_text( ) }| ).
    ENDIF.
  ENDMETHOD.

  METHOD load.

    DATA: lv_name TYPE fpname.

    lv_name = ms_item-obj_name.

    TRY.
        ri_wb_form = cl_fp_wb_form=>load( lv_name ).
      CATCH cx_fp_api.
        zcx_abapgit_exception=>raise( 'SFPF error, load' ).
    ENDTRY.

  ENDMETHOD.

  METHOD zif_abapgit_object~changed_by.

    SELECT SINGLE lastuser FROM fplayout
      INTO rv_user
      WHERE name = ms_item-obj_name
      AND state = 'A'.
    IF rv_user IS INITIAL.
      SELECT SINGLE firstuser FROM fplayout
        INTO rv_user
        WHERE name = ms_item-obj_name
        AND state = 'A'.
    ENDIF.
    IF rv_user IS INITIAL.
      rv_user = c_user_unknown.
    ENDIF.

  ENDMETHOD.

  METHOD zif_abapgit_object~delete.

    DATA: lv_name    TYPE fpname,
          lo_wb_form TYPE REF TO cl_fp_wb_form.

    lo_wb_form ?= load( ).

    lv_name = ms_item-obj_name.

    TRY.
        lo_wb_form->delete( lv_name ).
      CATCH cx_fp_api.
        zcx_abapgit_exception=>raise( 'SFPI error, delete' ).
    ENDTRY.

  ENDMETHOD.

  METHOD zif_abapgit_object~deserialize.

    DATA: lv_xstr      TYPE xstring,
          lv_layout    TYPE xstring,
          lv_name      TYPE fpname,
          li_wb_object TYPE REF TO if_fp_wb_form,
          li_form      TYPE REF TO if_fp_form,
          lx_fp_err    TYPE REF TO cx_fp_api.

    lv_name = ms_item-obj_name.
    lv_xstr = cl_ixml_80_20=>render_to_xstring( io_xml->get_raw( ) ).

    TRY.
        li_form = cl_fp_helper=>convert_xstring_to_form( lv_xstr ).

        IF mo_files->contains( c_layout_file_ext ) = abap_true.
          lv_layout = mo_files->read_raw( c_layout_file_ext ).
          li_form->get_layout( )->set_layout_data( lv_layout ).
        ENDIF.

        IF zif_abapgit_object~exists( ) = abap_true.
          cl_fp_wb_form=>delete( lv_name ).
        ENDIF.

        tadir_insert( iv_package ).
        li_wb_object = cl_fp_wb_form=>create( i_name = lv_name
                                              i_form = li_form ).
        li_wb_object->save( ).
        li_wb_object->free( ).
      CATCH cx_fp_api INTO lx_fp_err.
        zcx_abapgit_exception=>raise( |SFPF deserialization error: { lx_fp_err->get_text( ) }| ).
    ENDTRY.

    zcl_abapgit_objects_activation=>add_item( ms_item ).

  ENDMETHOD.

  METHOD zif_abapgit_object~exists.

    DATA: lv_name TYPE fpname.

    SELECT SINGLE name FROM fplayout
      INTO lv_name
      WHERE name = ms_item-obj_name
      AND state = 'A'.
    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.
    rv_active = is_active( ).
  ENDMETHOD.

  METHOD zif_abapgit_object~is_locked.

    DATA: lv_object TYPE seqg3-garg.

    lv_object = |{ ms_item-obj_name }|.
    OVERLAY lv_object WITH '                              '.
    lv_object = lv_object && '*'.

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

  ENDMETHOD.

  METHOD zif_abapgit_object~jump.

    CALL FUNCTION 'RS_TOOL_ACCESS'
      EXPORTING
        operation   = 'SHOW'
        object_name = ms_item-obj_name
        object_type = ms_item-obj_type.

  ENDMETHOD.

  METHOD zif_abapgit_object~serialize.

    DATA: lv_xstr            TYPE xstring,
          li_document        TYPE REF TO if_ixml_document,
          li_node_collection TYPE REF TO if_ixml_node_collection,
          li_node_iter       TYPE REF TO if_ixml_node_iterator,
          li_node            TYPE REF TO if_ixml_node,
          li_node_new        TYPE REF TO if_ixml_node,
          li_node_parent     TYPE REF TO if_ixml_node.

    lv_xstr = form_to_xstring( ).
    li_document = cl_ixml_80_20=>parse_to_document( stream_xstring = lv_xstr ).

*   Clear CACHE_INFO
    li_node_collection = li_document->get_elements_by_tag_name_ns( 'CACHE_INFO' ).
    IF li_node_collection IS NOT INITIAL.
      li_node_iter = li_node_collection->create_iterator( ).
      DO.
        li_node = li_node_iter->get_next( ).
        IF li_node IS INITIAL.
          EXIT.
        ENDIF.
        li_node_new = li_document->create_element_ns( 'CACHE_INFO' ).
        li_node_parent = li_node->get_parent( ).
        li_node_parent->replace_child( new_child = li_node_new
                                       old_child = li_node ).
      ENDDO.
    ENDIF.

    fix_oref( li_document ).
    io_xml->set_raw( li_document->get_root_element( ) ).

  ENDMETHOD.
ENDCLASS.

Fix for SFPF Clear CACHE_INFO (#3954)

Fix for SFPF Clear CACHE_INFO (#3954)

* Clear CACHE_INFO

#3868

* Indentation corrections

* Add Spaces for Parameters

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


*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_SERVICES_DB
*&---------------------------------------------------------------------*

CLASS lcl_services_db DEFINITION FINAL.
  PUBLIC SECTION.

    CLASS-METHODS delete
      IMPORTING is_key TYPE lcl_persistence_db=>ty_content
      RAISING   lcx_exception lcx_cancel.

    CLASS-METHODS update
      IMPORTING is_content TYPE lcl_persistence_db=>ty_content
      RAISING   lcx_exception.

ENDCLASS. "lcl_services_db

CLASS lcl_services_db IMPLEMENTATION.

  METHOD delete.

    DATA: lv_answer TYPE c LENGTH 1.

    ASSERT is_key-type IS NOT INITIAL.
    ASSERT is_key-value IS NOT INITIAL.

    lv_answer = lcl_popups=>popup_to_confirm(
      titlebar              = 'Warning'
      text_question         = 'Delete?'
      text_button_1         = 'Ok'
      icon_button_1         = 'ICON_DELETE'
      text_button_2         = 'Cancel'
      icon_button_2         = 'ICON_CANCEL'
      default_button        = '2'
      display_cancel_button = abap_false ).                 "#EC NOTEXT

    IF lv_answer = '2'.
      RAISE EXCEPTION TYPE lcx_cancel.
    ENDIF.

    lcl_app=>db( )->delete(
      iv_type  = is_key-type
      iv_value = is_key-value ).

    COMMIT WORK.

  ENDMETHOD.  " delete

  METHOD update.

    ASSERT is_content-type IS NOT INITIAL.
    ASSERT is_content-value IS NOT INITIAL.

    lcl_app=>db( )->update(
      iv_type  = is_content-type
      iv_value = is_content-value
      iv_data  = is_content-data_str ).

    COMMIT WORK.

  ENDMETHOD.  "update

ENDCLASS. "lcl_services_db

*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_SERVICES_DB
*&---------------------------------------------------------------------*

CLASS lcl_services_db DEFINITION FINAL.
  PUBLIC SECTION.

    CLASS-METHODS delete
      IMPORTING is_key TYPE lcl_persistence_db=>ty_content
      RAISING   lcx_exception lcx_cancel.

    CLASS-METHODS update
      IMPORTING is_content TYPE lcl_persistence_db=>ty_content
      RAISING   lcx_exception.

ENDCLASS. "lcl_services_db

CLASS lcl_services_db IMPLEMENTATION.

  METHOD delete.

    DATA: lv_answer TYPE c LENGTH 1.

    ASSERT is_key-type IS NOT INITIAL.
    ASSERT is_key-value IS NOT INITIAL.

    lv_answer = lcl_popups=>popup_to_confirm(
      titlebar              = 'Warning'
      text_question         = 'Delete?'
      text_button_1         = 'Ok'
      icon_button_1         = 'ICON_DELETE'
      text_button_2         = 'Cancel'
      icon_button_2         = 'ICON_CANCEL'
      default_button        = '2'
      display_cancel_button = abap_false ).                 "#EC NOTEXT

    IF lv_answer = '2'.
      RAISE EXCEPTION TYPE lcx_cancel.
    ENDIF.

    lcl_app=>db( )->delete(
      iv_type  = is_key-type
      iv_value = is_key-value ).

    COMMIT WORK.

  ENDMETHOD.  " delete

  METHOD update.

    ASSERT is_content-type IS NOT INITIAL.

    lcl_app=>db( )->update(
      iv_type  = is_content-type
      iv_value = is_content-value
      iv_data  = is_content-data_str ).

    COMMIT WORK.

  ENDMETHOD.  "update

ENDCLASS. "lcl_services_db

Remove unnecessary ASSERT

Remove unnecessary ASSERT

#828


*&---------------------------------------------------------------------*
*& Report  ZUSR_LISP_REPL
*& https://github.com/mydoghasworms/abap-lisp
*& Simple REPL for Lisp Interpreter written in ABAP
*& Martin Ceronio, June 2015
*& martin.ceronio@infosize.co.za
*&---------------------------------------------------------------------*

report  zusr_lisp_repl line-size 999.

include zlib_lisp.

data: lr_int type ref to lcl_lisp_interpreter. "The Lisp interpreter

parameters: input type string lower case.
parameters: output type string lower case.

at selection-screen output.

* Make result field output-only
  loop at screen.
    if screen-name = 'OUTPUT'.
      screen-input = 0.
      modify screen.
    endif.
  endloop.

at selection-screen.
* Initialize interpreter if not done yet
  if lr_int is not bound.
    create object lr_int.
  endif.

* Evaluate given code
  output = lr_int->eval_source( input ).
  clear input.

load-of-program.
* Hitting execute gets us back to this event and initializes the interpreter,
* so we preferably want to avoid that happening inadvertently:
  perform insert_into_excl(rsdbrunt) using: 'ONLI', 'SPOS', 'PRIN', 'SJOB'.

*&---------------------------------------------------------------------*
*& Report  ZUSR_LISP_REPL
*& https://github.com/mydoghasworms/abap-lisp
*& Simple REPL for Lisp Interpreter written in ABAP
*& Martin Ceronio, June 2015
*& martin.ceronio@infosize.co.za
*&---------------------------------------------------------------------*

report  zusr_lisp_repl line-size 999.

include zlib_lisp.

data: lr_int type ref to lcl_lisp_interpreter. "The Lisp interpreter

data: rt_begin type i.
data: rt_end   type i.

parameters: input type string lower case.
parameters: output type string lower case.
parameters: runtime type string lower case.

at selection-screen output.

* Make result field output-only
  loop at screen.
    if screen-name = 'OUTPUT' or screen-name = 'RUNTIME'.
      screen-input = 0.
      if screen-name = 'RUNTIME'.
        screen-display_3d = 0.
      endif.
      modify screen.
    endif.
  endloop.

at selection-screen.
* Initialize interpreter if not done yet
  if lr_int is not bound.
    create object lr_int.
  endif.

* Evaluate given code
  get RUN TIME FIELD rt_begin.
  output = lr_int->eval_source( input ).
  get RUN TIME FIELD rt_end.
  clear input.
  runtime = |{ rt_end - rt_begin } microseconds|.

load-of-program.
* Hitting execute gets us back to this event and initializes the interpreter,
* so we preferably want to avoid that happening inadvertently:
  perform insert_into_excl(rsdbrunt) using: 'ONLI', 'SPOS', 'PRIN', 'SJOB'.

Add runtime measurement to REPL

Add runtime measurement to REPL


CLASS zcl_abapgit_gui_page_repo_over DEFINITION
  PUBLIC
  INHERITING FROM zcl_abapgit_gui_page
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    METHODS constructor .
    METHODS zif_abapgit_gui_event_handler~on_event
        REDEFINITION .

  PROTECTED SECTION.
    METHODS:
      render_content REDEFINITION,
      scripts REDEFINITION.

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

    DATA:
      mv_order_by         TYPE string,
      mv_order_descending TYPE abap_bool,
      mv_filter           TYPE string,
      mv_time_zone        TYPE timezone.

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

      parse_filter
        IMPORTING
          it_postdata TYPE cnht_post_data_tab,

      apply_filter
        CHANGING
          ct_overview TYPE tty_overview,

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

      render_table_header
        IMPORTING
          io_html TYPE REF TO zcl_abapgit_html,

      render_table
        IMPORTING
          io_html     TYPE REF TO zcl_abapgit_html
          it_overview TYPE tty_overview,

      render_table_body
        IMPORTING
          io_html     TYPE REF TO zcl_abapgit_html
          it_overview TYPE tty_overview,

      render_header_bar
        IMPORTING
          io_html TYPE REF TO zcl_abapgit_html,

      apply_order_by
        CHANGING ct_overview TYPE tty_overview.

ENDCLASS.

CLASS zcl_abapgit_gui_page_repo_over IMPLEMENTATION.

  METHOD apply_filter.

    IF mv_filter IS NOT INITIAL.

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

    ENDIF.

  ENDMETHOD.

  METHOD constructor.

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

    CALL FUNCTION 'GET_SYSTEM_TIMEZONE'
      IMPORTING
        timezone            = mv_time_zone
      EXCEPTIONS
        customizing_missing = 1
        OTHERS              = 2.
    ASSERT sy-subrc = 0.

  ENDMETHOD.

  METHOD map_repo_list_to_overview.

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

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

    LOOP AT it_repo_list ASSIGNING <ls_repo>.

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

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

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

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

      ls_overview-deserialized_by = <ls_repo>-deserialized_by.

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

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

      INSERT ls_overview INTO TABLE rt_overview.

    ENDLOOP.

  ENDMETHOD.

  METHOD parse_filter.

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

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

    mv_filter = condense( mv_filter ).

  ENDMETHOD.

  METHOD render_content.

    DATA: lt_overview TYPE tty_overview.

    lt_overview = map_repo_list_to_overview( zcl_abapgit_persist_factory=>get_repo( )->list( ) ).

    apply_order_by( CHANGING ct_overview = lt_overview ).

    apply_filter( CHANGING ct_overview = lt_overview ).

    CREATE OBJECT ro_html.

    render_header_bar( ro_html ).
    render_table( io_html     = ro_html
                  it_overview = lt_overview ).

  ENDMETHOD.

  METHOD render_header_bar.

    io_html->add( |
| ).

    io_html->add( |<form class="inline" method="post" action="sapevent:{ c_action-apply_filter }">| ).

    io_html->add( render_text_input(
      iv_name  = |filter|
      iv_label = |Filter: |
      iv_value = mv_filter ) ).
    io_html->add( |<input type="submit" class="hidden-submit">| ).
    io_html->add( |</form>| ).

    io_html->add( zcl_abapgit_html=>a(
      iv_txt = 'Toggle detail'
      iv_act = |gHelper.toggleRepoListDetail()|
      iv_typ = zif_abapgit_html=>c_action_type-onclick ) ).

    io_html->add( |
| ).

  ENDMETHOD.

  METHOD render_table.

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

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

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

  ENDMETHOD.

  METHOD render_table_body.

    DATA:
      lv_type_icon     TYPE string,
      lv_favorite_icon TYPE string.

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

    io_html->add( '
' ).

    LOOP AT it_overview ASSIGNING <ls_overview>.

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

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

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

      io_html->add( |{ zcl_abapgit_html=>a( iv_txt = <ls_overview>-name
                                                iv_act = |{ c_action-select }?{ <ls_overview>-key }| ) }	| ).

      IF <ls_overview>-type = abap_false.
        io_html->add( |{ io_html->a( iv_txt = <ls_overview>-url
                                         iv_act = |{ zif_abapgit_definitions=>c_action-url }?|
                                               && |{ <ls_overview>-url }| ) }	| ).
      ELSE.
        io_html->add( |	| ).
      ENDIF.

      io_html->add( |{ <ls_overview>-package }	| ).
      io_html->add( |{ <ls_overview>-branch }	| ).
      io_html->add( |{ <ls_overview>-deserialized_by }	| ).
      io_html->add( |{ <ls_overview>-deserialized_at }	| ).
      io_html->add( |{ <ls_overview>-created_by }	| ).
      io_html->add( |{ <ls_overview>-created_at }	| ).
      io_html->add( |{ <ls_overview>-key }	| ).
      io_html->add( |
| ).

    ENDLOOP.

    io_html->add( |
| ).

  ENDMETHOD.

  METHOD render_table_header.

    DATA lt_col_spec TYPE zif_abapgit_definitions=>tty_col_spec.
    FIELD-SYMBOLS <ls_col> LIKE LINE OF lt_col_spec.

    DEFINE _add_col.
      APPEND INITIAL LINE TO lt_col_spec ASSIGNING <ls_col>.
      <ls_col>-tech_name    = &1.
      <ls_col>-display_name = &2.
      <ls_col>-css_class    = &3.
      <ls_col>-add_tz       = &4.
    END-OF-DEFINITION.

    "        technical name    display name      css class   add timezone
    _add_col 'FAVORITE'        ''                'wmin'      ''.
    _add_col 'TYPE'            ''                'wmin'      ''.
    _add_col 'NAME'            'Name'            ''          ''.
    _add_col 'URL'             'Url'             ''          ''.
    _add_col 'PACKAGE'         'Package'         ''          ''.
    _add_col 'BRANCH'          'Branch'          ''          ''.
    _add_col 'DESERIALIZED_BY' 'Deserialized by' 'ro-detail' ''.
    _add_col 'DESERIALIZED_AT' 'Deserialized at' 'ro-detail' 'X'.
    _add_col 'CREATED_BY'      'Created by'      'ro-detail' ''.
    _add_col 'CREATED_AT'      'Created at'      'ro-detail' 'X'.
    _add_col 'KEY'             'Key'             'ro-detail' ''.

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

    io_html->add( zcl_abapgit_gui_chunk_lib=>render_order_by_header_cells(
                      it_col_spec         = lt_col_spec
                      iv_order_by         = mv_order_by
                      iv_order_descending = mv_order_descending ) ).

    io_html->add( '
' ).
    io_html->add( '
' ).

  ENDMETHOD.

  METHOD render_text_input.

    DATA lv_attrs TYPE string.

    CREATE OBJECT ro_html.

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

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

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

  ENDMETHOD.

  METHOD scripts.

    ro_html = super->scripts( ).

    ro_html->add( 'setInitialFocus("filter");' ).
    ro_html->add( 'var gHelper = new RepoOverViewHelper();' ).

  ENDMETHOD.

  METHOD zif_abapgit_gui_event_handler~on_event.

    DATA: lv_key  TYPE zif_abapgit_persistence=>ty_value.

    CASE iv_action.
      WHEN c_action-select.

        lv_key = iv_getdata.

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

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

        ev_state = zcl_abapgit_gui=>c_event_state-go_back.

      WHEN zif_abapgit_definitions=>c_action-change_order_by.

        mv_order_by = zcl_abapgit_gui_chunk_lib=>parse_change_order_by( iv_getdata ).
        ev_state = zcl_abapgit_gui=>c_event_state-re_render.

      WHEN zif_abapgit_definitions=>c_action-direction.

        mv_order_descending = zcl_abapgit_gui_chunk_lib=>parse_direction( iv_getdata ).
        ev_state = zcl_abapgit_gui=>c_event_state-re_render.

      WHEN c_action-apply_filter.

        parse_filter( it_postdata ).
        ev_state = zcl_abapgit_gui=>c_event_state-re_render.

      WHEN OTHERS.

        super->zif_abapgit_gui_event_handler~on_event(
          EXPORTING
            iv_action    = iv_action
            iv_prev_page = iv_prev_page
            iv_getdata   = iv_getdata
            it_postdata  = it_postdata
          IMPORTING
            ei_page      = ei_page
            ev_state     = ev_state ).

    ENDCASE.

  ENDMETHOD.

  METHOD apply_order_by.

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

    IF mv_order_by IS NOT INITIAL.

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

    ENDIF.

  ENDMETHOD.

ENDCLASS.

CLASS zcl_abapgit_gui_page_repo_over DEFINITION
  PUBLIC
  INHERITING FROM zcl_abapgit_gui_page
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    METHODS constructor .
    METHODS zif_abapgit_gui_event_handler~on_event
        REDEFINITION .

  PROTECTED SECTION.
    METHODS:
      render_content REDEFINITION,
      scripts REDEFINITION.

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

    DATA:
      mv_order_by         TYPE string,
      mv_order_descending TYPE abap_bool,
      mv_filter           TYPE string,
      mv_time_zone        TYPE timezone,
      mt_col_spec         TYPE zif_abapgit_definitions=>tty_col_spec.

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

      parse_filter
        IMPORTING
          it_postdata TYPE cnht_post_data_tab,

      apply_filter
        CHANGING
          ct_overview TYPE tty_overview,

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

      render_table_header
        IMPORTING
          io_html TYPE REF TO zcl_abapgit_html,

      render_table
        IMPORTING
          io_html     TYPE REF TO zcl_abapgit_html
          it_overview TYPE tty_overview,

      render_table_body
        IMPORTING
          io_html     TYPE REF TO zcl_abapgit_html
          it_overview TYPE tty_overview,

      render_header_bar
        IMPORTING
          io_html TYPE REF TO zcl_abapgit_html,

      apply_order_by
        CHANGING ct_overview TYPE tty_overview,

      _add_col
        IMPORTING
          iv_descriptor TYPE string.

ENDCLASS.

CLASS ZCL_ABAPGIT_GUI_PAGE_REPO_OVER IMPLEMENTATION.

  METHOD apply_filter.

    IF mv_filter IS NOT INITIAL.

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

    ENDIF.

  ENDMETHOD.

  METHOD apply_order_by.

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

    IF mv_order_by IS NOT INITIAL.

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

    ENDIF.

  ENDMETHOD.

  METHOD constructor.

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

    CALL FUNCTION 'GET_SYSTEM_TIMEZONE'
      IMPORTING
        timezone            = mv_time_zone
      EXCEPTIONS
        customizing_missing = 1
        OTHERS              = 2.
    ASSERT sy-subrc = 0.

  ENDMETHOD.

  METHOD map_repo_list_to_overview.

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

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

    LOOP AT it_repo_list ASSIGNING <ls_repo>.

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

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

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

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

      ls_overview-deserialized_by = <ls_repo>-deserialized_by.

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

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

      INSERT ls_overview INTO TABLE rt_overview.

    ENDLOOP.

  ENDMETHOD.

  METHOD parse_filter.

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

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

    mv_filter = condense( mv_filter ).

  ENDMETHOD.

  METHOD render_content.

    DATA: lt_overview TYPE tty_overview.

    lt_overview = map_repo_list_to_overview( zcl_abapgit_persist_factory=>get_repo( )->list( ) ).

    apply_order_by( CHANGING ct_overview = lt_overview ).

    apply_filter( CHANGING ct_overview = lt_overview ).

    CREATE OBJECT ro_html.

    render_header_bar( ro_html ).
    render_table( io_html     = ro_html
                  it_overview = lt_overview ).

  ENDMETHOD.

  METHOD render_header_bar.

    io_html->add( |
| ).

    io_html->add( |<form class="inline" method="post" action="sapevent:{ c_action-apply_filter }">| ).

    io_html->add( render_text_input(
      iv_name  = |filter|
      iv_label = |Filter: |
      iv_value = mv_filter ) ).
    io_html->add( |<input type="submit" class="hidden-submit">| ).
    io_html->add( |</form>| ).

    io_html->add( zcl_abapgit_html=>a(
      iv_txt = 'Toggle detail'
      iv_act = |gHelper.toggleRepoListDetail()|
      iv_typ = zif_abapgit_html=>c_action_type-onclick ) ).

    io_html->add( |
| ).

  ENDMETHOD.

  METHOD render_table.

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

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

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

  ENDMETHOD.

  METHOD render_table_body.

    DATA:
      lv_type_icon     TYPE string,
      lv_favorite_icon TYPE string.

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

    io_html->add( '
' ).

    LOOP AT it_overview ASSIGNING <ls_overview>.

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

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

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

      io_html->add( |{ zcl_abapgit_html=>a( iv_txt = <ls_overview>-name
                                                iv_act = |{ c_action-select }?{ <ls_overview>-key }| ) }	| ).

      IF <ls_overview>-type = abap_false.
        io_html->add( |{ io_html->a( iv_txt = <ls_overview>-url
                                         iv_act = |{ zif_abapgit_definitions=>c_action-url }?|
                                               && |{ <ls_overview>-url }| ) }	| ).
      ELSE.
        io_html->add( |	| ).
      ENDIF.

      io_html->add( |{ <ls_overview>-package }	| ).
      io_html->add( |{ <ls_overview>-branch }	| ).
      io_html->add( |{ <ls_overview>-deserialized_by }	| ).
      io_html->add( |{ <ls_overview>-deserialized_at }	| ).
      io_html->add( |{ <ls_overview>-created_by }	| ).
      io_html->add( |{ <ls_overview>-created_at }	| ).
      io_html->add( |{ <ls_overview>-key }	| ).
      io_html->add( |
| ).

    ENDLOOP.

    io_html->add( |
| ).

  ENDMETHOD.

  METHOD render_table_header.

    CLEAR mt_col_spec.
    "          technical name  /display name    /css class /add timezone
    _add_col( 'FAVORITE        /                /wmin      / ' ).
    _add_col( 'TYPE            /                /wmin      / ' ).
    _add_col( 'NAME            /Name            /          / ' ).
    _add_col( 'URL             /Url             /          / ' ).
    _add_col( 'PACKAGE         /Package         /          / ' ).
    _add_col( 'BRANCH          /Branch          /          / ' ).
    _add_col( 'DESERIALIZED_BY /Deserialized by /ro-detail / ' ).
    _add_col( 'DESERIALIZED_AT /Deserialized at /ro-detail /X' ).
    _add_col( 'CREATED_BY      /Created by      /ro-detail / ' ).
    _add_col( 'CREATED_AT      /Created at      /ro-detail /X' ).
    _add_col( 'KEY             /Key             /ro-detail / ' ).

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

    io_html->add( zcl_abapgit_gui_chunk_lib=>render_order_by_header_cells(
                      it_col_spec         = mt_col_spec
                      iv_order_by         = mv_order_by
                      iv_order_descending = mv_order_descending ) ).

    io_html->add( '
' ).
    io_html->add( '
' ).

  ENDMETHOD.

  METHOD render_text_input.

    DATA lv_attrs TYPE string.

    CREATE OBJECT ro_html.

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

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

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

  ENDMETHOD.

  METHOD scripts.

    ro_html = super->scripts( ).

    ro_html->add( 'setInitialFocus("filter");' ).
    ro_html->add( 'var gHelper = new RepoOverViewHelper();' ).

  ENDMETHOD.

  METHOD zif_abapgit_gui_event_handler~on_event.

    DATA: lv_key  TYPE zif_abapgit_persistence=>ty_value.

    CASE iv_action.
      WHEN c_action-select.

        lv_key = iv_getdata.

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

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

        ev_state = zcl_abapgit_gui=>c_event_state-go_back.

      WHEN zif_abapgit_definitions=>c_action-change_order_by.

        mv_order_by = zcl_abapgit_gui_chunk_lib=>parse_change_order_by( iv_getdata ).
        ev_state = zcl_abapgit_gui=>c_event_state-re_render.

      WHEN zif_abapgit_definitions=>c_action-direction.

        mv_order_descending = zcl_abapgit_gui_chunk_lib=>parse_direction( iv_getdata ).
        ev_state = zcl_abapgit_gui=>c_event_state-re_render.

      WHEN c_action-apply_filter.

        parse_filter( it_postdata ).
        ev_state = zcl_abapgit_gui=>c_event_state-re_render.

      WHEN OTHERS.

        super->zif_abapgit_gui_event_handler~on_event(
          EXPORTING
            iv_action    = iv_action
            iv_prev_page = iv_prev_page
            iv_getdata   = iv_getdata
            it_postdata  = it_postdata
          IMPORTING
            ei_page      = ei_page
            ev_state     = ev_state ).

    ENDCASE.

  ENDMETHOD.

  METHOD _add_col.

    FIELD-SYMBOLS <ls_col> LIKE LINE OF mt_col_spec.
    APPEND INITIAL LINE TO mt_col_spec ASSIGNING <ls_col>.
    SPLIT iv_descriptor AT '/' INTO
      <ls_col>-tech_name
      <ls_col>-display_name
      <ls_col>-css_class
      <ls_col>-add_tz.
    CONDENSE <ls_col>-tech_name.
    CONDENSE <ls_col>-display_name.
    CONDENSE <ls_col>-css_class.
    CONDENSE <ls_col>-add_tz.

  ENDMETHOD.
ENDCLASS.

remove DEFINED from zcl_abapgit_gui_page_repo_over (#3185)

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


CLASS zcl_abapgit_default_task DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    CLASS-METHODS:
      get_instance
        RETURNING
          VALUE(ro_instance) TYPE REF TO zcl_abapgit_default_task
        RAISING
          zcx_abapgit_exception.

    METHODS:
      constructor
        RAISING
          zcx_abapgit_exception,

      set
        IMPORTING
          iv_package TYPE devclass
        RAISING
          zcx_abapgit_exception,

      reset
        RAISING
          zcx_abapgit_exception.

  PRIVATE SECTION.
    CLASS-DATA: mo_instance TYPE REF TO zcl_abapgit_default_task.

    DATA: mv_task_is_set_by_abapgit TYPE abap_bool,
          ms_save_default_task      TYPE e070use.

    METHODS:
      store_current_default_task
        RAISING
          zcx_abapgit_exception,

      restore_old_default_task
        RAISING
          zcx_abapgit_exception.

ENDCLASS.

CLASS zcl_abapgit_default_task IMPLEMENTATION.

  METHOD constructor.

    store_current_default_task( ).

  ENDMETHOD.

  METHOD get_instance.

    IF mo_instance IS NOT BOUND.

      CREATE OBJECT mo_instance.

    ENDIF.

    ro_instance = mo_instance.

  ENDMETHOD.

  METHOD reset.

    DATA: lt_e070use TYPE STANDARD TABLE OF e070use.

    IF mv_task_is_set_by_abapgit = abap_false.
      " if the default transport request task isn't set
      " by us there is nothing to do.
      RETURN.
    ENDIF.

    CALL FUNCTION 'TR_TASK_GET'
      TABLES
        tt_e070use       = lt_e070use    " Table of current settings
      EXCEPTIONS
        invalid_username = 1
        invalid_category = 2
        invalid_client   = 3
        OTHERS           = 4.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from TR_TASK_GET { sy-subrc }| ).
    ENDIF.

    READ TABLE lt_e070use ASSIGNING FIELD-SYMBOL(<ls_e070use>)
                          INDEX 1.
    ASSERT sy-subrc = 0.

    CALL FUNCTION 'TR_TASK_RESET'
      EXPORTING
        iv_username      = <ls_e070use>-username
        iv_order         = <ls_e070use>-ordernum
        iv_task          = <ls_e070use>-tasknum
        iv_dialog        = abap_false
      EXCEPTIONS
        invalid_username = 1
        invalid_order    = 2
        invalid_task     = 3
        OTHERS           = 4.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from TR_TASK_RESET { sy-subrc }| ).
    ENDIF.

    restore_old_default_task( ).

  ENDMETHOD.

  METHOD set.

    " checks whether object changes of the package are rerorded in transport
    " requests. If true then we set the default task, so that no annoying
    " transport request popups are shown while deserializing.

    DATA: li_package TYPE REF TO if_package,
          lt_e071    TYPE STANDARD TABLE OF e071,
          lt_e071k   TYPE STANDARD TABLE OF e071k,
          lv_order   TYPE trkorr,
          lv_task    TYPE trkorr.

    IF mv_task_is_set_by_abapgit = abap_true.
      " the default transport request task is already set by us
      " -> no reason to do it again.
      RETURN.
    ENDIF.

    cl_package_factory=>load_package(
      EXPORTING
        i_package_name             = iv_package
      IMPORTING
        e_package                  = li_package
      EXCEPTIONS
        object_not_existing        = 1
        unexpected_error           = 2
        intern_err                 = 3
        no_access                  = 4
        object_locked_and_modified = 5
        OTHERS                     = 6 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from CL_PACKAGE_FACTORY=>LOAD_PACKAGE { sy-subrc }| ).
    ENDIF.

    IF li_package->wbo_korr_flag = abap_false.
      " Objects of package are not recorded in transport request,
      " no need to proceed.
      RETURN.
    ENDIF.

    CALL FUNCTION 'TRINT_ORDER_CHOICE'
      IMPORTING
        we_order               = lv_order     " Selected request
        we_task                = lv_task    " Selected task
      TABLES
        wt_e071                = lt_e071    " Object table to be edited (for mass editing)
        wt_e071k               = lt_e071k    " Key table to be edited (for mass editing)
      EXCEPTIONS
        no_correction_selected = 1
        display_mode           = 2
        object_append_error    = 3
        recursive_call         = 4
        wrong_order_type       = 5
        OTHERS                 = 6.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from TRINT_ORDER_CHOICE { sy-subrc }| ).
    ENDIF.

    CALL FUNCTION 'TR_TASK_SET'
      EXPORTING
        iv_order          = lv_order    " Request to be s et
        iv_task           = lv_task    " Task to be set
      EXCEPTIONS
        invalid_username  = 1
        invalid_category  = 2
        invalid_client    = 3
        invalid_validdays = 4
        invalid_order     = 5
        invalid_task      = 6
        OTHERS            = 7.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from TR_TASK_SET { sy-subrc }| ).
    ENDIF.

    mv_task_is_set_by_abapgit = abap_true.

  ENDMETHOD.

  METHOD restore_old_default_task.

    IF ms_save_default_task IS INITIAL.
      " There wasn't a default transport request before
      " so we needn't restore anything.
      RETURN.
    ENDIF.

    CALL FUNCTION 'TR_TASK_SET'
      EXPORTING
        iv_order          = ms_save_default_task-ordernum    " Request to be s et
        iv_task           = ms_save_default_task-tasknum    " Task to be set
      EXCEPTIONS
        invalid_username  = 1
        invalid_category  = 2
        invalid_client    = 3
        invalid_validdays = 4
        invalid_order     = 5
        invalid_task      = 6
        OTHERS            = 7.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from TR_TASK_SET { sy-subrc }| ).
    ENDIF.

  ENDMETHOD.

  METHOD store_current_default_task.

    DATA: lt_e070use TYPE STANDARD TABLE OF e070use.

    " Save the current default task to restore it later...
    CALL FUNCTION 'TR_TASK_GET'
      TABLES
        tt_e070use       = lt_e070use     " Table of current settings
      EXCEPTIONS
        invalid_username = 1
        invalid_category = 2
        invalid_client   = 3
        OTHERS           = 4.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from TR_TASK_GET { sy-subrc }| ).
    ENDIF.

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

    READ TABLE lt_e070use INTO ms_save_default_task
                          INDEX 1.

  ENDMETHOD.

ENDCLASS.

CLASS zcl_abapgit_default_task DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    CLASS-METHODS:
      get_instance
        RETURNING
          VALUE(ro_instance) TYPE REF TO zcl_abapgit_default_task
        RAISING
          zcx_abapgit_exception.

    METHODS:
      constructor
        RAISING
          zcx_abapgit_exception,

      set
        IMPORTING
          iv_package TYPE devclass
        RAISING
          zcx_abapgit_exception,

      reset
        RAISING
          zcx_abapgit_exception.

  PRIVATE SECTION.
    CLASS-DATA: mo_instance TYPE REF TO zcl_abapgit_default_task.

    DATA: mv_task_is_set_by_abapgit TYPE abap_bool,
          ms_save_default_task      TYPE e070use.

    METHODS:
      store_current_default_task
        RAISING
          zcx_abapgit_exception,

      restore_old_default_task
        RAISING
          zcx_abapgit_exception.

ENDCLASS.

CLASS ZCL_ABAPGIT_DEFAULT_TASK IMPLEMENTATION.

  METHOD constructor.

    store_current_default_task( ).

  ENDMETHOD.

  METHOD get_instance.

    IF mo_instance IS NOT BOUND.

      CREATE OBJECT mo_instance.

    ENDIF.

    ro_instance = mo_instance.

  ENDMETHOD.

  METHOD reset.

    DATA: lt_e070use TYPE STANDARD TABLE OF e070use.

    IF mv_task_is_set_by_abapgit = abap_false.
      " if the default transport request task isn't set
      " by us there is nothing to do.
      RETURN.
    ENDIF.

    CALL FUNCTION 'TR_TASK_GET'
      TABLES
        tt_e070use       = lt_e070use    " Table of current settings
      EXCEPTIONS
        invalid_username = 1
        invalid_category = 2
        invalid_client   = 3
        OTHERS           = 4.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from TR_TASK_GET { sy-subrc }| ).
    ENDIF.

    READ TABLE lt_e070use ASSIGNING FIELD-SYMBOL(<ls_e070use>)
                          INDEX 1.
    ASSERT sy-subrc = 0.

    CALL FUNCTION 'TR_TASK_RESET'
      EXPORTING
        iv_username      = <ls_e070use>-username
        iv_order         = <ls_e070use>-ordernum
        iv_task          = <ls_e070use>-tasknum
        iv_dialog        = abap_false
      EXCEPTIONS
        invalid_username = 1
        invalid_order    = 2
        invalid_task     = 3
        OTHERS           = 4.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from TR_TASK_RESET { sy-subrc }| ).
    ENDIF.

    restore_old_default_task( ).

  ENDMETHOD.

  METHOD restore_old_default_task.

    IF ms_save_default_task IS INITIAL.
      " There wasn't a default transport request before
      " so we needn't restore anything.
      RETURN.
    ENDIF.

    CALL FUNCTION 'TR_TASK_SET'
      EXPORTING
        iv_order          = ms_save_default_task-ordernum    " Request to be s et
        iv_task           = ms_save_default_task-tasknum    " Task to be set
      EXCEPTIONS
        invalid_username  = 1
        invalid_category  = 2
        invalid_client    = 3
        invalid_validdays = 4
        invalid_order     = 5
        invalid_task      = 6
        OTHERS            = 7.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from TR_TASK_SET { sy-subrc }| ).
    ENDIF.

  ENDMETHOD.

  METHOD set.

    " checks whether object changes of the package are rerorded in transport
    " requests. If true then we set the default task, so that no annoying
    " transport request popups are shown while deserializing.

    DATA: li_package TYPE REF TO if_package,
          lt_e071    TYPE STANDARD TABLE OF e071,
          lt_e071k   TYPE STANDARD TABLE OF e071k,
          lv_order   TYPE trkorr,
          lv_task    TYPE trkorr.

    IF mv_task_is_set_by_abapgit = abap_true.
      " the default transport request task is already set by us
      " -> no reason to do it again.
      RETURN.
    ENDIF.

    cl_package_factory=>load_package(
      EXPORTING
        i_package_name             = iv_package
      IMPORTING
        e_package                  = li_package
      EXCEPTIONS
        object_not_existing        = 1
        unexpected_error           = 2
        intern_err                 = 3
        no_access                  = 4
        object_locked_and_modified = 5
        OTHERS                     = 6 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from CL_PACKAGE_FACTORY=>LOAD_PACKAGE { sy-subrc }| ).
    ENDIF.

    IF li_package->wbo_korr_flag = abap_false.
      " Objects of package are not recorded in transport request,
      " no need to proceed.
      RETURN.
    ENDIF.

    CALL FUNCTION 'TRINT_ORDER_CHOICE'
      IMPORTING
        we_order               = lv_order     " Selected request
        we_task                = lv_task    " Selected task
      TABLES
        wt_e071                = lt_e071    " Object table to be edited (for mass editing)
        wt_e071k               = lt_e071k    " Key table to be edited (for mass editing)
      EXCEPTIONS
        no_correction_selected = 1
        display_mode           = 2
        object_append_error    = 3
        recursive_call         = 4
        wrong_order_type       = 5
        OTHERS                 = 6.

    IF sy-subrc = 1.
      zcx_abapgit_exception=>raise( 'cancelled' ).
    ELSEIF sy-subrc > 1.
      zcx_abapgit_exception=>raise( |Error from TRINT_ORDER_CHOICE { sy-subrc }| ).
    ENDIF.

    CALL FUNCTION 'TR_TASK_SET'
      EXPORTING
        iv_order          = lv_order    " Request to be s et
        iv_task           = lv_task    " Task to be set
      EXCEPTIONS
        invalid_username  = 1
        invalid_category  = 2
        invalid_client    = 3
        invalid_validdays = 4
        invalid_order     = 5
        invalid_task      = 6
        OTHERS            = 7.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from TR_TASK_SET { sy-subrc }| ).
    ENDIF.

    mv_task_is_set_by_abapgit = abap_true.

  ENDMETHOD.

  METHOD store_current_default_task.

    DATA: lt_e070use TYPE STANDARD TABLE OF e070use.

    " Save the current default task to restore it later...
    CALL FUNCTION 'TR_TASK_GET'
      TABLES
        tt_e070use       = lt_e070use     " Table of current settings
      EXCEPTIONS
        invalid_username = 1
        invalid_category = 2
        invalid_client   = 3
        OTHERS           = 4.

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from TR_TASK_GET { sy-subrc }| ).
    ENDIF.

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

    READ TABLE lt_e070use INTO ms_save_default_task
                          INDEX 1.

  ENDMETHOD.
ENDCLASS.

Improve handling cancel of tranport request popup


CLASS zcl_abapgit_dot_abapgit DEFINITION
  PUBLIC
  CREATE PUBLIC .

  PUBLIC SECTION.

    CLASS-METHODS build_default
      RETURNING
        VALUE(ro_dot_abapgit) TYPE REF TO zcl_abapgit_dot_abapgit .
    CLASS-METHODS deserialize
      IMPORTING
        !iv_xstr              TYPE xstring
      RETURNING
        VALUE(ro_dot_abapgit) TYPE REF TO zcl_abapgit_dot_abapgit
      RAISING
        zcx_abapgit_exception .
    METHODS constructor
      IMPORTING
        !is_data TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit .
    METHODS serialize
      RETURNING
        VALUE(rv_xstr) TYPE xstring
      RAISING
        zcx_abapgit_exception .
    METHODS get_data
      RETURNING
        VALUE(rs_data) TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit .
    METHODS add_ignore
      IMPORTING
        !iv_path     TYPE string
        !iv_filename TYPE string .
    METHODS is_ignored
      IMPORTING
        !iv_path          TYPE string
        !iv_filename      TYPE string
      RETURNING
        VALUE(rv_ignored) TYPE abap_bool .
    METHODS remove_ignore
      IMPORTING
        !iv_path     TYPE string
        !iv_filename TYPE string .
    METHODS get_starting_folder
      RETURNING
        VALUE(rv_path) TYPE string .

    METHODS get_folder_logic
      RETURNING
        VALUE(rv_logic) TYPE string .

    METHODS set_folder_logic
      IMPORTING
        !iv_logic TYPE string .

    METHODS set_starting_folder
      IMPORTING
        !iv_path TYPE string .

    METHODS get_master_language
      RETURNING
        VALUE(rv_language) TYPE spras .
*      set_master_language
*        IMPORTING iv_language TYPE spras,
    METHODS get_signature
      RETURNING
        VALUE(rs_signature) TYPE zif_abapgit_definitions=>ty_file_signature
      RAISING
        zcx_abapgit_exception .
    METHODS get_requirements
      RETURNING
        VALUE(rt_requirements) TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt.
    METHODS set_requirements
      IMPORTING
        it_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt.
  PROTECTED SECTION.
  PRIVATE SECTION.
    DATA: ms_data TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit.

    CLASS-METHODS:
      to_xml
        IMPORTING is_data       TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit
        RETURNING VALUE(rv_xml) TYPE string
        RAISING   zcx_abapgit_exception,
      from_xml
        IMPORTING iv_xml         TYPE string
        RETURNING VALUE(rs_data) TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit.

ENDCLASS.

CLASS ZCL_ABAPGIT_DOT_ABAPGIT IMPLEMENTATION.

  METHOD add_ignore.

    DATA: lv_name TYPE string.

    FIELD-SYMBOLS: <lv_ignore> LIKE LINE OF ms_data-ignore.

    lv_name = iv_path && iv_filename.

    READ TABLE ms_data-ignore FROM lv_name TRANSPORTING NO FIELDS.
    IF sy-subrc = 0.
      RETURN.
    ENDIF.

    APPEND INITIAL LINE TO ms_data-ignore ASSIGNING <lv_ignore>.
    <lv_ignore> = lv_name.

  ENDMETHOD.

  METHOD build_default.

    DATA: ls_data TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit.

    ls_data-master_language = sy-langu.
    ls_data-starting_folder = '/src/'.
    ls_data-folder_logic    = zif_abapgit_dot_abapgit=>c_folder_logic-prefix.

    APPEND '/.gitignore' TO ls_data-ignore.
    APPEND '/LICENSE' TO ls_data-ignore.
    APPEND '/README.md' TO ls_data-ignore.
    APPEND '/package.json' TO ls_data-ignore.
    APPEND '/.travis.yml' TO ls_data-ignore.
    APPEND '/.gitlab-ci.yml' TO ls_data-ignore.
    APPEND '/abaplint.json' TO ls_data-ignore.

    CREATE OBJECT ro_dot_abapgit
      EXPORTING
        is_data = ls_data.

  ENDMETHOD.

  METHOD constructor.
    ms_data = is_data.
  ENDMETHOD.

  METHOD deserialize.

    DATA: lv_xml  TYPE string,
          ls_data TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit.

    lv_xml = zcl_abapgit_convert=>xstring_to_string_utf8( iv_xstr ).

    ls_data = from_xml( lv_xml ).

    CREATE OBJECT ro_dot_abapgit
      EXPORTING
        is_data = ls_data.

  ENDMETHOD.

  METHOD from_xml.

    DATA: lv_xml TYPE string.

    lv_xml = iv_xml.

* fix downward compatibility
    REPLACE ALL OCCURRENCES OF '<_--28C_DATA_--29>' IN lv_xml WITH '<DATA>'.
    REPLACE ALL OCCURRENCES OF '</_--28C_DATA_--29>' IN lv_xml WITH '</DATA>'.

    CALL TRANSFORMATION id
      OPTIONS value_handling = 'accept_data_loss'
      SOURCE XML lv_xml
      RESULT data = rs_data ##NO_TEXT.

* downward compatibility
    IF rs_data-folder_logic IS INITIAL.
      rs_data-folder_logic = zif_abapgit_dot_abapgit=>c_folder_logic-prefix.
    ENDIF.

  ENDMETHOD.

  METHOD get_data.
    rs_data = ms_data.
  ENDMETHOD.

  METHOD get_folder_logic.
    rv_logic = ms_data-folder_logic.
  ENDMETHOD.

  METHOD get_master_language.
    rv_language = ms_data-master_language.
  ENDMETHOD.

  METHOD get_requirements.
    rt_requirements = ms_data-requirements.
  ENDMETHOD.

  METHOD get_signature.

    rs_signature-path     = zif_abapgit_definitions=>c_root_dir.
    rs_signature-filename = zif_abapgit_definitions=>c_dot_abapgit.
    rs_signature-sha1     = zcl_abapgit_hash=>sha1( iv_type = zif_abapgit_definitions=>c_type-blob
                                                    iv_data = serialize( ) ).

  ENDMETHOD.

  METHOD get_starting_folder.
    rv_path = ms_data-starting_folder.
  ENDMETHOD.

  METHOD is_ignored.

    DATA: lv_name     TYPE string,
          lv_starting TYPE string,
          lv_dot      TYPE string,
          lv_count    TYPE i,
          lv_ignore   TYPE string.

    lv_name = iv_path && iv_filename.

    CONCATENATE ms_data-starting_folder '*' INTO lv_starting.
    CONCATENATE '/' zif_abapgit_definitions=>c_dot_abapgit INTO lv_dot.

    LOOP AT ms_data-ignore INTO lv_ignore.
      FIND ALL OCCURRENCES OF '/' IN lv_name MATCH COUNT lv_count.

      IF lv_name CP lv_ignore
          OR ( ms_data-starting_folder <> '/'
          AND lv_count > 1
          AND NOT lv_name CP lv_starting
          AND NOT lv_name = lv_dot ).
        rv_ignored = abap_true.
        RETURN.
      ENDIF.
    ENDLOOP.

  ENDMETHOD.

  METHOD remove_ignore.

    DATA: lv_name TYPE string.

    lv_name = iv_path && iv_filename.

    DELETE TABLE ms_data-ignore FROM lv_name.

  ENDMETHOD.

  METHOD serialize.

    DATA: lv_xml TYPE string.

    lv_xml = to_xml( ms_data ).

    rv_xstr = zcl_abapgit_convert=>string_to_xstring_utf8( lv_xml ).

  ENDMETHOD.

  METHOD set_folder_logic.
    ms_data-folder_logic = iv_logic.
  ENDMETHOD.

  METHOD set_requirements.
    ms_data-requirements = it_requirements.
  ENDMETHOD.

  METHOD set_starting_folder.
    ms_data-starting_folder = iv_path.
  ENDMETHOD.

  METHOD to_xml.

    CALL TRANSFORMATION id
      OPTIONS initial_components = 'suppress'
      SOURCE data = is_data
      RESULT XML rv_xml.

    rv_xml = zcl_abapgit_xml_pretty=>print( rv_xml ).

    REPLACE FIRST OCCURRENCE
      OF REGEX '<\?xml version="1\.0" encoding="[\w-]+"\?>'
      IN rv_xml
      WITH '<?xml version="1.0" encoding="utf-8"?>'.
    ASSERT sy-subrc = 0.

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_dot_abapgit DEFINITION
  PUBLIC
  CREATE PUBLIC .

  PUBLIC SECTION.

    CLASS-METHODS build_default
      RETURNING
        VALUE(ro_dot_abapgit) TYPE REF TO zcl_abapgit_dot_abapgit .
    CLASS-METHODS deserialize
      IMPORTING
        !iv_xstr              TYPE xstring
      RETURNING
        VALUE(ro_dot_abapgit) TYPE REF TO zcl_abapgit_dot_abapgit
      RAISING
        zcx_abapgit_exception .
    METHODS constructor
      IMPORTING
        !is_data TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit .
    METHODS serialize
      RETURNING
        VALUE(rv_xstr) TYPE xstring
      RAISING
        zcx_abapgit_exception .
    METHODS get_data
      RETURNING
        VALUE(rs_data) TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit .
    METHODS add_ignore
      IMPORTING
        !iv_path     TYPE string
        !iv_filename TYPE string .
    METHODS is_ignored
      IMPORTING
        !iv_path          TYPE string
        !iv_filename      TYPE string
      RETURNING
        VALUE(rv_ignored) TYPE abap_bool .
    METHODS remove_ignore
      IMPORTING
        !iv_path     TYPE string
        !iv_filename TYPE string .
    METHODS get_starting_folder
      RETURNING
        VALUE(rv_path) TYPE string .

    METHODS get_folder_logic
      RETURNING
        VALUE(rv_logic) TYPE string .

    METHODS set_folder_logic
      IMPORTING
        !iv_logic TYPE string .

    METHODS set_starting_folder
      IMPORTING
        !iv_path TYPE string .

    METHODS get_master_language
      RETURNING
        VALUE(rv_language) TYPE spras .
*      set_master_language
*        IMPORTING iv_language TYPE spras,
    METHODS get_signature
      RETURNING
        VALUE(rs_signature) TYPE zif_abapgit_definitions=>ty_file_signature
      RAISING
        zcx_abapgit_exception .
    METHODS get_requirements
      RETURNING
        VALUE(rt_requirements) TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt.
    METHODS set_requirements
      IMPORTING
        it_requirements TYPE zif_abapgit_dot_abapgit=>ty_requirement_tt.
  PROTECTED SECTION.
  PRIVATE SECTION.
    DATA: ms_data TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit.

    CLASS-METHODS:
      to_xml
        IMPORTING is_data       TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit
        RETURNING VALUE(rv_xml) TYPE string
        RAISING   zcx_abapgit_exception,
      from_xml
        IMPORTING iv_xml         TYPE string
        RETURNING VALUE(rs_data) TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit.

ENDCLASS.

CLASS ZCL_ABAPGIT_DOT_ABAPGIT IMPLEMENTATION.

  METHOD add_ignore.

    DATA: lv_name TYPE string.

    FIELD-SYMBOLS: <lv_ignore> LIKE LINE OF ms_data-ignore.

    lv_name = iv_path && iv_filename.

    READ TABLE ms_data-ignore FROM lv_name TRANSPORTING NO FIELDS.
    IF sy-subrc = 0.
      RETURN.
    ENDIF.

    APPEND INITIAL LINE TO ms_data-ignore ASSIGNING <lv_ignore>.
    <lv_ignore> = lv_name.

  ENDMETHOD.

  METHOD build_default.

    DATA: ls_data TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit.

    ls_data-master_language = sy-langu.
    ls_data-starting_folder = '/src/'.
    ls_data-folder_logic    = zif_abapgit_dot_abapgit=>c_folder_logic-prefix.

    APPEND '/.gitignore' TO ls_data-ignore.
    APPEND '/LICENSE' TO ls_data-ignore.
    APPEND '/README.md' TO ls_data-ignore.
    APPEND '/package.json' TO ls_data-ignore.
    APPEND '/.travis.yml' TO ls_data-ignore.
    APPEND '/.gitlab-ci.yml' TO ls_data-ignore.
    APPEND '/abaplint.json' TO ls_data-ignore.
    APPEND '/azure-pipelines.yml' TO ls_data-ignore.

    CREATE OBJECT ro_dot_abapgit
      EXPORTING
        is_data = ls_data.

  ENDMETHOD.

  METHOD constructor.
    ms_data = is_data.
  ENDMETHOD.

  METHOD deserialize.

    DATA: lv_xml  TYPE string,
          ls_data TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit.

    lv_xml = zcl_abapgit_convert=>xstring_to_string_utf8( iv_xstr ).

    ls_data = from_xml( lv_xml ).

    CREATE OBJECT ro_dot_abapgit
      EXPORTING
        is_data = ls_data.

  ENDMETHOD.

  METHOD from_xml.

    DATA: lv_xml TYPE string.

    lv_xml = iv_xml.

* fix downward compatibility
    REPLACE ALL OCCURRENCES OF '<_--28C_DATA_--29>' IN lv_xml WITH '<DATA>'.
    REPLACE ALL OCCURRENCES OF '</_--28C_DATA_--29>' IN lv_xml WITH '</DATA>'.

    CALL TRANSFORMATION id
      OPTIONS value_handling = 'accept_data_loss'
      SOURCE XML lv_xml
      RESULT data = rs_data ##NO_TEXT.

* downward compatibility
    IF rs_data-folder_logic IS INITIAL.
      rs_data-folder_logic = zif_abapgit_dot_abapgit=>c_folder_logic-prefix.
    ENDIF.

  ENDMETHOD.

  METHOD get_data.
    rs_data = ms_data.
  ENDMETHOD.

  METHOD get_folder_logic.
    rv_logic = ms_data-folder_logic.
  ENDMETHOD.

  METHOD get_master_language.
    rv_language = ms_data-master_language.
  ENDMETHOD.

  METHOD get_requirements.
    rt_requirements = ms_data-requirements.
  ENDMETHOD.

  METHOD get_signature.

    rs_signature-path     = zif_abapgit_definitions=>c_root_dir.
    rs_signature-filename = zif_abapgit_definitions=>c_dot_abapgit.
    rs_signature-sha1     = zcl_abapgit_hash=>sha1( iv_type = zif_abapgit_definitions=>c_type-blob
                                                    iv_data = serialize( ) ).

  ENDMETHOD.

  METHOD get_starting_folder.
    rv_path = ms_data-starting_folder.
  ENDMETHOD.

  METHOD is_ignored.

    DATA: lv_name     TYPE string,
          lv_starting TYPE string,
          lv_dot      TYPE string,
          lv_count    TYPE i,
          lv_ignore   TYPE string.

    lv_name = iv_path && iv_filename.

    CONCATENATE ms_data-starting_folder '*' INTO lv_starting.
    CONCATENATE '/' zif_abapgit_definitions=>c_dot_abapgit INTO lv_dot.

    LOOP AT ms_data-ignore INTO lv_ignore.
      FIND ALL OCCURRENCES OF '/' IN lv_name MATCH COUNT lv_count.

      IF lv_name CP lv_ignore
          OR ( ms_data-starting_folder <> '/'
          AND lv_count > 1
          AND NOT lv_name CP lv_starting
          AND NOT lv_name = lv_dot ).
        rv_ignored = abap_true.
        RETURN.
      ENDIF.
    ENDLOOP.

  ENDMETHOD.

  METHOD remove_ignore.

    DATA: lv_name TYPE string.

    lv_name = iv_path && iv_filename.

    DELETE TABLE ms_data-ignore FROM lv_name.

  ENDMETHOD.

  METHOD serialize.

    DATA: lv_xml TYPE string.

    lv_xml = to_xml( ms_data ).

    rv_xstr = zcl_abapgit_convert=>string_to_xstring_utf8( lv_xml ).

  ENDMETHOD.

  METHOD set_folder_logic.
    ms_data-folder_logic = iv_logic.
  ENDMETHOD.

  METHOD set_requirements.
    ms_data-requirements = it_requirements.
  ENDMETHOD.

  METHOD set_starting_folder.
    ms_data-starting_folder = iv_path.
  ENDMETHOD.

  METHOD to_xml.

    CALL TRANSFORMATION id
      OPTIONS initial_components = 'suppress'
      SOURCE data = is_data
      RESULT XML rv_xml.

    rv_xml = zcl_abapgit_xml_pretty=>print( rv_xml ).

    REPLACE FIRST OCCURRENCE
      OF REGEX '<\?xml version="1\.0" encoding="[\w-]+"\?>'
      IN rv_xml
      WITH '<?xml version="1.0" encoding="utf-8"?>'.
    ASSERT sy-subrc = 0.

  ENDMETHOD.
ENDCLASS.

add azure-pipelines.yml to default ignore