CLASS zcl_excel_reader_huge_file DEFINITION
  PUBLIC
  INHERITING FROM zcl_excel_reader_2007
  CREATE PUBLIC .

  PUBLIC SECTION.
  PROTECTED SECTION.

    METHODS load_shared_strings
        REDEFINITION .
    METHODS load_worksheet
        REDEFINITION .
  PRIVATE SECTION.

    TYPES:
      BEGIN OF t_cell_content,
        datatype TYPE zexcel_cell_data_type,
        value    TYPE zexcel_cell_value,
        formula  TYPE zexcel_cell_formula,
        style    TYPE zexcel_cell_style,
      END OF t_cell_content .
    TYPES:
      BEGIN OF t_cell_coord,
        row    TYPE zexcel_cell_row,
        column TYPE zexcel_cell_column_alpha,
      END OF t_cell_coord .
    TYPES:
      BEGIN OF t_cell.
        INCLUDE TYPE t_cell_coord AS coord.
        INCLUDE TYPE t_cell_content AS content.
      TYPES: END OF t_cell .

    INTERFACE if_sxml_node LOAD .
    CONSTANTS c_end_of_stream TYPE if_sxml_node=>node_type VALUE if_sxml_node=>co_nt_final. "#EC NOTEXT
    CONSTANTS c_element_open TYPE if_sxml_node=>node_type VALUE if_sxml_node=>co_nt_element_open. "#EC NOTEXT
    CONSTANTS c_element_close TYPE if_sxml_node=>node_type VALUE if_sxml_node=>co_nt_element_close. "#EC NOTEXT
    DATA:
      BEGIN OF gs_buffer_style,
        index TYPE i VALUE -1,
        guid  TYPE zexcel_cell_style,
      END OF gs_buffer_style .
    CONSTANTS c_attribute TYPE if_sxml_node=>node_type VALUE if_sxml_node=>co_nt_attribute. "#EC NOTEXT
    CONSTANTS c_node_value TYPE if_sxml_node=>node_type VALUE if_sxml_node=>co_nt_value. "#EC NOTEXT

    METHODS skip_to
      IMPORTING
        !iv_element_name TYPE string
        !io_reader       TYPE REF TO if_sxml_reader
      RAISING
        lcx_not_found .
    METHODS fill_cell_from_attributes
      IMPORTING
        !io_reader     TYPE REF TO if_sxml_reader
      RETURNING
        VALUE(es_cell) TYPE t_cell
      RAISING
        lcx_not_found .
    METHODS read_shared_strings
      IMPORTING
        !io_reader               TYPE REF TO if_sxml_reader
      RETURNING
        VALUE(et_shared_strings) TYPE stringtab .
    METHODS get_cell_coord
      IMPORTING
        !iv_coord       TYPE string
      RETURNING
        VALUE(es_coord) TYPE t_cell_coord .
    METHODS put_cell_to_worksheet
      IMPORTING
        !io_worksheet TYPE REF TO zcl_excel_worksheet
        !is_cell      TYPE t_cell .
    METHODS get_shared_string
      IMPORTING
        !iv_index       TYPE any
      RETURNING
        VALUE(ev_value) TYPE string
      RAISING
        lcx_not_found .
    METHODS get_style
      IMPORTING
        !iv_index            TYPE any
      RETURNING
        VALUE(ev_style_guid) TYPE zexcel_cell_style
      RAISING
        lcx_not_found .
    METHODS read_worksheet_data
      IMPORTING
        !io_reader    TYPE REF TO if_sxml_reader
        !io_worksheet TYPE REF TO zcl_excel_worksheet
      RAISING
        lcx_not_found .
    METHODS get_sxml_reader
      IMPORTING
        !iv_path         TYPE string
      RETURNING
        VALUE(eo_reader) TYPE REF TO if_sxml_reader
      RAISING
        zcx_excel .
ENDCLASS.



