From 976bedb12037d55cd5485678cc895a6bfddf73ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BCdiger=20Plantiko?= Date: Wed, 7 May 2014 15:19:00 +0200 Subject: [PATCH] Reader Refact & INCOMPATIBLE remove CAN_READ_FILE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Removed interface method ZOF_EXCEL_READER~CAN_READ_FILE As was suggested in the comments, the method ZIF_EXCEL_READER~CAN_READ_FILE is superfluous, as the exception ZCX_EXCEL in LOAD( ) / LOAD_FILE( ) already gives this information. This is an incompatible change! But it's unlikely that this method is used by many clients, as it didn't to anything useful. Also, the demo programs don't use it. 2. Switched the zip archive reader creation from the former "on demand" creation to a one-time creation in the LOAD method. Necessary for the next point: 3. Removed private attribute EXCEL2007 (which kept the input XSTRING): After generation of the zip archive, these data are not necessary any more. 4. Factored out Stefan Schmöcker's "alternate zip" concept into a local subclass: The object "zip" refers to a local class - which is either a proxy to CL_ABAP_ZIP, or creates a zip object of dynamic type and calls its GET method of a class dynamically (the "alternate zip" case). Depending on the answer of my question https://github.com/ivanfemia/abap2xlsx/issues/232#issuecomment-42115340 this abstraction could be removed in a forthcoming commit as soon as it has been clarified that CL_ABAP_ZIP works as expected in all cases. 5. Further cleaning.- Removed some comments like "ToDo: Comment this" if necessary, and also some misleading comments which gave unwanted attention to a certain defect (issue 234) and the circumstances of that defect, thereby obfuscating the far more general nature of the commented code. --- ZA2X/CLAS/ZCL_EXCEL_READER_2007.slnk | 562 +++++++++++++-------------- 1 file changed, 267 insertions(+), 295 deletions(-) diff --git a/ZA2X/CLAS/ZCL_EXCEL_READER_2007.slnk b/ZA2X/CLAS/ZCL_EXCEL_READER_2007.slnk index b7bff91..3817624 100644 --- a/ZA2X/CLAS/ZCL_EXCEL_READER_2007.slnk +++ b/ZA2X/CLAS/ZCL_EXCEL_READER_2007.slnk @@ -1,6 +1,6 @@ - - + - - - - - - - - - - - - - - - - *"* local class implementation for public class @@ -88,61 +88,175 @@ id TYPE string, type TYPE string, target TYPE string, - END OF t_relationship. + END OF t_relationship. + +* + CLASS lcl_abap_zip_archive DEFINITION + INHERITING FROM lcl_zip_archive + CREATE PRIVATE. + PUBLIC SECTION. + CLASS-METHODS create + IMPORTING i_data TYPE xstring + RETURNING value(r_zip) TYPE REF TO lcl_zip_archive + RAISING zcx_excel. + METHODS read REDEFINITION. + PRIVATE SECTION. + DATA: abap_zip TYPE REF TO cl_abap_zip. + METHODS constructor IMPORTING i_data TYPE xstring + RAISING zcx_excel. + ENDCLASS. "lcl_abap_zip_archive DEFINITION + +* + CLASS lcl_alternate_zip_archive DEFINITION + INHERITING FROM lcl_zip_archive + CREATE PRIVATE. + PUBLIC SECTION. + CLASS-METHODS create + IMPORTING i_data TYPE xstring + i_alternate_zip_class TYPE seoclsname + RETURNING value(r_zip) TYPE REF TO lcl_zip_archive + RAISING zcx_excel. + METHODS read REDEFINITION. + PRIVATE SECTION. + DATA: alternate_zip TYPE REF TO object. + METHODS constructor + IMPORTING i_data TYPE xstring + i_alternate_zip_class TYPE seoclsname + RAISING zcx_excel. + ENDCLASS. "lcl_alternate_zip_archive DEFINITION + +* + CLASS lcl_abap_zip_archive IMPLEMENTATION. + METHOD create. + CREATE OBJECT r_zip TYPE lcl_abap_zip_archive + EXPORTING + i_data = i_data. + ENDMETHOD. "create + METHOD constructor. + DATA: lv_errormessage TYPE string. + super->constructor( ). + CREATE OBJECT abap_zip. + abap_zip->load( + EXPORTING + zip = i_data + EXCEPTIONS + zip_parse_error = 1 + OTHERS = 2 ). + IF sy-subrc <> 0. + lv_errormessage = 'ZIP parse error'(002). + RAISE EXCEPTION TYPE zcx_excel + EXPORTING + error = lv_errormessage. + ENDIF. + + ENDMETHOD. "constructor + METHOD read. + DATA: lv_errormessage TYPE string. + CALL METHOD abap_zip->get + EXPORTING + name = i_filename + IMPORTING + content = r_content + EXCEPTIONS + zip_index_error = 1 + zip_decompression_error = 2 + OTHERS = 3. + IF sy-subrc <> 0. + lv_errormessage = 'File not found in zip-archive'(003). + RAISE EXCEPTION TYPE zcx_excel + EXPORTING + error = lv_errormessage. + ENDIF. + + ENDMETHOD. "read + ENDCLASS. "lcl_abap_zip_archive IMPLEMENTATION + +* + CLASS lcl_alternate_zip_archive IMPLEMENTATION. + METHOD create. + CREATE OBJECT r_zip TYPE lcl_alternate_zip_archive + EXPORTING + i_alternate_zip_class = i_alternate_zip_class + i_data = i_data. + ENDMETHOD. "create + METHOD constructor. + DATA: lv_errormessage TYPE string. + super->constructor( ). + CREATE OBJECT alternate_zip TYPE (i_alternate_zip_class). + TRY. + CALL METHOD alternate_zip->('LOAD') + EXPORTING + zip = i_data + EXCEPTIONS + zip_parse_error = 1 + OTHERS = 2. + CATCH cx_sy_dyn_call_illegal_method. + lv_errormessage = 'Method LOAD missing in alternative zipclass'. "#EC NOTEXT This is a workaround until class CL_ABAP_ZIP is fixed + RAISE EXCEPTION TYPE zcx_excel + EXPORTING + error = lv_errormessage. + ENDTRY. + + IF sy-subrc <> 0. + lv_errormessage = 'ZIP parse error'(002). + RAISE EXCEPTION TYPE zcx_excel + EXPORTING + error = lv_errormessage. + ENDIF. + + ENDMETHOD. "constructor + METHOD read. + DATA: lv_errormessage TYPE string. + TRY. + CALL METHOD alternate_zip->('GET') + EXPORTING + name = i_filename + IMPORTING + content = r_content " Contents + EXCEPTIONS + zip_index_error = 1 + zip_decompression_error = 2 + OTHERS = 3. + CATCH cx_sy_dyn_call_illegal_method. + lv_errormessage = 'Method GET missing in alternative zipclass'. "#EC NOTEXT This is a workaround until class CL_ABAP_ZIP is fixed + RAISE EXCEPTION TYPE zcx_excel + EXPORTING + error = lv_errormessage. + ENDTRY. + IF sy-subrc <> 0. + lv_errormessage = 'File not found in zip-archive'(003). + RAISE EXCEPTION TYPE zcx_excel + EXPORTING + error = lv_errormessage. + ENDIF. + + ENDMETHOD. "read + ENDCLASS. "lcl_alternate_zip_archive IMPLEMENTATION *"* use this source file for any type declarations (class *"* definitions, interfaces or data types) you need for method -*"* implementation or private method's signature +*"* implementation or private method's signature + +* +class lcl_zip_archive definition abstract. + public section. + methods read abstract + importing i_filename type csequence + returning value(r_content) type xstring " Remember copy-on-write! + raising zcx_excel. +endclass. *"* use this source file for any macro definitions you need *"* in the implementation part of the class - - - - - - - IXML - - - - - - - - method ZIF_EXCEL_READER~CAN_READ_FILE. -*--------------------------------------------------------------------* -* issue #230 - Pimp my Code -* - Stefan Schmöcker, (done) 2012-11-07 -* - ... -* changes: nothing done in code -* but started discussion about killing this method -*--------------------------------------------------------------------* -* For now always Unknown - r_readable = abap_undefined. - endmethod. - + + + + - method ZIF_EXCEL_READER~LOAD. + METHOD zif_excel_reader~load. *--------------------------------------------------------------------* * ToDos: * 2do§1 Map Document Properties to ZCL_EXCEL -*--------------------------------------------------------------------* - -*--------------------------------------------------------------------* -* issue #230 - Pimp my Code -* - Stefan Schmöcker, (done) 2012-11-07 -* - ... -* changes: renaming variables to naming conventions -* removing unused variables -* aligning code -* adding comments to explain what we are trying to achieve -* commenting on problems/future enhancements/todos we already know of or should decide upon -* adding me-> where possible -*--------------------------------------------------------------------* -* issue#234 - error reading xlsx written by libre office -* - Stefan Schmöcker, 2012-11-07 -* changes: passing new optional input parameter to private attribute *--------------------------------------------------------------------* CONSTANTS: lcv_core_properties TYPE string VALUE 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties', @@ -184,37 +298,36 @@ *--------------------------------------------------------------------* CREATE OBJECT r_excel. -*--------------------------------------------------------------------* -* issue#234 - error reading xlsx written by libre office - me->zif_excel_reader~gv_use_alternate_zip = i_use_alternate_zip. -*--------------------------------------------------------------------* - + zip = create_zip_archive( i_xlsx_binary = i_excel2007 + i_use_alternate_zip = i_use_alternate_zip ). *--------------------------------------------------------------------* * §2 Get file in folderstructure *--------------------------------------------------------------------* - me->excel2007 = i_excel2007. - lo_rels = me->get_ixml_from_zip_archive( '_rels/.rels' ). + lo_rels = get_ixml_from_zip_archive( '_rels/.rels' ). *--------------------------------------------------------------------* * §3 Cycle through the Relationship Tags and use the ones we need *--------------------------------------------------------------------* - lo_node ?= lo_rels->find_from_name( 'Relationship' ). "#EC NOTEXT + lo_node ?= lo_rels->find_from_name( 'Relationship' ). "#EC NOTEXT WHILE lo_node IS BOUND. - me->fill_struct_from_attributes( EXPORTING - ip_element = lo_node - CHANGING - cp_structure = ls_relationship ). + fill_struct_from_attributes( EXPORTING + ip_element = lo_node + CHANGING + cp_structure = ls_relationship ). CASE ls_relationship-type. + WHEN lcv_office_document. +*--------------------------------------------------------------------* +* Parse workbook - main part here +*--------------------------------------------------------------------* + load_workbook( iv_workbook_full_filename = ls_relationship-target + io_excel = r_excel ). + WHEN lcv_core_properties. " 2do§1 Map Document Properties to ZCL_EXCEL - WHEN lcv_office_document. - me->load_workbook( iv_workbook_full_filename = ls_relationship-target - io_excel = r_excel ). - WHEN OTHERS. ENDCASE. @@ -223,61 +336,49 @@ ENDWHILE. - endmethod. +ENDMETHOD. METHOD zif_excel_reader~load_file. -*--------------------------------------------------------------------* -* ToDos: -* 2do§1 decision whether to load from frontend or backend -* current behavior ( autodecide ) should be default -* add optional parameter to allow user to choose -* to load from backend even when online -* 2do§2 loosen typing of i_filename to CLIKE -*--------------------------------------------------------------------* + + DATA: lv_excel_data TYPE xstring. *--------------------------------------------------------------------* -* issue #230 - Pimp my Code -* - Stefan Schmöcker, (done) 2012-11-05 -* - ... -* changes: renaming variables to naming conventions -* renaming variables to indicate what they are used for -* adding comments to explain what we are trying to achieve -* message made to support multilinguality -* aligning code -* commenting on problems/future enhancements/todos we already know of or should decide upon -* adding issue # that has initiated the change - date provided to allow cleaning of code after a certain period -* explicit declaration of type of table instead of implicit declaration -* added errorhandling for open dataset +* Read file into binary string *--------------------------------------------------------------------* -* issue#234 - error reading xlsx written by libre office -* - Stefan Schmöcker, 2012-11-07 -* changes: passing new optional input parameter to private attribute -*--------------------------------------------------------------------* - - DATA: lv_excel_data TYPE xstring. - -* issue#234 - error reading xlsx written by libre office - me->zif_excel_reader~gv_use_alternate_zip = i_use_alternate_zip. - - IF i_from_applserver = abap_true. - lv_excel_data = me->read_from_applserver( i_filename = i_filename ). - ELSE. - lv_excel_data = me->read_from_local_file( i_filename = i_filename ). - ENDIF. + IF i_from_applserver = abap_true. + lv_excel_data = read_from_applserver( i_filename ). + ELSE. + lv_excel_data = read_from_local_file( i_filename ). + ENDIF. *--------------------------------------------------------------------* -* issue#234 - error reading xlsx written by libre office - r_excel = me->zif_excel_reader~load( i_excel2007 = lv_excel_data - i_use_alternate_zip = i_use_alternate_zip ). +* Parse Excel data into ZCL_EXCEL object from binary string *--------------------------------------------------------------------* + r_excel = zif_excel_reader~load( i_excel2007 = lv_excel_data + i_use_alternate_zip = i_use_alternate_zip ). - ENDMETHOD. +ENDMETHOD. + + + + + + METHOD create_zip_archive. + CASE i_use_alternate_zip. + WHEN space. + e_zip = lcl_abap_zip_archive=>create( i_xlsx_binary ). + WHEN OTHERS. + e_zip = lcl_alternate_zip_archive=>create( i_data = i_xlsx_binary + i_alternate_zip_class = i_use_alternate_zip ). + ENDCASE. +ENDMETHOD. + - method FILL_STRUCT_FROM_ATTRIBUTES. + method fill_struct_from_attributes. *--------------------------------------------------------------------* * issue #230 - Pimp my Code * - Stefan Schmöcker, (done) 2012-11-07 @@ -287,12 +388,12 @@ * adding comments to explain what we are trying to achieve *--------------------------------------------------------------------* - DATA: lv_name TYPE string, - lo_attributes TYPE REF TO if_ixml_named_node_map, - lo_attribute TYPE REF TO if_ixml_attribute, - lo_iterator TYPE REF TO if_ixml_node_iterator. + data: lv_name type string, + lo_attributes type ref to if_ixml_named_node_map, + lo_attribute type ref to if_ixml_attribute, + lo_iterator type ref to if_ixml_node_iterator. - FIELD-SYMBOLS: <component> TYPE ANY. + field-symbols: <component> type any. *--------------------------------------------------------------------* * The values of named attributes of a tag are being read and moved into corresponding @@ -305,182 +406,54 @@ * structure has fieldnames Id and Target these would be filled with * "rId3" and "docProps/app.xml" respectively *--------------------------------------------------------------------* - CLEAR cp_structure. + clear cp_structure. lo_attributes = ip_element->get_attributes( ). lo_iterator = lo_attributes->create_iterator( ). lo_attribute ?= lo_iterator->get_next( ). - WHILE lo_attribute IS BOUND. + while lo_attribute is bound. lv_name = lo_attribute->get_name( ). - TRANSLATE lv_name TO UPPER CASE. - ASSIGN COMPONENT lv_name OF STRUCTURE cp_structure TO <component>. - IF sy-subrc = 0. + translate lv_name to upper case. + assign component lv_name of structure cp_structure to <component>. + if sy-subrc = 0. <component> = lo_attribute->get_value( ). - ENDIF. + endif. lo_attribute ?= lo_iterator->get_next( ). - ENDWHILE. + endwhile. - endmethod. +endmethod. - method GET_FROM_ZIP_ARCHIVE. -*--------------------------------------------------------------------* -* issue #230 - Pimp my Code -* - Stefan Schmöcker, (done) 2012-11-07 -* - ... -* changes: aligning code -* adding comments to explain what we are trying to achieve -* changed message passed with exception -* message made to support multilinguality -*--------------------------------------------------------------------**--------------------------------------------------------------------* -* issue#234 - error reading xlsx written by libre office -* - Stefan Schmöcker, 2012-11-07 -* changes: copying coding and using ALTERNATE_ZIP in ELSE-Branch -*--------------------------------------------------------------------* + METHOD get_from_zip_archive. - DATA: lv_errormessage TYPE string. " Can't pass '...'(abc) to exception-class + ASSERT zip IS BOUND. " zip object has to exist at this point + r_content = zip->read( i_filename ). -*--------------------------------------------------------------------* -* An xlsx-file is basically a zip-archive -* From this zip-archive we need to extract one file in binary form -*--------------------------------------------------------------------* - IF me->zif_excel_reader~gv_use_alternate_zip IS INITIAL. "+#234 -*--------------------------------------------------------------------* -* Setup ABAP zip-class with binary exceldata if not done already -*--------------------------------------------------------------------* - IF me->zip IS NOT BOUND. - CREATE OBJECT me->zip. - zip->load( EXPORTING - zip = me->excel2007 - EXCEPTIONS - zip_parse_error = 1 - OTHERS = 2 ). - IF sy-subrc <> 0. - lv_errormessage = 'ZIP parse error'(002). - RAISE EXCEPTION TYPE zcx_excel - EXPORTING - error = lv_errormessage. - ENDIF. - ENDIF. - -*--------------------------------------------------------------------* -* Extract requested filename from archive if possible -*--------------------------------------------------------------------* - zip->get( EXPORTING - name = i_filename - IMPORTING - content = r_content " Contents - EXCEPTIONS - zip_index_error = 1 - zip_decompression_error = 2 - OTHERS = 3 ). - IF sy-subrc <> 0. - lv_errormessage = 'File not found in zip-archive'(003). - RAISE EXCEPTION TYPE zcx_excel - EXPORTING - error = lv_errormessage. - ENDIF. -*--------------------------------------------------------------------* -* issue#234 - begin of insertion -*--------------------------------------------------------------------* - ELSE. -*--------------------------------------------------------------------* -* Setup alternate ABAP zip-class with binary exceldata if not done already -* May become obsolete if SAP fixes standard CL_ABAP_ZIP -*--------------------------------------------------------------------* - IF me->alternate_zip IS NOT BOUND. - CREATE OBJECT me->alternate_zip TYPE (zif_excel_reader~gv_use_alternate_zip). - TRY. - CALL METHOD me->alternate_zip->('LOAD') - EXPORTING - zip = me->excel2007 - EXCEPTIONS - zip_parse_error = 1 - OTHERS = 2. - CATCH cx_sy_dyn_call_illegal_method. - lv_errormessage = 'Method LOAD missing in alternative zipclass'. "#EC NOTEXT This is a workaround until class CL_ABAP_ZIP is fixed - RAISE EXCEPTION TYPE zcx_excel - EXPORTING - error = lv_errormessage. - ENDTRY. - - IF sy-subrc <> 0. - lv_errormessage = 'ZIP parse error'(002). - RAISE EXCEPTION TYPE zcx_excel - EXPORTING - error = lv_errormessage. - ENDIF. - ENDIF. - -*--------------------------------------------------------------------* -* Extract requested filename from archive if possible -*--------------------------------------------------------------------* - TRY. - CALL METHOD me->alternate_zip->('GET') - EXPORTING - name = i_filename - IMPORTING - content = r_content " Contents - EXCEPTIONS - zip_index_error = 1 - zip_decompression_error = 2 - OTHERS = 3. - CATCH cx_sy_dyn_call_illegal_method. - lv_errormessage = 'Method GET missing in alternative zipclass'. "#EC NOTEXT This is a workaround until class CL_ABAP_ZIP is fixed - RAISE EXCEPTION TYPE zcx_excel - EXPORTING - error = lv_errormessage. - ENDTRY. - IF sy-subrc <> 0. - lv_errormessage = 'File not found in zip-archive'(003). - RAISE EXCEPTION TYPE zcx_excel - EXPORTING - error = lv_errormessage. - ENDIF. - ENDIF. -*--------------------------------------------------------------------* -* issue#234 - end of insertion -*--------------------------------------------------------------------* - - endmethod. +ENDMETHOD. - + - method GET_IXML_FROM_ZIP_ARCHIVE. -*--------------------------------------------------------------------* -* ToDos: -* 2do§1 Add comment what is being achieved here -*--------------------------------------------------------------------* - -*--------------------------------------------------------------------* -* issue #230 - Pimp my Code -* - Stefan Schmöcker, (done) 2012-11-07 -* - ... -* changes: renaming variables to naming conventions -* removing unnecessary type-pool -* aligning code -*--------------------------------------------------------------------* + METHOD get_ixml_from_zip_archive. DATA: lv_content TYPE xstring, - lo_ixml TYPE REF TO if_ixml, lo_streamfactory TYPE REF TO if_ixml_stream_factory, lo_istream TYPE REF TO if_ixml_istream, lo_parser TYPE REF TO if_ixml_parser. - *--------------------------------------------------------------------* -* 2do§1 ???? Something happens here ??? +* Load XML file from archive into an input stream, +* and parse that stream into an ixml object *--------------------------------------------------------------------* lv_content = me->get_from_zip_archive( i_filename ). lo_ixml = cl_ixml=>create( ). @@ -490,17 +463,16 @@ lo_parser = lo_ixml->create_parser( stream_factory = lo_streamfactory istream = lo_istream document = r_ixml ). - lo_parser->set_normalizing( is_normalizing ). lo_parser->set_validating( mode = if_ixml_parser=>co_no_validation ). lo_parser->parse( ). - endmethod. +ENDMETHOD. - - - - + + + + method LOAD_DRAWING_ANCHOR. TYPES: BEGIN OF t_c_nv_pr, @@ -651,7 +623,7 @@ - + method LOAD_SHARED_STRINGS. *--------------------------------------------------------------------* * ToDos: @@ -771,7 +743,7 @@ - + method LOAD_STYLES. *--------------------------------------------------------------------* @@ -1495,9 +1467,9 @@ endmethod. - - - + + + method LOAD_WORKBOOK. *--------------------------------------------------------------------* * ToDos: @@ -1916,7 +1888,7 @@ - + method LOAD_WORKSHEET. *--------------------------------------------------------------------* * ToDos: @@ -2616,10 +2588,10 @@ endmethod. - - - - + + + + method LOAD_WORKSHEET_DRAWING. TYPES: BEGIN OF t_c_nv_pr, @@ -2735,9 +2707,9 @@ endmethod. - - - + + + METHOD read_from_applserver. DATA: lv_filelength TYPE i, @@ -2779,9 +2751,9 @@ binary_tab = lt_binary_data. ENDMETHOD. - - - + + + METHOD read_from_local_file. DATA: lv_filelength TYPE i, lt_binary_data TYPE STANDARD TABLE OF x255 WITH NON-UNIQUE DEFAULT KEY, @@ -2838,9 +2810,9 @@ ENDMETHOD. - - - + + + method RESOLVE_PATH. *--------------------------------------------------------------------* * ToDos: @@ -2882,7 +2854,7 @@ endmethod. - + method RESOLVE_REFERENCED_FORMULAE. TYPES: BEGIN OF ty_referenced_cells, sheet TYPE REF TO zcl_excel_worksheet,