CLASS zcl_abapgit_dependencies DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    CLASS-METHODS resolve
      CHANGING
        !ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt
      RAISING
        zcx_abapgit_exception .
  PROTECTED SECTION.
  PRIVATE SECTION.

    TYPES:
      BEGIN OF ty_dependency,
        depname  TYPE dd02l-tabname,
        deptyp   TYPE c LENGTH 4,
        deplocal TYPE dd02l-as4local,
        refname  TYPE dd02l-tabname,
        reftyp   TYPE c LENGTH 4,
        kind     TYPE c LENGTH 1,
      END OF ty_dependency .
    TYPES:
      tty_dedenpency TYPE STANDARD TABLE OF ty_dependency
                                 WITH NON-UNIQUE DEFAULT KEY .
    TYPES:
      BEGIN OF ty_item,
        obj_type TYPE tadir-object,
        obj_name TYPE tadir-obj_name,
        devclass TYPE devclass,
      END OF ty_item .

    CLASS-METHODS resolve_ddic
      CHANGING
        !ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS get_ddls_dependencies
      IMPORTING
        iv_ddls_name         TYPE tadir-obj_name
      RETURNING
        VALUE(rt_dependency) TYPE tty_dedenpency .
    CLASS-METHODS resolve_packages
      CHANGING
        ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt.
ENDCLASS.

CLASS zcl_abapgit_dependencies IMPLEMENTATION.

  METHOD get_ddls_dependencies.

    DATA: lt_ddls_name TYPE TABLE OF ddsymtab,
          ls_ddls_name TYPE ddsymtab.

    ls_ddls_name-name = iv_ddls_name.
    INSERT ls_ddls_name INTO TABLE lt_ddls_name.

    PERFORM ('DDLS_GET_DEP') IN PROGRAM ('RADMASDL')
                             TABLES lt_ddls_name rt_dependency.

  ENDMETHOD.

  METHOD resolve.

    DATA: lv_tabclass TYPE dd02l-tabclass.

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

* misuse field KORRNUM to fix deletion sequence

    LOOP AT ct_tadir ASSIGNING <ls_tadir>.
      CASE <ls_tadir>-object.
        WHEN 'DEVC'.
          <ls_tadir>-korrnum = '9990'.
        WHEN 'IATU'.
          <ls_tadir>-korrnum = '5500'.
        WHEN 'IARP'.
          <ls_tadir>-korrnum = '5510'.
        WHEN 'IASP'.
          <ls_tadir>-korrnum = '5520'.
        WHEN 'SUSC'.
          <ls_tadir>-korrnum = '5000'.
        WHEN 'TTYP' OR 'TABL' OR 'VIEW'.
          SELECT SINGLE tabclass FROM dd02l
            INTO lv_tabclass
            WHERE tabname = <ls_tadir>-obj_name
            AND as4local = 'A'
            AND as4vers = '0000'.
          IF sy-subrc = 0 AND lv_tabclass = 'APPEND'.
* delete append structures before database tables
            <ls_tadir>-korrnum = '6500'.
          ELSE.
            <ls_tadir>-korrnum = '7000'.
          ENDIF.
        WHEN 'DTEL'.
          <ls_tadir>-korrnum = '8000'.
* ACID after PROG/FUGR/CLAS
        WHEN 'ACID'.
          <ls_tadir>-korrnum = '3000'.
        WHEN 'PARA'.
* PARA after DTEL
          <ls_tadir>-korrnum = '8100'.
        WHEN 'DOMA'.
          <ls_tadir>-korrnum = '9000'.
* AUTH after DCLS
        WHEN 'DCLS'.
          <ls_tadir>-korrnum = '7100'.
        WHEN 'AUTH'.
          <ls_tadir>-korrnum = '7000'.
        WHEN 'PROG'.
* delete includes after main programs
          SELECT COUNT(*) FROM reposrc
            WHERE progname = <ls_tadir>-obj_name
            AND r3state = 'A'
            AND subc = 'I'.
          IF sy-subrc = 0.
            <ls_tadir>-korrnum = '2000'.
          ELSE.
            <ls_tadir>-korrnum = '1000'.
          ENDIF.
        WHEN 'IDOC'.
          <ls_tadir>-korrnum = '2000'.
        WHEN 'IEXT'.
          <ls_tadir>-korrnum = '1500'.
        WHEN OTHERS.
          <ls_tadir>-korrnum = '1000'.
      ENDCASE.
    ENDLOOP.

    resolve_ddic( CHANGING ct_tadir = ct_tadir ).
    resolve_packages( CHANGING ct_tadir = ct_tadir ).

    SORT ct_tadir BY korrnum ASCENDING.

  ENDMETHOD.

  METHOD resolve_ddic.
* this will make sure the deletion sequence of structures/tables work
* in case they have dependencies with .INCLUDE

    TYPES: BEGIN OF ty_edge,
             from TYPE ty_item,
             to   TYPE ty_item,
           END OF ty_edge.

    DATA: lt_nodes        TYPE TABLE OF ty_item,
          lt_edges        TYPE TABLE OF ty_edge,
          lt_findstrings  TYPE TABLE OF rsfind,
          lv_plus         TYPE i VALUE 1,
          lv_find_obj_cls TYPE euobj-id,
          lv_index        TYPE i,
          lv_before       TYPE i,
          lt_founds       TYPE TABLE OF rsfindlst,
          lt_scope        TYPE STANDARD TABLE OF seu_obj,
          lt_dependency   TYPE tty_dedenpency.

    FIELD-SYMBOLS: <ls_tadir_ddls>      TYPE zif_abapgit_definitions=>ty_tadir,
                   <ls_dependency>      TYPE ty_dependency,
                   <ls_tadir_dependent> TYPE zif_abapgit_definitions=>ty_tadir,
                   <ls_tadir>           LIKE LINE OF ct_tadir,
                   <ls_edge>            LIKE LINE OF lt_edges,
                   <ls_found>           LIKE LINE OF lt_founds,
                   <ls_node>            LIKE LINE OF lt_nodes.

* build nodes
    LOOP AT ct_tadir ASSIGNING <ls_tadir>
        WHERE object = 'TABL'
        OR object = 'TTYP'.
      APPEND INITIAL LINE TO lt_nodes ASSIGNING <ls_node>.
      <ls_node>-obj_name = <ls_tadir>-obj_name.
      <ls_node>-obj_type = <ls_tadir>-object.
    ENDLOOP.

    APPEND 'TABL' TO lt_scope.
    APPEND 'STRU' TO lt_scope.
    APPEND 'TTYP' TO lt_scope.

* build edges
    LOOP AT lt_nodes ASSIGNING <ls_node>.

      CLEAR lt_findstrings.
      APPEND <ls_node>-obj_name TO lt_findstrings.
      lv_find_obj_cls = <ls_node>-obj_type.

      CALL FUNCTION 'RS_EU_CROSSREF'
        EXPORTING
          i_find_obj_cls           = lv_find_obj_cls
        TABLES
          i_findstrings            = lt_findstrings
          o_founds                 = lt_founds
          i_scope_object_cls       = lt_scope
        EXCEPTIONS
          not_executed             = 1
          not_found                = 2
          illegal_object           = 3
          no_cross_for_this_object = 4
          batch                    = 5
          batchjob_error           = 6
          wrong_type               = 7
          object_not_exist         = 8
          OTHERS                   = 9.
      IF sy-subrc <> 0.
        CONTINUE.
      ENDIF.

      LOOP AT lt_founds ASSIGNING <ls_found>.
        APPEND INITIAL LINE TO lt_edges ASSIGNING <ls_edge>.
        <ls_edge>-from = <ls_node>.

        <ls_edge>-to-obj_name   = <ls_found>-object.
        CASE <ls_found>-object_cls.
          WHEN 'DS'
              OR 'DT'.
            <ls_edge>-to-obj_type = 'TABL'.
          WHEN 'DA'.
            <ls_edge>-to-obj_type = 'TTYP'.
          WHEN OTHERS.
            zcx_abapgit_exception=>raise( 'resolve_ddic, unknown object_cls' ).
        ENDCASE.
      ENDLOOP.

    ENDLOOP.

* build DDLS edges
    LOOP AT ct_tadir ASSIGNING <ls_tadir_ddls>
                     WHERE object = 'DDLS'.

      CLEAR: lt_dependency.

      APPEND INITIAL LINE TO lt_nodes ASSIGNING <ls_node>.
      <ls_node>-obj_name = <ls_tadir_ddls>-obj_name.
      <ls_node>-obj_type = <ls_tadir_ddls>-object.

      lt_dependency = get_ddls_dependencies( <ls_tadir_ddls>-obj_name ).

      LOOP AT lt_dependency ASSIGNING <ls_dependency>
                            WHERE deptyp = 'DDLS'
                            AND refname = <ls_tadir_ddls>-obj_name.

        READ TABLE ct_tadir ASSIGNING <ls_tadir_dependent>
                            WITH KEY pgmid    = 'R3TR'
                                     object   = 'DDLS'
                                     obj_name = <ls_dependency>-depname
                            BINARY SEARCH.
        CHECK sy-subrc = 0.

        APPEND INITIAL LINE TO lt_edges ASSIGNING <ls_edge>.
        <ls_edge>-from = <ls_node>.
        <ls_edge>-to-obj_name = <ls_dependency>-depname.
        <ls_edge>-to-obj_type = 'DDLS'.

      ENDLOOP.

    ENDLOOP.

    DO.
      lv_before = lines( lt_nodes ).
      LOOP AT lt_nodes ASSIGNING <ls_node>.
        lv_index = sy-tabix.
        READ TABLE lt_edges WITH KEY
          from-obj_name = <ls_node>-obj_name
          from-obj_type = <ls_node>-obj_type
          TRANSPORTING NO FIELDS.
        IF sy-subrc <> 0.
          LOOP AT ct_tadir ASSIGNING <ls_tadir>
              WHERE obj_name = <ls_node>-obj_name
              AND object = <ls_node>-obj_type.
            <ls_tadir>-korrnum = <ls_tadir>-korrnum + lv_plus.
            CONDENSE <ls_tadir>-korrnum.
          ENDLOOP.
          DELETE lt_edges
            WHERE to-obj_name = <ls_node>-obj_name
            AND to-obj_type = <ls_node>-obj_type.
          DELETE lt_nodes INDEX lv_index.
          EXIT. " make sure the sequence is fixed
        ENDIF.
      ENDLOOP.
      IF lv_before = lines( lt_nodes ).
        EXIT.
      ENDIF.
      lv_plus = lv_plus + 1.
    ENDDO.

  ENDMETHOD.

  METHOD resolve_packages.

    DATA: lt_subpackages TYPE zif_abapgit_sap_package=>ty_devclass_tt.

    FIELD-SYMBOLS: <ls_tadir>            LIKE LINE OF ct_tadir,
                   <lv_subpackage>       LIKE LINE OF lt_subpackages,
                   <ls_tadir_subpackage> LIKE LINE OF ct_tadir.

    " List subpackage before corresponding superpackage

    LOOP AT ct_tadir ASSIGNING <ls_tadir>
                     WHERE object = 'DEVC'.

      lt_subpackages = zcl_abapgit_factory=>get_sap_package( |{ <ls_tadir>-obj_name }| )->list_subpackages( ).

      LOOP AT lt_subpackages ASSIGNING <lv_subpackage>.

        READ TABLE ct_tadir ASSIGNING <ls_tadir_subpackage>
                            WITH KEY object   = 'DEVC'
                                     obj_name = <lv_subpackage>.
        IF sy-subrc = 0.
          <ls_tadir_subpackage>-korrnum = condense( |{ <ls_tadir_subpackage>-korrnum - 1 }| ).
        ENDIF.

      ENDLOOP.

    ENDLOOP.

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_dependencies DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    CLASS-METHODS resolve
      CHANGING
        !ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt
      RAISING
        zcx_abapgit_exception .
  PROTECTED SECTION.
  PRIVATE SECTION.

    TYPES:
      BEGIN OF ty_dependency,
        depname  TYPE dd02l-tabname,
        deptyp   TYPE c LENGTH 4,
        deplocal TYPE dd02l-as4local,
        refname  TYPE dd02l-tabname,
        reftyp   TYPE c LENGTH 4,
        kind     TYPE c LENGTH 1,
      END OF ty_dependency .
    TYPES:
      tty_dedenpency TYPE STANDARD TABLE OF ty_dependency
                                 WITH NON-UNIQUE DEFAULT KEY .
    TYPES:
      BEGIN OF ty_item,
        obj_type TYPE tadir-object,
        obj_name TYPE tadir-obj_name,
        devclass TYPE devclass,
      END OF ty_item .

    CLASS-METHODS resolve_ddic
      CHANGING
        !ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS get_ddls_dependencies
      IMPORTING
        iv_ddls_name         TYPE tadir-obj_name
      RETURNING
        VALUE(rt_dependency) TYPE tty_dedenpency .
    CLASS-METHODS resolve_packages
      CHANGING
        ct_tadir TYPE zif_abapgit_definitions=>ty_tadir_tt.
