CLASS zcl_excel_fill_template DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. TYPES: tv_range_name TYPE c LENGTH 30, BEGIN OF ts_range, sheet TYPE zexcel_sheet_title, name TYPE tv_range_name, start TYPE zexcel_cell_row, stop TYPE zexcel_cell_row, id TYPE i, parent TYPE i, length TYPE i, END OF ts_range, tt_ranges TYPE STANDARD TABLE OF ts_range WITH DEFAULT KEY, BEGIN OF ts_variable, sheet TYPE zexcel_sheet_title, parent TYPE i, name TYPE tv_range_name, END OF ts_variable, BEGIN OF ts_name_style, sheet TYPE zexcel_sheet_title, name TYPE tv_range_name, parent TYPE i, numeric_counter TYPE i, date_counter TYPE i, time_counter TYPE i, text_counter TYPE i, END OF ts_name_style, tt_variables TYPE STANDARD TABLE OF ts_variable WITH DEFAULT KEY, tt_sheet_titles TYPE STANDARD TABLE OF zexcel_sheet_title WITH DEFAULT KEY, tt_name_styles TYPE STANDARD TABLE OF ts_name_style WITH DEFAULT KEY. DATA mt_sheet TYPE tt_sheet_titles READ-ONLY. DATA mt_range TYPE tt_ranges READ-ONLY. DATA mt_var TYPE tt_variables READ-ONLY. DATA mo_excel TYPE REF TO zcl_excel READ-ONLY. DATA mt_name_styles TYPE tt_name_styles READ-ONLY. CLASS-METHODS create IMPORTING !io_excel TYPE REF TO zcl_excel RETURNING VALUE(eo_template_filler) TYPE REF TO zcl_excel_fill_template RAISING zcx_excel. METHODS fill_sheet IMPORTING !iv_data TYPE zcl_excel_template_data=>ts_template_data_sheet RAISING zcx_excel . PROTECTED SECTION. PRIVATE SECTION. TYPES: tt_cell_data_no_key TYPE STANDARD TABLE OF zexcel_s_cell_data WITH DEFAULT KEY. METHODS fill_range IMPORTING !iv_sheet TYPE zexcel_sheet_title !iv_parent TYPE zexcel_cell_row !iv_data TYPE data VALUE(iv_range_length) TYPE zexcel_cell_row !io_sheet TYPE REF TO zcl_excel_worksheet CHANGING !ct_cells TYPE tt_cell_data_no_key !cv_diff TYPE zexcel_cell_row !ct_merged_cells TYPE zcl_excel_worksheet=>mty_ts_merge RAISING zcx_excel . METHODS get_range . METHODS validate_range IMPORTING !io_range TYPE REF TO zcl_excel_range . METHODS discard_overlapped . METHODS sign_range . METHODS find_var RAISING zcx_excel . ENDCLASS. CLASS zcl_excel_fill_template IMPLEMENTATION. METHOD create. CREATE OBJECT eo_template_filler . eo_template_filler->mo_excel = io_excel. eo_template_filler->get_range( ). eo_template_filler->discard_overlapped( ). eo_template_filler->sign_range( ). eo_template_filler->find_var( ). ENDMETHOD. METHOD discard_overlapped. DATA: lt_range TYPE tt_ranges. FIELD-SYMBOLS: TYPE ts_range, TYPE ts_range. SORT mt_range BY sheet start stop. LOOP AT mt_range ASSIGNING . LOOP AT mt_range ASSIGNING WHERE sheet = -sheet AND name <> -name AND stop >= -start AND start < -start AND stop < -stop. EXIT. ENDLOOP. IF sy-subrc NE 0. APPEND TO lt_range. ENDIF. ENDLOOP. mt_range = lt_range. SORT mt_range BY sheet start stop DESCENDING. ENDMETHOD. METHOD fill_range. DATA: lt_tmp_cells_template TYPE tt_cell_data_no_key, lt_cells_result TYPE tt_cell_data_no_key, lt_tmp_cells TYPE tt_cell_data_no_key, ls_cell TYPE zexcel_s_cell_data, lt_tmp_merged_cells_template TYPE zcl_excel_worksheet=>mty_ts_merge, lt_merged_cells_result TYPE zcl_excel_worksheet=>mty_ts_merge, lt_tmp_merged_cells TYPE zcl_excel_worksheet=>mty_ts_merge, ls_merged_cell LIKE LINE OF lt_tmp_merged_cells, lv_start_row TYPE i, lv_stop_row TYPE i, lv_cell_row TYPE i, lv_column_alpha TYPE string, lt_matches TYPE match_result_tab, lv_search TYPE string, lv_var_name TYPE string, lv_cell_value TYPE string. FIELD-SYMBOLS: TYPE ANY TABLE, TYPE any, TYPE ts_range, TYPE zexcel_s_cell_data, TYPE match_result, TYPE any. cv_diff = cv_diff + iv_range_length . lv_start_row = 1. * recursive fill nested range LOOP AT mt_range ASSIGNING WHERE sheet = iv_sheet AND parent = iv_parent. lv_stop_row = -start - 1. * update cells before any range LOOP AT ct_cells INTO ls_cell WHERE cell_row >= lv_start_row AND cell_row <= lv_stop_row . ls_cell-cell_row = ls_cell-cell_row + cv_diff. lv_column_alpha = zcl_excel_common=>convert_column2alpha( ls_cell-cell_column ). ls_cell-cell_coords = ls_cell-cell_row. CONCATENATE lv_column_alpha ls_cell-cell_coords INTO ls_cell-cell_coords. CONDENSE ls_cell-cell_coords NO-GAPS. APPEND ls_cell TO lt_cells_result. ENDLOOP. * update merged cells before range LOOP AT ct_merged_cells INTO ls_merged_cell WHERE row_from >= lv_start_row AND row_to <= lv_stop_row. ls_merged_cell-row_from = ls_merged_cell-row_from + cv_diff. ls_merged_cell-row_to = ls_merged_cell-row_to + cv_diff. APPEND ls_merged_cell TO lt_merged_cells_result. ENDLOOP. lv_start_row = -stop + 1. CLEAR: lt_tmp_cells_template, lt_tmp_merged_cells_template. *copy cell template LOOP AT ct_cells INTO ls_cell WHERE cell_row >= -start AND cell_row <= -stop. APPEND ls_cell TO lt_tmp_cells_template. ENDLOOP. LOOP AT ct_merged_cells INTO ls_merged_cell WHERE row_from >= -start AND row_to <= -stop. APPEND ls_merged_cell TO lt_tmp_merged_cells_template. ENDLOOP. ASSIGN COMPONENT -name OF STRUCTURE iv_data TO
. CHECK sy-subrc = 0. cv_diff = cv_diff - -length. *merge each line of data table with template LOOP AT
ASSIGNING . * make local copy lt_tmp_cells = lt_tmp_cells_template. lt_tmp_merged_cells = lt_tmp_merged_cells_template. *fill data fill_range( EXPORTING io_sheet = io_sheet iv_sheet = iv_sheet iv_parent = -id iv_data = iv_range_length = -length CHANGING ct_cells = lt_tmp_cells ct_merged_cells = lt_tmp_merged_cells cv_diff = cv_diff ). *collect data APPEND LINES OF lt_tmp_cells TO lt_cells_result. APPEND LINES OF lt_tmp_merged_cells TO lt_merged_cells_result. ENDLOOP. ENDLOOP. IF IS ASSIGNED. LOOP AT ct_cells INTO ls_cell WHERE cell_row > -stop . ls_cell-cell_row = ls_cell-cell_row + cv_diff. lv_column_alpha = zcl_excel_common=>convert_column2alpha( ls_cell-cell_column ). ls_cell-cell_coords = ls_cell-cell_row. CONCATENATE lv_column_alpha ls_cell-cell_coords INTO ls_cell-cell_coords. CONDENSE ls_cell-cell_coords NO-GAPS. APPEND ls_cell TO lt_cells_result. ENDLOOP. ct_cells = lt_cells_result. LOOP AT ct_merged_cells INTO ls_merged_cell WHERE row_from > -stop. ls_merged_cell-row_from = ls_merged_cell-row_from + cv_diff. ls_merged_cell-row_to = ls_merged_cell-row_to + cv_diff. APPEND ls_merged_cell TO lt_merged_cells_result. ENDLOOP. ct_merged_cells = lt_merged_cells_result. ELSE. LOOP AT ct_cells ASSIGNING . -cell_row = -cell_row + cv_diff. lv_column_alpha = zcl_excel_common=>convert_column2alpha( -cell_column ). -cell_coords = -cell_row. CONCATENATE lv_column_alpha -cell_coords INTO -cell_coords. CONDENSE -cell_coords NO-GAPS. ENDLOOP. LOOP AT ct_merged_cells INTO ls_merged_cell . ls_merged_cell-row_from = ls_merged_cell-row_from + cv_diff. ls_merged_cell-row_to = ls_merged_cell-row_to + cv_diff. APPEND ls_merged_cell TO lt_merged_cells_result. ENDLOOP. ct_merged_cells = lt_merged_cells_result. ENDIF. *check if variables in this range READ TABLE mt_var TRANSPORTING NO FIELDS WITH KEY sheet = iv_sheet parent = iv_parent. IF sy-subrc = 0. * replace variables of current range with data LOOP AT ct_cells ASSIGNING . CLEAR lt_matches. lv_cell_value = -cell_value. FIND ALL OCCURRENCES OF REGEX '\[[^\]]*\]' IN -cell_value RESULTS lt_matches. SORT lt_matches BY offset DESCENDING . LOOP AT lt_matches ASSIGNING . lv_search = -cell_value+-offset(-length). lv_var_name = lv_search. TRANSLATE lv_var_name TO UPPER CASE. TRANSLATE lv_var_name USING '[ ] '. CONDENSE lv_var_name . ASSIGN COMPONENT lv_var_name OF STRUCTURE iv_data TO . CHECK sy-subrc = 0. " Use SET_CELL to format correctly io_sheet->set_cell( ip_column = -cell_column ip_row = -cell_row - cv_diff ip_value = ). lv_cell_row = -cell_row - cv_diff. READ TABLE io_sheet->sheet_content INTO ls_cell WITH KEY cell_column = -cell_column cell_row = lv_cell_row. REPLACE ALL OCCURRENCES OF lv_search IN -cell_value WITH ls_cell-cell_value. ENDLOOP. IF lines( lt_matches ) = 1. lv_cell_row = -cell_row - cv_diff. READ TABLE io_sheet->sheet_content INTO ls_cell WITH KEY cell_column = -cell_column cell_row = lv_cell_row. -data_type = ls_cell-data_type. ENDIF. ENDLOOP. ENDIF. ENDMETHOD. METHOD fill_sheet. DATA: lo_worksheet TYPE REF TO zcl_excel_worksheet, lt_sheet_cells TYPE tt_cell_data_no_key, lt_merged_cells TYPE zcl_excel_worksheet=>mty_ts_merge, lt_merged_cells_2 TYPE zcl_excel_worksheet=>mty_ts_merge, lv_initial_diff TYPE i. FIELD-SYMBOLS: TYPE any, TYPE zexcel_s_cell_data, TYPE zcl_excel_worksheet=>mty_merge. lo_worksheet = mo_excel->get_worksheet_by_name( iv_data-sheet ). lt_sheet_cells = lo_worksheet->sheet_content. lt_merged_cells = lo_worksheet->mt_merged_cells. ASSIGN iv_data-data->* TO . fill_range( EXPORTING io_sheet = lo_worksheet iv_range_length = 0 iv_sheet = iv_data-sheet iv_parent = 0 iv_data = CHANGING ct_cells = lt_sheet_cells ct_merged_cells = lt_merged_cells cv_diff = lv_initial_diff ). CLEAR lo_worksheet->sheet_content. LOOP AT lt_sheet_cells ASSIGNING . INSERT INTO TABLE lo_worksheet->sheet_content. ENDLOOP. lt_merged_cells_2 = lo_worksheet->mt_merged_cells. LOOP AT lt_merged_cells_2 ASSIGNING . lo_worksheet->delete_merge( ip_cell_column = -col_from ip_cell_row = -row_from ). ENDLOOP. LOOP AT lt_merged_cells ASSIGNING . lo_worksheet->set_merge( ip_column_start = -col_from ip_column_end = -col_to ip_row = -row_from ip_row_to = -row_to ). ENDLOOP. ENDMETHOD. METHOD find_var. DATA: lv_row TYPE i, lv_column TYPE i, lv_column_alpha TYPE string, lv_value TYPE string, ls_name_style TYPE ts_name_style, lo_style TYPE REF TO zcl_excel_style, lo_worksheet TYPE REF TO zcl_excel_worksheet, ls_variable TYPE ts_variable, lv_highest_column TYPE zexcel_cell_column, lv_highest_row TYPE int4, lt_matches TYPE match_result_tab, lv_search TYPE string, lv_replace TYPE string. FIELD-SYMBOLS: TYPE match_result, TYPE ts_range, TYPE zexcel_sheet_title, TYPE ts_name_style. LOOP AT mt_sheet ASSIGNING . lo_worksheet ?= mo_excel->get_worksheet_by_name( ). lv_row = 1. lv_highest_column = lo_worksheet->get_highest_column( ). lv_highest_row = lo_worksheet->get_highest_row( ). WHILE lv_row <= lv_highest_row. lv_column = 1. WHILE lv_column <= lv_highest_column. lv_column_alpha = zcl_excel_common=>convert_column2alpha( lv_column ). CLEAR lo_style. lo_worksheet->get_cell( EXPORTING ip_column = lv_column_alpha ip_row = lv_row IMPORTING ep_value = lv_value ep_style = lo_style ). FIND ALL OCCURRENCES OF REGEX '\[[^\]]*\]' IN lv_value RESULTS lt_matches. LOOP AT lt_matches ASSIGNING . lv_search = lv_value+-offset(-length). lv_replace = lv_search. TRANSLATE lv_replace TO UPPER CASE. CLEAR ls_variable. ls_variable-sheet = . ls_variable-name = lv_replace. TRANSLATE ls_variable-name USING '[ ] '. CONDENSE ls_variable-name . LOOP AT mt_range ASSIGNING WHERE sheet = AND start <= lv_row AND stop >= lv_row. ls_variable-parent = -id. EXIT. ENDLOOP. READ TABLE mt_var TRANSPORTING NO FIELDS WITH KEY sheet = ls_variable-sheet name = ls_variable-name parent = ls_variable-parent. IF sy-subrc NE 0. APPEND ls_variable TO mt_var. ENDIF. READ TABLE mt_name_styles WITH KEY sheet = ls_variable-sheet name = ls_variable-name parent = ls_variable-parent ASSIGNING . IF sy-subrc NE 0. CLEAR ls_name_style. ls_name_style-sheet = . ls_name_style-name = ls_variable-name. ls_name_style-parent = ls_variable-parent. APPEND ls_name_style TO mt_name_styles ASSIGNING . ENDIF. IF lo_style IS NOT BOUND. -text_counter = -text_counter + 1. ELSE. IF lo_style->number_format->format_code CA '0' AND lo_style->number_format->format_code NS '0]'. -numeric_counter = -numeric_counter + 1. ELSEIF lo_style->number_format->format_code CA 'm' AND lo_style->number_format->format_code CA 'd' AND lo_style->number_format->format_code NA 'h'. -date_counter = -date_counter + 1. ELSEIF ( lo_style->number_format->format_code CA 'h' OR lo_style->number_format->format_code CA 's' ) AND lo_style->number_format->format_code NA 'd'. -time_counter = -time_counter + 1. ELSE. -text_counter = -text_counter + 1. ENDIF. ENDIF. ENDLOOP. lv_column = lv_column + 1. ENDWHILE. lv_row = lv_row + 1. ENDWHILE. ENDLOOP. SORT mt_range BY id . ENDMETHOD. METHOD get_range. DATA: lo_worksheets_iterator TYPE REF TO zcl_excel_collection_iterator, lo_worksheet TYPE REF TO zcl_excel_worksheet, lo_range_iterator TYPE REF TO zcl_excel_collection_iterator, lo_range TYPE REF TO zcl_excel_range. lo_worksheets_iterator = mo_excel->get_worksheets_iterator( ). WHILE lo_worksheets_iterator->has_next( ) = abap_true. lo_worksheet ?= lo_worksheets_iterator->get_next( ). APPEND lo_worksheet->get_title( ) TO mt_sheet. ENDWHILE. lo_range_iterator = mo_excel->get_ranges_iterator( ). WHILE lo_range_iterator->has_next( ) = abap_true. lo_range ?= lo_range_iterator->get_next( ). validate_range( lo_range ). ENDWHILE. ENDMETHOD. METHOD sign_range. DATA: lv_tabix TYPE i. FIELD-SYMBOLS: TYPE ts_range, TYPE ts_range. LOOP AT mt_range ASSIGNING . -id = sy-tabix. ENDLOOP. LOOP AT mt_range ASSIGNING . lv_tabix = sy-tabix + 1. LOOP AT mt_range ASSIGNING FROM lv_tabix WHERE sheet = -sheet. IF -start >= -start AND -stop <= -stop. -parent = -id. ENDIF. ENDLOOP. ENDLOOP. SORT mt_range BY id DESCENDING. ENDMETHOD. METHOD validate_range. DATA: lv_range_name TYPE string, lv_range_address TYPE string, lv_range_start TYPE string, lv_range_stop TYPE string, lv_range_sheet TYPE string, lv_tmp_value TYPE string, lt_cell_coord_parts TYPE TABLE OF string, lv_cell_coord_start TYPE string, lv_cell_coord_stop TYPE string, lv_column_start TYPE zexcel_cell_column_alpha, lv_column_end TYPE zexcel_cell_column_alpha, lv_row_start TYPE zexcel_cell_row, lv_row_end TYPE zexcel_cell_row. FIELD-SYMBOLS: TYPE ts_range. lv_range_name = io_range->name. TRANSLATE lv_range_name TO UPPER CASE. lv_range_address = io_range->get_value( ). SPLIT lv_range_address AT '!' INTO lv_range_sheet lv_tmp_value. SPLIT lv_tmp_value AT ':' INTO lv_range_start lv_range_stop. SPLIT lv_range_start AT '$' INTO TABLE lt_cell_coord_parts. IF lines( lt_cell_coord_parts ) > 2. TRY. zcl_excel_common=>convert_range2column_a_row( EXPORTING i_range = lv_range_address IMPORTING e_column_start = lv_column_start e_column_end = lv_column_end e_row_start = lv_row_start e_row_end = lv_row_end ). CATCH zcx_excel. " RETURN. ENDTRY. IF lv_column_start = 'A' AND lv_column_end = 'XFD'. lv_cell_coord_start = |{ lv_row_start }|. lv_cell_coord_stop = |{ lv_row_end }|. CLEAR lt_cell_coord_parts. CLEAR lv_range_stop. ELSE. RETURN. ENDIF. ENDIF. IF lines( lt_cell_coord_parts ) >= 2. READ TABLE lt_cell_coord_parts INTO lv_cell_coord_start INDEX 2. ENDIF. IF lv_cell_coord_start CO '0123456789'. APPEND INITIAL LINE TO mt_range ASSIGNING . -sheet = lv_range_sheet. -name = lv_range_name. -start = lv_cell_coord_start. SPLIT lv_range_stop AT '$' INTO TABLE lt_cell_coord_parts. READ TABLE lt_cell_coord_parts INTO lv_cell_coord_stop INDEX 2. -stop = lv_cell_coord_stop. -length = -stop - -start + 1. ENDIF. ENDMETHOD. ENDCLASS.