CLASS zcl_excel_reader_huge_file IMPLEMENTATION.


  METHOD fill_cell_from_attributes.

    WHILE io_reader->node_type NE c_end_of_stream.
      io_reader->next_attribute( ).
      IF io_reader->node_type NE c_attribute.
        EXIT.
      ENDIF.
      CASE io_reader->name.
        WHEN `t`.
          es_cell-datatype = io_reader->value.
        WHEN `s`.
          IF io_reader->value IS NOT INITIAL.
            es_cell-style = get_style( io_reader->value ).
          ENDIF.
        WHEN `r`.
          es_cell-coord = get_cell_coord( io_reader->value ).
      ENDCASE.
    ENDWHILE.

  ENDMETHOD.


  METHOD get_cell_coord.

    zcl_excel_common=>convert_columnrow2column_a_row(
      EXPORTING
        i_columnrow = iv_coord
      IMPORTING
        e_column    = es_coord-column
        e_row       = es_coord-row
      ).

  ENDMETHOD.


  METHOD get_shared_string.
    DATA: lv_tabix TYPE i,
          lv_error TYPE string.
    lv_tabix = iv_index + 1.
    READ TABLE shared_strings INTO ev_value INDEX lv_tabix.
    IF sy-subrc NE 0.
      CONCATENATE 'Entry ' iv_index ' not found in Shared String Table' INTO lv_error.
      RAISE EXCEPTION TYPE lcx_not_found
        EXPORTING
          error = lv_error.
    ENDIF.
  ENDMETHOD.


  METHOD get_style.

    DATA: lv_tabix TYPE i,
          lo_style TYPE REF TO zcl_excel_style,
          lv_error TYPE string.

    IF gs_buffer_style-index NE iv_index.
      lv_tabix = iv_index + 1.
      READ TABLE styles INTO lo_style INDEX lv_tabix.
      IF sy-subrc NE 0.
        CONCATENATE 'Entry ' iv_index ' not found in Style Table' INTO lv_error.
        RAISE EXCEPTION TYPE lcx_not_found
          EXPORTING
            error = lv_error.
      ELSE.
        gs_buffer_style-index = iv_index.
        gs_buffer_style-guid  = lo_style->get_guid( ).
      ENDIF.
    ENDIF.

    ev_style_guid = gs_buffer_style-guid.

  ENDMETHOD.


  METHOD get_sxml_reader.

    DATA: lv_xml TYPE xstring.

    lv_xml = get_from_zip_archive( iv_path ).
    eo_reader = cl_sxml_string_reader=>create( lv_xml ).

  ENDMETHOD.


  METHOD load_shared_strings.

    DATA: lo_reader TYPE REF TO if_sxml_reader.

    lo_reader = get_sxml_reader( ip_path ).

    shared_strings = read_shared_strings( lo_reader ).

  ENDMETHOD.


  METHOD load_worksheet.

    DATA: lo_reader TYPE REF TO if_sxml_reader.
    DATA: lx_not_found TYPE REF TO lcx_not_found.

    lo_reader = get_sxml_reader( ip_path ).

    TRY.

        read_worksheet_data( io_reader    = lo_reader
                             io_worksheet = io_worksheet ).

      CATCH lcx_not_found INTO lx_not_found.
        zcx_excel=>raise_text( lx_not_found->error ).
    ENDTRY.
  ENDMETHOD.


  METHOD put_cell_to_worksheet.
    CHECK is_cell-value IS NOT INITIAL
       OR is_cell-formula IS NOT INITIAL
       OR is_cell-style IS NOT INITIAL.
    CALL METHOD io_worksheet->set_cell
      EXPORTING
        ip_column    = is_cell-column
        ip_row       = is_cell-row
        ip_value     = is_cell-value
        ip_formula   = is_cell-formula
        ip_data_type = is_cell-datatype
        ip_style     = is_cell-style.
  ENDMETHOD.


  METHOD read_shared_strings.

    DATA lv_value TYPE string.

    WHILE io_reader->node_type NE c_end_of_stream.
      io_reader->next_node( ).
      IF io_reader->name EQ `t`.
        CASE io_reader->node_type .
          WHEN c_element_open .
            CLEAR lv_value .
          WHEN c_node_value .
            lv_value = lv_value && io_reader->value .
          WHEN c_element_close .
            APPEND lv_value TO et_shared_strings.
        ENDCASE .
      ENDIF.
    ENDWHILE.

  ENDMETHOD.


  METHOD read_worksheet_data.

    DATA: ls_cell   TYPE t_cell.

* Skip to <sheetData> element
    skip_to(  iv_element_name = `sheetData`  io_reader = io_reader ).

* Main loop: Evaluate the <c> elements and its children
    WHILE io_reader->node_type NE c_end_of_stream.
      io_reader->next_node( ).
      CASE io_reader->node_type.
        WHEN c_element_open.
          IF io_reader->name EQ `c`.
            ls_cell = fill_cell_from_attributes( io_reader ).
          ENDIF.
        WHEN c_node_value.
          CASE io_reader->name.
            WHEN `f`.
              ls_cell-formula = io_reader->value.
            WHEN `v`.
              IF ls_cell-datatype EQ `s`.
                ls_cell-value = get_shared_string( io_reader->value ).
              ELSE.
                ls_cell-value = io_reader->value.
              ENDIF.
            WHEN `t` OR `is`.
              ls_cell-value = io_reader->value.
          ENDCASE.
        WHEN c_element_close.
          CASE io_reader->name.
            WHEN `c`.
              put_cell_to_worksheet( is_cell = ls_cell io_worksheet = io_worksheet ).
            WHEN `sheetData`.
              EXIT.
          ENDCASE.
      ENDCASE.
    ENDWHILE.

  ENDMETHOD.


  METHOD skip_to.

    DATA: lv_error TYPE string.

* Skip forward to given element
    WHILE io_reader->name NE iv_element_name OR
          io_reader->node_type NE c_element_open.
      io_reader->next_node( ).
      IF io_reader->node_type = c_end_of_stream.
        CONCATENATE 'XML error: Didn''t find element <' iv_element_name '>' INTO lv_error.
        RAISE EXCEPTION TYPE lcx_not_found
          EXPORTING
            error = lv_error.
      ENDIF.
    ENDWHILE.


  ENDMETHOD.
ENDCLASS.