ENDCLASS.

CLASS zcl_abapgit_dependencies IMPLEMENTATION.

  METHOD get_ddls_dependencies.

    DATA: lt_ddls_name TYPE TABLE OF ddsymtab,
          ls_ddls_name TYPE ddsymtab.

    ls_ddls_name-name = iv_ddls_name.
    INSERT ls_ddls_name INTO TABLE lt_ddls_name.

    PERFORM ('DDLS_GET_DEP') IN PROGRAM ('RADMASDL')
                             TABLES lt_ddls_name rt_dependency.

  ENDMETHOD.

  METHOD resolve.

    DATA: lv_tabclass TYPE dd02l-tabclass.

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

    " misuse field KORRNUM to fix deletion sequence

    LOOP AT ct_tadir ASSIGNING <ls_tadir>.
      CASE <ls_tadir>-object.
        WHEN 'DEVC'.
          " Packages last
          <ls_tadir>-korrnum = '9990'.
        WHEN 'DOMA'.
          <ls_tadir>-korrnum = '9000'.
        WHEN 'PARA'.
          " PARA after DTEL
          <ls_tadir>-korrnum = '8100'.
        WHEN 'DTEL'.
          <ls_tadir>-korrnum = '8000'.
        WHEN 'DCLS'.
          " AUTH after DCLS
          <ls_tadir>-korrnum = '7100'.
        WHEN 'AUTH'.
          " AUTH after DCLS
          <ls_tadir>-korrnum = '7050'.
        WHEN 'TTYP' OR 'TABL' OR 'VIEW'.
          SELECT SINGLE tabclass FROM dd02l
            INTO lv_tabclass
            WHERE tabname = <ls_tadir>-obj_name
            AND as4local = 'A'
            AND as4vers = '0000'.
          IF sy-subrc = 0 AND lv_tabclass = 'APPEND'.
            " delete append structures before database tables
            <ls_tadir>-korrnum = '6500'.
          ELSE.
            <ls_tadir>-korrnum = '7000'.
          ENDIF.
        WHEN 'IASP'.
          <ls_tadir>-korrnum = '5520'.
        WHEN 'IARP'.
          <ls_tadir>-korrnum = '5510'.
        WHEN 'IATU'.
          <ls_tadir>-korrnum = '5500'.
        WHEN 'SUSC'.
          <ls_tadir>-korrnum = '5000'.
        WHEN 'ACID'.
          " ACID after PROG/FUGR/CLAS
          <ls_tadir>-korrnum = '3000'.
        WHEN 'PROG'.
          " delete includes after main programs
          SELECT COUNT(*) FROM reposrc
            WHERE progname = <ls_tadir>-obj_name
            AND r3state = 'A'
            AND subc = 'I'.
          IF sy-subrc = 0.
            <ls_tadir>-korrnum = '2000'.
          ELSE.
            <ls_tadir>-korrnum = '1000'.
          ENDIF.
        WHEN 'IDOC'.
          <ls_tadir>-korrnum = '2000'.
        WHEN 'IEXT'.
          <ls_tadir>-korrnum = '1500'.
        WHEN OTHERS.
          <ls_tadir>-korrnum = '1000'.
      ENDCASE.
    ENDLOOP.

    resolve_ddic( CHANGING ct_tadir = ct_tadir ).
    resolve_packages( CHANGING ct_tadir = ct_tadir ).

    SORT ct_tadir BY korrnum ASCENDING.

  ENDMETHOD.

  METHOD resolve_ddic.
* this will make sure the deletion sequence of structures/tables work
* in case they have dependencies with .INCLUDE

    TYPES: BEGIN OF ty_edge,
             from TYPE ty_item,
             to   TYPE ty_item,
           END OF ty_edge.

    DATA: lt_nodes        TYPE TABLE OF ty_item,
          lt_edges        TYPE TABLE OF ty_edge,
          lt_findstrings  TYPE TABLE OF rsfind,
          lv_plus         TYPE i VALUE 1,
          lv_find_obj_cls TYPE euobj-id,
          lv_index        TYPE i,
          lv_before       TYPE i,
          lt_founds       TYPE TABLE OF rsfindlst,
          lt_scope        TYPE STANDARD TABLE OF seu_obj,
          lt_dependency   TYPE tty_dedenpency.

    FIELD-SYMBOLS: <ls_tadir_ddls>      TYPE zif_abapgit_definitions=>ty_tadir,
                   <ls_dependency>      TYPE ty_dependency,
                   <ls_tadir_dependent> TYPE zif_abapgit_definitions=>ty_tadir,
                   <ls_tadir>           LIKE LINE OF ct_tadir,
                   <ls_edge>            LIKE LINE OF lt_edges,
                   <ls_found>           LIKE LINE OF lt_founds,
                   <ls_node>            LIKE LINE OF lt_nodes.

    " build nodes
    LOOP AT ct_tadir ASSIGNING <ls_tadir>
        WHERE object = 'TABL'
        OR object = 'VIEW'
        OR object = 'TTYP'.
      APPEND INITIAL LINE TO lt_nodes ASSIGNING <ls_node>.
      <ls_node>-obj_name = <ls_tadir>-obj_name.
      <ls_node>-obj_type = <ls_tadir>-object.
    ENDLOOP.

    APPEND 'TABL' TO lt_scope.
    APPEND 'VIEW' TO lt_scope.
    APPEND 'STRU' TO lt_scope.
    APPEND 'TTYP' TO lt_scope.

    " build edges
    LOOP AT lt_nodes ASSIGNING <ls_node>.

      CLEAR lt_findstrings.
      APPEND <ls_node>-obj_name TO lt_findstrings.
      lv_find_obj_cls = <ls_node>-obj_type.

      CALL FUNCTION 'RS_EU_CROSSREF'
        EXPORTING
          i_find_obj_cls           = lv_find_obj_cls
        TABLES
          i_findstrings            = lt_findstrings
          o_founds                 = lt_founds
          i_scope_object_cls       = lt_scope
        EXCEPTIONS
          not_executed             = 1
          not_found                = 2
          illegal_object           = 3
          no_cross_for_this_object = 4
          batch                    = 5
          batchjob_error           = 6
          wrong_type               = 7
          object_not_exist         = 8
          OTHERS                   = 9.
      IF sy-subrc <> 0.
        CONTINUE.
      ENDIF.

      LOOP AT lt_founds ASSIGNING <ls_found>.
        APPEND INITIAL LINE TO lt_edges ASSIGNING <ls_edge>.
        <ls_edge>-from = <ls_node>.

        <ls_edge>-to-obj_name   = <ls_found>-object.
        CASE <ls_found>-object_cls.
          WHEN 'DS'
              OR 'DT'.
            <ls_edge>-to-obj_type = 'TABL'.
          WHEN 'DV'.
            <ls_edge>-to-obj_type = 'VIEW'.
          WHEN 'DA'.
            <ls_edge>-to-obj_type = 'TTYP'.
          WHEN OTHERS.
            zcx_abapgit_exception=>raise( 'resolve_ddic, unknown object_cls' ).
        ENDCASE.
      ENDLOOP.

    ENDLOOP.

    " build DDLS edges
    LOOP AT ct_tadir ASSIGNING <ls_tadir_ddls>
                     WHERE object = 'DDLS'.

      CLEAR: lt_dependency.

      APPEND INITIAL LINE TO lt_nodes ASSIGNING <ls_node>.
      <ls_node>-obj_name = <ls_tadir_ddls>-obj_name.
      <ls_node>-obj_type = <ls_tadir_ddls>-object.

      lt_dependency = get_ddls_dependencies( <ls_tadir_ddls>-obj_name ).

      LOOP AT lt_dependency ASSIGNING <ls_dependency>
                            WHERE deptyp = 'DDLS'
                            AND refname = <ls_tadir_ddls>-obj_name.

        READ TABLE ct_tadir ASSIGNING <ls_tadir_dependent>
                            WITH KEY pgmid    = 'R3TR'
                                     object   = 'DDLS'
                                     obj_name = <ls_dependency>-depname
                            BINARY SEARCH.
        CHECK sy-subrc = 0.

        APPEND INITIAL LINE TO lt_edges ASSIGNING <ls_edge>.
        <ls_edge>-from = <ls_node>.
        <ls_edge>-to-obj_name = <ls_dependency>-depname.
        <ls_edge>-to-obj_type = 'DDLS'.

      ENDLOOP.

    ENDLOOP.

    DO.
      lv_before = lines( lt_nodes ).
      LOOP AT lt_nodes ASSIGNING <ls_node>.
        lv_index = sy-tabix.
        READ TABLE lt_edges WITH KEY
          from-obj_name = <ls_node>-obj_name
          from-obj_type = <ls_node>-obj_type
          TRANSPORTING NO FIELDS.
        IF sy-subrc <> 0.
          LOOP AT ct_tadir ASSIGNING <ls_tadir>
              WHERE obj_name = <ls_node>-obj_name
              AND object = <ls_node>-obj_type.
            <ls_tadir>-korrnum = <ls_tadir>-korrnum + lv_plus.
            CONDENSE <ls_tadir>-korrnum.
          ENDLOOP.
          DELETE lt_edges
            WHERE to-obj_name = <ls_node>-obj_name
            AND to-obj_type = <ls_node>-obj_type.
          DELETE lt_nodes INDEX lv_index.
          EXIT. " make sure the sequence is fixed
        ENDIF.
      ENDLOOP.
      IF lv_before = lines( lt_nodes ).
        EXIT.
      ENDIF.
      lv_plus = lv_plus + 1.
    ENDDO.

  ENDMETHOD.

  METHOD resolve_packages.

    DATA: lt_subpackages TYPE zif_abapgit_sap_package=>ty_devclass_tt.

    FIELD-SYMBOLS: <ls_tadir>            LIKE LINE OF ct_tadir,
                   <lv_subpackage>       LIKE LINE OF lt_subpackages,
                   <ls_tadir_subpackage> LIKE LINE OF ct_tadir.

    " List subpackage before corresponding superpackage

    LOOP AT ct_tadir ASSIGNING <ls_tadir>
                     WHERE object = 'DEVC'.

      lt_subpackages = zcl_abapgit_factory=>get_sap_package( |{ <ls_tadir>-obj_name }| )->list_subpackages( ).

      LOOP AT lt_subpackages ASSIGNING <lv_subpackage>.

        READ TABLE ct_tadir ASSIGNING <ls_tadir_subpackage>
                            WITH KEY object   = 'DEVC'
                                     obj_name = <lv_subpackage>.
        IF sy-subrc = 0.
          <ls_tadir_subpackage>-korrnum = condense( |{ <ls_tadir_subpackage>-korrnum - 1 }| ).
        ENDIF.

      ENDLOOP.

    ENDLOOP.

  ENDMETHOD.
