abapGit/src/zabapgit_xml.prog.abap
Thomas Erdösi a0a6dd7f39 Bugif for issue #995: Includes without textpools
Includes without a textpool (%3Dinclude description) are created are either as includes with an empty description (first include) or using the description of the preceding textpool. This issue can be reproduced with function groups created on SAP releases older than 7.31.
2017-10-13 14:32:15 +02:00

461 lines
14 KiB
ABAP

*&---------------------------------------------------------------------*
*& Include ZABAPGIT_XML
*&---------------------------------------------------------------------*
*----------------------------------------------------------------------*
* CLASS lcl_xml DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_xml DEFINITION ABSTRACT.
PUBLIC SECTION.
METHODS:
constructor.
PROTECTED SECTION.
DATA: mi_ixml TYPE REF TO if_ixml,
mi_xml_doc TYPE REF TO if_ixml_document,
ms_metadata TYPE zif_abapgit_definitions=>ty_metadata.
CONSTANTS: c_abapgit_tag TYPE string VALUE 'abapGit' ##NO_TEXT,
c_attr_version TYPE string VALUE 'version' ##NO_TEXT,
c_attr_serializer TYPE string VALUE 'serializer' ##NO_TEXT,
c_attr_serializer_version TYPE string VALUE 'serializer_version' ##NO_TEXT.
METHODS to_xml
IMPORTING iv_normalize TYPE sap_bool DEFAULT abap_true
RETURNING VALUE(rv_xml) TYPE string.
METHODS parse
IMPORTING iv_normalize TYPE abap_bool DEFAULT abap_true
iv_xml TYPE string
RAISING zcx_abapgit_exception.
PRIVATE SECTION.
METHODS error
IMPORTING ii_parser TYPE REF TO if_ixml_parser
RAISING zcx_abapgit_exception.
METHODS display_xml_error
RAISING zcx_abapgit_exception.
ENDCLASS. "lcl_xml DEFINITION
*----------------------------------------------------------------------*
* CLASS lcl_xml IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_xml IMPLEMENTATION.
METHOD constructor.
mi_ixml = cl_ixml=>create( ).
mi_xml_doc = mi_ixml->create_document( ).
ENDMETHOD. "constructor
METHOD parse.
DATA: li_stream_factory TYPE REF TO if_ixml_stream_factory,
li_istream TYPE REF TO if_ixml_istream,
li_element TYPE REF TO if_ixml_element,
li_version TYPE REF TO if_ixml_node,
li_parser TYPE REF TO if_ixml_parser.
ASSERT NOT iv_xml IS INITIAL.
li_stream_factory = mi_ixml->create_stream_factory( ).
li_istream = li_stream_factory->create_istream_string( iv_xml ).
li_parser = mi_ixml->create_parser( stream_factory = li_stream_factory
istream = li_istream
document = mi_xml_doc ).
li_parser->set_normalizing( iv_normalize ).
IF li_parser->parse( ) <> 0.
error( li_parser ).
ENDIF.
li_istream->close( ).
li_element = mi_xml_doc->find_from_name_ns( depth = 0 name = c_abapgit_tag ).
li_version = li_element->if_ixml_node~get_attributes(
)->get_named_item_ns( c_attr_version ) ##no_text.
IF li_version->get_value( ) <> zif_abapgit_definitions=>gc_xml_version.
display_xml_error( ).
ENDIF.
* buffer serializer metadata. Git node will be removed lateron
ms_metadata-class = li_element->get_attribute_ns( c_attr_serializer ).
ms_metadata-version = li_element->get_attribute_ns( c_attr_serializer_version ).
ENDMETHOD. "parse
METHOD display_xml_error.
DATA: lv_version TYPE string.
lv_version = |abapGit version: { zif_abapgit_definitions=>gc_abap_version }|.
CALL FUNCTION 'POPUP_TO_INFORM'
EXPORTING
titel = 'abapGit XML version mismatch'
txt1 = 'abapGit XML version mismatch'
txt2 = 'See http://larshp.github.io/abapGit/other-xml-mismatch.html'
txt3 = lv_version. "#EC NOTEXT
zcx_abapgit_exception=>raise( 'XML error' ).
ENDMETHOD. "display_xml_error
METHOD to_xml.
* will render to codepage UTF-16
DATA: li_ostream TYPE REF TO if_ixml_ostream,
li_renderer TYPE REF TO if_ixml_renderer,
li_streamfactory TYPE REF TO if_ixml_stream_factory.
li_streamfactory = mi_ixml->create_stream_factory( ).
li_ostream = li_streamfactory->create_ostream_cstring( rv_xml ).
li_renderer = mi_ixml->create_renderer( ostream = li_ostream
document = mi_xml_doc ).
li_renderer->set_normalizing( iv_normalize ).
li_renderer->render( ).
ENDMETHOD. "to_xml
METHOD error.
DATA: lv_error TYPE i,
lv_txt1 TYPE string,
lv_txt2 TYPE string,
lv_txt3 TYPE string,
lv_times TYPE i,
li_error TYPE REF TO if_ixml_parse_error.
IF ii_parser->num_errors( ) <> 0.
lv_times = ii_parser->num_errors( ).
DO lv_times TIMES.
lv_error = sy-index - 1.
li_error = ii_parser->get_error( lv_error ).
lv_txt1 = li_error->get_column( ).
CONCATENATE 'Column:' lv_txt1 INTO lv_txt1. "#EC NOTEXT
lv_txt2 = li_error->get_line( ).
CONCATENATE 'Line:' lv_txt2 INTO lv_txt2. "#EC NOTEXT
lv_txt3 = li_error->get_reason( ).
CALL FUNCTION 'POPUP_TO_INFORM'
EXPORTING
titel = 'Error from XML parser' "#EC NOTEXT
txt1 = lv_txt1
txt2 = lv_txt2
txt3 = lv_txt3.
ENDDO.
ENDIF.
zcx_abapgit_exception=>raise( 'Error while parsing XML' ).
ENDMETHOD. "error
ENDCLASS. "lcl_xml IMPLEMENTATION
*----------------------------------------------------------------------*
* CLASS lcl_xml_output DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_xml_output DEFINITION FINAL INHERITING FROM lcl_xml CREATE PUBLIC.
PUBLIC SECTION.
METHODS:
add
IMPORTING iv_name TYPE clike
ig_data TYPE any
RAISING zcx_abapgit_exception,
set_raw
IMPORTING ii_raw TYPE REF TO if_ixml_element,
add_xml
IMPORTING iv_name TYPE clike
ii_xml TYPE REF TO if_ixml_element,
build_asx_node
RETURNING VALUE(ri_element) TYPE REF TO if_ixml_element,
render
IMPORTING iv_normalize TYPE sap_bool DEFAULT abap_true
is_metadata TYPE zif_abapgit_definitions=>ty_metadata OPTIONAL
RETURNING VALUE(rv_xml) TYPE string.
PRIVATE SECTION.
DATA: mi_raw TYPE REF TO if_ixml_element.
ENDCLASS. "lcl_xml_output DEFINITION
*----------------------------------------------------------------------*
* CLASS lcl_xml_output IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_xml_output IMPLEMENTATION.
METHOD set_raw.
mi_raw = ii_raw.
ENDMETHOD. "set_raw
METHOD add.
DATA: li_node TYPE REF TO if_ixml_node,
li_doc TYPE REF TO if_ixml_document,
lt_stab TYPE abap_trans_srcbind_tab.
FIELD-SYMBOLS: <ls_stab> LIKE LINE OF lt_stab.
ASSERT NOT iv_name IS INITIAL.
IF ig_data IS INITIAL.
RETURN.
ENDIF.
APPEND INITIAL LINE TO lt_stab ASSIGNING <ls_stab>.
<ls_stab>-name = iv_name.
GET REFERENCE OF ig_data INTO <ls_stab>-value.
li_doc = cl_ixml=>create( )->create_document( ).
CALL TRANSFORMATION id
OPTIONS initial_components = 'suppress'
SOURCE (lt_stab)
RESULT XML li_doc.
li_node = mi_xml_doc->get_root( )->get_first_child( ).
IF li_node IS BOUND.
mi_xml_doc->get_root( )->get_first_child( )->get_first_child( )->append_child(
li_doc->get_root( )->get_first_child( )->get_first_child( )->get_first_child( ) ).
ELSE.
mi_xml_doc->get_root( )->append_child( li_doc->get_root( )->get_first_child( ) ).
ENDIF.
ENDMETHOD.
METHOD add_xml.
DATA: li_element TYPE REF TO if_ixml_element.
li_element = mi_xml_doc->create_element( iv_name ).
li_element->append_child( ii_xml ).
mi_xml_doc->get_root( )->get_first_child( )->get_first_child( )->append_child( li_element ).
ENDMETHOD.
METHOD render.
DATA: li_git TYPE REF TO if_ixml_element,
li_abap TYPE REF TO if_ixml_element.
IF mi_raw IS INITIAL.
li_abap ?= mi_xml_doc->get_root( )->get_first_child( ).
mi_xml_doc->get_root( )->remove_child( li_abap ).
IF li_abap IS INITIAL.
li_abap = build_asx_node( ).
ENDIF.
ELSE.
li_abap = mi_raw.
ENDIF.
li_git = mi_xml_doc->create_element( c_abapgit_tag ).
li_git->set_attribute( name = c_attr_version value = zif_abapgit_definitions=>gc_xml_version ).
IF NOT is_metadata IS INITIAL.
li_git->set_attribute( name = c_attr_serializer
value = is_metadata-class ).
li_git->set_attribute( name = c_attr_serializer_version
value = is_metadata-version ).
ENDIF.
li_git->append_child( li_abap ).
mi_xml_doc->get_root( )->append_child( li_git ).
rv_xml = to_xml( iv_normalize ).
ENDMETHOD. "render
METHOD build_asx_node.
DATA: li_attr TYPE REF TO if_ixml_attribute.
ri_element = mi_xml_doc->create_element_ns(
name = 'abap'
prefix = 'asx' ).
li_attr = mi_xml_doc->create_attribute_ns( 'version' ).
li_attr->if_ixml_node~set_value( '1.0' ).
ri_element->set_attribute_node_ns( li_attr ).
li_attr = mi_xml_doc->create_attribute_ns(
name = 'asx'
prefix = 'xmlns' ).
li_attr->if_ixml_node~set_value( 'http://www.sap.com/abapxml' ).
ri_element->set_attribute_node_ns( li_attr ).
ENDMETHOD.
ENDCLASS. "lcl_xml_output IMPLEMENTATION
*----------------------------------------------------------------------*
* CLASS lcl_xml_input DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_xml_input DEFINITION FINAL INHERITING FROM lcl_xml CREATE PUBLIC.
PUBLIC SECTION.
METHODS:
constructor
IMPORTING iv_xml TYPE clike
RAISING zcx_abapgit_exception,
read
IMPORTING iv_name TYPE clike
CHANGING cg_data TYPE any
RAISING zcx_abapgit_exception,
get_raw
RETURNING VALUE(ri_raw) TYPE REF TO if_ixml_document,
* todo, add read_xml to match add_xml in lcl_xml_output
get_metadata
RETURNING VALUE(rs_metadata) TYPE zif_abapgit_definitions=>ty_metadata.
PRIVATE SECTION.
METHODS: fix_xml.
ENDCLASS. "lcl_xml_input DEFINITION
*----------------------------------------------------------------------*
* CLASS lcl_xml_input IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_xml_input IMPLEMENTATION.
METHOD constructor.
super->constructor( ).
parse( iv_xml ).
fix_xml( ).
ENDMETHOD. "constructor
METHOD get_raw.
ri_raw = mi_xml_doc.
ENDMETHOD. "get_raw
METHOD fix_xml.
DATA: li_git TYPE REF TO if_ixml_element,
li_abap TYPE REF TO if_ixml_node.
li_git ?= mi_xml_doc->find_from_name_ns( depth = 0 name = c_abapgit_tag ).
li_abap = li_git->get_first_child( ).
mi_xml_doc->get_root( )->remove_child( li_git ).
mi_xml_doc->get_root( )->append_child( li_abap ).
ENDMETHOD. "fix_xml
METHOD read.
DATA: lx_error TYPE REF TO cx_transformation_error,
lt_rtab TYPE abap_trans_resbind_tab.
FIELD-SYMBOLS: <ls_rtab> LIKE LINE OF lt_rtab.
ASSERT NOT iv_name IS INITIAL.
CLEAR cg_data. "Initialize result to avoid problems with empty values
APPEND INITIAL LINE TO lt_rtab ASSIGNING <ls_rtab>.
<ls_rtab>-name = iv_name.
GET REFERENCE OF cg_data INTO <ls_rtab>-value.
TRY.
CALL TRANSFORMATION id
OPTIONS value_handling = 'accept_data_loss'
SOURCE XML mi_xml_doc
RESULT (lt_rtab) ##no_text.
CATCH cx_transformation_error INTO lx_error.
zcx_abapgit_exception=>raise( lx_error->if_message~get_text( ) ).
ENDTRY.
ENDMETHOD. "read
METHOD get_metadata.
rs_metadata = ms_metadata.
ENDMETHOD. "get_metadata
ENDCLASS. "lcl_xml_input IMPLEMENTATION
CLASS lcl_xml_pretty DEFINITION FINAL.
PUBLIC SECTION.
CLASS-METHODS: print
IMPORTING iv_xml TYPE string
iv_ignore_errors TYPE abap_bool DEFAULT abap_true
iv_unpretty TYPE abap_bool DEFAULT abap_false
RETURNING VALUE(rv_xml) TYPE string
RAISING zcx_abapgit_exception.
ENDCLASS.
CLASS lcl_xml_pretty IMPLEMENTATION.
METHOD print.
DATA: li_ixml TYPE REF TO if_ixml,
li_xml_doc TYPE REF TO if_ixml_document,
li_stream_factory TYPE REF TO if_ixml_stream_factory,
li_istream TYPE REF TO if_ixml_istream,
li_parser TYPE REF TO if_ixml_parser,
li_ostream TYPE REF TO if_ixml_ostream,
li_renderer TYPE REF TO if_ixml_renderer.
ASSERT NOT iv_xml IS INITIAL.
li_ixml = cl_ixml=>create( ).
li_xml_doc = li_ixml->create_document( ).
li_stream_factory = li_ixml->create_stream_factory( ).
li_istream = li_stream_factory->create_istream_string( iv_xml ).
li_parser = li_ixml->create_parser( stream_factory = li_stream_factory
istream = li_istream
document = li_xml_doc ).
li_parser->set_normalizing( abap_true ).
IF li_parser->parse( ) <> 0.
IF iv_ignore_errors = abap_true.
rv_xml = iv_xml.
RETURN.
ELSE.
zcx_abapgit_exception=>raise( 'error parsing xml' ).
ENDIF.
ENDIF.
li_istream->close( ).
li_ostream = li_stream_factory->create_ostream_cstring( rv_xml ).
li_renderer = li_ixml->create_renderer( ostream = li_ostream
document = li_xml_doc ).
li_renderer->set_normalizing( boolc( iv_unpretty = abap_false ) ).
li_renderer->render( ).
ENDMETHOD.
ENDCLASS.