ENDCLASS.

Uninstall: Resolve DDIC dependencies of VIEW (#3630)

Fix uninstall if table/structure references a view (ex https://github.com/abapGit-tests/TABL_include_VIEW)

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


CLASS zcl_abapgit_syntax_check DEFINITION
  PUBLIC
  CREATE PUBLIC .

  PUBLIC SECTION.

    CLASS-METHODS run
      IMPORTING
        !iv_package    TYPE devclass
      RETURNING
        VALUE(rt_list) TYPE scit_alvlist .
  PROTECTED SECTION.

    TYPES:
      ty_tdevc_tt TYPE STANDARD TABLE OF tdevc WITH DEFAULT KEY .

    CLASS-METHODS find_all_subpackages
      IMPORTING
        !iv_package        TYPE devclass
      RETURNING
        VALUE(rt_packages) TYPE ty_tdevc_tt .
    CLASS-METHODS create_inspection
      IMPORTING
        !io_set              TYPE REF TO cl_ci_objectset
        !io_variant          TYPE REF TO cl_ci_checkvariant
      RETURNING
        VALUE(ro_inspection) TYPE REF TO cl_ci_inspection .
    CLASS-METHODS create_objectset
      IMPORTING
        !iv_package   TYPE devclass
      RETURNING
        VALUE(ro_set) TYPE REF TO cl_ci_objectset .
    CLASS-METHODS create_variant
      RETURNING
        VALUE(ro_variant) TYPE REF TO cl_ci_checkvariant .
    CLASS-METHODS run_inspection
      IMPORTING
        !io_inspection TYPE REF TO cl_ci_inspection
      RETURNING
        VALUE(rt_list) TYPE scit_alvlist .
  PRIVATE SECTION.
ENDCLASS.

CLASS ZCL_ABAPGIT_SYNTAX_CHECK IMPLEMENTATION.

  METHOD create_inspection.

    cl_ci_inspection=>create(
      EXPORTING
        p_user           = sy-uname
        p_name           = ''
      RECEIVING
        p_ref            = ro_inspection
      EXCEPTIONS
        locked           = 1
        error_in_enqueue = 2
        not_authorized   = 3
        OTHERS           = 4 ).
    ASSERT sy-subrc = 0.

    ro_inspection->set(
      p_chkv = io_variant
      p_objs = io_set ).

  ENDMETHOD.

  METHOD create_objectset.

    DATA: lt_objs     TYPE scit_objs,
          lt_packages TYPE ty_tdevc_tt,
          ls_obj      LIKE LINE OF lt_objs.

    lt_packages = find_all_subpackages( iv_package ).
    IF lines( lt_packages ) = 0.
      RETURN.
    ENDIF.

    SELECT object AS objtype obj_name AS objname
      FROM tadir
      INTO CORRESPONDING FIELDS OF TABLE lt_objs
      FOR ALL ENTRIES IN lt_packages
      WHERE devclass = lt_packages-devclass
      AND pgmid = 'R3TR'.                               "#EC CI_GENBUFF

    ro_set = cl_ci_objectset=>save_from_list( lt_objs ).

  ENDMETHOD.

  METHOD create_variant.

    DATA: lt_variant TYPE sci_tstvar,
          ls_variant LIKE LINE OF lt_variant.

    cl_ci_checkvariant=>create(
      EXPORTING
        p_user              = sy-uname
      RECEIVING
        p_ref               = ro_variant
      EXCEPTIONS
        chkv_already_exists = 1
        locked              = 2
        error_in_enqueue    = 3
        not_authorized      = 4
        OTHERS              = 5 ).
    ASSERT sy-subrc = 0.

    ls_variant-testname = 'CL_CI_TEST_SYNTAX_CHECK'.
    INSERT ls_variant INTO TABLE lt_variant.

    ro_variant->set_variant(
      EXPORTING
        p_variant    = lt_variant
      EXCEPTIONS
        not_enqueued = 1
        OTHERS       = 2 ).
    ASSERT sy-subrc = 0.

  ENDMETHOD.

  METHOD find_all_subpackages.

* TODO, in the future, move this method to the ABAPGIT global package class

    DATA: ls_package LIKE LINE OF rt_packages.

    SELECT SINGLE * FROM tdevc INTO ls_package WHERE devclass = iv_package.
    ASSERT sy-subrc = 0.
    APPEND ls_package TO rt_packages.

    LOOP AT rt_packages INTO ls_package.
      SELECT * FROM tdevc APPENDING TABLE rt_packages
        WHERE parentcl = ls_package-devclass.
    ENDLOOP.

  ENDMETHOD.

  METHOD run.

    DATA: lo_set        TYPE REF TO cl_ci_objectset,
          lo_inspection TYPE REF TO cl_ci_inspection,
          lo_variant    TYPE REF TO cl_ci_checkvariant.

    lo_set = create_objectset( iv_package ).
    lo_variant = create_variant( ).

    lo_inspection = create_inspection(
      io_set     = lo_set
      io_variant = lo_variant ).

    rt_list = run_inspection( lo_inspection ).

  ENDMETHOD.

  METHOD run_inspection.

    io_inspection->run(
      EXCEPTIONS
        invalid_check_version = 1
        OTHERS                = 2 ).
    ASSERT sy-subrc = 0.

    io_inspection->plain_list(
      IMPORTING
        p_list = rt_list ).

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_syntax_check DEFINITION
  PUBLIC
  CREATE PUBLIC .

  PUBLIC SECTION.

    CLASS-METHODS run
      IMPORTING
        !iv_package    TYPE devclass
      RETURNING
        VALUE(rt_list) TYPE scit_alvlist .
  PROTECTED SECTION.

    TYPES:
      ty_tdevc_tt TYPE STANDARD TABLE OF tdevc WITH DEFAULT KEY .

    CLASS-METHODS find_all_subpackages
      IMPORTING
        !iv_package        TYPE devclass
      RETURNING
        VALUE(rt_packages) TYPE ty_tdevc_tt .
    CLASS-METHODS create_inspection
      IMPORTING
        !io_set              TYPE REF TO cl_ci_objectset
        !io_variant          TYPE REF TO cl_ci_checkvariant
      RETURNING
        VALUE(ro_inspection) TYPE REF TO cl_ci_inspection .
    CLASS-METHODS create_objectset
      IMPORTING
        !iv_package   TYPE devclass
      RETURNING
        VALUE(ro_set) TYPE REF TO cl_ci_objectset .
    CLASS-METHODS create_variant
      RETURNING
        VALUE(ro_variant) TYPE REF TO cl_ci_checkvariant .
    CLASS-METHODS run_inspection
      IMPORTING
        !io_inspection TYPE REF TO cl_ci_inspection
      RETURNING
        VALUE(rt_list) TYPE scit_alvlist .
  PRIVATE SECTION.
ENDCLASS.

CLASS ZCL_ABAPGIT_SYNTAX_CHECK IMPLEMENTATION.

  METHOD create_inspection.

    cl_ci_inspection=>create(
      EXPORTING
        p_user           = sy-uname
        p_name           = ''
      RECEIVING
        p_ref            = ro_inspection
      EXCEPTIONS
        locked           = 1
        error_in_enqueue = 2
        not_authorized   = 3
        OTHERS           = 4 ).
    ASSERT sy-subrc = 0.

    ro_inspection->set(
      p_chkv = io_variant
      p_objs = io_set ).

  ENDMETHOD.

  METHOD create_objectset.

    DATA: lt_objs     TYPE scit_objs,
          lt_packages TYPE ty_tdevc_tt.

    lt_packages = find_all_subpackages( iv_package ).
    IF lines( lt_packages ) = 0.
      RETURN.
    ENDIF.

    SELECT object AS objtype obj_name AS objname
      FROM tadir
      INTO CORRESPONDING FIELDS OF TABLE lt_objs
      FOR ALL ENTRIES IN lt_packages
      WHERE devclass = lt_packages-devclass
      AND pgmid = 'R3TR'.                               "#EC CI_GENBUFF

    ro_set = cl_ci_objectset=>save_from_list( lt_objs ).

  ENDMETHOD.

  METHOD create_variant.

    DATA: lt_variant TYPE sci_tstvar,
          ls_variant LIKE LINE OF lt_variant.

    cl_ci_checkvariant=>create(
      EXPORTING
        p_user              = sy-uname
      RECEIVING
        p_ref               = ro_variant
      EXCEPTIONS
        chkv_already_exists = 1
        locked              = 2
        error_in_enqueue    = 3
        not_authorized      = 4
        OTHERS              = 5 ).
    ASSERT sy-subrc = 0.

    ls_variant-testname = 'CL_CI_TEST_SYNTAX_CHECK'.
    INSERT ls_variant INTO TABLE lt_variant.

    ro_variant->set_variant(
      EXPORTING
        p_variant    = lt_variant
      EXCEPTIONS
        not_enqueued = 1
        OTHERS       = 2 ).
    ASSERT sy-subrc = 0.

  ENDMETHOD.

  METHOD find_all_subpackages.

* TODO, in the future, move this method to the ABAPGIT global package class

    DATA: ls_package LIKE LINE OF rt_packages,
          lt_found   LIKE rt_packages,
          lt_sub     LIKE rt_packages.

    SELECT SINGLE * FROM tdevc INTO ls_package WHERE devclass = iv_package.
    ASSERT sy-subrc = 0.
    APPEND ls_package TO rt_packages.

    SELECT * FROM tdevc APPENDING TABLE lt_sub
      WHERE parentcl = ls_package-devclass.

    LOOP AT lt_sub INTO ls_package.
      lt_found = find_all_subpackages( ls_package-devclass ).
      APPEND LINES OF lt_found TO rt_packages.
    ENDLOOP.

  ENDMETHOD.

  METHOD run.

    DATA: lo_set        TYPE REF TO cl_ci_objectset,
          lo_inspection TYPE REF TO cl_ci_inspection,
          lo_variant    TYPE REF TO cl_ci_checkvariant.

    lo_set = create_objectset( iv_package ).
    lo_variant = create_variant( ).

    lo_inspection = create_inspection(
      io_set     = lo_set
      io_variant = lo_variant ).

    rt_list = run_inspection( lo_inspection ).

  ENDMETHOD.

  METHOD run_inspection.

    io_inspection->run(
      EXCEPTIONS
        invalid_check_version = 1
        OTHERS                = 2 ).
    ASSERT sy-subrc = 0.

    io_inspection->plain_list(
      IMPORTING
        p_list = rt_list ).

  ENDMETHOD.
ENDCLASS.

change table recursion to method recursion #959


CLASS zcl_abapgit_object_dsys DEFINITION PUBLIC INHERITING FROM zcl_abapgit_objects_super FINAL.

  PUBLIC SECTION.

    INTERFACES zif_abapgit_object.

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

  PROTECTED SECTION.

  PRIVATE SECTION.

    CONSTANTS: c_typ TYPE dokhl-typ VALUE 'E',
               c_id  TYPE dokhl-id VALUE 'HY'.

    DATA: mv_doc_object  TYPE sobj_name.

    TYPES: BEGIN OF ty_data,
             doctitle TYPE dsyst-doktitle,
             head     TYPE thead,
             lines    TYPE tline_tab,
           END OF ty_data.

    METHODS deserialize_dsys
      IMPORTING
        io_xml TYPE REF TO zcl_abapgit_xml_input
      RAISING
        zcx_abapgit_exception.

    METHODS get_master_lang
      RETURNING
        VALUE(rv_language) TYPE spras.

ENDCLASS.

CLASS ZCL_ABAPGIT_OBJECT_DSYS IMPLEMENTATION.

  METHOD constructor.

    DATA: lv_prefix    TYPE namespace,
          lv_bare_name TYPE progname.

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

    CALL FUNCTION 'RS_NAME_SPLIT_NAMESPACE'
      EXPORTING
        name_with_namespace    = ms_item-obj_name
      IMPORTING
        namespace              = lv_prefix
        name_without_namespace = lv_bare_name.

    mv_doc_object = |{ lv_bare_name+0(4) }{ lv_prefix }{ lv_bare_name+4(*) }|.

  ENDMETHOD.

  METHOD deserialize_dsys.

    DATA: ls_data      TYPE ty_data,
          ls_docu_info TYPE dokil,
          lv_version   TYPE dokvers.

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

    CALL FUNCTION 'DOCU_INIT'
      EXPORTING
        id     = c_id
        langu  = mv_language
        object = mv_doc_object
        typ    = c_typ
      IMPORTING
        xdokil = ls_docu_info.

    lv_version = ls_docu_info-version.

    CALL FUNCTION 'DOCU_UPDATE'
      EXPORTING
        head    = ls_data-head
        state   = 'A'
        typ     = c_typ
        version = lv_version
      TABLES
        line    = ls_data-lines.

  ENDMETHOD.

  METHOD get_master_lang.

    DATA: lv_language TYPE spras.

    SELECT SINGLE langu FROM dokil INTO rv_language
      WHERE id = c_id
      AND object = mv_doc_object
      AND masterlang = abap_true.

    IF sy-subrc <> 0.
      rv_language = mv_language.
    ENDIF.

  ENDMETHOD.

  METHOD zif_abapgit_object~changed_by.

    rv_user = zcl_abapgit_factory=>get_longtexts( )->changed_by(
      iv_object_name = mv_doc_object
      iv_longtext_id = c_id ).

  ENDMETHOD.

  METHOD zif_abapgit_object~delete.

    zcl_abapgit_factory=>get_longtexts( )->delete(
      iv_object_name = mv_doc_object
      iv_longtext_id = c_id ).

  ENDMETHOD.

  METHOD zif_abapgit_object~deserialize.

    DATA: ls_metadata TYPE zif_abapgit_definitions=>ty_metadata.

    ls_metadata = io_xml->get_metadata( ).

    CASE ls_metadata-version.

      WHEN 'v1.0.0'.
        deserialize_dsys( io_xml ).

      WHEN 'v2.0.0'.
        zcl_abapgit_factory=>get_longtexts( )->deserialize(
          io_xml             = io_xml
          iv_master_language = mv_language ).

      WHEN OTHERS.
        zcx_abapgit_exception=>raise( 'unsupported DSYS version' ).

    ENDCASE.

    tadir_insert( iv_package ).

  ENDMETHOD.

  METHOD zif_abapgit_object~exists.

    DATA: lv_count TYPE i.

    SELECT SINGLE COUNT( * ) FROM dokil INTO lv_count
           WHERE id   = c_id
           AND object = mv_doc_object.                  "#EC CI_GENBUFF

    rv_bool = boolc( lv_count > 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( ).
    rs_metadata-delete_tadir = abap_true.
    rs_metadata-version = 'v2.0.0'.
  ENDMETHOD.

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

  METHOD zif_abapgit_object~is_locked.
    rv_is_locked = abap_false.
  ENDMETHOD.

  METHOD zif_abapgit_object~jump.

    DATA lv_lang TYPE sy-langu.

    lv_lang = get_master_lang( ).

    CALL FUNCTION 'DSYS_EDIT'
      EXPORTING
        dokclass         = mv_doc_object+0(4)
        dokname          = mv_doc_object+4(*)
        doklangu         = lv_lang
      EXCEPTIONS
        class_unknown    = 1
        object_not_found = 2
        OTHERS           = 3.

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

  ENDMETHOD.

  METHOD zif_abapgit_object~serialize.

    io_xml->i18n_params( abap_false ).

    zcl_abapgit_factory=>get_longtexts( )->serialize(
      iv_object_name = mv_doc_object
      iv_longtext_id = c_id
      io_xml         = io_xml ).

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_object_dsys DEFINITION PUBLIC INHERITING FROM zcl_abapgit_objects_super FINAL.

  PUBLIC SECTION.

    INTERFACES zif_abapgit_object.

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

  PROTECTED SECTION.

  PRIVATE SECTION.

    CONSTANTS: c_typ TYPE dokhl-typ VALUE 'E',
               c_id  TYPE dokhl-id VALUE 'HY'.

    DATA: mv_doc_object  TYPE sobj_name.

    TYPES: BEGIN OF ty_data,
             doctitle TYPE dsyst-doktitle,
             head     TYPE thead,
             lines    TYPE tline_tab,
           END OF ty_data.

    METHODS deserialize_dsys
      IMPORTING
        io_xml TYPE REF TO zcl_abapgit_xml_input
      RAISING
        zcx_abapgit_exception.

    METHODS get_master_lang
      RETURNING
        VALUE(rv_language) TYPE spras.

ENDCLASS.

CLASS ZCL_ABAPGIT_OBJECT_DSYS IMPLEMENTATION.

  METHOD constructor.

    DATA: lv_prefix    TYPE namespace,
          lv_bare_name TYPE progname.

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

    CALL FUNCTION 'RS_NAME_SPLIT_NAMESPACE'
      EXPORTING
        name_with_namespace    = ms_item-obj_name
      IMPORTING
        namespace              = lv_prefix
        name_without_namespace = lv_bare_name.

    mv_doc_object = |{ lv_bare_name+0(4) }{ lv_prefix }{ lv_bare_name+4(*) }|.

  ENDMETHOD.

  METHOD deserialize_dsys.

    DATA: ls_data      TYPE ty_data,
          ls_docu_info TYPE dokil,
          lv_version   TYPE dokvers,
          lv_doku_obj  TYPE doku_obj.

    lv_doku_obj = mv_doc_object.
    io_xml->read( EXPORTING iv_name = 'DSYS'
                  CHANGING cg_data = ls_data ).

    CALL FUNCTION 'DOCU_INIT'
      EXPORTING
        id     = c_id
        langu  = mv_language
        object = lv_doku_obj
        typ    = c_typ
      IMPORTING
        xdokil = ls_docu_info.

    lv_version = ls_docu_info-version.

    CALL FUNCTION 'DOCU_UPDATE'
      EXPORTING
        head    = ls_data-head
        state   = 'A'
        typ     = c_typ
        version = lv_version
      TABLES
        line    = ls_data-lines.

  ENDMETHOD.

  METHOD get_master_lang.

    DATA: lv_language TYPE spras.

    SELECT SINGLE langu FROM dokil INTO rv_language
      WHERE id = c_id
      AND object = mv_doc_object
      AND masterlang = abap_true.

    IF sy-subrc <> 0.
      rv_language = mv_language.
    ENDIF.

  ENDMETHOD.

  METHOD zif_abapgit_object~changed_by.

    rv_user = zcl_abapgit_factory=>get_longtexts( )->changed_by(
      iv_object_name = mv_doc_object
      iv_longtext_id = c_id ).

  ENDMETHOD.

  METHOD zif_abapgit_object~delete.

    zcl_abapgit_factory=>get_longtexts( )->delete(
      iv_object_name = mv_doc_object
      iv_longtext_id = c_id ).

  ENDMETHOD.

  METHOD zif_abapgit_object~deserialize.

    DATA: ls_metadata TYPE zif_abapgit_definitions=>ty_metadata.

    ls_metadata = io_xml->get_metadata( ).

    CASE ls_metadata-version.

      WHEN 'v1.0.0'.
        deserialize_dsys( io_xml ).

      WHEN 'v2.0.0'.
        zcl_abapgit_factory=>get_longtexts( )->deserialize(
          io_xml             = io_xml
          iv_master_language = mv_language ).

      WHEN OTHERS.
        zcx_abapgit_exception=>raise( 'unsupported DSYS version' ).

    ENDCASE.

    tadir_insert( iv_package ).

  ENDMETHOD.

  METHOD zif_abapgit_object~exists.

    DATA: lv_count TYPE i.

    SELECT SINGLE COUNT( * ) FROM dokil INTO lv_count
           WHERE id   = c_id
           AND object = mv_doc_object.                  "#EC CI_GENBUFF

    rv_bool = boolc( lv_count > 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( ).
    rs_metadata-delete_tadir = abap_true.
    rs_metadata-version = 'v2.0.0'.
  ENDMETHOD.

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

  METHOD zif_abapgit_object~is_locked.
    rv_is_locked = abap_false.
  ENDMETHOD.

  METHOD zif_abapgit_object~jump.

    DATA lv_lang TYPE sy-langu.

    lv_lang = get_master_lang( ).

    CALL FUNCTION 'DSYS_EDIT'
      EXPORTING
        dokclass         = mv_doc_object+0(4)
        dokname          = mv_doc_object+4(*)
        doklangu         = lv_lang
      EXCEPTIONS
        class_unknown    = 1
        object_not_found = 2
        OTHERS           = 3.

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

  ENDMETHOD.

  METHOD zif_abapgit_object~serialize.

    io_xml->i18n_params( abap_false ).

    zcl_abapgit_factory=>get_longtexts( )->serialize(
      iv_object_name = mv_doc_object
      iv_longtext_id = c_id
      io_xml         = io_xml ).

  ENDMETHOD.
ENDCLASS.

Fix dump when pulling dsys (#3686)

Fix dump when pulling dsys (#3686)

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


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

  PRIVATE SECTION.
    METHODS:
      setup,
      empty_include FOR TESTING RAISING cx_static_check,
      one_line_include FOR TESTING RAISING cx_static_check,
      one_line_include_2 FOR TESTING RAISING cx_static_check,
      one_line_include_3 FOR TESTING RAISING cx_static_check,
      two_line_include FOR TESTING RAISING cx_static_check,
      two_line_include_2 FOR TESTING RAISING cx_static_check,
      two_line_include_3 FOR TESTING RAISING cx_static_check,
      more_than_two_lines FOR TESTING RAISING cx_static_check,

      _given_source_is
        IMPORTING
          i_source TYPE LINE OF zif_abapgit_definitions=>ty_string_tt,
      _given_empty_test_include,
      _when_skip_is_calculated,
      _then_should_be_skipped,
      _then_should_not_be_skipped.

    DATA: mo_oo_serializer  TYPE REF TO zcl_abapgit_oo_serializer,
          mt_source         TYPE zif_abapgit_definitions=>ty_string_tt,
          mv_skip_testclass TYPE abap_bool.

ENDCLASS.

CLASS ltcl_oo_serialize IMPLEMENTATION.

  METHOD setup.

    CREATE OBJECT mo_oo_serializer.

  ENDMETHOD.

  METHOD empty_include.

    _given_empty_test_include( ).

    _when_skip_is_calculated( ).

    _then_should_be_skipped( ).

  ENDMETHOD.

  METHOD one_line_include.

    _given_source_is( `*"* use this source file for your ABAP unit test classes` ).

    _when_skip_is_calculated( ).

    _then_should_be_skipped( ).

  ENDMETHOD.

  METHOD one_line_include_2.

    _given_source_is( `*` ).

    _when_skip_is_calculated( ).

    _then_should_be_skipped( ).

  ENDMETHOD.

  METHOD one_line_include_3.

    _given_source_is( `write: 'This is ABAP'.` ).

    _when_skip_is_calculated( ).

    _then_should_not_be_skipped( ).

  ENDMETHOD.

  METHOD two_line_include.

    _given_source_is( `*"* use this source file for your ABAP unit test classes` ).
    _given_source_is( ``                                                         ).

    _when_skip_is_calculated( ).

    _then_should_be_skipped( ).

  ENDMETHOD.

  METHOD two_line_include_2.

    _given_source_is( `*"* use this source file for your ABAP unit test classes` ).
    _given_source_is( `write: 'This is ABAP'.`                                   ).

    _when_skip_is_calculated( ).

    _then_should_not_be_skipped( ).

  ENDMETHOD.

  METHOD two_line_include_3.

    _given_source_is( ` `                                                        ).
    _given_source_is( `*"* use this source file for your ABAP unit test classes` ).

    _when_skip_is_calculated( ).

    _then_should_not_be_skipped( ).

  ENDMETHOD.

  METHOD more_than_two_lines.

    _given_source_is( `*"* use this source file for your ABAP unit test classes` ).
    _given_source_is( `CLASS ltcl_test DEFINITION FINAL FOR TESTING`             ).
    _given_source_is( `  DURATION SHORT`                                         ).
    _given_source_is( `  RISK LEVEL HARMLESS.`                                   ).
    _given_source_is( ` `                                                        ).
    _given_source_is( `  PRIVATE SECTION.`                                       ).
    _given_source_is( `    METHODS:`                                             ).
    _given_source_is( `      first_test FOR TESTING RAISING cx_static_check.`    ).
    _given_source_is( `ENDCLASS.`                                                ).
    _given_source_is( ` `                                                        ).
    _given_source_is( `CLASS ltcl_test IMPLEMENTATION.`                          ).
    _given_source_is( ` `                                                        ).
    _given_source_is( `  METHOD first_test.`                                     ).
    _given_source_is( `    cl_abap_unit_assert=>fail( 'This is a real test' ).`  ).
    _given_source_is( `  ENDMETHOD.`                                             ).
    _given_source_is( ` `                                                        ).
    _given_source_is( `ENDCLASS.`                                                ).

    _when_skip_is_calculated( ).

    _then_should_not_be_skipped( ).

  ENDMETHOD.

  METHOD _given_source_is.

    INSERT i_source INTO TABLE mt_source.

  ENDMETHOD.

  METHOD _given_empty_test_include.

  ENDMETHOD.

  METHOD _when_skip_is_calculated.

    mv_skip_testclass = mo_oo_serializer->calculate_skip_testclass( mt_source ).

  ENDMETHOD.

  METHOD _then_should_be_skipped.

    cl_abap_unit_assert=>assert_equals(
      act = mv_skip_testclass
      exp = abap_true
      msg = |Testclass should be skipped| ).

  ENDMETHOD.

  METHOD _then_should_not_be_skipped.

    cl_abap_unit_assert=>assert_equals(
      act = mv_skip_testclass
      exp = abap_false
      msg = |Testclass should not be skipped| ).

  ENDMETHOD.

ENDCLASS.

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

  PRIVATE SECTION.
    METHODS:
      setup,
      empty_include FOR TESTING RAISING cx_static_check,
      one_line_include FOR TESTING RAISING cx_static_check,
      one_line_include_2 FOR TESTING RAISING cx_static_check,
      one_line_include_3 FOR TESTING RAISING cx_static_check,
      two_line_include FOR TESTING RAISING cx_static_check,
      two_line_include_2 FOR TESTING RAISING cx_static_check,
      two_line_include_3 FOR TESTING RAISING cx_static_check,
      more_than_two_lines FOR TESTING RAISING cx_static_check,

      _given_source_is
        IMPORTING
          iv_source TYPE LINE OF zif_abapgit_definitions=>ty_string_tt,
      _given_empty_test_include,
      _when_skip_is_calculated,
      _then_should_be_skipped,
      _then_should_not_be_skipped.

    DATA: mo_oo_serializer  TYPE REF TO zcl_abapgit_oo_serializer,
          mt_source         TYPE zif_abapgit_definitions=>ty_string_tt,
          mv_skip_testclass TYPE abap_bool.

ENDCLASS.

CLASS ltcl_oo_serialize IMPLEMENTATION.

  METHOD setup.

    CREATE OBJECT mo_oo_serializer.

  ENDMETHOD.

  METHOD empty_include.

    _given_empty_test_include( ).

    _when_skip_is_calculated( ).

    _then_should_be_skipped( ).

  ENDMETHOD.

  METHOD one_line_include.

    _given_source_is( `*"* use this source file for your ABAP unit test classes` ).

    _when_skip_is_calculated( ).

    _then_should_be_skipped( ).

  ENDMETHOD.

  METHOD one_line_include_2.

    _given_source_is( `*` ).

    _when_skip_is_calculated( ).

    _then_should_be_skipped( ).

  ENDMETHOD.

  METHOD one_line_include_3.

    _given_source_is( `write: 'This is ABAP'.` ).

    _when_skip_is_calculated( ).

    _then_should_not_be_skipped( ).

  ENDMETHOD.

  METHOD two_line_include.

    _given_source_is( `*"* use this source file for your ABAP unit test classes` ).
    _given_source_is( ``                                                         ).

    _when_skip_is_calculated( ).

    _then_should_be_skipped( ).

  ENDMETHOD.

  METHOD two_line_include_2.

    _given_source_is( `*"* use this source file for your ABAP unit test classes` ).
    _given_source_is( `write: 'This is ABAP'.`                                   ).

    _when_skip_is_calculated( ).

    _then_should_not_be_skipped( ).

  ENDMETHOD.

  METHOD two_line_include_3.

    _given_source_is( ` `                                                        ).
    _given_source_is( `*"* use this source file for your ABAP unit test classes` ).

    _when_skip_is_calculated( ).

    _then_should_not_be_skipped( ).

  ENDMETHOD.

  METHOD more_than_two_lines.

    _given_source_is( `*"* use this source file for your ABAP unit test classes` ).
    _given_source_is( `CLASS ltcl_test DEFINITION FINAL FOR TESTING`             ).
    _given_source_is( `  DURATION SHORT`                                         ).
    _given_source_is( `  RISK LEVEL HARMLESS.`                                   ).
    _given_source_is( ` `                                                        ).
    _given_source_is( `  PRIVATE SECTION.`                                       ).
    _given_source_is( `    METHODS:`                                             ).
    _given_source_is( `      first_test FOR TESTING RAISING cx_static_check.`    ).
    _given_source_is( `ENDCLASS.`                                                ).
    _given_source_is( ` `                                                        ).
    _given_source_is( `CLASS ltcl_test IMPLEMENTATION.`                          ).
    _given_source_is( ` `                                                        ).
    _given_source_is( `  METHOD first_test.`                                     ).
    _given_source_is( `    cl_abap_unit_assert=>fail( 'This is a real test' ).`  ).
    _given_source_is( `  ENDMETHOD.`                                             ).
    _given_source_is( ` `                                                        ).
    _given_source_is( `ENDCLASS.`                                                ).

    _when_skip_is_calculated( ).

    _then_should_not_be_skipped( ).

  ENDMETHOD.

  METHOD _given_source_is.

    INSERT iv_source INTO TABLE mt_source.

  ENDMETHOD.

  METHOD _given_empty_test_include.

  ENDMETHOD.

  METHOD _when_skip_is_calculated.

    mv_skip_testclass = mo_oo_serializer->calculate_skip_testclass( mt_source ).

  ENDMETHOD.

  METHOD _then_should_be_skipped.

    cl_abap_unit_assert=>assert_equals(
      act = mv_skip_testclass
      exp = abap_true
      msg = |Testclass should be skipped| ).

  ENDMETHOD.

  METHOD _then_should_not_be_skipped.

    cl_abap_unit_assert=>assert_equals(
      act = mv_skip_testclass
      exp = abap_false
      msg = |Testclass should not be skipped| ).

  ENDMETHOD.

ENDCLASS.

fix naming

fix naming


*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_FORMS
*&---------------------------------------------------------------------*

*&---------------------------------------------------------------------*
*&      Form  run
*&---------------------------------------------------------------------*
FORM run.

  DATA: lx_exception TYPE REF TO zcx_abapgit_exception.

  TRY.
      zcl_abapgit_migrations=>run( ).
      PERFORM open_gui.
    CATCH zcx_abapgit_exception INTO lx_exception.
      MESSAGE lx_exception TYPE 'E'.
  ENDTRY.

ENDFORM.                    "run

FORM open_gui RAISING zcx_abapgit_exception.

  IF sy-batch = abap_true.
    zcl_abapgit_background=>run( ).
  ELSE.

    zcl_abapgit_services_abapgit=>prepare_gui_startup( ).
    zcl_abapgit_ui_factory=>get_gui( )->go_home( ).
    CALL SELECTION-SCREEN 1001. " trigger screen

  ENDIF.

ENDFORM.

*&---------------------------------------------------------------------*
*&      Form  branch_popup
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->TT_FIELDS      text
*      -->PV_CODE        text
*      -->CS_ERROR       text
*      -->CV_SHOW_POPUP  text
*      -->RAISING        text
*      -->zcx_abapgit_exception  text
*      -->##CALLED       text
*      -->##NEEDED       text
*----------------------------------------------------------------------*
FORM branch_popup TABLES   tt_fields TYPE zif_abapgit_popups=>ty_sval_tt
                  USING    pv_code TYPE clike
                  CHANGING cs_error TYPE svale
                           cv_show_popup TYPE c
                  RAISING zcx_abapgit_exception ##called ##needed.
* called dynamically from function module POPUP_GET_VALUES_USER_BUTTONS

  DATA: lx_error  TYPE REF TO zcx_abapgit_exception,
        li_popups TYPE REF TO zif_abapgit_popups.

  TRY.
      li_popups = zcl_abapgit_ui_factory=>get_popups( ).
      li_popups->branch_popup_callback(
        EXPORTING
          iv_code       = pv_code
        CHANGING
          ct_fields     = tt_fields[]
          cs_error      = cs_error
          cv_show_popup = cv_show_popup ).

    CATCH zcx_abapgit_exception INTO lx_error.
      MESSAGE lx_error TYPE 'S' DISPLAY LIKE 'E'.
  ENDTRY.

ENDFORM.                    "branch_popup

FORM output.
  DATA: lt_ucomm TYPE TABLE OF sy-ucomm.

  PERFORM set_pf_status IN PROGRAM rsdbrunt IF FOUND.

  APPEND 'CRET' TO lt_ucomm.  "Button Execute
  APPEND 'SPOS' TO lt_ucomm.  "Button Save

  CALL FUNCTION 'RS_SET_SELSCREEN_STATUS'
    EXPORTING
      p_status  = sy-pfkey
    TABLES
      p_exclude = lt_ucomm.

ENDFORM.

FORM exit RAISING zcx_abapgit_exception.

  " The exit logic should only be applied for our 'main' selection screen 1001.
  " All other selection-screens are called as popups and shouldn't influence
  " the gui navigation as it would lead to inpredictable behaviour like dumps.
  IF sy-dynnr <> 1001.
    RETURN.
  ENDIF.

  CASE sy-ucomm.
    WHEN 'CBAC' OR 'CCAN'.  "Back & Escape
      IF zcl_abapgit_ui_factory=>get_gui( )->back( ) = abap_true. " end of stack
        zcl_abapgit_ui_factory=>get_gui( )->free( ). " Graceful shutdown
      ELSE.
        CALL SELECTION-SCREEN 1001.
      ENDIF.
  ENDCASE.
ENDFORM.

FORM password_popup
      USING
        pv_repo_url TYPE string
      CHANGING
        cv_user     TYPE string
        cv_pass     TYPE string.

  lcl_password_dialog=>popup(
    EXPORTING
      iv_repo_url     = pv_repo_url
    CHANGING
      cv_user         = cv_user
      cv_pass         = cv_pass ).

ENDFORM.

FORM remove_toolbar USING pv_dynnr TYPE sy-dynnr.

  DATA: ls_header               TYPE rpy_dyhead,
        lt_containers           TYPE dycatt_tab,
        lt_fields_to_containers TYPE dyfatc_tab,
        lt_flow_logic           TYPE swydyflow.

  CALL FUNCTION 'RPY_DYNPRO_READ'
    EXPORTING
      progname             = sy-cprog
      dynnr                = pv_dynnr
    IMPORTING
      header               = ls_header
    TABLES
      containers           = lt_containers
      fields_to_containers = lt_fields_to_containers
      flow_logic           = lt_flow_logic
    EXCEPTIONS
      cancelled            = 1
      not_found            = 2
      permission_error     = 3
      OTHERS               = 4.
  IF sy-subrc IS NOT INITIAL.
    RETURN. " Ignore errors, just exit
  ENDIF.

  IF ls_header-no_toolbar = abap_true.
    RETURN. " No change required
  ENDIF.

  ls_header-no_toolbar = abap_true.

  CALL FUNCTION 'RPY_DYNPRO_INSERT'
    EXPORTING
      header                 = ls_header
      suppress_exist_checks  = abap_true
    TABLES
      containers             = lt_containers
      fields_to_containers   = lt_fields_to_containers
      flow_logic             = lt_flow_logic
    EXCEPTIONS
      cancelled              = 1
      already_exists         = 2
      program_not_exists     = 3
      not_executed           = 4
      missing_required_field = 5
      illegal_field_value    = 6
      field_not_allowed      = 7
      not_generated          = 8
      illegal_field_position = 9
      OTHERS                 = 10.
  IF sy-subrc <> 2 AND sy-subrc <> 0.
    RETURN. " Ignore errors, just exit
  ENDIF.

ENDFORM.

*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_FORMS
*&---------------------------------------------------------------------*

*&---------------------------------------------------------------------*
*&      Form  run
*&---------------------------------------------------------------------*
FORM run.

  DATA: lx_exception TYPE REF TO zcx_abapgit_exception.

  TRY.
      zcl_abapgit_migrations=>run( ).
      PERFORM open_gui.
    CATCH zcx_abapgit_exception INTO lx_exception.
      MESSAGE lx_exception TYPE 'E'.
  ENDTRY.

ENDFORM.                    "run

FORM open_gui RAISING zcx_abapgit_exception.

  IF sy-batch = abap_true.
    zcl_abapgit_background=>run( ).
  ELSE.

    zcl_abapgit_services_abapgit=>prepare_gui_startup( ).
    zcl_abapgit_ui_factory=>get_gui( )->go_home( ).
    CALL SELECTION-SCREEN 1001. " trigger screen

  ENDIF.

ENDFORM.

*&---------------------------------------------------------------------*
*&      Form  branch_popup
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->TT_FIELDS      text
*      -->PV_CODE        text
*      -->CS_ERROR       text
*      -->CV_SHOW_POPUP  text
*      -->RAISING        text
*      -->zcx_abapgit_exception  text
*      -->##CALLED       text
*      -->##NEEDED       text
*----------------------------------------------------------------------*
FORM branch_popup TABLES   tt_fields TYPE zif_abapgit_popups=>ty_sval_tt
                  USING    pv_code TYPE clike
                  CHANGING cs_error TYPE svale
                           cv_show_popup TYPE c
                  RAISING zcx_abapgit_exception ##called ##needed.
* called dynamically from function module POPUP_GET_VALUES_USER_BUTTONS

  DATA: lx_error  TYPE REF TO zcx_abapgit_exception,
        li_popups TYPE REF TO zif_abapgit_popups.

  TRY.
      li_popups = zcl_abapgit_ui_factory=>get_popups( ).
      li_popups->branch_popup_callback(
        EXPORTING
          iv_code       = pv_code
        CHANGING
          ct_fields     = tt_fields[]
          cs_error      = cs_error
          cv_show_popup = cv_show_popup ).

    CATCH zcx_abapgit_exception INTO lx_error.
      MESSAGE lx_error TYPE 'S' DISPLAY LIKE 'E'.
  ENDTRY.

ENDFORM.                    "branch_popup

FORM output.
  DATA: lt_ucomm TYPE TABLE OF sy-ucomm.

  PERFORM set_pf_status IN PROGRAM rsdbrunt IF FOUND.

  APPEND 'CRET' TO lt_ucomm.  "Button Execute
  APPEND 'SPOS' TO lt_ucomm.  "Button Save

  CALL FUNCTION 'RS_SET_SELSCREEN_STATUS'
    EXPORTING
      p_status  = sy-pfkey
    TABLES
      p_exclude = lt_ucomm.

ENDFORM.

FORM exit RAISING zcx_abapgit_exception.

  " The exit logic should only be applied for our 'main' selection screen 1001.
  " All other selection-screens are called as popups and shouldn't influence
  " the gui navigation as it would lead to inpredictable behaviour like dumps.
  IF sy-dynnr <> 1001.
    RETURN.
  ENDIF.

  CASE sy-ucomm.
    WHEN 'CBAC' OR 'CCAN'.  "Back & Escape
      IF zcl_abapgit_ui_factory=>get_gui( )->back( ) = abap_true. " end of stack
        zcl_abapgit_ui_factory=>get_gui( )->free( ). " Graceful shutdown
      ELSE.
        LEAVE TO SCREEN 1001.
      ENDIF.
  ENDCASE.
ENDFORM.

FORM password_popup
      USING
        pv_repo_url TYPE string
      CHANGING
        cv_user     TYPE string
        cv_pass     TYPE string.

  lcl_password_dialog=>popup(
    EXPORTING
      iv_repo_url     = pv_repo_url
    CHANGING
      cv_user         = cv_user
      cv_pass         = cv_pass ).

ENDFORM.

FORM remove_toolbar USING pv_dynnr TYPE sy-dynnr.

  DATA: ls_header               TYPE rpy_dyhead,
        lt_containers           TYPE dycatt_tab,
        lt_fields_to_containers TYPE dyfatc_tab,
        lt_flow_logic           TYPE swydyflow.

  CALL FUNCTION 'RPY_DYNPRO_READ'
    EXPORTING
      progname             = sy-cprog
      dynnr                = pv_dynnr
    IMPORTING
      header               = ls_header
    TABLES
      containers           = lt_containers
      fields_to_containers = lt_fields_to_containers
      flow_logic           = lt_flow_logic
    EXCEPTIONS
      cancelled            = 1
      not_found            = 2
      permission_error     = 3
      OTHERS               = 4.
  IF sy-subrc IS NOT INITIAL.
    RETURN. " Ignore errors, just exit
  ENDIF.

  IF ls_header-no_toolbar = abap_true.
    RETURN. " No change required
  ENDIF.

  ls_header-no_toolbar = abap_true.

  CALL FUNCTION 'RPY_DYNPRO_INSERT'
    EXPORTING
      header                 = ls_header
      suppress_exist_checks  = abap_true
    TABLES
      containers             = lt_containers
      fields_to_containers   = lt_fields_to_containers
      flow_logic             = lt_flow_logic
    EXCEPTIONS
      cancelled              = 1
      already_exists         = 2
      program_not_exists     = 3
      not_executed           = 4
      missing_required_field = 5
      illegal_field_value    = 6
      field_not_allowed      = 7
      not_generated          = 8
      illegal_field_position = 9
      OTHERS                 = 10.
  IF sy-subrc <> 2 AND sy-subrc <> 0.
    RETURN. " Ignore errors, just exit
  ENDIF.

ENDFORM.

Fix dump when using abapGit for more than 50 navigation steps (#4288)

Reverting back to a proper exit of screens using `LEAVE TO SCREEN` and avoid increasing the size of the callstack.

Closes #4279

PS: The crash mentioned in #4027 was prevented by removing the commit hash from the check screen. It works also with `LEAVE TO SCREEN`.


*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_OBJECT_PINF
*&---------------------------------------------------------------------*

*----------------------------------------------------------------------*
*       CLASS lcl_object_PINF DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_object_pinf DEFINITION INHERITING FROM lcl_objects_super FINAL.

  PUBLIC SECTION.
    INTERFACES lif_object.

  PRIVATE SECTION.
    TYPES: BEGIN OF ty_pinf,
             attributes TYPE scompidtln,
             elements   TYPE STANDARD TABLE OF scomeldtln WITH DEFAULT KEY,
           END OF ty_pinf.

    TYPES: ty_elements TYPE STANDARD TABLE OF tpak_package_interf_elem_ref WITH DEFAULT KEY.

    METHODS:
      create_or_load
        IMPORTING is_pinf             TYPE ty_pinf
                  iv_package          TYPE devclass
        RETURNING VALUE(ri_interface) TYPE REF TO if_package_interface
        RAISING   lcx_exception,
      delete_elements
        IMPORTING ii_interface TYPE REF TO if_package_interface
        RAISING   lcx_exception,
      update_attributes
        IMPORTING is_pinf      TYPE ty_pinf
                  ii_interface TYPE REF TO if_package_interface
        RAISING   lcx_exception,
      update_elements
        IMPORTING is_pinf      TYPE ty_pinf
                  ii_interface TYPE REF TO if_package_interface
        RAISING   lcx_exception.

ENDCLASS.                    "lcl_object_PINF DEFINITION

*----------------------------------------------------------------------*
*       CLASS lcl_object_PINF IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_object_pinf IMPLEMENTATION.

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

  METHOD lif_object~changed_by.

    SELECT SINGLE changed_by FROM intf INTO rv_user
      WHERE intf_name = ms_item-obj_name.
    IF sy-subrc <> 0.
      rv_user = c_user_unknown.
    ENDIF.

  ENDMETHOD.

  METHOD lif_object~get_metadata.
    rs_metadata = get_metadata( ).
  ENDMETHOD.                    "lif_object~get_metadata

  METHOD lif_object~exists.

    DATA: lv_intf_name TYPE intf-intf_name.

    SELECT SINGLE intf_name FROM intf INTO lv_intf_name
      WHERE intf_name = ms_item-obj_name.
    rv_bool = boolc( sy-subrc = 0 ).

  ENDMETHOD.                    "lif_object~exists

  METHOD lif_object~serialize.

    DATA: ls_pinf      TYPE ty_pinf,
          lv_name      TYPE scomifnam,
          lt_elements  TYPE ty_elements,
          li_interface TYPE REF TO if_package_interface.

    FIELD-SYMBOLS: <lg_any>     TYPE any,
                   <li_element> LIKE LINE OF lt_elements,
                   <ls_element> LIKE LINE OF ls_pinf-elements.

    lv_name = ms_item-obj_name.

    cl_package_interface=>load_package_interface(
      EXPORTING
        i_package_interface_name = lv_name
        i_force_reload           = abap_true
      IMPORTING
        e_package_interface      = li_interface ).

    li_interface->get_all_attributes(
      IMPORTING e_package_interface_data = ls_pinf-attributes ).

    CLEAR: ls_pinf-attributes-pack_name,
           ls_pinf-attributes-author,
           ls_pinf-attributes-created_by,
           ls_pinf-attributes-created_on,
           ls_pinf-attributes-changed_by,
           ls_pinf-attributes-changed_on,
           ls_pinf-attributes-tadir_devc.

* fields does not exist in older SAP versions
    ASSIGN COMPONENT 'SW_COMP_LOGICAL_PACKAGE' OF STRUCTURE ls_pinf-attributes TO <lg_any>.
    IF sy-subrc = 0.
      CLEAR <lg_any>.
    ENDIF.
    ASSIGN COMPONENT 'SW_COMP_TADIR_PACKAGE' OF STRUCTURE ls_pinf-attributes TO <lg_any>.
    IF sy-subrc = 0.
      CLEAR <lg_any>.
    ENDIF.

    li_interface->get_elements( IMPORTING e_elements = lt_elements ).

    LOOP AT lt_elements ASSIGNING <li_element>.
      APPEND INITIAL LINE TO ls_pinf-elements ASSIGNING <ls_element>.
      <li_element>->get_all_attributes( IMPORTING e_element_data = <ls_element> ).
      CLEAR <ls_element>-elem_pack.
    ENDLOOP.

    io_xml->add( ig_data = ls_pinf
                 iv_name = 'PINF' ).

  ENDMETHOD.                    "serialize

  METHOD lif_object~deserialize.

    DATA: li_interface TYPE REF TO if_package_interface,
          ls_pinf      TYPE ty_pinf.

    io_xml->read( EXPORTING iv_name = 'PINF'
                  CHANGING cg_data = ls_pinf ).

    li_interface = create_or_load(
      is_pinf = ls_pinf
      iv_package = iv_package ).

    update_attributes(
      is_pinf      = ls_pinf
      ii_interface = li_interface ).

    update_elements(
      is_pinf      = ls_pinf
      ii_interface = li_interface ).

  ENDMETHOD.                    "deserialize

  METHOD update_attributes.

    DATA: ls_sign       TYPE scompisign,
          lv_changeable TYPE flag.

    ii_interface->get_changeable( IMPORTING e_changeable = lv_changeable ).
    IF lv_changeable = abap_false.
* at creation the object is already in change mode
      ii_interface->set_changeable( abap_true ).
    ENDIF.

    ls_sign-descript       = abap_true.
    ls_sign-pinftype       = abap_true.
    ls_sign-restricted     = abap_true.
    ls_sign-default_if     = abap_true.
    ls_sign-def_sever      = abap_true.
    ls_sign-acl_flag       = abap_true.
    ls_sign-pifstablty     = abap_true.
    ls_sign-release_status = abap_true.

    ii_interface->set_all_attributes(
      i_package_interface_data = is_pinf-attributes
      i_data_sign              = ls_sign ).

* looks like setting "i_suppress_dialog = abap_true" will make
* it fail for local($) packages
    ii_interface->save( ).

    ii_interface->set_changeable( abap_false ).

  ENDMETHOD.

  METHOD update_elements.

    DATA: lt_existing TYPE ty_elements,
          lt_add      TYPE scomeldata,
          lv_index    TYPE i,
          lv_found    TYPE abap_bool,
*          ls_sign     TYPE scomelsign,
          ls_attr     TYPE scomeldtln.

    FIELD-SYMBOLS: <li_element> LIKE LINE OF lt_existing,
                   <ls_element> LIKE LINE OF is_pinf-elements.

*    ls_sign-usag_restr                 = abap_true.
*    ls_sign-stability                  = abap_true.
*    ls_sign-no_check                   = abap_true.
*    ls_sign-useastype                  = abap_true.
*    ls_sign-asforgnkey                 = abap_true.
*    ls_sign-deprecation_type           = abap_true. backport
*    ls_sign-replacement_object_type    = abap_true. backport
*    ls_sign-replacement_object_name    = abap_true. backport
*    ls_sign-replacement_subobject_type = abap_true. backport
*    ls_sign-replacement_subobject_name = abap_true. backport

    ii_interface->set_elements_changeable( abap_true ).

    ii_interface->get_elements( IMPORTING e_elements = lt_existing ).

    LOOP AT is_pinf-elements ASSIGNING <ls_element>.

      lv_found = abap_false.
      LOOP AT lt_existing ASSIGNING <li_element>.
        lv_index = sy-tabix.
        <li_element>->get_all_attributes( IMPORTING e_element_data = ls_attr ).
        IF <ls_element>-elem_type = ls_attr-elem_type
            AND <ls_element>-elem_key = ls_attr-elem_key.
          DELETE lt_existing INDEX lv_index.
          CONTINUE. " current loop
        ENDIF.
      ENDLOOP.

      IF lv_found = abap_false.
        APPEND <ls_element> TO lt_add.
      ENDIF.
    ENDLOOP.

    ii_interface->remove_elements( lt_existing ).

    ii_interface->add_elements( lt_add ).

    ii_interface->save_elements( ).

    ii_interface->set_elements_changeable( abap_false ).

  ENDMETHOD.

  METHOD create_or_load.

    IF lif_object~exists( ) = abap_false.
      cl_package_interface=>create_new_package_interface(
        EXPORTING
          i_pkg_interface_name    = is_pinf-attributes-intf_name
          i_publisher_pkg_name    = iv_package
        IMPORTING
          e_package_interface     = ri_interface
        EXCEPTIONS
          object_already_existing = 1
          object_just_created     = 2
          interface_name_invalid  = 3
          unexpected_error        = 4
          OTHERS                  = 7 ).
      IF sy-subrc <> 0.
        lcx_exception=>raise( 'error creating new package interface' ).
      ENDIF.
    ELSE.
      cl_package_interface=>load_package_interface(
        EXPORTING
          i_package_interface_name   = is_pinf-attributes-intf_name
          i_force_reload             = abap_true
        IMPORTING
          e_package_interface        = ri_interface
        EXCEPTIONS
          db_read_error              = 1
          unexpected_error           = 2
          object_not_existing        = 3
          shorttext_not_existing     = 4
          object_locked_and_modified = 5
          OTHERS                     = 6 ).
      IF sy-subrc <> 0.
        lcx_exception=>raise( 'error loading package interface' ).
      ENDIF.
    ENDIF.

  ENDMETHOD.

  METHOD delete_elements.

    DATA: lt_elements TYPE ty_elements.

    FIELD-SYMBOLS: <li_element> LIKE LINE OF lt_elements.

    ii_interface->set_elements_changeable( abap_true ).

    ii_interface->get_elements( IMPORTING e_elements = lt_elements ).

    LOOP AT lt_elements ASSIGNING <li_element>.
      <li_element>->delete( ).
    ENDLOOP.

    ii_interface->save_elements( ).

  ENDMETHOD.

  METHOD lif_object~delete.

    DATA: lv_name      TYPE scomifnam,
          li_interface TYPE REF TO if_package_interface.

    lv_name = ms_item-obj_name.

    cl_package_interface=>load_package_interface(
      EXPORTING
        i_package_interface_name   = lv_name
      IMPORTING
        e_package_interface        = li_interface
      EXCEPTIONS
        db_read_error              = 1
        unexpected_error           = 2
        object_not_existing        = 3
        shorttext_not_existing     = 4
        object_locked_and_modified = 5
        OTHERS                     = 6 ).
    IF sy-subrc <> 0.
      lcx_exception=>raise( 'error loading package interface, delete' ).
    ENDIF.

* elements must be deleted before the package interface
* can be deleted
    delete_elements( li_interface ).

    li_interface->set_changeable( abap_true ).

    li_interface->delete( ).

    li_interface->save( ).

  ENDMETHOD.                    "delete

  METHOD lif_object~jump.

    CALL FUNCTION 'RS_TOOL_ACCESS'
      EXPORTING
        operation     = 'SHOW'
        object_name   = ms_item-obj_name
        object_type   = 'PINF'
        in_new_window = abap_true.

  ENDMETHOD.                    "jump

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

ENDCLASS.                    "lcl_object_PINF IMPLEMENTATION

*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_OBJECT_PINF
*&---------------------------------------------------------------------*

*----------------------------------------------------------------------*
*       CLASS lcl_object_PINF DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_object_pinf DEFINITION INHERITING FROM lcl_objects_super FINAL.

  PUBLIC SECTION.
    INTERFACES lif_object.

  PRIVATE SECTION.
    TYPES: BEGIN OF ty_pinf,
             attributes TYPE scompidtln,
             elements   TYPE STANDARD TABLE OF scomeldtln WITH DEFAULT KEY,
           END OF ty_pinf.

    TYPES: ty_elements TYPE STANDARD TABLE OF tpak_package_interf_elem_ref WITH DEFAULT KEY.

    METHODS:
      create_or_load
        IMPORTING is_pinf             TYPE ty_pinf
                  iv_package          TYPE devclass
        RETURNING VALUE(ri_interface) TYPE REF TO if_package_interface
        RAISING   lcx_exception,
      delete_elements
        IMPORTING ii_interface TYPE REF TO if_package_interface
        RAISING   lcx_exception,
      update_attributes
        IMPORTING is_pinf      TYPE ty_pinf
                  ii_interface TYPE REF TO if_package_interface
        RAISING   lcx_exception,
      update_elements
        IMPORTING is_pinf      TYPE ty_pinf
                  ii_interface TYPE REF TO if_package_interface
        RAISING   lcx_exception.

ENDCLASS.                    "lcl_object_PINF DEFINITION

*----------------------------------------------------------------------*
*       CLASS lcl_object_PINF IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_object_pinf IMPLEMENTATION.

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

  METHOD lif_object~changed_by.

    SELECT SINGLE changed_by FROM intf INTO rv_user
      WHERE intf_name = ms_item-obj_name.
    IF sy-subrc <> 0.
      rv_user = c_user_unknown.
    ENDIF.

  ENDMETHOD.

  METHOD lif_object~get_metadata.
    rs_metadata = get_metadata( ).
  ENDMETHOD.                    "lif_object~get_metadata

  METHOD lif_object~exists.

    DATA: lv_pack_name TYPE intf-pack_name,
          lv_main_pack TYPE tdevc-mainpack.

    SELECT SINGLE pack_name FROM intf INTO lv_pack_name
      WHERE intf_name = ms_item-obj_name.
    rv_bool = boolc( sy-subrc = 0 ).

    IF rv_bool = abap_true.
      SELECT SINGLE mainpack FROM tdevc INTO lv_main_pack
        WHERE devclass = lv_pack_name.
      rv_bool = boolc( sy-subrc = 0 ).
    ENDIF.

  ENDMETHOD.                    "lif_object~exists

  METHOD lif_object~serialize.

    DATA: ls_pinf      TYPE ty_pinf,
          lv_name      TYPE scomifnam,
          lt_elements  TYPE ty_elements,
          li_interface TYPE REF TO if_package_interface.

    FIELD-SYMBOLS: <lg_any>     TYPE any,
                   <li_element> LIKE LINE OF lt_elements,
                   <ls_element> LIKE LINE OF ls_pinf-elements.

    lv_name = ms_item-obj_name.

    cl_package_interface=>load_package_interface(
      EXPORTING
        i_package_interface_name = lv_name
        i_force_reload           = abap_true
      IMPORTING
        e_package_interface      = li_interface ).

    li_interface->get_all_attributes(
      IMPORTING e_package_interface_data = ls_pinf-attributes ).

    CLEAR: ls_pinf-attributes-pack_name,
           ls_pinf-attributes-author,
           ls_pinf-attributes-created_by,
           ls_pinf-attributes-created_on,
           ls_pinf-attributes-changed_by,
           ls_pinf-attributes-changed_on,
           ls_pinf-attributes-tadir_devc.

* fields does not exist in older SAP versions
    ASSIGN COMPONENT 'SW_COMP_LOGICAL_PACKAGE' OF STRUCTURE ls_pinf-attributes TO <lg_any>.
    IF sy-subrc = 0.
      CLEAR <lg_any>.
    ENDIF.
    ASSIGN COMPONENT 'SW_COMP_TADIR_PACKAGE' OF STRUCTURE ls_pinf-attributes TO <lg_any>.
    IF sy-subrc = 0.
      CLEAR <lg_any>.
    ENDIF.

    li_interface->get_elements( IMPORTING e_elements = lt_elements ).

    LOOP AT lt_elements ASSIGNING <li_element>.
      APPEND INITIAL LINE TO ls_pinf-elements ASSIGNING <ls_element>.
      <li_element>->get_all_attributes( IMPORTING e_element_data = <ls_element> ).
      CLEAR <ls_element>-elem_pack.
    ENDLOOP.

    io_xml->add( ig_data = ls_pinf
                 iv_name = 'PINF' ).

  ENDMETHOD.                    "serialize

  METHOD lif_object~deserialize.

    DATA: li_interface TYPE REF TO if_package_interface,
          ls_pinf      TYPE ty_pinf.

    io_xml->read( EXPORTING iv_name = 'PINF'
                  CHANGING cg_data = ls_pinf ).

    li_interface = create_or_load(
      is_pinf = ls_pinf
      iv_package = iv_package ).

    update_attributes(
      is_pinf      = ls_pinf
      ii_interface = li_interface ).

    update_elements(
      is_pinf      = ls_pinf
      ii_interface = li_interface ).

  ENDMETHOD.                    "deserialize

  METHOD update_attributes.

    DATA: ls_sign       TYPE scompisign,
          lv_changeable TYPE flag.

    ii_interface->get_changeable( IMPORTING e_changeable = lv_changeable ).
    IF lv_changeable = abap_false.
* at creation the object is already in change mode
      ii_interface->set_changeable( abap_true ).
    ENDIF.

    ls_sign-descript       = abap_true.
    ls_sign-pinftype       = abap_true.
    ls_sign-restricted     = abap_true.
    ls_sign-default_if     = abap_true.
    ls_sign-def_sever      = abap_true.
    ls_sign-acl_flag       = abap_true.
    ls_sign-pifstablty     = abap_true.
    ls_sign-release_status = abap_true.

    ii_interface->set_all_attributes(
      i_package_interface_data = is_pinf-attributes
      i_data_sign              = ls_sign ).

* looks like setting "i_suppress_dialog = abap_true" will make
* it fail for local($) packages
    ii_interface->save( ).

    ii_interface->set_changeable( abap_false ).

  ENDMETHOD.

  METHOD update_elements.

    DATA: lt_existing TYPE ty_elements,
          lt_add      TYPE scomeldata,
          lv_index    TYPE i,
          lv_found    TYPE abap_bool,
*          ls_sign     TYPE scomelsign,
          ls_attr     TYPE scomeldtln.

    FIELD-SYMBOLS: <li_element> LIKE LINE OF lt_existing,
                   <ls_element> LIKE LINE OF is_pinf-elements.

*    ls_sign-usag_restr                 = abap_true.
*    ls_sign-stability                  = abap_true.
*    ls_sign-no_check                   = abap_true.
*    ls_sign-useastype                  = abap_true.
*    ls_sign-asforgnkey                 = abap_true.
*    ls_sign-deprecation_type           = abap_true. backport
*    ls_sign-replacement_object_type    = abap_true. backport
*    ls_sign-replacement_object_name    = abap_true. backport
*    ls_sign-replacement_subobject_type = abap_true. backport
*    ls_sign-replacement_subobject_name = abap_true. backport

    ii_interface->set_elements_changeable( abap_true ).

    ii_interface->get_elements( IMPORTING e_elements = lt_existing ).

    LOOP AT is_pinf-elements ASSIGNING <ls_element>.

      lv_found = abap_false.
      LOOP AT lt_existing ASSIGNING <li_element>.
        lv_index = sy-tabix.
        <li_element>->get_all_attributes( IMPORTING e_element_data = ls_attr ).
        IF <ls_element>-elem_type = ls_attr-elem_type
            AND <ls_element>-elem_key = ls_attr-elem_key.
          DELETE lt_existing INDEX lv_index.
          CONTINUE. " current loop
        ENDIF.
      ENDLOOP.

      IF lv_found = abap_false.
        APPEND <ls_element> TO lt_add.
      ENDIF.
    ENDLOOP.

    ii_interface->remove_elements( lt_existing ).

    ii_interface->add_elements( lt_add ).

    ii_interface->save_elements( ).

    ii_interface->set_elements_changeable( abap_false ).

  ENDMETHOD.

  METHOD create_or_load.

    IF lif_object~exists( ) = abap_false.
      cl_package_interface=>create_new_package_interface(
        EXPORTING
          i_pkg_interface_name    = is_pinf-attributes-intf_name
          i_publisher_pkg_name    = iv_package
        IMPORTING
          e_package_interface     = ri_interface
        EXCEPTIONS
          object_already_existing = 1
          object_just_created     = 2
          interface_name_invalid  = 3
          unexpected_error        = 4
          OTHERS                  = 7 ).
      IF sy-subrc <> 0.
        lcx_exception=>raise( 'error creating new package interface' ).
      ENDIF.
    ELSE.
      cl_package_interface=>load_package_interface(
        EXPORTING
          i_package_interface_name   = is_pinf-attributes-intf_name
          i_force_reload             = abap_true
        IMPORTING
          e_package_interface        = ri_interface
        EXCEPTIONS
          db_read_error              = 1
          unexpected_error           = 2
          object_not_existing        = 3
          shorttext_not_existing     = 4
          object_locked_and_modified = 5
          OTHERS                     = 6 ).
      IF sy-subrc <> 0.
        lcx_exception=>raise( 'error loading package interface' ).
      ENDIF.
    ENDIF.

  ENDMETHOD.

  METHOD delete_elements.

    DATA: lt_elements TYPE ty_elements.

    FIELD-SYMBOLS: <li_element> LIKE LINE OF lt_elements.

    ii_interface->set_elements_changeable( abap_true ).

    ii_interface->get_elements( IMPORTING e_elements = lt_elements ).

    LOOP AT lt_elements ASSIGNING <li_element>.
      <li_element>->delete( ).
    ENDLOOP.

    ii_interface->save_elements( ).

  ENDMETHOD.

  METHOD lif_object~delete.

    DATA: lv_name      TYPE scomifnam,
          li_interface TYPE REF TO if_package_interface.

    lv_name = ms_item-obj_name.

    cl_package_interface=>load_package_interface(
      EXPORTING
        i_package_interface_name   = lv_name
      IMPORTING
        e_package_interface        = li_interface
      EXCEPTIONS
        db_read_error              = 1
        unexpected_error           = 2
        object_not_existing        = 3
        shorttext_not_existing     = 4
        object_locked_and_modified = 5
        OTHERS                     = 6 ).
    IF sy-subrc <> 0.
      lcx_exception=>raise( 'error loading package interface, delete' ).
    ENDIF.

* elements must be deleted before the package interface
* can be deleted
    delete_elements( li_interface ).

    li_interface->set_changeable( abap_true ).

    li_interface->delete( ).

    li_interface->save( ).

  ENDMETHOD.                    "delete

  METHOD lif_object~jump.

    CALL FUNCTION 'RS_TOOL_ACCESS'
      EXPORTING
        operation     = 'SHOW'
        object_name   = ms_item-obj_name
        object_type   = 'PINF'
        in_new_window = abap_true.

  ENDMETHOD.                    "jump

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

ENDCLASS.                    "lcl_object_PINF IMPLEMENTATION

fix exists

PINF: fix exists